Extracted installation methods, created a new class for theme installers
This commit is contained in:
+23
-19
@@ -42,23 +42,8 @@ class GlobalTheme:
|
||||
self.gst = os.path.join(self.destination_folder, self.destination_file) # use backup file if theme is installed
|
||||
|
||||
self.themes: list[ThemePrepare] = []
|
||||
|
||||
try:
|
||||
gnome_version = gnome.gnome_version()
|
||||
gnome_major = gnome_version.split(".")[0]
|
||||
if int(gnome_major) >= 44:
|
||||
self.themes += [
|
||||
self.__create_theme("gnome-shell-light", mode='light', should_label=True, is_filled=is_filled),
|
||||
self.__create_theme("gnome-shell-dark", mode='dark', is_filled=is_filled)
|
||||
]
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("Using single theme.")
|
||||
|
||||
if not self.themes:
|
||||
self.themes.append(
|
||||
self.__create_theme(
|
||||
"gnome-shell", mode=mode if mode else 'dark', is_filled=is_filled))
|
||||
self.is_filled = is_filled
|
||||
self.mode = mode
|
||||
|
||||
|
||||
def __create_theme(self, theme_type, mode=None, should_label=False, is_filled=False):
|
||||
@@ -66,6 +51,7 @@ class GlobalTheme:
|
||||
theme = Theme(theme_type, self.colors_json, self.theme_folder,
|
||||
self.extracted_theme, self.temp_folder,
|
||||
mode=mode, is_filled=is_filled)
|
||||
theme.prepare()
|
||||
theme_file = os.path.join(self.extracted_theme, f"{theme_type}.css")
|
||||
return ThemePrepare(theme=theme, theme_file=theme_file, should_label=should_label)
|
||||
|
||||
@@ -117,7 +103,7 @@ class GlobalTheme:
|
||||
gnome_styles = gnome_theme.read() + self.backup_trigger
|
||||
theme.add_to_start(gnome_styles)
|
||||
|
||||
def __prepare(self, hue, color, sat=None):
|
||||
def __generate_themes(self, hue, color, sat=None):
|
||||
"""
|
||||
Generate theme files for gnome-shell-theme.gresource.xml
|
||||
:param hue: color hue
|
||||
@@ -165,6 +151,24 @@ class GlobalTheme:
|
||||
</gresource>
|
||||
</gresources>"""
|
||||
|
||||
def prepare(self):
|
||||
try:
|
||||
gnome_version = gnome.gnome_version()
|
||||
gnome_major = gnome_version.split(".")[0]
|
||||
if int(gnome_major) >= 44:
|
||||
self.themes += [
|
||||
self.__create_theme("gnome-shell-light", mode='light', should_label=True, is_filled=self.is_filled),
|
||||
self.__create_theme("gnome-shell-dark", mode='dark', is_filled=self.is_filled)
|
||||
]
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("Using single theme.")
|
||||
|
||||
if not self.themes:
|
||||
self.themes.append(
|
||||
self.__create_theme(
|
||||
"gnome-shell", mode=self.mode if self.mode else 'dark', is_filled=self.is_filled))
|
||||
|
||||
def install(self, hue, sat=None):
|
||||
"""
|
||||
Install theme globally
|
||||
@@ -182,7 +186,7 @@ class GlobalTheme:
|
||||
self.__extract()
|
||||
|
||||
# generate theme files for global theme
|
||||
self.__prepare(hue, 'Marble', sat)
|
||||
self.__generate_themes(hue, 'Marble', sat)
|
||||
|
||||
# generate gnome-shell-theme.gresource.xml
|
||||
with open(f"{self.extracted_theme}/{self.destination_file}.xml", 'w') as gresource_xml:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from scripts.install.arguments_definer import ArgumentsDefiner
|
||||
@@ -0,0 +1,73 @@
|
||||
import argparse
|
||||
import textwrap
|
||||
from typing import Any
|
||||
|
||||
from scripts.tweaks_manager import TweaksManager
|
||||
|
||||
|
||||
class ArgumentsDefiner:
|
||||
def __init__(self, colors: dict[str, Any]):
|
||||
self._parser = argparse.ArgumentParser(prog="python install.py",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=self._get_epilog())
|
||||
|
||||
self._define_default_arguments()
|
||||
self._define_color_arguments(colors)
|
||||
self._define_custom_color_arguments()
|
||||
self._define_theme_styles_arguments()
|
||||
self._define_color_tweaks_arguments()
|
||||
self._define_gdm_arguments()
|
||||
self._define_tweaks_arguments()
|
||||
|
||||
def parse(self) -> argparse.Namespace:
|
||||
return self._parser.parse_args()
|
||||
|
||||
@staticmethod
|
||||
def _get_epilog():
|
||||
return textwrap.dedent('''
|
||||
Examples:
|
||||
-a all accent colors, light & dark mode
|
||||
--all --mode dark all accent colors, dark mode
|
||||
--purple --mode=light purple accent color, light mode
|
||||
--hue 150 --name coldgreen custom coldgreen accent color, light & dark mode
|
||||
--red --green --sat=70 red, green accent colors, 70% of stock saturation
|
||||
--hue=200 --name=grayblue --sat=50 --mode=dark
|
||||
custom grayblue accent color, 50% of stock saturation, dark mode
|
||||
''')
|
||||
|
||||
def _define_default_arguments(self):
|
||||
self._parser.add_argument('-r', '--remove', action='store_true', help='remove Marble themes')
|
||||
self._parser.add_argument('-ri', '--reinstall', action='store_true', help='reinstall Marble themes')
|
||||
|
||||
def _define_color_arguments(self, colors: dict[str, Any]):
|
||||
default_args = self._parser.add_argument_group('Install default theme')
|
||||
default_args.add_argument('-a', '--all', action='store_true', help='all available accent colors')
|
||||
|
||||
for color in colors:
|
||||
default_args.add_argument(f'--{color}', action='store_true', help=f'{color} theme only')
|
||||
|
||||
def _define_custom_color_arguments(self):
|
||||
custom_args = self._parser.add_argument_group('Install custom color theme')
|
||||
custom_args.add_argument('--hue', type=int, choices=range(0, 361), help='generate theme from Hue prompt',
|
||||
metavar='(0 - 360)')
|
||||
custom_args.add_argument('--name', nargs='?', help='theme name (optional)')
|
||||
|
||||
def _define_theme_styles_arguments(self):
|
||||
color_styles = self._parser.add_argument_group("Theme color styles")
|
||||
color_styles.add_argument("--filled", action="store_true", help="make accent color more vibrant")
|
||||
|
||||
def _define_color_tweaks_arguments(self):
|
||||
color_tweaks = self._parser.add_argument_group('Optional theme tweaks')
|
||||
color_tweaks.add_argument('--mode', choices=['light', 'dark'], help='select a specific theme mode to install')
|
||||
color_tweaks.add_argument('--sat', type=int, choices=range(0, 251),
|
||||
help='custom color saturation (<100%% - reduce, >100%% - increase)',
|
||||
metavar='(0 - 250)')
|
||||
|
||||
def _define_gdm_arguments(self):
|
||||
gdm_theming = self._parser.add_argument_group('GDM theming')
|
||||
gdm_theming.add_argument('--gdm', action='store_true', help='install GDM theme. \
|
||||
Requires root privileges. You must specify a specific color.')
|
||||
|
||||
def _define_tweaks_arguments(self):
|
||||
tweaks_manager = TweaksManager()
|
||||
tweaks_manager.define_arguments(self._parser)
|
||||
@@ -0,0 +1,17 @@
|
||||
import json
|
||||
|
||||
|
||||
class ColorsDefiner:
|
||||
# TODO: Create a class for each replacer
|
||||
replacers: dict[ str, # ACCENT-COLOR
|
||||
dict[ str, # default, light/dark
|
||||
str | dict[str, int] # random string, s/l/a
|
||||
]
|
||||
]
|
||||
# TODO: Create a class for each color
|
||||
colors: dict[str, dict[str, int]]
|
||||
|
||||
def __init__(self, filename):
|
||||
colors_dict = json.load(open(filename))
|
||||
self.replacers = colors_dict["elements"]
|
||||
self.colors = colors_dict["colors"]
|
||||
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
|
||||
from scripts import config
|
||||
from scripts.gdm import GlobalTheme
|
||||
from scripts.install.theme_installer import ThemeInstaller
|
||||
|
||||
|
||||
class GlobalThemeInstaller(ThemeInstaller):
|
||||
theme: GlobalTheme
|
||||
|
||||
def remove(self):
|
||||
gdm_rm_status = self.theme.remove()
|
||||
if gdm_rm_status == 0:
|
||||
print("GDM theme removed successfully.")
|
||||
|
||||
def _define_theme(self):
|
||||
gdm_temp = os.path.join(config.temp_folder, config.gdm_folder)
|
||||
self.theme = GlobalTheme(self.colors, f"{config.raw_theme_folder}/{config.gnome_folder}",
|
||||
config.global_gnome_shell_theme, config.gnome_shell_gresource,
|
||||
gdm_temp, mode=self.args.mode, is_filled=self.args.filled)
|
||||
|
||||
def _install_theme(self, hue, theme_name, sat):
|
||||
self.theme.prepare()
|
||||
self.theme.install(hue, sat)
|
||||
|
||||
def _apply_tweaks_to_theme(self):
|
||||
for theme in self.theme.themes:
|
||||
self._apply_tweaks(theme.theme)
|
||||
@@ -0,0 +1,31 @@
|
||||
import os.path
|
||||
|
||||
from scripts import config
|
||||
from scripts.install.theme_installer import ThemeInstaller
|
||||
from scripts.theme import Theme
|
||||
from scripts.utils import remove_files
|
||||
|
||||
|
||||
class LocalThemeInstaller(ThemeInstaller):
|
||||
theme: Theme
|
||||
|
||||
def remove(self):
|
||||
args = self.args
|
||||
colors = self.colors.colors
|
||||
if args.remove or args.reinstall:
|
||||
remove_files(args, colors)
|
||||
if not args.reinstall:
|
||||
return
|
||||
|
||||
def _define_theme(self):
|
||||
theme_folder = os.path.join(config.raw_theme_folder, config.gnome_folder)
|
||||
self.theme = Theme("gnome-shell", self.colors, theme_folder,
|
||||
config.themes_folder, config.temp_folder,
|
||||
mode=self.args.mode, is_filled=self.args.filled)
|
||||
|
||||
def _install_theme(self, hue, theme_name, sat):
|
||||
self.theme.prepare()
|
||||
self.theme.install(hue, theme_name, sat)
|
||||
|
||||
def _apply_tweaks_to_theme(self):
|
||||
self._apply_tweaks(self.theme)
|
||||
@@ -0,0 +1,74 @@
|
||||
import argparse
|
||||
|
||||
from scripts.install.colors_definer import ColorsDefiner
|
||||
from scripts.theme import Theme
|
||||
from scripts.tweaks_manager import TweaksManager
|
||||
|
||||
|
||||
class ThemeInstaller:
|
||||
theme: Theme
|
||||
|
||||
def __init__(self, args: argparse.Namespace, colors: ColorsDefiner):
|
||||
self.args = args
|
||||
self.colors = colors
|
||||
self.stop_after_first_installed_color = False
|
||||
self._define_theme()
|
||||
|
||||
def remove(self):
|
||||
pass
|
||||
|
||||
def install(self):
|
||||
self.theme.prepare()
|
||||
self._apply_tweaks_to_theme()
|
||||
self._apply_colors()
|
||||
|
||||
def _define_theme(self):
|
||||
pass
|
||||
|
||||
def _install_theme(self, hue, theme_name, sat):
|
||||
pass
|
||||
|
||||
def _apply_tweaks_to_theme(self):
|
||||
pass
|
||||
|
||||
def _apply_tweaks(self, theme):
|
||||
tweaks_manager = TweaksManager()
|
||||
tweaks_manager.apply_tweaks(self.args, theme, self.colors)
|
||||
|
||||
def _apply_colors(self):
|
||||
installed_any = False
|
||||
|
||||
if self.args.hue:
|
||||
installed_any = True
|
||||
self._apply_custom_color()
|
||||
else:
|
||||
installed_any = self._apply_default_color()
|
||||
|
||||
if not installed_any:
|
||||
raise Exception('No color arguments specified. Use -h or --help to see the available options.')
|
||||
|
||||
def _apply_custom_color(self):
|
||||
name = self.args.name
|
||||
hue = self.args.hue
|
||||
sat = self.args.sat
|
||||
|
||||
theme_name = name if name else f'hue{hue}'
|
||||
self._install_theme(hue, theme_name, sat)
|
||||
|
||||
def _apply_default_color(self) -> bool:
|
||||
colors = self.colors.colors
|
||||
args = self.args
|
||||
installed_any = False
|
||||
|
||||
for color, values in colors.items():
|
||||
if self.args.all or getattr(self.args, color, False):
|
||||
hue = values.get('h')
|
||||
sat = values.get('s', args.sat) # if saturation already defined in color (gray)
|
||||
|
||||
self._install_theme(hue, color, sat)
|
||||
installed_any = True
|
||||
|
||||
if self.stop_after_first_installed_color:
|
||||
break
|
||||
|
||||
return installed_any
|
||||
+31
-28
@@ -2,6 +2,7 @@ import os
|
||||
import shutil
|
||||
import colorsys # colorsys.hls_to_rgb(h, l, s)
|
||||
|
||||
from .install.colors_definer import ColorsDefiner
|
||||
from .utils import (
|
||||
replace_keywords, # replace keywords in file
|
||||
copy_files, # copy files from source to destination
|
||||
@@ -23,31 +24,14 @@ class Theme:
|
||||
:param is_filled: if True, theme will be filled
|
||||
"""
|
||||
|
||||
self.colors = colors_json
|
||||
self.colors: ColorsDefiner = colors_json
|
||||
self.temp_folder = f"{temp_folder}/{theme_type}"
|
||||
self.theme_folder = theme_folder
|
||||
self.theme_type = theme_type
|
||||
self.mode = [mode] if mode else ['light', 'dark']
|
||||
self.destination_folder = destination_folder
|
||||
self.main_styles = f"{self.temp_folder}/{theme_type}.css"
|
||||
|
||||
# move files to temp folder
|
||||
copy_files(self.theme_folder, self.temp_folder)
|
||||
generate_file(f"{self.theme_folder}", self.temp_folder, self.main_styles)
|
||||
# after generating main styles, remove .css and .versions folders
|
||||
shutil.rmtree(f"{self.temp_folder}/.css/", ignore_errors=True)
|
||||
shutil.rmtree(f"{self.temp_folder}/.versions/", ignore_errors=True)
|
||||
|
||||
# if theme is filled
|
||||
if is_filled:
|
||||
for apply_file in os.listdir(f"{self.temp_folder}/"):
|
||||
replace_keywords(f"{self.temp_folder}/{apply_file}",
|
||||
("BUTTON-COLOR", "ACCENT-FILLED-COLOR"),
|
||||
("BUTTON_HOVER", "ACCENT-FILLED_HOVER"),
|
||||
("BUTTON_ACTIVE", "ACCENT-FILLED_ACTIVE"),
|
||||
("BUTTON_INSENSITIVE", "ACCENT-FILLED_INSENSITIVE"),
|
||||
("BUTTON-TEXT-COLOR", "TEXT-BLACK-COLOR"),
|
||||
("BUTTON-TEXT_SECONDARY", "TEXT-BLACK_SECONDARY"))
|
||||
self.is_filled = is_filled
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
@@ -93,18 +77,18 @@ class Theme:
|
||||
|
||||
# colorsys works in range(0, 1)
|
||||
h = hue / 360
|
||||
for element in self.colors["elements"]:
|
||||
for element in self.colors.replacers:
|
||||
# if color has default color and hasn't been replaced
|
||||
if theme_mode not in self.colors["elements"][element] and self.colors["elements"][element]["default"]:
|
||||
default_element = self.colors["elements"][element]["default"]
|
||||
default_color = self.colors["elements"][default_element][theme_mode]
|
||||
self.colors["elements"][element][theme_mode] = default_color
|
||||
if theme_mode not in self.colors.replacers[element] and self.colors.replacers[element]["default"]:
|
||||
default_element = self.colors.replacers[element]["default"]
|
||||
default_color = self.colors.replacers[default_element][theme_mode]
|
||||
self.colors.replacers[element][theme_mode] = default_color
|
||||
|
||||
# convert sla to range(0, 1)
|
||||
lightness = int(self.colors["elements"][element][theme_mode]["l"]) / 100
|
||||
saturation = int(self.colors["elements"][element][theme_mode]["s"]) / 100 if sat is None else \
|
||||
int(self.colors["elements"][element][theme_mode]["s"]) * (sat / 100) / 100
|
||||
alpha = self.colors["elements"][element][theme_mode]["a"]
|
||||
lightness = int(self.colors.replacers[element][theme_mode]["l"]) / 100
|
||||
saturation = int(self.colors.replacers[element][theme_mode]["s"]) / 100 if sat is None else \
|
||||
int(self.colors.replacers[element][theme_mode]["s"]) * (sat / 100) / 100
|
||||
alpha = self.colors.replacers[element][theme_mode]["a"]
|
||||
|
||||
# convert hsl to rgb and multiply every item
|
||||
red, green, blue = [int(item * 255) for item in colorsys.hls_to_rgb(h, lightness, saturation)]
|
||||
@@ -127,6 +111,25 @@ class Theme:
|
||||
for apply_file in os.listdir(f"{source}/"):
|
||||
self.__apply_colors(hue, destination, theme_mode, apply_file, sat=sat)
|
||||
|
||||
def prepare(self):
|
||||
# move files to temp folder
|
||||
copy_files(self.theme_folder, self.temp_folder)
|
||||
generate_file(f"{self.theme_folder}", self.temp_folder, self.main_styles)
|
||||
# after generating main styles, remove .css and .versions folders
|
||||
shutil.rmtree(f"{self.temp_folder}/.css/", ignore_errors=True)
|
||||
shutil.rmtree(f"{self.temp_folder}/.versions/", ignore_errors=True)
|
||||
|
||||
# if theme is filled
|
||||
if self.is_filled:
|
||||
for apply_file in os.listdir(f"{self.temp_folder}/"):
|
||||
replace_keywords(f"{self.temp_folder}/{apply_file}",
|
||||
("BUTTON-COLOR", "ACCENT-FILLED-COLOR"),
|
||||
("BUTTON_HOVER", "ACCENT-FILLED_HOVER"),
|
||||
("BUTTON_ACTIVE", "ACCENT-FILLED_ACTIVE"),
|
||||
("BUTTON_INSENSITIVE", "ACCENT-FILLED_INSENSITIVE"),
|
||||
("BUTTON-TEXT-COLOR", "TEXT-BLACK-COLOR"),
|
||||
("BUTTON-TEXT_SECONDARY", "TEXT-BLACK_SECONDARY"))
|
||||
|
||||
def install(self, hue, name, sat=None, destination=None):
|
||||
"""
|
||||
Copy files and generate theme with different accent color
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
import argparse
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
from typing import Any
|
||||
|
||||
from .. import config
|
||||
import os
|
||||
|
||||
def remove_files(args: argparse.Namespace, colors: dict[str, str]):
|
||||
def remove_files(args: argparse.Namespace, colors: dict[str, Any]):
|
||||
"""Delete already installed Marble theme"""
|
||||
themes = detect_themes(config.themes_folder)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user