1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00

Feature/split move size hyprland windowrules (#2824)

* windowrules: add split move/size fields for Lua table syntax

* windowrules: remove deprecated Move/Size fields, switch QML to split fields

* fix: use resolved dms binary path in Proc.runCommand
This commit is contained in:
Kilian Mio
2026-07-14 00:30:26 +02:00
committed by GitHub
parent bb0be2b215
commit 21eaaef056
17 changed files with 404 additions and 80 deletions
+1
View File
@@ -11,6 +11,7 @@ Singleton {
readonly property var log: Log.scoped("Proc")
readonly property int noTimeout: -1
readonly property string dmsBin: Quickshell.env("DMS_EXECUTABLE") || "dms"
property int defaultDebounceMs: 50
property int defaultTimeoutMs: 10000
property var _procDebouncers: ({})
+1 -1
View File
@@ -104,7 +104,7 @@ DankModal {
function pickColorFromScreen() {
hideInstant();
Proc.runCommand("dms-color-pick", ["dms", "color", "pick", "--json"], (output, exitCode) => {
Proc.runCommand("dms-color-pick", [Proc.dmsBin, "color", "pick", "--json"], (output, exitCode) => {
if (exitCode !== 0) {
log.warn("dms color pick exited with code:", exitCode);
root.show();
+90 -23
View File
@@ -94,8 +94,10 @@ FloatingWindow {
noRoundingToggle.checked = false;
pinToggle.checked = false;
opaqueToggle.checked = false;
sizeInput.text = "";
moveInput.text = "";
moveXInput.text = "";
moveYInput.text = "";
sizeWInput.text = "";
sizeHInput.text = "";
monitorInput.text = "";
hyprWorkspaceInput.text = "";
mangoTagsInput.text = "";
@@ -219,14 +221,16 @@ FloatingWindow {
noRoundingToggle.checked = actions.norounding || false;
pinToggle.checked = actions.pin || false;
opaqueToggle.checked = actions.opaque || false;
sizeInput.text = actions.size || "";
moveInput.text = actions.move || "";
moveXInput.text = actions.moveX || "";
moveYInput.text = actions.moveY || "";
sizeWInput.text = actions.sizeWidth || "";
sizeHInput.text = actions.sizeHeight || "";
monitorInput.text = actions.monitor || "";
hyprWorkspaceInput.text = actions.workspace || "";
mangoTagsInput.text = actions.workspace || "";
mangoMonitorInput.text = actions.monitor || "";
mangoSizeInput.text = actions.size || "";
mangoSizeInput.text = (actions.sizeWidth && actions.sizeHeight) ? actions.sizeWidth + "x" + actions.sizeHeight : "";
mangoNoBlurToggle.checked = actions.noblur || false;
mangoNoBorderToggle.checked = actions.noborder || false;
mangoNoShadowToggle.checked = actions.noshadow || false;
@@ -400,10 +404,14 @@ FloatingWindow {
actions.pin = true;
if (opaqueToggle.checked)
actions.opaque = true;
if (sizeInput.text.trim())
actions.size = sizeInput.text.trim();
if (moveInput.text.trim())
actions.move = moveInput.text.trim();
if (sizeWInput.text.trim())
actions.sizeWidth = sizeWInput.text.trim();
if (sizeHInput.text.trim())
actions.sizeHeight = sizeHInput.text.trim();
if (moveXInput.text.trim())
actions.moveX = moveXInput.text.trim();
if (moveYInput.text.trim())
actions.moveY = moveYInput.text.trim();
if (monitorInput.text.trim())
actions.monitor = monitorInput.text.trim();
if (hyprWorkspaceInput.text.trim())
@@ -415,8 +423,13 @@ FloatingWindow {
actions.workspace = mangoTagsInput.text.trim();
if (mangoMonitorInput.text.trim())
actions.monitor = mangoMonitorInput.text.trim();
if (mangoSizeInput.text.trim())
actions.size = mangoSizeInput.text.trim();
if (mangoSizeInput.text.trim()) {
const parts = mangoSizeInput.text.trim().split(/x/i);
if (parts.length === 2) {
actions.sizeWidth = parts[0].trim();
actions.sizeHeight = parts[1].trim();
}
}
if (mangoNoBlurToggle.checked)
actions.noblur = true;
if (mangoNoBorderToggle.checked)
@@ -447,7 +460,7 @@ FloatingWindow {
if (isEditMode) {
const ruleJson = JSON.stringify(ruleData);
Proc.runCommand("update-windowrule", ["dms", "config", "windowrules", "update", compositor, editingRule.id, ruleJson], (output, exitCode) => {
Proc.runCommand("update-windowrule", [Proc.dmsBin, "config", "windowrules", "update", compositor, editingRule.id, ruleJson], (output, exitCode) => {
root.submitting = false;
if (exitCode !== 0)
return;
@@ -460,7 +473,7 @@ FloatingWindow {
});
} else {
const ruleJson = JSON.stringify(ruleData);
Proc.runCommand("add-windowrule", ["dms", "config", "windowrules", "add", compositor, ruleJson], (output, exitCode) => {
Proc.runCommand("add-windowrule", [Proc.dmsBin, "config", "windowrules", "add", compositor, ruleJson], (output, exitCode) => {
root.submitting = false;
if (exitCode !== 0)
return;
@@ -1580,11 +1593,11 @@ FloatingWindow {
visible: isHyprland
Column {
width: (parent.width - Theme.spacingM) / 2
width: (parent.width - Theme.spacingM * 3) / 4
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Size")
text: I18n.tr("X")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
@@ -1593,13 +1606,13 @@ FloatingWindow {
InputField {
width: parent.width
hasFocus: sizeInput.activeFocus
hasFocus: moveXInput.activeFocus
DankTextField {
id: sizeInput
id: moveXInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeSmall
textColor: Theme.surfaceText
placeholderText: "800 600"
placeholderText: "0"
backgroundColor: "transparent"
enabled: root.visible
}
@@ -1607,11 +1620,11 @@ FloatingWindow {
}
Column {
width: (parent.width - Theme.spacingM) / 2
width: (parent.width - Theme.spacingM * 3) / 4
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Move")
text: I18n.tr("Y")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
@@ -1620,13 +1633,67 @@ FloatingWindow {
InputField {
width: parent.width
hasFocus: moveInput.activeFocus
hasFocus: moveYInput.activeFocus
DankTextField {
id: moveInput
id: moveYInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeSmall
textColor: Theme.surfaceText
placeholderText: "100 100"
placeholderText: "0"
backgroundColor: "transparent"
enabled: root.visible
}
}
}
Column {
width: (parent.width - Theme.spacingM * 3) / 4
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("W")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
horizontalAlignment: Text.AlignLeft
}
InputField {
width: parent.width
hasFocus: sizeWInput.activeFocus
DankTextField {
id: sizeWInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeSmall
textColor: Theme.surfaceText
placeholderText: "800"
backgroundColor: "transparent"
enabled: root.visible
}
}
}
Column {
width: (parent.width - Theme.spacingM * 3) / 4
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("H")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
horizontalAlignment: Text.AlignLeft
}
InputField {
width: parent.width
hasFocus: sizeHInput.activeFocus
DankTextField {
id: sizeHInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeSmall
textColor: Theme.surfaceText
placeholderText: "600"
backgroundColor: "transparent"
enabled: root.visible
}
@@ -68,7 +68,7 @@ Item {
const compositorArg = compositor === "mango" ? "mangowc" : compositor;
checkingInclude = true;
Proc.runCommand("check-layout-include", ["dms", "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
Proc.runCommand("check-layout-include", [Proc.dmsBin, "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
checkingInclude = false;
if (exitCode !== 0) {
layoutIncludeStatus = {
@@ -1443,7 +1443,7 @@ Singleton {
const compositorArg = (compositor === "mango") ? "mangowc" : compositor;
checkingInclude = true;
Proc.runCommand("check-outputs-include", ["dms", "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
Proc.runCommand("check-outputs-include", [Proc.dmsBin, "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
checkingInclude = false;
if (exitCode !== 0) {
includeStatus = {
@@ -117,7 +117,7 @@ Item {
const compositorArg = (compositor === "mango") ? "mangowc" : compositor;
checkingCursorInclude = true;
Proc.runCommand("check-cursor-include", ["dms", "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
Proc.runCommand("check-cursor-include", [Proc.dmsBin, "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
checkingCursorInclude = false;
if (exitCode !== 0) {
cursorIncludeStatus = {
@@ -238,7 +238,7 @@ Item {
return;
matugenPreviewRequestKey = requestKey;
Proc.runCommand("", ["dms", "matugen", "preview", "--source-color", sourceColor, "--contrast", contrast.toString()], (output, exitCode) => {
Proc.runCommand("", [Proc.dmsBin, "matugen", "preview", "--source-color", sourceColor, "--contrast", contrast.toString()], (output, exitCode) => {
if (requestKey !== themeColorsTab.matugenPreviewRequestKey)
return;
if (exitCode !== 0) {
@@ -262,7 +262,7 @@ Item {
DMSService.listInstalledThemes();
if (PopoutService.pendingThemeInstall)
Qt.callLater(() => showThemeBrowser());
Proc.runCommand("template-check", ["dms", "matugen", "check"], (output, exitCode) => {
Proc.runCommand("template-check", [Proc.dmsBin, "matugen", "check"], (output, exitCode) => {
if (exitCode !== 0)
return;
try {
@@ -122,8 +122,10 @@ Item {
"norounding": I18n.tr("No Round"),
"pin": I18n.tr("Pin"),
"opaque": I18n.tr("Opaque"),
"size": I18n.tr("Size"),
"move": I18n.tr("Move"),
"sizeWidth": I18n.tr("W"),
"sizeHeight": I18n.tr("H"),
"moveX": I18n.tr("X"),
"moveY": I18n.tr("Y"),
"monitor": I18n.tr("Monitor"),
"workspace": I18n.tr("Workspace"),
"drawBorderWithBackground": I18n.tr("Border w/ Bg"),
@@ -198,7 +200,7 @@ Item {
}
checkingInclude = true;
Proc.runCommand("load-windowrules", ["dms", "config", "windowrules", "list", compositor], (output, exitCode) => {
Proc.runCommand("load-windowrules", [Proc.dmsBin, "config", "windowrules", "list", compositor], (output, exitCode) => {
checkingInclude = false;
if (exitCode !== 0) {
windowRules = [];
@@ -234,7 +236,7 @@ Item {
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "mango")
return;
Proc.runCommand("remove-windowrule", ["dms", "config", "windowrules", "remove", compositor, ruleId], (output, exitCode) => {
Proc.runCommand("remove-windowrule", [Proc.dmsBin, "config", "windowrules", "remove", compositor, ruleId], (output, exitCode) => {
if (exitCode === 0) {
if (CompositorService.isMango)
MangoService.reloadConfig();
@@ -260,7 +262,7 @@ Item {
const [moved] = ids.splice(fromIndex, 1);
ids.splice(toIndex, 0, moved);
Proc.runCommand("reorder-windowrules", ["dms", "config", "windowrules", "reorder", compositor, JSON.stringify(ids)], (output, exitCode) => {
Proc.runCommand("reorder-windowrules", [Proc.dmsBin, "config", "windowrules", "reorder", compositor, JSON.stringify(ids)], (output, exitCode) => {
if (exitCode === 0) {
if (CompositorService.isMango)
MangoService.reloadConfig();
+1 -1
View File
@@ -46,7 +46,7 @@ Singleton {
signal toplevelsChanged
function fetchRandrData() {
Proc.runCommand("randr", ["dms", "randr", "--json"], (output, exitCode) => {
Proc.runCommand("randr", [Proc.dmsBin, "randr", "--json"], (output, exitCode) => {
if (exitCode === 0 && output) {
try {
const data = JSON.parse(output.trim());
+1 -1
View File
@@ -128,7 +128,7 @@ Singleton {
return;
luaConfigStatusLoading = true;
Proc.runCommand("hypr-lua-config-status", ["dms", "config", "resolve-include", "hyprland", "outputs.lua"], (output, exitCode) => {
Proc.runCommand("hypr-lua-config-status", [Proc.dmsBin, "config", "resolve-include", "hyprland", "outputs.lua"], (output, exitCode) => {
luaConfigStatusLoading = false;
luaConfigStatusReady = true;
if (exitCode !== 0) {
+3 -3
View File
@@ -58,7 +58,7 @@ Singleton {
}
function refreshCount() {
Proc.runCommand("trash-count", ["dms", "trash", "count"], (output, exitCode) => {
Proc.runCommand("trash-count", [Proc.dmsBin, "trash", "count"], (output, exitCode) => {
if (exitCode !== 0) {
root.count = homeTrashModel.count;
return;
@@ -74,7 +74,7 @@ Singleton {
callback(false, "empty path");
return;
}
Proc.runCommand(null, ["dms", "trash", "put", path], (output, exitCode) => {
Proc.runCommand(null, [Proc.dmsBin, "trash", "put", path], (output, exitCode) => {
const ok = exitCode === 0;
if (!ok)
ToastService.showError(I18n.tr("Failed to move to trash"), path);
@@ -121,7 +121,7 @@ Singleton {
}
function emptyTrash() {
Proc.runCommand("trash-empty", ["dms", "trash", "empty"], (output, exitCode) => {
Proc.runCommand("trash-empty", [Proc.dmsBin, "trash", "empty"], (output, exitCode) => {
if (exitCode !== 0)
ToastService.showError(I18n.tr("Failed to empty trash"), output || "");
refreshCount();
+1 -1
View File
@@ -59,7 +59,7 @@ Item {
root.currentSearchText = searchLocation;
const encodedLocation = encodeURIComponent(searchLocation);
const searchUrl = "https://nominatim.openstreetmap.org/search?q=" + encodedLocation + "&format=json&limit=5&addressdetails=1";
Proc.runCommand("locationSearch", ["dms", "dl", "-4", "--timeout", "10", searchUrl], (output, exitCode) => {
Proc.runCommand("locationSearch", [Proc.dmsBin, "dl", "-4", "--timeout", "10", searchUrl], (output, exitCode) => {
root.isLoading = false;
if (exitCode !== 0) {
searchResultsModel.clear();