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