hoverpanel: fix popup dismiss race, replace click-outside mousearea with taphandler + grace guard; pinned panels draggable by header

This commit is contained in:
Damocles 2026-04-16 21:10:10 +02:00
parent a878067e16
commit 434f8f8ffd

View file

@ -43,14 +43,16 @@ PanelWindow {
property bool _winVisible: false property bool _winVisible: false
property bool _pinned: false property bool _pinned: false
property real _dragStartX: 0
// When pinned, only the pin button area receives input everything else passes through // 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 mask: _pinned ? _pinMask : null
property Region _pinMask: Region { property Region _pinMask: Region {
x: panelContainer.x + panelContainer.width - 28 x: panelContainer.x
y: 0 y: 0
width: 28 width: panelContainer.width
height: 24 height: 24
} }
@ -190,12 +192,21 @@ PanelWindow {
} }
} }
// Popup mode: click-outside dismiss (declared first = lowest z = below content) // Popup mode: click-outside dismiss.
MouseArea { // TapHandler fires for all taps; position check skips taps inside panelContainer.
// Gated on !_grace so spurious events during the 400ms opening window don't dismiss.
Item {
anchors.fill: parent anchors.fill: parent
visible: root.popupMode visible: root.popupMode
enabled: root.popupMode
onClicked: root.dismiss() TapHandler {
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)
root.dismiss();
}
}
} }
M.PopupBackground { M.PopupBackground {
@ -215,13 +226,6 @@ PanelWindow {
height: _panelColumn.height height: _panelColumn.height
opacity: 0 opacity: 0
// Popup mode: eat clicks on panel background so outer dismiss doesn't fire
MouseArea {
anchors.fill: parent
visible: root.popupMode
enabled: root.popupMode
}
HoverHandler { HoverHandler {
enabled: !root.popupMode && !root._pinned enabled: !root.popupMode && !root._pinned
onHoveredChanged: if (!root.popupMode && !root._pinned) onHoveredChanged: if (!root.popupMode && !root._pinned)
@ -235,10 +239,31 @@ PanelWindow {
// Header row: title + action buttons + pin shown in hover mode always, // Header row: title + action buttons + pin shown in hover mode always,
// and in popup mode when a title or actions are provided. // and in popup mode when a title or actions are provided.
Item { Item {
id: _headerItem
visible: !root.popupMode || root.panelTitle !== "" || root.titleActionsComponent !== null visible: !root.popupMode || root.panelTitle !== "" || root.titleActionsComponent !== null
width: parent.width width: parent.width
height: 24 height: 24
// Drag header to reposition panel while pinned (hover mode only)
DragHandler {
enabled: root._pinned && !root.popupMode
yAxis.enabled: false
onActiveChanged: if (active)
root._dragStartX = panelContainer.x
onActiveTranslationChanged: {
if (active) {
const sw = root.screen?.width ?? 1920;
panelContainer.x = Math.max(0, Math.min(root._dragStartX + activeTranslation.x, sw - root.contentWidth));
}
}
}
// Show move cursor on header when pinned
HoverHandler {
enabled: root._pinned && !root.popupMode
cursorShape: Qt.SizeHorCursor
}
Text { Text {
visible: root.panelTitle !== "" visible: root.panelTitle !== ""
anchors.left: parent.left anchors.left: parent.left