mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-02 10:32:07 -04:00
Compare commits
12 Commits
86c0064ff9
...
997011e008
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
997011e008 | ||
|
|
2504396435 | ||
|
|
d206723b36 | ||
|
|
a0ec3d59b8 | ||
|
|
17ef08aa58 | ||
|
|
57279d1c53 | ||
|
|
8b003ac9cd | ||
|
|
0ea10b0ad2 | ||
|
|
2db4c9daa0 | ||
|
|
363964e90b | ||
|
|
a7b49eba70 | ||
|
|
4ae334f60f |
6
.github/workflows/update-vendor-hash.yml
vendored
6
.github/workflows/update-vendor-hash.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
echo "Build succeeded, no hash update needed"
|
||||
exit 0
|
||||
fi
|
||||
new_hash=$(echo "$output" | grep -oP "got:\s+\K\S+" | head -n1)
|
||||
new_hash=$(echo "$output" | grep -oP "got:\s+\K\S+" | head -n1 || true)
|
||||
[ -n "$new_hash" ] || { echo "Could not extract new vendorHash"; echo "$output"; exit 1; }
|
||||
current_hash=$(grep -oP 'vendorHash = "\K[^"]+' flake.nix)
|
||||
[ "$current_hash" = "$new_hash" ] && { echo "vendorHash already up to date"; exit 0; }
|
||||
@@ -59,8 +59,8 @@ jobs:
|
||||
git config user.email "dms-ci[bot]@users.noreply.github.com"
|
||||
git add flake.nix
|
||||
git commit -m "nix: update vendorHash for go.mod changes" || exit 0
|
||||
git pull --rebase origin master
|
||||
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:master
|
||||
git pull --rebase origin ${{ github.ref_name }}
|
||||
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:${{ github.ref_name }}
|
||||
else
|
||||
echo "No changes to flake.nix"
|
||||
fi
|
||||
|
||||
@@ -71,7 +71,7 @@ var templateRegistry = []TemplateDef{
|
||||
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
|
||||
{ID: "vscode", Kind: TemplateKindVSCode},
|
||||
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml", Kind: TemplateKindEmacs},
|
||||
{ID: "zed", Commands: []string{"zed"}, ConfigFile: "zed.toml"},
|
||||
{ID: "zed", Commands: []string{"zed", "zeditor", "zedit"}, ConfigFile: "zed.toml"},
|
||||
}
|
||||
|
||||
func (c *ColorMode) GTKTheme() string {
|
||||
|
||||
@@ -2,10 +2,10 @@ package wlcontext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
@@ -123,6 +123,9 @@ func (sc *SharedContext) eventDispatcher() {
|
||||
{Fd: int32(sc.wakeR), Events: unix.POLLIN},
|
||||
}
|
||||
|
||||
consecutiveErrors := 0
|
||||
const maxConsecutiveErrors = 20
|
||||
|
||||
for {
|
||||
sc.drainCmdQueue()
|
||||
|
||||
@@ -153,9 +156,19 @@ func (sc *SharedContext) eventDispatcher() {
|
||||
}
|
||||
|
||||
if err := ctx.Dispatch(); err != nil && !os.IsTimeout(err) {
|
||||
log.Errorf("Wayland connection error: %v", err)
|
||||
return
|
||||
consecutiveErrors++
|
||||
log.Warnf("Wayland connection error (%d/%d): %v", consecutiveErrors, maxConsecutiveErrors, err)
|
||||
|
||||
if consecutiveErrors >= maxConsecutiveErrors {
|
||||
log.Errorf("Fatal: Wayland connection unrecoverable after %d attempts. Exiting dispatcher.", maxConsecutiveErrors)
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond * time.Duration(consecutiveErrors))
|
||||
continue
|
||||
}
|
||||
|
||||
consecutiveErrors = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
25
flake.nix
25
flake.nix
@@ -17,6 +17,25 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
goModVersion =
|
||||
let
|
||||
content = builtins.readFile ./core/go.mod;
|
||||
lines = builtins.filter builtins.isString (builtins.split "\n" content);
|
||||
goLines = builtins.filter (l: builtins.match "go [0-9]+\\..*" l != null) lines;
|
||||
matched =
|
||||
if goLines != [ ] then builtins.match "go ([0-9]+)\\.([0-9]+).*" (builtins.head goLines) else null;
|
||||
in
|
||||
if matched != null then
|
||||
{
|
||||
major = builtins.elemAt matched 0;
|
||||
minor = builtins.elemAt matched 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
major = "1";
|
||||
minor = "25";
|
||||
};
|
||||
goForPkgs = pkgs: pkgs.${"go_${goModVersion.major}_${goModVersion.minor}"};
|
||||
forEachSystem =
|
||||
fn:
|
||||
nixpkgs.lib.genAttrs [ "aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux" ] (
|
||||
@@ -76,7 +95,7 @@
|
||||
{
|
||||
extraQtPackages ? [ ],
|
||||
}:
|
||||
pkgs.buildGoModule (
|
||||
(pkgs.buildGoModule.override { go = goForPkgs pkgs; }) (
|
||||
let
|
||||
rootSrc = ./.;
|
||||
qtPackages = (qmlPkgs pkgs) ++ extraQtPackages;
|
||||
@@ -85,7 +104,7 @@
|
||||
inherit version;
|
||||
pname = "dms-shell";
|
||||
src = ./core;
|
||||
vendorHash = "sha256-dEk7IOd6aQwaxZruxQclN7TGMyb8EJOl6NBWRsoZ9HQ=";
|
||||
vendorHash = "sha256-cVUJXgzYMRSM0od1xzDVkMTdxHu3OIQX2bQ8AJbGQ1Q=";
|
||||
|
||||
subPackages = [ "cmd/dms" ];
|
||||
|
||||
@@ -187,7 +206,7 @@
|
||||
buildInputs =
|
||||
with pkgs;
|
||||
[
|
||||
go_1_25
|
||||
(goForPkgs pkgs)
|
||||
gopls
|
||||
delve
|
||||
go-tools
|
||||
|
||||
@@ -575,14 +575,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
if (!newSettings[identifier]) {
|
||||
newSettings[identifier] = {
|
||||
"enabled": false,
|
||||
"mode": "interval",
|
||||
"interval": 300,
|
||||
"time": "06:00"
|
||||
};
|
||||
}
|
||||
newSettings[identifier] = getMonitorCyclingSettings(screenName);
|
||||
newSettings[identifier].enabled = enabled;
|
||||
monitorCyclingSettings = newSettings;
|
||||
saveSettings();
|
||||
@@ -613,14 +606,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
if (!newSettings[identifier]) {
|
||||
newSettings[identifier] = {
|
||||
"enabled": false,
|
||||
"mode": "interval",
|
||||
"interval": 300,
|
||||
"time": "06:00"
|
||||
};
|
||||
}
|
||||
newSettings[identifier] = getMonitorCyclingSettings(screenName);
|
||||
newSettings[identifier].mode = mode;
|
||||
monitorCyclingSettings = newSettings;
|
||||
saveSettings();
|
||||
@@ -651,14 +637,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
if (!newSettings[identifier]) {
|
||||
newSettings[identifier] = {
|
||||
"enabled": false,
|
||||
"mode": "interval",
|
||||
"interval": 300,
|
||||
"time": "06:00"
|
||||
};
|
||||
}
|
||||
newSettings[identifier] = getMonitorCyclingSettings(screenName);
|
||||
newSettings[identifier].interval = interval;
|
||||
monitorCyclingSettings = newSettings;
|
||||
saveSettings();
|
||||
@@ -689,14 +668,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
if (!newSettings[identifier]) {
|
||||
newSettings[identifier] = {
|
||||
"enabled": false,
|
||||
"mode": "interval",
|
||||
"interval": 300,
|
||||
"time": "06:00"
|
||||
};
|
||||
}
|
||||
newSettings[identifier] = getMonitorCyclingSettings(screenName);
|
||||
newSettings[identifier].time = time;
|
||||
monitorCyclingSettings = newSettings;
|
||||
saveSettings();
|
||||
@@ -1205,7 +1177,7 @@ Singleton {
|
||||
"time": "06:00"
|
||||
};
|
||||
var value = _findMonitorValue(monitorCyclingSettings, screenName);
|
||||
return value !== undefined ? value : defaults;
|
||||
return Object.assign({}, defaults, value !== undefined ? value : {});
|
||||
}
|
||||
|
||||
FileView {
|
||||
|
||||
@@ -2,7 +2,6 @@ import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Modals.FileBrowser
|
||||
import qs.Services
|
||||
@@ -742,234 +741,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: variantSelector
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: activeThemeId !== "" && activeThemeVariants !== null && (isMultiVariant || (activeThemeVariants.options && activeThemeVariants.options.length > 0))
|
||||
|
||||
property string activeThemeId: {
|
||||
if (Theme.currentThemeCategory !== "registry" || Theme.currentTheme !== "custom")
|
||||
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 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, colorMode) : multiDefaults
|
||||
property string selectedFlavor: {
|
||||
var sf = storedMulti.flavor || multiDefaults.flavor || "";
|
||||
for (var i = 0; i < flavorOptions.length; i++) {
|
||||
if (flavorOptions[i].id === sf)
|
||||
return sf;
|
||||
}
|
||||
if (flavorOptions.length > 0)
|
||||
return flavorOptions[0].id;
|
||||
return sf;
|
||||
}
|
||||
property string selectedAccent: storedMulti.accent || multiDefaults.accent || ""
|
||||
property var flavorOptions: {
|
||||
if (!isMultiVariant || !activeThemeVariants?.flavors)
|
||||
return [];
|
||||
return activeThemeVariants.flavors.filter(f => {
|
||||
if (f.mode)
|
||||
return f.mode === colorMode || f.mode === "both";
|
||||
return !!f[colorMode];
|
||||
});
|
||||
}
|
||||
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)
|
||||
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: flavorButtonGroup.implicitHeight
|
||||
clip: true
|
||||
visible: variantSelector.isMultiVariant && variantSelector.flavorOptions.length > 1
|
||||
|
||||
DankButtonGroup {
|
||||
id: flavorButtonGroup
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
property int _count: variantSelector.flavorNames.length
|
||||
property real _maxPerItem: _count > 1 ? (parent.width - (_count - 1) * spacing) / _count : parent.width
|
||||
buttonPadding: _maxPerItem < 55 ? Theme.spacingXS : (_maxPerItem < 75 ? Theme.spacingS : Theme.spacingL)
|
||||
minButtonWidth: Math.min(_maxPerItem < 55 ? 28 : (_maxPerItem < 75 ? 44 : 64), Math.max(28, Math.floor(_maxPerItem)))
|
||||
textSize: _maxPerItem < 55 ? Theme.fontSizeSmall - 2 : (_maxPerItem < 75 ? Theme.fontSizeSmall : Theme.fontSizeMedium)
|
||||
checkEnabled: _maxPerItem >= 55
|
||||
property int pendingIndex: -1
|
||||
model: variantSelector.flavorNames
|
||||
currentIndex: pendingIndex >= 0 ? pendingIndex : variantSelector.flavorIndex
|
||||
selectionMode: "single"
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
pendingIndex = index;
|
||||
}
|
||||
onAnimationCompleted: {
|
||||
if (pendingIndex < 0 || pendingIndex >= variantSelector.flavorOptions.length)
|
||||
return;
|
||||
const flavorId = variantSelector.flavorOptions[pendingIndex]?.id;
|
||||
const idx = pendingIndex;
|
||||
pendingIndex = -1;
|
||||
if (!flavorId || flavorId === variantSelector.selectedFlavor)
|
||||
return;
|
||||
Theme.screenTransition();
|
||||
SettingsData.setRegistryThemeMultiVariant(variantSelector.activeThemeId, flavorId, variantSelector.selectedAccent, variantSelector.colorMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, variantSelector.colorMode);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
property int _count: variantSelector.variantNames.length
|
||||
property real _maxPerItem: _count > 1 ? (parent.width - (_count - 1) * spacing) / _count : parent.width
|
||||
buttonPadding: _maxPerItem < 55 ? Theme.spacingXS : (_maxPerItem < 75 ? Theme.spacingS : Theme.spacingL)
|
||||
minButtonWidth: Math.min(_maxPerItem < 55 ? 28 : (_maxPerItem < 75 ? 44 : 64), Math.max(28, Math.floor(_maxPerItem)))
|
||||
textSize: _maxPerItem < 55 ? Theme.fontSizeSmall - 2 : (_maxPerItem < 75 ? Theme.fontSizeSmall : Theme.fontSizeMedium)
|
||||
checkEnabled: _maxPerItem >= 55
|
||||
property int pendingIndex: -1
|
||||
model: variantSelector.variantNames
|
||||
currentIndex: pendingIndex >= 0 ? pendingIndex : variantSelector.selectedIndex
|
||||
selectionMode: "single"
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
pendingIndex = index;
|
||||
}
|
||||
onAnimationCompleted: {
|
||||
if (pendingIndex < 0 || !variantSelector.activeThemeVariants?.options)
|
||||
return;
|
||||
const variantId = variantSelector.activeThemeVariants.options[pendingIndex]?.id;
|
||||
const idx = pendingIndex;
|
||||
pendingIndex = -1;
|
||||
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
|
||||
@@ -987,6 +758,248 @@ Item {
|
||||
onClicked: showThemeBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: variantSelector
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: activeThemeId !== "" && activeThemeVariants !== null && (isMultiVariant || (activeThemeVariants.options && activeThemeVariants.options.length > 0))
|
||||
|
||||
property string activeThemeId: {
|
||||
switch (Theme.currentThemeCategory) {
|
||||
case "registry":
|
||||
if (Theme.currentTheme !== "custom")
|
||||
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 "";
|
||||
case "custom":
|
||||
return Theme.currentThemeId || "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
property var activeThemeVariants: {
|
||||
if (!activeThemeId)
|
||||
return null;
|
||||
switch (Theme.currentThemeCategory) {
|
||||
case "registry":
|
||||
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;
|
||||
case "custom":
|
||||
return Theme.currentThemeVariants || null;
|
||||
default:
|
||||
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, colorMode) : multiDefaults
|
||||
property string selectedFlavor: {
|
||||
var sf = storedMulti.flavor || multiDefaults.flavor || "";
|
||||
for (var i = 0; i < flavorOptions.length; i++) {
|
||||
if (flavorOptions[i].id === sf)
|
||||
return sf;
|
||||
}
|
||||
if (flavorOptions.length > 0)
|
||||
return flavorOptions[0].id;
|
||||
return sf;
|
||||
}
|
||||
property string selectedAccent: storedMulti.accent || multiDefaults.accent || ""
|
||||
property var flavorOptions: {
|
||||
if (!isMultiVariant || !activeThemeVariants?.flavors)
|
||||
return [];
|
||||
return activeThemeVariants.flavors.filter(f => {
|
||||
if (f.mode)
|
||||
return f.mode === colorMode || f.mode === "both";
|
||||
return !!f[colorMode];
|
||||
});
|
||||
}
|
||||
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)
|
||||
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: flavorButtonGroup.implicitHeight
|
||||
clip: true
|
||||
visible: variantSelector.isMultiVariant && variantSelector.flavorOptions.length > 1
|
||||
|
||||
DankButtonGroup {
|
||||
id: flavorButtonGroup
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
property int _count: variantSelector.flavorNames.length
|
||||
property real _maxPerItem: _count > 1 ? (parent.width - (_count - 1) * spacing) / _count : parent.width
|
||||
buttonPadding: _maxPerItem < 55 ? Theme.spacingXS : (_maxPerItem < 75 ? Theme.spacingS : Theme.spacingL)
|
||||
minButtonWidth: Math.min(_maxPerItem < 55 ? 28 : (_maxPerItem < 75 ? 44 : 64), Math.max(28, Math.floor(_maxPerItem)))
|
||||
textSize: _maxPerItem < 55 ? Theme.fontSizeSmall - 2 : (_maxPerItem < 75 ? Theme.fontSizeSmall : Theme.fontSizeMedium)
|
||||
checkEnabled: _maxPerItem >= 55
|
||||
property int pendingIndex: -1
|
||||
model: variantSelector.flavorNames
|
||||
currentIndex: pendingIndex >= 0 ? pendingIndex : variantSelector.flavorIndex
|
||||
selectionMode: "single"
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
pendingIndex = index;
|
||||
}
|
||||
onAnimationCompleted: {
|
||||
if (pendingIndex < 0 || pendingIndex >= variantSelector.flavorOptions.length)
|
||||
return;
|
||||
const flavorId = variantSelector.flavorOptions[pendingIndex]?.id;
|
||||
const idx = pendingIndex;
|
||||
pendingIndex = -1;
|
||||
if (!flavorId || flavorId === variantSelector.selectedFlavor)
|
||||
return;
|
||||
Theme.screenTransition();
|
||||
SettingsData.setRegistryThemeMultiVariant(variantSelector.activeThemeId, flavorId, variantSelector.selectedAccent, variantSelector.colorMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 || modelData[variantSelector.selectedFlavor]?.primary || 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, variantSelector.colorMode);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
property int _count: variantSelector.variantNames.length
|
||||
property real _maxPerItem: _count > 1 ? (parent.width - (_count - 1) * spacing) / _count : parent.width
|
||||
buttonPadding: _maxPerItem < 55 ? Theme.spacingXS : (_maxPerItem < 75 ? Theme.spacingS : Theme.spacingL)
|
||||
minButtonWidth: Math.min(_maxPerItem < 55 ? 28 : (_maxPerItem < 75 ? 44 : 64), Math.max(28, Math.floor(_maxPerItem)))
|
||||
textSize: _maxPerItem < 55 ? Theme.fontSizeSmall - 2 : (_maxPerItem < 75 ? Theme.fontSizeSmall : Theme.fontSizeMedium)
|
||||
checkEnabled: _maxPerItem >= 55
|
||||
property int pendingIndex: -1
|
||||
model: variantSelector.variantNames
|
||||
currentIndex: pendingIndex >= 0 ? pendingIndex : variantSelector.selectedIndex
|
||||
selectionMode: "single"
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
pendingIndex = index;
|
||||
}
|
||||
onAnimationCompleted: {
|
||||
if (pendingIndex < 0 || !variantSelector.activeThemeVariants?.options)
|
||||
return;
|
||||
const variantId = variantSelector.activeThemeVariants.options[pendingIndex]?.id;
|
||||
const idx = pendingIndex;
|
||||
pendingIndex = -1;
|
||||
if (!variantId || variantId === variantSelector.selectedVariant)
|
||||
return;
|
||||
Theme.screenTransition();
|
||||
SettingsData.setRegistryThemeVariant(variantSelector.activeThemeId, variantId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.Common
|
||||
|
||||
Singleton {
|
||||
|
||||
Reference in New Issue
Block a user