1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-31 08:52:49 -05:00

Compare commits

..

5 Commits

Author SHA1 Message Date
xxyangyoulin
1194f3ffb8 media: add scroll wheel behavior configuration (#1160) 2025-12-26 14:43:47 -05:00
bbedward
5ac81e6dd6 dankbar: dont apply exclusive zone to popup positioning 2025-12-26 14:38:34 -05:00
Lucas
987856a1de nix: update flake inputs (#1161) 2025-12-26 14:15:22 -05:00
bbedward
ef52ce0990 themes: support for variants 2025-12-26 14:00:14 -05:00
bbedward
06b14a5869 dankinstall: fix plasma session collision 2025-12-26 13:06:21 -05:00
17 changed files with 563 additions and 378 deletions

View File

@@ -199,6 +199,16 @@ func runShellInteractive(session bool) {
} }
} }
if os.Getenv("QT_QPA_PLATFORMTHEME") == "" {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3")
}
if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3")
}
if os.Getenv("QT_QPA_PLATFORM") == "" {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland")
}
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -433,6 +443,16 @@ func runShellDaemon(session bool) {
} }
} }
if os.Getenv("QT_QPA_PLATFORMTHEME") == "" {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3")
}
if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3")
}
if os.Getenv("QT_QPA_PLATFORM") == "" {
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland")
}
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0) devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil { if err != nil {
log.Fatalf("Error opening /dev/null: %v", err) log.Fatalf("Error opening /dev/null: %v", err)

View File

@@ -550,10 +550,7 @@ func (b *BaseDistribution) WriteEnvironmentConfig(terminal deps.Terminal) error
terminalCmd = "ghostty" terminalCmd = "ghostty"
} }
content := fmt.Sprintf(`QT_QPA_PLATFORM=wayland content := fmt.Sprintf(`ELECTRON_OZONE_PLATFORM_HINT=auto
ELECTRON_OZONE_PLATFORM_HINT=auto
QT_QPA_PLATFORMTHEME=gtk3
QT_QPA_PLATFORMTHEME_QT6=gtk3
TERMINAL=%s TERMINAL=%s
`, terminalCmd) `, terminalCmd)
@@ -567,12 +564,6 @@ TERMINAL=%s
} }
func (b *BaseDistribution) EnableDMSService(ctx context.Context, wm deps.WindowManager) error { func (b *BaseDistribution) EnableDMSService(ctx context.Context, wm deps.WindowManager) error {
cmd := exec.CommandContext(ctx, "systemctl", "--user", "enable", "--now", "dms")
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to enable dms service: %w", err)
}
b.log("Enabled dms systemd user service")
switch wm { switch wm {
case deps.WindowManagerNiri: case deps.WindowManagerNiri:
if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil { if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil {

View File

@@ -31,7 +31,7 @@ func HandleList(conn net.Conn, req models.Request) {
result := make([]ThemeInfo, len(themeList)) result := make([]ThemeInfo, len(themeList))
for i, t := range themeList { for i, t := range themeList {
installed, _ := manager.IsInstalled(t) installed, _ := manager.IsInstalled(t)
result[i] = ThemeInfo{ info := ThemeInfo{
ID: t.ID, ID: t.ID,
Name: t.Name, Name: t.Name,
Version: t.Version, Version: t.Version,
@@ -42,6 +42,17 @@ func HandleList(conn net.Conn, req models.Request) {
Installed: installed, Installed: installed,
FirstParty: isFirstParty(t.Author), 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}
}
}
result[i] = info
} }
models.Respond(conn, req.ID, result) models.Respond(conn, req.ID, result)

View File

@@ -8,6 +8,20 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes" "github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
) )
func addVariantsInfo(info *ThemeInfo, variants *themes.ThemeVariants) {
if variants == nil || len(variants.Options) == 0 {
return
}
info.HasVariants = true
info.Variants = &VariantsInfo{
Default: variants.Default,
Options: make([]VariantInfo, len(variants.Options)),
}
for i, v := range variants.Options {
info.Variants.Options[i] = VariantInfo{ID: v.ID, Name: v.Name}
}
}
func HandleListInstalled(conn net.Conn, req models.Request) { func HandleListInstalled(conn net.Conn, req models.Request) {
manager, err := themes.NewManager() manager, err := themes.NewManager()
if err != nil { if err != nil {
@@ -46,7 +60,7 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
hasUpdate = hasUpdates hasUpdate = hasUpdates
} }
result = append(result, ThemeInfo{ info := ThemeInfo{
ID: theme.ID, ID: theme.ID,
Name: theme.Name, Name: theme.Name,
Version: theme.Version, Version: theme.Version,
@@ -55,7 +69,9 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
SourceDir: id, SourceDir: id,
FirstParty: isFirstParty(theme.Author), FirstParty: isFirstParty(theme.Author),
HasUpdate: hasUpdate, HasUpdate: hasUpdate,
}) }
addVariantsInfo(&info, theme.Variants)
result = append(result, info)
} else { } else {
installed, err := manager.GetInstalledTheme(id) installed, err := manager.GetInstalledTheme(id)
if err != nil { if err != nil {
@@ -66,7 +82,7 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
}) })
continue continue
} }
result = append(result, ThemeInfo{ info := ThemeInfo{
ID: installed.ID, ID: installed.ID,
Name: installed.Name, Name: installed.Name,
Version: installed.Version, Version: installed.Version,
@@ -74,7 +90,9 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
Description: installed.Description, Description: installed.Description,
SourceDir: id, SourceDir: id,
FirstParty: isFirstParty(installed.Author), FirstParty: isFirstParty(installed.Author),
}) }
addVariantsInfo(&info, installed.Variants)
result = append(result, info)
} }
} }

View File

@@ -1,14 +1,26 @@
package themes package themes
type ThemeInfo struct { type VariantInfo struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` }
Author string `json:"author,omitempty"`
Description string `json:"description,omitempty"` type VariantsInfo struct {
PreviewPath string `json:"previewPath,omitempty"` Default string `json:"default,omitempty"`
SourceDir string `json:"sourceDir,omitempty"` Options []VariantInfo `json:"options,omitempty"`
Installed bool `json:"installed,omitempty"` }
FirstParty bool `json:"firstParty,omitempty"`
HasUpdate bool `json:"hasUpdate,omitempty"` type ThemeInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
Author string `json:"author,omitempty"`
Description string `json:"description,omitempty"`
PreviewPath string `json:"previewPath,omitempty"`
SourceDir string `json:"sourceDir,omitempty"`
Installed bool `json:"installed,omitempty"`
FirstParty bool `json:"firstParty,omitempty"`
HasUpdate bool `json:"hasUpdate,omitempty"`
HasVariants bool `json:"hasVariants,omitempty"`
Variants *VariantsInfo `json:"variants,omitempty"`
} }

View File

@@ -80,21 +80,35 @@ func (m *Manager) Install(theme Theme, registryThemeDir string) error {
return fmt.Errorf("failed to write theme file: %w", err) return fmt.Errorf("failed to write theme file: %w", err)
} }
for _, preview := range []string{"preview-dark.svg", "preview-light.svg"} { m.copyPreviewFiles(registryThemeDir, themeDir, theme)
srcPath := filepath.Join(registryThemeDir, preview) return nil
exists, _ := afero.Exists(m.fs, srcPath) }
if !exists {
func (m *Manager) copyPreviewFiles(srcDir, dstDir string, theme Theme) {
previews := []string{"preview-dark.svg", "preview-light.svg"}
if theme.Variants != nil {
for _, v := range theme.Variants.Options {
previews = append(previews,
fmt.Sprintf("preview-%s.svg", v.ID),
fmt.Sprintf("preview-%s-dark.svg", v.ID),
fmt.Sprintf("preview-%s-light.svg", v.ID),
)
}
}
for _, preview := range previews {
srcPath := filepath.Join(srcDir, preview)
if exists, _ := afero.Exists(m.fs, srcPath); !exists {
continue continue
} }
data, err := afero.ReadFile(m.fs, srcPath) data, err := afero.ReadFile(m.fs, srcPath)
if err != nil { if err != nil {
continue continue
} }
dstPath := filepath.Join(themeDir, preview) dstPath := filepath.Join(dstDir, preview)
_ = afero.WriteFile(m.fs, dstPath, data, 0644) _ = afero.WriteFile(m.fs, dstPath, data, 0644)
} }
return nil
} }
func (m *Manager) InstallFromRegistry(registry *Registry, themeID string) error { func (m *Manager) InstallFromRegistry(registry *Registry, themeID string) error {

View File

@@ -13,35 +13,49 @@ import (
const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git" const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git"
type ColorScheme struct { type ColorScheme struct {
Primary string `json:"primary"` Primary string `json:"primary,omitempty"`
PrimaryText string `json:"primaryText"` PrimaryText string `json:"primaryText,omitempty"`
PrimaryContainer string `json:"primaryContainer"` PrimaryContainer string `json:"primaryContainer,omitempty"`
Secondary string `json:"secondary"` Secondary string `json:"secondary,omitempty"`
Surface string `json:"surface"` Surface string `json:"surface,omitempty"`
SurfaceText string `json:"surfaceText"` SurfaceText string `json:"surfaceText,omitempty"`
SurfaceVariant string `json:"surfaceVariant"` SurfaceVariant string `json:"surfaceVariant,omitempty"`
SurfaceVariantText string `json:"surfaceVariantText"` SurfaceVariantText string `json:"surfaceVariantText,omitempty"`
SurfaceTint string `json:"surfaceTint"` SurfaceTint string `json:"surfaceTint,omitempty"`
Background string `json:"background"` Background string `json:"background,omitempty"`
BackgroundText string `json:"backgroundText"` BackgroundText string `json:"backgroundText,omitempty"`
Outline string `json:"outline"` Outline string `json:"outline,omitempty"`
SurfaceContainer string `json:"surfaceContainer"` SurfaceContainer string `json:"surfaceContainer,omitempty"`
SurfaceContainerHigh string `json:"surfaceContainerHigh"` SurfaceContainerHigh string `json:"surfaceContainerHigh,omitempty"`
Error string `json:"error"` SurfaceContainerHighest string `json:"surfaceContainerHighest,omitempty"`
Warning string `json:"warning"` Error string `json:"error,omitempty"`
Info string `json:"info"` Warning string `json:"warning,omitempty"`
Info string `json:"info,omitempty"`
}
type ThemeVariant struct {
ID string `json:"id"`
Name string `json:"name"`
Dark ColorScheme `json:"dark,omitempty"`
Light ColorScheme `json:"light,omitempty"`
}
type ThemeVariants struct {
Default string `json:"default,omitempty"`
Options []ThemeVariant `json:"options,omitempty"`
} }
type Theme struct { type Theme struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
Author string `json:"author"` Author string `json:"author"`
Description string `json:"description"` Description string `json:"description"`
Dark ColorScheme `json:"dark"` Dark ColorScheme `json:"dark"`
Light ColorScheme `json:"light"` Light ColorScheme `json:"light"`
PreviewPath string `json:"-"` Variants *ThemeVariants `json:"variants,omitempty"`
SourceDir string `json:"sourceDir,omitempty"` PreviewPath string `json:"-"`
SourceDir string `json:"sourceDir,omitempty"`
} }
type GitClient interface { type GitClient interface {

22
flake.lock generated
View File

@@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762835999, "lastModified": 1765838956,
"narHash": "sha256-UykYGrGFOFTmDpKTLNxj1wvd1gbDG4TkqLNSbV0TYwk=", "narHash": "sha256-A3a2ZfvjirX8VIdIPI+nAyukWs6vx4vet3fU0mpr7lU=",
"owner": "AvengeMedia", "owner": "AvengeMedia",
"repo": "dgop", "repo": "dgop",
"rev": "799301991cd5dcea9b64245f9d500dcc76615653", "rev": "0ff697a4e3418966caa714c838fc73f1ef6ba59b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -22,11 +22,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1764950072, "lastModified": 1766651565,
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", "narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f61125a668a320878494449750330ca58b78c557", "rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -43,16 +43,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1764663772, "lastModified": 1766386896,
"narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=", "narHash": "sha256-1uql4y229Rh+/2da99OVNe6DfsjObukXkf60TYRCvhI=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460", "rev": "3918290c1bcd93ed81291844d9f1ed146672dbfc",
"revCount": 713, "revCount": 714,
"type": "git", "type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell" "url": "https://git.outfoxxed.me/quickshell/quickshell"
}, },
"original": { "original": {
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460", "rev": "3918290c1bcd93ed81291844d9f1ed146672dbfc",
"type": "git", "type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell" "url": "https://git.outfoxxed.me/quickshell/quickshell"
} }

View File

@@ -8,7 +8,7 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
quickshell = { quickshell = {
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=26531fc46ef17e9365b03770edd3fb9206fcb460"; url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=3918290c1bcd93ed81291844d9f1ed146672dbfc";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };

View File

@@ -65,6 +65,7 @@ Singleton {
property string currentThemeName: "blue" property string currentThemeName: "blue"
property string currentThemeCategory: "generic" property string currentThemeCategory: "generic"
property string customThemeFile: "" property string customThemeFile: ""
property var registryThemeVariants: ({})
property string matugenScheme: "scheme-tonal-spot" property string matugenScheme: "scheme-tonal-spot"
property bool runUserMatugenTemplates: true property bool runUserMatugenTemplates: true
property string matugenTargetMonitor: "" property string matugenTargetMonitor: ""
@@ -176,6 +177,7 @@ Singleton {
property bool waveProgressEnabled: true property bool waveProgressEnabled: true
property bool scrollTitleEnabled: true property bool scrollTitleEnabled: true
property bool audioVisualizerEnabled: true property bool audioVisualizerEnabled: true
property bool audioScrollEnabled: true
property bool clockCompactMode: false property bool clockCompactMode: false
property bool focusedWindowCompactMode: false property bool focusedWindowCompactMode: false
property bool runningAppsCompactMode: true property bool runningAppsCompactMode: true
@@ -915,7 +917,8 @@ Singleton {
const defaultBar = barConfigs[0] || getBarConfig("default"); const defaultBar = barConfigs[0] || getBarConfig("default");
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4); const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top); const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
const bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0); const rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
const bottomGap = Math.max(0, rawBottomGap);
const useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true); const useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4); const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
@@ -1556,6 +1559,19 @@ Singleton {
return workspaceNameIcons[workspaceName] || null; return workspaceNameIcons[workspaceName] || null;
} }
function getRegistryThemeVariant(themeId, defaultVariant) {
return registryThemeVariants[themeId] || defaultVariant || "";
}
function setRegistryThemeVariant(themeId, variantId) {
var variants = JSON.parse(JSON.stringify(registryThemeVariants));
variants[themeId] = variantId;
registryThemeVariants = variants;
saveSettings();
if (typeof Theme !== "undefined")
Theme.reloadCustomThemeVariant();
}
function toggleDankBarVisible() { function toggleDankBarVisible() {
const defaultBar = barConfigs[0] || getBarConfig("default"); const defaultBar = barConfigs[0] || getBarConfig("default");
if (defaultBar) { if (defaultBar) {

View File

@@ -93,6 +93,8 @@ Singleton {
property var _pendingGenerateParams: null property var _pendingGenerateParams: null
property var customThemeData: null property var customThemeData: null
property var customThemeRawData: null property var customThemeRawData: null
readonly property var currentThemeVariants: customThemeRawData?.variants || null
readonly property string currentThemeId: customThemeRawData?.id || ""
Component.onCompleted: { Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", stateDir]); Quickshell.execDetached(["mkdir", "-p", stateDir]);
@@ -604,21 +606,60 @@ Singleton {
function loadCustomTheme(themeData) { function loadCustomTheme(themeData) {
customThemeRawData = themeData; customThemeRawData = themeData;
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
var baseColors = {};
if (themeData.dark || themeData.light) { if (themeData.dark || themeData.light) {
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"; baseColors = themeData[colorMode] || themeData.dark || themeData.light || {};
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light;
customThemeData = selectedTheme;
} else { } else {
customThemeData = themeData; baseColors = themeData;
} }
if (themeData.variants && themeData.variants.options && themeData.variants.options.length > 0) {
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);
generateSystemThemesFromCurrentTheme();
return;
}
}
customThemeData = baseColors;
generateSystemThemesFromCurrentTheme(); generateSystemThemesFromCurrentTheme();
} }
function findVariant(options, variantId) {
if (!variantId || !options)
return null;
for (var i = 0; i < options.length; i++) {
if (options[i].id === variantId)
return options[i];
}
return options[0] || null;
}
function mergeColors(base, overlay) {
var result = JSON.parse(JSON.stringify(base));
for (var key in overlay) {
if (overlay[key])
result[key] = overlay[key];
}
return result;
}
function loadCustomThemeFromFile(filePath) { function loadCustomThemeFromFile(filePath) {
customThemeFileView.path = filePath; customThemeFileView.path = filePath;
} }
function reloadCustomThemeVariant() {
if (currentTheme !== "custom" || !customThemeRawData)
return;
loadCustomTheme(customThemeRawData);
}
property alias availableThemeNames: root._availableThemeNames property alias availableThemeNames: root._availableThemeNames
readonly property var _availableThemeNames: StockThemes.getAllThemeNames() readonly property var _availableThemeNames: StockThemes.getAllThemeNames()
property string currentThemeName: currentTheme property string currentThemeName: currentTheme
@@ -912,6 +953,16 @@ Singleton {
if (customThemeRawData && (customThemeRawData.dark || customThemeRawData.light)) { if (customThemeRawData && (customThemeRawData.dark || customThemeRawData.light)) {
darkTheme = customThemeRawData.dark || customThemeRawData.light; darkTheme = customThemeRawData.dark || customThemeRawData.light;
lightTheme = customThemeRawData.light || customThemeRawData.dark; lightTheme = customThemeRawData.light || customThemeRawData.dark;
if (customThemeRawData.variants && customThemeRawData.variants.options) {
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 || {});
}
}
} else { } else {
darkTheme = customThemeData; darkTheme = customThemeData;
lightTheme = customThemeData; lightTheme = customThemeData;

View File

@@ -9,6 +9,7 @@ var SPEC = {
currentThemeName: { def: "blue", onChange: "applyStoredTheme" }, currentThemeName: { def: "blue", onChange: "applyStoredTheme" },
currentThemeCategory: { def: "generic" }, currentThemeCategory: { def: "generic" },
customThemeFile: { def: "" }, customThemeFile: { def: "" },
registryThemeVariants: { def: {} },
matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" }, matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" },
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" }, runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" }, matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
@@ -91,6 +92,7 @@ var SPEC = {
waveProgressEnabled: { def: true }, waveProgressEnabled: { def: true },
scrollTitleEnabled: { def: true }, scrollTitleEnabled: { def: true },
audioVisualizerEnabled: { def: true }, audioVisualizerEnabled: { def: true },
audioScrollEnabled: { def: true },
clockCompactMode: { def: false }, clockCompactMode: { def: false },
focusedWindowCompactMode: { def: false }, focusedWindowCompactMode: { def: false },
runningAppsCompactMode: { def: true }, runningAppsCompactMode: { def: true },

View File

@@ -96,12 +96,7 @@ PanelWindow {
} }
} }
WlrLayershell.layer: { WlrLayershell.layer: dBarLayer
if ((barConfig?.autoHide ?? false) && topBarCore.reveal) {
return WlrLayer.Overlay;
}
return dBarLayer;
}
WlrLayershell.namespace: "dms:bar" WlrLayershell.namespace: "dms:bar"
signal colorPickerRequested signal colorPickerRequested

View File

@@ -52,9 +52,12 @@ BasePill {
property real touchpadThreshold: 100 property real touchpadThreshold: 100
onWheel: function (wheelEvent) { onWheel: function (wheelEvent) {
wheelEvent.accepted = true;
if (!usePlayerVolume) if (!usePlayerVolume)
return; return;
if (!SettingsData.audioScrollEnabled)
return;
wheelEvent.accepted = true;
const deltaY = wheelEvent.angleDelta.y; const deltaY = wheelEvent.angleDelta.y;
const isMouseWheelY = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0; const isMouseWheelY = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;

View File

@@ -43,6 +43,13 @@ Item {
checked: SettingsData.audioVisualizerEnabled checked: SettingsData.audioVisualizerEnabled
onToggled: checked => SettingsData.set("audioVisualizerEnabled", checked) onToggled: checked => SettingsData.set("audioVisualizerEnabled", checked)
} }
SettingsToggleRow {
text: I18n.tr("Scroll Wheel")
description: I18n.tr("Scroll on widget changes media volume")
checked: SettingsData.audioScrollEnabled
onToggled: checked => SettingsData.set("audioScrollEnabled", checked)
}
} }
} }
} }

View File

@@ -382,13 +382,23 @@ FloatingWindow {
} }
delegate: Rectangle { delegate: Rectangle {
id: themeDelegate
width: themeBrowserList.width width: themeBrowserList.width
height: hasPreview ? 140 : themeDelegateContent.implicitHeight + Theme.spacingM * 2 height: hasPreview ? 140 : themeDelegateContent.implicitHeight + Theme.spacingM * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex
property bool isInstalled: modelData.installed || false property bool isInstalled: modelData.installed || false
property bool isFirstParty: modelData.firstParty || false property bool isFirstParty: modelData.firstParty || false
property string previewPath: "/tmp/dankdots-plugin-registry/themes/" + (modelData.sourceDir || modelData.id) + "/preview-" + (Theme.isLightMode ? "light" : "dark") + ".svg" 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 previewPath: {
const baseDir = "/tmp/dankdots-plugin-registry/themes/" + (modelData.sourceDir || modelData.id);
const mode = Theme.isLightMode ? "light" : "dark";
if (hasVariants && selectedVariantId)
return baseDir + "/preview-" + selectedVariantId + "-" + mode + ".svg";
return baseDir + "/preview-" + mode + ".svg";
}
property bool hasPreview: previewImage.status === Image.Ready property bool hasPreview: previewImage.status === Image.Ready
color: isSelected ? Theme.primarySelected : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) color: isSelected ? Theme.primarySelected : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
border.color: isSelected ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) border.color: isSelected ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
@@ -479,6 +489,26 @@ FloatingWindow {
font.weight: Font.Medium font.weight: Font.Medium
} }
} }
Rectangle {
height: 18
width: variantsText.implicitWidth + Theme.spacingS
radius: 9
color: Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.15)
border.color: Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.4)
border.width: 1
visible: themeDelegate.hasVariants
anchors.verticalCenter: parent.verticalCenter
StyledText {
id: variantsText
anchors.centerIn: parent
text: I18n.tr("%1 variants").arg(themeDelegate.variants?.options?.length ?? 0)
font.pixelSize: Theme.fontSizeSmall
color: Theme.secondary
font.weight: Font.Medium
}
}
} }
StyledText { StyledText {
@@ -495,10 +525,44 @@ FloatingWindow {
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
maximumLineCount: 3 maximumLineCount: themeDelegate.hasVariants ? 2 : 3
elide: Text.ElideRight elide: Text.ElideRight
visible: modelData.description && modelData.description.length > 0 visible: modelData.description && modelData.description.length > 0
} }
Flow {
width: parent.width
spacing: Theme.spacingXS
visible: themeDelegate.hasVariants
Repeater {
model: themeDelegate.variants?.options ?? []
Rectangle {
property bool isActive: themeDelegate.selectedVariantId === modelData.id
height: 22
width: variantChipText.implicitWidth + Theme.spacingS * 2
radius: 11
color: isActive ? Theme.primary : Theme.surfaceContainerHigh
border.color: isActive ? Theme.primary : Theme.outline
border.width: 1
StyledText {
id: variantChipText
anchors.centerIn: parent
text: modelData.name
font.pixelSize: Theme.fontSizeSmall
color: isActive ? Theme.primaryText : Theme.surfaceText
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: themeDelegate.selectedVariantId = modelData.id
}
}
}
}
} }
Rectangle { Rectangle {

View File

@@ -108,317 +108,184 @@ Item {
} }
Column { Column {
id: themeCategoryColumn
spacing: Theme.spacingM spacing: Theme.spacingM
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
DankButtonGroup { Item {
id: themeCategoryGroup width: parent.width
property bool isRegistryTheme: Theme.currentThemeCategory === "registry" height: themeCategoryGroup.implicitHeight
property int currentThemeIndex: { clip: true
if (isRegistryTheme)
return 4;
if (Theme.currentTheme === Theme.dynamic)
return 2;
if (Theme.currentThemeName === "custom")
return 3;
if (Theme.currentThemeCategory === "catppuccin")
return 1;
return 0;
}
property int pendingThemeIndex: -1
model: DMSService.dmsAvailable ? ["Generic", "Catppuccin", "Auto", "Custom", "Registry"] : ["Generic", "Catppuccin", "Auto", "Custom"] DankButtonGroup {
currentIndex: currentThemeIndex id: themeCategoryGroup
selectionMode: "single" anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: parent.horizontalCenter buttonPadding: parent.width < 420 ? Theme.spacingS : Theme.spacingL
onSelectionChanged: (index, selected) => { minButtonWidth: parent.width < 420 ? 44 : 64
if (!selected) textSize: parent.width < 420 ? Theme.fontSizeSmall : Theme.fontSizeMedium
return; property bool isRegistryTheme: Theme.currentThemeCategory === "registry"
pendingThemeIndex = index; property int currentThemeIndex: {
} if (isRegistryTheme)
onAnimationCompleted: { return 4;
if (pendingThemeIndex === -1) if (Theme.currentTheme === Theme.dynamic)
return; return 2;
switch (pendingThemeIndex) { if (Theme.currentThemeName === "custom")
case 0: return 3;
Theme.switchThemeCategory("generic", "blue"); if (Theme.currentThemeCategory === "catppuccin")
break; return 1;
case 1: return 0;
Theme.switchThemeCategory("catppuccin", "cat-mauve"); }
break; property int pendingThemeIndex: -1
case 2:
if (ToastService.wallpaperErrorStatus === "matugen_missing") model: DMSService.dmsAvailable ? ["Generic", "Catppuccin", "Auto", "Custom", "Registry"] : ["Generic", "Catppuccin", "Auto", "Custom"]
ToastService.showError(I18n.tr("matugen not found - install matugen package for dynamic theming", "matugen error")); currentIndex: currentThemeIndex
else if (ToastService.wallpaperErrorStatus === "error") selectionMode: "single"
ToastService.showError(I18n.tr("Wallpaper processing failed - check wallpaper path", "wallpaper error")); onSelectionChanged: (index, selected) => {
else if (!selected)
Theme.switchThemeCategory("dynamic", Theme.dynamic); return;
break; pendingThemeIndex = index;
case 3: }
Theme.switchThemeCategory("custom", "custom"); onAnimationCompleted: {
break; if (pendingThemeIndex === -1)
case 4: return;
Theme.switchThemeCategory("registry", ""); switch (pendingThemeIndex) {
break; case 0:
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")
ToastService.showError(I18n.tr("Wallpaper processing failed - check wallpaper path", "wallpaper error"));
else
Theme.switchThemeCategory("dynamic", Theme.dynamic);
break;
case 3:
Theme.switchThemeCategory("custom", "custom");
break;
case 4:
Theme.switchThemeCategory("registry", "");
break;
}
pendingThemeIndex = -1;
} }
pendingThemeIndex = -1;
} }
} }
Column { Flow {
id: genericColorFlow
spacing: Theme.spacingS spacing: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter width: parent.width
visible: Theme.currentThemeCategory === "generic" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom" visible: Theme.currentThemeCategory === "generic" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom"
property int dotSize: width < 300 ? 28 : 32
Row { Repeater {
spacing: Theme.spacingM model: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
anchors.horizontalCenter: parent.horizontalCenter
Repeater { Rectangle {
model: ["blue", "purple", "green", "orange", "red"] 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
Rectangle { Rectangle {
required property string modelData width: nameText.contentWidth + Theme.spacingS * 2
property string themeName: modelData height: nameText.contentHeight + Theme.spacingXS * 2
width: 32 color: Theme.surfaceContainer
height: 32 radius: Theme.cornerRadius
radius: 16 anchors.bottom: parent.top
color: Theme.getThemeColors(themeName).primary anchors.bottomMargin: Theme.spacingXS
border.color: Theme.outline anchors.horizontalCenter: parent.horizontalCenter
border.width: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 2 : 1 visible: mouseArea.containsMouse
scale: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 1.1 : 1
Rectangle { StyledText {
width: nameText.contentWidth + Theme.spacingS * 2 id: nameText
height: nameText.contentHeight + Theme.spacingXS * 2 text: Theme.getThemeColors(parent.parent.themeName).name
color: Theme.surfaceContainer font.pixelSize: Theme.fontSizeSmall
radius: Theme.cornerRadius color: Theme.surfaceText
anchors.bottom: parent.top anchors.centerIn: parent
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
}
}
Behavior on border.width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
} }
} }
}
}
Row { MouseArea {
spacing: Theme.spacingM id: mouseArea
anchors.horizontalCenter: parent.horizontalCenter anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: Theme.switchTheme(parent.themeName)
}
Repeater { Behavior on scale {
model: ["cyan", "pink", "amber", "coral", "monochrome"] NumberAnimation {
duration: Theme.shortDuration
Rectangle { easing.type: Theme.emphasizedEasing
required property string modelData
property string themeName: modelData
width: 32
height: 32
radius: 16
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
Rectangle {
width: nameText2.contentWidth + Theme.spacingS * 2
height: nameText2.contentHeight + Theme.spacingXS * 2
color: Theme.surfaceContainer
radius: Theme.cornerRadius
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingXS
anchors.horizontalCenter: parent.horizontalCenter
visible: mouseArea2.containsMouse
StyledText {
id: nameText2
text: Theme.getThemeColors(parent.parent.themeName).name
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.centerIn: parent
}
}
MouseArea {
id: mouseArea2
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 border.width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
} }
} }
} }
} }
} }
Column { Flow {
id: catColorFlow
spacing: Theme.spacingS spacing: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter width: parent.width
visible: Theme.currentThemeCategory === "catppuccin" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom" visible: Theme.currentThemeCategory === "catppuccin" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom"
property int dotSize: width < 300 ? 28 : 32
Row { Repeater {
spacing: Theme.spacingM 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"]
anchors.horizontalCenter: parent.horizontalCenter
Repeater { Rectangle {
model: ["cat-rosewater", "cat-flamingo", "cat-pink", "cat-mauve", "cat-red", "cat-maroon", "cat-peach"] 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 { Rectangle {
required property string modelData width: nameTextCat.contentWidth + Theme.spacingS * 2
property string themeName: modelData height: nameTextCat.contentHeight + Theme.spacingXS * 2
width: 32 color: Theme.surfaceContainer
height: 32 radius: Theme.cornerRadius
radius: 16 anchors.bottom: parent.top
color: Theme.getCatppuccinColor(themeName) anchors.bottomMargin: Theme.spacingXS
border.color: Theme.outline anchors.horizontalCenter: parent.horizontalCenter
border.width: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 2 : 1 visible: mouseAreaCat.containsMouse
scale: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 1.1 : 1
Rectangle { StyledText {
width: nameTextCat.contentWidth + Theme.spacingS * 2 id: nameTextCat
height: nameTextCat.contentHeight + Theme.spacingXS * 2 text: Theme.getCatppuccinVariantName(parent.parent.themeName)
color: Theme.surfaceContainer font.pixelSize: Theme.fontSizeSmall
radius: Theme.cornerRadius color: Theme.surfaceText
anchors.bottom: parent.top anchors.centerIn: parent
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 border.width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
} }
} }
}
}
Row { MouseArea {
spacing: Theme.spacingM id: mouseAreaCat
anchors.horizontalCenter: parent.horizontalCenter anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: Theme.switchTheme(parent.themeName)
}
Repeater { Behavior on scale {
model: ["cat-yellow", "cat-green", "cat-teal", "cat-sky", "cat-sapphire", "cat-blue", "cat-lavender"] NumberAnimation {
duration: Theme.shortDuration
Rectangle { easing.type: Theme.emphasizedEasing
required property string modelData
property string themeName: modelData
width: 32
height: 32
radius: 16
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: nameTextCat2.contentWidth + Theme.spacingS * 2
height: nameTextCat2.contentHeight + Theme.spacingXS * 2
color: Theme.surfaceContainer
radius: Theme.cornerRadius
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingXS
anchors.horizontalCenter: parent.horizontalCenter
visible: mouseAreaCat2.containsMouse
StyledText {
id: nameTextCat2
text: Theme.getCatppuccinVariantName(parent.parent.themeName)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.centerIn: parent
}
}
MouseArea {
id: mouseAreaCat2
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 border.width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
} }
} }
} }
@@ -608,7 +475,10 @@ Item {
visible: Theme.currentThemeCategory === "registry" visible: Theme.currentThemeCategory === "registry"
Grid { Grid {
columns: 3 id: themeGrid
property int cardWidth: registrySection.width < 350 ? 100 : 140
property int cardHeight: registrySection.width < 350 ? 72 : 100
columns: Math.max(1, Math.floor((registrySection.width + spacing) / (cardWidth + spacing)))
spacing: Theme.spacingS spacing: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: themeColorsTab.installedRegistryThemes.length > 0 visible: themeColorsTab.installedRegistryThemes.length > 0
@@ -619,9 +489,18 @@ Item {
Rectangle { Rectangle {
id: themeCard id: themeCard
property bool isActive: Theme.currentThemeCategory === "registry" && Theme.currentThemeName === "custom" && SettingsData.customThemeFile && SettingsData.customThemeFile.endsWith((modelData.sourceDir || modelData.id) + "/theme.json") property bool isActive: Theme.currentThemeCategory === "registry" && Theme.currentThemeName === "custom" && SettingsData.customThemeFile && SettingsData.customThemeFile.endsWith((modelData.sourceDir || modelData.id) + "/theme.json")
property string previewPath: Quickshell.env("HOME") + "/.config/DankMaterialShell/themes/" + (modelData.sourceDir || modelData.id) + "/preview-" + (Theme.isLightMode ? "light" : "dark") + ".svg" property bool hasVariants: modelData.hasVariants || false
width: 140 property var variants: modelData.variants || null
height: 100 property string selectedVariant: hasVariants ? SettingsData.getRegistryThemeVariant(modelData.id, variants?.default || "") : ""
property string previewPath: {
const baseDir = Quickshell.env("HOME") + "/.config/DankMaterialShell/themes/" + (modelData.sourceDir || modelData.id);
const mode = Theme.isLightMode ? "light" : "dark";
if (hasVariants && selectedVariant)
return baseDir + "/preview-" + selectedVariant + "-" + mode + ".svg";
return baseDir + "/preview-" + mode + ".svg";
}
width: themeGrid.cardWidth
height: themeGrid.cardHeight
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceVariant color: Theme.surfaceVariant
border.color: isActive ? Theme.primary : Theme.outline border.color: isActive ? Theme.primary : Theme.outline
@@ -648,7 +527,7 @@ Item {
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: "palette" name: "palette"
size: 32 size: themeGrid.cardWidth < 120 ? 24 : 32
color: Theme.primary color: Theme.primary
visible: previewImage.status === Image.Error || previewImage.status === Image.Null visible: previewImage.status === Image.Error || previewImage.status === Image.Null
} }
@@ -657,18 +536,18 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
height: 24 height: themeGrid.cardWidth < 120 ? 18 : 22
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(0, 0, 0, 0.6) color: Qt.rgba(0, 0, 0, 0.6)
StyledText { StyledText {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData.name text: modelData.name
font.pixelSize: Theme.fontSizeSmall font.pixelSize: themeGrid.cardWidth < 120 ? Theme.fontSizeSmall - 2 : Theme.fontSizeSmall
color: "white" color: "white"
font.weight: Font.Medium font.weight: Font.Medium
elide: Text.ElideRight elide: Text.ElideRight
width: parent.width - Theme.spacingS * 2 width: parent.width - Theme.spacingXS * 2
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
} }
@@ -676,21 +555,40 @@ Item {
Rectangle { Rectangle {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: 4 anchors.margins: themeGrid.cardWidth < 120 ? 2 : 4
width: 20 width: themeGrid.cardWidth < 120 ? 16 : 20
height: 20 height: width
radius: 10 radius: width / 2
color: Theme.primary color: Theme.primary
visible: themeCard.isActive visible: themeCard.isActive
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: "check" name: "check"
size: 14 size: themeGrid.cardWidth < 120 ? 10 : 14
color: Theme.surface color: Theme.surface
} }
} }
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: themeGrid.cardWidth < 120 ? 2 : 4
width: themeGrid.cardWidth < 120 ? 16 : 20
height: width
radius: width / 2
color: Theme.secondary
visible: themeCard.hasVariants && !deleteButton.visible
StyledText {
anchors.centerIn: parent
text: themeCard.variants?.options?.length || 0
font.pixelSize: themeGrid.cardWidth < 120 ? Theme.fontSizeSmall - 4 : Theme.fontSizeSmall - 2
color: Theme.surface
font.weight: Font.Bold
}
}
MouseArea { MouseArea {
id: cardMouseArea id: cardMouseArea
anchors.fill: parent anchors.fill: parent
@@ -708,10 +606,10 @@ Item {
id: deleteButton id: deleteButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.margins: 4 anchors.margins: themeGrid.cardWidth < 120 ? 2 : 4
width: 24 width: themeGrid.cardWidth < 120 ? 18 : 24
height: 24 height: width
radius: 12 radius: width / 2
color: deleteMouseArea.containsMouse ? Theme.error : Qt.rgba(0, 0, 0, 0.6) color: deleteMouseArea.containsMouse ? Theme.error : Qt.rgba(0, 0, 0, 0.6)
opacity: cardMouseArea.containsMouse || deleteMouseArea.containsMouse ? 1 : 0 opacity: cardMouseArea.containsMouse || deleteMouseArea.containsMouse ? 1 : 0
visible: opacity > 0 visible: opacity > 0
@@ -725,7 +623,7 @@ Item {
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: "close" name: "close"
size: 14 size: themeGrid.cardWidth < 120 ? 10 : 14
color: "white" color: "white"
} }
@@ -751,6 +649,75 @@ Item {
} }
} }
Column {
id: variantSelector
width: parent.width
spacing: Theme.spacingS
visible: activeThemeVariants !== null && activeThemeVariants.options && activeThemeVariants.options.length > 0
property string activeThemeId: {
if (Theme.currentThemeCategory !== "registry")
return "";
for (var i = 0; i < themeColorsTab.installedRegistryThemes.length; i++) {
var t = themeColorsTab.installedRegistryThemes[i];
if (SettingsData.customThemeFile && SettingsData.customThemeFile.endsWith((t.sourceDir || t.id) + "/theme.json"))
return t.id;
}
return "";
}
property var activeThemeVariants: {
if (!activeThemeId)
return null;
for (var i = 0; i < themeColorsTab.installedRegistryThemes.length; i++) {
var t = themeColorsTab.installedRegistryThemes[i];
if (t.id === activeThemeId && t.hasVariants)
return t.variants;
}
return null;
}
property string selectedVariant: activeThemeId ? SettingsData.getRegistryThemeVariant(activeThemeId, activeThemeVariants?.default || "") : ""
property var variantNames: {
if (!activeThemeVariants?.options)
return [];
return activeThemeVariants.options.map(v => v.name);
}
property int selectedIndex: {
if (!activeThemeVariants?.options || !selectedVariant)
return 0;
for (var i = 0; i < activeThemeVariants.options.length; i++) {
if (activeThemeVariants.options[i].id === selectedVariant)
return i;
}
return 0;
}
Item {
width: parent.width
height: variantButtonGroup.implicitHeight
clip: true
DankButtonGroup {
id: variantButtonGroup
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.variantNames
currentIndex: variantSelector.selectedIndex
selectionMode: "single"
onAnimationCompleted: {
if (currentIndex < 0 || !variantSelector.activeThemeVariants?.options)
return;
const variantId = variantSelector.activeThemeVariants.options[currentIndex]?.id;
if (!variantId || variantId === variantSelector.selectedVariant)
return;
Theme.screenTransition();
SettingsData.setRegistryThemeVariant(variantSelector.activeThemeId, variantId);
}
}
}
}
StyledText { StyledText {
text: I18n.tr("No themes installed. Browse themes to install from the registry.", "no registry themes installed hint") text: I18n.tr("No themes installed. Browse themes to install from the registry.", "no registry themes installed hint")
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall