mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2026-02-27 16:11:09 +00:00
- Add theme id - WIP theme cache key - Force scheme (light/dark) for dynamic theme - でびるんちゃんかわいい
144 lines
4.9 KiB
Python
144 lines
4.9 KiB
Python
import dataclasses
|
|
import json
|
|
from typing import overload
|
|
|
|
import structlog
|
|
from PySide6.QtCore import Property, QObject, QResource, Qt, Signal
|
|
from PySide6.QtGui import QGuiApplication, QPalette
|
|
|
|
from .material3 import Material3DynamicThemeImpl, Material3ThemeImpl
|
|
from .qml import ThemeQmlExposer
|
|
from .shared import ThemeImpl, ThemeInfo, TThemeInfoCacheKey, _TCustomPalette, _TScheme
|
|
|
|
QML_IMPORT_NAME = "internal.ui.theme"
|
|
QML_IMPORT_MAJOR_VERSION = 1
|
|
QML_IMPORT_MINOR_VERSION = 0
|
|
|
|
_THEME_CACHES: dict[TThemeInfoCacheKey, ThemeImpl] = {}
|
|
|
|
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
|
|
|
|
|
|
class ThemeManager(QObject):
|
|
_void = Signal()
|
|
themeChanged = Signal()
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
|
|
self._qPalette = QPalette()
|
|
self._customPalette: _TCustomPalette = ThemeImpl.DEFAULT_CUSTOM_PALETTE
|
|
|
|
self._lastThemeInfo = ThemeImpl().info
|
|
self._qmlExposer = ThemeQmlExposer(themeImpl=ThemeImpl())
|
|
|
|
self._cacheMaterial3DynamicTheme(themeId="default")
|
|
self._cacheMaterial3DynamicTheme(themeId="tempest")
|
|
|
|
self._cacheMaterial3DynamicTheme(themeId="devilun")
|
|
self._cacheMaterial3DynamicTheme(themeId="kupya")
|
|
|
|
self.setTheme("material3-dynamic", "devilun")
|
|
|
|
def getCurrentScheme(self) -> _TScheme:
|
|
qApp: QGuiApplication = QGuiApplication.instance() # pyright: ignore[reportAssignmentType]
|
|
return (
|
|
"dark"
|
|
if qApp.styleHints().colorScheme() == Qt.ColorScheme.Dark
|
|
else "light"
|
|
)
|
|
|
|
def _loadQResourceJson(self, resourcePath: str):
|
|
resource = QResource(resourcePath)
|
|
if not resource.isValid():
|
|
raise ValueError(f"Resource {resourcePath!r} invalid")
|
|
|
|
return json.loads(resource.uncompressedData().data().decode("utf-8"))
|
|
|
|
def getMaterial3Theme(self, themeId: str, scheme: _TScheme) -> Material3ThemeImpl:
|
|
themeData = self._loadQResourceJson(f":/themes/m3_{themeId}.json")
|
|
return Material3ThemeImpl(themeData=themeData, scheme=scheme)
|
|
|
|
def getMaterial3DynamicTheme(
|
|
self, themeId: str, scheme: _TScheme
|
|
) -> Material3DynamicThemeImpl:
|
|
themeData = self._loadQResourceJson(f":/themes/m3-dynamic_{themeId}.json")
|
|
return Material3DynamicThemeImpl(themeData=themeData, scheme=scheme)
|
|
|
|
def _cacheTheme(self, *, themeImpl: ThemeImpl):
|
|
_THEME_CACHES[themeImpl.info.cacheKey()] = themeImpl
|
|
logger.debug("Theme %r cached", themeImpl.info)
|
|
|
|
def _getCachedTheme(self, *, key: TThemeInfoCacheKey):
|
|
cachedTheme = _THEME_CACHES.get(key)
|
|
if cachedTheme is None:
|
|
raise KeyError(f"Theme {key!r} not cached")
|
|
return cachedTheme
|
|
|
|
def _cacheMaterial3Theme(self, *, themeId: str):
|
|
self._cacheTheme(
|
|
themeImpl=self.getMaterial3Theme(themeId=themeId, scheme="light"),
|
|
)
|
|
self._cacheTheme(
|
|
themeImpl=self.getMaterial3Theme(themeId=themeId, scheme="dark"),
|
|
)
|
|
|
|
def _cacheMaterial3DynamicTheme(self, *, themeId: str):
|
|
self._cacheTheme(
|
|
themeImpl=self.getMaterial3DynamicTheme(themeId=themeId, scheme="light")
|
|
)
|
|
self._cacheTheme(
|
|
themeImpl=self.getMaterial3DynamicTheme(themeId=themeId, scheme="dark")
|
|
)
|
|
|
|
@overload
|
|
def setTheme(self, *, themeInfo: ThemeInfo): ...
|
|
|
|
@overload
|
|
def setTheme(
|
|
self, themeSeries: str, themeId: str, scheme: _TScheme | None = None, /
|
|
): ...
|
|
|
|
def setTheme(self, *args, **kwargs):
|
|
if "themeInfo" in kwargs:
|
|
cacheKey = kwargs["themeInfo"].cacheKey()
|
|
elif 2 <= len(args) <= 3:
|
|
themeSeries = args[0]
|
|
themeId = args[1]
|
|
schemeArg = args[2] if len(args) > 2 else None
|
|
scheme = schemeArg or self.getCurrentScheme()
|
|
|
|
cacheKey: TThemeInfoCacheKey = (themeSeries, themeId, scheme)
|
|
else:
|
|
raise TypeError("Invalid setTheme() call")
|
|
|
|
logger.debug("Preparing to set theme %r", cacheKey)
|
|
|
|
cachedTheme = self._getCachedTheme(key=cacheKey)
|
|
|
|
self._qPalette = cachedTheme.qPalette
|
|
self._customPalette = cachedTheme.customPalette
|
|
self._lastThemeInfo = cachedTheme.info
|
|
self._qmlExposer.themeImpl = cachedTheme
|
|
|
|
self.themeChanged.emit()
|
|
|
|
def updateTheme(self, scheme: _TScheme | None = None):
|
|
themeInfo = dataclasses.replace(self._lastThemeInfo) # make a copy
|
|
scheme = scheme or self.getCurrentScheme()
|
|
themeInfo.scheme = scheme
|
|
|
|
self.setTheme(themeInfo=themeInfo)
|
|
|
|
@Property(QPalette, notify=themeChanged)
|
|
def qPalette(self) -> QPalette:
|
|
return self._qPalette
|
|
|
|
@Property(dict, notify=themeChanged)
|
|
def customPalette(self) -> _TCustomPalette:
|
|
return self._customPalette
|
|
|
|
@Property(ThemeQmlExposer, notify=themeChanged)
|
|
def qmlExposer(self) -> ThemeQmlExposer:
|
|
return self._qmlExposer
|