1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

cursor: hypr, mango, and dankinstall support for configs

This commit is contained in:
bbedward
2026-01-06 20:35:22 -05:00
parent 721700190b
commit ad43053b94
24 changed files with 2741 additions and 709 deletions

View File

@@ -1,3 +1,4 @@
import QtCore
import QtQuick
import QtQuick.Effects
import Quickshell
@@ -17,6 +18,79 @@ Item {
property var installedRegistryThemes: []
property var templateDetection: ({})
property var cursorIncludeStatus: ({
"exists": false,
"included": false
})
property bool checkingCursorInclude: false
property bool fixingCursorInclude: false
function getCursorConfigPaths() {
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
switch (CompositorService.compositor) {
case "niri":
return {
"configFile": configDir + "/niri/config.kdl",
"cursorFile": configDir + "/niri/dms/cursor.kdl",
"grepPattern": 'include.*"dms/cursor.kdl"',
"includeLine": 'include "dms/cursor.kdl"'
};
case "hyprland":
return {
"configFile": configDir + "/hypr/hyprland.conf",
"cursorFile": configDir + "/hypr/dms/cursor.conf",
"grepPattern": 'source.*dms/cursor.conf',
"includeLine": "source = ./dms/cursor.conf"
};
case "dwl":
return {
"configFile": configDir + "/mango/config.conf",
"cursorFile": configDir + "/mango/dms/cursor.conf",
"grepPattern": 'source.*dms/cursor.conf',
"includeLine": "source=./dms/cursor.conf"
};
default:
return null;
}
}
function checkCursorIncludeStatus() {
const paths = getCursorConfigPaths();
if (!paths) {
cursorIncludeStatus = {
"exists": false,
"included": false
};
return;
}
checkingCursorInclude = true;
Proc.runCommand("check-cursor-include", ["sh", "-c", `exists=false; included=false; ` + `[ -f "${paths.cursorFile}" ] && exists=true; ` + `[ -f "${paths.configFile}" ] && grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" | grep -q '${paths.grepPattern}' && included=true; ` + `echo "$exists $included"`], (output, exitCode) => {
checkingCursorInclude = false;
const parts = output.trim().split(" ");
cursorIncludeStatus = {
"exists": parts[0] === "true",
"included": parts[1] === "true"
};
});
}
function fixCursorInclude() {
const paths = getCursorConfigPaths();
if (!paths)
return;
fixingCursorInclude = true;
const cursorDir = paths.cursorFile.substring(0, paths.cursorFile.lastIndexOf("/"));
const unixTime = Math.floor(Date.now() / 1000);
const backupFile = paths.configFile + ".backup" + unixTime;
Proc.runCommand("fix-cursor-include", ["sh", "-c", `cp "${paths.configFile}" "${backupFile}" 2>/dev/null; ` + `mkdir -p "${cursorDir}" && ` + `touch "${paths.cursorFile}" && ` + `if ! grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" 2>/dev/null | grep -q '${paths.grepPattern}'; then ` + `echo '' >> "${paths.configFile}" && ` + `echo '${paths.includeLine}' >> "${paths.configFile}"; fi`], (output, exitCode) => {
fixingCursorInclude = false;
if (exitCode !== 0)
return;
checkCursorIncludeStatus();
SettingsData.updateCompositorCursor();
});
}
function isTemplateDetected(templateId) {
if (!templateDetection || Object.keys(templateDetection).length === 0)
return true;
@@ -45,6 +119,8 @@ Item {
if (PopoutService.pendingThemeInstall)
Qt.callLater(() => themeBrowser.show());
templateCheckProcess.running = true;
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
checkCursorIncludeStatus();
}
Process {
@@ -1316,6 +1392,195 @@ Item {
}
}
SettingsCard {
tab: "theme"
tags: ["cursor", "mouse", "pointer", "theme", "size"]
title: I18n.tr("Cursor Theme")
settingKey: "cursorTheme"
iconName: "mouse"
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl
Column {
width: parent.width
spacing: Theme.spacingM
StyledRect {
id: cursorWarningBox
width: parent.width
height: cursorWarningContent.implicitHeight + Theme.spacingM * 2
radius: Theme.cornerRadius
readonly property bool showError: themeColorsTab.cursorIncludeStatus.exists && !themeColorsTab.cursorIncludeStatus.included
readonly property bool showSetup: !themeColorsTab.cursorIncludeStatus.exists && !themeColorsTab.cursorIncludeStatus.included
color: (showError || showSetup) ? Theme.withAlpha(Theme.warning, 0.15) : "transparent"
border.color: (showError || showSetup) ? Theme.withAlpha(Theme.warning, 0.3) : "transparent"
border.width: 1
visible: (showError || showSetup) && !themeColorsTab.checkingCursorInclude
Row {
id: cursorWarningContent
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingM
DankIcon {
name: "warning"
size: Theme.iconSize
color: Theme.warning
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - Theme.iconSize - (cursorFixButton.visible ? cursorFixButton.width + Theme.spacingM : 0) - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: cursorWarningBox.showSetup ? I18n.tr("Cursor Config Not Configured") : I18n.tr("Cursor Include Missing")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.warning
}
StyledText {
text: cursorWarningBox.showSetup ? I18n.tr("Click 'Setup' to create cursor config and add include to your compositor config.") : I18n.tr("dms/cursor config exists but is not included. Cursor settings won't apply.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
}
DankButton {
id: cursorFixButton
visible: cursorWarningBox.showError || cursorWarningBox.showSetup
text: themeColorsTab.fixingCursorInclude ? I18n.tr("Fixing...") : (cursorWarningBox.showSetup ? I18n.tr("Setup") : I18n.tr("Fix Now"))
backgroundColor: Theme.warning
textColor: Theme.background
enabled: !themeColorsTab.fixingCursorInclude
anchors.verticalCenter: parent.verticalCenter
onClicked: 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: 16
maximum: 48
unit: "px"
defaultValue: 24
onSliderValueChanged: newValue => SettingsData.setCursorSize(newValue)
}
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.isDwl)
return SettingsData.cursorSettings.dwl?.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.isDwl) {
if (!updated.dwl)
updated.dwl = {};
updated.dwl.cursorHideTimeout = newValue;
}
SettingsData.set("cursorSettings", updated);
}
}
}
}
SettingsCard {
tab: "theme"
tags: ["matugen", "templates", "theming"]
@@ -1640,86 +1905,6 @@ Item {
}
}
SettingsCard {
tab: "theme"
tags: ["cursor", "mouse", "pointer", "theme", "size"]
title: I18n.tr("Cursor Theme")
settingKey: "cursorTheme"
iconName: "mouse"
visible: CompositorService.isNiri
Column {
width: parent.width
spacing: Theme.spacingM
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: 16
maximum: 48
unit: "px"
defaultValue: 24
onSliderValueChanged: newValue => SettingsData.setCursorSize(newValue)
}
SettingsToggleRow {
tab: "theme"
tags: ["cursor", "hide", "typing"]
settingKey: "cursorHideWhenTyping"
text: I18n.tr("Hide When Typing")
description: I18n.tr("Hide cursor when pressing keyboard keys")
checked: SettingsData.cursorSettings.niri?.hideWhenTyping || false
onToggled: checked => {
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
if (!updated.niri)
updated.niri = {};
updated.niri.hideWhenTyping = 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: SettingsData.cursorSettings.niri?.hideAfterInactiveMs || 0
minimum: 0
maximum: 5000
unit: "ms"
defaultValue: 0
onSliderValueChanged: newValue => {
const updated = JSON.parse(JSON.stringify(SettingsData.cursorSettings));
if (!updated.niri)
updated.niri = {};
updated.niri.hideAfterInactiveMs = newValue;
SettingsData.set("cursorSettings", updated);
}
}
}
}
SettingsCard {
tab: "theme"
tags: ["system", "app", "theming", "gtk", "qt"]