mirror of
https://github.com/imarkoff/Marble-shell-theme.git
synced 2025-09-25 20:56:39 -07:00
Covered Theme module with tests
- Extracted `ColorReplacementGenerator`; - Extracted `ColorConverterImpl`; - Added documentation to some classes; - `hex_to_rgba` now supports shorthand hex colors (#fff).
This commit is contained in:
44
scripts/utils/theme/color_replacement_generator.py
Normal file
44
scripts/utils/theme/color_replacement_generator.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import copy
|
||||
|
||||
from scripts.install.colors_definer import ColorsDefiner
|
||||
from scripts.types.installation_color import InstallationMode, InstallationColor
|
||||
from scripts.utils.color_converter.color_converter import ColorConverter
|
||||
|
||||
|
||||
class ColorReplacementGenerator:
|
||||
def __init__(self, colors_provider: ColorsDefiner, color_converter: ColorConverter):
|
||||
self.colors = copy.deepcopy(colors_provider)
|
||||
self.color_converter = color_converter
|
||||
|
||||
def convert(self, mode: InstallationMode, theme_color: InstallationColor) -> list[tuple[str, str]]:
|
||||
"""Generate a list of color replacements for the given theme color and mode"""
|
||||
return [
|
||||
(element, self._create_rgba_value(element, mode, theme_color))
|
||||
for element in self.colors.replacers
|
||||
]
|
||||
|
||||
def _create_rgba_value(self, element: str, mode: str, theme_color: InstallationColor) -> str:
|
||||
"""Create RGBA value for the specified element"""
|
||||
color_def = self._get_color_definition(element, mode)
|
||||
|
||||
lightness = int(color_def["l"]) / 100
|
||||
saturation = int(color_def["s"]) / 100
|
||||
if theme_color.saturation is not None:
|
||||
saturation *= theme_color.saturation / 100
|
||||
alpha = color_def["a"]
|
||||
|
||||
red, green, blue = self.color_converter.hsl_to_rgb(
|
||||
theme_color.hue, saturation, lightness
|
||||
)
|
||||
|
||||
return f"rgba({red}, {green}, {blue}, {alpha})"
|
||||
|
||||
def _get_color_definition(self, element: str, mode: str) -> dict:
|
||||
"""Get color definition for element, handling defaults if needed"""
|
||||
replacer = self.colors.replacers[element]
|
||||
|
||||
if mode not in replacer and replacer["default"]:
|
||||
default_element = replacer["default"]
|
||||
return self.colors.replacers[default_element][mode]
|
||||
|
||||
return replacer[mode]
|
@@ -2,11 +2,12 @@ import os.path
|
||||
|
||||
from scripts import config
|
||||
from scripts.install.colors_definer import ColorsDefiner
|
||||
from scripts.utils.color_converter import ColorConverterImpl
|
||||
from scripts.utils.color_converter.color_converter_impl import ColorConverterImpl
|
||||
from scripts.utils.logger.console import Console
|
||||
from scripts.utils.style_manager import StyleManager
|
||||
from scripts.utils.theme.theme import Theme
|
||||
from scripts.utils.theme.theme_color_applier import ColorReplacementGenerator, ThemeColorApplier
|
||||
from scripts.utils.theme.theme_color_applier import ThemeColorApplier
|
||||
from scripts.utils.theme.color_replacement_generator import ColorReplacementGenerator
|
||||
from scripts.utils.theme.theme_installer import ThemeInstaller
|
||||
from scripts.utils.theme.theme_path_provider import ThemePathProvider
|
||||
from scripts.utils.theme.theme_preparation import ThemePreparation
|
||||
|
@@ -1,16 +1,14 @@
|
||||
import copy
|
||||
import os
|
||||
|
||||
from scripts.install.colors_definer import ColorsDefiner
|
||||
from scripts.types.installation_color import InstallationColor, InstallationMode
|
||||
from scripts.utils import replace_keywords
|
||||
from scripts.utils.color_converter import ColorConverter
|
||||
from scripts.utils.theme.color_replacement_generator import ColorReplacementGenerator
|
||||
|
||||
|
||||
class ThemeColorApplier:
|
||||
"""Class to apply theme colors to files in a directory."""
|
||||
|
||||
def __init__(self, color_replacement_generator: "ColorReplacementGenerator"):
|
||||
def __init__(self, color_replacement_generator: ColorReplacementGenerator):
|
||||
self.color_replacement_generator = color_replacement_generator
|
||||
|
||||
def apply(self, theme_color: InstallationColor, destination: str, mode: InstallationMode):
|
||||
@@ -20,42 +18,3 @@ class ThemeColorApplier:
|
||||
for filename in os.listdir(destination):
|
||||
file_path = os.path.join(destination, filename)
|
||||
replace_keywords(file_path, *replacements)
|
||||
|
||||
|
||||
class ColorReplacementGenerator:
|
||||
def __init__(self, colors_provider: ColorsDefiner, color_converter: ColorConverter):
|
||||
self.colors = copy.deepcopy(colors_provider)
|
||||
self.color_converter = color_converter
|
||||
|
||||
def convert(self, mode: InstallationMode, theme_color: InstallationColor) -> list[tuple[str, str]]:
|
||||
"""Generate a list of color replacements for the given theme color and mode"""
|
||||
return [
|
||||
(element, self._create_rgba_value(element, mode, theme_color))
|
||||
for element in self.colors.replacers
|
||||
]
|
||||
|
||||
def _create_rgba_value(self, element: str, mode: str, theme_color: InstallationColor) -> str:
|
||||
"""Create RGBA value for the specified element"""
|
||||
color_def = self._get_color_definition(element, mode)
|
||||
|
||||
lightness = int(color_def["l"]) / 100
|
||||
saturation = int(color_def["s"]) / 100
|
||||
if theme_color.saturation is not None:
|
||||
saturation *= theme_color.saturation / 100
|
||||
alpha = color_def["a"]
|
||||
|
||||
red, green, blue = self.color_converter.hsl_to_rgb(
|
||||
theme_color.hue, saturation, lightness
|
||||
)
|
||||
|
||||
return f"rgba({red}, {green}, {blue}, {alpha})"
|
||||
|
||||
def _get_color_definition(self, element: str, mode: str) -> dict:
|
||||
"""Get color definition for element, handling defaults if needed"""
|
||||
replacer = self.colors.replacers[element]
|
||||
|
||||
if mode not in replacer and replacer["default"]:
|
||||
default_element = replacer["default"]
|
||||
return self.colors.replacers[default_element][mode]
|
||||
|
||||
return replacer[mode]
|
@@ -1,4 +1,20 @@
|
||||
import os
|
||||
|
||||
|
||||
class ThemePathProvider:
|
||||
@staticmethod
|
||||
def get_theme_path(themes_folder: str, path_name: str, theme_mode: str, theme_type: str) -> str:
|
||||
return f"{themes_folder}/Marble-{path_name}-{theme_mode}/{theme_type}/"
|
||||
def get_theme_path(themes_folder: str, color_name: str, theme_mode: str, theme_type: str) -> str:
|
||||
"""
|
||||
Generates the path for the theme based on the provided parameters.
|
||||
:param themes_folder: The base folder where themes are stored.
|
||||
:param color_name: The name of the color scheme.
|
||||
:param theme_mode: The mode of the theme (e.g., 'light' or 'dark').
|
||||
:param theme_type: The type of the theme (e.g., 'gnome-shell', 'gtk').
|
||||
"""
|
||||
if not themes_folder or not color_name or not theme_mode or not theme_type:
|
||||
raise ValueError("All parameters must be non-empty strings.")
|
||||
|
||||
marble_name = '-'.join(["Marble", color_name, theme_mode])
|
||||
final_path = os.path.join(themes_folder, marble_name, theme_type, "")
|
||||
|
||||
return final_path
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from scripts.utils import replace_keywords
|
||||
from scripts.utils.theme.theme_temp_manager import ThemeTempManager
|
||||
@@ -45,11 +44,10 @@ class ThemePreparation:
|
||||
"""
|
||||
Extract theme from source folder and prepare it for installation.
|
||||
"""
|
||||
self.file_manager.prepare_files(self.sources_location)
|
||||
self.file_manager.copy_to_temp(self.sources_location)
|
||||
self.style_manager.generate_combined_styles(self.sources_location, self.temp_folder)
|
||||
self.file_manager.cleanup()
|
||||
|
||||
@warnings.deprecated
|
||||
def replace_filled_keywords(self):
|
||||
"""
|
||||
Replace keywords in the theme files for filled mode.
|
||||
@@ -62,4 +60,4 @@ class ThemePreparation:
|
||||
("BUTTON_ACTIVE", "ACCENT-FILLED_ACTIVE"),
|
||||
("BUTTON_INSENSITIVE", "ACCENT-FILLED_INSENSITIVE"),
|
||||
("BUTTON-TEXT-COLOR", "TEXT-BLACK-COLOR"),
|
||||
("BUTTON-TEXT_SECONDARY", "TEXT-BLACK_SECONDARY"))
|
||||
("BUTTON-TEXT_SECONDARY", "TEXT-BLACK_SECONDARY"))
|
||||
|
@@ -1,8 +1,6 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from scripts.utils import copy_files
|
||||
|
||||
|
||||
class ThemeTempManager:
|
||||
"""
|
||||
@@ -10,18 +8,21 @@ class ThemeTempManager:
|
||||
"""
|
||||
def __init__(self, temp_folder: str):
|
||||
self.temp_folder = temp_folder
|
||||
os.makedirs(self.temp_folder, exist_ok=True)
|
||||
|
||||
def copy_to_temp(self, content: str):
|
||||
"""
|
||||
Copy a file or directory to the temporary folder.
|
||||
If the content is a file, it will be copied directly.
|
||||
If the content is a directory, all its contents will be copied to the temp folder.
|
||||
"""
|
||||
if os.path.isfile(content):
|
||||
shutil.copy(content, self.temp_folder)
|
||||
final_path = os.path.join(self.temp_folder, os.path.basename(content))
|
||||
shutil.copy(content, final_path)
|
||||
else:
|
||||
shutil.copytree(content, self.temp_folder)
|
||||
shutil.copytree(content, self.temp_folder, dirs_exist_ok=True)
|
||||
return self
|
||||
|
||||
def prepare_files(self, sources_location: str):
|
||||
"""Prepare files in temp folder"""
|
||||
copy_files(sources_location, self.temp_folder)
|
||||
|
||||
def cleanup(self):
|
||||
"""Remove temporary folders"""
|
||||
shutil.rmtree(f"{self.temp_folder}/.css/", ignore_errors=True)
|
||||
|
Reference in New Issue
Block a user