diff --git a/core/internal/server/themes/list.go b/core/internal/server/themes/list.go index 2f60b5cd..9564be1e 100644 --- a/core/internal/server/themes/list.go +++ b/core/internal/server/themes/list.go @@ -42,16 +42,7 @@ func HandleList(conn net.Conn, req models.Request) { Installed: installed, FirstParty: isFirstParty(t.Author), } - if t.Variants != nil && len(t.Variants.Options) > 0 { - info.HasVariants = true - info.Variants = &VariantsInfo{ - Default: t.Variants.Default, - Options: make([]VariantInfo, len(t.Variants.Options)), - } - for j, v := range t.Variants.Options { - info.Variants.Options[j] = VariantInfo{ID: v.ID, Name: v.Name} - } - } + addVariantsInfo(&info, t.Variants) result[i] = info } diff --git a/core/internal/server/themes/list_installed.go b/core/internal/server/themes/list_installed.go index aee480b7..4f956174 100644 --- a/core/internal/server/themes/list_installed.go +++ b/core/internal/server/themes/list_installed.go @@ -9,7 +9,64 @@ import ( ) func addVariantsInfo(info *ThemeInfo, variants *themes.ThemeVariants) { - if variants == nil || len(variants.Options) == 0 { + if variants == nil { + return + } + + if variants.Type == "multi" { + if len(variants.Flavors) == 0 && len(variants.Accents) == 0 { + return + } + info.HasVariants = true + info.Variants = &VariantsInfo{ + Type: "multi", + Flavors: make([]FlavorInfo, len(variants.Flavors)), + Accents: make([]AccentInfo, len(variants.Accents)), + } + if variants.Defaults != nil { + info.Variants.Defaults = &MultiDefaults{ + Dark: variants.Defaults.Dark, + Light: variants.Defaults.Light, + } + } + for i, f := range variants.Flavors { + mode := "" + switch { + case f.Dark.Primary != "" && f.Light.Primary != "": + mode = "both" + case f.Dark.Primary != "": + mode = "dark" + case f.Light.Primary != "": + mode = "light" + default: + if f.Dark.Surface != "" { + mode = "dark" + } else if f.Light.Surface != "" { + mode = "light" + } + } + info.Variants.Flavors[i] = FlavorInfo{ID: f.ID, Name: f.Name, Mode: mode} + } + for i, a := range variants.Accents { + color := "" + if colors, ok := a.FlavorColors["mocha"]; ok && colors.Primary != "" { + color = colors.Primary + } else if colors, ok := a.FlavorColors["latte"]; ok && colors.Primary != "" { + color = colors.Primary + } else { + for _, c := range a.FlavorColors { + if c.Primary != "" { + color = c.Primary + break + } + } + } + info.Variants.Accents[i] = AccentInfo{ID: a.ID, Name: a.Name, Color: color} + } + return + } + + if len(variants.Options) == 0 { return } info.HasVariants = true diff --git a/core/internal/server/themes/types.go b/core/internal/server/themes/types.go index 04f8960c..0ad19b4f 100644 --- a/core/internal/server/themes/types.go +++ b/core/internal/server/themes/types.go @@ -5,9 +5,30 @@ type VariantInfo struct { Name string `json:"name"` } +type FlavorInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Mode string `json:"mode,omitempty"` +} + +type AccentInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Color string `json:"color,omitempty"` +} + +type MultiDefaults struct { + Dark map[string]string `json:"dark,omitempty"` + Light map[string]string `json:"light,omitempty"` +} + type VariantsInfo struct { - Default string `json:"default,omitempty"` - Options []VariantInfo `json:"options,omitempty"` + Type string `json:"type,omitempty"` + Default string `json:"default,omitempty"` + Defaults *MultiDefaults `json:"defaults,omitempty"` + Options []VariantInfo `json:"options,omitempty"` + Flavors []FlavorInfo `json:"flavors,omitempty"` + Accents []AccentInfo `json:"accents,omitempty"` } type ThemeInfo struct { diff --git a/core/internal/themes/registry.go b/core/internal/themes/registry.go index 4a32ba3c..401eb605 100644 --- a/core/internal/themes/registry.go +++ b/core/internal/themes/registry.go @@ -2,6 +2,7 @@ package themes import ( "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -40,9 +41,67 @@ type ThemeVariant struct { Light ColorScheme `json:"light,omitempty"` } +type ThemeFlavor struct { + ID string `json:"id"` + Name string `json:"name"` + Dark ColorScheme `json:"dark,omitempty"` + Light ColorScheme `json:"light,omitempty"` +} + +type ThemeAccent struct { + ID string `json:"id"` + Name string `json:"name"` + FlavorColors map[string]ColorScheme `json:"-"` +} + +func (a *ThemeAccent) UnmarshalJSON(data []byte) error { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + a.FlavorColors = make(map[string]ColorScheme) + var mErr error + for key, value := range raw { + switch key { + case "id": + mErr = errors.Join(mErr, json.Unmarshal(value, &a.ID)) + case "name": + mErr = errors.Join(mErr, json.Unmarshal(value, &a.Name)) + default: + var colors ColorScheme + if err := json.Unmarshal(value, &colors); err == nil { + a.FlavorColors[key] = colors + } else { + mErr = errors.Join(mErr, fmt.Errorf("failed to unmarshal flavor colors for key %s: %w", key, err)) + } + } + } + return mErr +} + +func (a ThemeAccent) MarshalJSON() ([]byte, error) { + m := map[string]any{ + "id": a.ID, + "name": a.Name, + } + for k, v := range a.FlavorColors { + m[k] = v + } + return json.Marshal(m) +} + +type MultiVariantDefaults struct { + Dark map[string]string `json:"dark,omitempty"` + Light map[string]string `json:"light,omitempty"` +} + type ThemeVariants struct { - Default string `json:"default,omitempty"` - Options []ThemeVariant `json:"options,omitempty"` + Type string `json:"type,omitempty"` + Default string `json:"default,omitempty"` + Defaults *MultiVariantDefaults `json:"defaults,omitempty"` + Options []ThemeVariant `json:"options,omitempty"` + Flavors []ThemeFlavor `json:"flavors,omitempty"` + Accents []ThemeAccent `json:"accents,omitempty"` } type Theme struct { diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 5e287f51..cf95032e 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -1560,7 +1560,10 @@ Singleton { } function getRegistryThemeVariant(themeId, defaultVariant) { - return registryThemeVariants[themeId] || defaultVariant || ""; + var stored = registryThemeVariants[themeId]; + if (typeof stored === "string") + return stored || defaultVariant || ""; + return defaultVariant || ""; } function setRegistryThemeVariant(themeId, variantId) { @@ -1572,6 +1575,25 @@ Singleton { Theme.reloadCustomThemeVariant(); } + function getRegistryThemeMultiVariant(themeId, defaults) { + var stored = registryThemeVariants[themeId]; + if (stored && typeof stored === "object") + return stored; + return defaults || {}; + } + + function setRegistryThemeMultiVariant(themeId, flavor, accent) { + var variants = JSON.parse(JSON.stringify(registryThemeVariants)); + variants[themeId] = { + flavor: flavor, + accent: accent + }; + registryThemeVariants = variants; + saveSettings(); + if (typeof Theme !== "undefined") + Theme.reloadCustomThemeVariant(); + } + function toggleDankBarVisible() { const defaultBar = barConfigs[0] || getBarConfig("default"); if (defaultBar) { diff --git a/quickshell/Common/StockThemes.js b/quickshell/Common/StockThemes.js index 5c73d412..154b224c 100644 --- a/quickshell/Common/StockThemes.js +++ b/quickshell/Common/StockThemes.js @@ -1,545 +1,430 @@ // Stock theme definitions for DankMaterialShell // Separated from Theme.qml to keep that file clean -const CatppuccinMocha = { - surface: "#181825", - surfaceText: "#cdd6f4", - surfaceVariant: "#1e1e2e", - surfaceVariantText: "#a6adc8", - background: "#181825", - backgroundText: "#cdd6f4", - outline: "#6c7086", - surfaceContainer: "#1e1e2e", - surfaceContainerHigh: "#313244", - surfaceContainerHighest: "#45475a" -} - -const CatppuccinLatte = { - surface: "#e6e9ef", - surfaceText: "#4c4f69", - surfaceVariant: "#e6e9ef", - surfaceVariantText: "#6c6f85", - background: "#eff1f5", - backgroundText: "#4c4f69", - outline: "#9ca0b0", - surfaceContainer: "#dce0e8", - surfaceContainerHigh: "#ccd0da", - surfaceContainerHighest: "#bcc0cc" -} - -const CatppuccinVariants = { - "cat-rosewater": { - name: "Rosewater", - dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#7d5d56", surfaceTint: "#f5e0dc" }, - light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f6e7e3", surfaceTint: "#dc8a78" } - }, - "cat-flamingo": { - name: "Flamingo", - dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#7a555a", surfaceTint: "#f2cdcd" }, - light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f6e5e5", surfaceTint: "#dd7878" } - }, - "cat-pink": { - name: "Pink", - dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#7a3f69", surfaceTint: "#f5c2e7" }, - light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7d7ee", surfaceTint: "#ea76cb" } - }, - "cat-mauve": { - name: "Mauve", - dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#55307f", surfaceTint: "#cba6f7" }, - light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#eadcff", surfaceTint: "#8839ef" } - }, - "cat-red": { - name: "Red", - dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#6f2438", surfaceTint: "#f38ba8" }, - light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f6d0d6", surfaceTint: "#d20f39" } - }, - "cat-maroon": { - name: "Maroon", - dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#6d3641", surfaceTint: "#eba0ac" }, - light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f7d8dc", surfaceTint: "#e64553" } - }, - "cat-peach": { - name: "Peach", - dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#734226", surfaceTint: "#fab387" }, - light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffe4d5", surfaceTint: "#fe640b" } - }, - "cat-yellow": { - name: "Yellow", - dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#6e5a2f", surfaceTint: "#f9e2af" }, - light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff6d6", surfaceTint: "#df8e1d" } - }, - "cat-green": { - name: "Green", - dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#2f5f36", surfaceTint: "#a6e3a1" }, - light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#dff4e0", surfaceTint: "#40a02b" } - }, - "cat-teal": { - name: "Teal", - dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2e5e59", surfaceTint: "#94e2d5" }, - light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#daf3f1", surfaceTint: "#179299" } - }, - "cat-sky": { - name: "Sky", - dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#24586a", surfaceTint: "#89dceb" }, - light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#dbf1fb", surfaceTint: "#04a5e5" } - }, - "cat-sapphire": { - name: "Sapphire", - dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#1f4d6f", surfaceTint: "#74c7ec" }, - light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#def3f8", surfaceTint: "#209fb5" } - }, - "cat-blue": { - name: "Blue", - dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#243f75", surfaceTint: "#89b4fa" }, - light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e0e9ff", surfaceTint: "#1e66f5" } - }, - "cat-lavender": { - name: "Lavender", - dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#3f4481", surfaceTint: "#b4befe" }, - light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#e5e8ff", surfaceTint: "#7287fd" } - } -} - -function getCatppuccinTheme(variant, isLight = false) { - const variantData = CatppuccinVariants[variant] - if (!variantData) return null - - const baseColors = isLight ? CatppuccinLatte : CatppuccinMocha - const accentColors = isLight ? variantData.light : variantData.dark - - return Object.assign({ - name: `${variantData.name}${isLight ? ' Light' : ''}` - }, baseColors, accentColors) -} - const StockThemes = { - DARK: { - blue: { - name: "Blue", - primary: "#42a5f5", - primaryText: "#000000", - primaryContainer: "#0d47a1", - secondary: "#8ab4f8", - surface: "#101418", - surfaceText: "#e0e2e8", - surfaceVariant: "#42474e", - surfaceVariantText: "#c2c7cf", - surfaceTint: "#8ab4f8", - background: "#101418", - backgroundText: "#e0e2e8", - outline: "#8c9199", - surfaceContainer: "#1d2024", - surfaceContainerHigh: "#272a2f", - surfaceContainerHighest: "#32353a" - }, - purple: { - name: "Purple", - primary: "#D0BCFF", - primaryText: "#381E72", - primaryContainer: "#4F378B", - secondary: "#CCC2DC", - surface: "#141218", - surfaceText: "#e6e0e9", - surfaceVariant: "#49454e", - surfaceVariantText: "#cac4cf", - surfaceTint: "#D0BCFF", - background: "#141218", - backgroundText: "#e6e0e9", - outline: "#948f99", - surfaceContainer: "#211f24", - surfaceContainerHigh: "#2b292f", - surfaceContainerHighest: "#36343a" - }, - green: { - name: "Green", - primary: "#4caf50", - primaryText: "#000000", - primaryContainer: "#1b5e20", - secondary: "#81c995", - surface: "#10140f", - surfaceText: "#e0e4db", - surfaceVariant: "#424940", - surfaceVariantText: "#c2c9bd", - surfaceTint: "#81c995", - background: "#10140f", - backgroundText: "#e0e4db", - outline: "#8c9388", - surfaceContainer: "#1d211b", - surfaceContainerHigh: "#272b25", - surfaceContainerHighest: "#323630" - }, - orange: { - name: "Orange", - primary: "#ff6d00", - primaryText: "#000000", - primaryContainer: "#3e2723", - secondary: "#ffb74d", - surface: "#1a120e", - surfaceText: "#f0dfd8", - surfaceVariant: "#52443d", - surfaceVariantText: "#d7c2b9", - surfaceTint: "#ffb74d", - background: "#1a120e", - backgroundText: "#f0dfd8", - outline: "#a08d85", - surfaceContainer: "#271e1a", - surfaceContainerHigh: "#322824", - surfaceContainerHighest: "#3d332e" - }, - red: { - name: "Red", - primary: "#f44336", - primaryText: "#000000", - primaryContainer: "#4a0e0e", - secondary: "#f28b82", - surface: "#1a1110", - surfaceText: "#f1dedc", - surfaceVariant: "#534341", - surfaceVariantText: "#d8c2be", - surfaceTint: "#f28b82", - background: "#1a1110", - backgroundText: "#f1dedc", - outline: "#a08c89", - surfaceContainer: "#271d1c", - surfaceContainerHigh: "#322826", - surfaceContainerHighest: "#3d3231" - }, - cyan: { - name: "Cyan", - primary: "#00bcd4", - primaryText: "#000000", - primaryContainer: "#004d5c", - secondary: "#4dd0e1", - surface: "#0e1416", - surfaceText: "#dee3e5", - surfaceVariant: "#3f484a", - surfaceVariantText: "#bfc8ca", - surfaceTint: "#4dd0e1", - background: "#0e1416", - backgroundText: "#dee3e5", - outline: "#899295", - surfaceContainer: "#1b2122", - surfaceContainerHigh: "#252b2c", - surfaceContainerHighest: "#303637" - }, - pink: { - name: "Pink", - primary: "#e91e63", - primaryText: "#000000", - primaryContainer: "#4a0e2f", - secondary: "#f8bbd9", - surface: "#191112", - surfaceText: "#f0dee0", - surfaceVariant: "#524345", - surfaceVariantText: "#d6c2c3", - surfaceTint: "#f8bbd9", - background: "#191112", - backgroundText: "#f0dee0", - outline: "#9f8c8e", - surfaceContainer: "#261d1e", - surfaceContainerHigh: "#312829", - surfaceContainerHighest: "#3c3233" - }, - amber: { - name: "Amber", - primary: "#ffc107", - primaryText: "#000000", - primaryContainer: "#4a3c00", - secondary: "#ffd54f", - surface: "#17130b", - surfaceText: "#ebe1d4", - surfaceVariant: "#4d4639", - surfaceVariantText: "#d0c5b4", - surfaceTint: "#ffd54f", - background: "#17130b", - backgroundText: "#ebe1d4", - outline: "#998f80", - surfaceContainer: "#231f17", - surfaceContainerHigh: "#2e2921", - surfaceContainerHighest: "#39342b" - }, - coral: { - name: "Coral", - primary: "#ffb4ab", - primaryText: "#000000", - primaryContainer: "#8c1d18", - secondary: "#f9dedc", - surface: "#1a1110", - surfaceText: "#f1dedc", - surfaceVariant: "#534341", - surfaceVariantText: "#d8c2bf", - surfaceTint: "#ffb4ab", - background: "#1a1110", - backgroundText: "#f1dedc", - outline: "#a08c8a", - surfaceContainer: "#271d1c", - surfaceContainerHigh: "#322826", - surfaceContainerHighest: "#3d3231" - }, - monochrome: { - name: "Monochrome", - primary: "#ffffff", - primaryText: "#2b303c", - primaryContainer: "#424753", - secondary: "#c4c6d0", - surface: "#2a2a2a", - surfaceText: "#e4e2e3", - surfaceVariant: "#474648", - surfaceVariantText: "#c8c6c7", - surfaceTint: "#c2c6d6", - background: "#131315", - backgroundText: "#e4e2e3", - outline: "#929092", - surfaceContainer: "#353535", - surfaceContainerHigh: "#424242", - surfaceContainerHighest: "#505050", - error: "#ffb4ab", - warning: "#3f4759", - info: "#595e6c", - matugen_type: "scheme-monochrome" - } + DARK: { + blue: { + name: "Blue", + primary: "#42a5f5", + primaryText: "#000000", + primaryContainer: "#0d47a1", + secondary: "#8ab4f8", + surface: "#101418", + surfaceText: "#e0e2e8", + surfaceVariant: "#42474e", + surfaceVariantText: "#c2c7cf", + surfaceTint: "#8ab4f8", + background: "#101418", + backgroundText: "#e0e2e8", + outline: "#8c9199", + surfaceContainer: "#1d2024", + surfaceContainerHigh: "#272a2f", + surfaceContainerHighest: "#32353a", }, - LIGHT: { - blue: { - name: "Blue Light", - primary: "#1976d2", - primaryText: "#ffffff", - primaryContainer: "#e3f2fd", - secondary: "#42a5f5", - surface: "#f7f9ff", - surfaceText: "#181c20", - surfaceVariant: "#dee3eb", - surfaceVariantText: "#42474e", - surfaceTint: "#1976d2", - background: "#f7f9ff", - backgroundText: "#181c20", - outline: "#72777f", - surfaceContainer: "#eceef4", - surfaceContainerHigh: "#e6e8ee", - surfaceContainerHighest: "#e0e2e8" - }, - purple: { - name: "Purple Light", - primary: "#6750A4", - primaryText: "#ffffff", - primaryContainer: "#EADDFF", - secondary: "#625B71", - surface: "#fef7ff", - surfaceText: "#1d1b20", - surfaceVariant: "#e7e0eb", - surfaceVariantText: "#49454e", - surfaceTint: "#6750A4", - background: "#fef7ff", - backgroundText: "#1d1b20", - outline: "#7a757f", - surfaceContainer: "#f2ecf4", - surfaceContainerHigh: "#ece6ee", - surfaceContainerHighest: "#e6e0e9" - }, - green: { - name: "Green Light", - primary: "#2e7d32", - primaryText: "#ffffff", - primaryContainer: "#e8f5e8", - secondary: "#4caf50", - surface: "#f7fbf1", - surfaceText: "#191d17", - surfaceVariant: "#dee5d8", - surfaceVariantText: "#424940", - surfaceTint: "#2e7d32", - background: "#f7fbf1", - backgroundText: "#191d17", - outline: "#72796f", - surfaceContainer: "#ecefe6", - surfaceContainerHigh: "#e6e9e0", - surfaceContainerHighest: "#e0e4db" - }, - orange: { - name: "Orange Light", - primary: "#e65100", - primaryText: "#ffffff", - primaryContainer: "#ffecb3", - secondary: "#ff9800", - surface: "#fff8f6", - surfaceText: "#221a16", - surfaceVariant: "#f4ded5", - surfaceVariantText: "#52443d", - surfaceTint: "#e65100", - background: "#fff8f6", - backgroundText: "#221a16", - outline: "#85736c", - surfaceContainer: "#fceae3", - surfaceContainerHigh: "#f6e5de", - surfaceContainerHighest: "#f0dfd8" - }, - red: { - name: "Red Light", - primary: "#d32f2f", - primaryText: "#ffffff", - primaryContainer: "#ffebee", - secondary: "#f44336", - surface: "#fff8f7", - surfaceText: "#231918", - surfaceVariant: "#f5ddda", - surfaceVariantText: "#534341", - surfaceTint: "#d32f2f", - background: "#fff8f7", - backgroundText: "#231918", - outline: "#857370", - surfaceContainer: "#fceae7", - surfaceContainerHigh: "#f7e4e1", - surfaceContainerHighest: "#f1dedc" - }, - cyan: { - name: "Cyan Light", - primary: "#0097a7", - primaryText: "#ffffff", - primaryContainer: "#e0f2f1", - secondary: "#00bcd4", - surface: "#f5fafc", - surfaceText: "#171d1e", - surfaceVariant: "#dbe4e6", - surfaceVariantText: "#3f484a", - surfaceTint: "#0097a7", - background: "#f5fafc", - backgroundText: "#171d1e", - outline: "#6f797b", - surfaceContainer: "#e9eff0", - surfaceContainerHigh: "#e3e9eb", - surfaceContainerHighest: "#dee3e5" - }, - pink: { - name: "Pink Light", - primary: "#c2185b", - primaryText: "#ffffff", - primaryContainer: "#fce4ec", - secondary: "#e91e63", - surface: "#fff8f7", - surfaceText: "#22191a", - surfaceVariant: "#f3dddf", - surfaceVariantText: "#524345", - surfaceTint: "#c2185b", - background: "#fff8f7", - backgroundText: "#22191a", - outline: "#847375", - surfaceContainer: "#fbeaeb", - surfaceContainerHigh: "#f5e4e5", - surfaceContainerHighest: "#f0dee0" - }, - amber: { - name: "Amber Light", - primary: "#ff8f00", - primaryText: "#000000", - primaryContainer: "#fff8e1", - secondary: "#ffc107", - surface: "#fff8f2", - surfaceText: "#1f1b13", - surfaceVariant: "#ede1cf", - surfaceVariantText: "#4d4639", - surfaceTint: "#ff8f00", - background: "#fff8f2", - backgroundText: "#1f1b13", - outline: "#7f7667", - surfaceContainer: "#f6ecdf", - surfaceContainerHigh: "#f1e7d9", - surfaceContainerHighest: "#ebe1d4" - }, - coral: { - name: "Coral Light", - primary: "#8c1d18", - primaryText: "#ffffff", - primaryContainer: "#ffdad6", - secondary: "#ff5449", - surface: "#fff8f7", - surfaceText: "#231918", - surfaceVariant: "#f5ddda", - surfaceVariantText: "#534341", - surfaceTint: "#8c1d18", - background: "#fff8f7", - backgroundText: "#231918", - outline: "#857371", - surfaceContainer: "#fceae7", - surfaceContainerHigh: "#f6e4e2", - surfaceContainerHighest: "#f1dedc" - }, - monochrome: { - name: "Monochrome Light", - primary: "#2b303c", - primaryText: "#ffffff", - primaryContainer: "#d6d7dc", - secondary: "#4a4d56", - surface: "#f5f5f6", - surfaceText: "#2a2a2a", - surfaceVariant: "#e0e0e2", - surfaceVariantText: "#424242", - surfaceTint: "#5a5f6e", - background: "#ffffff", - backgroundText: "#1a1a1a", - outline: "#757577", - surfaceContainer: "#e8e8ea", - surfaceContainerHigh: "#dcdcde", - surfaceContainerHighest: "#d0d0d2", - error: "#ba1a1a", - warning: "#f9e79f", - info: "#5d6475", - matugen_type: "scheme-monochrome" - } - } -} + purple: { + name: "Purple", + primary: "#D0BCFF", + primaryText: "#381E72", + primaryContainer: "#4F378B", + secondary: "#CCC2DC", + surface: "#141218", + surfaceText: "#e6e0e9", + surfaceVariant: "#49454e", + surfaceVariantText: "#cac4cf", + surfaceTint: "#D0BCFF", + background: "#141218", + backgroundText: "#e6e0e9", + outline: "#948f99", + surfaceContainer: "#211f24", + surfaceContainerHigh: "#2b292f", + surfaceContainerHighest: "#36343a", + }, + green: { + name: "Green", + primary: "#4caf50", + primaryText: "#000000", + primaryContainer: "#1b5e20", + secondary: "#81c995", + surface: "#10140f", + surfaceText: "#e0e4db", + surfaceVariant: "#424940", + surfaceVariantText: "#c2c9bd", + surfaceTint: "#81c995", + background: "#10140f", + backgroundText: "#e0e4db", + outline: "#8c9388", + surfaceContainer: "#1d211b", + surfaceContainerHigh: "#272b25", + surfaceContainerHighest: "#323630", + }, + orange: { + name: "Orange", + primary: "#ff6d00", + primaryText: "#000000", + primaryContainer: "#3e2723", + secondary: "#ffb74d", + surface: "#1a120e", + surfaceText: "#f0dfd8", + surfaceVariant: "#52443d", + surfaceVariantText: "#d7c2b9", + surfaceTint: "#ffb74d", + background: "#1a120e", + backgroundText: "#f0dfd8", + outline: "#a08d85", + surfaceContainer: "#271e1a", + surfaceContainerHigh: "#322824", + surfaceContainerHighest: "#3d332e", + }, + red: { + name: "Red", + primary: "#f44336", + primaryText: "#000000", + primaryContainer: "#4a0e0e", + secondary: "#f28b82", + surface: "#1a1110", + surfaceText: "#f1dedc", + surfaceVariant: "#534341", + surfaceVariantText: "#d8c2be", + surfaceTint: "#f28b82", + background: "#1a1110", + backgroundText: "#f1dedc", + outline: "#a08c89", + surfaceContainer: "#271d1c", + surfaceContainerHigh: "#322826", + surfaceContainerHighest: "#3d3231", + }, + cyan: { + name: "Cyan", + primary: "#00bcd4", + primaryText: "#000000", + primaryContainer: "#004d5c", + secondary: "#4dd0e1", + surface: "#0e1416", + surfaceText: "#dee3e5", + surfaceVariant: "#3f484a", + surfaceVariantText: "#bfc8ca", + surfaceTint: "#4dd0e1", + background: "#0e1416", + backgroundText: "#dee3e5", + outline: "#899295", + surfaceContainer: "#1b2122", + surfaceContainerHigh: "#252b2c", + surfaceContainerHighest: "#303637", + }, + pink: { + name: "Pink", + primary: "#e91e63", + primaryText: "#000000", + primaryContainer: "#4a0e2f", + secondary: "#f8bbd9", + surface: "#191112", + surfaceText: "#f0dee0", + surfaceVariant: "#524345", + surfaceVariantText: "#d6c2c3", + surfaceTint: "#f8bbd9", + background: "#191112", + backgroundText: "#f0dee0", + outline: "#9f8c8e", + surfaceContainer: "#261d1e", + surfaceContainerHigh: "#312829", + surfaceContainerHighest: "#3c3233", + }, + amber: { + name: "Amber", + primary: "#ffc107", + primaryText: "#000000", + primaryContainer: "#4a3c00", + secondary: "#ffd54f", + surface: "#17130b", + surfaceText: "#ebe1d4", + surfaceVariant: "#4d4639", + surfaceVariantText: "#d0c5b4", + surfaceTint: "#ffd54f", + background: "#17130b", + backgroundText: "#ebe1d4", + outline: "#998f80", + surfaceContainer: "#231f17", + surfaceContainerHigh: "#2e2921", + surfaceContainerHighest: "#39342b", + }, + coral: { + name: "Coral", + primary: "#ffb4ab", + primaryText: "#000000", + primaryContainer: "#8c1d18", + secondary: "#f9dedc", + surface: "#1a1110", + surfaceText: "#f1dedc", + surfaceVariant: "#534341", + surfaceVariantText: "#d8c2bf", + surfaceTint: "#ffb4ab", + background: "#1a1110", + backgroundText: "#f1dedc", + outline: "#a08c8a", + surfaceContainer: "#271d1c", + surfaceContainerHigh: "#322826", + surfaceContainerHighest: "#3d3231", + }, + monochrome: { + name: "Monochrome", + primary: "#ffffff", + primaryText: "#2b303c", + primaryContainer: "#424753", + secondary: "#c4c6d0", + surface: "#2a2a2a", + surfaceText: "#e4e2e3", + surfaceVariant: "#474648", + surfaceVariantText: "#c8c6c7", + surfaceTint: "#c2c6d6", + background: "#131315", + backgroundText: "#e4e2e3", + outline: "#929092", + surfaceContainer: "#353535", + surfaceContainerHigh: "#424242", + surfaceContainerHighest: "#505050", + error: "#ffb4ab", + warning: "#3f4759", + info: "#595e6c", + matugen_type: "scheme-monochrome", + }, + }, + LIGHT: { + blue: { + name: "Blue Light", + primary: "#1976d2", + primaryText: "#ffffff", + primaryContainer: "#e3f2fd", + secondary: "#42a5f5", + surface: "#f7f9ff", + surfaceText: "#181c20", + surfaceVariant: "#dee3eb", + surfaceVariantText: "#42474e", + surfaceTint: "#1976d2", + background: "#f7f9ff", + backgroundText: "#181c20", + outline: "#72777f", + surfaceContainer: "#eceef4", + surfaceContainerHigh: "#e6e8ee", + surfaceContainerHighest: "#e0e2e8", + }, + purple: { + name: "Purple Light", + primary: "#6750A4", + primaryText: "#ffffff", + primaryContainer: "#EADDFF", + secondary: "#625B71", + surface: "#fef7ff", + surfaceText: "#1d1b20", + surfaceVariant: "#e7e0eb", + surfaceVariantText: "#49454e", + surfaceTint: "#6750A4", + background: "#fef7ff", + backgroundText: "#1d1b20", + outline: "#7a757f", + surfaceContainer: "#f2ecf4", + surfaceContainerHigh: "#ece6ee", + surfaceContainerHighest: "#e6e0e9", + }, + green: { + name: "Green Light", + primary: "#2e7d32", + primaryText: "#ffffff", + primaryContainer: "#e8f5e8", + secondary: "#4caf50", + surface: "#f7fbf1", + surfaceText: "#191d17", + surfaceVariant: "#dee5d8", + surfaceVariantText: "#424940", + surfaceTint: "#2e7d32", + background: "#f7fbf1", + backgroundText: "#191d17", + outline: "#72796f", + surfaceContainer: "#ecefe6", + surfaceContainerHigh: "#e6e9e0", + surfaceContainerHighest: "#e0e4db", + }, + orange: { + name: "Orange Light", + primary: "#e65100", + primaryText: "#ffffff", + primaryContainer: "#ffecb3", + secondary: "#ff9800", + surface: "#fff8f6", + surfaceText: "#221a16", + surfaceVariant: "#f4ded5", + surfaceVariantText: "#52443d", + surfaceTint: "#e65100", + background: "#fff8f6", + backgroundText: "#221a16", + outline: "#85736c", + surfaceContainer: "#fceae3", + surfaceContainerHigh: "#f6e5de", + surfaceContainerHighest: "#f0dfd8", + }, + red: { + name: "Red Light", + primary: "#d32f2f", + primaryText: "#ffffff", + primaryContainer: "#ffebee", + secondary: "#f44336", + surface: "#fff8f7", + surfaceText: "#231918", + surfaceVariant: "#f5ddda", + surfaceVariantText: "#534341", + surfaceTint: "#d32f2f", + background: "#fff8f7", + backgroundText: "#231918", + outline: "#857370", + surfaceContainer: "#fceae7", + surfaceContainerHigh: "#f7e4e1", + surfaceContainerHighest: "#f1dedc", + }, + cyan: { + name: "Cyan Light", + primary: "#0097a7", + primaryText: "#ffffff", + primaryContainer: "#e0f2f1", + secondary: "#00bcd4", + surface: "#f5fafc", + surfaceText: "#171d1e", + surfaceVariant: "#dbe4e6", + surfaceVariantText: "#3f484a", + surfaceTint: "#0097a7", + background: "#f5fafc", + backgroundText: "#171d1e", + outline: "#6f797b", + surfaceContainer: "#e9eff0", + surfaceContainerHigh: "#e3e9eb", + surfaceContainerHighest: "#dee3e5", + }, + pink: { + name: "Pink Light", + primary: "#c2185b", + primaryText: "#ffffff", + primaryContainer: "#fce4ec", + secondary: "#e91e63", + surface: "#fff8f7", + surfaceText: "#22191a", + surfaceVariant: "#f3dddf", + surfaceVariantText: "#524345", + surfaceTint: "#c2185b", + background: "#fff8f7", + backgroundText: "#22191a", + outline: "#847375", + surfaceContainer: "#fbeaeb", + surfaceContainerHigh: "#f5e4e5", + surfaceContainerHighest: "#f0dee0", + }, + amber: { + name: "Amber Light", + primary: "#ff8f00", + primaryText: "#000000", + primaryContainer: "#fff8e1", + secondary: "#ffc107", + surface: "#fff8f2", + surfaceText: "#1f1b13", + surfaceVariant: "#ede1cf", + surfaceVariantText: "#4d4639", + surfaceTint: "#ff8f00", + background: "#fff8f2", + backgroundText: "#1f1b13", + outline: "#7f7667", + surfaceContainer: "#f6ecdf", + surfaceContainerHigh: "#f1e7d9", + surfaceContainerHighest: "#ebe1d4", + }, + coral: { + name: "Coral Light", + primary: "#8c1d18", + primaryText: "#ffffff", + primaryContainer: "#ffdad6", + secondary: "#ff5449", + surface: "#fff8f7", + surfaceText: "#231918", + surfaceVariant: "#f5ddda", + surfaceVariantText: "#534341", + surfaceTint: "#8c1d18", + background: "#fff8f7", + backgroundText: "#231918", + outline: "#857371", + surfaceContainer: "#fceae7", + surfaceContainerHigh: "#f6e4e2", + surfaceContainerHighest: "#f1dedc", + }, + monochrome: { + name: "Monochrome Light", + primary: "#2b303c", + primaryText: "#ffffff", + primaryContainer: "#d6d7dc", + secondary: "#4a4d56", + surface: "#f5f5f6", + surfaceText: "#2a2a2a", + surfaceVariant: "#e0e0e2", + surfaceVariantText: "#424242", + surfaceTint: "#5a5f6e", + background: "#ffffff", + backgroundText: "#1a1a1a", + outline: "#757577", + surfaceContainer: "#e8e8ea", + surfaceContainerHigh: "#dcdcde", + surfaceContainerHighest: "#d0d0d2", + error: "#ba1a1a", + warning: "#f9e79f", + info: "#5d6475", + matugen_type: "scheme-monochrome", + }, + }, +}; const ThemeCategories = { - GENERIC: { - name: "Generic", - variants: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"] - }, - CATPPUCCIN: { - name: "Catppuccin", - variants: Object.keys(CatppuccinVariants) - } -} + GENERIC: { + name: "Generic", + variants: [ + "blue", + "purple", + "green", + "orange", + "red", + "cyan", + "pink", + "amber", + "coral", + "monochrome", + ], + }, +}; const ThemeNames = { - BLUE: "blue", - PURPLE: "purple", - GREEN: "green", - ORANGE: "orange", - RED: "red", - CYAN: "cyan", - PINK: "pink", - AMBER: "amber", - CORAL: "coral", - MONOCHROME: "monochrome", - DYNAMIC: "dynamic" -} + BLUE: "blue", + PURPLE: "purple", + GREEN: "green", + ORANGE: "orange", + RED: "red", + CYAN: "cyan", + PINK: "pink", + AMBER: "amber", + CORAL: "coral", + MONOCHROME: "monochrome", + DYNAMIC: "dynamic", +}; function isStockTheme(themeName) { - return Object.keys(StockThemes.DARK).includes(themeName) -} - -function isCatppuccinVariant(themeName) { - return Object.keys(CatppuccinVariants).includes(themeName) + return Object.keys(StockThemes.DARK).includes(themeName); } function getAvailableThemes(isLight = false) { - return isLight ? StockThemes.LIGHT : StockThemes.DARK + return isLight ? StockThemes.LIGHT : StockThemes.DARK; } function getThemeByName(themeName, isLight = false) { - if (isCatppuccinVariant(themeName)) { - return getCatppuccinTheme(themeName, isLight) - } - const themes = getAvailableThemes(isLight) - return themes[themeName] || themes.blue + const themes = getAvailableThemes(isLight); + return themes[themeName] || themes.blue; } function getAllThemeNames() { - return Object.keys(StockThemes.DARK) -} - -function getCatppuccinVariantNames() { - return Object.keys(CatppuccinVariants) + return Object.keys(StockThemes.DARK); } function getThemeCategories() { - return ThemeCategories + return ThemeCategories; } diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index 8dffbc55..1473a03c 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -491,11 +491,7 @@ Singleton { } else { currentTheme = themeName; if (currentThemeCategory !== "registry") { - if (StockThemes.isCatppuccinVariant(themeName)) { - currentThemeCategory = "catppuccin"; - } else { - currentThemeCategory = "generic"; - } + currentThemeCategory = "generic"; } } const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode); @@ -564,46 +560,6 @@ Singleton { themeCategoryTransitionTimer.restart(); } - function getCatppuccinColor(variantName) { - const catColors = { - "cat-rosewater": "#f5e0dc", - "cat-flamingo": "#f2cdcd", - "cat-pink": "#f5c2e7", - "cat-mauve": "#cba6f7", - "cat-red": "#f38ba8", - "cat-maroon": "#eba0ac", - "cat-peach": "#fab387", - "cat-yellow": "#f9e2af", - "cat-green": "#a6e3a1", - "cat-teal": "#94e2d5", - "cat-sky": "#89dceb", - "cat-sapphire": "#74c7ec", - "cat-blue": "#89b4fa", - "cat-lavender": "#b4befe" - }; - return catColors[variantName] || "#cba6f7"; - } - - function getCatppuccinVariantName(variantName) { - const catNames = { - "cat-rosewater": "Rosewater", - "cat-flamingo": "Flamingo", - "cat-pink": "Pink", - "cat-mauve": "Mauve", - "cat-red": "Red", - "cat-maroon": "Maroon", - "cat-peach": "Peach", - "cat-yellow": "Yellow", - "cat-green": "Green", - "cat-teal": "Teal", - "cat-sky": "Sky", - "cat-sapphire": "Sapphire", - "cat-blue": "Blue", - "cat-lavender": "Lavender" - }; - return catNames[variantName] || "Unknown"; - } - function loadCustomTheme(themeData) { customThemeRawData = themeData; const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"; @@ -615,16 +571,47 @@ Singleton { baseColors = themeData; } - if (themeData.variants && themeData.variants.options && themeData.variants.options.length > 0) { + if (themeData.variants) { const themeId = themeData.id || ""; - const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default; - const variant = findVariant(themeData.variants.options, selectedVariantId); - if (variant) { - const variantColors = variant[colorMode] || variant.dark || variant.light || {}; - customThemeData = mergeColors(baseColors, variantColors); + + if (themeData.variants.type === "multi" && themeData.variants.flavors && themeData.variants.accents) { + const defaults = themeData.variants.defaults || {}; + const modeDefaults = defaults[colorMode] || defaults.dark || {}; + const stored = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, modeDefaults) : modeDefaults; + var flavorId = stored.flavor || modeDefaults.flavor || ""; + const accentId = stored.accent || modeDefaults.accent || ""; + var flavor = findVariant(themeData.variants.flavors, flavorId); + if (flavor) { + const hasCurrentModeColors = flavor[colorMode] && (flavor[colorMode].primary || flavor[colorMode].surface); + if (!hasCurrentModeColors) { + flavorId = modeDefaults.flavor || ""; + flavor = findVariant(themeData.variants.flavors, flavorId); + } + } + const accent = findAccent(themeData.variants.accents, accentId); + if (flavor) { + const flavorColors = flavor[colorMode] || flavor.dark || flavor.light || {}; + baseColors = mergeColors(baseColors, flavorColors); + } + if (accent && flavor) { + const accentColors = accent[flavor.id] || {}; + baseColors = mergeColors(baseColors, accentColors); + } + customThemeData = baseColors; generateSystemThemesFromCurrentTheme(); return; } + + if (themeData.variants.options && themeData.variants.options.length > 0) { + const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default; + const variant = findVariant(themeData.variants.options, selectedVariantId); + if (variant) { + const variantColors = variant[colorMode] || variant.dark || variant.light || {}; + customThemeData = mergeColors(baseColors, variantColors); + generateSystemThemesFromCurrentTheme(); + return; + } + } } customThemeData = baseColors; @@ -641,6 +628,16 @@ Singleton { return options[0] || null; } + function findAccent(accents, accentId) { + if (!accentId || !accents) + return null; + for (var i = 0; i < accents.length; i++) { + if (accents[i].id === accentId) + return accents[i]; + } + return accents[0] || null; + } + function mergeColors(base, overlay) { var result = JSON.parse(JSON.stringify(base)); for (var key in overlay) { @@ -954,13 +951,38 @@ Singleton { darkTheme = customThemeRawData.dark || customThemeRawData.light; lightTheme = customThemeRawData.light || customThemeRawData.dark; - if (customThemeRawData.variants && customThemeRawData.variants.options) { + if (customThemeRawData.variants) { const themeId = customThemeRawData.id || ""; - const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default; - const variant = findVariant(customThemeRawData.variants.options, selectedVariantId); - if (variant) { - darkTheme = mergeColors(darkTheme, variant.dark || {}); - lightTheme = mergeColors(lightTheme, variant.light || {}); + + if (customThemeRawData.variants.type === "multi" && customThemeRawData.variants.flavors && customThemeRawData.variants.accents) { + const defaults = customThemeRawData.variants.defaults || {}; + const darkDefaults = defaults.dark || {}; + const lightDefaults = defaults.light || defaults.dark || {}; + const storedDark = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, darkDefaults) : darkDefaults; + const storedLight = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, lightDefaults) : lightDefaults; + const darkFlavorId = storedDark.flavor || darkDefaults.flavor || ""; + const lightFlavorId = storedLight.flavor || lightDefaults.flavor || ""; + const accentId = storedDark.accent || darkDefaults.accent || ""; + const darkFlavor = findVariant(customThemeRawData.variants.flavors, darkFlavorId); + const lightFlavor = findVariant(customThemeRawData.variants.flavors, lightFlavorId); + const accent = findAccent(customThemeRawData.variants.accents, accentId); + if (darkFlavor) { + darkTheme = mergeColors(darkTheme, darkFlavor.dark || {}); + if (accent) + darkTheme = mergeColors(darkTheme, accent[darkFlavor.id] || {}); + } + if (lightFlavor) { + lightTheme = mergeColors(lightTheme, lightFlavor.light || {}); + if (accent) + lightTheme = mergeColors(lightTheme, accent[lightFlavor.id] || {}); + } + } else if (customThemeRawData.variants.options) { + const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default; + const variant = findVariant(customThemeRawData.variants.options, selectedVariantId); + if (variant) { + darkTheme = mergeColors(darkTheme, variant.dark || {}); + lightTheme = mergeColors(lightTheme, variant.light || {}); + } } } } else { diff --git a/quickshell/Modals/Settings/SettingsSidebar.qml b/quickshell/Modals/Settings/SettingsSidebar.qml index bc12210c..69e7731e 100644 --- a/quickshell/Modals/Settings/SettingsSidebar.qml +++ b/quickshell/Modals/Settings/SettingsSidebar.qml @@ -604,15 +604,6 @@ Rectangle { } event.accepted = true; } - Keys.onPressed: event => { - if (event.key === Qt.Key_J) { - navNext(); - event.accepted = true; - } else if (event.key === Qt.Key_K) { - navPrev(); - event.accepted = true; - } - } } } @@ -694,10 +685,6 @@ Rectangle { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: root.selectSearchResult(resultDelegate.modelData) - onContainsMouseChanged: { - if (containsMouse) - root.searchSelectedIndex = resultDelegate.index; - } } Behavior on color { @@ -715,7 +702,7 @@ Rectangle { font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText horizontalAlignment: Text.AlignHCenter - visible: searchField.text.length >= 2 && SettingsSearchService.results.length === 0 + visible: searchField.text.length > 0 && SettingsSearchService.results.length === 0 topPadding: Theme.spacingM } } diff --git a/quickshell/Modules/Settings/ThemeBrowser.qml b/quickshell/Modules/Settings/ThemeBrowser.qml index 11674893..6a586228 100644 --- a/quickshell/Modules/Settings/ThemeBrowser.qml +++ b/quickshell/Modules/Settings/ThemeBrowser.qml @@ -391,12 +391,24 @@ FloatingWindow { property bool isFirstParty: modelData.firstParty || false property bool hasVariants: modelData.hasVariants || false property var variants: modelData.variants || null - property string selectedVariantId: hasVariants && variants ? (variants.default || (variants.options[0]?.id ?? "")) : "" + property string selectedVariantId: { + if (!hasVariants || !variants) + return ""; + if (variants.type === "multi") { + const mode = Theme.isLightMode ? "light" : "dark"; + const defaults = variants.defaults?.[mode] || variants.defaults?.dark || {}; + return (defaults.flavor || "") + (defaults.accent ? "-" + defaults.accent : ""); + } + return variants.default || (variants.options?.[0]?.id ?? ""); + } property string previewPath: { const baseDir = "/tmp/dankdots-plugin-registry/themes/" + (modelData.sourceDir || modelData.id); const mode = Theme.isLightMode ? "light" : "dark"; - if (hasVariants && selectedVariantId) + if (hasVariants && selectedVariantId) { + if (variants?.type === "multi") + return baseDir + "/preview-" + selectedVariantId + ".svg"; return baseDir + "/preview-" + selectedVariantId + "-" + mode + ".svg"; + } return baseDir + "/preview-" + mode + ".svg"; } property bool hasPreview: previewImage.status === Image.Ready @@ -503,7 +515,11 @@ FloatingWindow { StyledText { id: variantsText anchors.centerIn: parent - text: I18n.tr("%1 variants").arg(themeDelegate.variants?.options?.length ?? 0) + text: { + if (themeDelegate.variants?.type === "multi") + return I18n.tr("%1 variants").arg(themeDelegate.variants?.accents?.length ?? 0); + return I18n.tr("%1 variants").arg(themeDelegate.variants?.options?.length ?? 0); + } font.pixelSize: Theme.fontSizeSmall color: Theme.secondary font.weight: Font.Medium @@ -533,7 +549,7 @@ FloatingWindow { Flow { width: parent.width spacing: Theme.spacingXS - visible: themeDelegate.hasVariants + visible: themeDelegate.hasVariants && themeDelegate.variants?.type !== "multi" Repeater { model: themeDelegate.variants?.options ?? [] @@ -563,6 +579,36 @@ FloatingWindow { } } } + + Flow { + width: parent.width + spacing: Theme.spacingXS + visible: themeDelegate.hasVariants && themeDelegate.variants?.type === "multi" + + Repeater { + model: themeDelegate.variants?.accents ?? [] + + Rectangle { + width: 18 + height: 18 + radius: 9 + color: modelData.color || Theme.primary + border.color: Theme.outline + border.width: 1 + + DankTooltipV2 { + id: accentTooltip + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: accentTooltip.show(modelData.name || modelData.id, parent, 0, 0, "top") + onExited: accentTooltip.hide() + } + } + } + } } Rectangle { diff --git a/quickshell/Modules/Settings/ThemeColorsTab.qml b/quickshell/Modules/Settings/ThemeColorsTab.qml index e352ce84..f77b8868 100644 --- a/quickshell/Modules/Settings/ThemeColorsTab.qml +++ b/quickshell/Modules/Settings/ThemeColorsTab.qml @@ -78,8 +78,6 @@ Item { return I18n.tr("Current Theme: %1", "current theme label").arg(I18n.tr("Dynamic", "dynamic theme name")); if (Theme.currentThemeCategory === "registry" && registryThemeName) return I18n.tr("Current Theme: %1", "current theme label").arg(registryThemeName); - if (Theme.currentThemeCategory === "catppuccin") - return I18n.tr("Current Theme: %1", "current theme label").arg("Catppuccin " + Theme.getThemeColors(Theme.currentThemeName).name); return I18n.tr("Current Theme: %1", "current theme label").arg(Theme.getThemeColors(Theme.currentThemeName).name); } font.pixelSize: Theme.fontSizeMedium @@ -94,8 +92,6 @@ Item { return I18n.tr("Material colors generated from wallpaper", "dynamic theme description"); if (Theme.currentThemeCategory === "registry") return I18n.tr("Color theme from DMS registry", "registry theme description"); - if (Theme.currentThemeCategory === "catppuccin") - return I18n.tr("Soothing pastel theme based on Catppuccin", "catppuccin theme description"); if (Theme.currentTheme === Theme.custom) return I18n.tr("Custom theme loaded from JSON file", "custom theme description"); return I18n.tr("Material Design inspired color themes", "generic theme description"); @@ -129,18 +125,16 @@ Item { property bool isRegistryTheme: Theme.currentThemeCategory === "registry" property int currentThemeIndex: { if (isRegistryTheme) - return 4; - if (Theme.currentTheme === Theme.dynamic) - return 2; - if (Theme.currentThemeName === "custom") return 3; - if (Theme.currentThemeCategory === "catppuccin") + if (Theme.currentTheme === Theme.dynamic) return 1; + if (Theme.currentThemeName === "custom") + return 2; return 0; } property int pendingThemeIndex: -1 - model: DMSService.dmsAvailable ? ["Generic", "Catppuccin", "Auto", "Custom", "Registry"] : ["Generic", "Catppuccin", "Auto", "Custom"] + model: DMSService.dmsAvailable ? ["Generic", "Auto", "Custom", "Browse"] : ["Generic", "Auto", "Custom"] currentIndex: currentThemeIndex selectionMode: "single" onSelectionChanged: (index, selected) => { @@ -156,9 +150,6 @@ Item { Theme.switchThemeCategory("generic", "blue"); break; case 1: - Theme.switchThemeCategory("catppuccin", "cat-mauve"); - break; - case 2: if (ToastService.wallpaperErrorStatus === "matugen_missing") ToastService.showError(I18n.tr("matugen not found - install matugen package for dynamic theming", "matugen error")); else if (ToastService.wallpaperErrorStatus === "error") @@ -166,10 +157,10 @@ Item { else Theme.switchThemeCategory("dynamic", Theme.dynamic); break; - case 3: + case 2: Theme.switchThemeCategory("custom", "custom"); break; - case 4: + case 3: Theme.switchThemeCategory("registry", ""); break; } @@ -178,116 +169,66 @@ Item { } } - Flow { - id: genericColorFlow - spacing: Theme.spacingS + Item { width: parent.width + height: genericColorGrid.implicitHeight visible: Theme.currentThemeCategory === "generic" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom" - property int dotSize: width < 300 ? 28 : 32 - Repeater { - model: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"] + Grid { + id: genericColorGrid + property var colorList: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"] + property int dotSize: parent.width < 300 ? 28 : 32 + columns: Math.ceil(colorList.length / 2) + rowSpacing: Theme.spacingS + columnSpacing: Theme.spacingS + anchors.horizontalCenter: parent.horizontalCenter - Rectangle { - required property string modelData - property string themeName: modelData - width: genericColorFlow.dotSize - height: genericColorFlow.dotSize - radius: width / 2 - color: Theme.getThemeColors(themeName).primary - border.color: Theme.outline - border.width: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 2 : 1 - scale: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 1.1 : 1 + Repeater { + model: genericColorGrid.colorList Rectangle { - width: nameText.contentWidth + Theme.spacingS * 2 - height: nameText.contentHeight + Theme.spacingXS * 2 - color: Theme.surfaceContainer - radius: Theme.cornerRadius - anchors.bottom: parent.top - anchors.bottomMargin: Theme.spacingXS - anchors.horizontalCenter: parent.horizontalCenter - visible: mouseArea.containsMouse + required property string modelData + property string themeName: modelData + width: genericColorGrid.dotSize + height: genericColorGrid.dotSize + radius: width / 2 + color: Theme.getThemeColors(themeName).primary + border.color: Theme.outline + border.width: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 2 : 1 + scale: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 1.1 : 1 - StyledText { - id: nameText - text: Theme.getThemeColors(parent.parent.themeName).name - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - anchors.centerIn: parent + Rectangle { + width: nameText.contentWidth + Theme.spacingS * 2 + height: nameText.contentHeight + Theme.spacingXS * 2 + color: Theme.surfaceContainer + radius: Theme.cornerRadius + anchors.bottom: parent.top + anchors.bottomMargin: Theme.spacingXS + anchors.horizontalCenter: parent.horizontalCenter + visible: mouseArea.containsMouse + + StyledText { + id: nameText + text: Theme.getThemeColors(parent.parent.themeName).name + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.centerIn: parent + } } - } - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: Theme.switchTheme(parent.themeName) - } - - Behavior on scale { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.emphasizedEasing + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: Theme.switchTheme(parent.themeName) } - } - } - } - } - Flow { - id: catColorFlow - spacing: Theme.spacingS - width: parent.width - visible: Theme.currentThemeCategory === "catppuccin" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom" - property int dotSize: width < 300 ? 28 : 32 - - Repeater { - model: ["cat-rosewater", "cat-flamingo", "cat-pink", "cat-mauve", "cat-red", "cat-maroon", "cat-peach", "cat-yellow", "cat-green", "cat-teal", "cat-sky", "cat-sapphire", "cat-blue", "cat-lavender"] - - Rectangle { - required property string modelData - property string themeName: modelData - width: catColorFlow.dotSize - height: catColorFlow.dotSize - radius: width / 2 - color: Theme.getCatppuccinColor(themeName) - border.color: Theme.outline - border.width: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 2 : 1 - scale: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 1.1 : 1 - - Rectangle { - width: nameTextCat.contentWidth + Theme.spacingS * 2 - height: nameTextCat.contentHeight + Theme.spacingXS * 2 - color: Theme.surfaceContainer - radius: Theme.cornerRadius - anchors.bottom: parent.top - anchors.bottomMargin: Theme.spacingXS - anchors.horizontalCenter: parent.horizontalCenter - visible: mouseAreaCat.containsMouse - - StyledText { - id: nameTextCat - text: Theme.getCatppuccinVariantName(parent.parent.themeName) - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - anchors.centerIn: parent - } - } - - MouseArea { - id: mouseAreaCat - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: Theme.switchTheme(parent.themeName) - } - - Behavior on scale { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.emphasizedEasing + Behavior on scale { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.emphasizedEasing + } } } } @@ -584,7 +525,11 @@ Item { StyledText { anchors.centerIn: parent - text: themeCard.variants?.options?.length || 0 + text: { + if (themeCard.variants?.type === "multi") + return themeCard.variants?.accents?.length || 0; + return themeCard.variants?.options?.length || 0; + } font.pixelSize: themeGrid.cardWidth < 120 ? Theme.fontSizeSmall - 4 : Theme.fontSizeSmall - 2 color: Theme.surface font.weight: Font.Bold @@ -655,10 +600,10 @@ Item { id: variantSelector width: parent.width spacing: Theme.spacingS - visible: activeThemeVariants !== null && activeThemeVariants.options && activeThemeVariants.options.length > 0 + visible: activeThemeId !== "" && activeThemeVariants !== null && (isMultiVariant || (activeThemeVariants.options && activeThemeVariants.options.length > 0)) property string activeThemeId: { - if (Theme.currentThemeCategory !== "registry") + if (Theme.currentThemeCategory !== "registry" || Theme.currentTheme !== "custom") return ""; for (var i = 0; i < themeColorsTab.installedRegistryThemes.length; i++) { var t = themeColorsTab.installedRegistryThemes[i]; @@ -677,6 +622,29 @@ Item { } return null; } + property bool isMultiVariant: activeThemeVariants?.type === "multi" + property string colorMode: Theme.isLightMode ? "light" : "dark" + property var multiDefaults: { + if (!isMultiVariant || !activeThemeVariants?.defaults) + return {}; + return activeThemeVariants.defaults[colorMode] || activeThemeVariants.defaults.dark || {}; + } + property var storedMulti: activeThemeId ? SettingsData.getRegistryThemeMultiVariant(activeThemeId, multiDefaults) : multiDefaults + property string selectedFlavor: storedMulti.flavor || multiDefaults.flavor || "" + property string selectedAccent: storedMulti.accent || multiDefaults.accent || "" + property var flavorOptions: { + if (!isMultiVariant || !activeThemeVariants?.flavors) + return []; + return activeThemeVariants.flavors.filter(f => f.mode === colorMode || f.mode === "both"); + } + property var flavorNames: flavorOptions.map(f => f.name) + property int flavorIndex: { + for (var i = 0; i < flavorOptions.length; i++) { + if (flavorOptions[i].id === selectedFlavor) + return i; + } + return 0; + } property string selectedVariant: activeThemeId ? SettingsData.getRegistryThemeVariant(activeThemeId, activeThemeVariants?.default || "") : "" property var variantNames: { if (!activeThemeVariants?.options) @@ -693,10 +661,111 @@ Item { return 0; } + Item { + width: parent.width + height: flavorButtonGroup.implicitHeight + clip: true + visible: variantSelector.isMultiVariant && variantSelector.flavorOptions.length > 1 + + DankButtonGroup { + id: flavorButtonGroup + anchors.horizontalCenter: parent.horizontalCenter + buttonPadding: parent.width < 400 ? Theme.spacingS : Theme.spacingL + minButtonWidth: parent.width < 400 ? 44 : 64 + textSize: parent.width < 400 ? Theme.fontSizeSmall : Theme.fontSizeMedium + model: variantSelector.flavorNames + currentIndex: variantSelector.flavorIndex + selectionMode: "single" + onAnimationCompleted: { + if (currentIndex < 0 || currentIndex >= variantSelector.flavorOptions.length) + return; + const flavorId = variantSelector.flavorOptions[currentIndex]?.id; + if (!flavorId || flavorId === variantSelector.selectedFlavor) + return; + Theme.screenTransition(); + SettingsData.setRegistryThemeMultiVariant(variantSelector.activeThemeId, flavorId, variantSelector.selectedAccent); + } + } + } + + Item { + width: parent.width + height: accentColorsGrid.implicitHeight + visible: variantSelector.isMultiVariant && variantSelector.activeThemeVariants?.accents?.length > 0 + + Grid { + id: accentColorsGrid + property int accentCount: variantSelector.activeThemeVariants?.accents?.length ?? 0 + property int dotSize: parent.width < 300 ? 28 : 32 + columns: accentCount > 0 ? Math.ceil(accentCount / 2) : 1 + rowSpacing: Theme.spacingS + columnSpacing: Theme.spacingS + anchors.horizontalCenter: parent.horizontalCenter + + Repeater { + model: variantSelector.activeThemeVariants?.accents || [] + + Rectangle { + required property var modelData + required property int index + property string accentId: modelData.id + property bool isSelected: accentId === variantSelector.selectedAccent + width: accentColorsGrid.dotSize + height: accentColorsGrid.dotSize + radius: width / 2 + color: modelData.color || Theme.primary + border.color: Theme.outline + border.width: isSelected ? 2 : 1 + scale: isSelected ? 1.1 : 1 + + Rectangle { + width: accentNameText.contentWidth + Theme.spacingS * 2 + height: accentNameText.contentHeight + Theme.spacingXS * 2 + color: Theme.surfaceContainer + radius: Theme.cornerRadius + anchors.bottom: parent.top + anchors.bottomMargin: Theme.spacingXS + anchors.horizontalCenter: parent.horizontalCenter + visible: accentMouseArea.containsMouse + + StyledText { + id: accentNameText + text: modelData.name + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.centerIn: parent + } + } + + MouseArea { + id: accentMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (parent.isSelected) + return; + Theme.screenTransition(); + SettingsData.setRegistryThemeMultiVariant(variantSelector.activeThemeId, variantSelector.selectedFlavor, parent.accentId); + } + } + + Behavior on scale { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.emphasizedEasing + } + } + } + } + } + } + Item { width: parent.width height: variantButtonGroup.implicitHeight clip: true + visible: !variantSelector.isMultiVariant && variantSelector.variantNames.length > 0 DankButtonGroup { id: variantButtonGroup diff --git a/quickshell/Services/SettingsSearchService.qml b/quickshell/Services/SettingsSearchService.qml index bf8cfb0e..edf55770 100644 --- a/quickshell/Services/SettingsSearchService.qml +++ b/quickshell/Services/SettingsSearchService.qml @@ -652,7 +652,7 @@ Singleton { function search(text) { query = text; - if (!text || text.length < 2) { + if (!text) { results = []; return; }