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:
@@ -79,12 +79,6 @@ Singleton {
|
||||
|
||||
function _closePopout(popout) {
|
||||
try {
|
||||
if (popout?.hoverDismissEnabled) {
|
||||
if (typeof popout.closeFromHoverDismiss === "function") {
|
||||
popout.closeFromHoverDismiss();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (popout.hoverDismissEnabled !== undefined)
|
||||
popout.hoverDismissEnabled = false;
|
||||
switch (true) {
|
||||
@@ -104,6 +98,20 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function _dismissPopoutFromHover(popout) {
|
||||
try {
|
||||
if (!popout || popout.hoverDismissEnabled !== true)
|
||||
return;
|
||||
if (typeof popout.closeFromHoverDismiss === "function") {
|
||||
popout.closeFromHoverDismiss();
|
||||
return;
|
||||
}
|
||||
_closePopout(popout);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function _isStale(popout) {
|
||||
try {
|
||||
if (!popout || !("shouldBeVisible" in popout))
|
||||
@@ -174,6 +182,19 @@ Singleton {
|
||||
_closePopout(popout);
|
||||
}
|
||||
|
||||
function dismissHoverPopoutForScreen(screen) {
|
||||
if (!screen)
|
||||
return;
|
||||
const screenName = screen.name;
|
||||
const popout = currentPopoutsByScreen[screenName];
|
||||
if (!popout || _isStale(popout)) {
|
||||
currentPopoutsByScreen[screenName] = null;
|
||||
currentPopoutTriggers[screenName] = null;
|
||||
return;
|
||||
}
|
||||
_dismissPopoutFromHover(popout);
|
||||
}
|
||||
|
||||
function cancelHoverDismiss(screen) {
|
||||
const popout = getActivePopout(screen);
|
||||
if (popout?.cancelHoverDismiss)
|
||||
@@ -191,7 +212,8 @@ Singleton {
|
||||
const p = getActivePopout(screen);
|
||||
if (!p || !_isPopoutPresented(p))
|
||||
return false;
|
||||
return p.hoverDismissEnabled === false || p.hoverDismissSuspended === true;
|
||||
const dismissSuspended = p.effectiveHoverDismissSuspended ?? p.hoverDismissSuspended;
|
||||
return p.hoverDismissEnabled === false || dismissSuspended === true;
|
||||
}
|
||||
|
||||
function isCurrentPopout(popout, screenName) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -122,6 +122,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
parentModal: modalAdapter
|
||||
viewModeContext: "appDrawer"
|
||||
transientSurfaceTracker: appDrawerPopout.transientSurfaceTracker
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
|
||||
@@ -608,7 +608,7 @@ Item {
|
||||
function closeHoverSurfaces() {
|
||||
_closeHoverNotepad();
|
||||
activeHoverTrigger = "";
|
||||
PopoutManager.closePopoutForScreen(barWindow?.screen);
|
||||
PopoutManager.dismissHoverPopoutForScreen(barWindow?.screen);
|
||||
TrayMenuManager.closeAllMenus();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ DankListView {
|
||||
property int swipingCardIndex: -1
|
||||
property real swipingCardOffset: 0
|
||||
property bool _stableHeightUpdatePending: false
|
||||
property var transientSurfaceTracker: null
|
||||
readonly property real shadowBlurPx: Theme.elevationEnabled ? ((Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined) ? Theme.elevationLevel1.blurPx : 4) : 0
|
||||
readonly property real shadowHorizontalGutter: Theme.snap(Math.max(Theme.spacingS, Math.min(32, shadowBlurPx * 1.5 + 6)), 1)
|
||||
readonly property real shadowVerticalGutter: Theme.snap(Math.max(Theme.spacingXS, 6), 1)
|
||||
@@ -173,6 +174,7 @@ DankListView {
|
||||
notificationGroup: modelData
|
||||
keyboardNavigationActive: listView.keyboardActive
|
||||
animateExpansion: listView.cardAnimateExpansion && listView.listInitialized
|
||||
transientSurfaceTracker: listView.transientSurfaceTracker
|
||||
opacity: {
|
||||
const swipeAmount = Math.abs(delegateRoot.swipeOffset);
|
||||
if (swipeAmount <= delegateRoot.swipeFadeStartOffset)
|
||||
|
||||
@@ -30,6 +30,7 @@ Rectangle {
|
||||
property real swipingNotificationOffset: 0
|
||||
property real listLevelAdjacentScaleInfluence: 1.0
|
||||
property bool listLevelScaleAnimationsEnabled: true
|
||||
property var transientSurfaceTracker: null
|
||||
|
||||
readonly property bool compactMode: SettingsData.notificationCompactMode
|
||||
readonly property real cardPadding: compactMode ? Theme.notificationCardPaddingCompact : Theme.notificationCardPadding
|
||||
@@ -63,6 +64,20 @@ Rectangle {
|
||||
});
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
transientSurfaceTracker?.unregister(root);
|
||||
notificationCardContextMenu.close();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.transientSurfaceTracker
|
||||
ignoreUnknownSignals: true
|
||||
|
||||
function onCloseRequested() {
|
||||
notificationCardContextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
function expansionMotionDuration() {
|
||||
if (isDescriptionToggleAnimation)
|
||||
return descriptionExpanded ? Theme.notificationInlineExpandDuration : Theme.notificationInlineCollapseDuration;
|
||||
@@ -1059,6 +1074,8 @@ Rectangle {
|
||||
width: 220
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
onVisibleChanged: root.transientSurfaceTracker?.setActive(root, visible, null)
|
||||
|
||||
background: Rectangle {
|
||||
color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
radius: Theme.cornerRadius
|
||||
|
||||
@@ -283,11 +283,13 @@ DankPopout {
|
||||
NotificationHeader {
|
||||
id: notificationHeader
|
||||
objectName: "notificationHeader"
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
onHeightChanged: notificationContent.cachedHeaderHeight = height
|
||||
}
|
||||
|
||||
NotificationSettings {
|
||||
id: notificationSettings
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
expanded: notificationHeader.showSettings
|
||||
maxAllowedHeight: notificationContent.settingsMaxHeight
|
||||
}
|
||||
@@ -306,6 +308,7 @@ DankPopout {
|
||||
anchors.topMargin: -(shadowVerticalGutter + delegateShadowGutter / 2)
|
||||
anchors.bottomMargin: -(shadowVerticalGutter + delegateShadowGutter / 2)
|
||||
cardAnimateExpansion: true
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ Item {
|
||||
property bool showSettings: false
|
||||
property int currentTab: 0
|
||||
property bool showDndMenu: false
|
||||
property var transientSurfaceTracker: null
|
||||
|
||||
onShowDndMenuChanged: transientSurfaceTracker?.setActive(root, showDndMenu, null)
|
||||
Component.onDestruction: transientSurfaceTracker?.unregister(root)
|
||||
|
||||
Connections {
|
||||
target: root.transientSurfaceTracker
|
||||
ignoreUnknownSignals: true
|
||||
|
||||
function onCloseRequested() {
|
||||
root.showDndMenu = false;
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentTabChanged: {
|
||||
if (currentTab === 1 && !SettingsData.notificationHistoryEnabled)
|
||||
|
||||
@@ -7,6 +7,7 @@ Rectangle {
|
||||
|
||||
property bool expanded: false
|
||||
property real maxAllowedHeight: 0
|
||||
property var transientSurfaceTracker: null
|
||||
readonly property real naturalContentHeight: contentColumn.height + Theme.spacingL * 2
|
||||
|
||||
width: parent.width
|
||||
@@ -178,6 +179,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: lowTimeoutDropdown
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
text: I18n.tr("Low Priority")
|
||||
description: I18n.tr("Timeout for low priority notifications")
|
||||
currentValue: getTimeoutText(SettingsData.notificationTimeoutLow)
|
||||
@@ -193,6 +196,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: normalTimeoutDropdown
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
text: I18n.tr("Normal Priority")
|
||||
description: I18n.tr("Timeout for normal priority notifications")
|
||||
currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal)
|
||||
@@ -208,6 +213,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: criticalTimeoutDropdown
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
text: I18n.tr("Critical Priority")
|
||||
description: I18n.tr("Timeout for critical priority notifications")
|
||||
currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical)
|
||||
|
||||
@@ -11,12 +11,25 @@ PanelWindow {
|
||||
property string appName: ""
|
||||
property string desktopEntry: ""
|
||||
property point anchorPos: Qt.point(0, 0)
|
||||
property var transientSurfaceTracker: null
|
||||
|
||||
signal muted
|
||||
signal dismissRequested
|
||||
|
||||
readonly property bool isMuted: SettingsData.isAppMuted(appName, desktopEntry)
|
||||
|
||||
onVisibleChanged: transientSurfaceTracker?.setActive(root, visible, root)
|
||||
Component.onDestruction: transientSurfaceTracker?.unregister(root)
|
||||
|
||||
Connections {
|
||||
target: root.transientSurfaceTracker
|
||||
ignoreUnknownSignals: true
|
||||
|
||||
function onCloseRequested() {
|
||||
root.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
function showAt(x, y, targetScreen) {
|
||||
if (targetScreen)
|
||||
screen = targetScreen;
|
||||
|
||||
@@ -70,6 +70,11 @@ PanelWindow {
|
||||
property real _storedTopMargin: 0
|
||||
property real _storedBottomMargin: 0
|
||||
property bool _inlineGeometryReady: false
|
||||
readonly property bool contextMenuActive: transientSurfaces.active
|
||||
|
||||
TransientSurfaceTracker {
|
||||
id: transientSurfaces
|
||||
}
|
||||
readonly property bool directionalEffect: Theme.isDirectionalEffect
|
||||
readonly property bool depthEffect: Theme.isDepthEffect
|
||||
readonly property real entryTravel: {
|
||||
@@ -122,6 +127,7 @@ PanelWindow {
|
||||
if (exiting || _isDestroying) {
|
||||
return;
|
||||
}
|
||||
closeTransientUi();
|
||||
exiting = true;
|
||||
exitStarted();
|
||||
popupChromeGeometryChanged();
|
||||
@@ -135,6 +141,7 @@ PanelWindow {
|
||||
if (_isDestroying) {
|
||||
return;
|
||||
}
|
||||
closeTransientUi();
|
||||
_isDestroying = true;
|
||||
exiting = true;
|
||||
visible = false;
|
||||
@@ -147,6 +154,7 @@ PanelWindow {
|
||||
return;
|
||||
}
|
||||
|
||||
closeTransientUi();
|
||||
_finalized = true;
|
||||
_isDestroying = true;
|
||||
exitWatchdog.stop();
|
||||
@@ -155,6 +163,11 @@ PanelWindow {
|
||||
win.exitFinished();
|
||||
}
|
||||
|
||||
function closeTransientUi() {
|
||||
transientSurfaces.closeAll();
|
||||
popupContextMenuLoader.active = false;
|
||||
}
|
||||
|
||||
visible: !_finalized
|
||||
WlrLayershell.layer: {
|
||||
const shouldUseOverlay = notificationData && (SettingsData.notificationOverlayEnabled || notificationData.urgency === NotificationUrgency.Critical);
|
||||
@@ -274,6 +287,7 @@ PanelWindow {
|
||||
}
|
||||
onNotificationDataChanged: {
|
||||
if (!_isDestroying) {
|
||||
closeTransientUi();
|
||||
if (SettingsData.notificationPopupPrivacyMode)
|
||||
descriptionExpanded = false;
|
||||
wrapperConn.target = win.notificationData || null;
|
||||
@@ -286,6 +300,7 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
Component.onDestruction: {
|
||||
closeTransientUi();
|
||||
_isDestroying = true;
|
||||
exitWatchdog.stop();
|
||||
if (notificationData && notificationData.timer) {
|
||||
@@ -716,7 +731,7 @@ PanelWindow {
|
||||
if (cardHoverHandler.hovered) {
|
||||
if (notificationData.timer)
|
||||
notificationData.timer.stop();
|
||||
} else if (notificationData.popup && notificationData.timer) {
|
||||
} else if (!win.contextMenuActive && notificationData.popup && notificationData.timer) {
|
||||
notificationData.timer.restart();
|
||||
}
|
||||
}
|
||||
@@ -1357,11 +1372,26 @@ PanelWindow {
|
||||
interval: 160
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (notificationData && notificationData.timer && !exiting && !_isDestroying)
|
||||
if (notificationData && notificationData.timer && !contextMenuActive && !exiting && !_isDestroying)
|
||||
notificationData.timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: popupContextMenuLoader.item
|
||||
ignoreUnknownSignals: true
|
||||
|
||||
function onVisibleChanged() {
|
||||
if (!notificationData?.timer || exiting || _isDestroying)
|
||||
return;
|
||||
if (win.contextMenuActive) {
|
||||
notificationData.timer.stop();
|
||||
} else if (!cardHoverHandler.hovered && notificationData.popup) {
|
||||
notificationData.timer.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: exitWatchdog
|
||||
|
||||
@@ -1387,6 +1417,7 @@ PanelWindow {
|
||||
active: false
|
||||
|
||||
sourceComponent: NotificationContextMenu {
|
||||
transientSurfaceTracker: transientSurfaces
|
||||
appName: notificationData?.appName ?? ""
|
||||
desktopEntry: notificationData?.desktopEntry ?? ""
|
||||
onMuted: {
|
||||
|
||||
@@ -12,10 +12,23 @@ Popup {
|
||||
property int selectedIndex: -1
|
||||
property bool keyboardNavigation: false
|
||||
property var parentFocusItem: null
|
||||
property var transientSurfaceTracker: null
|
||||
|
||||
signal menuClosed
|
||||
signal processKilled
|
||||
|
||||
onVisibleChanged: transientSurfaceTracker?.setActive(root, visible, null)
|
||||
Component.onDestruction: transientSurfaceTracker?.unregister(root)
|
||||
|
||||
Connections {
|
||||
target: processContextMenu.transientSurfaceTracker
|
||||
ignoreUnknownSignals: true
|
||||
|
||||
function onCloseRequested() {
|
||||
processContextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var menuItems: [
|
||||
{
|
||||
text: I18n.tr("Copy PID"),
|
||||
|
||||
@@ -66,6 +66,7 @@ DankPopout {
|
||||
|
||||
ProcessContextMenu {
|
||||
id: processContextMenu
|
||||
transientSurfaceTracker: processListPopout.transientSurfaceTracker
|
||||
}
|
||||
|
||||
content: Component {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user