diff --git a/README.md b/README.md index 0e14f97..443cca3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ img -Pywal is a tool that generates a color palette from the dominant colors in an image. It then applies the colors system-wide and on-the-fly in all of your favourite programs. +Pywal is a tool that generates a color palette from the dominant colors in an image. It then applies the colors system-wide and on-the-fly in all of your favourite programs. There are currently 5 supported color generation backends, each providing a different palette of colors from each image. You're bound to find an appealing color-scheme. @@ -21,4 +21,4 @@ The goal of Pywal was to be as out of the way as possible. It doesn't modify any Terminal emulators and TTYs have their color-schemes updated in real-time with no delay. With minimal configuration this functionality can be extended to almost anything running on your system. -### More: \[[Installation](https://github.com/dylanaraps/pywal/wiki/Installation)\] \[[Getting Started](https://github.com/dylanaraps/pywal/wiki/Getting-Started)\] \[[Customization](https://github.com/dylanaraps/pywal/wiki/Customization)\] \[[Wiki](https://github.com/dylanaraps/pywal/wiki)\] \[[Screenshots](https://www.reddit.com/r/unixporn/search?q=wal&restrict_sr=on&sort=relevance&t=all)\] +### More: \[[Installation](https://github.com/dylanaraps/pywal/wiki/Installation)] \[[Getting Started](https://github.com/dylanaraps/pywal/wiki/Getting-Started)] \[[Customization](https://github.com/dylanaraps/pywal/wiki/Customization)] \[[Wiki](https://github.com/dylanaraps/pywal/wiki)] \[[Screenshots](https://www.reddit.com/r/unixporn/search?q=wal&restrict_sr=on&sort=relevance&t=all)] diff --git a/pywal/__main__.py b/pywal/__main__.py index 0f0f077..91ead11 100644 --- a/pywal/__main__.py +++ b/pywal/__main__.py @@ -45,7 +45,7 @@ def get_args(): arg.add_argument("--theme", "-f", metavar="/path/to/file or theme_name", help="Which colorscheme file to use. \ - Use 'wal --theme' to list builtin themes.", + Use 'wal --theme' to list builtin and user themes.", const="list_themes", nargs="?") arg.add_argument("--iterative", action="store_true", @@ -82,6 +82,11 @@ def get_args(): arg.add_argument("-o", metavar="\"script_name\"", action="append", help="External script to run after \"wal\".") + arg.add_argument("-p", metavar="\"theme_name\"", + help="permanently save theme to " + "$XDG_CONFIG_HOME/wal/colorschemes with " + "the specified name") + arg.add_argument("-q", action="store_true", help="Quiet mode, don\'t print anything.") @@ -192,6 +197,9 @@ def parse_args(parser): if not args.n: wallpaper.change(colors_plain["wallpaper"]) + if args.p: + theme.save(colors_plain, args.p, args.l) + sequences.send(colors_plain, to_send=not args.s, vte_fix=args.vte) if sys.stdout.isatty(): diff --git a/pywal/export.py b/pywal/export.py index 88c98a6..0c71a76 100644 --- a/pywal/export.py +++ b/pywal/export.py @@ -3,22 +3,60 @@ Export colors in various formats. """ import logging import os +import re -from .settings import CACHE_DIR, MODULE_DIR, CONF_DIR from . import util +from .settings import CACHE_DIR, CONF_DIR, MODULE_DIR def template(colors, input_file, output_file=None): """Read template file, substitute markers and save the file elsewhere.""" template_data = util.read_file_raw(input_file) + for i, l in enumerate(template_data): + for match in re.finditer(r"(?<=(? 1: + new_color = function(*func[1].split(",")) + else: + new_color = function() + # string to replace generated colors + if func[0] != '.': + replace_str += "." + replace_str += "(".join(func) + ")" + # If the color was changed, replace with a unique identifier. + if new_color is not colors[cname]: + template_data[i] = l.replace( + replace_str, "color" + new_color.strip) + colors["color" + new_color.strip] = new_color try: template_data = "".join(template_data).format(**colors) except ValueError: logging.error("Syntax error in template file '%s'.", input_file) return - util.save_file(template_data, output_file) @@ -52,6 +90,7 @@ def get_export_type(export_type): "speedcrunch": "colors-speedcrunch.json", "sway": "colors-sway", "tty": "colors-tty.sh", + "vscode": "colors-vscode.json", "waybar": "colors-waybar.css", "xresources": "colors.Xresources", "xmonad": "colors.hs", diff --git a/pywal/templates/colors-vscode.json b/pywal/templates/colors-vscode.json new file mode 100644 index 0000000..0239717 --- /dev/null +++ b/pywal/templates/colors-vscode.json @@ -0,0 +1,14 @@ +{{ + "editor.tokenColorCustomizations": {{ + "functions": "{color1}", + "keywords": "{color2}", + "numbers": "{color3}", + "strings": "{color4}", + "types": "{color5}", + "variables": "{color6}", + "comments": "{color8}" + }}, + "workbench.colorCustomizations": {{ + "editor.background": "{background}" + }} +}} diff --git a/pywal/theme.py b/pywal/theme.py index fcd1dd1..9dc4f13 100644 --- a/pywal/theme.py +++ b/pywal/theme.py @@ -6,7 +6,7 @@ import os import random import sys -from .settings import CONF_DIR, MODULE_DIR +from .settings import CACHE_DIR, CONF_DIR, MODULE_DIR from . import util @@ -19,20 +19,30 @@ def list_out(): user_themes = [theme.name.replace(".json", "") for theme in list_themes_user()] + try: + last_used_theme = util.read_file(os.path.join( + CACHE_DIR, "last_used_theme"))[0].replace(".json", "") + except FileNotFoundError: + last_used_theme = "" + if user_themes: print("\033[1;32mUser Themes\033[0m:") - print(" -", "\n - ".join(sorted(user_themes))) + print(" -", "\n - ".join(t + " (last used)" if t == last_used_theme + else t for t in sorted(user_themes))) print("\033[1;32mDark Themes\033[0m:") - print(" -", "\n - ".join(sorted(dark_themes))) + print(" -", "\n - ".join(t + " (last used)" if t == last_used_theme else t + for t in sorted(dark_themes))) print("\033[1;32mLight Themes\033[0m:") - print(" -", "\n - ".join(sorted(ligh_themes))) + print(" -", "\n - ".join(t + " (last used)" if t == last_used_theme else t + for t in sorted(ligh_themes))) print("\033[1;32mExtra\033[0m:") print(" - random (select a random dark theme)") print(" - random_dark (select a random dark theme)") print(" - random_light (select a random light theme)") + print(" - random_user (select a random user theme)") def list_themes(dark=True): @@ -88,6 +98,13 @@ def get_random_theme(dark=True): return themes[0] +def get_random_theme_user(): + """Get a random theme file from user theme directories.""" + themes = [theme.path for theme in list_themes_user()] + random.shuffle(themes) + return themes[0] + + def file(input_file, light=False): """Import colorscheme from json file.""" util.create_dir(os.path.join(CONF_DIR, "colorschemes/light/")) @@ -106,6 +123,9 @@ def file(input_file, light=False): elif input_file == "random_light": theme_file = get_random_theme(light) + elif input_file == "random_user": + theme_file = get_random_theme_user() + elif os.path.isfile(user_theme_file): theme_file = user_theme_file @@ -116,9 +136,19 @@ def file(input_file, light=False): if os.path.isfile(theme_file): logging.info("Set theme to \033[1;37m%s\033[0m.", os.path.basename(theme_file)) + util.save_file(os.path.basename(theme_file), + os.path.join(CACHE_DIR, "last_used_theme")) return parse(theme_file) logging.error("No %s colorscheme file found.", bri) logging.error("Try adding '-l' to set light themes.") logging.error("Try removing '-l' to set dark themes.") sys.exit(1) + + +def save(colors, theme_name, light=False): + """Save colors to a theme file.""" + theme_file = theme_name + ".json" + theme_path = os.path.join(CONF_DIR, "colorschemes", + "light" if light else "dark", theme_file) + util.save_file_json(colors, theme_path) diff --git a/pywal/util.py b/pywal/util.py index e4b146a..cd6628a 100644 --- a/pywal/util.py +++ b/pywal/util.py @@ -5,10 +5,11 @@ import colorsys import json import logging import os +import platform +import re import shutil import subprocess import sys -import platform class Color: @@ -35,7 +36,7 @@ class Color: def rgba(self): """Convert a hex color to rgba.""" return "rgba(%s,%s,%s,%s)" % (*hex_to_rgb(self.hex_color), - int(self.alpha_num)/100) + int(self.alpha_num) / 100) @property def alpha(self): @@ -57,6 +58,21 @@ class Color: """Strip '#' from color.""" return self.hex_color[1:] + def lighten(self, percent): + """Lighten color by percent""" + percent = float(re.sub(r'[\D\.]', '', str(percent))) + return Color(lighten_color(self.hex_color, percent / 100)) + + def darken(self, percent): + """Darken color by percent""" + percent = float(re.sub(r'[\D\.]', '', str(percent))) + return Color(darken_color(self.hex_color, percent / 100)) + + def saturate(self, percent): + """Saturate a color""" + percent = float(re.sub(r'[\D\.]', '', str(percent))) + return Color(saturate_color(self.hex_color, percent / 100)) + def read_file(input_file): """Read data from a file and trim newlines.""" @@ -156,11 +172,11 @@ def blend_color(color, color2): def saturate_color(color, amount): """Saturate a hex color.""" r, g, b = hex_to_rgb(color) - r, g, b = [x/255.0 for x in (r, g, b)] + r, g, b = [x / 255.0 for x in (r, g, b)] h, l, s = colorsys.rgb_to_hls(r, g, b) s = amount r, g, b = colorsys.hls_to_rgb(h, l, s) - r, g, b = [x*255.0 for x in (r, g, b)] + r, g, b = [x * 255.0 for x in (r, g, b)] return rgb_to_hex((int(r), int(g), int(b)))