feat: add ColorAwareApp mixin (resolves #465)

This commit is contained in:
aclist 2026-08-23 19:28:42 +09:00
parent e1e034fffd
commit fad8d2f43b
6 changed files with 106 additions and 4 deletions

View File

@ -4,6 +4,9 @@ from typing import Any, Literal, Self, TYPE_CHECKING
# import time
from enum import Enum
from dzgui.views.mixins.colorscheme import ColorAwareApp
# TODO: import dialog titles
from dzgui.api.mods import remove_stale_signatures as remove_stale
from dzgui.config.ipdb import get_ipdb
@ -37,7 +40,7 @@ class Success(Enum):
# TODO: strings for "Running", "Failed", etc.
# TODO: add margins to tree
class BootDialog(Gtk.Dialog):
class BootDialog(ColorAwareApp, Gtk.Dialog): # type: ignore
def __init__(self, parent: "BootWindow", xdg: "Xdg", version: str) -> None:
super().__init__(
title=dialog_header,

View File

@ -3,6 +3,7 @@ import sys
from typing import Self
from dzgui.util.strings import dialog_error, dialog_header
from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
@ -10,7 +11,7 @@ gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa E402
class AbortDialog(Gtk.MessageDialog):
class AbortDialog(ColorAwareApp, Gtk.MessageDialog):
def __init__(self, string: str, buttons: Gtk.ButtonsType) -> None:
super().__init__(
title=dialog_header,

View File

@ -13,6 +13,7 @@ from dzgui.util.css import load_css
from dzgui.util.format import format_exception
from dzgui.views.components.box import HBox
from dzgui.views.dialogs.wizard import ScrolledWizardPage, CheckboxWithLabel
from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
@ -217,7 +218,7 @@ class UninstallPage(ScrolledWizardPage):
pass
class UninstallWizard(Gtk.Application):
class UninstallWizard(ColorAwareApp, Gtk.Application): # type: ignore
def __init__(self, is_deck: bool, paths: dict[str, str]) -> None:
super().__init__()
config = Path(paths["XDG_CONFIG_HOME"])

View File

@ -29,6 +29,7 @@ from dzgui.util.css import add_class, load_css
from dzgui.views.components.buttons import WebButton
from dzgui.views.components.entry import APIEntry
from dzgui.views.components.misc import ClientCombo
from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
@ -706,7 +707,7 @@ class SteamPathPage(EnumeratedWizardPage):
self.err_box.set_visible(False)
class SetupWizard(Gtk.Application):
class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore
def __init__(self, is_deck: bool, XDG: "Xdg") -> None:
super().__init__()
GLib.set_prgname(APP_NAME)

View File

@ -0,0 +1,37 @@
from typing import Any
from dzgui.const.constants import (
GIO_SETTINGS_KEY,
GIO_SETTINGS_INTERFACE,
GIO_SETTINGS_PROP,
GIO_SETTINGS_VAL,
)
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio # noqa
class ColorAwareApp:
"""
Polls the local theme's color scheme settings.
Intended to be consumed by Gtk.Applications (or ephemeral widgets like EarlyAlertDialogs)
on initialization, prior to being realized. Applying scheme changes too late in the chain
may cause a blinking effect.
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.interface_settings = Gio.Settings.new(GIO_SETTINGS_INTERFACE)
self.interface_settings.connect(
"changed::color-scheme", self._on_color_scheme_changed
)
self.default_settings = Gtk.Settings.get_default()
self._on_color_scheme_changed(self.interface_settings, GIO_SETTINGS_PROP)
def _on_color_scheme_changed(self, settings: Gio.Settings, prop: str) -> None:
state = self.interface_settings.get_string(prop) == GIO_SETTINGS_VAL
if self.default_settings is None:
return
self.default_settings.set_property(GIO_SETTINGS_KEY, state)

59
tests/test_colorscheme.py Normal file
View File

@ -0,0 +1,59 @@
import pytest
import subprocess
from dzgui.const.constants import GIO_SETTINGS_KEY
from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio, GLib # noqa
class TestWidget(ColorAwareApp, Gtk.Button):
def __init__(self) -> None:
super().__init__()
self.state: bool
def check_theme(self) -> None:
self.state = self.get_settings().get_property(GIO_SETTINGS_KEY)
Gtk.main_quit()
@pytest.fixture
def widget() -> TestWidget:
return TestWidget()
@pytest.mark.integration
@pytest.mark.parametrize("key, expect", [("default", False), ("prefer-dark", True)])
def test_light_mode(widget, key: str, expect: bool) -> None:
local_pref = subprocess.run(
["gsettings", "get", "org.gnome.desktop.interface", "color-scheme"],
capture_output=True,
text=True,
).stdout
subprocess.run(
[
"gsettings",
"set",
"org.gnome.desktop.interface",
"color-scheme",
key,
],
)
# NOTE: allow changes to propagate
GLib.idle_add(widget.check_theme)
Gtk.main()
subprocess.run(
[
"gsettings",
"set",
"org.gnome.desktop.interface",
"color-scheme",
local_pref,
]
)
assert widget.state is expect