mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-30 08:22:51 -05:00
feat: DMS Cursor Control - Size & Theme in niri
This commit is contained in:
@@ -246,6 +246,17 @@ Singleton {
|
|||||||
property bool qt6ctAvailable: false
|
property bool qt6ctAvailable: false
|
||||||
property bool gtkAvailable: false
|
property bool gtkAvailable: false
|
||||||
|
|
||||||
|
property var cursorSettings: ({
|
||||||
|
"theme": "System Default",
|
||||||
|
"size": 24,
|
||||||
|
"niri": {
|
||||||
|
"hideWhenTyping": false,
|
||||||
|
"hideAfterInactiveMs": 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
property var availableCursorThemes: ["System Default"]
|
||||||
|
property string systemDefaultCursorTheme: ""
|
||||||
|
|
||||||
property string launcherLogoMode: "apps"
|
property string launcherLogoMode: "apps"
|
||||||
property string launcherLogoCustomPath: ""
|
property string launcherLogoCustomPath: ""
|
||||||
property string launcherLogoColorOverride: ""
|
property string launcherLogoColorOverride: ""
|
||||||
@@ -852,6 +863,7 @@ Singleton {
|
|||||||
_hasLoaded = true;
|
_hasLoaded = true;
|
||||||
applyStoredTheme();
|
applyStoredTheme();
|
||||||
applyStoredIconTheme();
|
applyStoredIconTheme();
|
||||||
|
updateCompositorCursor();
|
||||||
Processes.detectQtTools();
|
Processes.detectQtTools();
|
||||||
|
|
||||||
_checkSettingsWritable();
|
_checkSettingsWritable();
|
||||||
@@ -982,6 +994,46 @@ Singleton {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function detectAvailableCursorThemes() {
|
||||||
|
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
||||||
|
const localData = Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation));
|
||||||
|
const homeDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation));
|
||||||
|
|
||||||
|
const dataDirs = xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat([localData]) : ["/usr/share", "/usr/local/share", localData];
|
||||||
|
|
||||||
|
const cursorPaths = dataDirs.map(d => d + "/icons").concat([homeDir + "/.icons", homeDir + "/.local/share/icons"]);
|
||||||
|
const pathsArg = cursorPaths.join(" ");
|
||||||
|
|
||||||
|
const script = `
|
||||||
|
echo "SYSDEFAULT:$(gsettings get org.gnome.desktop.interface cursor-theme 2>/dev/null | sed "s/'//g" || echo '')"
|
||||||
|
for dir in ${pathsArg}; do
|
||||||
|
[ -d "$dir" ] || continue
|
||||||
|
for theme in "$dir"/*/; do
|
||||||
|
[ -d "$theme" ] || continue
|
||||||
|
[ -d "$theme/cursors" ] || continue
|
||||||
|
basename "$theme"
|
||||||
|
done
|
||||||
|
done | grep -v '^icons$' | grep -v '^default$' | sort -u
|
||||||
|
`;
|
||||||
|
|
||||||
|
Proc.runCommand("detectCursorThemes", ["sh", "-c", script], (output, exitCode) => {
|
||||||
|
const themes = ["System Default"];
|
||||||
|
if (output && output.trim()) {
|
||||||
|
const lines = output.trim().split('\n');
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i].trim();
|
||||||
|
if (line.startsWith("SYSDEFAULT:")) {
|
||||||
|
systemDefaultCursorTheme = line.substring(11).trim();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line)
|
||||||
|
themes.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
availableCursorThemes = themes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getEffectiveTimeFormat() {
|
function getEffectiveTimeFormat() {
|
||||||
if (use24HourClock) {
|
if (use24HourClock) {
|
||||||
return showSeconds ? "hh:mm:ss" : "hh:mm";
|
return showSeconds ? "hh:mm:ss" : "hh:mm";
|
||||||
@@ -1510,6 +1562,38 @@ Singleton {
|
|||||||
Theme.generateSystemThemesFromCurrentTheme();
|
Theme.generateSystemThemesFromCurrentTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCursorTheme(themeName) {
|
||||||
|
const updated = JSON.parse(JSON.stringify(cursorSettings));
|
||||||
|
updated.theme = themeName;
|
||||||
|
cursorSettings = updated;
|
||||||
|
saveSettings();
|
||||||
|
updateCompositorCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCursorSize(size) {
|
||||||
|
const updated = JSON.parse(JSON.stringify(cursorSettings));
|
||||||
|
updated.size = size;
|
||||||
|
cursorSettings = updated;
|
||||||
|
saveSettings();
|
||||||
|
updateCompositorCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCompositorCursor() {
|
||||||
|
if (typeof CompositorService === "undefined")
|
||||||
|
return;
|
||||||
|
if (CompositorService.isNiri && typeof NiriService !== "undefined")
|
||||||
|
NiriService.generateNiriCursorConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCursorEnvPrefix() {
|
||||||
|
const themeName = cursorSettings.theme === "System Default" ? (systemDefaultCursorTheme || "") : cursorSettings.theme;
|
||||||
|
const size = cursorSettings.size || 24;
|
||||||
|
|
||||||
|
if (!themeName)
|
||||||
|
return `env XCURSOR_SIZE=${size}`;
|
||||||
|
return `env XCURSOR_THEME="${themeName}" XCURSOR_SIZE=${size}`;
|
||||||
|
}
|
||||||
|
|
||||||
function setGtkThemingEnabled(enabled) {
|
function setGtkThemingEnabled(enabled) {
|
||||||
set("gtkThemingEnabled", enabled);
|
set("gtkThemingEnabled", enabled);
|
||||||
if (enabled && typeof Theme !== "undefined") {
|
if (enabled && typeof Theme !== "undefined") {
|
||||||
@@ -1914,6 +1998,7 @@ Singleton {
|
|||||||
_hasLoaded = true;
|
_hasLoaded = true;
|
||||||
applyStoredTheme();
|
applyStoredTheme();
|
||||||
applyStoredIconTheme();
|
applyStoredIconTheme();
|
||||||
|
updateCompositorCursor();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_parseError = true;
|
_parseError = true;
|
||||||
const msg = e.message;
|
const msg = e.message;
|
||||||
|
|||||||
@@ -134,6 +134,10 @@ var SPEC = {
|
|||||||
qt6ctAvailable: { def: false, persist: false },
|
qt6ctAvailable: { def: false, persist: false },
|
||||||
gtkAvailable: { def: false, persist: false },
|
gtkAvailable: { def: false, persist: false },
|
||||||
|
|
||||||
|
cursorSettings: { def: { theme: "System Default", size: 24, niri: { hideWhenTyping: false, hideAfterInactiveMs: 0 } }, onChange: "updateCompositorCursor" },
|
||||||
|
availableCursorThemes: { def: ["System Default"], persist: false },
|
||||||
|
systemDefaultCursorTheme: { def: "", persist: false },
|
||||||
|
|
||||||
launcherLogoMode: { def: "apps" },
|
launcherLogoMode: { def: "apps" },
|
||||||
launcherLogoCustomPath: { def: "" },
|
launcherLogoCustomPath: { def: "" },
|
||||||
launcherLogoColorOverride: { def: "" },
|
launcherLogoColorOverride: { def: "" },
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Item {
|
|||||||
id: themeColorsTab
|
id: themeColorsTab
|
||||||
|
|
||||||
property var cachedIconThemes: SettingsData.availableIconThemes
|
property var cachedIconThemes: SettingsData.availableIconThemes
|
||||||
|
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
||||||
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
||||||
property var installedRegistryThemes: []
|
property var installedRegistryThemes: []
|
||||||
property var templateDetection: ({})
|
property var templateDetection: ({})
|
||||||
@@ -38,6 +39,7 @@ Item {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
SettingsData.detectAvailableIconThemes();
|
SettingsData.detectAvailableIconThemes();
|
||||||
|
SettingsData.detectAvailableCursorThemes();
|
||||||
if (DMSService.dmsAvailable)
|
if (DMSService.dmsAvailable)
|
||||||
DMSService.listInstalledThemes();
|
DMSService.listInstalledThemes();
|
||||||
if (PopoutService.pendingThemeInstall)
|
if (PopoutService.pendingThemeInstall)
|
||||||
@@ -1638,6 +1640,86 @@ 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 {
|
SettingsCard {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
tags: ["system", "app", "theming", "gtk", "qt"]
|
tags: ["system", "app", "theming", "gtk", "qt"]
|
||||||
|
|||||||
@@ -145,6 +145,20 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: writeCursorProcess
|
||||||
|
property string cursorContent: ""
|
||||||
|
property string cursorPath: ""
|
||||||
|
|
||||||
|
onExited: exitCode => {
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.info("NiriService: Generated cursor config at", cursorPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.warn("NiriService: Failed to write cursor config, exit code:", exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: ensureOutputsProcess
|
id: ensureOutputsProcess
|
||||||
property string outputsPath: ""
|
property string outputsPath: ""
|
||||||
@@ -1090,6 +1104,59 @@ Singleton {
|
|||||||
writeBlurruleProcess.running = true;
|
writeBlurruleProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateNiriCursorConfig() {
|
||||||
|
if (!CompositorService.isNiri)
|
||||||
|
return;
|
||||||
|
|
||||||
|
console.log("NiriService: Generating cursor config...");
|
||||||
|
|
||||||
|
const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : {
|
||||||
|
theme: "System Default",
|
||||||
|
size: 24,
|
||||||
|
niri: {}
|
||||||
|
};
|
||||||
|
const themeName = settings.theme === "System Default" ? (SettingsData.systemDefaultCursorTheme || "") : settings.theme;
|
||||||
|
const size = settings.size || 24;
|
||||||
|
const hideWhenTyping = settings.niri?.hideWhenTyping || false;
|
||||||
|
const hideAfterMs = settings.niri?.hideAfterInactiveMs || 0;
|
||||||
|
|
||||||
|
const dmsWarning = `// ! DO NOT EDIT !
|
||||||
|
// ! AUTO-GENERATED BY DMS !
|
||||||
|
// ! CHANGES WILL BE OVERWRITTEN !
|
||||||
|
// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE !
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
let cursorContent = dmsWarning + `cursor {\n`;
|
||||||
|
|
||||||
|
if (themeName) {
|
||||||
|
cursorContent += ` xcursor-theme "${themeName}"\n`;
|
||||||
|
}
|
||||||
|
cursorContent += ` xcursor-size ${size}\n`;
|
||||||
|
|
||||||
|
if (hideWhenTyping) {
|
||||||
|
cursorContent += ` hide-when-typing\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideAfterMs > 0) {
|
||||||
|
cursorContent += ` hide-after-inactive-ms ${hideAfterMs}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorContent += `}`;
|
||||||
|
|
||||||
|
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
||||||
|
const niriDmsDir = configDir + "/niri/dms";
|
||||||
|
const cursorPath = niriDmsDir + "/cursor.kdl";
|
||||||
|
|
||||||
|
writeCursorProcess.cursorContent = cursorContent;
|
||||||
|
writeCursorProcess.cursorPath = cursorPath;
|
||||||
|
|
||||||
|
const escapedCursorContent = cursorContent.replace(/'/g, "'\\''");
|
||||||
|
|
||||||
|
writeCursorProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && printf '%s' '${escapedCursorContent}' > "${cursorPath}"`];
|
||||||
|
writeCursorProcess.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
function updateOutputPosition(outputName, x, y) {
|
function updateOutputPosition(outputName, x, y) {
|
||||||
if (!outputs || !outputs[outputName])
|
if (!outputs || !outputs[outputName])
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -190,7 +190,13 @@ Singleton {
|
|||||||
|
|
||||||
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
const cursorPrefix = typeof SettingsData.getCursorEnvPrefix !== "undefined" ? SettingsData.getCursorEnvPrefix() : "";
|
||||||
|
|
||||||
|
let prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||||
|
if (cursorPrefix) {
|
||||||
|
prefix = prefix.length > 0 ? `${cursorPrefix} ${prefix}` : cursorPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
const workDir = desktopEntry.workingDirectory || Quickshell.env("HOME");
|
const workDir = desktopEntry.workingDirectory || Quickshell.env("HOME");
|
||||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||||
const shellCmd = prefix.length > 0 ? `${prefix} ${escapedCmd}` : escapedCmd;
|
const shellCmd = prefix.length > 0 ? `${prefix} ${escapedCmd}` : escapedCmd;
|
||||||
@@ -230,7 +236,12 @@ Singleton {
|
|||||||
|
|
||||||
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
const userPrefix = SettingsData.launchPrefix?.trim() || "";
|
||||||
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
|
||||||
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
const cursorPrefix = typeof SettingsData.getCursorEnvPrefix !== "undefined" ? SettingsData.getCursorEnvPrefix() : "";
|
||||||
|
|
||||||
|
let prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
|
||||||
|
if (cursorPrefix) {
|
||||||
|
prefix = prefix.length > 0 ? `${cursorPrefix} ${prefix}` : cursorPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
if (prefix.length > 0 && needsShellExecution(prefix)) {
|
||||||
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
|
||||||
|
|||||||
Reference in New Issue
Block a user