mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-18 17:24:01 -04:00
* feat(theme): add support for matugen smart mode and type * add another dry run to get smart type preview * fix too much keywords in search index * revert dont extract translations * feat gate "Auto From Wallpaper" toggle row and the available matugen scheme for the pallete drop down * re-extract settings search index * make portal sync no longer disables matugen smart mode * make sure cosmic theme mode also follow change from smart mode * add translator context for Auto (Light Mode) / Auto (Dark Mode) / Auto From Wallpaper * fix: gate matugen smart mode checks on the dynamic theme so stock themes keep portal sync and fix tile labels in control center
3197 lines
164 KiB
QML
3197 lines
164 KiB
QML
import QtCore
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import Quickshell.Widgets
|
|
import qs.Common
|
|
import qs.Modals.FileBrowser
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
import "../../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
|
import "../../Common/Format.js" as Format
|
|
|
|
Item {
|
|
id: themeColorsTab
|
|
|
|
property var parentModal: null
|
|
property string pendingExtractJson: ""
|
|
readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive
|
|
readonly property bool frameModeActive: SettingsData.frameEnabled
|
|
property var cachedIconThemes: SettingsData.availableIconThemes
|
|
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
|
readonly property bool matugenSmartCapable: Theme.matugenAvailable && DMSService.matugenSmartSupported
|
|
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.filter(option => DMSService.matugenSmartSupported || option.value !== "scheme-smart").map(option => option.label)
|
|
property var matugenSchemePreviews: ({})
|
|
property string matugenPreviewSource: ""
|
|
property string matugenPreviewImage: ""
|
|
property real matugenPreviewContrast: 0
|
|
property string matugenPreviewRequestKey: ""
|
|
property var installedRegistryThemes: []
|
|
property var templateDetection: []
|
|
readonly property var neovimDarkBaseThemes: ["aquarium", "ashes", "aylin", "ayu_dark", "bearded-arc", "carbonfox", "catppuccin", "chadracula", "chadracula-evondev", "chadtain", "chocolate", "darcula-dark", "dark_horizon", "decay", "default-dark", "doomchad", "eldritch", "embark", "everblush", "everforest", "falcon", "flexoki", "flouromachine", "gatekeeper", "github_dark", "gruvbox", "gruvchad", "hiberbee", "horizon", "jabuti", "jellybeans", "kanagawa", "kanagawa-dragon", "material-darker", "material-deep-ocean", "melange", "midnight_breeze", "mito-laser", "monekai", "monochrome", "mountain", "neofusion", "nightfox", "nightlamp", "nightowl", "nord", "obsidian-ember", "oceanic-next", "onedark", "onenord", "oxocarbon", "palenight", "pastelDark", "pastelbeans", "penumbra_dark", "poimandres", "radium", "rosepine", "rxyhn", "scaryforest", "seoul256_dark", "solarized_dark", "solarized_osaka", "starlight", "sweetpastel", "tokyodark", "tokyonight", "tomorrow_night", "tundra", "vesper", "vscode_dark", "wombat", "yoru", "zenburn"]
|
|
readonly property var neovimLightBaseThemes: ["ayu_light", "blossom_light", "catppuccin-latte", "default-light", "everforest_light", "flex-light", "flexoki-light", "github_light", "gruvbox_light", "material-lighter", "nano-light", "oceanic-light", "one_light", "onenord_light", "penumbra_light", "rosepine-dawn", "seoul256_light", "solarized_light", "sunrise_breeze", "vscode_light"]
|
|
readonly property var matugenSchemeColorMap: {
|
|
const map = {};
|
|
const mode = SessionData.isLightMode ? "light" : "dark";
|
|
for (var i = 0; i < Theme.availableMatugenSchemes.length; i++) {
|
|
const option = Theme.availableMatugenSchemes[i];
|
|
const preview = matugenSchemePreviews[option.value] || matugenSchemePreviews["scheme-tonal-spot"];
|
|
if (preview?.[mode])
|
|
map[option.label] = preview[mode];
|
|
}
|
|
return map;
|
|
}
|
|
readonly property var widgetBackgroundOptions: [({
|
|
"value": "sth",
|
|
"label": I18n.tr("Overlay", "widget background color option"),
|
|
"previewColor": Theme.blend(Theme.surfaceContainerHigh, Theme.surfaceText, 0.24)
|
|
}), ({
|
|
"value": "s",
|
|
"label": I18n.tr("Surface", "widget background color option")
|
|
}), ({
|
|
"value": "sc",
|
|
"label": I18n.tr("Surface Container", "widget background color option")
|
|
}), ({
|
|
"value": "sch",
|
|
"label": I18n.tr("Surface High", "widget background color option")
|
|
}), ({
|
|
"value": "primaryContainer",
|
|
"label": I18n.tr("Primary Container", "widget background color option")
|
|
}), ({
|
|
"value": "secondaryContainer",
|
|
"label": I18n.tr("Secondary Container", "widget background color option")
|
|
}), ({
|
|
"value": "tertiaryContainer",
|
|
"label": I18n.tr("Tertiary Container", "widget background color option")
|
|
}), ({
|
|
"value": "custom",
|
|
"label": I18n.tr("Custom", "widget background color option")
|
|
})]
|
|
|
|
property var cursorIncludeStatus: defaultIncludeStatus()
|
|
readonly property bool cursorReadOnly: CompositorService.isHyprland && cursorIncludeStatus.readOnly === true
|
|
property bool checkingCursorInclude: false
|
|
property bool fixingCursorInclude: false
|
|
|
|
property var windowRulesIncludeStatus: defaultIncludeStatus()
|
|
readonly property bool windowRulesReadOnly: CompositorService.isHyprland && windowRulesIncludeStatus.readOnly === true
|
|
property bool checkingWindowRulesInclude: false
|
|
property bool fixingWindowRulesInclude: false
|
|
|
|
readonly property var includeConfigSpecs: ({
|
|
"cursor": ({
|
|
"niri": {
|
|
"configName": "config.kdl",
|
|
"fragmentName": "cursor.kdl",
|
|
"grepPattern": 'include.*"dms/cursor.kdl"',
|
|
"includeLine": 'include "dms/cursor.kdl"'
|
|
},
|
|
"hyprland": {
|
|
"configName": "hyprland.lua",
|
|
"fragmentName": "cursor.lua",
|
|
"grepPattern": "dms.cursor",
|
|
"includeLine": "require(\"dms.cursor\")"
|
|
},
|
|
"mango": {
|
|
"configName": "config.conf",
|
|
"fragmentName": "cursor.conf",
|
|
"grepPattern": "source.*dms/cursor.conf",
|
|
"includeLine": "source=./dms/cursor.conf"
|
|
}
|
|
}),
|
|
"windowrules": ({
|
|
"niri": {
|
|
"configName": "config.kdl",
|
|
"fragmentName": "windowrules.kdl",
|
|
"grepPattern": 'include.*"dms/windowrules.kdl"',
|
|
"includeLine": 'include "dms/windowrules.kdl"'
|
|
},
|
|
"hyprland": {
|
|
"configName": "hyprland.lua",
|
|
"fragmentName": "windowrules.lua",
|
|
"grepPattern": "dms.windowrules",
|
|
"includeLine": "require(\"dms.windowrules\")"
|
|
},
|
|
"mango": {
|
|
"configName": "config.conf",
|
|
"fragmentName": "windowrules.conf",
|
|
"grepPattern": "dms/windowrules.conf",
|
|
"includeLine": "source=./dms/windowrules.conf"
|
|
}
|
|
})
|
|
})
|
|
|
|
function defaultIncludeStatus() {
|
|
return {
|
|
"exists": false,
|
|
"included": false,
|
|
"configFormat": "",
|
|
"readOnly": false
|
|
};
|
|
}
|
|
|
|
function getIncludeConfigPaths(includeKind) {
|
|
const spec = includeConfigSpecs[includeKind]?.[CompositorService.compositor];
|
|
if (!spec)
|
|
return null;
|
|
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/" + CompositorService.compositor;
|
|
return {
|
|
"configFile": configDir + "/" + spec.configName,
|
|
"fragmentFile": configDir + "/dms/" + spec.fragmentName,
|
|
"grepPattern": spec.grepPattern,
|
|
"includeLine": spec.includeLine
|
|
};
|
|
}
|
|
|
|
function checkIncludeStatus(includeKind, procTag, onFinished) {
|
|
const spec = includeConfigSpecs[includeKind]?.[CompositorService.compositor];
|
|
if (!spec) {
|
|
onFinished(defaultIncludeStatus());
|
|
return;
|
|
}
|
|
const compositor = CompositorService.compositor;
|
|
const compositorArg = (compositor === "mango") ? "mangowc" : compositor;
|
|
Proc.runCommand(procTag, [Proc.dmsBin, "config", "resolve-include", compositorArg, spec.fragmentName], (output, exitCode) => {
|
|
if (exitCode !== 0) {
|
|
onFinished(defaultIncludeStatus());
|
|
return;
|
|
}
|
|
try {
|
|
onFinished(JSON.parse(output.trim()));
|
|
} catch (e) {
|
|
onFinished(defaultIncludeStatus());
|
|
}
|
|
});
|
|
}
|
|
|
|
function checkCursorIncludeStatus() {
|
|
checkingCursorInclude = true;
|
|
checkIncludeStatus("cursor", "check-cursor-include", status => {
|
|
checkingCursorInclude = false;
|
|
cursorIncludeStatus = status;
|
|
});
|
|
}
|
|
|
|
function checkWindowRulesIncludeStatus() {
|
|
checkingWindowRulesInclude = true;
|
|
checkIncludeStatus("windowrules", "check-windowrules-include-theme", status => {
|
|
checkingWindowRulesInclude = false;
|
|
windowRulesIncludeStatus = status;
|
|
});
|
|
}
|
|
|
|
function fixInclude(includeKind, procTag, readOnly, onFinished) {
|
|
if (readOnly) {
|
|
ToastService.showWarning(I18n.tr("Hyprland conf mode"), I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before changing these settings."), "dms setup", "hyprland-migration");
|
|
onFinished(false);
|
|
return;
|
|
}
|
|
const paths = getIncludeConfigPaths(includeKind);
|
|
if (!paths) {
|
|
onFinished(false);
|
|
return;
|
|
}
|
|
const unixTime = Math.floor(Date.now() / 1000);
|
|
const script = ConfigIncludeResolve.buildRepairScript({
|
|
configFile: paths.configFile,
|
|
backupFile: paths.configFile + ".backup" + unixTime,
|
|
fragmentFile: paths.fragmentFile,
|
|
grepPattern: paths.grepPattern,
|
|
includeLine: paths.includeLine
|
|
});
|
|
Proc.runCommand(procTag, ["sh", "-c", script], (output, exitCode) => onFinished(exitCode === 0));
|
|
}
|
|
|
|
function fixCursorInclude() {
|
|
fixingCursorInclude = true;
|
|
fixInclude("cursor", "fix-cursor-include", cursorReadOnly, success => {
|
|
fixingCursorInclude = false;
|
|
if (!success)
|
|
return;
|
|
checkCursorIncludeStatus();
|
|
SettingsData.updateCompositorCursor();
|
|
});
|
|
}
|
|
|
|
function fixWindowRulesInclude() {
|
|
fixingWindowRulesInclude = true;
|
|
fixInclude("windowrules", "fix-windowrules-include-theme", windowRulesReadOnly, success => {
|
|
fixingWindowRulesInclude = false;
|
|
if (!success)
|
|
return;
|
|
if (CompositorService.isMango)
|
|
MangoService.reloadConfig();
|
|
checkWindowRulesIncludeStatus();
|
|
CompositorService.applyDmsWindowFloatingRule();
|
|
});
|
|
}
|
|
|
|
function unsyncFloatingWindowSettings() {
|
|
if (!SettingsData.floatingWindowSyncGlobal)
|
|
return;
|
|
SettingsData.set("floatingWindowTransparency", SettingsData.popupTransparency);
|
|
SettingsData.set("floatingWindowForegroundLayers", SettingsData.blurForegroundLayers ?? true);
|
|
SettingsData.set("floatingWindowForegroundTransparency", SettingsData.foregroundLayerTransparency ?? 1.0);
|
|
SettingsData.set("floatingWindowSyncGlobal", false);
|
|
}
|
|
|
|
function isTemplateDetected(templateId) {
|
|
if (!templateDetection || templateDetection.length === 0)
|
|
return true;
|
|
var item = templateDetection.find(i => i.id === templateId);
|
|
return !item || item.detected !== false;
|
|
}
|
|
|
|
function getTemplateDescription(templateId, baseDescription) {
|
|
if (isTemplateDetected(templateId))
|
|
return baseDescription;
|
|
if (baseDescription)
|
|
return baseDescription + " · " + I18n.tr("Not detected");
|
|
return I18n.tr("Not detected");
|
|
}
|
|
|
|
function getTemplateDescriptionColor(templateId) {
|
|
if (isTemplateDetected(templateId))
|
|
return Theme.surfaceVariantText;
|
|
return Theme.warning;
|
|
}
|
|
|
|
function openSurfaceBorderColorPicker() {
|
|
PopoutService.colorPickerModal.selectedColor = SettingsData.blurBorderCustomColor ?? "#ffffff";
|
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Surface Border Color");
|
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
|
|
SettingsData.set("blurBorderCustomColor", color.toString());
|
|
};
|
|
PopoutService.colorPickerModal.open();
|
|
}
|
|
|
|
function openM3ShadowColorPicker() {
|
|
PopoutService.colorPickerModal.selectedColor = SettingsData.m3ElevationCustomColor ?? "#000000";
|
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Shadow Color");
|
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
|
|
SettingsData.set("m3ElevationCustomColor", color.toString());
|
|
};
|
|
PopoutService.colorPickerModal.show();
|
|
}
|
|
|
|
function warnIfMissingQtTheme() {
|
|
if (Quickshell.env("QT_QPA_PLATFORMTHEME") === "gtk3" || Quickshell.env("QT_QPA_PLATFORMTHEME") === "qt6ct" || Quickshell.env("QT_QPA_PLATFORMTHEME_QT6") === "qt6ct" || SettingsData.qtengineActive || Quickshell.env("QT_QPA_PLATFORMTHEME") === "kde")
|
|
return;
|
|
ToastService.showError(I18n.tr("Missing Environment Variables", "qt theme env error title"), I18n.tr("You need to set one of:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct OR\nQT_QPA_PLATFORMTHEME=qtengine\nas environment variables, and then restart the shell.\n\nOnly qt6ct requires qt6ct-kde to be installed.", "qt theme env error body"));
|
|
}
|
|
|
|
function refreshMatugenSchemePreviews() {
|
|
if (!Theme.matugenAvailable)
|
|
return;
|
|
const sourceColor = Theme.getMatugenColor("source_color", Theme.primary).toString();
|
|
const contrast = SettingsData.matugenContrast ?? 0;
|
|
const imagePath = (Theme.rawWallpaperPath && !Theme.rawWallpaperPath.startsWith("#")) ? Theme.rawWallpaperPath : "";
|
|
const requestKey = sourceColor + "|" + contrast + "|" + imagePath;
|
|
if (sourceColor === matugenPreviewSource && contrast === matugenPreviewContrast && imagePath === matugenPreviewImage && Object.keys(matugenSchemePreviews).length > 0)
|
|
return;
|
|
if (requestKey === matugenPreviewRequestKey)
|
|
return;
|
|
matugenPreviewRequestKey = requestKey;
|
|
|
|
const args = [Proc.dmsBin, "matugen", "preview", "--source-color", sourceColor, "--contrast", contrast.toString()];
|
|
if (imagePath)
|
|
args.push("--image", imagePath);
|
|
Proc.runCommand("", args, (output, exitCode) => {
|
|
if (requestKey !== themeColorsTab.matugenPreviewRequestKey)
|
|
return;
|
|
if (exitCode !== 0) {
|
|
themeColorsTab.matugenPreviewRequestKey = "";
|
|
return;
|
|
}
|
|
try {
|
|
themeColorsTab.matugenSchemePreviews = JSON.parse(output.trim());
|
|
themeColorsTab.matugenPreviewSource = sourceColor;
|
|
themeColorsTab.matugenPreviewImage = imagePath;
|
|
themeColorsTab.matugenPreviewContrast = contrast;
|
|
} catch (e) {
|
|
themeColorsTab.matugenPreviewRequestKey = "";
|
|
}
|
|
});
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
SettingsData.detectAvailableIconThemes();
|
|
SettingsData.detectAvailableCursorThemes();
|
|
if (DMSService.dmsAvailable)
|
|
DMSService.listInstalledThemes();
|
|
if (PopoutService.pendingThemeInstall)
|
|
Qt.callLater(() => showThemeBrowser());
|
|
Proc.runCommand("template-check", [Proc.dmsBin, "matugen", "check"], (output, exitCode) => {
|
|
if (exitCode !== 0)
|
|
return;
|
|
try {
|
|
themeColorsTab.templateDetection = JSON.parse(output.trim());
|
|
} catch (e) {}
|
|
});
|
|
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) {
|
|
checkCursorIncludeStatus();
|
|
checkWindowRulesIncludeStatus();
|
|
}
|
|
refreshMatugenSchemePreviews();
|
|
}
|
|
|
|
Connections {
|
|
target: DMSService
|
|
function onInstalledThemesReceived(themes) {
|
|
themeColorsTab.installedRegistryThemes = themes;
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: PopoutService
|
|
function onPendingThemeInstallChanged() {
|
|
if (PopoutService.pendingThemeInstall)
|
|
showThemeBrowser();
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Theme
|
|
function onMatugenColorsChanged() {
|
|
themeColorsTab.refreshMatugenSchemePreviews();
|
|
}
|
|
function onMatugenAvailableChanged() {
|
|
themeColorsTab.refreshMatugenSchemePreviews();
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: SettingsData
|
|
function onMatugenContrastChanged() {
|
|
themeColorsTab.refreshMatugenSchemePreviews();
|
|
}
|
|
}
|
|
|
|
component IncludeWarningBox: StyledRect {
|
|
id: includeWarningBox
|
|
|
|
property bool checking: false
|
|
property bool fixing: false
|
|
property bool includeReadOnly: false
|
|
property bool alreadyIncluded: false
|
|
property bool visibleCondition: true
|
|
property string fragmentPath: ""
|
|
property var onSetup: function () {}
|
|
|
|
readonly property bool showLegacy: includeReadOnly
|
|
readonly property bool showSetup: !showLegacy && !alreadyIncluded
|
|
|
|
width: parent.width
|
|
height: includeWarningContent.implicitHeight + Theme.spacingL * 2
|
|
radius: Theme.cornerRadius
|
|
color: Theme.withAlpha(Theme.primary, 0.15)
|
|
border.color: Theme.withAlpha(Theme.primary, 0.3)
|
|
border.width: 1
|
|
visible: visibleCondition && (showLegacy || showSetup) && !checking
|
|
|
|
Row {
|
|
id: includeWarningContent
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingL
|
|
spacing: Theme.spacingM
|
|
|
|
DankIcon {
|
|
name: "warning"
|
|
size: Theme.iconSize
|
|
color: Theme.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - Theme.iconSize - (includeFixButton.visible ? includeFixButton.width + Theme.spacingM : 0) - Theme.spacingM
|
|
spacing: Theme.spacingXS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
StyledText {
|
|
text: includeWarningBox.showLegacy ? I18n.tr("Hyprland conf mode") : I18n.tr("First Time Setup")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.primary
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
StyledText {
|
|
text: includeWarningBox.showLegacy ? I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before changing these settings.") : I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg(includeWarningBox.fragmentPath)
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
}
|
|
|
|
DankButton {
|
|
id: includeFixButton
|
|
visible: !includeWarningBox.showLegacy && includeWarningBox.showSetup
|
|
text: includeWarningBox.fixing ? I18n.tr("Setting up...") : I18n.tr("Setup")
|
|
backgroundColor: Theme.primary
|
|
textColor: Theme.primaryText
|
|
enabled: !includeWarningBox.fixing
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
onClicked: includeWarningBox.onSetup()
|
|
}
|
|
}
|
|
}
|
|
|
|
DankFlickable {
|
|
anchors.fill: parent
|
|
clip: true
|
|
contentHeight: mainColumn.height + Theme.spacingXL
|
|
contentWidth: width
|
|
|
|
Column {
|
|
id: mainColumn
|
|
topPadding: 4
|
|
|
|
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: Theme.spacingXL
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["color", "palette", "theme", "appearance"]
|
|
title: I18n.tr("Theme Color")
|
|
settingKey: "themeColor"
|
|
iconName: "palette"
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
StyledText {
|
|
property string registryThemeName: {
|
|
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.name;
|
|
}
|
|
return "";
|
|
}
|
|
text: {
|
|
if (Theme.currentTheme === Theme.dynamic)
|
|
return I18n.tr("Current Theme: %1", "current theme label").arg(I18n.tr("Dynamic", "dynamic theme name"));
|
|
if (Theme.currentThemeCategory === "registry" && registryThemeName)
|
|
return I18n.tr("Current Theme: %1", "current theme label").arg(registryThemeName);
|
|
return I18n.tr("Current Theme: %1", "current theme label").arg(Theme.getThemeColors(Theme.currentThemeName).name);
|
|
}
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceText
|
|
font.weight: Font.Medium
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: {
|
|
if (Theme.currentTheme === Theme.dynamic)
|
|
return I18n.tr("Material colors generated from wallpaper", "dynamic theme description");
|
|
if (Theme.currentThemeCategory === "registry")
|
|
return I18n.tr("Color theme from DMS registry", "registry theme description");
|
|
if (Theme.currentTheme === Theme.custom)
|
|
return I18n.tr("Custom theme loaded from JSON file", "custom theme description");
|
|
return I18n.tr("Material Design inspired color themes", "generic theme description");
|
|
}
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
wrapMode: Text.WordWrap
|
|
width: Math.min(parent.width, 400)
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
|
|
Column {
|
|
id: themeCategoryColumn
|
|
spacing: Theme.spacingM
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
width: parent.width
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: themeCategoryGroup.implicitHeight
|
|
clip: true
|
|
|
|
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 pendingIndex: -1
|
|
property int computedIndex: {
|
|
if (isRegistryTheme)
|
|
return 3;
|
|
if (Theme.currentTheme === Theme.dynamic)
|
|
return 1;
|
|
if (Theme.currentThemeName === "custom")
|
|
return 2;
|
|
return 0;
|
|
}
|
|
|
|
model: DMSService.dmsAvailable ? [I18n.tr("Generic", "theme category option"), I18n.tr("Auto", "theme category option"), I18n.tr("Custom", "theme category option"), I18n.tr("Browse", "theme category option")] : [I18n.tr("Generic", "theme category option"), I18n.tr("Auto", "theme category option"), I18n.tr("Custom", "theme category option")]
|
|
currentIndex: pendingIndex >= 0 ? pendingIndex : computedIndex
|
|
selectionMode: "single"
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected)
|
|
return;
|
|
pendingIndex = index;
|
|
}
|
|
onAnimationCompleted: {
|
|
if (pendingIndex < 0)
|
|
return;
|
|
const idx = pendingIndex;
|
|
pendingIndex = -1;
|
|
switch (idx) {
|
|
case 0:
|
|
Theme.switchThemeCategory("generic", "blue");
|
|
break;
|
|
case 1:
|
|
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 2:
|
|
Theme.switchThemeCategory("custom", "custom");
|
|
break;
|
|
case 3:
|
|
Theme.switchThemeCategory("registry", "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: genericColorGrid.implicitHeight + Math.ceil(genericColorGrid.dotSize * 0.05)
|
|
visible: Theme.currentThemeCategory === "generic" && Theme.currentTheme !== Theme.dynamic && Theme.currentThemeName !== "custom"
|
|
|
|
Grid {
|
|
id: genericColorGrid
|
|
property var colorList: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
|
|
property int dotSize: parent.width < 300 ? 28 : 32
|
|
columns: Math.ceil(colorList.length / 2)
|
|
rowSpacing: Theme.spacingS
|
|
columnSpacing: Theme.spacingS
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
Repeater {
|
|
model: genericColorGrid.colorList
|
|
|
|
Rectangle {
|
|
required property string modelData
|
|
property string themeName: modelData
|
|
width: genericColorGrid.dotSize
|
|
height: genericColorGrid.dotSize
|
|
radius: width / 2
|
|
color: Theme.getThemeColors(themeName).primary
|
|
border.color: Theme.outline
|
|
border.width: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 2 : 1
|
|
scale: (Theme.currentThemeName === themeName && Theme.currentTheme !== Theme.dynamic) ? 1.1 : 1
|
|
|
|
Rectangle {
|
|
width: nameText.contentWidth + Theme.spacingS * 2
|
|
height: nameText.contentHeight + Theme.spacingXS * 2
|
|
color: Theme.floatingWindowSurface
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: Theme.currentTheme === Theme.dynamic && Theme.currentThemeCategory !== "registry"
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
StyledRect {
|
|
width: 120
|
|
height: 90
|
|
radius: Theme.cornerRadius
|
|
color: Theme.surfaceVariant
|
|
|
|
ClippingRectangle {
|
|
anchors.fill: parent
|
|
anchors.margins: 1
|
|
radius: Theme.cornerRadius - 1
|
|
color: "transparent"
|
|
|
|
Image {
|
|
anchors.fill: parent
|
|
source: {
|
|
var wp = Theme.wallpaperPath;
|
|
if (!wp || wp === "" || wp.startsWith("#"))
|
|
return "";
|
|
if (wp.startsWith("file://"))
|
|
wp = wp.substring(7);
|
|
return "file://" + wp.split('/').map(s => encodeURIComponent(s)).join('/');
|
|
}
|
|
fillMode: Image.PreserveAspectCrop
|
|
visible: Theme.wallpaperPath && !Theme.wallpaperPath.startsWith("#")
|
|
sourceSize.width: 120
|
|
sourceSize.height: 120
|
|
asynchronous: true
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
anchors.margins: 1
|
|
radius: Theme.cornerRadius - 1
|
|
color: Theme.wallpaperPath && Theme.wallpaperPath.startsWith("#") ? Theme.wallpaperPath : Theme.withAlpha(Theme.wallpaperPath, 0)
|
|
visible: Theme.wallpaperPath && Theme.wallpaperPath.startsWith("#")
|
|
}
|
|
|
|
DankIcon {
|
|
anchors.centerIn: parent
|
|
name: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? "error" : "palette"
|
|
size: Theme.iconSizeLarge
|
|
color: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceVariantText
|
|
visible: !Theme.wallpaperPath
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - 120 - Theme.spacingM - 36 - Theme.spacingM
|
|
spacing: Theme.spacingS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
StyledText {
|
|
text: {
|
|
if (ToastService.wallpaperErrorStatus === "error")
|
|
return I18n.tr("Wallpaper Error", "wallpaper error status");
|
|
if (ToastService.wallpaperErrorStatus === "matugen_missing")
|
|
return I18n.tr("Matugen Missing", "matugen not found status");
|
|
if (Theme.wallpaperPath)
|
|
return Theme.wallpaperPath.split('/').pop();
|
|
return I18n.tr("No wallpaper selected", "no wallpaper status");
|
|
}
|
|
font.pixelSize: Theme.fontSizeLarge
|
|
color: Theme.surfaceText
|
|
elide: Text.ElideMiddle
|
|
maximumLineCount: 1
|
|
width: parent.width
|
|
}
|
|
|
|
StyledText {
|
|
id: wallpaperPathText
|
|
text: {
|
|
if (ToastService.wallpaperErrorStatus === "error")
|
|
return I18n.tr("Wallpaper processing failed", "wallpaper processing error");
|
|
if (ToastService.wallpaperErrorStatus === "matugen_missing")
|
|
return I18n.tr("Install matugen package for dynamic theming", "matugen installation hint");
|
|
if (Theme.wallpaperPath)
|
|
return Theme.wallpaperPath;
|
|
return I18n.tr("Dynamic colors from wallpaper", "dynamic colors description");
|
|
}
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceVariantText
|
|
elide: Text.ElideMiddle
|
|
maximumLineCount: 2
|
|
width: parent.width
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
}
|
|
|
|
DankActionButton {
|
|
buttonSize: 36
|
|
iconName: "download"
|
|
iconSize: Theme.iconSize
|
|
backgroundColor: Theme.primaryHover
|
|
iconColor: Theme.primary
|
|
tooltipText: I18n.tr("Extract theme to JSON", "extract theme tooltip")
|
|
anchors.bottom: wallpaperPathText.bottom
|
|
onClicked: {
|
|
pendingExtractJson = Theme.extractCurrentTheme();
|
|
saveBrowserLoader.active = true;
|
|
if (saveBrowserLoader.item)
|
|
saveBrowserLoader.item.open();
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "palette", "algorithm", "dynamic"]
|
|
settingKey: "matugenScheme"
|
|
text: I18n.tr("Matugen Palette")
|
|
description: I18n.tr("Select the palette algorithm used for wallpaper-based colors")
|
|
options: cachedMatugenSchemes
|
|
optionColorMap: matugenSchemeColorMap
|
|
currentValue: Theme.getMatugenScheme(SettingsData.matugenScheme).label
|
|
enabled: Theme.matugenAvailable
|
|
opacity: enabled ? 1 : 0.4
|
|
onValueChanged: value => {
|
|
for (var i = 0; i < Theme.availableMatugenSchemes.length; i++) {
|
|
var option = Theme.availableMatugenSchemes[i];
|
|
if (option.label === value) {
|
|
SettingsData.setMatugenScheme(option.value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: {
|
|
var scheme = Theme.getMatugenScheme(SettingsData.matugenScheme);
|
|
return scheme.description + " (" + scheme.value + ")";
|
|
}
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width - Theme.spacingM * 2
|
|
x: Theme.spacingM
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "contrast", "dynamic"]
|
|
settingKey: "matugenContrast"
|
|
text: I18n.tr("Matugen Contrast")
|
|
description: I18n.tr("Adjusts contrast of generated colors (-100 = minimum, 0 = standard, 100 = maximum)")
|
|
value: Math.round(SettingsData.matugenContrast * 100)
|
|
minimum: -100
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 0
|
|
enabled: Theme.matugenAvailable
|
|
opacity: enabled ? 1 : 0.4
|
|
onSliderDragFinished: finalValue => SettingsData.setMatugenContrast(finalValue / 100)
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: Theme.currentThemeName === "custom" && Theme.currentThemeCategory !== "registry"
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
DankActionButton {
|
|
buttonSize: 48
|
|
iconName: "folder_open"
|
|
iconSize: Theme.iconSize
|
|
backgroundColor: Theme.primaryHover
|
|
iconColor: Theme.primary
|
|
onClicked: fileBrowserModal.open()
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - 48 - Theme.spacingM
|
|
spacing: Theme.spacingXS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
StyledText {
|
|
text: SettingsData.customThemeFile ? SettingsData.customThemeFile.split('/').pop() : I18n.tr("No custom theme file", "no custom theme file status")
|
|
font.pixelSize: Theme.fontSizeLarge
|
|
color: Theme.surfaceText
|
|
elide: Text.ElideMiddle
|
|
maximumLineCount: 1
|
|
width: parent.width
|
|
}
|
|
|
|
StyledText {
|
|
text: SettingsData.customThemeFile || I18n.tr("Click to select a custom theme JSON file", "custom theme file hint")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
elide: Text.ElideMiddle
|
|
maximumLineCount: 1
|
|
width: parent.width
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
id: registrySection
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: Theme.currentThemeCategory === "registry"
|
|
|
|
Grid {
|
|
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
|
|
|
|
Repeater {
|
|
model: themeColorsTab.installedRegistryThemes
|
|
|
|
Rectangle {
|
|
id: themeCard
|
|
property bool isActive: Theme.currentThemeCategory === "registry" && Theme.currentThemeName === "custom" && SettingsData.customThemeFile && SettingsData.customThemeFile.endsWith((modelData.sourceDir || modelData.id) + "/theme.json")
|
|
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
|
|
border.width: isActive ? 2 : 1
|
|
scale: isActive ? 1.03 : 1
|
|
|
|
Behavior on scale {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
easing.type: Theme.emphasizedEasing
|
|
}
|
|
}
|
|
|
|
Image {
|
|
id: previewImage
|
|
anchors.fill: parent
|
|
anchors.margins: 2
|
|
source: "file://" + themeCard.previewPath
|
|
fillMode: Image.PreserveAspectFit
|
|
smooth: true
|
|
mipmap: true
|
|
}
|
|
|
|
DankIcon {
|
|
anchors.centerIn: parent
|
|
name: "palette"
|
|
size: themeGrid.cardWidth < 120 ? 24 : 32
|
|
color: Theme.primary
|
|
visible: previewImage.status === Image.Error || previewImage.status === Image.Null
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
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: themeGrid.cardWidth < 120 ? Theme.fontSizeSmall - 2 : Theme.fontSizeSmall
|
|
color: "white"
|
|
font.weight: Font.Medium
|
|
elide: Text.ElideRight
|
|
width: parent.width - Theme.spacingXS * 2
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.top: parent.top
|
|
anchors.right: parent.right
|
|
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: 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: {
|
|
if (themeCard.variants?.type === "multi")
|
|
return themeCard.variants?.accents?.length || 0;
|
|
return themeCard.variants?.options?.length || 0;
|
|
}
|
|
font.pixelSize: themeGrid.cardWidth < 120 ? Theme.fontSizeSmall - 4 : Theme.fontSizeSmall - 2
|
|
color: Theme.surface
|
|
font.weight: Font.Bold
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: cardMouseArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
const themesDir = Quickshell.env("HOME") + "/.config/DankMaterialShell/themes";
|
|
const themePath = themesDir + "/" + (modelData.sourceDir || modelData.id) + "/theme.json";
|
|
SettingsData.set("customThemeFile", themePath);
|
|
Theme.switchTheme("custom", true, true);
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: deleteButton
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
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
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
}
|
|
}
|
|
|
|
DankIcon {
|
|
anchors.centerIn: parent
|
|
name: "close"
|
|
size: themeGrid.cardWidth < 120 ? 10 : 14
|
|
color: "white"
|
|
}
|
|
|
|
MouseArea {
|
|
id: deleteMouseArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
ToastService.showInfo(I18n.tr("Uninstalling: %1", "uninstallation progress").arg(modelData.name));
|
|
DMSService.uninstallTheme(modelData.id, response => {
|
|
if (response.error) {
|
|
ToastService.showError(I18n.tr("Uninstall failed: %1", "uninstallation error").arg(response.error));
|
|
return;
|
|
}
|
|
ToastService.showInfo(I18n.tr("Uninstalled: %1", "uninstallation success").arg(modelData.name));
|
|
DMSService.listInstalledThemes();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("No themes installed. Browse themes to install from the registry.", "no registry themes installed hint")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
visible: themeColorsTab.installedRegistryThemes.length === 0
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
|
|
DankButton {
|
|
text: I18n.tr("Browse Themes", "browse themes button")
|
|
iconName: "store"
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
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.floatingWindowSurface
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["light", "dark", "mode", "appearance", "automatic", "color", "schedule", "sunrise", "sunset"]
|
|
title: I18n.tr("Color Mode")
|
|
settingKey: "colorMode"
|
|
iconName: "contrast"
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["light", "dark", "mode"]
|
|
settingKey: "isLightMode"
|
|
text: I18n.tr("Light Mode")
|
|
description: I18n.tr("Use light theme instead of dark theme")
|
|
checked: SessionData.isLightMode
|
|
onToggled: checked => {
|
|
Theme.screenTransition();
|
|
Theme.setLightMode(checked);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "smart", "wallpaper", "auto", "mode"]
|
|
settingKey: "matugenSmartMode"
|
|
text: I18n.tr("Auto From Wallpaper", "toggle that lets wallpaper brightness decide light/dark mode")
|
|
description: I18n.tr("Dark or light mode follows the wallpaper brightness")
|
|
checked: SettingsData.matugenSmartMode
|
|
visible: matugenSmartCapable
|
|
enabled: Theme.currentTheme === Theme.dynamic
|
|
opacity: enabled ? 1 : 0.4
|
|
onToggled: checked => {
|
|
if (checked && SessionData.themeModeAutoEnabled)
|
|
SessionData.setThemeModeAutoEnabled(false);
|
|
SettingsData.setMatugenSmartMode(checked);
|
|
}
|
|
}
|
|
|
|
DankToggle {
|
|
id: themeModeAutoToggle
|
|
width: parent.width
|
|
text: I18n.tr("Automatic Control")
|
|
checked: SessionData.themeModeAutoEnabled
|
|
onToggled: checked => {
|
|
if (checked && SettingsData.matugenSmartMode)
|
|
SettingsData.setMatugenSmartMode(false);
|
|
SessionData.setThemeModeAutoEnabled(checked);
|
|
}
|
|
|
|
Connections {
|
|
target: SessionData
|
|
function onThemeModeAutoEnabledChanged() {
|
|
themeModeAutoToggle.checked = SessionData.themeModeAutoEnabled;
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: SessionData.themeModeAutoEnabled
|
|
|
|
DankToggle {
|
|
width: parent.width
|
|
text: I18n.tr("Share Gamma Control Settings")
|
|
checked: SessionData.themeModeShareGammaSettings
|
|
onToggled: checked => {
|
|
SessionData.setThemeModeShareGammaSettings(checked);
|
|
}
|
|
}
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: 45 + Theme.spacingM
|
|
|
|
DankTabBar {
|
|
id: themeModeTabBar
|
|
width: 200
|
|
height: 45
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
model: [
|
|
{
|
|
"text": I18n.tr("Time", "theme auto mode tab"),
|
|
"icon": "access_time"
|
|
},
|
|
{
|
|
"text": I18n.tr("Location", "theme auto mode tab"),
|
|
"icon": "place"
|
|
}
|
|
]
|
|
|
|
Component.onCompleted: {
|
|
currentIndex = SessionData.themeModeAutoMode === "location" ? 1 : 0;
|
|
Qt.callLater(updateIndicator);
|
|
}
|
|
|
|
onTabClicked: index => {
|
|
SessionData.setThemeModeAutoMode(index === 1 ? "location" : "time");
|
|
currentIndex = index;
|
|
}
|
|
|
|
Connections {
|
|
target: SessionData
|
|
function onThemeModeAutoModeChanged() {
|
|
themeModeTabBar.currentIndex = SessionData.themeModeAutoMode === "location" ? 1 : 0;
|
|
Qt.callLater(themeModeTabBar.updateIndicator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: SessionData.themeModeAutoMode === "time" && !SessionData.themeModeShareGammaSettings
|
|
|
|
Column {
|
|
spacing: Theme.spacingXS
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
Row {
|
|
spacing: Theme.spacingM
|
|
|
|
StyledText {
|
|
text: ""
|
|
width: 50
|
|
height: 20
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Hour")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: 70
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Minute")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: 70
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
|
|
Row {
|
|
spacing: Theme.spacingM
|
|
|
|
StyledText {
|
|
text: I18n.tr("Start")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceText
|
|
width: 50
|
|
height: 40
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
DankDropdown {
|
|
dropdownWidth: 70
|
|
currentValue: SessionData.themeModeStartHour.toString()
|
|
options: {
|
|
var hours = [];
|
|
for (var i = 0; i < 24; i++)
|
|
hours.push(i.toString());
|
|
return hours;
|
|
}
|
|
onValueChanged: value => {
|
|
SessionData.setThemeModeStartHour(parseInt(value));
|
|
}
|
|
}
|
|
|
|
DankDropdown {
|
|
dropdownWidth: 70
|
|
currentValue: SessionData.themeModeStartMinute.toString().padStart(2, '0')
|
|
options: {
|
|
var minutes = [];
|
|
for (var i = 0; i < 60; i += 5) {
|
|
minutes.push(i.toString().padStart(2, '0'));
|
|
}
|
|
return minutes;
|
|
}
|
|
onValueChanged: value => {
|
|
SessionData.setThemeModeStartMinute(parseInt(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
Row {
|
|
spacing: Theme.spacingM
|
|
|
|
StyledText {
|
|
text: I18n.tr("End")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceText
|
|
width: 50
|
|
height: 40
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
DankDropdown {
|
|
dropdownWidth: 70
|
|
currentValue: SessionData.themeModeEndHour.toString()
|
|
options: {
|
|
var hours = [];
|
|
for (var i = 0; i < 24; i++)
|
|
hours.push(i.toString());
|
|
return hours;
|
|
}
|
|
onValueChanged: value => {
|
|
SessionData.setThemeModeEndHour(parseInt(value));
|
|
}
|
|
}
|
|
|
|
DankDropdown {
|
|
dropdownWidth: 70
|
|
currentValue: SessionData.themeModeEndMinute.toString().padStart(2, '0')
|
|
options: {
|
|
var minutes = [];
|
|
for (var i = 0; i < 60; i += 5) {
|
|
minutes.push(i.toString().padStart(2, '0'));
|
|
}
|
|
return minutes;
|
|
}
|
|
onValueChanged: value => {
|
|
SessionData.setThemeModeEndMinute(parseInt(value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsLocationSection {
|
|
width: parent.width
|
|
visible: SessionData.themeModeAutoMode === "location" && !SessionData.themeModeShareGammaSettings
|
|
description: I18n.tr("Uses sunrise/sunset times based on your location.")
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: I18n.tr("Using shared settings from Gamma Control")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.primary
|
|
wrapMode: Text.WordWrap
|
|
horizontalAlignment: Text.AlignHCenter
|
|
visible: SessionData.themeModeShareGammaSettings
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: statusRow.implicitHeight + Theme.spacingM * 2
|
|
radius: Theme.cornerRadius
|
|
color: Theme.floatingWindowNestedSurface
|
|
border.color: Theme.outlineMedium
|
|
border.width: Theme.layerOutlineWidth
|
|
|
|
Row {
|
|
id: statusRow
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingL
|
|
width: parent.width - Theme.spacingM * 2
|
|
|
|
Column {
|
|
spacing: Theme.spacingXXS
|
|
width: (parent.width - Theme.spacingL * 2) / 3
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
Row {
|
|
spacing: Theme.spacingS
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
Rectangle {
|
|
width: 8
|
|
height: 8
|
|
radius: 4
|
|
color: SessionData.themeModeAutoEnabled ? Theme.success : Theme.error
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Automation")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: SessionData.themeModeAutoEnabled ? I18n.tr("Enabled") : I18n.tr("Disabled")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
width: parent.width
|
|
}
|
|
}
|
|
|
|
Column {
|
|
spacing: Theme.spacingXXS
|
|
width: (parent.width - Theme.spacingL * 2) / 3
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
Row {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
name: SessionData.isLightMode ? "light_mode" : "dark_mode"
|
|
size: Theme.iconSize
|
|
color: SessionData.isLightMode ? "#FFA726" : "#7E57C2"
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: SessionData.isLightMode ? I18n.tr("Light Mode") : I18n.tr("Dark Mode")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Bold
|
|
color: Theme.surfaceText
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Active")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
width: parent.width
|
|
}
|
|
}
|
|
|
|
Column {
|
|
spacing: Theme.spacingXXS
|
|
width: (parent.width - Theme.spacingL * 2) / 3
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: SessionData.themeModeAutoEnabled && SessionData.themeModeNextTransition
|
|
|
|
Row {
|
|
spacing: Theme.spacingS
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
DankIcon {
|
|
name: "schedule"
|
|
size: Theme.iconSize
|
|
color: Theme.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Next Transition")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: Format.formatIsoTime(SessionData.themeModeNextTransition)
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
width: parent.width
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["transparency", "opacity", "widget", "styling"]
|
|
title: I18n.tr("Widget Styling")
|
|
settingKey: "widgetStyling"
|
|
iconName: "opacity"
|
|
|
|
SettingsButtonGroupRow {
|
|
tab: "theme"
|
|
tags: ["widget", "text", "style", "colorful", "default"]
|
|
settingKey: "widgetColorMode"
|
|
text: I18n.tr("Widget Text Style")
|
|
description: I18n.tr("Choose neutral or accent-colored widget text")
|
|
model: [I18n.tr("Default", "widget style option"), I18n.tr("Colorful", "widget style option")]
|
|
currentIndex: SettingsData.widgetColorMode === "colorful" ? 1 : 0
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected)
|
|
return;
|
|
SettingsData.set("widgetColorMode", index === 1 ? "colorful" : "default");
|
|
}
|
|
}
|
|
|
|
ColorDropdownRow {
|
|
tab: "theme"
|
|
tags: ["widget", "background", "color", "surface", "material"]
|
|
settingKey: "widgetBackgroundColor"
|
|
text: I18n.tr("Widget Background Color")
|
|
dropdownWidth: 220
|
|
options: themeColorsTab.widgetBackgroundOptions
|
|
currentMode: SettingsData.widgetBackgroundColor
|
|
customColor: SettingsData.widgetBackgroundCustomColor || "#6750A4"
|
|
pickerTitle: I18n.tr("Widget Background Color")
|
|
onModeSelected: mode => SettingsData.set("widgetBackgroundColor", mode)
|
|
onCustomColorSelected: selectedColor => SettingsData.set("widgetBackgroundCustomColor", selectedColor.toString())
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
id: widgetBackgroundCustomStrengthSlider
|
|
visible: SettingsData.widgetBackgroundColor === "custom"
|
|
tab: "theme"
|
|
tags: ["widget", "background", "color", "custom", "blend"]
|
|
settingKey: "widgetBackgroundCustomStrength"
|
|
text: I18n.tr("Custom Blend")
|
|
description: I18n.tr("Blend between Surface High and the selected custom color")
|
|
value: Math.round(SettingsData.widgetBackgroundCustomStrength * 100)
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 40
|
|
onSliderValueChanged: newValue => SettingsData.set("widgetBackgroundCustomStrength", newValue / 100)
|
|
|
|
Binding {
|
|
target: widgetBackgroundCustomStrengthSlider
|
|
property: "value"
|
|
value: Math.round(SettingsData.widgetBackgroundCustomStrength * 100)
|
|
restoreMode: Binding.RestoreBinding
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["control", "center", "tile", "button", "color", "active"]
|
|
settingKey: "controlCenterTileColorMode"
|
|
text: I18n.tr("Control Center Tile Color")
|
|
description: I18n.tr("Active tile background and icon color", "control center tile color setting description")
|
|
options: [I18n.tr("Primary", "tile color option"), I18n.tr("Primary Container", "tile color option"), I18n.tr("Secondary", "tile color option"), I18n.tr("Surface Variant", "tile color option")]
|
|
optionColorMap: ({
|
|
[I18n.tr("Primary", "tile color option")]: Theme.roleColor("primary"),
|
|
[I18n.tr("Primary Container", "tile color option")]: Theme.roleColor("primaryContainer"),
|
|
[I18n.tr("Secondary", "tile color option")]: Theme.roleColor("secondary"),
|
|
[I18n.tr("Surface Variant", "tile color option")]: Theme.roleColor("surfaceVariant")
|
|
})
|
|
currentValue: {
|
|
switch (SettingsData.controlCenterTileColorMode) {
|
|
case "primaryContainer":
|
|
return I18n.tr("Primary Container", "tile color option");
|
|
case "secondary":
|
|
return I18n.tr("Secondary", "tile color option");
|
|
case "surfaceVariant":
|
|
return I18n.tr("Surface Variant", "tile color option");
|
|
default:
|
|
return I18n.tr("Primary", "tile color option");
|
|
}
|
|
}
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Primary Container", "tile color option")) {
|
|
SettingsData.set("controlCenterTileColorMode", "primaryContainer");
|
|
} else if (value === I18n.tr("Secondary", "tile color option")) {
|
|
SettingsData.set("controlCenterTileColorMode", "secondary");
|
|
} else if (value === I18n.tr("Surface Variant", "tile color option")) {
|
|
SettingsData.set("controlCenterTileColorMode", "surfaceVariant");
|
|
} else {
|
|
SettingsData.set("controlCenterTileColorMode", "primary");
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["button", "color", "primary", "accent"]
|
|
settingKey: "buttonColorMode"
|
|
text: I18n.tr("Button Color")
|
|
description: I18n.tr("Color for primary action buttons")
|
|
options: [I18n.tr("Primary", "button color option"), I18n.tr("Primary Container", "button color option"), I18n.tr("Secondary", "button color option"), I18n.tr("Surface Variant", "button color option")]
|
|
optionColorMap: ({
|
|
[I18n.tr("Primary", "button color option")]: Theme.roleColor("primary"),
|
|
[I18n.tr("Primary Container", "button color option")]: Theme.roleColor("primaryContainer"),
|
|
[I18n.tr("Secondary", "button color option")]: Theme.roleColor("secondary"),
|
|
[I18n.tr("Surface Variant", "button color option")]: Theme.roleColor("surfaceVariant")
|
|
})
|
|
currentValue: {
|
|
switch (SettingsData.buttonColorMode) {
|
|
case "primaryContainer":
|
|
return I18n.tr("Primary Container", "button color option");
|
|
case "secondary":
|
|
return I18n.tr("Secondary", "button color option");
|
|
case "surfaceVariant":
|
|
return I18n.tr("Surface Variant", "button color option");
|
|
default:
|
|
return I18n.tr("Primary", "button color option");
|
|
}
|
|
}
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Primary Container", "button color option")) {
|
|
SettingsData.set("buttonColorMode", "primaryContainer");
|
|
} else if (value === I18n.tr("Secondary", "button color option")) {
|
|
SettingsData.set("buttonColorMode", "secondary");
|
|
} else if (value === I18n.tr("Surface Variant", "button color option")) {
|
|
SettingsData.set("buttonColorMode", "surfaceVariant");
|
|
} else {
|
|
SettingsData.set("buttonColorMode", "primary");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["surface", "popup", "transparency", "opacity", "modal", "border", "outline", "corner", "radius"]
|
|
title: I18n.tr("Surface Styling")
|
|
settingKey: "surfaceStyling"
|
|
iconName: "layers"
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["surface", "popup", "transparency", "opacity", "modal"]
|
|
settingKey: "popupTransparency"
|
|
text: I18n.tr("Surface Opacity")
|
|
description: I18n.tr("Controls opacity of shell surfaces, popouts, and modals", "Surface Opacity setting description")
|
|
visible: !themeColorsTab.connectedFrameModeActive
|
|
value: Math.round(SettingsData.popupTransparency * 100)
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 100
|
|
onSliderValueChanged: newValue => SettingsData.set("popupTransparency", newValue / 100)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["foreground", "layers", "contrast", "surface", "blur", "glass", "frosted"]
|
|
settingKey: "blurForegroundLayers"
|
|
text: I18n.tr("Foreground Layers")
|
|
description: I18n.tr("Show foreground surfaces on panels for stronger contrast")
|
|
checked: SettingsData.blurForegroundLayers ?? true
|
|
onToggled: checked => SettingsData.set("blurForegroundLayers", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["foreground", "layers", "opacity", "transparency", "contrast", "cards"]
|
|
settingKey: "foregroundLayerTransparency"
|
|
text: I18n.tr("Foreground Opacity")
|
|
description: I18n.tr("Opacity of foreground cards and nested surfaces on shell panels")
|
|
visible: SettingsData.blurForegroundLayers ?? true
|
|
value: Math.round((SettingsData.foregroundLayerTransparency ?? 1.0) * 100)
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 100
|
|
onSliderValueChanged: newValue => SettingsData.set("foregroundLayerTransparency", newValue / 100)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["foreground", "layers", "outline", "border", "cards", "widgets", "notifications", "control center"]
|
|
settingKey: "blurLayerOutlineOpacity"
|
|
text: I18n.tr("Layer Outline Opacity")
|
|
description: I18n.tr("Controls outlines around foreground cards, pills, and notification cards")
|
|
value: Math.round((SettingsData.blurLayerOutlineOpacity ?? 0.12) * 100)
|
|
minimum: 0
|
|
maximum: 40
|
|
unit: "%"
|
|
defaultValue: 12
|
|
onSliderValueChanged: newValue => SettingsData.set("blurLayerOutlineOpacity", newValue / 100)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["surface", "popup", "modal", "border", "outline", "edge"]
|
|
settingKey: "blurBorderEnabled"
|
|
text: I18n.tr("Surface Border Outline")
|
|
description: I18n.tr("Outline around shell surfaces")
|
|
checked: SettingsData.blurBorderEnabled ?? true
|
|
onToggled: checked => SettingsData.set("blurBorderEnabled", checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["surface", "popup", "modal", "border", "outline", "edge"]
|
|
settingKey: "blurBorderColor"
|
|
text: I18n.tr("Surface Border Color")
|
|
description: I18n.tr("Border color around popouts, modals, and other shell surfaces")
|
|
visible: SettingsData.blurBorderEnabled ?? true
|
|
options: [I18n.tr("Outline", "surface border color"), I18n.tr("Primary", "surface border color"), I18n.tr("Secondary", "surface border color"), I18n.tr("Text Color", "surface border color"), I18n.tr("Custom", "surface border color")]
|
|
optionColorMap: ({
|
|
[I18n.tr("Outline", "surface border color")]: Theme.outline,
|
|
[I18n.tr("Primary", "surface border color")]: Theme.primary,
|
|
[I18n.tr("Secondary", "surface border color")]: Theme.secondary,
|
|
[I18n.tr("Text Color", "surface border color")]: Theme.surfaceText,
|
|
[I18n.tr("Custom", "surface border color")]: SettingsData.blurBorderCustomColor ?? "#ffffff"
|
|
})
|
|
currentValue: {
|
|
switch (SettingsData.blurBorderColor) {
|
|
case "primary":
|
|
return I18n.tr("Primary", "surface border color");
|
|
case "secondary":
|
|
return I18n.tr("Secondary", "surface border color");
|
|
case "surfaceText":
|
|
return I18n.tr("Text Color", "surface border color");
|
|
case "custom":
|
|
return I18n.tr("Custom", "surface border color");
|
|
default:
|
|
return I18n.tr("Outline", "surface border color");
|
|
}
|
|
}
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Primary", "surface border color")) {
|
|
SettingsData.set("blurBorderColor", "primary");
|
|
} else if (value === I18n.tr("Secondary", "surface border color")) {
|
|
SettingsData.set("blurBorderColor", "secondary");
|
|
} else if (value === I18n.tr("Text Color", "surface border color")) {
|
|
SettingsData.set("blurBorderColor", "surfaceText");
|
|
} else if (value === I18n.tr("Custom", "surface border color")) {
|
|
SettingsData.set("blurBorderColor", "custom");
|
|
openSurfaceBorderColorPicker();
|
|
} else {
|
|
SettingsData.set("blurBorderColor", "outline");
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["surface", "popup", "modal", "border", "opacity"]
|
|
settingKey: "blurBorderOpacity"
|
|
text: I18n.tr("Surface Border Opacity")
|
|
description: I18n.tr("Controls the outline of popouts, modals, and other shell surfaces")
|
|
visible: SettingsData.blurBorderEnabled ?? true
|
|
value: Math.round((SettingsData.blurBorderOpacity ?? 0.35) * 100)
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 35
|
|
onSliderValueChanged: newValue => SettingsData.set("blurBorderOpacity", newValue / 100)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["corner", "radius", "rounded", "square"]
|
|
settingKey: "cornerRadius"
|
|
text: I18n.tr("Corner Radius")
|
|
description: I18n.tr("0 = square corners")
|
|
value: SettingsData.cornerRadius
|
|
minimum: 0
|
|
maximum: 32
|
|
unit: "px"
|
|
defaultValue: 12
|
|
onSliderValueChanged: newValue => SettingsData.setCornerRadius(newValue)
|
|
}
|
|
|
|
SettingsControlledBy {
|
|
visible: themeColorsTab.connectedFrameModeActive
|
|
parentModal: themeColorsTab.parentModal
|
|
settingLabel: I18n.tr("Surface Opacity")
|
|
reason: I18n.tr("Managed by Frame in Connected Mode")
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["floating", "window", "settings", "notepad", "authentication", "polkit", "opacity", "transparency", "foreground", "tile", "tiling"]
|
|
title: I18n.tr("Floating Windows")
|
|
settingKey: "floatingWindows"
|
|
iconName: "open_in_new"
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["floating", "window", "sync", "global", "surface", "opacity"]
|
|
settingKey: "floatingWindowSyncGlobal"
|
|
text: I18n.tr("Sync with Global Settings")
|
|
description: I18n.tr("Floating windows follow Surface Styling settings")
|
|
checked: SettingsData.floatingWindowSyncGlobal ?? true
|
|
onToggled: checked => SettingsData.set("floatingWindowSyncGlobal", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
id: floatingWindowOpacitySlider
|
|
tab: "theme"
|
|
tags: ["floating", "window", "opacity", "transparency"]
|
|
settingKey: "floatingWindowTransparency"
|
|
text: I18n.tr("Window Opacity")
|
|
description: I18n.tr("Opacity of floating DMS windows like Settings, Notepad, and authentication prompts")
|
|
value: Math.round(Theme.floatingWindowTransparency * 100)
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 100
|
|
onSliderValueChanged: newValue => {
|
|
themeColorsTab.unsyncFloatingWindowSettings();
|
|
SettingsData.set("floatingWindowTransparency", newValue / 100);
|
|
}
|
|
|
|
Binding {
|
|
target: floatingWindowOpacitySlider
|
|
property: "value"
|
|
value: Math.round(Theme.floatingWindowTransparency * 100)
|
|
restoreMode: Binding.RestoreBinding
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["floating", "window", "foreground", "layers", "contrast", "cards", "blur", "glass"]
|
|
settingKey: "floatingWindowForegroundLayers"
|
|
text: I18n.tr("Foreground Layers")
|
|
description: I18n.tr("Show foreground surfaces on cards inside floating windows")
|
|
checked: Theme.floatingWindowForegroundLayers
|
|
onToggled: checked => {
|
|
themeColorsTab.unsyncFloatingWindowSettings();
|
|
SettingsData.set("floatingWindowForegroundLayers", checked);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
id: floatingWindowForegroundOpacitySlider
|
|
tab: "theme"
|
|
tags: ["floating", "window", "foreground", "layers", "opacity", "transparency", "cards"]
|
|
settingKey: "floatingWindowForegroundTransparency"
|
|
text: I18n.tr("Foreground Opacity")
|
|
description: I18n.tr("Opacity of cards and nested surfaces inside floating windows")
|
|
visible: Theme.floatingWindowForegroundLayers
|
|
value: Math.round(Theme.floatingWindowForegroundTransparency * 100)
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 100
|
|
onSliderValueChanged: newValue => {
|
|
themeColorsTab.unsyncFloatingWindowSettings();
|
|
SettingsData.set("floatingWindowForegroundTransparency", newValue / 100);
|
|
}
|
|
|
|
Binding {
|
|
target: floatingWindowForegroundOpacitySlider
|
|
property: "value"
|
|
value: Math.round(Theme.floatingWindowForegroundTransparency * 100)
|
|
restoreMode: Binding.RestoreBinding
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["floating", "window", "tile", "tiling", "compositor", "rule", "niri", "hyprland", "mango"]
|
|
settingKey: "dmsWindowsFloating"
|
|
text: I18n.tr("Open Windows Floating")
|
|
description: I18n.tr("Open DMS windows floating instead of tiled")
|
|
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango
|
|
checked: SettingsData.dmsWindowsFloating ?? true
|
|
onToggled: checked => {
|
|
SettingsData.set("dmsWindowsFloating", checked);
|
|
if (checked)
|
|
themeColorsTab.checkWindowRulesIncludeStatus();
|
|
}
|
|
}
|
|
|
|
IncludeWarningBox {
|
|
visibleCondition: (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango) && (SettingsData.dmsWindowsFloating ?? true)
|
|
checking: themeColorsTab.checkingWindowRulesInclude
|
|
fixing: themeColorsTab.fixingWindowRulesInclude
|
|
includeReadOnly: themeColorsTab.windowRulesReadOnly
|
|
alreadyIncluded: themeColorsTab.windowRulesIncludeStatus.included
|
|
fragmentPath: "dms/windowrules"
|
|
onSetup: themeColorsTab.fixWindowRulesInclude
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
|
title: I18n.tr("Background Blur")
|
|
settingKey: "blurEnabled"
|
|
iconName: "blur_on"
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
|
settingKey: "blurEnabled"
|
|
text: I18n.tr("Background Blur")
|
|
description: !BlurService.available ? I18n.tr("Your compositor does not support background blur (ext-background-effect-v1)") : I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support. Adjust Opacity accordingly.")
|
|
checked: SettingsData.blurEnabled ?? false
|
|
enabled: BlurService.available
|
|
onToggled: checked => SettingsData.set("blurEnabled", checked)
|
|
}
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: xrayHintRow.implicitHeight
|
|
visible: CompositorService.isNiri || CompositorService.isHyprland
|
|
|
|
Row {
|
|
id: xrayHintRow
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
name: "info"
|
|
size: Theme.iconSizeSmall
|
|
color: Theme.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width - Theme.iconSizeSmall - Theme.spacingS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: I18n.tr("Xray options are in Compositor → Layout")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.primary
|
|
wrapMode: Text.Wrap
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: PopoutService.openSettingsWithTab("compositor_layout")
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "lift", "m3", "material"]
|
|
title: I18n.tr("Shadows")
|
|
settingKey: "m3ElevationEnabled"
|
|
iconName: "layers"
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "lift", "m3", "material"]
|
|
settingKey: "m3ElevationEnabled"
|
|
text: I18n.tr("Shadows")
|
|
description: I18n.tr("Material inspired shadows and elevation on modals, popouts, and dialogs")
|
|
checked: SettingsData.m3ElevationEnabled ?? true
|
|
onToggled: checked => SettingsData.set("m3ElevationEnabled", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "intensity", "blur", "m3"]
|
|
settingKey: "m3ElevationIntensity"
|
|
text: I18n.tr("Shadow Intensity")
|
|
description: I18n.tr("Controls the base blur radius and offset of shadows")
|
|
value: SettingsData.m3ElevationIntensity ?? 12
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "px"
|
|
defaultValue: 12
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onSliderValueChanged: newValue => SettingsData.set("m3ElevationIntensity", newValue)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "opacity", "transparency", "m3"]
|
|
settingKey: "m3ElevationOpacity"
|
|
text: I18n.tr("Shadow Opacity")
|
|
value: SettingsData.m3ElevationOpacity ?? 30
|
|
minimum: 0
|
|
maximum: 100
|
|
unit: "%"
|
|
defaultValue: 30
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onSliderValueChanged: newValue => SettingsData.set("m3ElevationOpacity", newValue)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "color", "m3"]
|
|
settingKey: "m3ElevationColorMode"
|
|
text: I18n.tr("Shadow Color")
|
|
description: I18n.tr("Base color for shadows (opacity is applied automatically)")
|
|
options: [I18n.tr("Default (Black)", "shadow color option"), I18n.tr("Text Color", "shadow color option"), I18n.tr("Primary", "shadow color option"), I18n.tr("Surface Variant", "shadow color option"), I18n.tr("Custom", "shadow color option")]
|
|
optionColorMap: ({
|
|
[I18n.tr("Default (Black)", "shadow color option")]: "#000000",
|
|
[I18n.tr("Text Color", "shadow color option")]: Theme.surfaceText,
|
|
[I18n.tr("Primary", "shadow color option")]: Theme.primary,
|
|
[I18n.tr("Surface Variant", "shadow color option")]: Theme.surfaceVariant,
|
|
[I18n.tr("Custom", "shadow color option")]: SettingsData.m3ElevationCustomColor ?? "#000000"
|
|
})
|
|
currentValue: {
|
|
switch (SettingsData.m3ElevationColorMode) {
|
|
case "text":
|
|
return I18n.tr("Text Color", "shadow color option");
|
|
case "primary":
|
|
return I18n.tr("Primary", "shadow color option");
|
|
case "surfaceVariant":
|
|
return I18n.tr("Surface Variant", "shadow color option");
|
|
case "custom":
|
|
return I18n.tr("Custom", "shadow color option");
|
|
default:
|
|
return I18n.tr("Default (Black)", "shadow color option");
|
|
}
|
|
}
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Primary", "shadow color option")) {
|
|
SettingsData.set("m3ElevationColorMode", "primary");
|
|
} else if (value === I18n.tr("Surface Variant", "shadow color option")) {
|
|
SettingsData.set("m3ElevationColorMode", "surfaceVariant");
|
|
} else if (value === I18n.tr("Custom", "shadow color option")) {
|
|
SettingsData.set("m3ElevationColorMode", "custom");
|
|
openM3ShadowColorPicker();
|
|
} else if (value === I18n.tr("Text Color", "shadow color option")) {
|
|
SettingsData.set("m3ElevationColorMode", "text");
|
|
} else {
|
|
SettingsData.set("m3ElevationColorMode", "default");
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "direction", "light", "advanced", "m3"]
|
|
settingKey: "m3ElevationLightDirection"
|
|
text: I18n.tr("Light Direction")
|
|
description: I18n.tr("Controls shadow cast direction for elevation layers")
|
|
options: [I18n.tr("Auto (Bar-aware)", "shadow direction option"), I18n.tr("Top (Default)", "shadow direction option"), I18n.tr("Top Left", "shadow direction option"), I18n.tr("Top Right", "shadow direction option"), I18n.tr("Bottom", "shadow direction option")]
|
|
currentValue: {
|
|
switch (SettingsData.m3ElevationLightDirection) {
|
|
case "autoBar":
|
|
return I18n.tr("Auto (Bar-aware)", "shadow direction option");
|
|
case "topLeft":
|
|
return I18n.tr("Top Left", "shadow direction option");
|
|
case "topRight":
|
|
return I18n.tr("Top Right", "shadow direction option");
|
|
case "bottom":
|
|
return I18n.tr("Bottom", "shadow direction option");
|
|
default:
|
|
return I18n.tr("Top (Default)", "shadow direction option");
|
|
}
|
|
}
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Auto (Bar-aware)", "shadow direction option")) {
|
|
SettingsData.set("m3ElevationLightDirection", "autoBar");
|
|
} else if (value === I18n.tr("Top Left", "shadow direction option")) {
|
|
SettingsData.set("m3ElevationLightDirection", "topLeft");
|
|
} else if (value === I18n.tr("Top Right", "shadow direction option")) {
|
|
SettingsData.set("m3ElevationLightDirection", "topRight");
|
|
} else if (value === I18n.tr("Bottom", "shadow direction option")) {
|
|
SettingsData.set("m3ElevationLightDirection", "bottom");
|
|
} else {
|
|
SettingsData.set("m3ElevationLightDirection", "top");
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
visible: (SettingsData.m3ElevationEnabled ?? true) && SettingsData.m3ElevationColorMode === "custom"
|
|
width: parent.width
|
|
implicitHeight: 36
|
|
height: implicitHeight
|
|
|
|
Row {
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: Theme.spacingM
|
|
|
|
StyledText {
|
|
text: I18n.tr("Custom Shadow Color")
|
|
color: Theme.surfaceText
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
Rectangle {
|
|
width: 26
|
|
height: 26
|
|
radius: 13
|
|
color: SettingsData.m3ElevationCustomColor ?? "#000000"
|
|
border.color: Theme.outline
|
|
border.width: 1
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: openM3ShadowColorPicker()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "modal", "dialog", "m3"]
|
|
settingKey: "modalElevationEnabled"
|
|
text: I18n.tr("Modal Shadows")
|
|
description: I18n.tr("Shadow elevation on modals and dialogs")
|
|
checked: SettingsData.modalElevationEnabled ?? true
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onToggled: checked => SettingsData.set("modalElevationEnabled", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "popout", "popup", "osd", "dropdown", "m3"]
|
|
settingKey: "popoutElevationEnabled"
|
|
text: I18n.tr("Popout Shadows")
|
|
description: I18n.tr("Shadow elevation on popouts, OSDs, and dropdowns")
|
|
checked: SettingsData.popoutElevationEnabled ?? true
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onToggled: checked => SettingsData.set("popoutElevationEnabled", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["elevation", "shadow", "bar", "panel", "navigation", "m3"]
|
|
settingKey: "barElevationEnabled"
|
|
text: I18n.tr("Bar Shadows")
|
|
description: I18n.tr("Shadow elevation on bars and panels")
|
|
checked: SettingsData.barElevationEnabled ?? true
|
|
visible: SettingsData.m3ElevationEnabled ?? true
|
|
onToggled: checked => SettingsData.set("barElevationEnabled", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["applications", "portal", "dark", "terminal"]
|
|
title: I18n.tr("Applications")
|
|
settingKey: "applications"
|
|
iconName: "apps"
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["portal", "sync", "dark", "mode"]
|
|
settingKey: "syncModeWithPortal"
|
|
text: I18n.tr("Sync Mode with Portal")
|
|
description: I18n.tr("Sync dark mode with settings portals for system-wide theme hints")
|
|
checked: SettingsData.syncModeWithPortal
|
|
onToggled: checked => SettingsData.set("syncModeWithPortal", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["terminal", "dark", "always"]
|
|
settingKey: "terminalsAlwaysDark"
|
|
text: I18n.tr("Terminals - Always use Dark Theme")
|
|
description: I18n.tr("Force terminal applications to always use dark color schemes")
|
|
checked: SettingsData.terminalsAlwaysDark
|
|
onToggled: checked => SettingsData.set("terminalsAlwaysDark", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["cursor", "mouse", "pointer", "theme", "size"]
|
|
title: I18n.tr("Cursor Theme")
|
|
settingKey: "cursorTheme"
|
|
iconName: "mouse"
|
|
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isMango
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
IncludeWarningBox {
|
|
checking: themeColorsTab.checkingCursorInclude
|
|
fixing: themeColorsTab.fixingCursorInclude
|
|
includeReadOnly: themeColorsTab.cursorReadOnly
|
|
alreadyIncluded: themeColorsTab.cursorIncludeStatus.included
|
|
fragmentPath: "dms/cursor"
|
|
onSetup: themeColorsTab.fixCursorInclude
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "mouse", "pointer", "theme"]
|
|
settingKey: "cursorTheme"
|
|
text: I18n.tr("Cursor Theme")
|
|
description: I18n.tr("Mouse pointer appearance")
|
|
currentValue: SettingsData.cursorSettings.theme
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: cachedCursorThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setCursorTheme(value);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "mouse", "pointer", "size"]
|
|
settingKey: "cursorSize"
|
|
text: I18n.tr("Cursor Size")
|
|
description: I18n.tr("Mouse pointer size in pixels")
|
|
value: SettingsData.cursorSettings.size
|
|
minimum: 12
|
|
maximum: 128
|
|
unit: "px"
|
|
defaultValue: 24
|
|
onSliderValueChanged: newValue => SettingsData.setCursorSize(newValue)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["mango", "touchpad", "trackpad", "natural", "scrolling"]
|
|
settingKey: "mangoTrackpadNaturalScrolling"
|
|
text: I18n.tr("Natural Touchpad Scrolling")
|
|
description: I18n.tr("Invert touchpad scroll direction")
|
|
visible: CompositorService.isMango
|
|
checked: SettingsData.mangoTrackpadNaturalScrolling
|
|
onToggled: checked => SettingsData.set("mangoTrackpadNaturalScrolling", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "hide", "typing"]
|
|
settingKey: "cursorHideWhenTyping"
|
|
text: I18n.tr("Hide When Typing")
|
|
description: I18n.tr("Hide cursor when pressing keyboard keys")
|
|
visible: CompositorService.isNiri || CompositorService.isHyprland
|
|
checked: {
|
|
if (CompositorService.isNiri)
|
|
return SettingsData.cursorSettings.niri?.hideWhenTyping || false;
|
|
if (CompositorService.isHyprland)
|
|
return SettingsData.cursorSettings.hyprland?.hideOnKeyPress || false;
|
|
return false;
|
|
}
|
|
onToggled: checked => {
|
|
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
|
|
if (CompositorService.isNiri) {
|
|
if (!updated.niri)
|
|
updated.niri = {};
|
|
updated.niri.hideWhenTyping = checked;
|
|
} else if (CompositorService.isHyprland) {
|
|
if (!updated.hyprland)
|
|
updated.hyprland = {};
|
|
updated.hyprland.hideOnKeyPress = checked;
|
|
}
|
|
SettingsData.set("cursorSettings", updated);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "hide", "touch"]
|
|
settingKey: "cursorHideOnTouch"
|
|
text: I18n.tr("Hide on Touch")
|
|
description: I18n.tr("Hide cursor when using touch input")
|
|
visible: CompositorService.isHyprland
|
|
checked: SettingsData.cursorSettings.hyprland?.hideOnTouch || false
|
|
onToggled: checked => {
|
|
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
|
|
if (!updated.hyprland)
|
|
updated.hyprland = {};
|
|
updated.hyprland.hideOnTouch = checked;
|
|
SettingsData.set("cursorSettings", updated);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tab: "theme"
|
|
tags: ["cursor", "hide", "timeout", "inactive"]
|
|
settingKey: "cursorHideAfterInactive"
|
|
text: I18n.tr("Auto-Hide Timeout")
|
|
description: I18n.tr("Hide cursor after inactivity (0 = disabled)")
|
|
value: {
|
|
if (CompositorService.isNiri)
|
|
return SettingsData.cursorSettings.niri?.hideAfterInactiveMs || 0;
|
|
if (CompositorService.isHyprland)
|
|
return SettingsData.cursorSettings.hyprland?.inactiveTimeout || 0;
|
|
if (CompositorService.isMango)
|
|
return SettingsData.cursorSettings.mango?.cursorHideTimeout || 0;
|
|
return 0;
|
|
}
|
|
minimum: 0
|
|
maximum: CompositorService.isNiri ? 5000 : 10
|
|
unit: CompositorService.isNiri ? "ms" : "s"
|
|
defaultValue: 0
|
|
onSliderValueChanged: newValue => {
|
|
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
|
|
if (CompositorService.isNiri) {
|
|
if (!updated.niri)
|
|
updated.niri = {};
|
|
updated.niri.hideAfterInactiveMs = newValue;
|
|
} else if (CompositorService.isHyprland) {
|
|
if (!updated.hyprland)
|
|
updated.hyprland = {};
|
|
updated.hyprland.inactiveTimeout = newValue;
|
|
} else if (CompositorService.isMango) {
|
|
if (!updated.mango)
|
|
updated.mango = {};
|
|
updated.mango.cursorHideTimeout = newValue;
|
|
}
|
|
SettingsData.set("cursorSettings", updated);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system"]
|
|
title: I18n.tr("Icon Theme")
|
|
settingKey: "iconTheme"
|
|
iconName: "interests"
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "light", "dark", "mode"]
|
|
settingKey: "iconThemePerMode"
|
|
text: I18n.tr("Separate Light & Dark Themes")
|
|
description: I18n.tr("Use different icon themes for light and dark mode")
|
|
checked: SettingsData.iconThemePerMode
|
|
onToggled: checked => SettingsData.setIconThemePerMode(checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system"]
|
|
settingKey: "iconTheme"
|
|
text: I18n.tr("Icon Theme")
|
|
description: I18n.tr("DankShell & System Icons (requires restart)")
|
|
visible: !SettingsData.iconThemePerMode
|
|
currentValue: SettingsData.iconThemeDark
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: cachedIconThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setIconThemeForMode(value, false);
|
|
warnIfMissingQtTheme();
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system", "dark"]
|
|
settingKey: "iconThemeDark"
|
|
text: I18n.tr("Dark Mode Icon Theme")
|
|
description: I18n.tr("DankShell & System Icons (requires restart)")
|
|
visible: SettingsData.iconThemePerMode
|
|
currentValue: SettingsData.iconThemeDark
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: cachedIconThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setIconThemeForMode(value, false);
|
|
warnIfMissingQtTheme();
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
tab: "theme"
|
|
tags: ["icon", "theme", "system", "light"]
|
|
settingKey: "iconThemeLight"
|
|
text: I18n.tr("Light Mode Icon Theme")
|
|
description: I18n.tr("DankShell & System Icons (requires restart)")
|
|
visible: SettingsData.iconThemePerMode
|
|
currentValue: SettingsData.iconThemeLight
|
|
enableFuzzySearch: true
|
|
popupWidthOffset: 100
|
|
maxPopupHeight: 236
|
|
options: cachedIconThemes
|
|
onValueChanged: value => {
|
|
SettingsData.setIconThemeForMode(value, true);
|
|
warnIfMissingQtTheme();
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["matugen", "templates", "theming"]
|
|
title: I18n.tr("Matugen Templates")
|
|
settingKey: "matugenTemplates"
|
|
iconName: "auto_awesome"
|
|
collapsible: true
|
|
expanded: false
|
|
visible: Theme.matugenAvailable
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "user", "templates"]
|
|
settingKey: "runUserMatugenTemplates"
|
|
text: I18n.tr("Run User Templates")
|
|
description: ""
|
|
checked: SettingsData.runUserMatugenTemplates
|
|
onToggled: checked => SettingsData.set("runUserMatugenTemplates", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "dms", "templates"]
|
|
settingKey: "runDmsMatugenTemplates"
|
|
text: I18n.tr("Run DMS Templates")
|
|
description: ""
|
|
checked: SettingsData.runDmsMatugenTemplates
|
|
onToggled: checked => SettingsData.set("runDmsMatugenTemplates", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "gtk", "template"]
|
|
settingKey: "matugenTemplateGtk"
|
|
text: "GTK"
|
|
description: getTemplateDescription("gtk", "")
|
|
descriptionColor: getTemplateDescriptionColor("gtk")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateGtk
|
|
onToggled: checked => SettingsData.set("matugenTemplateGtk", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "niri", "template"]
|
|
settingKey: "matugenTemplateNiri"
|
|
text: "niri"
|
|
description: getTemplateDescription("niri", "")
|
|
descriptionColor: getTemplateDescriptionColor("niri")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateNiri
|
|
onToggled: checked => SettingsData.set("matugenTemplateNiri", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "hyprland", "template"]
|
|
settingKey: "matugenTemplateHyprland"
|
|
text: "Hyprland"
|
|
description: getTemplateDescription("hyprland", "")
|
|
descriptionColor: getTemplateDescriptionColor("hyprland")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateHyprland
|
|
onToggled: checked => SettingsData.set("matugenTemplateHyprland", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "mangowc", "template"]
|
|
settingKey: "matugenTemplateMangowc"
|
|
text: "mangowc"
|
|
description: getTemplateDescription("mangowc", "")
|
|
descriptionColor: getTemplateDescriptionColor("mangowc")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateMangowc
|
|
onToggled: checked => SettingsData.set("matugenTemplateMangowc", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "qt5ct", "template"]
|
|
settingKey: "matugenTemplateQt5ct"
|
|
text: "qt5ct"
|
|
description: getTemplateDescription("qt5ct", "")
|
|
descriptionColor: getTemplateDescriptionColor("qt5ct")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateQt5ct
|
|
onToggled: checked => SettingsData.set("matugenTemplateQt5ct", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "qt6ct", "template"]
|
|
settingKey: "matugenTemplateQt6ct"
|
|
text: "qt6ct"
|
|
description: getTemplateDescription("qt6ct", "")
|
|
descriptionColor: getTemplateDescriptionColor("qt6ct")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateQt6ct
|
|
onToggled: checked => SettingsData.set("matugenTemplateQt6ct", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "qtengine", "template", "qt"]
|
|
settingKey: "matugenTemplateQtengine"
|
|
text: "qtengine"
|
|
description: getTemplateDescription("qtengine", "")
|
|
descriptionColor: getTemplateDescriptionColor("qtengine")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateQtengine
|
|
onToggled: checked => SettingsData.set("matugenTemplateQtengine", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "fcitx5", "input", "template"]
|
|
settingKey: "matugenTemplateFcitx5"
|
|
text: "Fcitx5"
|
|
description: getTemplateDescription("fcitx5", "")
|
|
descriptionColor: getTemplateDescriptionColor("fcitx5")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateFcitx5
|
|
onToggled: checked => SettingsData.set("matugenTemplateFcitx5", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "firefox", "template"]
|
|
settingKey: "matugenTemplateFirefox"
|
|
text: "Firefox"
|
|
description: getTemplateDescription("firefox", "")
|
|
descriptionColor: getTemplateDescriptionColor("firefox")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateFirefox
|
|
onToggled: checked => SettingsData.set("matugenTemplateFirefox", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "pywalfox", "template"]
|
|
settingKey: "matugenTemplatePywalfox"
|
|
text: "pywalfox"
|
|
description: getTemplateDescription("pywalfox", "")
|
|
descriptionColor: getTemplateDescriptionColor("pywalfox")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplatePywalfox
|
|
onToggled: checked => SettingsData.set("matugenTemplatePywalfox", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "zenbrowser", "template"]
|
|
settingKey: "matugenTemplateZenBrowser"
|
|
text: "zenbrowser"
|
|
description: getTemplateDescription("zenbrowser", "")
|
|
descriptionColor: getTemplateDescriptionColor("zenbrowser")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateZenBrowser
|
|
onToggled: checked => SettingsData.set("matugenTemplateZenBrowser", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "vesktop", "discord", "template"]
|
|
settingKey: "matugenTemplateVesktop"
|
|
text: "vesktop"
|
|
description: getTemplateDescription("vesktop", "")
|
|
descriptionColor: getTemplateDescriptionColor("vesktop")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateVesktop
|
|
onToggled: checked => SettingsData.set("matugenTemplateVesktop", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "vencord", "discord", "template"]
|
|
settingKey: "matugenTemplateVencord"
|
|
text: "vencord"
|
|
description: getTemplateDescription("vencord", "")
|
|
descriptionColor: getTemplateDescriptionColor("vencord")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateVencord
|
|
onToggled: checked => SettingsData.set("matugenTemplateVencord", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "equibop", "discord", "template"]
|
|
settingKey: "matugenTemplateEquibop"
|
|
text: "equibop"
|
|
description: getTemplateDescription("equibop", "")
|
|
descriptionColor: getTemplateDescriptionColor("equibop")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateEquibop
|
|
onToggled: checked => SettingsData.set("matugenTemplateEquibop", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "ghostty", "terminal", "template"]
|
|
settingKey: "matugenTemplateGhostty"
|
|
text: "Ghostty"
|
|
description: getTemplateDescription("ghostty", "")
|
|
descriptionColor: getTemplateDescriptionColor("ghostty")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateGhostty
|
|
onToggled: checked => SettingsData.set("matugenTemplateGhostty", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "kitty", "terminal", "template"]
|
|
settingKey: "matugenTemplateKitty"
|
|
text: "kitty"
|
|
description: getTemplateDescription("kitty", "")
|
|
descriptionColor: getTemplateDescriptionColor("kitty")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateKitty
|
|
onToggled: checked => SettingsData.set("matugenTemplateKitty", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "foot", "terminal", "template"]
|
|
settingKey: "matugenTemplateFoot"
|
|
text: "foot"
|
|
description: getTemplateDescription("foot", "")
|
|
descriptionColor: getTemplateDescriptionColor("foot")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateFoot
|
|
onToggled: checked => SettingsData.set("matugenTemplateFoot", checked)
|
|
}
|
|
|
|
SettingsDivider {
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
id: neovimThemeToggle
|
|
tab: "theme"
|
|
tags: ["matugen", "neovim", "terminal", "template"]
|
|
settingKey: "matugenTemplateNeovim"
|
|
text: "neovim"
|
|
description: getTemplateDescription("nvim", I18n.tr("Required plugin: ") + "https://github.com/AvengeMedia/base46")
|
|
descriptionColor: getTemplateDescriptionColor("nvim")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateNeovim
|
|
onToggled: checked => SettingsData.set("matugenTemplateNeovim", checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
text: I18n.tr("Dark mode base")
|
|
tab: "theme"
|
|
tags: ["matugen", "neovim", "terminal", "template"]
|
|
settingKey: "matugenTemplateNeovimSettings"
|
|
description: "Base to derive dark theme from"
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
currentValue: SettingsData.matugenTemplateNeovimSettings?.dark?.baseTheme ?? "github_dark"
|
|
options: themeColorsTab.neovimDarkBaseThemes.concat(themeColorsTab.neovimLightBaseThemes)
|
|
enableFuzzySearch: true
|
|
onValueChanged: value => {
|
|
const settings = SettingsData.matugenTemplateNeovimSettings;
|
|
settings.dark.baseTheme = value;
|
|
SettingsData.set("matugenTemplateNeovimSettings", settings);
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
text: I18n.tr("Light mode base")
|
|
tab: "theme"
|
|
tags: ["matugen", "neovim", "terminal", "template"]
|
|
settingKey: "matugenTemplateNeovimSettings"
|
|
description: "Base to derive light theme from"
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
currentValue: SettingsData.matugenTemplateNeovimSettings?.light?.baseTheme ?? "github_light"
|
|
options: themeColorsTab.neovimLightBaseThemes.concat(themeColorsTab.neovimDarkBaseThemes)
|
|
enableFuzzySearch: true
|
|
onValueChanged: value => {
|
|
const settings = SettingsData.matugenTemplateNeovimSettings;
|
|
settings.light.baseTheme = value;
|
|
SettingsData.set("matugenTemplateNeovimSettings", settings);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
text: I18n.tr("Dark mode harmony")
|
|
tags: ["matugen", "neovim", "terminal", "template"]
|
|
settingKey: "matugenTemplateNeovimSettings"
|
|
description: "How much should the base dark theme be tinted"
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
minimum: 0
|
|
maximum: 100
|
|
value: (SettingsData.matugenTemplateNeovimSettings?.dark?.harmony ?? 0.5) * 100
|
|
defaultValue: 50
|
|
onSliderValueChanged: value => {
|
|
const settings = SettingsData.matugenTemplateNeovimSettings;
|
|
settings.dark.harmony = value / 100;
|
|
SettingsData.set("matugenTemplateNeovimSettings", settings);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
text: I18n.tr("Light mode harmony")
|
|
tags: ["matugen", "neovim", "terminal", "template"]
|
|
settingKey: "matugenTemplateNeovimSettings"
|
|
description: "How much should the base light theme be tinted"
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
minimum: 0
|
|
maximum: 100
|
|
value: (SettingsData.matugenTemplateNeovimSettings?.light?.harmony ?? 0.5) * 100
|
|
defaultValue: 50
|
|
onSliderValueChanged: value => {
|
|
const settings = SettingsData.matugenTemplateNeovimSettings;
|
|
settings.light.harmony = value / 100;
|
|
SettingsData.set("matugenTemplateNeovimSettings", settings);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
text: I18n.tr("Follow DMS background color")
|
|
tags: ["matugen", "neovim", "terminal", "template"]
|
|
settingKey: "matugenTemplateNeovimSetBackground"
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
checked: SettingsData.matugenTemplateNeovimSetBackground ?? true
|
|
onToggled: checked => SettingsData.set("matugenTemplateNeovimSetBackground", checked)
|
|
}
|
|
|
|
SettingsDivider {
|
|
visible: neovimThemeToggle.visible && neovimThemeToggle.checked
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "alacritty", "terminal", "template"]
|
|
settingKey: "matugenTemplateAlacritty"
|
|
text: "Alacritty"
|
|
description: getTemplateDescription("alacritty", "")
|
|
descriptionColor: getTemplateDescriptionColor("alacritty")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateAlacritty
|
|
onToggled: checked => SettingsData.set("matugenTemplateAlacritty", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "wezterm", "terminal", "template"]
|
|
settingKey: "matugenTemplateWezterm"
|
|
text: "WezTerm"
|
|
description: getTemplateDescription("wezterm", "")
|
|
descriptionColor: getTemplateDescriptionColor("wezterm")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateWezterm
|
|
onToggled: checked => SettingsData.set("matugenTemplateWezterm", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "dgop", "template"]
|
|
settingKey: "matugenTemplateDgop"
|
|
text: "dgop"
|
|
description: getTemplateDescription("dgop", "")
|
|
descriptionColor: getTemplateDescriptionColor("dgop")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateDgop
|
|
onToggled: checked => SettingsData.set("matugenTemplateDgop", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "kcolorscheme", "kde", "template"]
|
|
settingKey: "matugenTemplateKcolorscheme"
|
|
text: "KColorScheme"
|
|
description: getTemplateDescription("kcolorscheme", "")
|
|
descriptionColor: getTemplateDescriptionColor("kcolorscheme")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateKcolorscheme
|
|
onToggled: checked => SettingsData.set("matugenTemplateKcolorscheme", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "vscode", "code", "template"]
|
|
settingKey: "matugenTemplateVscode"
|
|
text: "VS Code"
|
|
description: getTemplateDescription("vscode", I18n.tr("Requires the DMS Theme extension from the editor marketplace", "vscode matugen template description"))
|
|
descriptionColor: getTemplateDescriptionColor("vscode")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateVscode
|
|
onToggled: checked => SettingsData.set("matugenTemplateVscode", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "emacs", "template"]
|
|
settingKey: "matugenTemplateEmacs"
|
|
text: "Emacs"
|
|
description: getTemplateDescription("emacs", "")
|
|
descriptionColor: getTemplateDescriptionColor("emacs")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateEmacs
|
|
onToggled: checked => SettingsData.set("matugenTemplateEmacs", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tab: "theme"
|
|
tags: ["matugen", "zed", "template"]
|
|
settingKey: "matugenTemplateZed"
|
|
text: "Zed"
|
|
description: getTemplateDescription("zed", "")
|
|
descriptionColor: getTemplateDescriptionColor("zed")
|
|
visible: SettingsData.runDmsMatugenTemplates
|
|
checked: SettingsData.matugenTemplateZed
|
|
onToggled: checked => SettingsData.set("matugenTemplateZed", checked)
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: warningText.implicitHeight + Theme.spacingM * 2
|
|
radius: Theme.cornerRadius
|
|
color: Theme.warningHover
|
|
|
|
Row {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingM
|
|
spacing: Theme.spacingM
|
|
|
|
DankIcon {
|
|
name: "info"
|
|
size: Theme.iconSizeSmall
|
|
color: Theme.warning
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
id: warningText
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
text: I18n.tr("The below settings will modify your GTK and Qt settings. If you wish to preserve your current configurations, please back them up (qt5ct.conf|qt6ct.conf|qtengine/config.json and ~/.config/gtk-3.0|gtk-4.0).")
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width - Theme.iconSizeSmall - Theme.spacingM
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
tab: "theme"
|
|
tags: ["system", "app", "theming", "gtk", "qt"]
|
|
title: I18n.tr("System App Theming")
|
|
settingKey: "systemAppTheming"
|
|
iconName: "brush"
|
|
visible: Theme.matugenAvailable
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
|
|
Rectangle {
|
|
width: (parent.width - Theme.spacingM) / 2
|
|
height: 48
|
|
radius: Theme.cornerRadius
|
|
color: Theme.primaryHover
|
|
|
|
Row {
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
name: "settings"
|
|
size: 16
|
|
color: Theme.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Apply GTK Colors")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.primary
|
|
font.weight: Font.Medium
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: Theme.applyGtkColors()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: (parent.width - Theme.spacingM) / 2
|
|
height: 48
|
|
radius: Theme.cornerRadius
|
|
color: Theme.primaryHover
|
|
|
|
Row {
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
name: "settings"
|
|
size: 16
|
|
color: Theme.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Apply Qt Colors")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.primary
|
|
font.weight: Font.Medium
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: Theme.applyQtColors()
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr('Generate baseline GTK3/4, QT5/QT6, or qtengine configurations to follow DMS colors (only qt6ct requires qt6ct-kde). Only needed once.<br /><br />It is recommended to configure %1 prior to applying GTK themes.').arg(`<a href="https://github.com/AvengeMedia/DankMaterialShell/blob/master/README.md#Theming" style="text-decoration:none; color:${Theme.primary};">adw-gtk3</a>`)
|
|
textFormat: Text.RichText
|
|
linkColor: Theme.primary
|
|
onLinkActivated: url => Qt.openUrlExternally(url)
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
acceptedButtons: Qt.NoButton
|
|
propagateComposedEvents: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FileBrowserModal {
|
|
id: fileBrowserModal
|
|
browserTitle: I18n.tr("Select Custom Theme", "custom theme file browser title")
|
|
filterExtensions: ["*.json"]
|
|
showHiddenFiles: true
|
|
|
|
function selectCustomTheme() {
|
|
shouldBeVisible = true;
|
|
}
|
|
|
|
onFileSelected: function (filePath) {
|
|
if (filePath.endsWith(".json")) {
|
|
SettingsData.set("customThemeFile", filePath);
|
|
Theme.switchTheme("custom");
|
|
close();
|
|
}
|
|
}
|
|
}
|
|
|
|
LazyLoader {
|
|
id: saveBrowserLoader
|
|
active: false
|
|
|
|
FileBrowserSurfaceModal {
|
|
id: saveBrowser
|
|
|
|
browserTitle: I18n.tr("Save Extracted Theme", "extract theme save dialog title")
|
|
browserIcon: "download"
|
|
browserType: "default"
|
|
fileExtensions: ["*.json"]
|
|
allowStacking: true
|
|
saveMode: true
|
|
defaultFileName: "dms-extracted-theme.json"
|
|
|
|
onFileSelected: path => {
|
|
saveExtractedTheme(pendingExtractJson, Paths.strip(path));
|
|
close();
|
|
}
|
|
}
|
|
}
|
|
|
|
LazyLoader {
|
|
id: themeBrowserLoader
|
|
active: false
|
|
|
|
ThemeBrowser {
|
|
id: themeBrowserItem
|
|
parentModal: themeColorsTab.parentModal
|
|
}
|
|
}
|
|
|
|
function showThemeBrowser() {
|
|
themeBrowserLoader.active = true;
|
|
if (themeBrowserLoader.item)
|
|
themeBrowserLoader.item.show();
|
|
}
|
|
|
|
FileView {
|
|
id: extractSaveFileView
|
|
blockWrites: true
|
|
preload: false
|
|
atomicWrites: true
|
|
printErrors: true
|
|
|
|
onSaved: {
|
|
ToastService.showInfo(I18n.tr("Theme extracted to: %1", "extract theme success").arg(Paths.strip(extractSaveFileView.path)));
|
|
}
|
|
|
|
onSaveFailed: error => {
|
|
ToastService.showError(I18n.tr("Failed to extract theme", "extract theme error"));
|
|
log.warn("Failed to write extracted theme to " + extractSaveFileView.path + ": " + error);
|
|
}
|
|
}
|
|
|
|
function saveExtractedTheme(json, outputPath) {
|
|
extractSaveFileView.path = outputPath;
|
|
extractSaveFileView.setText(json);
|
|
}
|
|
}
|