diff --git a/quickshell/Modals/Common/DankModal.qml b/quickshell/Modals/Common/DankModal.qml index 81592ad43..cdb8ba615 100644 --- a/quickshell/Modals/Common/DankModal.qml +++ b/quickshell/Modals/Common/DankModal.qml @@ -1,5 +1,4 @@ import QtQuick -import Quickshell.Hyprland import qs.Common import qs.Services import qs.Widgets @@ -61,12 +60,9 @@ Item { } // Hyprland OnDemand grab delivers keyboard focus to the modal content surface. - HyprlandFocusGrab { + DankFocusGrab { windows: (root.contentWindow ? [root.contentWindow] : []).concat(root.transientSurfaceTracker?.focusWindows ?? []) - active: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus) - - property var restoreToplevel: null - onActiveChanged: restoreToplevel = active ? KeyboardFocus.captureActiveToplevel() : KeyboardFocus.restoreToplevel(restoreToplevel) + wanted: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus) } readonly property var contentWindow: impl.item ? impl.item.contentWindow : null readonly property var effectiveScreen: impl.item ? impl.item.effectiveScreen : null diff --git a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml index cd8989245..2bb3cf101 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml @@ -1,7 +1,6 @@ import QtQuick import QtQuick.Effects import Quickshell -import Quickshell.Hyprland import Quickshell.Services.SystemTray import Quickshell.Wayland import Quickshell.Widgets @@ -1048,12 +1047,9 @@ BasePill { WlrLayershell.namespace: "dms:tray-overflow-menu" color: "transparent" - HyprlandFocusGrab { + DankFocusGrab { windows: [overflowMenu].concat(KeyboardFocus.barWindows) - active: root.useOverflowPopup && KeyboardFocus.wantsGrab(root.menuOpen, null) - - property var restoreToplevel: null - onActiveChanged: restoreToplevel = active ? KeyboardFocus.captureActiveToplevel() : KeyboardFocus.restoreToplevel(restoreToplevel) + wanted: root.useOverflowPopup && KeyboardFocus.wantsGrab(root.menuOpen, null) } Connections { @@ -1601,12 +1597,9 @@ BasePill { WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(menuRoot.showMenu, null) color: "transparent" - HyprlandFocusGrab { + DankFocusGrab { windows: [menuWindow].concat(KeyboardFocus.barWindows) - active: KeyboardFocus.wantsGrab(menuRoot.showMenu, null) - - property var restoreToplevel: null - onActiveChanged: restoreToplevel = active ? KeyboardFocus.captureActiveToplevel() : KeyboardFocus.restoreToplevel(restoreToplevel) + wanted: KeyboardFocus.wantsGrab(menuRoot.showMenu, null) } anchors { diff --git a/quickshell/Widgets/DankFocusGrab.qml b/quickshell/Widgets/DankFocusGrab.qml new file mode 100644 index 000000000..32f923800 --- /dev/null +++ b/quickshell/Widgets/DankFocusGrab.qml @@ -0,0 +1,41 @@ +import QtQuick +import Quickshell.Hyprland +import qs.Common + +// Delays grab release so keyboardFocus=None commits before the grab dies, +// keeping Hyprland from handing focus back to the closing surface (#2577) +HyprlandFocusGrab { + id: root + + property bool wanted: false + property bool _held: false + property bool _compositorCleared: false + property var _restoreToplevel: null + + property Timer _releaseTimer: Timer { + interval: 50 + onTriggered: { + root._held = false; + root.active = false; + root._restoreToplevel = root._compositorCleared ? null : KeyboardFocus.restoreToplevel(root._restoreToplevel); + } + } + + onWantedChanged: _sync() + Component.onCompleted: _sync() + + function _sync() { + if (!wanted) { + if (_held) + _releaseTimer.restart(); + return; + } + _releaseTimer.stop(); + _held = true; + _compositorCleared = false; + _restoreToplevel = KeyboardFocus.captureActiveToplevel(); + active = true; + } + + onCleared: _compositorCleared = true +} diff --git a/quickshell/Widgets/DankPopout.qml b/quickshell/Widgets/DankPopout.qml index cc82d1fad..a63bc69bc 100644 --- a/quickshell/Widgets/DankPopout.qml +++ b/quickshell/Widgets/DankPopout.qml @@ -1,5 +1,4 @@ import QtQuick -import Quickshell.Hyprland import qs.Common import qs.Services @@ -62,7 +61,7 @@ Item { } // Hyprland OnDemand grab: whitelist popout surfaces and bars so dismiss clicks still land. - HyprlandFocusGrab { + DankFocusGrab { windows: { const list = []; if (root.contentWindow) @@ -72,10 +71,7 @@ Item { const transientWindows = root.transientSurfaceTracker?.focusWindows ?? []; return list.concat(transientWindows).concat(KeyboardFocus.barWindows); } - active: KeyboardFocus.wantsGrab(root.shouldBeVisible, root.customKeyboardFocus) - - property var restoreToplevel: null - onActiveChanged: restoreToplevel = active ? KeyboardFocus.captureActiveToplevel() : KeyboardFocus.restoreToplevel(restoreToplevel) + wanted: KeyboardFocus.wantsGrab(root.shouldBeVisible, root.customKeyboardFocus) } Loader {