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
This commit is contained in:
ArtyIF 2022-07-21 22:03:18 +03:00
parent 8df3e25787
commit f5a0e0715e
14 changed files with 322 additions and 35 deletions

View file

@ -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)

View file

@ -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',

View file

@ -4,7 +4,8 @@
<file>settings_schema.json</file>
<file>presets/adwaita.json</file>
<file>presets/adwaita-dark.json</file>
<file>ui/parsing_error.ui</file>
<file>ui/error.ui</file>
<file>ui/palette_shades.ui</file>
<file>ui/option.ui</file>
<file>ui/window.ui</file>
</gresource>

View file

@ -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")

View file

@ -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 <a href=\"https://developer.gnome.org/hig/reference/palette.html\">GNOME Human Interface Guidelines</a>.")
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))

View file

@ -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',
]

68
src/palette_shades.py Normal file
View file

@ -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()

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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
}
]
}

View file

@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
template AdwcustomizerParsingError : ListBoxRow {
template AdwcustomizerError : ListBoxRow {
Box {
orientation: vertical;
margin-top: 6;

View file

@ -0,0 +1,6 @@
using Gtk 4.0;
using Adw 1;
template AdwcustomizerPaletteShades : Adw.ActionRow {
title: "Option Title";
}

View file

@ -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;

View file

@ -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"]))