hoverpanel: free-floating drag (2d, drop mask during drag), extend popup grace to 1500ms, loosen click-outside bounds

This commit is contained in:
Damocles 2026-04-16 23:12:58 +02:00
parent 434f8f8ffd
commit 2e881de130

View file

@ -44,16 +44,19 @@ PanelWindow {
property bool _winVisible: false property bool _winVisible: false
property bool _pinned: false property bool _pinned: false
property real _dragStartX: 0 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. // When pinned: mask = full panel so content is interactive, desktop accessible outside.
// Full header width allows drag-to-reposition; pin button tap is within that region too. // When dragging: mask = null (full screen) so Niri keeps delivering events when cursor
mask: _pinned ? _pinMask : null // leaves the panel bounds mid-drag.
mask: (_pinned && !_dragging) ? _pinMask : null
property Region _pinMask: Region { property Region _pinMask: Region {
x: panelContainer.x x: panelContainer.x
y: 0 y: panelContainer.y
width: panelContainer.width width: panelContainer.width
height: 24 height: panelContainer.height
} }
WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.layer: WlrLayer.Overlay
@ -84,10 +87,12 @@ PanelWindow {
// Grace period: after _show(), suppress auto-close briefly so Niri has time // 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). // 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 property bool _grace: false
Timer { Timer {
id: _graceTimer id: _graceTimer
interval: 400 interval: root.popupMode ? 1500 : 400
onTriggered: { onTriggered: {
root._grace = false; root._grace = false;
if (!root.showPanel && !root._pinned) if (!root.showPanel && !root._pinned)
@ -203,7 +208,8 @@ PanelWindow {
enabled: !root._grace enabled: !root._grace
onTapped: { onTapped: {
const p = point.position; 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(); root.dismiss();
} }
} }
@ -244,16 +250,24 @@ PanelWindow {
width: parent.width width: parent.width
height: 24 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 { DragHandler {
enabled: root._pinned && !root.popupMode enabled: root._pinned && !root.popupMode
yAxis.enabled: false onActiveChanged: {
onActiveChanged: if (active) root._dragging = active;
root._dragStartX = panelContainer.x if (active) {
root._dragStartX = panelContainer.x;
root._dragStartY = panelContainer.y;
}
}
onActiveTranslationChanged: { onActiveTranslationChanged: {
if (active) { if (active) {
const sw = root.screen?.width ?? 1920; 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.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 // Show move cursor on header when pinned
HoverHandler { HoverHandler {
enabled: root._pinned && !root.popupMode enabled: root._pinned && !root.popupMode
cursorShape: Qt.SizeHorCursor cursorShape: Qt.SizeAllCursor
} }
Text { Text {