diff --git a/core/internal/config/embedded/hyprland.conf b/core/internal/config/embedded/hyprland.conf index afe78eb1..7723dde6 100644 --- a/core/internal/config/embedded/hyprland.conf +++ b/core/internal/config/embedded/hyprland.conf @@ -140,8 +140,8 @@ $mod = SUPER bind = $mod, T, exec, {{TERMINAL_COMMAND}} bind = $mod, space, exec, dms ipc call spotlight toggle bind = $mod, V, exec, dms ipc call clipboard toggle -bind = $mod, M, exec, dms ipc call processlist toggle -bind = $mod, comma, exec, dms ipc call settings toggle +bind = $mod, M, exec, dms ipc call processlist focusOrToggle +bind = $mod, comma, exec, dms ipc call settings focusOrToggle bind = $mod, N, exec, dms ipc call notifications toggle bind = $mod SHIFT, N, exec, dms ipc call notepad toggle bind = $mod, Y, exec, dms ipc call dankdash wallpaper @@ -153,7 +153,7 @@ bind = $mod SHIFT, Slash, exec, dms ipc call keybinds toggle hyprland # === Security === bind = $mod ALT, L, exec, dms ipc call lock lock bind = $mod SHIFT, E, exit -bind = CTRL ALT, Delete, exec, dms ipc call processlist toggle +bind = CTRL ALT, Delete, exec, dms ipc call processlist focusOrToggle # === Audio Controls === bindel = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3 diff --git a/core/internal/config/embedded/niri.kdl b/core/internal/config/embedded/niri.kdl index d435eeae..133ca52e 100644 --- a/core/internal/config/embedded/niri.kdl +++ b/core/internal/config/embedded/niri.kdl @@ -238,10 +238,10 @@ binds { spawn "dms" "ipc" "call" "clipboard" "toggle"; } Mod+M hotkey-overlay-title="Task Manager" { - spawn "dms" "ipc" "call" "processlist" "toggle"; + spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; } Mod+Comma hotkey-overlay-title="Settings" { - spawn "dms" "ipc" "call" "settings" "toggle"; + spawn "dms" "ipc" "call" "settings" "focusOrToggle"; } Mod+Y hotkey-overlay-title="Browse Wallpapers" { spawn "dms" "ipc" "call" "dankdash" "wallpaper"; @@ -255,7 +255,7 @@ binds { } Mod+Shift+E { quit; } Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" { - spawn "dms" "ipc" "call" "processlist" "toggle"; + spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; } // === Audio Controls === diff --git a/quickshell/DMSShellIPC.qml b/quickshell/DMSShellIPC.qml index bf436473..62305352 100644 --- a/quickshell/DMSShellIPC.qml +++ b/quickshell/DMSShellIPC.qml @@ -79,6 +79,14 @@ Item { return "PROCESSLIST_TOGGLE_SUCCESS"; } + function focusOrToggle(): string { + root.processListModalLoader.active = true; + if (root.processListModalLoader.item) + root.processListModalLoader.item.focusOrToggle(); + + return "PROCESSLIST_FOCUS_OR_TOGGLE_SUCCESS"; + } + target: "processlist" } @@ -598,6 +606,11 @@ Item { return "SETTINGS_TOGGLE_SUCCESS"; } + function focusOrToggle(): string { + PopoutService.focusOrToggleSettings(); + return "SETTINGS_FOCUS_OR_TOGGLE_SUCCESS"; + } + function get(key: string): string { return JSON.stringify(SettingsData?.[key]) } diff --git a/quickshell/Modals/ProcessListModal.qml b/quickshell/Modals/ProcessListModal.qml index 67633474..dd432625 100644 --- a/quickshell/Modals/ProcessListModal.qml +++ b/quickshell/Modals/ProcessListModal.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import Quickshell +import Quickshell.Wayland import qs.Common import qs.Modules.ProcessList import qs.Services @@ -39,6 +40,27 @@ FloatingWindow { visible = !visible; } + function focusOrToggle() { + if (!DgopService.dgopAvailable) { + console.warn("ProcessListModal: dgop is not available"); + return; + } + if (visible) { + const modalTitle = I18n.tr("System Monitor", "sysmon window title"); + for (const toplevel of ToplevelManager.toplevels.values) { + if (toplevel.title !== "System Monitor" && toplevel.title !== modalTitle) + continue; + if (toplevel.activated) { + hide(); + return; + } + toplevel.activate(); + return; + } + } + show(); + } + objectName: "processListModal" title: I18n.tr("System Monitor", "sysmon window title") minimumSize: Qt.size(650, 400) diff --git a/quickshell/Modals/Spotlight/SpotlightModal.qml b/quickshell/Modals/Spotlight/SpotlightModal.qml index 734f5628..7d16c4ae 100644 --- a/quickshell/Modals/Spotlight/SpotlightModal.qml +++ b/quickshell/Modals/Spotlight/SpotlightModal.qml @@ -73,6 +73,10 @@ DankModal { borderWidth: 1 enableShadow: true keepContentLoaded: true + animationScaleCollapsed: 0.96 + animationDuration: Theme.expressiveDurations.expressiveDefaultSpatial + animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial + animationExitCurve: Theme.expressiveCurves.emphasized onVisibleChanged: () => { if (!visible) return; diff --git a/quickshell/Modules/ControlCenter/Components/HeaderPane.qml b/quickshell/Modules/ControlCenter/Components/HeaderPane.qml index 74c26035..6049a480 100644 --- a/quickshell/Modules/ControlCenter/Components/HeaderPane.qml +++ b/quickshell/Modules/ControlCenter/Components/HeaderPane.qml @@ -99,7 +99,7 @@ Rectangle { backgroundColor: "transparent" onClicked: { root.settingsButtonClicked(); - PopoutService.openSettings(); + PopoutService.focusOrToggleSettings(); } } diff --git a/quickshell/Modules/DankDash/DankDashPopout.qml b/quickshell/Modules/DankDash/DankDashPopout.qml index 02858c19..58807a17 100644 --- a/quickshell/Modules/DankDash/DankDashPopout.qml +++ b/quickshell/Modules/DankDash/DankDashPopout.qml @@ -301,7 +301,7 @@ DankPopout { let settingsIndex = SettingsData.weatherEnabled ? 4 : 3; if (index === settingsIndex) { dashVisible = false; - PopoutService.openSettings(); + PopoutService.focusOrToggleSettings(); } } } diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index 69d07fcf..c513855e 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -2,6 +2,8 @@ pragma Singleton pragma ComponentBehavior: Bound import QtQuick import Quickshell +import Quickshell.Wayland +import qs.Common Singleton { id: root @@ -219,6 +221,23 @@ Singleton { } } + function focusOrToggleSettings() { + if (settingsModal?.visible) { + const settingsTitle = I18n.tr("Settings", "settings window title"); + for (const toplevel of ToplevelManager.toplevels.values) { + if (toplevel.title !== "Settings" && toplevel.title !== settingsTitle) + continue; + if (toplevel.activated) { + settingsModal.hide(); + return; + } + toplevel.activate(); + return; + } + } + openSettings(); + } + function unloadSettings() { if (settingsModalLoader) { settingsModal = null;