quickshell: apply dropdown pattern to tray context menu
Same morphing animation, concave corners, and dismiss behavior as the calendar popup. Menu drops down from bar at icon position. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3b7cc98fc9
commit
0aba95779a
1 changed files with 95 additions and 20 deletions
|
|
@ -714,7 +714,10 @@ in
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
onClicked: (event) => {
|
onClicked: (event) => {
|
||||||
|
if (contextMenu.justDismissed) return;
|
||||||
if (event.button === Qt.RightButton && modelData.hasMenu) {
|
if (event.button === Qt.RightButton && modelData.hasMenu) {
|
||||||
|
let pos = parent.mapToItem(bar.contentItem, parent.width / 2, 0);
|
||||||
|
contextMenu.menuX = pos.x;
|
||||||
contextMenu.trayItem = modelData;
|
contextMenu.trayItem = modelData;
|
||||||
menuOpener.menu = modelData.menu;
|
menuOpener.menu = modelData.menu;
|
||||||
contextMenu.visible = true;
|
contextMenu.visible = true;
|
||||||
|
|
@ -732,32 +735,112 @@ in
|
||||||
PopupWindow {
|
PopupWindow {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
property var trayItem: null
|
property var trayItem: null
|
||||||
anchor.item: trayArea
|
property bool open: false
|
||||||
anchor.edges: Edges.Bottom | Edges.Right
|
property bool justDismissed: false
|
||||||
anchor.gravity: Edges.Bottom | Edges.Left
|
property real menuX: 0
|
||||||
|
property real fullWidth: menuItems.width + 16
|
||||||
|
property real fullHeight: menuItems.height + 12
|
||||||
|
|
||||||
|
anchor.window: bar
|
||||||
|
anchor.rect.x: menuX - (fullWidth + 16) / 2
|
||||||
|
anchor.rect.y: bar.height
|
||||||
|
anchor.edges: Edges.Top | Edges.Left
|
||||||
|
anchor.gravity: Edges.Bottom | Edges.Right
|
||||||
anchor.adjustment: PopupAdjustment.Slide
|
anchor.adjustment: PopupAdjustment.Slide
|
||||||
grabFocus: true
|
grabFocus: true
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
implicitWidth: menuColumn.width + 2
|
implicitWidth: fullWidth + 16
|
||||||
implicitHeight: menuColumn.height + 2
|
implicitHeight: fullHeight + 4
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
open = true;
|
||||||
|
} else {
|
||||||
|
open = false;
|
||||||
|
justDismissed = true;
|
||||||
|
menuDismissGuard.start();
|
||||||
|
menuOpener.menu = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: menuDismissGuard
|
||||||
|
interval: 100
|
||||||
|
onTriggered: contextMenu.justDismissed = false
|
||||||
|
}
|
||||||
|
|
||||||
QsMenuOpener {
|
QsMenuOpener {
|
||||||
id: menuOpener
|
id: menuOpener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Concave corner — left
|
||||||
|
Item {
|
||||||
|
anchors.right: menuContent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: 8
|
||||||
|
height: Math.min(8, menuContent.height)
|
||||||
|
clip: true
|
||||||
|
visible: menuContent.height > 0
|
||||||
|
Canvas {
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: 8; height: 8
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.clearRect(0, 0, 8, 8);
|
||||||
|
ctx.fillStyle = "#D1${c.base00}";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, 0); ctx.lineTo(8, 0); ctx.lineTo(8, 8);
|
||||||
|
ctx.arc(0, 8, 8, 0, -Math.PI / 2, true);
|
||||||
|
ctx.closePath(); ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concave corner — right
|
||||||
|
Item {
|
||||||
|
anchors.left: menuContent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: 8
|
||||||
|
height: Math.min(8, menuContent.height)
|
||||||
|
clip: true
|
||||||
|
visible: menuContent.height > 0
|
||||||
|
Canvas {
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: 8; height: 8
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.clearRect(0, 0, 8, 8);
|
||||||
|
ctx.fillStyle = "#D1${c.base00}";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, 0); ctx.lineTo(8, 0);
|
||||||
|
ctx.arc(8, 8, 8, -Math.PI / 2, Math.PI, true);
|
||||||
|
ctx.closePath(); ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: menuColumn
|
id: menuContent
|
||||||
width: menuItems.width + 16
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
height: menuItems.height + 12
|
anchors.top: parent.top
|
||||||
color: "#${c.base00}"
|
width: contextMenu.fullWidth
|
||||||
border.color: "#${c.base03}"
|
height: contextMenu.open ? contextMenu.fullHeight : 0
|
||||||
border.width: 1
|
color: "#D1${c.base00}"
|
||||||
radius: 8
|
radius: 8
|
||||||
|
topLeftRadius: 0
|
||||||
|
topRightRadius: 0
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: menuItems
|
id: menuItems
|
||||||
anchors.centerIn: parent
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 6
|
||||||
width: 200
|
width: 200
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
|
@ -771,7 +854,6 @@ in
|
||||||
? "#${c.base02}" : "transparent"
|
? "#${c.base02}" : "transparent"
|
||||||
radius: modelData.isSeparator ? 0 : 4
|
radius: modelData.isSeparator ? 0 : 4
|
||||||
|
|
||||||
// Separator line
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: modelData.isSeparator
|
visible: modelData.isSeparator
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
@ -780,7 +862,6 @@ in
|
||||||
color: "#${c.base03}"
|
color: "#${c.base03}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Menu item content
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !modelData.isSeparator
|
visible: !modelData.isSeparator
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -820,12 +901,6 @@ in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (!visible) {
|
|
||||||
menuOpener.menu = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calendar popup
|
// Calendar popup
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue