From f5a0e0715e943296ae5b65ee0640e9b7491cf6e5 Mon Sep 17 00:00:00 2001 From: ArtyIF Date: Thu, 21 Jul 2022 22:03:18 +0300 Subject: [PATCH] Added support for changing palette colors It does surprisingly less than expected. If you can't load your old presets, add the "palette" section from src/presets/adwaita.json to your preset file --- README.md | 9 +++- meson.build | 2 +- src/adwcustomizer.gresource.xml | 3 +- src/{parsing_error.py => error.py} | 8 +-- src/main.py | 65 ++++++++++++++++------- src/meson.build | 6 ++- src/palette_shades.py | 68 +++++++++++++++++++++++++ src/presets/adwaita-dark.json | 65 +++++++++++++++++++++++ src/presets/adwaita.json | 65 +++++++++++++++++++++++ src/settings_schema.json | 47 +++++++++++++++++ src/ui/{parsing_error.blp => error.blp} | 2 +- src/ui/palette_shades.blp | 6 +++ src/ui/window.blp | 1 - src/window.py | 10 ++-- 14 files changed, 322 insertions(+), 35 deletions(-) rename src/{parsing_error.py => error.py} (92%) create mode 100644 src/palette_shades.py rename src/ui/{parsing_error.blp => error.blp} (92%) create mode 100644 src/ui/palette_shades.blp diff --git a/README.md b/README.md index c0a2b91d..4e843d1e 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,15 @@ This tool is currently WIP, but it already has a plenty of features and is very - [x] Load and create custom presets - [x] View adw-gtk3's support of variables - [x] View parsing errors -- [ ] Customize palette colors +- [x] Customize palette colors - [ ] Add custom CSS code - [ ] Normalize color variables to hexadecimal or `rgba(r, g, b, a)` format -- [ ] Add support for GNOME Shell customization +- [ ] Secure the code +- [ ] Release on Flathub - [ ] Full theme preview +- [ ] Customize GNOME Shell +- [ ] Customize GDM +- [ ] Customize Firefox GNOME theme - [ ] Localization ## Donations @@ -66,5 +70,6 @@ I am broke, and I live in Russia (I don't support the "military operation" curre - Bitcoin: `1FrvFbfqWEZepmuH2Bayo8tSvHEr6NnANJ` - Ethereum/ERC20: `0x37e03C589F28831bD78C4B41Bf866e8ddB2bed3E` - Binance Coin/BEP2: `bnb1aewh72p3u80qd8ufywhseuvjupy9mhxtrmwnm4` (no memo required, type anything in there if it's required by the exchange) +- Solana: `Brs84xPGUjDJhCi7GH4EHRQi4dXvWFC6kYy9jY8oQpar` If you're in Russia, then send me an email to artyomisflash@mail.ru, and I'll send you my details (Tinkoff card number or SberBank/SBP/Qiwi/YooMoney/Megafon phone number) diff --git a/meson.build b/meson.build index 319f9e0b..e8fc8676 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('adwcustomizer', - version: '0.0.20', + version: '0.0.21', meson_version: '>= 0.59.0', default_options: [ 'warning_level=2', 'werror=false', diff --git a/src/adwcustomizer.gresource.xml b/src/adwcustomizer.gresource.xml index 3f174bef..4f130b09 100644 --- a/src/adwcustomizer.gresource.xml +++ b/src/adwcustomizer.gresource.xml @@ -4,7 +4,8 @@ settings_schema.json presets/adwaita.json presets/adwaita-dark.json - ui/parsing_error.ui + ui/error.ui + ui/palette_shades.ui ui/option.ui ui/window.ui diff --git a/src/parsing_error.py b/src/error.py similarity index 92% rename from src/parsing_error.py rename to src/error.py index 58d00d09..684c6407 100644 --- a/src/parsing_error.py +++ b/src/error.py @@ -1,4 +1,4 @@ -# parsing_error.py +# error.py # # Copyright 2022 Adwaita Manager Team # @@ -29,9 +29,9 @@ from gi.repository import Gtk, Gdk, Gio, Adw import json -@Gtk.Template(resource_path='/com/github/ArtyIF/AdwCustomizer/ui/parsing_error.ui') -class AdwcustomizerParsingError(Gtk.ListBoxRow): - __gtype_name__ = 'AdwcustomizerParsingError' +@Gtk.Template(resource_path='/com/github/ArtyIF/AdwCustomizer/ui/error.ui') +class AdwcustomizerError(Gtk.ListBoxRow): + __gtype_name__ = 'AdwcustomizerError' error_label = Gtk.Template.Child("error-label") element_label = Gtk.Template.Child("element-label") diff --git a/src/main.py b/src/main.py index 79cdc2eb..e30e1c40 100644 --- a/src/main.py +++ b/src/main.py @@ -31,13 +31,14 @@ import gi import json import os import re -import subprocess +import traceback gi.require_version('Gtk', '4.0') gi.require_version('Adw', '1') from gi.repository import Gtk, Gdk, Gio, Adw, GLib from .window import AdwcustomizerMainWindow +from .palette_shades import AdwcustomizerPaletteShades from .option import AdwcustomizerOption def to_slug_case(non_slug): @@ -58,6 +59,8 @@ class AdwcustomizerApplication(Adw.Application): necessary. """ self.variables = {} + self.palette = {} + self.global_errors = [] self.is_ready = False win = self.props.active_window @@ -75,12 +78,28 @@ class AdwcustomizerApplication(Adw.Application): pref_group.set_description(group["description"]) for variable in group["variables"]: - pref_variable = AdwcustomizerOption(variable["name"], variable["title"], variable["adw_gtk3_support"], variable.get("explanation")) + pref_variable = AdwcustomizerOption(variable["name"], + variable["title"], + variable["adw_gtk3_support"], + variable.get("explanation")) pref_group.add(pref_variable) self.pref_variables[variable["name"]] = pref_variable win.content.add(pref_group) + self.pref_palette_shades = {} + palette_pref_group = Adw.PreferencesGroup() + palette_pref_group.set_name("palette_colors") + palette_pref_group.set_title("Palette Colors") + palette_pref_group.set_description("Named palette colors used by some applications. Default colors follow the GNOME Human Interface Guidelines.") + for color in self.settings_schema["palette"]: + palette_shades = AdwcustomizerPaletteShades(color["prefix"], + color["title"], + color["n_shades"]) + palette_pref_group.add(palette_shades) + self.pref_palette_shades[color["prefix"]] = palette_shades + win.content.add(palette_pref_group) + # usually flatpak takes care of that, but in case it doesn't we do it ourselves preset_directory = os.path.join(os.environ['XDG_CONFIG_HOME'], "adwcustomizer", "presets") if not os.path.exists(preset_directory): @@ -101,10 +120,18 @@ class AdwcustomizerApplication(Adw.Application): with open(os.environ['XDG_CONFIG_HOME'] + "/adwcustomizer/presets/" + file, 'r') as f: preset_text = f.read() preset = json.loads(preset_text) + if preset.get('variables') is None: + raise KeyError('variables') + if preset.get('palette') is None: + raise KeyError('palette') self.custom_presets[file.replace('.json', '')] = preset['name'] except Exception as e: - print("Failed to load " + file + ":\n" + str(e)) - self.custom_presets["error-" + file.replace('.json', '')] = file + " (Failed to Load)" + self.global_errors.append({ + "error": "Failed to load preset: " + file, + "element": str(e), + "line": traceback.format_exception(e)[2].strip() + }) + self.props.active_window.update_errors(self.global_errors) custom_menu_section = Gio.Menu() for preset in self.custom_presets.keys(): @@ -124,24 +151,22 @@ class AdwcustomizerApplication(Adw.Application): preset_text = "" with open(preset_path, 'r') as f: preset_text = f.read() - preset = json.loads(preset_text) - self.props.active_window.set_current_preset_name(preset["name"]) - - self.variables = preset["variables"] - self.load_preset_variables() + self.load_preset_variables(json.loads(preset_text)) def load_preset_from_resource(self, preset_path): preset_text = Gio.resources_lookup_data(preset_path, 0).get_data().decode() - preset = json.loads(preset_text) + self.load_preset_variables(json.loads(preset_text)) + + def load_preset_variables(self, preset): self.props.active_window.set_current_preset_name(preset["name"]) - self.variables = preset["variables"] - self.load_preset_variables() - - def load_preset_variables(self): + self.palette = preset["palette"] for key in self.variables.keys(): if key in self.pref_variables: self.pref_variables[key].update_value(self.variables[key]) + for key in self.palette.keys(): + if key in self.pref_palette_shades: + self.pref_palette_shades[key].update_shades(self.palette[key]) self.reload_variables() @@ -149,12 +174,15 @@ class AdwcustomizerApplication(Adw.Application): final_css = "" for key in self.variables.keys(): final_css += "@define-color {0} {1};\n".format(key, self.variables[key]) + for prefix_key in self.palette.keys(): + for key in self.palette[prefix_key].keys(): + final_css += "@define-color {0} {1};\n".format(prefix_key + key, self.palette[prefix_key][key]) return final_css def reload_variables(self): parsing_errors = [] css_provider = Gtk.CssProvider() - def on_parsing_error(provider, section, error): + def on_error(provider, section, error): start_location = section.get_start_location().chars end_location = section.get_end_location().chars line_number = section.get_end_location().lines @@ -163,9 +191,9 @@ class AdwcustomizerApplication(Adw.Application): "element": self.generate_css()[start_location:end_location], "line": self.generate_css().splitlines()[line_number] }) - css_provider.connect("parsing-error", on_parsing_error) + css_provider.connect("parsing-error", on_error) css_provider.load_from_data(self.generate_css().encode()) - self.props.active_window.update_parsing_errors(parsing_errors) + self.props.active_window.update_errors(self.global_errors + parsing_errors) # loading with the priority above user to override the applied config Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER + 1) @@ -237,7 +265,8 @@ class AdwcustomizerApplication(Adw.Application): with open(os.environ['XDG_CONFIG_HOME'] + "/adwcustomizer/presets/" + to_slug_case(entry.get_text()) + ".json", 'w') as f: object_to_write = { "name": entry.get_text(), - "variables": self.variables + "variables": self.variables, + "palette": self.palette } f.write(json.dumps(object_to_write, indent=4)) diff --git a/src/meson.build b/src/meson.build index aa7216e3..da708554 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,7 +4,8 @@ gnome = import('gnome') blueprints = custom_target('blueprints', input: files( - 'ui/parsing_error.blp', + 'ui/error.blp', + 'ui/palette_shades.blp', 'ui/option.blp', 'ui/window.blp', ), @@ -39,7 +40,8 @@ configure_file( adwcustomizer_sources = [ '__init__.py', 'main.py', - 'parsing_error.py', + 'error.py', + 'palette_shades.py', 'option.py', 'window.py', ] diff --git a/src/palette_shades.py b/src/palette_shades.py new file mode 100644 index 00000000..5f9f71a9 --- /dev/null +++ b/src/palette_shades.py @@ -0,0 +1,68 @@ +# palette_shades.py +# +# Copyright 2022 Adwaita Manager Team +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name(s) of the above copyright +# holders shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written +# authorization. + +from gi.repository import Gtk, Gdk, Gio, Adw +import json + +@Gtk.Template(resource_path='/com/github/ArtyIF/AdwCustomizer/ui/palette_shades.ui') +class AdwcustomizerPaletteShades(Adw.ActionRow): + __gtype_name__ = 'AdwcustomizerPaletteShades' + + def __init__(self, prefix, color_title, n_shades, **kwargs): + super().__init__(**kwargs) + + self.prefix = prefix + self.set_name(prefix + "shades") + self.set_title(color_title) + + self.color_pickers = {} + for i in range(1, n_shades + 1): + picker = Gtk.ColorButton() + picker.set_name(prefix + str(i)) + picker.set_rgba(Gdk.RGBA(red=0, green=0, blue=0, alpha=0)) + picker.set_valign(Gtk.Align.CENTER) + picker.connect("color-set", self.on_color_changed) + self.color_pickers[str(i)] = picker + self.add_suffix(picker) + + def on_color_changed(self, *args): + shades = {} + for picker_key in self.color_pickers.keys(): + shades[picker_key] = self.color_pickers[picker_key].get_rgba().to_string() + self.update_shades(shades, update_from="color_value") + + def update_shades(self, shades, **kwargs): + for i in range(1, len(shades) + 1): + new_rgba = Gdk.RGBA() + if new_rgba.parse(shades[str(i)]): + self.color_pickers[str(i)].set_rgba(new_rgba) + if Gtk.Application.get_default().is_ready and kwargs.get("update_from") == "color_value": + Gtk.Application.get_default().palette[self.prefix][str(i)] = shades[str(i)] + + if Gtk.Application.get_default().is_ready and kwargs.get("update_from") == "color_value": + Gtk.Application.get_default().reload_variables() diff --git a/src/presets/adwaita-dark.json b/src/presets/adwaita-dark.json index e9ef416c..a459d2d8 100644 --- a/src/presets/adwaita-dark.json +++ b/src/presets/adwaita-dark.json @@ -45,5 +45,70 @@ "shade_color": "#383838", "scrollbar_outline_color": "rgba(0, 0, 0, 0.5)" + }, + "palette": { + "blue_": { + "1": "#99c1f1", + "2": "#62a0ea", + "3": "#3584e4", + "4": "#1c71d8", + "5": "#1a5fb4" + }, + "green_": { + "1": "#8ff0a4", + "2": "#57e389", + "3": "#33d17a", + "4": "#2ec27e", + "5": "#26a269" + }, + "yellow_": { + "1": "#f9f06b", + "2": "#f8e45c", + "3": "#f6d32d", + "4": "#f5c211", + "5": "#e5a50a" + }, + "orange_": { + "1": "#ffbe6f", + "2": "#ffa348", + "3": "#ff7800", + "4": "#e66100", + "5": "#c64600" + }, + "red_": { + "1": "#f66151", + "2": "#ed333b", + "3": "#e01b24", + "4": "#c01c28", + "5": "#a51d2d" + }, + "purple_": { + "1": "#dc8add", + "2": "#c061cb", + "3": "#9141ac", + "4": "#813d9c", + "5": "#613583" + }, + "brown_": { + "1": "#cdab8f", + "2": "#b5835a", + "3": "#986a44", + "4": "#865e3c", + "5": "#63452c" + }, + "light_": { + "1": "#ffffff", + "2": "#f6f5f4", + "3": "#deddda", + "4": "#c0bfbc", + "5": "#9a9996" + }, + "dark_": { + "1": "#77767b", + "2": "#5e5c64", + "3": "#3d3846", + "4": "#241f31", + "5": "#000000" + } } } \ No newline at end of file diff --git a/src/presets/adwaita.json b/src/presets/adwaita.json index b6903bbf..eb1bc398 100644 --- a/src/presets/adwaita.json +++ b/src/presets/adwaita.json @@ -45,5 +45,70 @@ "shade_color": "#ffffff", "scrollbar_outline_color": "#ffffff" + }, + "palette": { + "blue_": { + "1": "#99c1f1", + "2": "#62a0ea", + "3": "#3584e4", + "4": "#1c71d8", + "5": "#1a5fb4" + }, + "green_": { + "1": "#8ff0a4", + "2": "#57e389", + "3": "#33d17a", + "4": "#2ec27e", + "5": "#26a269" + }, + "yellow_": { + "1": "#f9f06b", + "2": "#f8e45c", + "3": "#f6d32d", + "4": "#f5c211", + "5": "#e5a50a" + }, + "orange_": { + "1": "#ffbe6f", + "2": "#ffa348", + "3": "#ff7800", + "4": "#e66100", + "5": "#c64600" + }, + "red_": { + "1": "#f66151", + "2": "#ed333b", + "3": "#e01b24", + "4": "#c01c28", + "5": "#a51d2d" + }, + "purple_": { + "1": "#dc8add", + "2": "#c061cb", + "3": "#9141ac", + "4": "#813d9c", + "5": "#613583" + }, + "brown_": { + "1": "#cdab8f", + "2": "#b5835a", + "3": "#986a44", + "4": "#865e3c", + "5": "#63452c" + }, + "light_": { + "1": "#ffffff", + "2": "#f6f5f4", + "3": "#deddda", + "4": "#c0bfbc", + "5": "#9a9996" + }, + "dark_": { + "1": "#77767b", + "2": "#5e5c64", + "3": "#3d3846", + "4": "#241f31", + "5": "#000000" + } } } \ No newline at end of file diff --git a/src/settings_schema.json b/src/settings_schema.json index 4600f818..f4317828 100644 --- a/src/settings_schema.json +++ b/src/settings_schema.json @@ -260,5 +260,52 @@ } ] } + ], + "palette": [ + { + "prefix": "blue_", + "title": "Blue", + "n_shades": 5 + }, + { + "prefix": "green_", + "title": "Green", + "n_shades": 5 + }, + { + "prefix": "yellow_", + "title": "Yellow", + "n_shades": 5 + }, + { + "prefix": "orange_", + "title": "Orange", + "n_shades": 5 + }, + { + "prefix": "red_", + "title": "Red", + "n_shades": 5 + }, + { + "prefix": "purple_", + "title": "Purple", + "n_shades": 5 + }, + { + "prefix": "brown_", + "title": "Brown", + "n_shades": 5 + }, + { + "prefix": "light_", + "title": "Light", + "n_shades": 5 + }, + { + "prefix": "dark_", + "title": "Dark", + "n_shades": 5 + } ] } \ No newline at end of file diff --git a/src/ui/parsing_error.blp b/src/ui/error.blp similarity index 92% rename from src/ui/parsing_error.blp rename to src/ui/error.blp index f36ed757..9ac1a6f6 100644 --- a/src/ui/parsing_error.blp +++ b/src/ui/error.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template AdwcustomizerParsingError : ListBoxRow { +template AdwcustomizerError : ListBoxRow { Box { orientation: vertical; margin-top: 6; diff --git a/src/ui/palette_shades.blp b/src/ui/palette_shades.blp new file mode 100644 index 00000000..a03600a7 --- /dev/null +++ b/src/ui/palette_shades.blp @@ -0,0 +1,6 @@ +using Gtk 4.0; +using Adw 1; + +template AdwcustomizerPaletteShades : Adw.ActionRow { + title: "Option Title"; +} \ No newline at end of file diff --git a/src/ui/window.blp b/src/ui/window.blp index 608dd5ac..2b0fdb04 100644 --- a/src/ui/window.blp +++ b/src/ui/window.blp @@ -2,7 +2,6 @@ using Gtk 4.0; using Adw 1; template AdwcustomizerMainWindow : Gtk.ApplicationWindow { - styles ["devel"] title: "Adwaita Manager"; default-width: 1280; default-height: 720; diff --git a/src/window.py b/src/window.py index 2714401f..f62584cf 100644 --- a/src/window.py +++ b/src/window.py @@ -28,7 +28,7 @@ from gi.repository import Gtk import json -from .parsing_error import AdwcustomizerParsingError +from .error import AdwcustomizerError @Gtk.Template(resource_path='/com/github/ArtyIF/AdwCustomizer/ui/window.ui') class AdwcustomizerMainWindow(Gtk.ApplicationWindow): @@ -46,12 +46,12 @@ class AdwcustomizerMainWindow(Gtk.ApplicationWindow): def set_current_preset_name(self, new_name): self.presets_dropdown.set_label(new_name) - def update_parsing_errors(self, parsing_errors): + def update_errors(self, errors): child = self.errors_list.get_row_at_index(0) while child is not None: self.errors_list.remove(child) child = self.errors_list.get_row_at_index(0) - self.errors_button.set_visible(len(parsing_errors) > 0) - for parsing_error in parsing_errors: - self.errors_list.append(AdwcustomizerParsingError(parsing_error["error"], parsing_error["element"], parsing_error["line"])) + self.errors_button.set_visible(len(errors) > 0) + for error in errors: + self.errors_list.append(AdwcustomizerError(error["error"], error["element"], error["line"]))