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

fix(Hover): refactor & update hover tracking w/context menus

Fixes #2737
This commit is contained in:
purian23
2026-07-04 00:08:58 -04:00
parent 2861cc89c6
commit c554d973ef
30 changed files with 297 additions and 27 deletions
+13
View File
@@ -54,11 +54,14 @@ Item {
property bool addHorizontalPadding: false
property string emptyText: ""
property bool usePopupTransparency: !checkParentDisablesTransparency()
property var transientSurfaceTracker: null
signal valueChanged(string value)
property bool menuOpen: false
onMenuOpenChanged: transientSurfaceTracker?.setActive(root, menuOpen, null)
function closeDropdownMenu() {
if (!root.menuOpen && !dropdownMenu.opened && !dropdownMenu.visible)
return;
@@ -113,10 +116,20 @@ Item {
implicitHeight: !showTrigger ? 0 : (compactMode ? 40 : Math.max(60, labelColumn.implicitHeight + Theme.spacingM))
Component.onDestruction: {
transientSurfaceTracker?.unregister(root);
if (root.menuOpen || dropdownMenu.opened || dropdownMenu.visible)
dropdownMenu.close();
}
Connections {
target: root.transientSurfaceTracker
ignoreUnknownSignals: true
function onCloseRequested() {
root.closeDropdownMenu();
}
}
Column {
id: labelColumn
+14 -3
View File
@@ -27,6 +27,8 @@ Item {
property bool hoverDismissEnabled: false
property bool hoverDismissSuspended: false
property var customKeyboardFocus: null
readonly property alias transientSurfaceTracker: _transientSurfaceTracker
readonly property bool effectiveHoverDismissSuspended: hoverDismissSuspended || (transientSurfaceTracker?.active ?? false)
property bool backgroundInteractive: true
property bool contentHandlesKeys: false
property bool fullHeightSurface: false
@@ -55,6 +57,10 @@ Item {
readonly property var backgroundWindow: impl.item ? impl.item.backgroundWindow : null
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
TransientSurfaceTracker {
id: _transientSurfaceTracker
}
// Hyprland OnDemand grab: whitelist popout surfaces and bars so dismiss clicks still land.
HyprlandFocusGrab {
windows: {
@@ -63,7 +69,8 @@ Item {
list.push(root.contentWindow);
if (root.backgroundWindow && root.backgroundWindow !== root.contentWindow)
list.push(root.backgroundWindow);
return list.concat(KeyboardFocus.barWindows);
const transientWindows = root.transientSurfaceTracker?.focusWindows ?? [];
return list.concat(transientWindows).concat(KeyboardFocus.barWindows);
}
active: KeyboardFocus.wantsGrab(root.shouldBeVisible, root.customKeyboardFocus)
@@ -170,6 +177,7 @@ Item {
function close() {
_pendingOpen = false;
_pendingOpenTimer.stop();
transientSurfaceTracker?.closeAll?.();
if (impl.item)
impl.item.close();
}
@@ -186,8 +194,9 @@ Item {
}
function closeFromHoverDismiss() {
if (hoverDismissSuspended)
if (effectiveHoverDismissSuspended)
return;
transientSurfaceTracker?.closeAll?.();
hoverDismissEnabled = false;
// Enable animations using standard Theme-bound popout motion to preserve bindings.
if (impl.item)
@@ -308,7 +317,7 @@ Item {
it.effectiveBarPosition = Qt.binding(() => root.effectiveBarPosition);
it.effectiveBarBottomGap = Qt.binding(() => root.effectiveBarBottomGap);
it.hoverDismissEnabled = Qt.binding(() => root.hoverDismissEnabled);
it.hoverDismissSuspended = Qt.binding(() => root.hoverDismissSuspended);
it.hoverDismissSuspended = Qt.binding(() => root.effectiveHoverDismissSuspended);
it.shouldBeVisible = root.shouldBeVisible;
if (root._primeContent && typeof it.primeContent === "function")
@@ -332,6 +341,8 @@ Item {
Connections {
target: root
function onShouldBeVisibleChanged() {
if (!root.shouldBeVisible)
root.transientSurfaceTracker?.closeAll?.();
if (impl.item && impl.item.shouldBeVisible !== root.shouldBeVisible)
impl.item.shouldBeVisible = root.shouldBeVisible;
}
@@ -0,0 +1,37 @@
pragma ComponentBehavior: Bound
import QtQuick
QtObject {
id: root
property var _entries: []
readonly property bool active: _entries.length > 0
readonly property var focusWindows: _entries.map(entry => entry.focusWindow).filter(window => window)
signal closeRequested
function setActive(owner, active, focusWindow) {
if (!owner)
return;
const next = _entries.filter(entry => entry.owner !== owner);
if (active) {
next.push({
"owner": owner,
"focusWindow": focusWindow ?? null
});
}
_entries = next;
}
function unregister(owner) {
setActive(owner, false, null);
}
function closeAll() {
if (_entries.length === 0)
return;
closeRequested();
_entries = [];
}
}