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
@@ -8,6 +8,7 @@ Item {
id: clipboardContent
required property var modal
property var transientSurfaceTracker: null
property alias searchField: searchField
property alias clipboardListView: clipboardListView
@@ -66,7 +67,7 @@ Item {
contextMenu.hide();
}
readonly property bool contextMenuActive: contextMenu.openState
readonly property bool contextMenuActive: contextMenu.renderActive
anchors.fill: parent
@@ -74,6 +75,7 @@ Item {
id: contextMenu
modal: clipboardContent.modal
parentHandler: clipboardContent
transientSurfaceTracker: clipboardContent.transientSurfaceTracker
}
Column {
@@ -23,6 +23,8 @@ Item {
property real anchorY: 0
property bool openState: false
property bool renderActive: false
property var transientSurfaceTracker: null
readonly property alias contextWindow: menuWindow
readonly property bool blurActive: renderActive && openState && BlurService.enabled && Theme.connectedSurfaceBlurEnabled
readonly property bool hasPinnedDuplicate: !!entry && !entry.pinned && ClipboardService.getPinnedEntryByHash(entry.hash) !== null
@@ -90,6 +92,18 @@ Item {
readonly property real effectiveMenuHeight: Math.min(maxMenuHeight, naturalMenuHeight)
readonly property bool menuScrolls: naturalMenuHeight > effectiveMenuHeight + 0.5
onRenderActiveChanged: transientSurfaceTracker?.setActive(root, renderActive, contextWindow)
Component.onDestruction: transientSurfaceTracker?.unregister(root)
Connections {
target: root.transientSurfaceTracker
ignoreUnknownSignals: true
function onCloseRequested() {
root.hide();
}
}
TextMetrics {
id: menuTextMetrics
text: root.longestMenuText
@@ -9,6 +9,7 @@ FocusScope {
property var clearConfirmDialog: null
property var surfaceHost: null
property var transientSurfaceTracker: null
property string activeTab: "recents"
property bool showKeyboardHints: false
@@ -198,6 +199,7 @@ FocusScope {
id: historyContent
anchors.fill: parent
modal: root
transientSurfaceTracker: root.transientSurfaceTracker
}
}
@@ -130,6 +130,7 @@ DankModal {
content: Component {
ClipboardHistoryContent {
surfaceHost: clipboardHistoryModal
transientSurfaceTracker: clipboardHistoryModal.transientSurfaceTracker
clearConfirmDialog: clearConfirmDialog
onCloseRequested: clipboardHistoryModal.hide()
onInstantCloseRequested: clipboardHistoryModal.instantHide()
@@ -141,6 +141,7 @@ DankPopout {
LayoutMirroring.childrenInherit: true
surfaceHost: root
transientSurfaceTracker: root.transientSurfaceTracker
clearConfirmDialog: clearConfirmDialog
onCloseRequested: root.hide()
onInstantCloseRequested: root.hide()
+11 -1
View File
@@ -2,6 +2,7 @@ import QtQuick
import Quickshell.Hyprland
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
@@ -38,6 +39,7 @@ Item {
property bool keepContentLoaded: false
property bool keepPopoutsOpen: false
property var customKeyboardFocus: null
readonly property alias transientSurfaceTracker: _transientSurfaceTracker
property bool useOverlayLayer: false
signal opened
@@ -47,6 +49,10 @@ Item {
readonly property var contentLoader: impl.item ? impl.item.contentLoader : null
readonly property alias modalFocusScope: _modalFocusScope
TransientSurfaceTracker {
id: _transientSurfaceTracker
}
FocusScope {
id: _modalFocusScope
objectName: "modalFocusScope"
@@ -56,7 +62,7 @@ Item {
// Hyprland OnDemand grab delivers keyboard focus to the modal content surface.
HyprlandFocusGrab {
windows: root.contentWindow ? [root.contentWindow] : []
windows: (root.contentWindow ? [root.contentWindow] : []).concat(root.transientSurfaceTracker?.focusWindows ?? [])
active: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus)
property var restoreToplevel: null
@@ -79,11 +85,13 @@ Item {
}
function close() {
transientSurfaceTracker?.closeAll?.();
if (impl.item)
impl.item.close();
}
function instantClose() {
transientSurfaceTracker?.closeAll?.();
if (impl.item && typeof impl.item.instantClose === "function")
impl.item.instantClose();
}
@@ -175,6 +183,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;
}
@@ -35,6 +35,10 @@ Item {
property real _frozenMotionX: 0
property real _frozenMotionY: 0
TransientSurfaceTracker {
id: transientSurfaces
}
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
readonly property var effectiveScreen: contentWindow.screen
readonly property real screenWidth: effectiveScreen?.width ?? 1920
@@ -499,10 +503,18 @@ Item {
}
// Handles hover dismissal grace periods for edge-hover sessions w/cursor
readonly property bool _edgeRetractEnabled: (modalHandle && modalHandle.edgeHoverManaged === true) && spotlightOpen && !isClosing
readonly property bool _edgeRetractEnabled: (modalHandle && modalHandle.edgeHoverManaged === true) && spotlightOpen && !isClosing && !transientSurfaces.active
property bool _edgeBodyHover: false
property bool _edgeArmed: false
on_EdgeRetractEnabledChanged: {
if (!_edgeRetractEnabled) {
_edgeRetractGrace.stop();
} else if (_edgeArmed && !_edgeBodyHover) {
_edgeRetractGrace.restart();
}
}
Timer {
id: _edgeRetractGrace
interval: 150
@@ -534,9 +546,7 @@ Item {
HyprlandFocusGrab {
id: focusGrab
readonly property var contextMenuWindow: root.spotlightContent?.activeContextMenu?.contextWindow ?? null
readonly property bool contextMenuActive: root.spotlightContent?.activeContextMenu?.renderActive ?? false
windows: contextMenuActive && contextMenuWindow ? [contentWindow, contextMenuWindow] : [contentWindow]
windows: [contentWindow].concat(transientSurfaces.focusWindows)
active: root.useHyprlandFocusGrab && root.spotlightOpen
onCleared: {
@@ -869,6 +879,7 @@ Item {
sourceComponent: LauncherContent {
focus: true
parentModal: root
transientSurfaceTracker: transientSurfaces
}
onLoaded: {
@@ -26,6 +26,10 @@ Item {
property string _pendingMode: ""
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
TransientSurfaceTracker {
id: transientSurfaces
}
readonly property var effectiveScreen: launcherWindow.screen
readonly property real screenWidth: effectiveScreen?.width ?? 1920
readonly property real screenHeight: effectiveScreen?.height ?? 1080
@@ -228,9 +232,7 @@ Item {
HyprlandFocusGrab {
id: focusGrab
readonly property var contextMenuWindow: root.spotlightContent?.activeContextMenu?.contextWindow ?? null
readonly property bool contextMenuActive: root.spotlightContent?.activeContextMenu?.renderActive ?? false
windows: contextMenuActive && contextMenuWindow ? [launcherWindow, contextMenuWindow] : [launcherWindow]
windows: [launcherWindow].concat(transientSurfaces.focusWindows)
active: root.useHyprlandFocusGrab && root.keyboardActive
onCleared: {
if (spotlightOpen)
@@ -482,6 +484,7 @@ Item {
sourceComponent: SpotlightLauncherContent {
focus: true
parentModal: root
transientSurfaceTracker: transientSurfaces
}
onLoaded: {
@@ -27,6 +27,10 @@ Item {
readonly property bool unloadContentOnClose: SettingsData.dankLauncherV2UnloadOnClose
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
TransientSurfaceTracker {
id: transientSurfaces
}
readonly property var effectiveScreen: launcherWindow.screen
readonly property real screenWidth: effectiveScreen?.width ?? 1920
readonly property real screenHeight: effectiveScreen?.height ?? 1080
@@ -260,9 +264,7 @@ Item {
HyprlandFocusGrab {
id: focusGrab
readonly property var contextMenuWindow: root.spotlightContent?.activeContextMenu?.contextWindow ?? null
readonly property bool contextMenuActive: root.spotlightContent?.activeContextMenu?.renderActive ?? false
windows: contextMenuActive && contextMenuWindow ? [launcherWindow, contextMenuWindow] : [launcherWindow]
windows: [launcherWindow].concat(transientSurfaces.focusWindows)
active: root.useHyprlandFocusGrab && root.keyboardActive
onCleared: {
@@ -530,6 +532,7 @@ Item {
sourceComponent: LauncherContent {
focus: true
parentModal: root
transientSurfaceTracker: transientSurfaces
}
onLoaded: {
@@ -18,6 +18,7 @@ FocusScope {
property alias resultsList: resultsList
property alias actionPanel: actionPanel
readonly property alias activeContextMenu: contextMenu
property var transientSurfaceTracker: null
property bool editMode: false
property var editingApp: null
@@ -43,7 +44,7 @@ FocusScope {
}
function closeTransientUi() {
contextMenu.hide();
transientSurfaceTracker?.closeAll?.();
actionPanel.hide();
root.enabled = true;
}
@@ -123,6 +124,7 @@ FocusScope {
controller: root.controller
searchField: root.searchField
parentHandler: root
transientSurfaceTracker: root.transientSurfaceTracker
onEditAppRequested: app => {
root.openEditMode(app);
@@ -779,6 +781,7 @@ FocusScope {
anchors.fill: parent
controller: root.controller
leadingSectionHeaderAtBottom: contentHolder.inverted
transientSurfaceTracker: root.transientSurfaceTracker
onItemRightClicked: (index, item, sceneX, sceneY) => {
if (item && contextMenu.hasContextMenuActions(item)) {
@@ -25,6 +25,7 @@ Item {
property real anchorY: 0
property bool openState: false
property bool renderActive: false
property var transientSurfaceTracker: null
readonly property alias contextWindow: menuWindow
readonly property bool blurActive: renderActive && openState && BlurService.enabled && Theme.connectedSurfaceBlurEnabled
@@ -49,6 +50,18 @@ Item {
signal hideRequested
signal editAppRequested(var app)
onRenderActiveChanged: transientSurfaceTracker?.setActive(root, renderActive, contextWindow)
Component.onDestruction: transientSurfaceTracker?.unregister(root)
Connections {
target: root.transientSurfaceTracker
ignoreUnknownSignals: true
function onCloseRequested() {
root.hide();
}
}
TextMetrics {
id: menuTextMetrics
text: root.longestMenuText
@@ -15,6 +15,7 @@ Item {
property var _visualRows: []
property var _flatIndexToRowMap: ({})
property var _cumulativeHeights: []
property var transientSurfaceTracker: null
readonly property bool _bottomSectionHeaderActive: leadingSectionHeaderAtBottom && (controller?.sections?.length ?? 0) > 0
signal itemRightClicked(int index, var item, real mouseX, real mouseY)
@@ -259,6 +260,7 @@ Item {
return root.controller?.canChangeSectionViewMode(delegateRoot.modelData?.sectionId ?? "") ?? false;
}
canCollapse: root.controller?.canCollapseSection(delegateRoot.modelData?.sectionId ?? "") ?? false
transientSurfaceTracker: root.transientSurfaceTracker
}
ResultItem {
@@ -447,6 +449,7 @@ Item {
return root.controller?.canCollapseSection(stickyHeader.stickyHeaderSection?.id) ?? false;
}
isSticky: true
transientSurfaceTracker: root.transientSurfaceTracker
}
}
@@ -472,6 +475,7 @@ Item {
canCollapse: root.controller?.canCollapseSection(section?.id ?? "") ?? false
isSticky: true
popupAbove: true
transientSurfaceTracker: root.transientSurfaceTracker
}
Item {
@@ -17,9 +17,21 @@ Rectangle {
property bool isSticky: false
property bool popupAbove: false
property Item popupAboveItem: null
property var transientSurfaceTracker: null
signal viewModeToggled
Component.onDestruction: transientSurfaceTracker?.unregister(root)
Connections {
target: root.transientSurfaceTracker
ignoreUnknownSignals: true
function onCloseRequested() {
categoryPopup.close();
}
}
width: parent?.width ?? 200
height: 32
color: isSticky ? Theme.withAlpha(Theme.surfaceHover, 0) : (hoverArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.surfaceHover, 0))
@@ -142,6 +154,8 @@ Rectangle {
dim: false
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
onVisibleChanged: root.transientSurfaceTracker?.setActive(root, visible, null)
background: Rectangle {
color: "transparent"
}
@@ -12,6 +12,7 @@ FocusScope {
property alias searchField: searchInput
property alias controller: searchController
readonly property alias activeContextMenu: contextMenu
property var transientSurfaceTracker: null
readonly property bool _hasQuery: searchInput.text.length > 0
readonly property real _searchBarH: 56
@@ -48,7 +49,7 @@ FocusScope {
}
function closeTransientUi() {
contextMenu.hide();
transientSurfaceTracker?.closeAll?.();
root.enabled = true;
}
@@ -220,6 +221,7 @@ FocusScope {
searchField: searchInput
parentHandler: root
allowEditActions: false
transientSurfaceTracker: root.transientSurfaceTracker
}
Connections {
+3
View File
@@ -210,12 +210,14 @@ DankModal {
NotificationHeader {
id: notificationHeader
keyboardController: modalKeyboardController
transientSurfaceTracker: notificationModal.transientSurfaceTracker
onCurrentTabChanged: notificationModal.currentTab = currentTab
Component.onCompleted: notificationModal.notificationHeaderRef = notificationHeader
}
NotificationSettings {
id: notificationSettings
transientSurfaceTracker: notificationModal.transientSurfaceTracker
expanded: notificationHeader.showSettings
}
@@ -225,6 +227,7 @@ DankModal {
height: parent.height - y
visible: notificationHeader.currentTab === 0
keyboardController: modalKeyboardController
transientSurfaceTracker: notificationModal.transientSurfaceTracker
Component.onCompleted: {
notificationModal.notificationListRef = notificationList;
if (modalKeyboardController) {