From 2fea7d56bc528bd0a0e2881f9bb39e64d18f154a Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Mon, 3 Nov 2025 14:47:25 +0100 Subject: [PATCH] animations... --- Bar.qml | 1 + Modules/TrayItem.qml | 27 +++---- Modules/TrayMenu.qml | 179 ++++++++++++++++++++++++++++++++++++----- Modules/TrayWidget.qml | 2 + 4 files changed, 171 insertions(+), 38 deletions(-) diff --git a/Bar.qml b/Bar.qml index bbdb1ae..d0327d7 100644 --- a/Bar.qml +++ b/Bar.qml @@ -29,6 +29,7 @@ Scope { color: "transparent" Rectangle { + id: backgroundRect anchors.fill: parent color: "#801a1a1a" radius: 0 diff --git a/Modules/TrayItem.qml b/Modules/TrayItem.qml index 962692c..7908060 100644 --- a/Modules/TrayItem.qml +++ b/Modules/TrayItem.qml @@ -14,6 +14,8 @@ MouseArea { id: root required property SystemTrayItem item + required property PanelWindow bar + property point globalPos implicitWidth: 22 implicitHeight: 22 @@ -21,6 +23,10 @@ MouseArea { hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton + onPositionChanged: { + globalPos = root.mapToItem(root.bar.backgroundRect, 0, 0); + } + IconImage { id: icon @@ -51,23 +57,8 @@ MouseArea { TrayMenu { id: trayMenu menu: menuOpener - anchor.item: root - anchor.edges: Edges.Bottom - anchor.rect.x: 11 - anchor.rect.y: 25 - onVisibleChanged: { - if ( grab.active && !visible ) { - grab.active = false; - } - } - - HyprlandFocusGrab { - id: grab - windows: [ trayMenu ] - onCleared: { - trayMenu.visible = false; - } - } + trayItemRect: root.globalPos + bar: root.bar } } @@ -79,7 +70,7 @@ MouseArea { trayMenu.menu = menuOpener; } trayMenu.visible = !trayMenu.visible; - grab.active = true; + console.log(root.x); } } diff --git a/Modules/TrayMenu.qml b/Modules/TrayMenu.qml index b0918c5..91d2d40 100644 --- a/Modules/TrayMenu.qml +++ b/Modules/TrayMenu.qml @@ -6,12 +6,41 @@ import QtQuick.Layouts import Qt5Compat.GraphicalEffects import Quickshell.Hyprland -PopupWindow { +PanelWindow { id: root signal menuActionTriggered() required property QsMenuOpener menu + required property point trayItemRect + required property PanelWindow bar property var menuStack: [] + property real scaleValue: 0 + + property int height: calcHeight() + property int entryHeight: 30 + property int maxWidth: { + let menuWidth = 0; + for ( let i = 0; i < listLayout.count; i++ ) { + if ( !listLayout.model.values[i].isSeparator ) { + let entry = listLayout.model.values[i]; + tempMetrics.text = entry.text; + let textWidth = tempMetrics.width + 20 + (entry.icon ?? "" ? 30 : 0) + (entry.hasChildren ? 30 : 0); + if ( textWidth > menuWidth ) { + menuWidth = textWidth; + } + } + } + return menuWidth; + } + + visible: false + color: "transparent" + anchors { + top: true + left: true + right: true + bottom: true + } function calcHeight() { let count = 0; @@ -24,6 +53,7 @@ PopupWindow { } } if ( root.menuStack.length > 0 ) { + backEntry.visible = true; count++; } return (count * entryHeight) + ((count - 1) * 2) + (separatorCount * 3) + 10; @@ -47,12 +77,16 @@ PopupWindow { function goBack() { if ( root.menuStack.length > 0 ) { root.menu = root.menuStack.pop(); + backEntry.visible = false; } } onVisibleChanged: { - if ( visible ) { - root.menuStack = []; + if ( !visible ) { + goBack(); + } else { + scaleValue = 0; + scaleAnimation.start(); } } @@ -61,38 +95,121 @@ PopupWindow { text: "" } - property int height: calcHeight() - property int entryHeight: 30 - property int maxWidth: calcWidth() - implicitWidth: maxWidth + 20 - implicitHeight: height - color: "transparent" - anchor.gravity: Edges.Bottom - Behavior on implicitHeight { - NumberAnimation { - duration: MaterialEasing.standardTime - easing.bezierCurve: MaterialEasing.standard + NumberAnimation { + id: scaleAnimation + target: root + property: "scaleValue" + from: 0 + to: 1 + duration: 150 + easing.type: Easing.OutCubic + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + root.visible = false; } } - Behavior on implicitWidth { - NumberAnimation { - duration: MaterialEasing.standardTime - easing.bezierCurve: MaterialEasing.standard + + Behavior on menu { + SequentialAnimation { + ParallelAnimation { + NumberAnimation { + duration: MaterialEasing.standardTime / 2 + easing.bezierCurve: MaterialEasing.expressiveEffects + from: 0 + property: "x" + target: translateAnim + to: -listLayout.width / 2 + } + + NumberAnimation { + duration: MaterialEasing.standardTime / 2 + easing.bezierCurve: MaterialEasing.standard + from: 1 + property: "opacity" + target: columnLayout + to: 0 + } + } + + PropertyAction { + property: "menu" + target: columnLayout + } + + ParallelAnimation { + NumberAnimation { + duration: MaterialEasing.standardTime / 2 + easing.bezierCurve: MaterialEasing.standard + from: 0 + property: "opacity" + target: columnLayout + to: 1 + } + + NumberAnimation { + duration: MaterialEasing.standardTime / 2 + easing.bezierCurve: MaterialEasing.expressiveEffects + from: listLayout.width / 2 + property: "x" + target: translateAnim + to: 0 + } + } } } Rectangle { id: menuRect - anchors.fill: parent + x: root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 10 + y: root.trayItemRect.y - 5 + implicitHeight: root.height + implicitWidth: root.maxWidth + 20 color: "#80151515" radius: 8 border.color: "#40FFFFFF" + clip: true + + Behavior on implicitHeight { + NumberAnimation { + duration: MaterialEasing.standardTime + easing.bezierCurve: MaterialEasing.standard + } + } + + Behavior on implicitWidth { + NumberAnimation { + duration: MaterialEasing.standardTime + easing.bezierCurve: MaterialEasing.standard + } + } + + transform: [ + Scale { + origin.x: menuRect.width / 2 + origin.y: 0 + xScale: root.scaleValue + yScale: root.scaleValue + } + ] + ColumnLayout { + id: columnLayout anchors.fill: parent anchors.margins: 5 spacing: 0 + transform: [ + Translate { + id: translateAnim + x: 0 + y: 0 + } + ] ListView { id: listLayout Layout.fillWidth: true @@ -101,6 +218,27 @@ PopupWindow { model: ScriptModel { values: [...root.menu?.children.values] } + + add: Transition { + NumberAnimation { + property: "x" + from: listLayout.width + to: 0 + duration: 200 + easing.type: Easing.OutCubic + } + } + + move: Transition { + NumberAnimation { + property: "x" + from: 0 + to: -listLayout.width + duration: 200 + easing.type: Easing.InCubic + } + } + delegate: Rectangle { id: menuItem required property QsMenuEntry modelData @@ -177,7 +315,8 @@ PopupWindow { } } Rectangle { - visible: true + id: backEntry + visible: false Layout.fillWidth: true Layout.preferredHeight: root.entryHeight color: mouseAreaBack.containsMouse ? "#15FFFFFF" : "transparent" diff --git a/Modules/TrayWidget.qml b/Modules/TrayWidget.qml index 13f4a52..030d985 100644 --- a/Modules/TrayWidget.qml +++ b/Modules/TrayWidget.qml @@ -20,8 +20,10 @@ Rectangle { id: repeater model: SystemTray.items TrayItem { + id: trayItem required property SystemTrayItem modelData item: modelData + bar: root.bar } } }