diff --git a/modules/HoverPanel.qml b/modules/HoverPanel.qml index 59f1e44..2b3a80b 100644 --- a/modules/HoverPanel.qml +++ b/modules/HoverPanel.qml @@ -44,16 +44,19 @@ PanelWindow { property bool _winVisible: false property bool _pinned: false property real _dragStartX: 0 + property real _dragStartY: 0 + property bool _dragging: false - // When pinned, only the header bar receives input — content passes through to desktop. - // Full header width allows drag-to-reposition; pin button tap is within that region too. - mask: _pinned ? _pinMask : null + // When pinned: mask = full panel so content is interactive, desktop accessible outside. + // When dragging: mask = null (full screen) so Niri keeps delivering events when cursor + // leaves the panel bounds mid-drag. + mask: (_pinned && !_dragging) ? _pinMask : null property Region _pinMask: Region { x: panelContainer.x - y: 0 + y: panelContainer.y width: panelContainer.width - height: 24 + height: panelContainer.height } WlrLayershell.layer: WlrLayer.Overlay @@ -84,10 +87,12 @@ PanelWindow { // Grace period: after _show(), suppress auto-close briefly so Niri has time // to route wl_pointer.enter to the new overlay surface (cursor may be stationary). + // Popup mode gets a longer window (1500ms) so the user can move the cursor to the + // panel content after clicking the bar without accidentally dismissing it. property bool _grace: false Timer { id: _graceTimer - interval: 400 + interval: root.popupMode ? 1500 : 400 onTriggered: { root._grace = false; if (!root.showPanel && !root._pinned) @@ -203,7 +208,8 @@ PanelWindow { enabled: !root._grace onTapped: { const p = point.position; - if (p.x < panelContainer.x || p.x > panelContainer.x + panelContainer.width || p.y < panelContainer.y || p.y > panelContainer.y + panelContainer.height) + const pad = 8; + if (p.x < panelContainer.x - pad || p.x > panelContainer.x + panelContainer.width + pad || p.y < panelContainer.y - pad || p.y > panelContainer.y + panelContainer.height + pad) root.dismiss(); } } @@ -244,16 +250,24 @@ PanelWindow { width: parent.width height: 24 - // Drag header to reposition panel while pinned (hover mode only) + // Drag header to freely reposition panel while pinned (hover mode only). + // _dragging clears the input mask so Niri keeps delivering events when the + // cursor leaves the panel bounds during a fast drag. DragHandler { enabled: root._pinned && !root.popupMode - yAxis.enabled: false - onActiveChanged: if (active) - root._dragStartX = panelContainer.x + onActiveChanged: { + root._dragging = active; + if (active) { + root._dragStartX = panelContainer.x; + root._dragStartY = panelContainer.y; + } + } onActiveTranslationChanged: { if (active) { const sw = root.screen?.width ?? 1920; + const sh = root.screen?.height ?? 1080; panelContainer.x = Math.max(0, Math.min(root._dragStartX + activeTranslation.x, sw - root.contentWidth)); + panelContainer.y = Math.max(0, Math.min(root._dragStartY + activeTranslation.y, sh - panelContainer.height)); } } } @@ -261,7 +275,7 @@ PanelWindow { // Show move cursor on header when pinned HoverHandler { enabled: root._pinned && !root.popupMode - cursorShape: Qt.SizeHorCursor + cursorShape: Qt.SizeAllCursor } Text {