mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
themes: support for variants
This commit is contained in:
@@ -31,7 +31,7 @@ func HandleList(conn net.Conn, req models.Request) {
|
||||
result := make([]ThemeInfo, len(themeList))
|
||||
for i, t := range themeList {
|
||||
installed, _ := manager.IsInstalled(t)
|
||||
result[i] = ThemeInfo{
|
||||
info := ThemeInfo{
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Version: t.Version,
|
||||
@@ -42,6 +42,17 @@ 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}
|
||||
}
|
||||
}
|
||||
result[i] = info
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, result)
|
||||
|
||||
@@ -8,6 +8,20 @@ import (
|
||||
"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) {
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
@@ -46,7 +60,7 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
|
||||
hasUpdate = hasUpdates
|
||||
}
|
||||
|
||||
result = append(result, ThemeInfo{
|
||||
info := ThemeInfo{
|
||||
ID: theme.ID,
|
||||
Name: theme.Name,
|
||||
Version: theme.Version,
|
||||
@@ -55,7 +69,9 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
|
||||
SourceDir: id,
|
||||
FirstParty: isFirstParty(theme.Author),
|
||||
HasUpdate: hasUpdate,
|
||||
})
|
||||
}
|
||||
addVariantsInfo(&info, theme.Variants)
|
||||
result = append(result, info)
|
||||
} else {
|
||||
installed, err := manager.GetInstalledTheme(id)
|
||||
if err != nil {
|
||||
@@ -66,7 +82,7 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
|
||||
})
|
||||
continue
|
||||
}
|
||||
result = append(result, ThemeInfo{
|
||||
info := ThemeInfo{
|
||||
ID: installed.ID,
|
||||
Name: installed.Name,
|
||||
Version: installed.Version,
|
||||
@@ -74,7 +90,9 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
|
||||
Description: installed.Description,
|
||||
SourceDir: id,
|
||||
FirstParty: isFirstParty(installed.Author),
|
||||
})
|
||||
}
|
||||
addVariantsInfo(&info, installed.Variants)
|
||||
result = append(result, info)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
package themes
|
||||
|
||||
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"`
|
||||
type VariantInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type VariantsInfo struct {
|
||||
Default string `json:"default,omitempty"`
|
||||
Options []VariantInfo `json:"options,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"`
|
||||
}
|
||||
|
||||
@@ -80,21 +80,35 @@ func (m *Manager) Install(theme Theme, registryThemeDir string) error {
|
||||
return fmt.Errorf("failed to write theme file: %w", err)
|
||||
}
|
||||
|
||||
for _, preview := range []string{"preview-dark.svg", "preview-light.svg"} {
|
||||
srcPath := filepath.Join(registryThemeDir, preview)
|
||||
exists, _ := afero.Exists(m.fs, srcPath)
|
||||
if !exists {
|
||||
m.copyPreviewFiles(registryThemeDir, themeDir, theme)
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
data, err := afero.ReadFile(m.fs, srcPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dstPath := filepath.Join(themeDir, preview)
|
||||
dstPath := filepath.Join(dstDir, preview)
|
||||
_ = afero.WriteFile(m.fs, dstPath, data, 0644)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) InstallFromRegistry(registry *Registry, themeID string) error {
|
||||
|
||||
@@ -13,35 +13,49 @@ import (
|
||||
const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git"
|
||||
|
||||
type ColorScheme struct {
|
||||
Primary string `json:"primary"`
|
||||
PrimaryText string `json:"primaryText"`
|
||||
PrimaryContainer string `json:"primaryContainer"`
|
||||
Secondary string `json:"secondary"`
|
||||
Surface string `json:"surface"`
|
||||
SurfaceText string `json:"surfaceText"`
|
||||
SurfaceVariant string `json:"surfaceVariant"`
|
||||
SurfaceVariantText string `json:"surfaceVariantText"`
|
||||
SurfaceTint string `json:"surfaceTint"`
|
||||
Background string `json:"background"`
|
||||
BackgroundText string `json:"backgroundText"`
|
||||
Outline string `json:"outline"`
|
||||
SurfaceContainer string `json:"surfaceContainer"`
|
||||
SurfaceContainerHigh string `json:"surfaceContainerHigh"`
|
||||
Error string `json:"error"`
|
||||
Warning string `json:"warning"`
|
||||
Info string `json:"info"`
|
||||
Primary string `json:"primary,omitempty"`
|
||||
PrimaryText string `json:"primaryText,omitempty"`
|
||||
PrimaryContainer string `json:"primaryContainer,omitempty"`
|
||||
Secondary string `json:"secondary,omitempty"`
|
||||
Surface string `json:"surface,omitempty"`
|
||||
SurfaceText string `json:"surfaceText,omitempty"`
|
||||
SurfaceVariant string `json:"surfaceVariant,omitempty"`
|
||||
SurfaceVariantText string `json:"surfaceVariantText,omitempty"`
|
||||
SurfaceTint string `json:"surfaceTint,omitempty"`
|
||||
Background string `json:"background,omitempty"`
|
||||
BackgroundText string `json:"backgroundText,omitempty"`
|
||||
Outline string `json:"outline,omitempty"`
|
||||
SurfaceContainer string `json:"surfaceContainer,omitempty"`
|
||||
SurfaceContainerHigh string `json:"surfaceContainerHigh,omitempty"`
|
||||
SurfaceContainerHighest string `json:"surfaceContainerHighest,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
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 {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Author string `json:"author"`
|
||||
Description string `json:"description"`
|
||||
Dark ColorScheme `json:"dark"`
|
||||
Light ColorScheme `json:"light"`
|
||||
PreviewPath string `json:"-"`
|
||||
SourceDir string `json:"sourceDir,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Author string `json:"author"`
|
||||
Description string `json:"description"`
|
||||
Dark ColorScheme `json:"dark"`
|
||||
Light ColorScheme `json:"light"`
|
||||
Variants *ThemeVariants `json:"variants,omitempty"`
|
||||
PreviewPath string `json:"-"`
|
||||
SourceDir string `json:"sourceDir,omitempty"`
|
||||
}
|
||||
|
||||
type GitClient interface {
|
||||
|
||||
@@ -65,6 +65,7 @@ Singleton {
|
||||
property string currentThemeName: "blue"
|
||||
property string currentThemeCategory: "generic"
|
||||
property string customThemeFile: ""
|
||||
property var registryThemeVariants: ({})
|
||||
property string matugenScheme: "scheme-tonal-spot"
|
||||
property bool runUserMatugenTemplates: true
|
||||
property string matugenTargetMonitor: ""
|
||||
@@ -1556,6 +1557,19 @@ Singleton {
|
||||
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() {
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
|
||||
@@ -93,6 +93,8 @@ Singleton {
|
||||
property var _pendingGenerateParams: null
|
||||
property var customThemeData: null
|
||||
property var customThemeRawData: null
|
||||
readonly property var currentThemeVariants: customThemeRawData?.variants || null
|
||||
readonly property string currentThemeId: customThemeRawData?.id || ""
|
||||
|
||||
Component.onCompleted: {
|
||||
Quickshell.execDetached(["mkdir", "-p", stateDir]);
|
||||
@@ -604,21 +606,60 @@ Singleton {
|
||||
|
||||
function loadCustomTheme(themeData) {
|
||||
customThemeRawData = themeData;
|
||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
||||
|
||||
var baseColors = {};
|
||||
if (themeData.dark || themeData.light) {
|
||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
||||
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light;
|
||||
customThemeData = selectedTheme;
|
||||
baseColors = themeData[colorMode] || themeData.dark || themeData.light || {};
|
||||
} 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();
|
||||
}
|
||||
|
||||
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) {
|
||||
customThemeFileView.path = filePath;
|
||||
}
|
||||
|
||||
function reloadCustomThemeVariant() {
|
||||
if (currentTheme !== "custom" || !customThemeRawData)
|
||||
return;
|
||||
loadCustomTheme(customThemeRawData);
|
||||
}
|
||||
|
||||
property alias availableThemeNames: root._availableThemeNames
|
||||
readonly property var _availableThemeNames: StockThemes.getAllThemeNames()
|
||||
property string currentThemeName: currentTheme
|
||||
@@ -912,6 +953,16 @@ Singleton {
|
||||
if (customThemeRawData && (customThemeRawData.dark || customThemeRawData.light)) {
|
||||
darkTheme = customThemeRawData.dark || customThemeRawData.light;
|
||||
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 {
|
||||
darkTheme = customThemeData;
|
||||
lightTheme = customThemeData;
|
||||
|
||||
@@ -9,6 +9,7 @@ var SPEC = {
|
||||
currentThemeName: { def: "blue", onChange: "applyStoredTheme" },
|
||||
currentThemeCategory: { def: "generic" },
|
||||
customThemeFile: { def: "" },
|
||||
registryThemeVariants: { def: {} },
|
||||
matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" },
|
||||
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
|
||||
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
|
||||
|
||||
@@ -382,13 +382,23 @@ FloatingWindow {
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
id: themeDelegate
|
||||
width: themeBrowserList.width
|
||||
height: hasPreview ? 140 : themeDelegateContent.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex
|
||||
property bool isInstalled: modelData.installed || 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
|
||||
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)
|
||||
@@ -479,6 +489,26 @@ FloatingWindow {
|
||||
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 {
|
||||
@@ -495,10 +525,44 @@ FloatingWindow {
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 3
|
||||
maximumLineCount: themeDelegate.hasVariants ? 2 : 3
|
||||
elide: Text.ElideRight
|
||||
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 {
|
||||
|
||||
@@ -108,317 +108,184 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
id: themeCategoryColumn
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
|
||||
DankButtonGroup {
|
||||
id: themeCategoryGroup
|
||||
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")
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
property int pendingThemeIndex: -1
|
||||
Item {
|
||||
width: parent.width
|
||||
height: themeCategoryGroup.implicitHeight
|
||||
clip: true
|
||||
|
||||
model: DMSService.dmsAvailable ? ["Generic", "Catppuccin", "Auto", "Custom", "Registry"] : ["Generic", "Catppuccin", "Auto", "Custom"]
|
||||
currentIndex: currentThemeIndex
|
||||
selectionMode: "single"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
pendingThemeIndex = index;
|
||||
}
|
||||
onAnimationCompleted: {
|
||||
if (pendingThemeIndex === -1)
|
||||
return;
|
||||
switch (pendingThemeIndex) {
|
||||
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;
|
||||
DankButtonGroup {
|
||||
id: themeCategoryGroup
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
buttonPadding: parent.width < 420 ? Theme.spacingS : Theme.spacingL
|
||||
minButtonWidth: parent.width < 420 ? 44 : 64
|
||||
textSize: parent.width < 420 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||
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")
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
property int pendingThemeIndex: -1
|
||||
|
||||
model: DMSService.dmsAvailable ? ["Generic", "Catppuccin", "Auto", "Custom", "Registry"] : ["Generic", "Catppuccin", "Auto", "Custom"]
|
||||
currentIndex: currentThemeIndex
|
||||
selectionMode: "single"
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
pendingThemeIndex = index;
|
||||
}
|
||||
onAnimationCompleted: {
|
||||
if (pendingThemeIndex === -1)
|
||||
return;
|
||||
switch (pendingThemeIndex) {
|
||||
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
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
visible: Theme.currentThemeCategory === "generic" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom"
|
||||
property int dotSize: width < 300 ? 28 : 32
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Repeater {
|
||||
model: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
|
||||
|
||||
Repeater {
|
||||
model: ["blue", "purple", "green", "orange", "red"]
|
||||
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
|
||||
|
||||
Rectangle {
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
StyledText {
|
||||
id: nameText
|
||||
text: Theme.getThemeColors(parent.parent.themeName).name
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: Theme.switchTheme(parent.themeName)
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ["cyan", "pink", "amber", "coral", "monochrome"]
|
||||
|
||||
Rectangle {
|
||||
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
|
||||
}
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
Flow {
|
||||
id: catColorFlow
|
||||
spacing: Theme.spacingS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
visible: Theme.currentThemeCategory === "catppuccin" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom"
|
||||
property int dotSize: width < 300 ? 28 : 32
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
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"]
|
||||
|
||||
Repeater {
|
||||
model: ["cat-rosewater", "cat-flamingo", "cat-pink", "cat-mauve", "cat-red", "cat-maroon", "cat-peach"]
|
||||
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 {
|
||||
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
|
||||
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
|
||||
|
||||
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 border.width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
StyledText {
|
||||
id: nameTextCat
|
||||
text: Theme.getCatppuccinVariantName(parent.parent.themeName)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
MouseArea {
|
||||
id: mouseAreaCat
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: Theme.switchTheme(parent.themeName)
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ["cat-yellow", "cat-green", "cat-teal", "cat-sky", "cat-sapphire", "cat-blue", "cat-lavender"]
|
||||
|
||||
Rectangle {
|
||||
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
|
||||
}
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,7 +475,10 @@ Item {
|
||||
visible: Theme.currentThemeCategory === "registry"
|
||||
|
||||
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
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: themeColorsTab.installedRegistryThemes.length > 0
|
||||
@@ -619,9 +489,18 @@ Item {
|
||||
Rectangle {
|
||||
id: themeCard
|
||||
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"
|
||||
width: 140
|
||||
height: 100
|
||||
property bool hasVariants: modelData.hasVariants || false
|
||||
property var variants: modelData.variants || null
|
||||
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
|
||||
color: Theme.surfaceVariant
|
||||
border.color: isActive ? Theme.primary : Theme.outline
|
||||
@@ -648,7 +527,7 @@ Item {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "palette"
|
||||
size: 32
|
||||
size: themeGrid.cardWidth < 120 ? 24 : 32
|
||||
color: Theme.primary
|
||||
visible: previewImage.status === Image.Error || previewImage.status === Image.Null
|
||||
}
|
||||
@@ -657,18 +536,18 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 24
|
||||
height: themeGrid.cardWidth < 120 ? 18 : 22
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(0, 0, 0, 0.6)
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.pixelSize: themeGrid.cardWidth < 120 ? Theme.fontSizeSmall - 2 : Theme.fontSizeSmall
|
||||
color: "white"
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
width: parent.width - Theme.spacingXS * 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
@@ -676,21 +555,40 @@ Item {
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 4
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
anchors.margins: themeGrid.cardWidth < 120 ? 2 : 4
|
||||
width: themeGrid.cardWidth < 120 ? 16 : 20
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: Theme.primary
|
||||
visible: themeCard.isActive
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "check"
|
||||
size: 14
|
||||
size: themeGrid.cardWidth < 120 ? 10 : 14
|
||||
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 {
|
||||
id: cardMouseArea
|
||||
anchors.fill: parent
|
||||
@@ -708,10 +606,10 @@ Item {
|
||||
id: deleteButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 4
|
||||
width: 24
|
||||
height: 24
|
||||
radius: 12
|
||||
anchors.margins: themeGrid.cardWidth < 120 ? 2 : 4
|
||||
width: themeGrid.cardWidth < 120 ? 18 : 24
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: deleteMouseArea.containsMouse ? Theme.error : Qt.rgba(0, 0, 0, 0.6)
|
||||
opacity: cardMouseArea.containsMouse || deleteMouseArea.containsMouse ? 1 : 0
|
||||
visible: opacity > 0
|
||||
@@ -725,7 +623,7 @@ Item {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "close"
|
||||
size: 14
|
||||
size: themeGrid.cardWidth < 120 ? 10 : 14
|
||||
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 {
|
||||
text: I18n.tr("No themes installed. Browse themes to install from the registry.", "no registry themes installed hint")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
|
||||
Reference in New Issue
Block a user