From 36798312014b43e6d78c58b72ef937e1b79586ca Mon Sep 17 00:00:00 2001 From: 283375 Date: Sun, 9 Nov 2025 00:32:28 +0800 Subject: [PATCH] wip: theme system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add theme id - WIP theme cache key - Force scheme (light/dark) for dynamic theme - でびるんちゃんかわいい --- ui/resources/resources.qrc | 6 +- ui/resources/themes/default.json | 427 -------------------- ui/resources/themes/m3-dynamic_default.json | 8 + ui/resources/themes/m3-dynamic_devilun.json | 11 + ui/resources/themes/m3-dynamic_kupya.json | 10 + ui/resources/themes/m3-dynamic_tempest.json | 8 + ui/resources/themes/tempest.json | 427 -------------------- ui/theme/manager.py | 99 ++--- ui/theme/material3.py | 92 ++--- ui/theme/qml.py | 8 + ui/theme/shared.py | 21 +- ui/theme/types.py | 72 ++++ 12 files changed, 221 insertions(+), 968 deletions(-) delete mode 100644 ui/resources/themes/default.json create mode 100644 ui/resources/themes/m3-dynamic_default.json create mode 100644 ui/resources/themes/m3-dynamic_devilun.json create mode 100644 ui/resources/themes/m3-dynamic_kupya.json create mode 100644 ui/resources/themes/m3-dynamic_tempest.json delete mode 100644 ui/resources/themes/tempest.json create mode 100644 ui/theme/types.py diff --git a/ui/resources/resources.qrc b/ui/resources/resources.qrc index ea38850..716e8a0 100644 --- a/ui/resources/resources.qrc +++ b/ui/resources/resources.qrc @@ -20,7 +20,9 @@ lang/zh_CN.qm lang/en_US.qm - themes/default.json - themes/tempest.json + themes/m3-dynamic_default.json + themes/m3-dynamic_tempest.json + themes/m3-dynamic_devilun.json + themes/m3-dynamic_kupya.json diff --git a/ui/resources/themes/default.json b/ui/resources/themes/default.json deleted file mode 100644 index ea75394..0000000 --- a/ui/resources/themes/default.json +++ /dev/null @@ -1,427 +0,0 @@ -{ - "//url": "http://material-foundation.github.io/material-theme-builder/?primary=%234E486C&custom%3ASuccess=%2300C555&colorMatch=true", - "name": "default", - "description": "TYPE: CUSTOM\nMaterial Theme Builder export", - "seed": "#4E486C", - "coreColors": { - "primary": "#4E486C" - }, - "extendedColors": [ - { - "name": "Success", - "color": "#00C555", - "description": "", - "harmonized": true - } - ], - "schemes": { - "light": { - "primary": "#373154", - "surfaceTint": "#605A7F", - "onPrimary": "#FFFFFF", - "primaryContainer": "#4E486C", - "onPrimaryContainer": "#C0B8E3", - "secondary": "#605C6C", - "onSecondary": "#FFFFFF", - "secondaryContainer": "#E3DDF0", - "onSecondaryContainer": "#646071", - "tertiary": "#4F2B40", - "onTertiary": "#FFFFFF", - "tertiaryContainer": "#684157", - "onTertiaryContainer": "#E3B0CA", - "error": "#BA1A1A", - "onError": "#FFFFFF", - "errorContainer": "#FFDAD6", - "onErrorContainer": "#93000A", - "background": "#FDF8FC", - "onBackground": "#1C1B1E", - "surface": "#FDF8FC", - "onSurface": "#1C1B1E", - "surfaceVariant": "#E6E1EA", - "onSurfaceVariant": "#48464D", - "outline": "#79767E", - "outlineVariant": "#C9C5CE", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#313033", - "inverseOnSurface": "#F4EFF3", - "inversePrimary": "#C9C1EC", - "primaryFixed": "#E6DEFF", - "onPrimaryFixed": "#1C1738", - "primaryFixedDim": "#C9C1EC", - "onPrimaryFixedVariant": "#484266", - "secondaryFixed": "#E6E0F3", - "onSecondaryFixed": "#1C1A27", - "secondaryFixedDim": "#C9C4D7", - "onSecondaryFixedVariant": "#484554", - "tertiaryFixed": "#FFD8EA", - "onTertiaryFixed": "#301024", - "tertiaryFixedDim": "#ECB8D2", - "onTertiaryFixedVariant": "#613B51", - "surfaceDim": "#DDD9DC", - "surfaceBright": "#FDF8FC", - "surfaceContainerLowest": "#FFFFFF", - "surfaceContainerLow": "#F7F2F6", - "surfaceContainer": "#F1ECF0", - "surfaceContainerHigh": "#EBE7EA", - "surfaceContainerHighest": "#E6E1E5" - }, - "light-medium-contrast": { - "primary": "#373154", - "surfaceTint": "#605A7F", - "onPrimary": "#FFFFFF", - "primaryContainer": "#4E486C", - "onPrimaryContainer": "#EDE7FF", - "secondary": "#373443", - "onSecondary": "#FFFFFF", - "secondaryContainer": "#6F6B7B", - "onSecondaryContainer": "#FFFFFF", - "tertiary": "#4E2B40", - "onTertiary": "#FFFFFF", - "tertiaryContainer": "#684157", - "onTertiaryContainer": "#FFE4EF", - "error": "#740006", - "onError": "#FFFFFF", - "errorContainer": "#CF2C27", - "onErrorContainer": "#FFFFFF", - "background": "#FDF8FC", - "onBackground": "#1C1B1E", - "surface": "#FDF8FC", - "onSurface": "#121113", - "surfaceVariant": "#E6E1EA", - "onSurfaceVariant": "#37353D", - "outline": "#545159", - "outlineVariant": "#6F6C74", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#313033", - "inverseOnSurface": "#F4EFF3", - "inversePrimary": "#C9C1EC", - "primaryFixed": "#6F688E", - "onPrimaryFixed": "#FFFFFF", - "primaryFixedDim": "#565075", - "onPrimaryFixedVariant": "#FFFFFF", - "secondaryFixed": "#6F6B7B", - "onSecondaryFixed": "#FFFFFF", - "secondaryFixedDim": "#565363", - "onSecondaryFixedVariant": "#FFFFFF", - "tertiaryFixed": "#8B6078", - "onTertiaryFixed": "#FFFFFF", - "tertiaryFixedDim": "#71495F", - "onTertiaryFixedVariant": "#FFFFFF", - "surfaceDim": "#C9C5C9", - "surfaceBright": "#FDF8FC", - "surfaceContainerLowest": "#FFFFFF", - "surfaceContainerLow": "#F7F2F6", - "surfaceContainer": "#EBE7EA", - "surfaceContainerHigh": "#E0DCDF", - "surfaceContainerHighest": "#D4D0D4" - }, - "light-high-contrast": { - "primary": "#2D2749", - "surfaceTint": "#605A7F", - "onPrimary": "#FFFFFF", - "primaryContainer": "#4A4468", - "onPrimaryContainer": "#FFFFFF", - "secondary": "#2D2A39", - "onSecondary": "#FFFFFF", - "secondaryContainer": "#4A4757", - "onSecondaryContainer": "#FFFFFF", - "tertiary": "#432135", - "onTertiary": "#FFFFFF", - "tertiaryContainer": "#643D53", - "onTertiaryContainer": "#FFFFFF", - "error": "#600004", - "onError": "#FFFFFF", - "errorContainer": "#98000A", - "onErrorContainer": "#FFFFFF", - "background": "#FDF8FC", - "onBackground": "#1C1B1E", - "surface": "#FDF8FC", - "onSurface": "#000000", - "surfaceVariant": "#E6E1EA", - "onSurfaceVariant": "#000000", - "outline": "#2D2B32", - "outlineVariant": "#4A4850", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#313033", - "inverseOnSurface": "#FFFFFF", - "inversePrimary": "#C9C1EC", - "primaryFixed": "#4A4468", - "onPrimaryFixed": "#FFFFFF", - "primaryFixedDim": "#342E50", - "onPrimaryFixedVariant": "#FFFFFF", - "secondaryFixed": "#4A4757", - "onSecondaryFixed": "#FFFFFF", - "secondaryFixedDim": "#34313F", - "onSecondaryFixedVariant": "#FFFFFF", - "tertiaryFixed": "#643D53", - "onTertiaryFixed": "#FFFFFF", - "tertiaryFixedDim": "#4A273C", - "onTertiaryFixedVariant": "#FFFFFF", - "surfaceDim": "#BBB8BB", - "surfaceBright": "#FDF8FC", - "surfaceContainerLowest": "#FFFFFF", - "surfaceContainerLow": "#F4EFF3", - "surfaceContainer": "#E6E1E5", - "surfaceContainerHigh": "#D7D3D7", - "surfaceContainerHighest": "#C9C5C9" - }, - "dark": { - "primary": "#C9C1EC", - "surfaceTint": "#C9C1EC", - "onPrimary": "#312C4E", - "primaryContainer": "#4E486C", - "onPrimaryContainer": "#C0B8E3", - "secondary": "#C9C4D7", - "onSecondary": "#312E3D", - "secondaryContainer": "#4A4757", - "onSecondaryContainer": "#BBB6C8", - "tertiary": "#ECB8D2", - "onTertiary": "#48253A", - "tertiaryContainer": "#684157", - "onTertiaryContainer": "#E3B0CA", - "error": "#FFB4AB", - "onError": "#690005", - "errorContainer": "#93000A", - "onErrorContainer": "#FFDAD6", - "background": "#141315", - "onBackground": "#E6E1E5", - "surface": "#141315", - "onSurface": "#E6E1E5", - "surfaceVariant": "#48464D", - "onSurfaceVariant": "#C9C5CE", - "outline": "#938F98", - "outlineVariant": "#48464D", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#E6E1E5", - "inverseOnSurface": "#313033", - "inversePrimary": "#605A7F", - "primaryFixed": "#E6DEFF", - "onPrimaryFixed": "#1C1738", - "primaryFixedDim": "#C9C1EC", - "onPrimaryFixedVariant": "#484266", - "secondaryFixed": "#E6E0F3", - "onSecondaryFixed": "#1C1A27", - "secondaryFixedDim": "#C9C4D7", - "onSecondaryFixedVariant": "#484554", - "tertiaryFixed": "#FFD8EA", - "onTertiaryFixed": "#301024", - "tertiaryFixedDim": "#ECB8D2", - "onTertiaryFixedVariant": "#613B51", - "surfaceDim": "#141315", - "surfaceBright": "#3A393B", - "surfaceContainerLowest": "#0F0E10", - "surfaceContainerLow": "#1C1B1E", - "surfaceContainer": "#201F22", - "surfaceContainerHigh": "#2B292C", - "surfaceContainerHighest": "#363437" - }, - "dark-medium-contrast": { - "primary": "#E0D7FF", - "surfaceTint": "#C9C1EC", - "onPrimary": "#262142", - "primaryContainer": "#938CB4", - "onPrimaryContainer": "#000000", - "secondary": "#DFD9ED", - "onSecondary": "#262432", - "secondaryContainer": "#938EA0", - "onSecondaryContainer": "#000000", - "tertiary": "#FFCFE7", - "onTertiary": "#3C1A2E", - "tertiaryContainer": "#B2839C", - "onTertiaryContainer": "#000000", - "error": "#FFD2CC", - "onError": "#540003", - "errorContainer": "#FF5449", - "onErrorContainer": "#000000", - "background": "#141315", - "onBackground": "#E6E1E5", - "surface": "#141315", - "onSurface": "#FFFFFF", - "surfaceVariant": "#48464D", - "onSurfaceVariant": "#DFDAE4", - "outline": "#B4B0BA", - "outlineVariant": "#928F98", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#E6E1E5", - "inverseOnSurface": "#2B292C", - "inversePrimary": "#494367", - "primaryFixed": "#E6DEFF", - "onPrimaryFixed": "#120C2D", - "primaryFixedDim": "#C9C1EC", - "onPrimaryFixedVariant": "#373254", - "secondaryFixed": "#E6E0F3", - "onSecondaryFixed": "#120F1D", - "secondaryFixedDim": "#C9C4D7", - "onSecondaryFixedVariant": "#373443", - "tertiaryFixed": "#FFD8EA", - "onTertiaryFixed": "#230619", - "tertiaryFixedDim": "#ECB8D2", - "onTertiaryFixedVariant": "#4E2B40", - "surfaceDim": "#141315", - "surfaceBright": "#454447", - "surfaceContainerLowest": "#080709", - "surfaceContainerLow": "#1E1D20", - "surfaceContainer": "#28272A", - "surfaceContainerHigh": "#333235", - "surfaceContainerHighest": "#3F3D40" - }, - "dark-high-contrast": { - "primary": "#F3EDFF", - "surfaceTint": "#C9C1EC", - "onPrimary": "#000000", - "primaryContainer": "#C6BDE8", - "onPrimaryContainer": "#0B0627", - "secondary": "#F3EDFF", - "onSecondary": "#000000", - "secondaryContainer": "#C5C0D3", - "onSecondaryContainer": "#0C0916", - "tertiary": "#FFEBF3", - "onTertiary": "#000000", - "tertiaryContainer": "#E7B4CE", - "onTertiaryContainer": "#1C0213", - "error": "#FFECE9", - "onError": "#000000", - "errorContainer": "#FFAEA4", - "onErrorContainer": "#220001", - "background": "#141315", - "onBackground": "#E6E1E5", - "surface": "#141315", - "onSurface": "#FFFFFF", - "surfaceVariant": "#48464D", - "onSurfaceVariant": "#FFFFFF", - "outline": "#F3EEF8", - "outlineVariant": "#C5C1CA", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#E6E1E5", - "inverseOnSurface": "#000000", - "inversePrimary": "#494367", - "primaryFixed": "#E6DEFF", - "onPrimaryFixed": "#000000", - "primaryFixedDim": "#C9C1EC", - "onPrimaryFixedVariant": "#120C2D", - "secondaryFixed": "#E6E0F3", - "onSecondaryFixed": "#000000", - "secondaryFixedDim": "#C9C4D7", - "onSecondaryFixedVariant": "#120F1D", - "tertiaryFixed": "#FFD8EA", - "onTertiaryFixed": "#000000", - "tertiaryFixedDim": "#ECB8D2", - "onTertiaryFixedVariant": "#230619", - "surfaceDim": "#141315", - "surfaceBright": "#514F52", - "surfaceContainerLowest": "#000000", - "surfaceContainerLow": "#201F22", - "surfaceContainer": "#313033", - "surfaceContainerHigh": "#3C3B3E", - "surfaceContainerHighest": "#484649" - } - }, - "palettes": { - "primary": { - "0": "#000000", - "5": "#110B2C", - "10": "#1C1738", - "15": "#272142", - "20": "#312C4E", - "25": "#3C375A", - "30": "#484266", - "35": "#544E72", - "40": "#605A7F", - "50": "#797299", - "60": "#938CB4", - "70": "#AEA6CF", - "80": "#C9C1EC", - "90": "#E6DEFF", - "95": "#F4EEFF", - "98": "#FDF8FF", - "99": "#FFFBFF", - "100": "#FFFFFF" - }, - "secondary": { - "0": "#000000", - "5": "#111018", - "10": "#1C1A23", - "15": "#26252D", - "20": "#312F38", - "25": "#3C3A43", - "30": "#48454F", - "35": "#54515B", - "40": "#605D67", - "50": "#797580", - "60": "#938F9A", - "70": "#AEA9B5", - "80": "#C9C4D0", - "90": "#E6E0EC", - "95": "#F4EEFB", - "98": "#FDF8FF", - "99": "#FFFBFF", - "100": "#FFFFFF" - }, - "tertiary": { - "0": "#000000", - "5": "#1B0C13", - "10": "#27171E", - "15": "#322128", - "20": "#3E2B33", - "25": "#49363E", - "30": "#564149", - "35": "#624D55", - "40": "#6F5861", - "50": "#897179", - "60": "#A38A93", - "70": "#BFA4AD", - "80": "#DCBFC9", - "90": "#F9DBE5", - "95": "#FFECF1", - "98": "#FFF8F8", - "99": "#FFFBFF", - "100": "#FFFFFF" - }, - "neutral": { - "0": "#000000", - "5": "#111112", - "10": "#1C1B1D", - "15": "#262527", - "20": "#313032", - "25": "#3C3B3D", - "30": "#484648", - "35": "#545254", - "40": "#605E60", - "50": "#797678", - "60": "#939092", - "70": "#ADAAAC", - "80": "#C9C5C7", - "90": "#E5E1E3", - "95": "#F4EFF1", - "98": "#FDF8FA", - "99": "#FFFBFF", - "100": "#FFFFFF" - }, - "neutral-variant": { - "0": "#000000", - "5": "#111014", - "10": "#1C1B1F", - "15": "#262529", - "20": "#313034", - "25": "#3C3B3F", - "30": "#48464A", - "35": "#545256", - "40": "#605D62", - "50": "#79767B", - "60": "#939094", - "70": "#AEAAAF", - "80": "#C9C5CA", - "90": "#E6E1E6", - "95": "#F4EFF4", - "98": "#FDF8FD", - "99": "#FFFBFF", - "100": "#FFFFFF" - } - } -} diff --git a/ui/resources/themes/m3-dynamic_default.json b/ui/resources/themes/m3-dynamic_default.json new file mode 100644 index 0000000..206787d --- /dev/null +++ b/ui/resources/themes/m3-dynamic_default.json @@ -0,0 +1,8 @@ +{ + "id": "default", + "name": "Default", + "colors": { + "primary": "#4e486c" + }, + "options": {} +} diff --git a/ui/resources/themes/m3-dynamic_devilun.json b/ui/resources/themes/m3-dynamic_devilun.json new file mode 100644 index 0000000..7438c55 --- /dev/null +++ b/ui/resources/themes/m3-dynamic_devilun.json @@ -0,0 +1,11 @@ +{ + "id": "devilun", + "name": "Devilun", + "colors": { + "primary": "#d1779d", + "secondary": "#85a9a5" + }, + "options": { + "forceScheme": "dark" + } +} diff --git a/ui/resources/themes/m3-dynamic_kupya.json b/ui/resources/themes/m3-dynamic_kupya.json new file mode 100644 index 0000000..36ce192 --- /dev/null +++ b/ui/resources/themes/m3-dynamic_kupya.json @@ -0,0 +1,10 @@ +{ + "id": "kupya", + "name": "Kupya", + "colors": { + "primary": "#ffeb9e" + }, + "options": { + "forceScheme": "light" + } +} diff --git a/ui/resources/themes/m3-dynamic_tempest.json b/ui/resources/themes/m3-dynamic_tempest.json new file mode 100644 index 0000000..00f893b --- /dev/null +++ b/ui/resources/themes/m3-dynamic_tempest.json @@ -0,0 +1,8 @@ +{ + "id": "tempest", + "name": "Tempest", + "colors": { + "primary": "#186d98" + }, + "options": {} +} diff --git a/ui/resources/themes/tempest.json b/ui/resources/themes/tempest.json deleted file mode 100644 index 9fdbbe8..0000000 --- a/ui/resources/themes/tempest.json +++ /dev/null @@ -1,427 +0,0 @@ -{ - "//url": "http://material-foundation.github.io/material-theme-builder/?primary=%23186D98&custom%3ASuccess=%2300C555&colorMatch=true", - "name": "tempest", - "description": "TYPE: CUSTOM\nMaterial Theme Builder export", - "seed": "#186D98", - "coreColors": { - "primary": "#186D98" - }, - "extendedColors": [ - { - "name": "Success", - "color": "#00C555", - "description": "", - "harmonized": true - } - ], - "schemes": { - "light": { - "primary": "#005479", - "surfaceTint": "#03658F", - "onPrimary": "#FFFFFF", - "primaryContainer": "#186D98", - "onPrimaryContainer": "#CEE9FF", - "secondary": "#496173", - "onSecondary": "#FFFFFF", - "secondaryContainer": "#C9E3F8", - "onSecondaryContainer": "#4D6678", - "tertiary": "#683D7A", - "onTertiary": "#FFFFFF", - "tertiaryContainer": "#825594", - "onTertiaryContainer": "#F9DCFF", - "error": "#BA1A1A", - "onError": "#FFFFFF", - "errorContainer": "#FFDAD6", - "onErrorContainer": "#93000A", - "background": "#F7F9FD", - "onBackground": "#191C1F", - "surface": "#F7F9FD", - "onSurface": "#191C1F", - "surfaceVariant": "#DCE3EB", - "onSurfaceVariant": "#40484E", - "outline": "#70787F", - "outlineVariant": "#C0C7CF", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#2D3134", - "inverseOnSurface": "#EFF1F5", - "inversePrimary": "#88CEFE", - "primaryFixed": "#C8E6FF", - "onPrimaryFixed": "#001E2E", - "primaryFixedDim": "#88CEFE", - "onPrimaryFixedVariant": "#004C6D", - "secondaryFixed": "#CCE6FB", - "onSecondaryFixed": "#021E2D", - "secondaryFixedDim": "#B0CADF", - "onSecondaryFixedVariant": "#314A5B", - "tertiaryFixed": "#F8D8FF", - "onTertiaryFixed": "#300443", - "tertiaryFixedDim": "#E8B4FA", - "onTertiaryFixedVariant": "#603572", - "surfaceDim": "#D8DADE", - "surfaceBright": "#F7F9FD", - "surfaceContainerLowest": "#FFFFFF", - "surfaceContainerLow": "#F2F4F7", - "surfaceContainer": "#ECEEF2", - "surfaceContainerHigh": "#E6E8EC", - "surfaceContainerHighest": "#E0E2E6" - }, - "light-medium-contrast": { - "primary": "#003A55", - "surfaceTint": "#03658F", - "onPrimary": "#FFFFFF", - "primaryContainer": "#186D98", - "onPrimaryContainer": "#FFFFFF", - "secondary": "#203949", - "onSecondary": "#FFFFFF", - "secondaryContainer": "#577082", - "onSecondaryContainer": "#FFFFFF", - "tertiary": "#4E2460", - "onTertiary": "#FFFFFF", - "tertiaryContainer": "#825594", - "onTertiaryContainer": "#FFFFFF", - "error": "#740006", - "onError": "#FFFFFF", - "errorContainer": "#CF2C27", - "onErrorContainer": "#FFFFFF", - "background": "#F7F9FD", - "onBackground": "#191C1F", - "surface": "#F7F9FD", - "onSurface": "#0E1214", - "surfaceVariant": "#DCE3EB", - "onSurfaceVariant": "#2F373D", - "outline": "#4C535A", - "outlineVariant": "#666E75", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#2D3134", - "inverseOnSurface": "#EFF1F5", - "inversePrimary": "#88CEFE", - "primaryFixed": "#23749F", - "onPrimaryFixed": "#FFFFFF", - "primaryFixedDim": "#005B82", - "onPrimaryFixedVariant": "#FFFFFF", - "secondaryFixed": "#577082", - "onSecondaryFixed": "#FFFFFF", - "secondaryFixedDim": "#3F5869", - "onSecondaryFixedVariant": "#FFFFFF", - "tertiaryFixed": "#895C9B", - "onTertiaryFixed": "#FFFFFF", - "tertiaryFixedDim": "#6F4381", - "onTertiaryFixedVariant": "#FFFFFF", - "surfaceDim": "#C4C7CA", - "surfaceBright": "#F7F9FD", - "surfaceContainerLowest": "#FFFFFF", - "surfaceContainerLow": "#F2F4F7", - "surfaceContainer": "#E6E8EC", - "surfaceContainerHigh": "#DBDDE1", - "surfaceContainerHighest": "#CFD2D5" - }, - "light-high-contrast": { - "primary": "#003046", - "surfaceTint": "#03658F", - "onPrimary": "#FFFFFF", - "primaryContainer": "#004E71", - "onPrimaryContainer": "#FFFFFF", - "secondary": "#152F3F", - "onSecondary": "#FFFFFF", - "secondaryContainer": "#344C5D", - "onSecondaryContainer": "#FFFFFF", - "tertiary": "#421955", - "onTertiary": "#FFFFFF", - "tertiaryContainer": "#623874", - "onTertiaryContainer": "#FFFFFF", - "error": "#600004", - "onError": "#FFFFFF", - "errorContainer": "#98000A", - "onErrorContainer": "#FFFFFF", - "background": "#F7F9FD", - "onBackground": "#191C1F", - "surface": "#F7F9FD", - "onSurface": "#000000", - "surfaceVariant": "#DCE3EB", - "onSurfaceVariant": "#000000", - "outline": "#252D33", - "outlineVariant": "#424A51", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#2D3134", - "inverseOnSurface": "#FFFFFF", - "inversePrimary": "#88CEFE", - "primaryFixed": "#004E71", - "onPrimaryFixed": "#FFFFFF", - "primaryFixedDim": "#003650", - "onPrimaryFixedVariant": "#FFFFFF", - "secondaryFixed": "#344C5D", - "onSecondaryFixed": "#FFFFFF", - "secondaryFixedDim": "#1C3546", - "onSecondaryFixedVariant": "#FFFFFF", - "tertiaryFixed": "#623874", - "onTertiaryFixed": "#FFFFFF", - "tertiaryFixedDim": "#4A205C", - "onTertiaryFixedVariant": "#FFFFFF", - "surfaceDim": "#B6B9BD", - "surfaceBright": "#F7F9FD", - "surfaceContainerLowest": "#FFFFFF", - "surfaceContainerLow": "#EFF1F5", - "surfaceContainer": "#E0E2E6", - "surfaceContainerHigh": "#D2D4D8", - "surfaceContainerHighest": "#C4C7CA" - }, - "dark": { - "primary": "#88CEFE", - "surfaceTint": "#88CEFE", - "onPrimary": "#00344D", - "primaryContainer": "#186D98", - "onPrimaryContainer": "#CEE9FF", - "secondary": "#B0CADF", - "onSecondary": "#1A3343", - "secondaryContainer": "#314A5B", - "onSecondaryContainer": "#9FB8CD", - "tertiary": "#E8B4FA", - "onTertiary": "#471E59", - "tertiaryContainer": "#825594", - "onTertiaryContainer": "#F9DCFF", - "error": "#FFB4AB", - "onError": "#690005", - "errorContainer": "#93000A", - "onErrorContainer": "#FFDAD6", - "background": "#101417", - "onBackground": "#E0E2E6", - "surface": "#101417", - "onSurface": "#E0E2E6", - "surfaceVariant": "#40484E", - "onSurfaceVariant": "#C0C7CF", - "outline": "#8A9299", - "outlineVariant": "#40484E", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#E0E2E6", - "inverseOnSurface": "#2D3134", - "inversePrimary": "#03658F", - "primaryFixed": "#C8E6FF", - "onPrimaryFixed": "#001E2E", - "primaryFixedDim": "#88CEFE", - "onPrimaryFixedVariant": "#004C6D", - "secondaryFixed": "#CCE6FB", - "onSecondaryFixed": "#021E2D", - "secondaryFixedDim": "#B0CADF", - "onSecondaryFixedVariant": "#314A5B", - "tertiaryFixed": "#F8D8FF", - "onTertiaryFixed": "#300443", - "tertiaryFixedDim": "#E8B4FA", - "onTertiaryFixedVariant": "#603572", - "surfaceDim": "#101417", - "surfaceBright": "#363A3D", - "surfaceContainerLowest": "#0B0F11", - "surfaceContainerLow": "#191C1F", - "surfaceContainer": "#1D2023", - "surfaceContainerHigh": "#272A2D", - "surfaceContainerHighest": "#323538" - }, - "dark-medium-contrast": { - "primary": "#BBE1FF", - "surfaceTint": "#88CEFE", - "onPrimary": "#00293D", - "primaryContainer": "#5098C5", - "onPrimaryContainer": "#000000", - "secondary": "#C6E0F5", - "onSecondary": "#0D2838", - "secondaryContainer": "#7B94A7", - "onSecondaryContainer": "#000000", - "tertiary": "#F5D0FF", - "onTertiary": "#3B114E", - "tertiaryContainer": "#AF7FC1", - "onTertiaryContainer": "#000000", - "error": "#FFD2CC", - "onError": "#540003", - "errorContainer": "#FF5449", - "onErrorContainer": "#000000", - "background": "#101417", - "onBackground": "#E0E2E6", - "surface": "#101417", - "onSurface": "#FFFFFF", - "surfaceVariant": "#40484E", - "onSurfaceVariant": "#D6DDE5", - "outline": "#ABB3BB", - "outlineVariant": "#899199", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#E0E2E6", - "inverseOnSurface": "#272A2D", - "inversePrimary": "#004D6F", - "primaryFixed": "#C8E6FF", - "onPrimaryFixed": "#00131F", - "primaryFixedDim": "#88CEFE", - "onPrimaryFixedVariant": "#003A55", - "secondaryFixed": "#CCE6FB", - "onSecondaryFixed": "#00131F", - "secondaryFixedDim": "#B0CADF", - "onSecondaryFixedVariant": "#203949", - "tertiaryFixed": "#F8D8FF", - "onTertiaryFixed": "#220032", - "tertiaryFixedDim": "#E8B4FA", - "onTertiaryFixedVariant": "#4E2460", - "surfaceDim": "#101417", - "surfaceBright": "#424548", - "surfaceContainerLowest": "#05080A", - "surfaceContainerLow": "#1B1E21", - "surfaceContainer": "#25282B", - "surfaceContainerHigh": "#303336", - "surfaceContainerHighest": "#3B3E41" - }, - "dark-high-contrast": { - "primary": "#E3F2FF", - "surfaceTint": "#88CEFE", - "onPrimary": "#000000", - "primaryContainer": "#84CAFA", - "onPrimaryContainer": "#000D17", - "secondary": "#E3F2FF", - "onSecondary": "#000000", - "secondaryContainer": "#ACC6DB", - "onSecondaryContainer": "#000D17", - "tertiary": "#FDEAFF", - "onTertiary": "#000000", - "tertiaryContainer": "#E4B0F6", - "onTertiaryContainer": "#190026", - "error": "#FFECE9", - "onError": "#000000", - "errorContainer": "#FFAEA4", - "onErrorContainer": "#220001", - "background": "#101417", - "onBackground": "#E0E2E6", - "surface": "#101417", - "onSurface": "#FFFFFF", - "surfaceVariant": "#40484E", - "onSurfaceVariant": "#FFFFFF", - "outline": "#E9F1F9", - "outlineVariant": "#BCC3CB", - "shadow": "#000000", - "scrim": "#000000", - "inverseSurface": "#E0E2E6", - "inverseOnSurface": "#000000", - "inversePrimary": "#004D6F", - "primaryFixed": "#C8E6FF", - "onPrimaryFixed": "#000000", - "primaryFixedDim": "#88CEFE", - "onPrimaryFixedVariant": "#00131F", - "secondaryFixed": "#CCE6FB", - "onSecondaryFixed": "#000000", - "secondaryFixedDim": "#B0CADF", - "onSecondaryFixedVariant": "#00131F", - "tertiaryFixed": "#F8D8FF", - "onTertiaryFixed": "#000000", - "tertiaryFixedDim": "#E8B4FA", - "onTertiaryFixedVariant": "#220032", - "surfaceDim": "#101417", - "surfaceBright": "#4D5054", - "surfaceContainerLowest": "#000000", - "surfaceContainerLow": "#1D2023", - "surfaceContainer": "#2D3134", - "surfaceContainerHigh": "#383C3F", - "surfaceContainerHighest": "#44474A" - } - }, - "palettes": { - "primary": { - "0": "#000000", - "5": "#00131F", - "10": "#001E2E", - "15": "#00293D", - "20": "#00344D", - "25": "#00405D", - "30": "#004C6D", - "35": "#00587E", - "40": "#03658F", - "50": "#317EAA", - "60": "#5098C5", - "70": "#6CB3E1", - "80": "#88CEFE", - "90": "#C8E6FF", - "95": "#E5F2FF", - "98": "#F6FAFF", - "99": "#FBFCFF", - "100": "#FFFFFF" - }, - "secondary": { - "0": "#000000", - "5": "#05121B", - "10": "#0F1D26", - "15": "#1A2731", - "20": "#25323C", - "25": "#303D47", - "30": "#3B4853", - "35": "#46545F", - "40": "#52606B", - "50": "#6B7984", - "60": "#84929E", - "70": "#9FADB9", - "80": "#BAC8D5", - "90": "#D6E4F2", - "95": "#E5F2FF", - "98": "#F6FAFF", - "99": "#FBFCFF", - "100": "#FFFFFF" - }, - "tertiary": { - "0": "#000000", - "5": "#140D25", - "10": "#1E1730", - "15": "#29223B", - "20": "#342C46", - "25": "#3F3752", - "30": "#4B425E", - "35": "#564E6A", - "40": "#635A76", - "50": "#7C7290", - "60": "#968CAB", - "70": "#B1A6C6", - "80": "#CDC1E2", - "90": "#E9DDFF", - "95": "#F6EDFF", - "98": "#FEF7FF", - "99": "#FFFBFF", - "100": "#FFFFFF" - }, - "neutral": { - "0": "#000000", - "5": "#0F1113", - "10": "#1A1C1E", - "15": "#242628", - "20": "#2F3132", - "25": "#3A3B3D", - "30": "#454749", - "35": "#515254", - "40": "#5D5E60", - "50": "#767779", - "60": "#909193", - "70": "#AAABAD", - "80": "#C6C6C8", - "90": "#E2E2E4", - "95": "#F1F0F3", - "98": "#F9F9FB", - "99": "#FCFCFE", - "100": "#FFFFFF" - }, - "neutral-variant": { - "0": "#000000", - "5": "#0C1215", - "10": "#171C20", - "15": "#21262B", - "20": "#2C3135", - "25": "#373C40", - "30": "#42474C", - "35": "#4E5358", - "40": "#5A5F64", - "50": "#73787D", - "60": "#8C9196", - "70": "#A7ACB1", - "80": "#C2C7CC", - "90": "#DEE3E8", - "95": "#EDF1F7", - "98": "#F6FAFF", - "99": "#FBFCFF", - "100": "#FFFFFF" - } - } -} diff --git a/ui/theme/manager.py b/ui/theme/manager.py index c7dd819..05bc181 100644 --- a/ui/theme/manager.py +++ b/ui/theme/manager.py @@ -4,17 +4,17 @@ from typing import overload import structlog from PySide6.QtCore import Property, QObject, QResource, Qt, Signal -from PySide6.QtGui import QColor, QGuiApplication, QPalette +from PySide6.QtGui import QGuiApplication, QPalette from .material3 import Material3DynamicThemeImpl, Material3ThemeImpl from .qml import ThemeQmlExposer -from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme +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[ThemeInfo, ThemeImpl] = {} +_THEME_CACHES: dict[TThemeInfoCacheKey, ThemeImpl] = {} logger: structlog.stdlib.BoundLogger = structlog.get_logger() @@ -27,30 +27,18 @@ class ThemeManager(QObject): super().__init__(parent) self._qPalette = QPalette() - self._customPalette: _TCustomPalette = { - "primary": QColor.fromString("#616161"), - "success": QColor.fromString("#616161"), - "error": QColor.fromString("#616161"), - } + self._customPalette: _TCustomPalette = ThemeImpl.DEFAULT_CUSTOM_PALETTE - self._lastThemeInfo = ThemeInfo( - series="material3", - name="default", - scheme=self.getCurrentScheme(), - ) + self._lastThemeInfo = ThemeImpl().info self._qmlExposer = ThemeQmlExposer(themeImpl=ThemeImpl()) - self._cacheMaterial3Theme(themeName="default", scheme="light") - self._cacheMaterial3Theme(themeName="default", scheme="dark") - self._cacheMaterial3Theme(themeName="tempest", scheme="light") - self._cacheMaterial3Theme(themeName="tempest", scheme="dark") + self._cacheMaterial3DynamicTheme(themeId="default") + self._cacheMaterial3DynamicTheme(themeId="tempest") - self._cacheMaterial3DynamicTheme(themeName="default", scheme="light") - self._cacheMaterial3DynamicTheme(themeName="default", scheme="dark") - self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="light") - self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="dark") + self._cacheMaterial3DynamicTheme(themeId="devilun") + self._cacheMaterial3DynamicTheme(themeId="kupya") - self.setTheme("material3-dynamic", "default") + self.setTheme("material3-dynamic", "devilun") def getCurrentScheme(self) -> _TScheme: qApp: QGuiApplication = QGuiApplication.instance() # pyright: ignore[reportAssignmentType] @@ -60,52 +48,47 @@ class ThemeManager(QObject): else "light" ) - def getMaterial3Theme(self, themeName: str, scheme: _TScheme) -> Material3ThemeImpl: - themeDataResource = QResource(f":/themes/{themeName}.json") - if not themeDataResource.isValid(): - raise ValueError(f"Material3 theme {themeName!r} not found") + def _loadQResourceJson(self, resourcePath: str): + resource = QResource(resourcePath) + if not resource.isValid(): + raise ValueError(f"Resource {resourcePath!r} invalid") - themeData = json.loads( - themeDataResource.uncompressedData().data().decode("utf-8") - ) + 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, themeName: str, scheme: _TScheme + self, themeId: str, scheme: _TScheme ) -> Material3DynamicThemeImpl: - themeDataResource = QResource(f":/themes/{themeName}.json") - if not themeDataResource.isValid(): - raise ValueError(f"Material3 theme {themeName!r} not found") - - themeData = json.loads( - themeDataResource.uncompressedData().data().decode("utf-8") - ) - - return Material3DynamicThemeImpl( - sourceColorHex=themeData["seed"], - scheme=scheme, - name=themeName, - ) + themeData = self._loadQResourceJson(f":/themes/m3-dynamic_{themeId}.json") + return Material3DynamicThemeImpl(themeData=themeData, scheme=scheme) def _cacheTheme(self, *, themeImpl: ThemeImpl): - _THEME_CACHES[themeImpl.info] = themeImpl + _THEME_CACHES[themeImpl.info.cacheKey()] = themeImpl logger.debug("Theme %r cached", themeImpl.info) - def _getCachedTheme(self, *, themeInfo: ThemeInfo): - cachedTheme = _THEME_CACHES.get(themeInfo) + def _getCachedTheme(self, *, key: TThemeInfoCacheKey): + cachedTheme = _THEME_CACHES.get(key) if cachedTheme is None: - raise KeyError(f"Theme {themeInfo!r} not cached") + raise KeyError(f"Theme {key!r} not cached") return cachedTheme - def _cacheMaterial3Theme(self, *, themeName: str, scheme: _TScheme): + def _cacheMaterial3Theme(self, *, themeId: str): self._cacheTheme( - themeImpl=self.getMaterial3Theme(themeName=themeName, scheme=scheme), + themeImpl=self.getMaterial3Theme(themeId=themeId, scheme="light"), + ) + self._cacheTheme( + themeImpl=self.getMaterial3Theme(themeId=themeId, scheme="dark"), ) - def _cacheMaterial3DynamicTheme(self, *, themeName: str, scheme: _TScheme): + def _cacheMaterial3DynamicTheme(self, *, themeId: str): self._cacheTheme( - themeImpl=self.getMaterial3DynamicTheme(themeName=themeName, scheme=scheme) + themeImpl=self.getMaterial3DynamicTheme(themeId=themeId, scheme="light") + ) + self._cacheTheme( + themeImpl=self.getMaterial3DynamicTheme(themeId=themeId, scheme="dark") ) @overload @@ -113,29 +96,29 @@ class ThemeManager(QObject): @overload def setTheme( - self, themeSeries: str, themeName: str, scheme: _TScheme | None = None, / + self, themeSeries: str, themeId: str, scheme: _TScheme | None = None, / ): ... def setTheme(self, *args, **kwargs): if "themeInfo" in kwargs: - themeInfo = kwargs["themeInfo"] + cacheKey = kwargs["themeInfo"].cacheKey() elif 2 <= len(args) <= 3: themeSeries = args[0] - themeName = args[1] + themeId = args[1] schemeArg = args[2] if len(args) > 2 else None scheme = schemeArg or self.getCurrentScheme() - themeInfo = ThemeInfo(series=themeSeries, name=themeName, scheme=scheme) + cacheKey: TThemeInfoCacheKey = (themeSeries, themeId, scheme) else: raise TypeError("Invalid setTheme() call") - logger.debug("Preparing to set theme %r", themeInfo) + logger.debug("Preparing to set theme %r", cacheKey) - cachedTheme = self._getCachedTheme(themeInfo=themeInfo) + cachedTheme = self._getCachedTheme(key=cacheKey) self._qPalette = cachedTheme.qPalette self._customPalette = cachedTheme.customPalette - self._lastThemeInfo = themeInfo + self._lastThemeInfo = cachedTheme.info self._qmlExposer.themeImpl = cachedTheme self.themeChanged.emit() diff --git a/ui/theme/material3.py b/ui/theme/material3.py index 16f58f5..6b41182 100644 --- a/ui/theme/material3.py +++ b/ui/theme/material3.py @@ -1,5 +1,3 @@ -from typing import TypedDict - from materialyoucolor.blend import Blend from materialyoucolor.dynamiccolor.contrast_curve import ContrastCurve from materialyoucolor.dynamiccolor.dynamic_color import DynamicColor, FromPaletteOptions @@ -10,48 +8,12 @@ from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot from PySide6.QtGui import QColor, QPalette from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme - - -class _M3ThemeDataExtendedColorItem(TypedDict): - name: str - color: str - description: str - harmonized: bool - - -_M3ThemeDataSchemes = TypedDict( - "_M3ThemeDataSchemes", - { - "light": dict[str, str], - "light-medium-contrast": dict[str, str], - "light-high-contrast": dict[str, str], - "dark": dict[str, str], - "dark-medium-contrast": dict[str, str], - "dark-high-contrast": dict[str, str], - }, +from .types import ( + TMaterial3DynamicThemeData, + TMaterial3ThemeData, + TMaterial3ThemeDataExtendedColorItem, ) -_M3ThemeDataPalettes = TypedDict( - "_M3ThemeDataPalettes", - { - "primary": dict[str, str], - "secondary": dict[str, str], - "tertiary": dict[str, str], - "neutral": dict[str, str], - "neutral-variant": dict[str, str], - }, -) - - -class _M3ThemeData(TypedDict): - name: str - description: str - seed: str - coreColors: dict[str, str] - extendedColors: list[_M3ThemeDataExtendedColorItem] - schemes: _M3ThemeDataSchemes - palettes: _M3ThemeDataPalettes - def _hexToHct(hexColor: str) -> Hct: pureHexPart = hexColor[1:] if hexColor.startswith("#") else hexColor @@ -87,7 +49,7 @@ class Material3ThemeImpl(ThemeImpl): QPalette.ColorRole.LinkVisited: "tertiaryContainer", } - def __init__(self, *, themeData: _M3ThemeData, scheme: _TScheme): + def __init__(self, *, themeData: TMaterial3ThemeData, scheme: _TScheme): self.themeData = themeData self.scheme: _TScheme = scheme @@ -96,7 +58,7 @@ class Material3ThemeImpl(ThemeImpl): def _findExtendedColor( self, colorName: str - ) -> _M3ThemeDataExtendedColorItem | None: + ) -> TMaterial3ThemeDataExtendedColorItem | None: return next( (it for it in self.themeData["extendedColors"] if it["name"] == colorName), None, @@ -106,6 +68,7 @@ class Material3ThemeImpl(ThemeImpl): def info(self): return ThemeInfo( series="material3", + id=self.themeData["id"], name=self.themeData["name"], scheme=self.scheme, ) @@ -137,6 +100,8 @@ class Material3ThemeImpl(ThemeImpl): "primary": _hctToQColor(primaryHct), "success": _hctToQColor(successHarmonizedHct), "error": QColor.fromString(self.themeData["schemes"][self.scheme]["error"]), + "toolTipBase": self.qPalette.color(QPalette.ColorRole.ToolTipBase), + "toolTipText": self.qPalette.color(QPalette.ColorRole.ToolTipText), } @@ -169,20 +134,45 @@ class Material3DynamicThemeImpl(ThemeImpl): "success": "#00c555", } - def __init__(self, sourceColorHex: str, scheme: _TScheme, *, name: str): + def __init__(self, themeData: TMaterial3DynamicThemeData, scheme: _TScheme): + force_scheme = themeData["options"].get("forceScheme") + if force_scheme: + is_dark = force_scheme == "dark" + else: + is_dark = scheme == "dark" + + # TODO: more elegant way? + self.preferredScheme: _TScheme = scheme # for theme caching + self.actualScheme = "dark" if is_dark else "light" + + self.themeId = themeData["id"] + self.themeName = themeData["name"] + self.material3Scheme = SchemeTonalSpot( - _hexToHct(sourceColorHex), - is_dark=scheme == "dark", + _hexToHct(themeData["colors"]["primary"]), + is_dark=is_dark, contrast_level=0.0, ) - self.name = name + + secondary_color = themeData["colors"].get("secondary") + if secondary_color: + self.material3Scheme.secondary_palette = TonalPalette.from_hct( + _hexToHct(secondary_color) + ) + + tertiary_color = themeData["colors"].get("tertiary") + if tertiary_color: + self.material3Scheme.tertiary_palette = TonalPalette.from_hct( + _hexToHct(tertiary_color) + ) @property def info(self): return ThemeInfo( series="material3-dynamic", - name=self.name, - scheme="dark" if self.material3Scheme.is_dark else "light", + id=self.themeId, + name=self.themeName, + scheme=self.preferredScheme, ) @property @@ -242,4 +232,6 @@ class Material3DynamicThemeImpl(ThemeImpl): extendedPalettes["success"].get_hct(self.material3Scheme) ), "error": _hctToQColor(errorHct), + "toolTipBase": self.qPalette.color(QPalette.ColorRole.ToolTipBase), + "toolTipText": self.qPalette.color(QPalette.ColorRole.ToolTipText), } diff --git a/ui/theme/qml.py b/ui/theme/qml.py index 6209067..05fe455 100644 --- a/ui/theme/qml.py +++ b/ui/theme/qml.py @@ -35,3 +35,11 @@ class ThemeQmlExposer(QObject): @Property(QColor, notify=themeChanged) def error(self): return self._themeImpl.customPalette["error"] + + @Property(QColor, notify=themeChanged) + def toolTipBase(self): + return self._themeImpl.customPalette["toolTipBase"] + + @Property(QColor, notify=themeChanged) + def toolTipText(self): + return self._themeImpl.customPalette["toolTipText"] diff --git a/ui/theme/shared.py b/ui/theme/shared.py index e323dc1..9c471f5 100644 --- a/ui/theme/shared.py +++ b/ui/theme/shared.py @@ -9,30 +9,43 @@ class _TCustomPalette(TypedDict): success: QColor error: QColor + toolTipBase: QColor + toolTipText: QColor + _TScheme = Literal["light", "dark"] +TThemeInfoCacheKey = tuple[str, str, _TScheme] + @dataclass class ThemeInfo: series: str + id: str name: str scheme: _TScheme - def __hash__(self) -> int: - return hash((self.series, self.name, self.scheme)) + def cacheKey(self) -> TThemeInfoCacheKey: + return (self.series, self.id, self.scheme) class ThemeImpl: - DEFAULT_CUSTOM_PALETTE = { + DEFAULT_CUSTOM_PALETTE: _TCustomPalette = { "primary": QColor.fromString("#616161"), "success": QColor.fromString("#616161"), "error": QColor.fromString("#616161"), + "toolTipBase": QColor.fromString("#616161"), + "toolTipText": QColor.fromString("#616161"), } @property def info(self) -> ThemeInfo: - return ThemeInfo(series="placeholder", name="placeholder", scheme="dark") + return ThemeInfo( + series="placeholder", + id="placeholder", + name="placeholder", + scheme="dark", + ) @property def qPalette(self) -> QPalette: diff --git a/ui/theme/types.py b/ui/theme/types.py new file mode 100644 index 0000000..5760bfe --- /dev/null +++ b/ui/theme/types.py @@ -0,0 +1,72 @@ +from typing import TypedDict + +from .shared import _TScheme + +# region material3 + + +class TMaterial3ThemeDataExtendedColorItem(TypedDict): + name: str + color: str + description: str + harmonized: bool + + +TMaterial3ThemeDataSchemes = TypedDict( + "TMaterial3ThemeDataSchemes", + { + "light": dict[str, str], + "light-medium-contrast": dict[str, str], + "light-high-contrast": dict[str, str], + "dark": dict[str, str], + "dark-medium-contrast": dict[str, str], + "dark-high-contrast": dict[str, str], + }, +) + +TMaterial3ThemeDataPalettes = TypedDict( + "TMaterial3ThemeDataPalettes", + { + "primary": dict[str, str], + "secondary": dict[str, str], + "tertiary": dict[str, str], + "neutral": dict[str, str], + "neutral-variant": dict[str, str], + }, +) + + +class TMaterial3ThemeData(TypedDict): + id: str + name: str + description: str + seed: str + coreColors: dict[str, str] + extendedColors: list[TMaterial3ThemeDataExtendedColorItem] + schemes: TMaterial3ThemeDataSchemes + palettes: TMaterial3ThemeDataPalettes + + +# endregion + +# region material3-dynamic + + +class TMaterial3DynamicThemeDataColors(TypedDict): + primary: str + secondary: str | None + tertiary: str | None + + +class TMaterial3DynamicThemeDataOptions(TypedDict): + forceScheme: _TScheme | None + + +class TMaterial3DynamicThemeData(TypedDict): + id: str + name: str + colors: TMaterial3DynamicThemeDataColors + options: TMaterial3DynamicThemeDataOptions + + +# endregion