backend/models: refactor Preset model object

This commit is the first in a series of commits that will introduce new CLI interface for Gradience. Currently we need to move some parts of code that reside mostly in frontend modules to their new backend modules, and this commit is the beggining of this whole ordeal.

* Refactor Preset model object, in order to make it more readable and to allow `new` function that directly takes new values for properties
* Move presets_dir from models/preset to new backend/globals module
* Create new utils/colors module (will be used in later commits)
* Update all modules that are affected by above changes
This commit is contained in:
tfuxu 2022-12-08 12:43:12 +01:00
parent afe840759f
commit e834e55b11
No known key found for this signature in database
GPG key ID: 79CFC3B9B31C098A
10 changed files with 201 additions and 62 deletions

View file

@ -0,0 +1,25 @@
# globals.py
#
# Change the look of Adwaita, with ease
# Copyright (C) 2022 Gradience Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
presets_dir = os.path.join(
os.environ.get("XDG_CONFIG_HOME", os.environ["HOME"] + "/.config"),
"presets",
)

View file

@ -26,6 +26,7 @@ gradience_sources = [
'__init__.py',
'css_parser.py',
'flatpak_overrides.py',
'globals.py',
'logger.py',
'preset_downloader.py'
]

View file

@ -21,21 +21,84 @@ import os
from gradience.frontend.settings_schema import settings_schema
from gradience.backend.utils.common import to_slug_case
from gradience.backend.globals import presets_dir
from gradience.backend.logger import Logger
logging = Logger()
presets_dir = os.path.join(
os.environ.get("XDG_CONFIG_HOME", os.environ["HOME"] + "/.config"),
"presets",
)
# Adwaita default colors palette
adw_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",
}
}
class Preset:
variables = {}
palette = {}
palette = adw_palette
custom_css = {
"gtk4": "",
"gtk3": "",
@ -45,31 +108,58 @@ class Preset:
preset_path = "new_preset"
badges = {}
def __init__(self, preset_path=None, text=None, preset=None):
if preset_path:
self.load_preset(preset_path=preset_path)
elif text: # load from resource
self.load_preset(text=text)
elif preset: # css or dict
self.load_preset(preset=preset)
else:
raise Exception("Failed to create a new Preset object: Preset created without content")
def __init__(self):
pass
def new(self, display_name: str, variables: dict, palette=None, custom_css=None, badges=None):
self.display_name = display_name
self.variables = variables
if palette:
self.palette = palette
if custom_css:
self.custom_css = custom_css
if badges:
self.badges = badges
def new_from_path(self, preset_path: str):
self.preset_path = preset_path
def load_preset(self, preset_path=None, text=None, preset=None):
try:
if not preset:
if text:
preset_text = text
elif preset_path:
self.preset_path = preset_path
with open(self.preset_path, "r", encoding="utf-8") as file:
preset_text = file.read()
file.close()
else:
raise Exception("load_preset must be called with a path, text, or preset")
with open(self.preset_path, "r", encoding="utf-8") as file:
preset_text = file.read()
file.close()
except OSError as e:
logging.error(f"Failed to read contents of a preset in location: {self.preset_path}. Exc: {e}")
preset = json.loads(preset_text)
try:
preset = json.loads(preset_text)
except json.JSONDecodeError as e:
logging.error(f"Error while decoding JSON data. Exc: {e}")
self.__load_values(preset)
return self
def new_from_resource(self, text: str):
preset_text = text
try:
preset = json.loads(preset_text)
except json.JSONDecodeError as e:
logging.error(f"Error while decoding JSON data. Exc: {e}")
self.__load_values(preset)
return self
def new_from_dict(self, preset: dict):
self.__load_values(preset)
return self
def __load_values(self, preset):
try:
self.display_name = preset["name"]
self.variables = preset["variables"]
self.palette = preset["palette"]
@ -85,24 +175,21 @@ class Preset:
for app_type in settings_schema["custom_css_app_types"]:
self.custom_css[app_type] = ""
except Exception as e:
if self.preset_path:
logging.error(f"Failed to load preset {self.preset_path}. Exc: {e}")
else:
logging.error(f"Failed to load preset with unknown path. Exc: {e}")
logging.error(f"Failed to create a new preset object. Exc: {e}")
# Rename an existing preset
def rename_preset(self, name):
def rename(self, name):
self.display_name = name
old_path = self.preset_path
self.preset_path = os.path.join(
os.path.dirname(self.preset_path),
to_slug_case(name) + ".json")
self.save_preset(to=self.preset_path)
self.save_to_file(to=self.preset_path)
os.remove(old_path)
# Save a new user preset (or overwrite one)
def save_preset(self, name=None, plugins_list=None, to=None):
def save_to_file(self, name=None, plugins_list=None, to=None):
self.display_name = name if name else self.display_name
if to is None:
@ -141,5 +228,6 @@ class Preset:
file.write(json.dumps(object_to_write, indent=4))
file.close()
# TODO: Add validation
def validate(self):
return True

View file

@ -19,7 +19,8 @@
import os
from gradience.backend.utils.common import to_slug_case
from gradience.backend.models.preset import Preset, presets_dir
from gradience.backend.globals import presets_dir
from gradience.backend.models.preset import Preset
class Repo:
@ -34,5 +35,6 @@ class Repo:
presets = {}
for preset in os.listdir(self.path):
if preset.endswith(".json"):
presets[preset[:-5]] = Preset(os.path.join(self.path, preset))
preset_path = os.path.join(self.path, preset)
presets[preset[:-5]] = Preset().new_from_path(preset_path)
return presets

View file

@ -21,7 +21,7 @@ import json
from gi.repository import GLib, Soup
from gradience.backend.models.preset import presets_dir
from gradience.backend.globals import presets_dir
from gradience.backend.utils.common import to_slug_case
from gradience.backend.logger import Logger

View file

@ -0,0 +1,31 @@
# colors.py
#
# Change the look of Adwaita, with ease
# Copyright (C) 2022 Gradience Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import material_color_utilities_python as monet
def rgba_from_argb(argb, alpha=None) -> str:
base = "rgba({}, {}, {}, {})"
red = monet.redFromArgb(argb)
green = monet.greenFromArgb(argb)
blue = monet.blueFromArgb(argb)
if not alpha:
alpha = monet.alphaFromArgb(argb)
return base.format(red, green, blue, alpha)

View file

@ -2,6 +2,7 @@ utilsdir = 'gradience/backend/utils'
gradience_sources = [
'__init__.py',
'colors.py',
'common.py'
]
PY_INSTALLDIR.install_sources(gradience_sources, subdir: utilsdir)

View file

@ -25,8 +25,10 @@ from pathlib import Path
from material_color_utilities_python import *
from gi.repository import Gtk, Gdk, Gio, Adw, GLib, Xdp, XdpGtk4
from gradience.backend.globals import presets_dir
from gradience.backend.css_parser import parse_css
from gradience.backend.models.preset import Preset, presets_dir
from gradience.backend.models.preset import Preset
from gradience.backend.utils.colors import rgba_from_argb
from gradience.backend.utils.common import to_slug_case
from gradience.backend.constants import *
@ -288,7 +290,7 @@ class GradienceApplication(Adw.Application):
"palette": palette,
"custom_css": {"gtk4": custom_css},
}
self.preset = Preset(preset=preset)
self.preset = Preset().new_from_dict(preset)
self.load_preset_variables_from_preset()
except OSError: # fallback to adwaita
logging.warning("Custom preset not found. Fallback to Adwaita")
@ -320,13 +322,13 @@ class GradienceApplication(Adw.Application):
def load_preset_from_file(self, preset_path):
logging.debug(f"load preset from file {preset_path}")
self.preset = Preset(preset_path=preset_path)
self.preset = Preset().new_from_path(preset_path)
self.load_preset_variables_from_preset()
def load_preset_from_resource(self, preset_path):
preset_text = Gio.resources_lookup_data(
preset_path, 0).get_data().decode()
self.preset = Preset(text=preset_text)
self.preset = Preset().new_from_resource(text=preset_text)
self.load_preset_variables_from_preset()
def load_preset_variables_from_preset(self, preset=None):
@ -374,29 +376,17 @@ class GradienceApplication(Adw.Application):
self.reload_variables()
@staticmethod
def rgba_from_argb(argb, alpha=None) -> str:
base = "rgba({}, {}, {}, {})"
red = redFromArgb(argb)
green = greenFromArgb(argb)
blue = blueFromArgb(argb)
if not alpha:
alpha = alphaFromArgb(argb)
return base.format(red, green, blue, alpha)
def update_theme_from_monet(self, theme, tone, monet_theme):
palettes = theme["palettes"]
def update_theme_from_monet(self, monet, tone, monet_theme):
palettes = monet["palettes"]
monet_theme = monet_theme.get_string().lower() # dark / light
palette = {}
i = 0
for color in palettes.values():
i += 1
for i, color in zip(range(1, 7), palettes.values()):
palette[str(i)] = hexFromArgb(color.tone(int(tone.get_string())))
self.pref_palette_shades["monet"].update_shades(palette)
if monet_theme == "auto":
if self.style_manager.get_dark():
monet_theme = "dark"
@ -506,6 +496,7 @@ class GradienceApplication(Adw.Application):
self.reload_variables()
# TODO: Move to backend/utils modules
def generate_gtk_css(self, app_type):
final_css = ""
for key in self.variables.keys():
@ -782,7 +773,7 @@ class GradienceApplication(Adw.Application):
def save_preset(self, _unused, response, preset_entry):
if response == "save":
self.preset.save_preset(preset_entry.get_text(), self.plugins_list)
self.preset.save_to_file(preset_entry.get_text(), self.plugins_list)
self.clear_dirty()
self.win.toast_overlay.add_toast(
Adw.Toast(title=_("Preset saved")))

View file

@ -25,7 +25,7 @@ from pathlib import Path
from gi.repository import Gtk, Adw, GLib
from gradience.backend.preset_downloader import fetch_presets
from gradience.backend.models.preset import presets_dir
from gradience.backend.globals import presets_dir
from gradience.backend.constants import rootdir
from gradience.frontend.widgets.preset_row import GradiencePresetRow

View file

@ -22,7 +22,7 @@ from gi.repository import Gtk, Adw, Xdp, XdpGtk4
from gradience.frontend.views.share_window import GradienceShareWindow
from gradience.backend.utils.common import to_slug_case
from gradience.backend.models.preset import Preset, presets_dir
from gradience.backend.models.preset import Preset
from gradience.backend.constants import rootdir
from gradience.backend.logger import Logger
@ -61,7 +61,7 @@ class GradiencePresetRow(Adw.ExpanderRow):
self.win = win
self.toast_overlay = self.win.toast_overlay
self.preset = Preset(preset_path)
self.preset = Preset().new_from_path(preset_path)
if self.preset.badges:
self.has_badges = True
@ -124,7 +124,7 @@ class GradiencePresetRow(Adw.ExpanderRow):
if self.name_entry_toggle.get_active():
self.value_stack.set_visible_child(self.name_entry)
else:
self.preset.rename_preset(self.name_entry.get_text())
self.preset.rename(self.name_entry.get_text())
self.value_stack.set_visible_child(self.apply_button)
def on_report_btn_clicked(self, *_args):