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

niri/keybinds: expose when-locked, inhibitied, repeat through GUI editor

fixes #1437
This commit is contained in:
bbedward
2026-02-05 11:55:25 -05:00
parent a103b93583
commit 2583dbd3f2
7 changed files with 513 additions and 313 deletions

View File

@@ -63,6 +63,7 @@ func init() {
keybindsSetCmd.Flags().Bool("allow-when-locked", false, "Allow when screen is locked") keybindsSetCmd.Flags().Bool("allow-when-locked", false, "Allow when screen is locked")
keybindsSetCmd.Flags().Int("cooldown-ms", 0, "Cooldown in milliseconds") keybindsSetCmd.Flags().Int("cooldown-ms", 0, "Cooldown in milliseconds")
keybindsSetCmd.Flags().Bool("no-repeat", false, "Disable key repeat") keybindsSetCmd.Flags().Bool("no-repeat", false, "Disable key repeat")
keybindsSetCmd.Flags().Bool("no-inhibiting", false, "Keep bind active when shortcuts are inhibited (allow-inhibiting=false)")
keybindsSetCmd.Flags().String("replace-key", "", "Original key to replace (removes old key)") keybindsSetCmd.Flags().String("replace-key", "", "Original key to replace (removes old key)")
keybindsSetCmd.Flags().String("flags", "", "Hyprland bind flags (e.g., 'e' for repeat, 'l' for locked, 'r' for release)") keybindsSetCmd.Flags().String("flags", "", "Hyprland bind flags (e.g., 'e' for repeat, 'l' for locked, 'r' for release)")
@@ -212,6 +213,9 @@ func runKeybindsSet(cmd *cobra.Command, args []string) {
if v, _ := cmd.Flags().GetBool("no-repeat"); v { if v, _ := cmd.Flags().GetBool("no-repeat"); v {
options["repeat"] = false options["repeat"] = false
} }
if v, _ := cmd.Flags().GetBool("no-inhibiting"); v {
options["allow-inhibiting"] = false
}
if v, _ := cmd.Flags().GetString("flags"); v != "" { if v, _ := cmd.Flags().GetString("flags"); v != "" {
options["flags"] = v options["flags"] = v
} }

View File

@@ -118,6 +118,9 @@ func (n *NiriProvider) categorizeByAction(action string) string {
return "Overview" return "Overview"
case action == "quit" || case action == "quit" ||
action == "power-off-monitors" || action == "power-off-monitors" ||
action == "power-on-monitors" ||
action == "suspend" ||
action == "do-screen-transition" ||
action == "toggle-keyboard-shortcuts-inhibit" || action == "toggle-keyboard-shortcuts-inhibit" ||
strings.Contains(action, "dpms"): strings.Contains(action, "dpms"):
return "System" return "System"
@@ -151,13 +154,16 @@ func (n *NiriProvider) convertKeybind(kb *NiriKeyBinding, subcategory string, co
} }
bind := keybinds.Keybind{ bind := keybinds.Keybind{
Key: keyStr, Key: keyStr,
Description: kb.Description, Description: kb.Description,
Action: rawAction, Action: rawAction,
Subcategory: subcategory, Subcategory: subcategory,
Source: source, Source: source,
HideOnOverlay: kb.HideOnOverlay, HideOnOverlay: kb.HideOnOverlay,
CooldownMs: kb.CooldownMs, CooldownMs: kb.CooldownMs,
AllowWhenLocked: kb.AllowWhenLocked,
AllowInhibiting: kb.AllowInhibiting,
Repeat: kb.Repeat,
} }
if source == "dms" && conflicts != nil { if source == "dms" && conflicts != nil {
@@ -341,14 +347,10 @@ func (n *NiriProvider) buildActionFromNode(bindNode *document.Node) string {
} }
if actionNode.Properties != nil { if actionNode.Properties != nil {
if val, ok := actionNode.Properties.Get("focus"); ok { for _, propName := range []string{"focus", "show-pointer", "write-to-disk", "skip-confirmation", "delay-ms"} {
parts = append(parts, "focus="+val.String()) if val, ok := actionNode.Properties.Get(propName); ok {
} parts = append(parts, propName+"="+val.String())
if val, ok := actionNode.Properties.Get("show-pointer"); ok { }
parts = append(parts, "show-pointer="+val.String())
}
if val, ok := actionNode.Properties.Get("write-to-disk"); ok {
parts = append(parts, "write-to-disk="+val.String())
} }
} }
@@ -372,6 +374,9 @@ func (n *NiriProvider) extractOptions(node *document.Node) map[string]any {
if val, ok := node.Properties.Get("allow-when-locked"); ok { if val, ok := node.Properties.Get("allow-when-locked"); ok {
opts["allow-when-locked"] = val.String() == "true" opts["allow-when-locked"] = val.String() == "true"
} }
if val, ok := node.Properties.Get("allow-inhibiting"); ok {
opts["allow-inhibiting"] = val.String() == "true"
}
return opts return opts
} }
@@ -405,6 +410,9 @@ func (n *NiriProvider) buildBindNode(bind *overrideBind) *document.Node {
if v, ok := bind.Options["allow-when-locked"]; ok && v == true { if v, ok := bind.Options["allow-when-locked"]; ok && v == true {
node.AddProperty("allow-when-locked", true, "") node.AddProperty("allow-when-locked", true, "")
} }
if v, ok := bind.Options["allow-inhibiting"]; ok && v == false {
node.AddProperty("allow-inhibiting", false, "")
}
} }
if bind.Description != "" { if bind.Description != "" {

View File

@@ -12,14 +12,17 @@ import (
) )
type NiriKeyBinding struct { type NiriKeyBinding struct {
Mods []string Mods []string
Key string Key string
Action string Action string
Args []string Args []string
Description string Description string
HideOnOverlay bool HideOnOverlay bool
CooldownMs int CooldownMs int
Source string AllowWhenLocked bool
AllowInhibiting *bool
Repeat *bool
Source string
} }
type NiriSection struct { type NiriSection struct {
@@ -269,8 +272,10 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
args = append(args, arg.ValueString()) args = append(args, arg.ValueString())
} }
if actionNode.Properties != nil { if actionNode.Properties != nil {
if val, ok := actionNode.Properties.Get("focus"); ok { for _, propName := range []string{"focus", "show-pointer", "write-to-disk", "skip-confirmation", "delay-ms"} {
args = append(args, "focus="+val.String()) if val, ok := actionNode.Properties.Get(propName); ok {
args = append(args, propName+"="+val.String())
}
} }
} }
} }
@@ -278,6 +283,9 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
var description string var description string
var hideOnOverlay bool var hideOnOverlay bool
var cooldownMs int var cooldownMs int
var allowWhenLocked bool
var allowInhibiting *bool
var repeat *bool
if node.Properties != nil { if node.Properties != nil {
if val, ok := node.Properties.Get("hotkey-overlay-title"); ok { if val, ok := node.Properties.Get("hotkey-overlay-title"); ok {
switch val.ValueString() { switch val.ValueString() {
@@ -290,17 +298,31 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
if val, ok := node.Properties.Get("cooldown-ms"); ok { if val, ok := node.Properties.Get("cooldown-ms"); ok {
cooldownMs, _ = strconv.Atoi(val.String()) cooldownMs, _ = strconv.Atoi(val.String())
} }
if val, ok := node.Properties.Get("allow-when-locked"); ok {
allowWhenLocked = val.String() == "true"
}
if val, ok := node.Properties.Get("allow-inhibiting"); ok {
v := val.String() == "true"
allowInhibiting = &v
}
if val, ok := node.Properties.Get("repeat"); ok {
v := val.String() == "true"
repeat = &v
}
} }
return &NiriKeyBinding{ return &NiriKeyBinding{
Mods: mods, Mods: mods,
Key: key, Key: key,
Action: action, Action: action,
Args: args, Args: args,
Description: description, Description: description,
HideOnOverlay: hideOnOverlay, HideOnOverlay: hideOnOverlay,
CooldownMs: cooldownMs, CooldownMs: cooldownMs,
Source: p.currentSource, AllowWhenLocked: allowWhenLocked,
AllowInhibiting: allowInhibiting,
Repeat: repeat,
Source: p.currentSource,
} }
} }

View File

@@ -1,15 +1,18 @@
package keybinds package keybinds
type Keybind struct { type Keybind struct {
Key string `json:"key"` Key string `json:"key"`
Description string `json:"desc"` Description string `json:"desc"`
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
Subcategory string `json:"subcat,omitempty"` Subcategory string `json:"subcat,omitempty"`
Source string `json:"source,omitempty"` Source string `json:"source,omitempty"`
HideOnOverlay bool `json:"hideOnOverlay,omitempty"` HideOnOverlay bool `json:"hideOnOverlay,omitempty"`
CooldownMs int `json:"cooldownMs,omitempty"` CooldownMs int `json:"cooldownMs,omitempty"`
Flags string `json:"flags,omitempty"` // Hyprland bind flags: e=repeat, l=locked, r=release, o=long-press Flags string `json:"flags,omitempty"` // Hyprland bind flags: e=repeat, l=locked, r=release, o=long-press
Conflict *Keybind `json:"conflict,omitempty"` AllowWhenLocked bool `json:"allowWhenLocked,omitempty"`
AllowInhibiting *bool `json:"allowInhibiting,omitempty"` // nil=default(true), false=explicitly disabled
Repeat *bool `json:"repeat,omitempty"` // nil=default(true), false=explicitly disabled
Conflict *Keybind `json:"conflict,omitempty"`
} }
type DMSBindsStatus struct { type DMSBindsStatus struct {

View File

@@ -121,7 +121,8 @@ const NIRI_ACTIONS = {
{ id: "expand-column-to-available-width", label: "Expand to Available Width" }, { id: "expand-column-to-available-width", label: "Expand to Available Width" },
{ id: "consume-or-expel-window-left", label: "Consume/Expel Left" }, { id: "consume-or-expel-window-left", label: "Consume/Expel Left" },
{ id: "consume-or-expel-window-right", label: "Consume/Expel Right" }, { id: "consume-or-expel-window-right", label: "Consume/Expel Right" },
{ id: "toggle-column-tabbed-display", label: "Toggle Tabbed" } { id: "toggle-column-tabbed-display", label: "Toggle Tabbed" },
{ id: "toggle-window-rule-opacity", label: "Toggle Window Opacity" }
], ],
"Focus": [ "Focus": [
{ id: "focus-column-left", label: "Focus Left" }, { id: "focus-column-left", label: "Focus Left" },
@@ -168,6 +169,7 @@ const NIRI_ACTIONS = {
"System": [ "System": [
{ id: "toggle-overview", label: "Toggle Overview" }, { id: "toggle-overview", label: "Toggle Overview" },
{ id: "show-hotkey-overlay", label: "Show Hotkey Overlay" }, { id: "show-hotkey-overlay", label: "Show Hotkey Overlay" },
{ id: "do-screen-transition", label: "Screen Transition" },
{ id: "power-off-monitors", label: "Power Off Monitors" }, { id: "power-off-monitors", label: "Power Off Monitors" },
{ id: "power-on-monitors", label: "Power On Monitors" }, { id: "power-on-monitors", label: "Power On Monitors" },
{ id: "toggle-keyboard-shortcuts-inhibit", label: "Toggle Shortcuts Inhibit" }, { id: "toggle-keyboard-shortcuts-inhibit", label: "Toggle Shortcuts Inhibit" },
@@ -420,6 +422,12 @@ const COMPOSITOR_ACTIONS = {
const CATEGORY_ORDER = ["DMS", "Execute", "Workspace", "Tags", "Window", "Move/Resize", "Focus", "Move", "Layout", "Groups", "Monitor", "Scratchpad", "Screenshot", "System", "Pass-through", "Overview", "Alt-Tab", "Other"]; const CATEGORY_ORDER = ["DMS", "Execute", "Workspace", "Tags", "Window", "Move/Resize", "Focus", "Move", "Layout", "Groups", "Monitor", "Scratchpad", "Screenshot", "System", "Pass-through", "Overview", "Alt-Tab", "Other"];
const NIRI_ACTION_ARGS = { const NIRI_ACTION_ARGS = {
"quit": {
args: [{ name: "skip-confirmation", type: "bool", label: "Skip confirmation" }]
},
"do-screen-transition": {
args: [{ name: "delay-ms", type: "number", label: "Delay (ms)", placeholder: "250" }]
},
"set-column-width": { "set-column-width": {
args: [{ name: "value", type: "text", label: "Width", placeholder: "+10%, -10%, 50%" }] args: [{ name: "value", type: "text", label: "Width", placeholder: "+10%, -10%, 50%" }]
}, },
@@ -756,14 +764,14 @@ function getDmsActions(isNiri, isHyprland) {
continue; continue;
} }
switch (action.compositor) { switch (action.compositor) {
case "niri": case "niri":
if (isNiri) if (isNiri)
result.push(action); result.push(action);
break; break;
case "hyprland": case "hyprland":
if (isHyprland) if (isHyprland)
result.push(action); result.push(action);
break; break;
} }
} }
return result; return result;
@@ -856,13 +864,13 @@ function isValidAction(action) {
if (!action) if (!action)
return false; return false;
switch (action) { switch (action) {
case "spawn": case "spawn":
case "spawn ": case "spawn ":
case "spawn sh -c \"\"": case "spawn sh -c \"\"":
case "spawn sh -c ''": case "spawn sh -c ''":
case "spawn_shell": case "spawn_shell":
case "spawn_shell ": case "spawn_shell ":
return false; return false;
} }
return true; return true;
} }
@@ -882,7 +890,7 @@ function buildSpawnAction(command, args) {
return ""; return "";
let parts = [command]; let parts = [command];
if (args && args.length > 0) if (args && args.length > 0)
parts = parts.concat(args.filter(function(a) { return a; })); parts = parts.concat(args.filter(function (a) { return a; }));
return "spawn " + parts.join(" "); return "spawn " + parts.join(" ");
} }
@@ -899,7 +907,7 @@ function parseSpawnCommand(action) {
if (!action || !action.startsWith("spawn ")) if (!action || !action.startsWith("spawn "))
return { command: "", args: [] }; return { command: "", args: [] };
const rest = action.slice(6); const rest = action.slice(6);
const parts = rest.split(" ").filter(function(p) { return p; }); const parts = rest.split(" ").filter(function (p) { return p; });
return { return {
command: parts[0] || "", command: parts[0] || "",
args: parts.slice(1) args: parts.slice(1)
@@ -961,130 +969,138 @@ function parseCompositorActionArgs(compositor, action) {
var argParts = parts.slice(1); var argParts = parts.slice(1);
switch (compositor) { switch (compositor) {
case "niri": case "niri":
switch (base) { switch (base) {
case "move-column-to-workspace": case "move-column-to-workspace":
for (var i = 0; i < argParts.length; i++) { for (var i = 0; i < argParts.length; i++) {
if (argParts[i] === "focus=true" || argParts[i] === "focus=false") { if (argParts[i] === "focus=true" || argParts[i] === "focus=false") {
args.focus = argParts[i] === "focus=true"; args.focus = argParts[i] === "focus=true";
} else if (!args.index) { } else if (!args.index) {
args.index = argParts[i]; args.index = argParts[i];
}
}
break;
case "move-column-to-workspace-down":
case "move-column-to-workspace-up":
for (var k = 0; k < argParts.length; k++) {
if (argParts[k] === "focus=true" || argParts[k] === "focus=false")
args.focus = argParts[k] === "focus=true";
}
break;
default:
for (var j = 0; j < argParts.length; j++) {
var kv = argParts[j].split("=");
if (kv.length === 2) {
switch (kv[1]) {
case "true":
args[kv[0]] = true;
break;
case "false":
args[kv[0]] = false;
break;
default:
args[kv[0]] = kv[1];
}
} else {
args.value = args.value ? (args.value + " " + argParts[j]) : argParts[j];
}
}
}
break;
case "mangowc":
if (argConfig.args && argConfig.args.length > 0 && argParts.length > 0) {
var paramStr = argParts.join(" ");
var paramValues = paramStr.split(",");
for (var m = 0; m < argConfig.args.length && m < paramValues.length; m++) {
args[argConfig.args[m].name] = paramValues[m];
} }
} }
break; break;
case "move-column-to-workspace-down": case "hyprland":
case "move-column-to-workspace-up": if (argConfig.args && argConfig.args.length > 0) {
for (var k = 0; k < argParts.length; k++) { switch (base) {
if (argParts[k] === "focus=true" || argParts[k] === "focus=false") case "resizewindowpixel":
args.focus = argParts[k] === "focus=true"; case "movewindowpixel":
var commaIdx = argParts.join(" ").indexOf(",");
if (commaIdx !== -1) {
var fullStr = argParts.join(" ");
args[argConfig.args[0].name] = fullStr.substring(0, commaIdx);
args[argConfig.args[1].name] = fullStr.substring(commaIdx + 1);
} else if (argParts.length > 0) {
args[argConfig.args[0].name] = argParts.join(" ");
}
break;
case "movetoworkspace":
case "movetoworkspacesilent":
case "tagwindow":
case "alterzorder":
if (argParts.length >= 2) {
args[argConfig.args[0].name] = argParts[0];
args[argConfig.args[1].name] = argParts.slice(1).join(" ");
} else if (argParts.length === 1) {
args[argConfig.args[0].name] = argParts[0];
}
break;
case "moveworkspacetomonitor":
case "swapactiveworkspaces":
case "renameworkspace":
case "fullscreenstate":
case "movecursor":
if (argParts.length >= 2) {
args[argConfig.args[0].name] = argParts[0];
args[argConfig.args[1].name] = argParts[1];
} else if (argParts.length === 1) {
args[argConfig.args[0].name] = argParts[0];
}
break;
case "setprop":
if (argParts.length >= 3) {
args.window = argParts[0];
args.property = argParts[1];
args.value = argParts.slice(2).join(" ");
} else if (argParts.length === 2) {
args.window = argParts[0];
args.property = argParts[1];
}
break;
case "sendshortcut":
if (argParts.length >= 3) {
args.mod = argParts[0];
args.key = argParts[1];
args.window = argParts.slice(2).join(" ");
} else if (argParts.length >= 2) {
args.mod = argParts[0];
args.key = argParts[1];
}
break;
case "sendkeystate":
if (argParts.length >= 4) {
args.mod = argParts[0];
args.key = argParts[1];
args.state = argParts[2];
args.window = argParts.slice(3).join(" ");
}
break;
case "signalwindow":
if (argParts.length >= 2) {
args.window = argParts[0];
args.signal = argParts[1];
}
break;
default:
if (argParts.length > 0) {
if (argConfig.args.length === 1) {
args[argConfig.args[0].name] = argParts.join(" ");
} else {
args.value = argParts.join(" ");
}
}
}
} }
break; break;
default: default:
if (base.startsWith("screenshot")) { if (argParts.length > 0)
for (var j = 0; j < argParts.length; j++) {
var kv = argParts[j].split("=");
if (kv.length === 2)
args[kv[0]] = kv[1] === "true";
}
} else if (argParts.length > 0) {
args.value = argParts.join(" "); args.value = argParts.join(" ");
}
}
break;
case "mangowc":
if (argConfig.args && argConfig.args.length > 0 && argParts.length > 0) {
var paramStr = argParts.join(" ");
var paramValues = paramStr.split(",");
for (var m = 0; m < argConfig.args.length && m < paramValues.length; m++) {
args[argConfig.args[m].name] = paramValues[m];
}
}
break;
case "hyprland":
if (argConfig.args && argConfig.args.length > 0) {
switch (base) {
case "resizewindowpixel":
case "movewindowpixel":
var commaIdx = argParts.join(" ").indexOf(",");
if (commaIdx !== -1) {
var fullStr = argParts.join(" ");
args[argConfig.args[0].name] = fullStr.substring(0, commaIdx);
args[argConfig.args[1].name] = fullStr.substring(commaIdx + 1);
} else if (argParts.length > 0) {
args[argConfig.args[0].name] = argParts.join(" ");
}
break;
case "movetoworkspace":
case "movetoworkspacesilent":
case "tagwindow":
case "alterzorder":
if (argParts.length >= 2) {
args[argConfig.args[0].name] = argParts[0];
args[argConfig.args[1].name] = argParts.slice(1).join(" ");
} else if (argParts.length === 1) {
args[argConfig.args[0].name] = argParts[0];
}
break;
case "moveworkspacetomonitor":
case "swapactiveworkspaces":
case "renameworkspace":
case "fullscreenstate":
case "movecursor":
if (argParts.length >= 2) {
args[argConfig.args[0].name] = argParts[0];
args[argConfig.args[1].name] = argParts[1];
} else if (argParts.length === 1) {
args[argConfig.args[0].name] = argParts[0];
}
break;
case "setprop":
if (argParts.length >= 3) {
args.window = argParts[0];
args.property = argParts[1];
args.value = argParts.slice(2).join(" ");
} else if (argParts.length === 2) {
args.window = argParts[0];
args.property = argParts[1];
}
break;
case "sendshortcut":
if (argParts.length >= 3) {
args.mod = argParts[0];
args.key = argParts[1];
args.window = argParts.slice(2).join(" ");
} else if (argParts.length >= 2) {
args.mod = argParts[0];
args.key = argParts[1];
}
break;
case "sendkeystate":
if (argParts.length >= 4) {
args.mod = argParts[0];
args.key = argParts[1];
args.state = argParts[2];
args.window = argParts.slice(3).join(" ");
}
break;
case "signalwindow":
if (argParts.length >= 2) {
args.window = argParts[0];
args.signal = argParts[1];
}
break;
default:
if (argParts.length > 0) {
if (argConfig.args.length === 1) {
args[argConfig.args[0].name] = argParts.join(" ");
} else {
args.value = argParts.join(" ");
}
}
}
}
break;
default:
if (argParts.length > 0)
args.value = argParts.join(" ");
} }
return { base: base, args: args }; return { base: base, args: args };
@@ -1100,125 +1116,118 @@ function buildCompositorAction(compositor, base, args) {
return base; return base;
switch (compositor) { switch (compositor) {
case "niri": case "niri":
switch (base) { switch (base) {
case "move-column-to-workspace": case "move-column-to-workspace":
if (args.index) if (args.index)
parts.push(args.index); parts.push(args.index);
if (args.focus === false) if (args.focus === false)
parts.push("focus=false"); parts.push("focus=false");
break;
case "move-column-to-workspace-down":
case "move-column-to-workspace-up":
if (args.focus === false)
parts.push("focus=false");
break;
default:
if (args.value)
parts.push(args.value);
else if (args.index)
parts.push(args.index);
for (var prop in args) {
switch (prop) {
case "value":
case "index":
continue;
}
var val = args[prop];
if (val === true)
parts.push(prop + "=true");
else if (val === false)
parts.push(prop + "=false");
else if (val !== undefined && val !== null && val !== "")
parts.push(prop + "=" + val);
}
}
break; break;
case "move-column-to-workspace-down": case "mangowc":
case "move-column-to-workspace-up": var compositorArgs = ACTION_ARGS.mangowc;
if (args.focus === false) if (compositorArgs && compositorArgs[base] && compositorArgs[base].args) {
parts.push("focus=false"); var argConfig = compositorArgs[base].args;
var argValues = [];
for (var i = 0; i < argConfig.length; i++) {
var argDef = argConfig[i];
var val = args[argDef.name];
if (val === undefined || val === "")
val = argDef.default || "";
if (val === "" && argValues.length === 0)
continue;
argValues.push(val);
}
if (argValues.length > 0)
parts.push(argValues.join(","));
} else if (args.value) {
parts.push(args.value);
}
break;
case "hyprland":
var hyprArgs = ACTION_ARGS.hyprland;
if (hyprArgs && hyprArgs[base] && hyprArgs[base].args) {
var hyprConfig = hyprArgs[base].args;
switch (base) {
case "resizewindowpixel":
case "movewindowpixel":
if (args[hyprConfig[0].name])
parts.push(args[hyprConfig[0].name]);
if (args[hyprConfig[1].name])
parts[parts.length - 1] += "," + args[hyprConfig[1].name];
break;
case "setprop":
if (args.window)
parts.push(args.window);
if (args.property)
parts.push(args.property);
if (args.value)
parts.push(args.value);
break;
case "sendshortcut":
if (args.mod)
parts.push(args.mod);
if (args.key)
parts.push(args.key);
if (args.window)
parts.push(args.window);
break;
case "sendkeystate":
if (args.mod)
parts.push(args.mod);
if (args.key)
parts.push(args.key);
if (args.state)
parts.push(args.state);
if (args.window)
parts.push(args.window);
break;
case "signalwindow":
if (args.window)
parts.push(args.window);
if (args.signal)
parts.push(args.signal);
break;
default:
for (var j = 0; j < hyprConfig.length; j++) {
var hVal = args[hyprConfig[j].name];
if (hVal !== undefined && hVal !== "")
parts.push(hVal);
}
}
} else if (args.value) {
parts.push(args.value);
}
break; break;
default: default:
switch (base) { if (args.value)
case "screenshot":
if (args["show-pointer"] === true)
parts.push("show-pointer=true");
else if (args["show-pointer"] === false)
parts.push("show-pointer=false");
break;
case "screenshot-screen":
if (args["show-pointer"] === true)
parts.push("show-pointer=true");
else if (args["show-pointer"] === false)
parts.push("show-pointer=false");
if (args["write-to-disk"] === true)
parts.push("write-to-disk=true");
break;
case "screenshot-window":
if (args["write-to-disk"] === true)
parts.push("write-to-disk=true");
break;
}
if (args.value) {
parts.push(args.value); parts.push(args.value);
} else if (args.index) {
parts.push(args.index);
}
}
break;
case "mangowc":
var compositorArgs = ACTION_ARGS.mangowc;
if (compositorArgs && compositorArgs[base] && compositorArgs[base].args) {
var argConfig = compositorArgs[base].args;
var argValues = [];
for (var i = 0; i < argConfig.length; i++) {
var argDef = argConfig[i];
var val = args[argDef.name];
if (val === undefined || val === "")
val = argDef.default || "";
if (val === "" && argValues.length === 0)
continue;
argValues.push(val);
}
if (argValues.length > 0)
parts.push(argValues.join(","));
} else if (args.value) {
parts.push(args.value);
}
break;
case "hyprland":
var hyprArgs = ACTION_ARGS.hyprland;
if (hyprArgs && hyprArgs[base] && hyprArgs[base].args) {
var hyprConfig = hyprArgs[base].args;
switch (base) {
case "resizewindowpixel":
case "movewindowpixel":
if (args[hyprConfig[0].name])
parts.push(args[hyprConfig[0].name]);
if (args[hyprConfig[1].name])
parts[parts.length - 1] += "," + args[hyprConfig[1].name];
break;
case "setprop":
if (args.window)
parts.push(args.window);
if (args.property)
parts.push(args.property);
if (args.value)
parts.push(args.value);
break;
case "sendshortcut":
if (args.mod)
parts.push(args.mod);
if (args.key)
parts.push(args.key);
if (args.window)
parts.push(args.window);
break;
case "sendkeystate":
if (args.mod)
parts.push(args.mod);
if (args.key)
parts.push(args.key);
if (args.state)
parts.push(args.state);
if (args.window)
parts.push(args.window);
break;
case "signalwindow":
if (args.window)
parts.push(args.window);
if (args.signal)
parts.push(args.signal);
break;
default:
for (var j = 0; j < hyprConfig.length; j++) {
var hVal = args[hyprConfig[j].name];
if (hVal !== undefined && hVal !== "")
parts.push(hVal);
}
}
} else if (args.value) {
parts.push(args.value);
}
break;
default:
if (args.value)
parts.push(args.value);
} }
return parts.join(" "); return parts.join(" ");
@@ -1246,22 +1255,22 @@ function parseDmsActionArgs(action) {
for (var i = 0; i < rest.length; i++) { for (var i = 0; i < rest.length; i++) {
var c = rest[i]; var c = rest[i];
switch (c) { switch (c) {
case '"': case '"':
inQuotes = !inQuotes; inQuotes = !inQuotes;
hadQuotes = true; hadQuotes = true;
break; break;
case ' ': case ' ':
if (inQuotes) { if (inQuotes) {
current += c;
} else if (current || hadQuotes) {
tokens.push(current);
current = "";
hadQuotes = false;
}
break;
default:
current += c; current += c;
} else if (current || hadQuotes) { break;
tokens.push(current);
current = "";
hadQuotes = false;
}
break;
default:
current += c;
break;
} }
} }
if (current || hadQuotes) if (current || hadQuotes)

View File

@@ -384,7 +384,10 @@ Singleton {
"source": bind.source || "config", "source": bind.source || "config",
"isOverride": bind.source === "dms", "isOverride": bind.source === "dms",
"cooldownMs": bind.cooldownMs || 0, "cooldownMs": bind.cooldownMs || 0,
"flags": bind.flags || "" "flags": bind.flags || "",
"allowWhenLocked": bind.allowWhenLocked || false,
"allowInhibiting": bind.allowInhibiting,
"repeat": bind.repeat
}; };
if (actionMap[action]) { if (actionMap[action]) {
actionMap[action].keys.push(keyData); actionMap[action].keys.push(keyData);
@@ -454,6 +457,12 @@ Singleton {
cmd.push("--replace-key", originalKey); cmd.push("--replace-key", originalKey);
if (bindData.cooldownMs > 0) if (bindData.cooldownMs > 0)
cmd.push("--cooldown-ms", String(bindData.cooldownMs)); cmd.push("--cooldown-ms", String(bindData.cooldownMs));
if (bindData.allowWhenLocked)
cmd.push("--allow-when-locked");
if (bindData.repeat === false)
cmd.push("--no-repeat");
if (bindData.allowInhibiting === false)
cmd.push("--no-inhibiting");
if (bindData.flags) if (bindData.flags)
cmd.push("--flags", bindData.flags); cmd.push("--flags", bindData.flags);
saveProcess.command = cmd; saveProcess.command = cmd;

View File

@@ -28,8 +28,14 @@ Item {
property string editDesc: "" property string editDesc: ""
property int editCooldownMs: 0 property int editCooldownMs: 0
property string editFlags: "" property string editFlags: ""
property bool editAllowWhenLocked: false
property var editRepeat: undefined
property var editAllowInhibiting: undefined
property int _savedCooldownMs: -1 property int _savedCooldownMs: -1
property string _savedFlags: "" property string _savedFlags: ""
property var _savedAllowWhenLocked: undefined
property var _savedRepeat: undefined
property var _savedAllowInhibiting: undefined
property bool hasChanges: false property bool hasChanges: false
property string _actionType: "" property string _actionType: ""
property bool addingNewKey: false property bool addingNewKey: false
@@ -115,6 +121,24 @@ Item {
} else { } else {
editFlags = keys[i].flags || ""; editFlags = keys[i].flags || "";
} }
if (_savedAllowWhenLocked !== undefined) {
editAllowWhenLocked = _savedAllowWhenLocked;
_savedAllowWhenLocked = undefined;
} else {
editAllowWhenLocked = keys[i].allowWhenLocked || false;
}
if (_savedRepeat !== undefined) {
editRepeat = _savedRepeat;
_savedRepeat = undefined;
} else {
editRepeat = keys[i].repeat;
}
if (_savedAllowInhibiting !== undefined) {
editAllowInhibiting = _savedAllowInhibiting;
_savedAllowInhibiting = undefined;
} else {
editAllowInhibiting = keys[i].allowInhibiting;
}
hasChanges = false; hasChanges = false;
_actionType = Actions.getActionType(editAction); _actionType = Actions.getActionType(editAction);
useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(KeybindsService.currentProvider, editAction); useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(KeybindsService.currentProvider, editAction);
@@ -136,6 +160,9 @@ Item {
editDesc = bindData.desc || ""; editDesc = bindData.desc || "";
editCooldownMs = editingKeyIndex >= 0 ? (keys[editingKeyIndex].cooldownMs || 0) : 0; editCooldownMs = editingKeyIndex >= 0 ? (keys[editingKeyIndex].cooldownMs || 0) : 0;
editFlags = editingKeyIndex >= 0 ? (keys[editingKeyIndex].flags || "") : ""; editFlags = editingKeyIndex >= 0 ? (keys[editingKeyIndex].flags || "") : "";
editAllowWhenLocked = editingKeyIndex >= 0 ? (keys[editingKeyIndex].allowWhenLocked || false) : false;
editRepeat = editingKeyIndex >= 0 ? keys[editingKeyIndex].repeat : undefined;
editAllowInhibiting = editingKeyIndex >= 0 ? keys[editingKeyIndex].allowInhibiting : undefined;
hasChanges = false; hasChanges = false;
_actionType = Actions.getActionType(editAction); _actionType = Actions.getActionType(editAction);
useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(KeybindsService.currentProvider, editAction); useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(KeybindsService.currentProvider, editAction);
@@ -156,6 +183,9 @@ Item {
editKey = keys[index].key; editKey = keys[index].key;
editCooldownMs = keys[index].cooldownMs || 0; editCooldownMs = keys[index].cooldownMs || 0;
editFlags = keys[index].flags || ""; editFlags = keys[index].flags || "";
editAllowWhenLocked = keys[index].allowWhenLocked || false;
editRepeat = keys[index].repeat;
editAllowInhibiting = keys[index].allowInhibiting;
hasChanges = false; hasChanges = false;
} }
@@ -170,10 +200,20 @@ Item {
editCooldownMs = changes.cooldownMs; editCooldownMs = changes.cooldownMs;
if (changes.flags !== undefined) if (changes.flags !== undefined)
editFlags = changes.flags; editFlags = changes.flags;
const origKey = editingKeyIndex >= 0 && editingKeyIndex < keys.length ? keys[editingKeyIndex].key : ""; if (changes.allowWhenLocked !== undefined)
const origCooldown = editingKeyIndex >= 0 && editingKeyIndex < keys.length ? (keys[editingKeyIndex].cooldownMs || 0) : 0; editAllowWhenLocked = changes.allowWhenLocked;
const origFlags = editingKeyIndex >= 0 && editingKeyIndex < keys.length ? (keys[editingKeyIndex].flags || "") : ""; if (changes.repeat !== undefined)
hasChanges = editKey !== origKey || editAction !== (bindData.action || "") || editDesc !== (bindData.desc || "") || editCooldownMs !== origCooldown || editFlags !== origFlags; editRepeat = changes.repeat;
if (changes.allowInhibiting !== undefined)
editAllowInhibiting = changes.allowInhibiting;
const hasKey = editingKeyIndex >= 0 && editingKeyIndex < keys.length;
const origKey = hasKey ? keys[editingKeyIndex].key : "";
const origCooldown = hasKey ? (keys[editingKeyIndex].cooldownMs || 0) : 0;
const origFlags = hasKey ? (keys[editingKeyIndex].flags || "") : "";
const origAllowWhenLocked = hasKey ? (keys[editingKeyIndex].allowWhenLocked || false) : false;
const origRepeat = hasKey ? keys[editingKeyIndex].repeat : undefined;
const origAllowInhibiting = hasKey ? keys[editingKeyIndex].allowInhibiting : undefined;
hasChanges = editKey !== origKey || editAction !== (bindData.action || "") || editDesc !== (bindData.desc || "") || editCooldownMs !== origCooldown || editFlags !== origFlags || editAllowWhenLocked !== origAllowWhenLocked || editRepeat !== origRepeat || editAllowInhibiting !== origAllowInhibiting;
} }
function canSave() { function canSave() {
@@ -193,12 +233,18 @@ Item {
desc = expandedLoader.item.currentTitle; desc = expandedLoader.item.currentTitle;
_savedCooldownMs = editCooldownMs; _savedCooldownMs = editCooldownMs;
_savedFlags = editFlags; _savedFlags = editFlags;
_savedAllowWhenLocked = editAllowWhenLocked;
_savedRepeat = editRepeat;
_savedAllowInhibiting = editAllowInhibiting;
saveBind(origKey, { saveBind(origKey, {
"key": editKey, "key": editKey,
"action": editAction, "action": editAction,
"desc": desc, "desc": desc,
"cooldownMs": editCooldownMs, "cooldownMs": editCooldownMs,
"flags": editFlags "flags": editFlags,
"allowWhenLocked": editAllowWhenLocked,
"repeat": editRepeat,
"allowInhibiting": editAllowInhibiting
}); });
hasChanges = false; hasChanges = false;
addingNewKey = false; addingNewKey = false;
@@ -1322,6 +1368,29 @@ Item {
} }
} }
} }
RowLayout {
visible: optionsRow.argConfig?.base === "quit"
spacing: Theme.spacingXS
DankToggle {
checked: optionsRow.parsedArgs?.args["skip-confirmation"] === true
onToggled: newChecked => {
const args = newChecked ? {
"skip-confirmation": true
} : {};
root.updateEdit({
"action": Actions.buildCompositorAction(KeybindsService.currentProvider, "quit", args)
});
}
}
StyledText {
text: I18n.tr("Skip confirmation")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
} }
} }
@@ -1633,6 +1702,82 @@ Item {
} }
} }
RowLayout {
Layout.fillWidth: true
spacing: Theme.spacingM
visible: KeybindsService.currentProvider === "niri"
StyledText {
text: I18n.tr("Options")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
Layout.preferredWidth: root._labelWidth
}
Flow {
Layout.fillWidth: true
spacing: Theme.spacingM
RowLayout {
spacing: Theme.spacingXS
DankToggle {
checked: root.editRepeat !== false
onToggled: newChecked => {
root.updateEdit({
"repeat": newChecked ? undefined : false
});
}
}
StyledText {
text: I18n.tr("Repeat")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
RowLayout {
spacing: Theme.spacingXS
DankToggle {
checked: root.editAllowWhenLocked
onToggled: newChecked => {
root.updateEdit({
"allowWhenLocked": newChecked
});
}
}
StyledText {
text: I18n.tr("When locked")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
RowLayout {
spacing: Theme.spacingXS
DankToggle {
checked: root.editAllowInhibiting !== false
onToggled: newChecked => {
root.updateEdit({
"allowInhibiting": newChecked ? undefined : false
});
}
}
StyledText {
text: I18n.tr("Inhibitable")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
}
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 1 Layout.preferredHeight: 1