diff --git a/README.md b/README.md index 224bc9a..a2ad7e1 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@
- fabric + fabric

diff --git a/config/material-colors/generate.py b/config/material-colors/generate.py index 5f43db1..32504e0 100644 --- a/config/material-colors/generate.py +++ b/config/material-colors/generate.py @@ -1,40 +1,28 @@ -import argparse -import hashlib -import json import os -import pickle import random -import re import subprocess - -import numpy as np -from materialyoucolor.dynamiccolor.material_dynamic_colors import ( - MaterialDynamicColors, -) # type: ignore # noqa -from materialyoucolor.hct import Hct # type: ignore -from materialyoucolor.quantize import QuantizeCelebi # type: ignore -from materialyoucolor.scheme.dynamic_scheme import DynamicScheme # type: ignore # noqa -from materialyoucolor.scheme.scheme_content import SchemeContent # type: ignore # noqa -from materialyoucolor.scheme.scheme_expressive import ( - SchemeExpressive, -) # type: ignore # noqa -from materialyoucolor.scheme.scheme_fidelity import ( - SchemeFidelity, -) # type: ignore # noqa -from materialyoucolor.scheme.scheme_fruit_salad import ( - SchemeFruitSalad, -) # type: ignore # noqa -from materialyoucolor.scheme.scheme_monochrome import ( - SchemeMonochrome, -) # type: ignore # noqa -from materialyoucolor.scheme.scheme_neutral import SchemeNeutral # type: ignore # noqa -from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow # type: ignore # noqa -from materialyoucolor.scheme.scheme_tonal_spot import ( - SchemeTonalSpot, -) # type: ignore # noqa -from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant # type: ignore # noqa -from materialyoucolor.score.score import Score # type: ignore +import json from PIL import Image +import argparse +from materialyoucolor.quantize import QuantizeCelebi # type: ignore +from materialyoucolor.score.score import Score # type: ignore +from materialyoucolor.hct import Hct # type: ignore +from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors # type: ignore # noqa +from materialyoucolor.scheme.dynamic_scheme import DynamicScheme # type: ignore # noqa +from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot # type: ignore # noqa +from materialyoucolor.scheme.scheme_expressive import SchemeExpressive # type: ignore # noqa +from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad # type: ignore # noqa +from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome # type: ignore # noqa +from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow # type: ignore # noqa +from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant # type: ignore # noqa +from materialyoucolor.scheme.scheme_neutral import SchemeNeutral # type: ignore # noqa +from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity # type: ignore # noqa +from materialyoucolor.scheme.scheme_content import SchemeContent # type: ignore # noqa +import hashlib +import pickle +import numpy as np +import re + schemes = { "tonalSpot": SchemeTonalSpot, @@ -53,7 +41,7 @@ def join(*args): return os.path.join(*args) -home = os.path.expanduser("~") +home = os.path.expanduser('~') script_dir = os.path.dirname(os.path.abspath(__file__)) cache_path = f"{home}/.cache/material/" @@ -61,14 +49,14 @@ os.makedirs(cache_path, exist_ok=True) def rgb_to_hex(rgb): - return "#{:02x}{:02x}{:02x}".format(*rgb[:3]) + return '#{:02x}{:02x}{:02x}'.format(*rgb[:3]) def rgba_to_rgb(rgba): - return f"{rgba[0]}, {rgba[1]}, {rgba[2]}" + return f'{rgba[0]}, {rgba[1]}, {rgba[2]}' -class Color: +class Color(): def __init__(self, scheme) -> None: self.scheme = scheme @@ -82,12 +70,8 @@ class Color: class ColorsCache: def __init__( - self, - colors: DynamicScheme | dict, - wallpaper: str, - original_color: int, - contrast_level: int, - scheme_name: str, + self, colors: DynamicScheme | dict, wallpaper: str, + original_color: int, contrast_level: int, scheme_name: str ) -> None: self.colors: dict[str, str] = {} self.wallpaper = wallpaper @@ -111,7 +95,7 @@ def colors_dict(cache: ColorsCache): "colors": cache.colors, "original_color": cache.original_color, "contrast_level": cache.contrast_level, - "scheme_name": cache.scheme_name, + "scheme_name": cache.scheme_name } return dict @@ -124,7 +108,10 @@ def get_cache_object(object: dict | str): original_color = object["original_color"] contrast_level = object.get("contrast_level", 0) scheme_name = object.get("scheme_name", "tonalSpot") - return ColorsCache(colors, wallpaper, original_color, contrast_level, scheme_name) + return ColorsCache( + colors, wallpaper, original_color, + contrast_level, scheme_name + ) def get_file_list(folder_path): @@ -137,9 +124,11 @@ def get_file_list(folder_path): ready_templates = { "colors.css": "@define-color {name} {hex};\n", - "colors.scss": "${name}: {hex};\n", + "colors.scss": "${name}: {hex};\n" +} +additional = { + "onBackground": "foreground" } -additional = {"onBackground": "foreground"} def generate_color_map(scheme: DynamicScheme): @@ -158,23 +147,25 @@ class ColorFormatter: def apply_transformations(self, value, transformations): intermediate_transforms = [ - t for t in transformations if not (t.startswith("strip") or t == "rgb") + t for t in transformations + if not (t.startswith("strip") or t == "rgb") ] final_transforms = [ - t for t in transformations if t.startswith("strip") or t == "rgb" + t for t in transformations + if t.startswith("strip") or t == "rgb" ] for transform in intermediate_transforms: if transform.startswith("lighten"): - percent = int(re.search(r"\d+", transform).group()) + percent = int(re.search(r'\d+', transform).group()) value = self.adjust_brightness(value, percent) elif transform.startswith("darken"): - percent = int(re.search(r"\d+", transform).group()) + percent = int(re.search(r'\d+', transform).group()) value = self.adjust_brightness(value, -percent) for transform in final_transforms: if transform.startswith("strip"): - value = value.lstrip("#") + value = value.lstrip('#') elif transform == "rgb": value = self.hex_to_rgb(value) @@ -184,22 +175,24 @@ class ColorFormatter: def min_max(v): return min(255, max(0, v)) - hex_color = hex_color.lstrip("#") - r, g, b = (int(hex_color[i : i + 2], 16) for i in (0, 2, 4)) + hex_color = hex_color.lstrip('#') + r, g, b = (int(hex_color[i:i+2], 16) for i in (0, 2, 4)) r = min_max(int(r * (1 + factor / 100))) g = min_max(int(g * (1 + factor / 100))) b = min_max(int(b * (1 + factor / 100))) - return f"#{r:02X}{g:02X}{b:02X}".lower() + return f'#{r:02X}{g:02X}{b:02X}'.lower() def hex_to_rgb(self, hex_color): - hex_color = hex_color.lstrip("#") - r, g, b = (int(hex_color[i : i + 2], 16) for i in (0, 2, 4)) - return f"{r},{g},{b}" + hex_color = hex_color.lstrip('#') + r, g, b = (int(hex_color[i:i+2], 16) for i in (0, 2, 4)) + return f'{r},{g},{b}' def parse_transformations(self, transformations_str): - transformations = re.findall(r"(\w+)(?:\((\d*)\))?", transformations_str) + transformations = re.findall( + r'(\w+)(?:\((\d*)\))?', transformations_str + ) result = [] for command, arg in transformations: if command: @@ -210,7 +203,7 @@ class ColorFormatter: return result def format(self, text): - pattern = r"(<(\w+)(?:\.(.+))?>)" + pattern = r'(<(\w+)(?:\.(.+))?>)' matches = re.findall(pattern, text) result = [] last_end = 0 @@ -218,30 +211,27 @@ class ColorFormatter: for match in matches: full_match, key, transformations_str = match if key in self.color_map: - result.append(text[last_end : text.find(full_match, last_end)]) + result.append(text[last_end:text.find(full_match, last_end)]) value = self.color_map[key] if transformations_str: - transformations = self.parse_transformations(transformations_str) + transformations = self.parse_transformations( + transformations_str + ) value = self.apply_transformations(value, transformations) result.append(value) last_end = text.find(full_match, last_end) + len(full_match) result.append(text[last_end:]) - result = "".join(result) - pattern = r"<\\\\([^>]+)>" + result = ''.join(result) + pattern = r'<\\\\([^>]+)>' - result = re.sub(pattern, r"<\1>", result) + result = re.sub(pattern, r'<\1>', result) return result -def generate_templates( - folder: str, - output_folder: str, - scheme: DynamicScheme, - color_scheme: str, - wallpaper: str = "", -): +def generate_templates(folder: str, output_folder: str, scheme: DynamicScheme, + color_scheme: str, wallpaper: str = ''): if not os.path.exists(output_folder): os.makedirs(output_folder) if not os.path.exists(folder): @@ -254,14 +244,41 @@ def generate_templates( formatter = ColorFormatter(scheme) template = formatter.format(template) template = ( - template.replace("", color_scheme) + template + .replace("", color_scheme) .replace("", output_folder) .replace("", wallpaper) ) new_path = join(output_folder, os.path.basename(file_path)) - with open(new_path, "w") as f: + with open(new_path, 'w') as f: f.write(template) + for file in ready_templates: + _template = "" + for color in vars(MaterialDynamicColors).keys(): + color_name = getattr(MaterialDynamicColors, color) + if hasattr(color_name, "get_hct"): + rgba = color_name.get_hct(scheme).to_rgba() + hex_color = rgb_to_hex(rgba) + rgb_color = rgba_to_rgb(rgba) + new_line = ready_templates[file].format( + name=color, + hex=hex_color, + rgb=rgb_color + ) + _template += new_line + if color in additional: + new_line = ready_templates[file].format( + name=additional[color], + hex=hex_color, + rgb=rgb_color + ) + _template += new_line + + new_path = join(output_folder, os.path.basename(file)) + with open(new_path, 'w') as f: + f.write(_template) + def run_hooks(folder: str): if not os.path.exists(folder): @@ -270,9 +287,9 @@ def run_hooks(folder: str): for file_path in file_list: file_name = os.path.basename(file_path) if str(file_name).endswith(".py"): - subprocess.run(["python", "-O", file_path]) + subprocess.run(['python', '-O', file_path]) elif str(file_name).endswith(".sh"): - subprocess.run(["sh", file_path]) + subprocess.run(['sh', file_path]) def process_image(image_path, quality=2, num_colors=128): @@ -280,18 +297,18 @@ def process_image(image_path, quality=2, num_colors=128): global cache_path _cache_path = join(cache_path, "cached_colors") hash_object = hashlib.md5(image_path.encode()) - cache_filename = hash_object.hexdigest() + ".pkl" + cache_filename = hash_object.hexdigest() + '.pkl' os.makedirs(_cache_path, exist_ok=True) return join(_cache_path, cache_filename) def load_from_cache(cache_path): if os.path.exists(cache_path): - with open(cache_path, "rb") as f: + with open(cache_path, 'rb') as f: return pickle.load(f) return None def save_to_cache(cache_path, data): - with open(cache_path, "wb") as f: + with open(cache_path, 'wb') as f: pickle.dump(data, f) cache_path = get_cache_path(image_path) @@ -300,7 +317,7 @@ def process_image(image_path, quality=2, num_colors=128): if cached_result is not None: return cached_result - image = Image.open(image_path).convert("RGB") + image = Image.open(image_path).convert('RGB') image_data = np.array(image) pixel_array = image_data[::quality, ::quality].reshape(-1, 3) @@ -315,11 +332,8 @@ def process_image(image_path, quality=2, num_colors=128): def main( - color_scheme: str, - image_path: str, - use_color: int | None = None, - scheme_name: str | None = None, - contrast_level: int = 0, + color_scheme: str, image_path: str, use_color: int | None = None, + scheme_name: str | None = None, contrast_level: int = 0 ): scheme_name = scheme_name or "tonalSpot" scheme = schemes[scheme_name] @@ -330,24 +344,34 @@ def main( else: color = use_color - scheme = scheme(Hct.from_int(color), is_dark, contrast_level) + scheme = scheme( + Hct.from_int(color), + is_dark, + contrast_level + ) # for color in vars(MaterialDynamicColors).keys(): # color_name = getattr(MaterialDynamicColors, color) # if hasattr(color_name, "get_hct"): # print(color, rgb_to_hex(color_name.get_hct(scheme).to_rgba())) - with open(join(cache_path, "colors.json"), "w") as f: - object = ColorsCache(scheme, image_path, color, contrast_level, scheme_name) + with open(join(cache_path, "colors.json"), 'w') as f: + object = ColorsCache( + scheme, image_path, color, contrast_level, scheme_name + ) json.dump(colors_dict(object), f, indent=2) generate_templates( f"{script_dir}/templates", cache_path, scheme, color_scheme, image_path ) + generate_templates( + f"{script_dir}/templates/svg", join(cache_path, "svg/"), scheme, + color_scheme, image_path + ) if is_dark: - subprocess.run(["sh", join(script_dir, "gtk-material.sh")]) + subprocess.run(['sh', join(script_dir, 'gtk-material.sh')]) else: - subprocess.run(["sh", join(script_dir, "gtk-material.sh"), "--light"]) + subprocess.run(['sh', join(script_dir, 'gtk-material.sh'), '--light']) run_hooks(join(script_dir, "hooks")) @@ -355,27 +379,26 @@ def main( def _argparse() -> tuple[bool, bool, str, str, str, str]: parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group(required=True) - group.add_argument("-R", help="Restore previous colorscheme.", action="store_true") group.add_argument( - "-w", help="Use last used wallpaper for color generation.", action="store_true" + "-R", help="Restore previous colorscheme.", action='store_true' + ) + group.add_argument( + "-w", help="Use last used wallpaper for color generation.", + action='store_true' + ) + group.add_argument( + "--image", type=str, help="Image for color scheme generation" ) - group.add_argument("--image", type=str, help="Image for color scheme generation") group.add_argument( "--color", type=str, help="Main color for color scheme generation" ) parser.add_argument( - "--color-scheme", - type=str, - default="dark", - choices=["dark", "light"], - help="Color scheme variant", + '--color-scheme', type=str, default='dark', choices=['dark', 'light'], + help="Color scheme variant" ) parser.add_argument( - "--scheme", - type=str, - default="", - choices=list(schemes.keys()), - help="Scheme for color generation", + '--scheme', type=str, default="", + choices=list(schemes.keys()), help="Scheme for color generation" ) args = parser.parse_args() @@ -387,23 +410,15 @@ def _argparse() -> tuple[bool, bool, str, str, str, str]: use_color = args.color scheme = args.scheme return ( - restore_palette, - use_last_used_wallpaper, - image, - color_scheme, - use_color, - scheme, + restore_palette, use_last_used_wallpaper, image, + color_scheme, use_color, scheme ) def _main(): ( - restore_palette, - use_last_used_wallpaper, - image, - color_scheme, - use_color, - scheme, + restore_palette, use_last_used_wallpaper, image, + color_scheme, use_color, scheme ) = _argparse() if not scheme: scheme = None @@ -412,17 +427,13 @@ def _main(): with open(join(cache_path, "colors.json")) as f: content = get_cache_object(f.read()) main( - color_scheme, - image_path=content.wallpaper, - use_color=int(use_color.lstrip("#"), 16), - scheme_name=scheme, + color_scheme, image_path=content.wallpaper, + use_color=int(use_color.lstrip('#'), 16), scheme_name=scheme ) except Exception: main( - color_scheme, - image_path="/dev/null", - use_color=int(use_color.lstrip("#"), 16), - scheme_name=scheme, + color_scheme, image_path="/dev/null", + use_color=int(use_color.lstrip('#'), 16), scheme_name=scheme ) elif restore_palette: @@ -433,7 +444,7 @@ def _main(): image_path=content.wallpaper, use_color=content.original_color, scheme_name=scheme or content.scheme_name, - contrast_level=content.contrast_level, + contrast_level=content.contrast_level ) elif use_last_used_wallpaper: with open(join(cache_path, "colors.json")) as f: @@ -442,12 +453,13 @@ def _main(): color_scheme, image_path=content.wallpaper, scheme_name=scheme or content.scheme_name, - contrast_level=content.contrast_level, + contrast_level=content.contrast_level ) else: if os.path.isdir(image): files = [ - f for f in os.listdir(image) if f.endswith((".png", ".jpg", ".jpeg")) + f for f in os.listdir(image) + if f.endswith(('.png', '.jpg', '.jpeg')) ] if files: image = join(image, random.choice(files)) diff --git a/config/dark-theme.sh b/config/scripts/dark-theme.sh similarity index 92% rename from config/dark-theme.sh rename to config/scripts/dark-theme.sh index fcb5461..a43afcf 100755 --- a/config/dark-theme.sh +++ b/config/scripts/dark-theme.sh @@ -14,8 +14,8 @@ get_relative_path() { } # Assign relative paths to variables -GENERATOR=$(get_relative_path "./material-colors/generate.py") -SETTINGS_FILE=$(get_relative_path "./assets/settings.json") +GENERATOR=$(get_relative_path "../material-colors/generate.py") +SETTINGS_FILE=$(get_relative_path "../assets/settings.json") # Function to read a value from the JSON file get_json_value() { diff --git a/config/wallpaper.py b/config/scripts/wallpaper.py similarity index 99% rename from config/wallpaper.py rename to config/scripts/wallpaper.py index 1703b5a..02c8403 100644 --- a/config/wallpaper.py +++ b/config/scripts/wallpaper.py @@ -22,7 +22,7 @@ def get_relative_path(path: str, level: int = 1) -> str: lock_file_path = "/tmp/wallpaper.lock" -settings_file_path = get_relative_path("../config/assets/settings.json") +settings_file_path = get_relative_path("../assets/settings.json") def acquire_lock(): diff --git a/config/wayland-idle-inhibitor.py b/config/scripts/wayland-idle-inhibitor.py similarity index 100% rename from config/wayland-idle-inhibitor.py rename to config/scripts/wayland-idle-inhibitor.py diff --git a/modules/launcher/components/wallpapers.py b/modules/launcher/components/wallpapers.py index bf05e60..869a6c7 100644 --- a/modules/launcher/components/wallpapers.py +++ b/modules/launcher/components/wallpapers.py @@ -156,7 +156,7 @@ class WallpaperSelector(Box): new_mode = "dark" if not current_state else "light" # Call the shell script with the --set argument - command = f"bash {get_relative_path('../../../config/dark-theme.sh')} --set {new_mode}" + command = f"bash {get_relative_path('../../../config/scripts/dark-theme.sh')} --set {new_mode}" GLib.spawn_command_line_async(command) # Set the appropriate icon based on the mode