diff --git a/quickshell/Common/KeyUtils.js b/quickshell/Common/KeyUtils.js index 51c0b92a..5c712d01 100644 --- a/quickshell/Common/KeyUtils.js +++ b/quickshell/Common/KeyUtils.js @@ -39,11 +39,12 @@ const KEY_MAP = { 16777329: "XF86AudioMute", 16842808: "XF86AudioMicMute", 16777344: "XF86AudioPlay", - 16777345: "XF86AudioPause", - 16777346: "XF86AudioStop", + 16777345: "XF86AudioStop", + 16777346: "XF86AudioPrev", 16777347: "XF86AudioNext", - 16777348: "XF86AudioPrev", - 16842792: "XF86AudioRecord", + 16777348: "XF86AudioPause", + 16777349: "XF86AudioMedia", + 16777350: "XF86AudioRecord", 16842798: "XF86MonBrightnessUp", 16842797: "XF86MonBrightnessDown", 16842800: "XF86KbdBrightnessUp", diff --git a/quickshell/Common/KeybindActions.js b/quickshell/Common/KeybindActions.js index d2731061..3e263662 100644 --- a/quickshell/Common/KeybindActions.js +++ b/quickshell/Common/KeybindActions.js @@ -49,26 +49,26 @@ const DMS_ACTIONS = [ { id: "spawn dms ipc call inhibit toggle", label: "Idle Inhibit: Toggle" }, { id: "spawn dms ipc call inhibit enable", label: "Idle Inhibit: Enable" }, { id: "spawn dms ipc call inhibit disable", label: "Idle Inhibit: Disable" }, - { id: "spawn dms ipc call audio increment", label: "Volume Up" }, + { id: "spawn dms ipc call audio increment 5", label: "Volume Up" }, { id: "spawn dms ipc call audio increment 1", label: "Volume Up (1%)" }, { id: "spawn dms ipc call audio increment 5", label: "Volume Up (5%)" }, { id: "spawn dms ipc call audio increment 10", label: "Volume Up (10%)" }, - { id: "spawn dms ipc call audio decrement", label: "Volume Down" }, + { id: "spawn dms ipc call audio decrement 5", label: "Volume Down" }, { id: "spawn dms ipc call audio decrement 1", label: "Volume Down (1%)" }, { id: "spawn dms ipc call audio decrement 5", label: "Volume Down (5%)" }, { id: "spawn dms ipc call audio decrement 10", label: "Volume Down (10%)" }, { id: "spawn dms ipc call audio mute", label: "Volume Mute Toggle" }, { id: "spawn dms ipc call audio micmute", label: "Microphone Mute Toggle" }, { id: "spawn dms ipc call audio cycleoutput", label: "Audio Output: Cycle" }, - { id: "spawn dms ipc call brightness increment", label: "Brightness Up" }, - { id: "spawn dms ipc call brightness increment 1", label: "Brightness Up (1%)" }, - { id: "spawn dms ipc call brightness increment 5", label: "Brightness Up (5%)" }, - { id: "spawn dms ipc call brightness increment 10", label: "Brightness Up (10%)" }, - { id: "spawn dms ipc call brightness decrement", label: "Brightness Down" }, - { id: "spawn dms ipc call brightness decrement 1", label: "Brightness Down (1%)" }, - { id: "spawn dms ipc call brightness decrement 5", label: "Brightness Down (5%)" }, - { id: "spawn dms ipc call brightness decrement 10", label: "Brightness Down (10%)" }, - { id: "spawn dms ipc call brightness toggleExponential", label: "Brightness: Toggle Exponential" }, + { id: "spawn dms ipc call brightness increment 5 \"\"", label: "Brightness Up" }, + { id: "spawn dms ipc call brightness increment 1 \"\"", label: "Brightness Up (1%)" }, + { id: "spawn dms ipc call brightness increment 5 \"\"", label: "Brightness Up (5%)" }, + { id: "spawn dms ipc call brightness increment 10 \"\"", label: "Brightness Up (10%)" }, + { id: "spawn dms ipc call brightness decrement 5 \"\"", label: "Brightness Down" }, + { id: "spawn dms ipc call brightness decrement 1 \"\"", label: "Brightness Down (1%)" }, + { id: "spawn dms ipc call brightness decrement 5 \"\"", label: "Brightness Down (5%)" }, + { id: "spawn dms ipc call brightness decrement 10 \"\"", label: "Brightness Down (10%)" }, + { id: "spawn dms ipc call brightness toggleExponential \"\"", label: "Brightness: Toggle Exponential" }, { id: "spawn dms ipc call theme toggle", label: "Theme: Toggle Light/Dark" }, { id: "spawn dms ipc call theme light", label: "Theme: Light Mode" }, { id: "spawn dms ipc call theme dark", label: "Theme: Dark Mode" }, @@ -223,19 +223,37 @@ const ACTION_ARGS = { const DMS_ACTION_ARGS = { "audio increment": { base: "spawn dms ipc call audio increment", - args: [{ name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "" }] + args: [{ name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "5" }] }, "audio decrement": { base: "spawn dms ipc call audio decrement", - args: [{ name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "" }] + args: [{ name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "5" }] }, "brightness increment": { base: "spawn dms ipc call brightness increment", - args: [{ name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "" }] + args: [ + { name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "5" }, + { name: "device", type: "text", label: "Device", placeholder: "leave empty for default", default: "" } + ] }, "brightness decrement": { base: "spawn dms ipc call brightness decrement", - args: [{ name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "" }] + args: [ + { name: "amount", type: "number", label: "Amount %", placeholder: "5", default: "5" }, + { name: "device", type: "text", label: "Device", placeholder: "leave empty for default", default: "" } + ] + }, + "brightness toggleExponential": { + base: "spawn dms ipc call brightness toggleExponential", + args: [ + { name: "device", type: "text", label: "Device", placeholder: "leave empty for default", default: "" } + ] + }, + "dash toggle": { + base: "spawn dms ipc call dash toggle", + args: [ + { name: "tab", type: "text", label: "Tab", placeholder: "overview, media, wallpaper, weather", default: "" } + ] } }; @@ -243,6 +261,10 @@ function getActionTypes() { return ACTION_TYPES; } +function getDmsActionArgs() { + return DMS_ACTION_ARGS; +} + function getDmsActions(isNiri, isHyprland) { const result = []; for (let i = 0; i < DMS_ACTIONS.length; i++) { @@ -495,10 +517,48 @@ function parseDmsActionArgs(action) { for (var key in DMS_ACTION_ARGS) { var config = DMS_ACTION_ARGS[key]; - if (action.startsWith(config.base)) { - var rest = action.slice(config.base.length).trim(); - return { base: key, args: { amount: rest || "" } }; + if (!action.startsWith(config.base)) + continue; + + var rest = action.slice(config.base.length).trim(); + var result = { base: key, args: {} }; + + if (!rest) + return result; + + var tokens = []; + var current = ""; + var inQuotes = false; + var hadQuotes = false; + for (var i = 0; i < rest.length; i++) { + var c = rest[i]; + switch (c) { + case '"': + inQuotes = !inQuotes; + hadQuotes = true; + break; + case ' ': + if (inQuotes) { + current += c; + } else if (current || hadQuotes) { + tokens.push(current); + current = ""; + hadQuotes = false; + } + break; + default: + current += c; + break; + } } + if (current || hadQuotes) + tokens.push(current); + + for (var j = 0; j < config.args.length && j < tokens.length; j++) { + result.args[config.args[j].name] = tokens[j]; + } + + return result; } return { base: action, args: {} }; @@ -509,11 +569,24 @@ function buildDmsAction(baseKey, args) { if (!config) return ""; - var action = config.base; - if (args && args.amount) - action += " " + args.amount; + var parts = [config.base]; - return action; + for (var i = 0; i < config.args.length; i++) { + var argDef = config.args[i]; + var value = args?.[argDef.name]; + if (value === undefined || value === null) + value = argDef.default ?? ""; + + if (argDef.type === "text" && value === "") { + parts.push('""'); + } else if (value !== "") { + parts.push(value); + } else { + break; + } + } + + return parts.join(" "); } function getScreenshotOptions() { diff --git a/quickshell/Widgets/KeybindItem.qml b/quickshell/Widgets/KeybindItem.qml index b759b75f..acf98d86 100644 --- a/quickshell/Widgets/KeybindItem.qml +++ b/quickshell/Widgets/KeybindItem.qml @@ -880,26 +880,18 @@ Item { } RowLayout { + id: dmsArgsRow Layout.fillWidth: true spacing: Theme.spacingM - property var dmsArgConfig: { - const action = root.editAction; - if (!action) - return null; - if (action.indexOf("audio increment") !== -1 || action.indexOf("audio decrement") !== -1 || action.indexOf("brightness increment") !== -1 || action.indexOf("brightness decrement") !== -1) { - const parts = action.split(" "); - const lastPart = parts[parts.length - 1]; - const hasAmount = /^\d+$/.test(lastPart); - return { - hasAmount: hasAmount, - amount: hasAmount ? lastPart : "" - }; - } - return null; - } + readonly property var argConfig: Actions.getActionArgConfig(root.editAction) + readonly property var parsedArgs: argConfig?.type === "dms" ? Actions.parseDmsActionArgs(root.editAction) : null + readonly property var dmsActionArgs: Actions.getDmsActionArgs() + readonly property bool hasAmountArg: parsedArgs?.base ? (dmsActionArgs?.[parsedArgs.base]?.args?.some(a => a.name === "amount") ?? false) : false + readonly property bool hasDeviceArg: parsedArgs?.base ? (dmsActionArgs?.[parsedArgs.base]?.args?.some(a => a.name === "device") ?? false) : false + readonly property bool hasTabArg: parsedArgs?.base ? (dmsActionArgs?.[parsedArgs.base]?.args?.some(a => a.name === "tab") ?? false) : false - visible: root._actionType === "dms" && dmsArgConfig !== null + visible: root._actionType === "dms" && argConfig?.type === "dms" StyledText { text: I18n.tr("Amount") @@ -907,26 +899,36 @@ Item { font.weight: Font.Medium color: Theme.surfaceVariantText Layout.preferredWidth: 60 + visible: dmsArgsRow.hasAmountArg } DankTextField { + id: dmsAmountField Layout.preferredWidth: 80 Layout.preferredHeight: 40 placeholderText: "5" - text: parent.dmsArgConfig?.amount || "" - onTextChanged: { - if (!parent.dmsArgConfig) + visible: dmsArgsRow.hasAmountArg + + Connections { + target: dmsArgsRow + function onParsedArgsChanged() { + const newText = dmsArgsRow.parsedArgs?.args?.amount || ""; + if (dmsAmountField.text !== newText) + dmsAmountField.text = newText; + } + } + + Component.onCompleted: { + text = dmsArgsRow.parsedArgs?.args?.amount || ""; + } + + onEditingFinished: { + if (!dmsArgsRow.parsedArgs) return; - const action = root.editAction; - const parts = action.split(" "); - const lastPart = parts[parts.length - 1]; - const hasOldAmount = /^\d+$/.test(lastPart); - if (hasOldAmount) - parts.pop(); - if (text && /^\d+$/.test(text)) - parts.push(text); + const newArgs = Object.assign({}, dmsArgsRow.parsedArgs.args); + newArgs.amount = text || "5"; root.updateEdit({ - action: parts.join(" ") + action: Actions.buildDmsAction(dmsArgsRow.parsedArgs.base, newArgs) }); } } @@ -935,10 +937,105 @@ Item { text: "%" font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText + visible: dmsArgsRow.hasAmountArg + } + + StyledText { + text: I18n.tr("Device") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceVariantText + Layout.leftMargin: dmsArgsRow.hasAmountArg ? Theme.spacingM : 0 + Layout.preferredWidth: dmsArgsRow.hasAmountArg ? -1 : 60 + visible: dmsArgsRow.hasDeviceArg + } + + DankTextField { + id: dmsDeviceField + Layout.fillWidth: true + Layout.preferredHeight: 40 + placeholderText: I18n.tr("leave empty for default") + visible: dmsArgsRow.hasDeviceArg + + Connections { + target: dmsArgsRow + function onParsedArgsChanged() { + const newText = dmsArgsRow.parsedArgs?.args?.device || ""; + if (dmsDeviceField.text !== newText) + dmsDeviceField.text = newText; + } + } + + Component.onCompleted: { + text = dmsArgsRow.parsedArgs?.args?.device || ""; + } + + onEditingFinished: { + if (!dmsArgsRow.parsedArgs) + return; + const newArgs = Object.assign({}, dmsArgsRow.parsedArgs.args); + newArgs.device = text; + root.updateEdit({ + action: Actions.buildDmsAction(dmsArgsRow.parsedArgs.base, newArgs) + }); + } } Item { Layout.fillWidth: true + visible: !dmsArgsRow.hasDeviceArg && !dmsArgsRow.hasTabArg + } + + StyledText { + text: I18n.tr("Tab") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceVariantText + Layout.preferredWidth: 60 + visible: dmsArgsRow.hasTabArg + } + + DankDropdown { + id: dmsTabDropdown + Layout.fillWidth: true + compactMode: true + visible: dmsArgsRow.hasTabArg + currentValue: { + const tab = dmsArgsRow.parsedArgs?.args?.tab || ""; + switch (tab) { + case "media": + return I18n.tr("Media"); + case "wallpaper": + return I18n.tr("Wallpaper"); + case "weather": + return I18n.tr("Weather"); + default: + return I18n.tr("Overview"); + } + } + options: [I18n.tr("Overview"), I18n.tr("Media"), I18n.tr("Wallpaper"), I18n.tr("Weather")] + onValueChanged: value => { + if (!dmsArgsRow.parsedArgs) + return; + const newArgs = Object.assign({}, dmsArgsRow.parsedArgs.args); + switch (value) { + case I18n.tr("Media"): + newArgs.tab = "media"; + break; + case I18n.tr("Wallpaper"): + newArgs.tab = "wallpaper"; + break; + case I18n.tr("Weather"): + newArgs.tab = "weather"; + break; + default: + newArgs.tab = ""; + break; + } + root.updateEdit({ + action: Actions.buildDmsAction(dmsArgsRow.parsedArgs.base, newArgs) + }); + } } }