diff --git a/biglinux-livecd/usr/share/biglinux/livecd/desktop_theme.py b/biglinux-livecd/usr/share/biglinux/livecd/desktop_theme.py index 7e7a06ea..802933b1 100644 --- a/biglinux-livecd/usr/share/biglinux/livecd/desktop_theme.py +++ b/biglinux-livecd/usr/share/biglinux/livecd/desktop_theme.py @@ -7,11 +7,15 @@ from collections.abc import Mapping from typing import Protocol +from gnome_layout import LAYOUT_NAMES + logger = logging.getLogger(__name__) GNOME_LIGHT_STYLE_UUID = "light-style@gnome-shell-extensions.gcampax.github.com" GNOME_USER_THEME_UUID = "user-theme@gnome-shell-extensions.gcampax.github.com" GNOME_KIWI_UUID = "kiwi@kemma" GNOME_DTP_UUID = "dash-to-panel@jderose9.github.com" +GNOME_ALWAYS_DARK_LAYOUTS = frozenset({"biggnome", "g-unity", "minimal"}) +GNOME_ORCHIS_LAYOUTS = frozenset({"biggnome", "desk-ux"}) MAX_SETTINGS_BYTES = 1024 * 1024 SettingsChanges = Mapping[str, Mapping[str, str]] @@ -22,6 +26,7 @@ class ThemeHost(Protocol): theme_list_script: str theme_apply_script: str theme_state_file: str + gnome_layout_state_file: str def _run_command( self, @@ -205,23 +210,35 @@ def _settings_key_values( def _gnome_extension_changes( - settings_file: str, dark: bool + settings_file: str, + *, + user_theme: bool, + light_style: bool, ) -> dict[str, dict[str, str]]: values = _settings_key_values(settings_file, "org/gnome/shell") if values is None: return {} enabled = _parse_settings_list(values.get("enabled-extensions", "[]")) disabled = _parse_settings_list(values.get("disabled-extensions", "[]")) - if dark: - enabled = [item for item in enabled if item != GNOME_LIGHT_STYLE_UUID] + enabled = [ + item + for item in enabled + if item not in {GNOME_USER_THEME_UUID, GNOME_LIGHT_STYLE_UUID} + ] + disabled = [ + item + for item in disabled + if item not in {GNOME_USER_THEME_UUID, GNOME_LIGHT_STYLE_UUID} + ] + if user_theme: disabled = [item for item in disabled if item != GNOME_USER_THEME_UUID] enabled.append(GNOME_USER_THEME_UUID) - disabled.append(GNOME_LIGHT_STYLE_UUID) else: - enabled = [item for item in enabled if item != GNOME_USER_THEME_UUID] - disabled = [item for item in disabled if item != GNOME_LIGHT_STYLE_UUID] - enabled.append(GNOME_LIGHT_STYLE_UUID) disabled.append(GNOME_USER_THEME_UUID) + if light_style: + enabled.append(GNOME_LIGHT_STYLE_UUID) + else: + disabled.append(GNOME_LIGHT_STYLE_UUID) return { "org/gnome/shell": { "enabled-extensions": repr(list(dict.fromkeys(enabled))), @@ -231,19 +248,39 @@ def _gnome_extension_changes( def _gnome_layout_class(settings_file: str) -> str: - values = _settings_key_values(settings_file, "org/gnome/shell") - if values is None: - return "biggnome" - enabled = _parse_settings_list(values.get("enabled-extensions", "[]")) + shell_values = _settings_key_values(settings_file, "org/gnome/shell") or {} + user_theme_values = ( + _settings_key_values( + settings_file, + "org/gnome/shell/extensions/user-theme", + ) + or {} + ) + enabled = _parse_settings_list(shell_values.get("enabled-extensions", "[]")) + user_theme_name = user_theme_values.get("name", "").strip().strip("'\"") + if user_theme_name: + return "desk-ux" if GNOME_DTP_UUID in enabled else "biggnome" if GNOME_KIWI_UUID in enabled: - return "kiwi" - if GNOME_DTP_UUID in enabled: - return "panel" - return "biggnome" + return "minimal" + return "hybrid" + + +def _selected_gnome_layout(host: ThemeHost) -> str: + state_file = getattr(host, "gnome_layout_state_file", "") + if not state_file: + return "" + try: + layout = _read_regular_text(state_file).strip() + except (OSError, UnicodeError): + return "" + return layout if layout in LAYOUT_NAMES else "" def _desktop_changes( - desktop_environment: str, settings_file: str, dark: bool + desktop_environment: str, + settings_file: str, + dark: bool, + gnome_layout: str = "", ) -> dict[str, dict[str, str]]: color_scheme = "'prefer-dark'" if dark else "'default'" gtk_theme = "'adw-gtk3-dark'" if dark else "'adw-gtk3'" @@ -264,16 +301,31 @@ def _desktop_changes( "name": "'Big-Orange'" if dark else "'Big-Orange-Light'" } elif desktop_environment == "GNOME": - layout_class = _gnome_layout_class(settings_file) - if dark and layout_class != "kiwi": - changes["org/gnome/shell/extensions/user-theme"] = {"name": "'Big-Blue'"} - changes.update(_gnome_extension_changes(settings_file, dark=True)) - elif not dark and layout_class == "biggnome": - changes["org/gnome/shell/extensions/user-theme"] = {"name": "'Big-Blue'"} - changes.update(_gnome_extension_changes(settings_file, dark=True)) - elif not dark and layout_class == "panel": - changes["org/gnome/shell/extensions/user-theme"] = {"name": "'Big-Blue'"} - changes.update(_gnome_extension_changes(settings_file, dark=False)) + layout = ( + gnome_layout + if gnome_layout in LAYOUT_NAMES + else _gnome_layout_class(settings_file) + ) + orchis = layout in GNOME_ORCHIS_LAYOUTS + shell_theme = ( + "'Big-Blue-Light'" + if layout == "desk-ux" and not dark + else "'Big-Blue'" + if orchis + else "''" + ) + changes["org/gnome/shell/extensions/user-theme"] = {"name": shell_theme} + changes.update( + _gnome_extension_changes( + settings_file, + user_theme=orchis, + light_style=( + not dark + and not orchis + and layout not in GNOME_ALWAYS_DARK_LAYOUTS + ), + ) + ) return changes @@ -298,13 +350,20 @@ def apply_simple_theme(host: ThemeHost, theme: str) -> bool: return False dark = theme == "dark" desktop_environment = host.get_desktop_environment() + gnome_layout = "" if desktop_environment == "GNOME": host._ensure_gnome_settings_file() + gnome_layout = _selected_gnome_layout(host) settings_file = settings_file_path(desktop_environment) if not modify_settings_file( host, settings_file, - _desktop_changes(desktop_environment, settings_file, dark), + _desktop_changes( + desktop_environment, + settings_file, + dark, + gnome_layout=gnome_layout, + ), ): return False if desktop_environment == "XFCE": diff --git a/tests/test_desktop_settings.py b/tests/test_desktop_settings.py index e2b49824..8f727b3a 100644 --- a/tests/test_desktop_settings.py +++ b/tests/test_desktop_settings.py @@ -1,5 +1,6 @@ from __future__ import annotations +import ast import os import sys from pathlib import Path @@ -11,11 +12,10 @@ sys.path.insert(0, str(LIBRARY)) from desktop_theme import ( # noqa: E402 - GNOME_DTP_UUID, - GNOME_KIWI_UUID, GNOME_LIGHT_STYLE_UUID, GNOME_USER_THEME_UUID, _desktop_changes, + _selected_gnome_layout, apply_packaged_theme, apply_simple_theme, update_settings_text, @@ -177,31 +177,63 @@ def test_gnome_layout_normalization_is_monitor_independent() -> None: assert "unrelated='kept'" in normalized +@pytest.mark.parametrize("dark", [False, True], ids=["light", "dark"]) @pytest.mark.parametrize( - ("extensions", "expected_enabled", "has_user_theme"), + ("layout", "user_theme", "light_style", "light_name", "dark_name"), [ - (["dash-to-dock@micxgx.gmail.com"], GNOME_USER_THEME_UUID, True), - ([GNOME_DTP_UUID], GNOME_LIGHT_STYLE_UUID, True), - ([GNOME_KIWI_UUID], "", False), + ("biggnome", True, False, "'Big-Blue'", "'Big-Blue'"), + ("desk-ux", True, False, "'Big-Blue-Light'", "'Big-Blue'"), + ("hybrid", False, True, "''", "''"), + ("classic", False, True, "''", "''"), + ("g-unity", False, False, "''", "''"), + ("minimal", False, False, "''", "''"), ], ) -def test_gnome_light_theme_respects_layout_shell_contract( +def test_gnome_theme_matrix_respects_each_layout_shell_contract( tmp_path: Path, - extensions: list[str], - expected_enabled: str, - has_user_theme: bool, + layout: str, + user_theme: bool, + light_style: bool, + light_name: str, + dark_name: str, + dark: bool, ) -> None: settings = tmp_path / "settings.gnome" settings.write_text( "[org/gnome/shell]\n" - f"enabled-extensions={extensions!r}\n" + f"enabled-extensions={[GNOME_USER_THEME_UUID, GNOME_LIGHT_STYLE_UUID]!r}\n" "disabled-extensions=[]\n", encoding="utf-8", ) - changes = _desktop_changes("GNOME", str(settings), dark=False) - shell_changes = changes.get("org/gnome/shell", {}) - if expected_enabled: - assert expected_enabled in shell_changes["enabled-extensions"] - else: - assert shell_changes == {} - assert ("org/gnome/shell/extensions/user-theme" in changes) is has_user_theme + changes = _desktop_changes( + "GNOME", + str(settings), + dark=dark, + gnome_layout=layout, + ) + shell_changes = changes["org/gnome/shell"] + enabled = ast.literal_eval(shell_changes["enabled-extensions"]) + disabled = ast.literal_eval(shell_changes["disabled-extensions"]) + expected_light_style = light_style and not dark + + assert (GNOME_USER_THEME_UUID in enabled) is user_theme + assert (GNOME_USER_THEME_UUID in disabled) is not user_theme + assert (GNOME_LIGHT_STYLE_UUID in enabled) is expected_light_style + assert (GNOME_LIGHT_STYLE_UUID in disabled) is not expected_light_style + assert changes["org/gnome/shell/extensions/user-theme"] == { + "name": dark_name if dark else light_name + } + assert changes["org/gnome/desktop/interface"]["color-scheme"] == ( + "'prefer-dark'" if dark else "'default'" + ) + + +def test_selected_gnome_layout_uses_live_state(tmp_path: Path) -> None: + state = tmp_path / "big_gnome_layout" + state.write_text("hybrid\n", encoding="utf-8") + host = type("Host", (), {"gnome_layout_state_file": str(state)})() + + assert _selected_gnome_layout(host) == "hybrid" + + state.write_text("../../invalid\n", encoding="utf-8") + assert _selected_gnome_layout(host) == ""