mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
DockMenu: auto hides when not hovering the menu, simplified with a single mouse area.
This commit is contained in:
+90
-81
@@ -14,9 +14,12 @@ PopupWindow {
|
||||
property var toplevel: null
|
||||
property Item anchorItem: null
|
||||
property real scaling: 1.0
|
||||
property bool hovered: menuMouseArea.containsMouse || activateMouseArea.containsMouse || pinMouseArea.containsMouse || closeMouseArea.containsMouse
|
||||
property bool hovered: menuMouseArea.containsMouse
|
||||
property var onAppClosed: null // Callback function for when an app is closed
|
||||
|
||||
// Track which menu item is hovered
|
||||
property int hoveredItem: -1 // -1: none, 0: focus, 1: pin, 2: close
|
||||
|
||||
signal requestClose
|
||||
|
||||
implicitWidth: 140 * scaling
|
||||
@@ -70,25 +73,64 @@ PopupWindow {
|
||||
visible = false
|
||||
}
|
||||
|
||||
// Close menu when clicking on background, track hover for the whole menu area
|
||||
MouseArea {
|
||||
id: menuMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: function (mouse) {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
root.hide() // Close on right-click
|
||||
} else {
|
||||
root.hide() // Close when clicking on the background (outside menu content)
|
||||
// Helper function to determine which menu item is under the mouse
|
||||
function getHoveredItem(mouseY) {
|
||||
const itemHeight = 32 * scaling
|
||||
const startY = Style.marginM * scaling
|
||||
const relativeY = mouseY - startY
|
||||
|
||||
if (relativeY < 0)
|
||||
return -1
|
||||
|
||||
const itemIndex = Math.floor(relativeY / itemHeight)
|
||||
return itemIndex >= 0 && itemIndex < 3 ? itemIndex : -1
|
||||
}
|
||||
|
||||
// Handle menu item clicks
|
||||
function handleItemClick(itemIndex) {
|
||||
switch (itemIndex) {
|
||||
case 0:
|
||||
// Focus
|
||||
if (root.toplevel?.activate) {
|
||||
root.toplevel.activate()
|
||||
}
|
||||
root.requestClose()
|
||||
break
|
||||
case 1:
|
||||
// Pin/Unpin
|
||||
if (root.toplevel?.appId) {
|
||||
root.toggleAppPin(root.toplevel.appId)
|
||||
}
|
||||
root.requestClose()
|
||||
break
|
||||
case 2:
|
||||
// Close
|
||||
// Check if toplevel is still valid before trying to close it
|
||||
const isValidToplevel = root.toplevel && ToplevelManager && ToplevelManager.toplevels.values.includes(root.toplevel)
|
||||
|
||||
if (isValidToplevel && root.toplevel.close) {
|
||||
root.toplevel.close()
|
||||
// Trigger immediate dock update callback if provided
|
||||
if (root.onAppClosed && typeof root.onAppClosed === "function") {
|
||||
Qt.callLater(root.onAppClosed)
|
||||
}
|
||||
} else {
|
||||
Logger.warn("DockMenu", "Cannot close app - invalid toplevel reference")
|
||||
}
|
||||
root.hide()
|
||||
root.requestClose()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequences: ["Escape"]
|
||||
enabled: root.visible
|
||||
onActivated: root.hide()
|
||||
Timer {
|
||||
id: closeTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -98,12 +140,32 @@ PopupWindow {
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Prevent clicks inside the menu from closing it
|
||||
// Single MouseArea to handle both auto-close and menu interactions
|
||||
MouseArea {
|
||||
id: menuMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
hoverEnabled: true
|
||||
cursorShape: root.hoveredItem >= 0 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
|
||||
} // Do nothing, just consume the click
|
||||
onEntered: {
|
||||
closeTimer.stop()
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.hoveredItem = -1
|
||||
closeTimer.start()
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
root.hoveredItem = root.getHoveredItem(mouse.y)
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
const clickedItem = root.getHoveredItem(mouse.y)
|
||||
if (clickedItem >= 0) {
|
||||
root.handleItemClick(clickedItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -116,7 +178,7 @@ PopupWindow {
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 32 * scaling
|
||||
color: activateMouseArea.containsMouse ? Color.mTertiary : Color.transparent
|
||||
color: root.hoveredItem === 0 ? Color.mTertiary : Color.transparent
|
||||
radius: Style.radiusXS * scaling
|
||||
|
||||
Row {
|
||||
@@ -128,38 +190,24 @@ PopupWindow {
|
||||
NIcon {
|
||||
icon: "eye"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: activateMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === 0 ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("dock.menu.focus")
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: activateMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === 0 ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: activateMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
if (root.toplevel?.activate) {
|
||||
root.toplevel.activate()
|
||||
}
|
||||
root.requestClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pin/Unpin item
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 32 * scaling
|
||||
color: pinMouseArea.containsMouse ? Color.mTertiary : Color.transparent
|
||||
color: root.hoveredItem === 1 ? Color.mTertiary : Color.transparent
|
||||
radius: Style.radiusXS * scaling
|
||||
|
||||
Row {
|
||||
@@ -175,7 +223,7 @@ PopupWindow {
|
||||
return root.isAppPinned(root.toplevel.appId) ? "unpin" : "pin"
|
||||
}
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: pinMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === 1 ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -186,32 +234,17 @@ PopupWindow {
|
||||
return root.isAppPinned(root.toplevel.appId) ? I18n.tr("dock.menu.unpin") : I18n.tr("dock.menu.pin")
|
||||
}
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: pinMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === 1 ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pinMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
if (root.toplevel?.appId) {
|
||||
root.toggleAppPin(root.toplevel.appId)
|
||||
}
|
||||
//root.hide()
|
||||
root.requestClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close item
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 32 * scaling
|
||||
color: closeMouseArea.containsMouse ? Color.mTertiary : Color.transparent
|
||||
color: root.hoveredItem === 2 ? Color.mTertiary : Color.transparent
|
||||
radius: Style.radiusXS * scaling
|
||||
|
||||
Row {
|
||||
@@ -223,41 +256,17 @@ PopupWindow {
|
||||
NIcon {
|
||||
icon: "close"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: closeMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === 2 ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("dock.menu.close")
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: closeMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === 2 ? Color.mOnTertiary : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
// Check if toplevel is still valid before trying to close it
|
||||
const isValidToplevel = root.toplevel && ToplevelManager && ToplevelManager.toplevels.values.includes(root.toplevel)
|
||||
|
||||
if (isValidToplevel && root.toplevel.close) {
|
||||
root.toplevel.close()
|
||||
// Trigger immediate dock update callback if provided
|
||||
if (root.onAppClosed && typeof root.onAppClosed === "function") {
|
||||
Qt.callLater(root.onAppClosed)
|
||||
}
|
||||
} else {
|
||||
Logger.warn("DockMenu", "Cannot close app - invalid toplevel reference")
|
||||
}
|
||||
root.hide()
|
||||
root.requestClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user