tray menu refresh

This commit is contained in:
Zacharias-Brohn
2025-11-08 14:42:20 +01:00
parent 70916e4755
commit 6d52325d89
12 changed files with 748 additions and 454 deletions
-47
View File
@@ -1,47 +0,0 @@
// CustomTrayMenu.qml
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import QtQuick.Window // for Window, flags
import qs.Modules
PopupWindow {
id: popup
color: "#00202020"
required property QsMenuOpener trayMenu
Column {
id: contentColumn
anchors.fill: parent
spacing: 4
Repeater {
id: repeater
model: popup.trayMenu.children
Row {
id: entryRow
anchors.fill: parent
property var entry: modelData
MouseArea {
anchors.fill: parent
onClicked: {
if (entryRow.entry.triggered) {
entryRow.entry.triggered()
}
popup.visible = false
}
}
Image {
source: entryRow.entry.icon
width: 20; height: 20
visible: entryRow.entry.icon !== ""
}
Text {
text: entryRow.entry.text
color: "black"
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ Item {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 16
text: "󰢮"
text: "\ue30f"
color: "#ffffff"
}
-91
View File
@@ -1,91 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import Quickshell.Hyprland
PopupWindow {
id: root
required property QsMenuHandle menu
property int height: entryCount * ( entryHeight + 3 )
property int entryHeight: 30
property int entryCount: 0
implicitWidth: 300
implicitHeight: height
color: "transparent"
QsMenuOpener {
id: menuOpener
menu: root.menu
}
HyprlandFocusGrab {
id: grab
windows: [ root ]
onCleared: {
root.visible = false;
}
}
onVisibleChanged: {
grab.active = root.visible;
}
Rectangle {
id: menuRect
anchors.fill: parent
color: "#90000000"
radius: 8
border.color: "#10FFFFFF"
ColumnLayout {
id: columnLayout
anchors.fill: parent
anchors.margins: 5
spacing: 2
Repeater {
id: repeater
model: menuOpener.children
Rectangle {
id: menuItem
width: root.implicitWidth
Layout.maximumWidth: parent.width
Layout.fillHeight: true
height: root.entryHeight
color: mouseArea.containsMouse && !modelData.isSeparator ? "#15FFFFFF" : "transparent"
radius: 4
visible: modelData.isSeparator ? false : true
required property QsMenuEntry modelData
Component.onCompleted: {
if ( !modelData.isSeparator ) {
root.entryCount += 1;
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
preventStealing: true
propagateComposedEvents: true
acceptedButtons: Qt.LeftButton
onClicked: {
if ( !menuItem.modelData.hasChildren ) {
menuItem.modelData.triggered();
root.visible = false;
}
}
}
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
text: menuItem.modelData.text
color: "white"
}
}
}
}
}
}
+1 -1
View File
@@ -48,7 +48,7 @@ Item {
Process {
id: swayncProcess
command: ["swaync-client", "-t", "-sw"]
command: ["sh", "-c", "qs -p /home/zach/GitProjects/z-bar-qt/notification-test/shell.qml ipc call root showCenter"]
running: false
}
+13 -18
View File
@@ -3,8 +3,8 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Caelestia
import Quickshell
import Quickshell.DBusMenu
import Quickshell.Widgets
import Quickshell.Hyprland
import Quickshell.Services.SystemTray
@@ -38,35 +38,30 @@ MouseArea {
smooth: false
asynchronous: true
ImageAnalyser {
id: analyser
sourceItem: icon
rescaleSize: 22
}
TrayMenu {
id: trayMenu
menu: menuOpener
trayMenu: root.item?.menu
trayItemRect: root.globalPos
bar: root.bar
}
}
Connections {
target: trayMenu
function onVisibleChanged() {
if ( !trayMenu.visible ) {
trayMenu.trayMenu = null;
}
}
}
onClicked: {
if ( mouse.button === Qt.LeftButton ) {
root.item.activate();
} else if ( mouse.button === Qt.RightButton ) {
if ( trayMenu.menu != menuOpener ) {
trayMenu.menu = menuOpener;
}
trayMenu.trayMenu = root.item?.menu;
trayMenu.visible = !trayMenu.visible;
console.log(root.x);
trayMenu.focusGrab = true;
}
}
QsMenuOpener {
id: menuOpener
menu: root.item?.menu
}
}
+118 -136
View File
@@ -1,24 +1,34 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.DBusMenu
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell.Hyprland
import QtQml
PanelWindow {
id: root
signal menuActionTriggered()
required property QsMenuOpener menu
required property QsMenuHandle trayMenu
required property point trayItemRect
required property PanelWindow bar
property var menuStack: []
property real scaleValue: 0
property int height: calcSize("h")
property alias focusGrab: grab.active
property int entryHeight: 30
property int maxWidth: calcSize("w")
property int biggestWidth: 0
QsMenuOpener {
id: menuOpener
menu: root.trayMenu
}
// onTrayMenuChanged: {
// listLayout.forceLayout();
// }
visible: false
color: "transparent"
@@ -29,61 +39,29 @@ PanelWindow {
bottom: true
}
function calcSize(String) {
if ( String === "w" ) {
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;
}
if ( String === "h" ) {
let count = 0;
let separatorCount = 0;
for (let i = 0; i < listLayout.count; i++) {
if (!listLayout.model.values[i].isSeparator) {
count++;
} else {
separatorCount++;
}
}
if ( root.menuStack.length > 0 ) {
backEntry.visible = true;
count++;
}
return (count * entryHeight) + ((count - 1) * 2) + (separatorCount * 3) + 10;
}
}
mask: Region { id: mask; item: menuRect }
function goBack() {
if ( root.menuStack.length > 0 ) {
root.menu = root.menuStack.pop();
menuChangeAnimation.start();
root.biggestWidth = 0;
root.trayMenu = root.menuStack.pop();
listLayout.positionViewAtBeginning();
backEntry.visible = false;
}
}
function updateMask() {
root.mask.changed();
}
onVisibleChanged: {
if ( !visible ) {
goBack();
} else {
if ( visible ) {
scaleValue = 0;
scaleAnimation.start();
}
}
TextMetrics {
id: tempMetrics
text: ""
}
NumberAnimation {
id: scaleAnimation
target: root
@@ -92,90 +70,85 @@ PanelWindow {
to: 1
duration: 150
easing.type: Easing.OutCubic
onStopped: {
root.updateMask();
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
HyprlandFocusGrab {
id: grab
windows: [ root ]
active: false
onCleared: {
root.visible = false;
}
}
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
}
SequentialAnimation {
id: menuChangeAnimation
ParallelAnimation {
NumberAnimation {
duration: MaterialEasing.standardTime / 2
easing.bezierCurve: MaterialEasing.expressiveEffects
from: 0
property: "x"
target: translateAnim
to: -listLayout.width / 2
}
PropertyAction {
property: "menu"
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
}
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
}
NumberAnimation {
duration: MaterialEasing.standardTime / 2
easing.bezierCurve: MaterialEasing.expressiveEffects
from: listLayout.width / 2
property: "x"
target: translateAnim
to: 0
}
}
}
onMenuActionTriggered: {
if ( root.menuStack.length > 0 ) {
backEntry.visible = true;
}
}
Rectangle {
id: menuRect
x: root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11
y: root.trayItemRect.y - 5
implicitHeight: root.height
implicitWidth: root.maxWidth + 20
x: Math.round( root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11 )
y: Math.round( root.trayItemRect.y - 5 )
implicitWidth: listLayout.contentWidth + 10
implicitHeight: listLayout.contentHeight + ( root.menuStack.length > 0 ? root.entryHeight + 10 : 10 )
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
@@ -185,6 +158,19 @@ PanelWindow {
}
]
Behavior on implicitWidth {
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
}
}
Behavior on implicitHeight {
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
}
}
ColumnLayout {
id: columnLayout
@@ -201,31 +187,11 @@ PanelWindow {
ListView {
id: listLayout
Layout.fillWidth: true
Layout.preferredHeight: root.height - ( root.menuStack.length > 0 ? root.entryHeight + 10 : 0 )
Layout.preferredHeight: contentHeight
spacing: 2
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
}
}
contentWidth: root.biggestWidth
contentHeight: contentItem.childrenRect.height
model: menuOpener.children
delegate: Rectangle {
id: menuItem
@@ -235,7 +201,7 @@ PanelWindow {
}
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
property bool containsMouseAndNotEnabled: mouseArea.containsMouse && !menuItem.modelData.enabled
width: root.implicitWidth
width: widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20
anchors.left: parent.left
anchors.right: parent.right
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
@@ -243,6 +209,19 @@ PanelWindow {
radius: 4
visible: true
Component.onCompleted: {
var biggestWidth = root.biggestWidth;
var currentWidth = widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20;
if ( currentWidth > biggestWidth ) {
root.biggestWidth = currentWidth;
}
}
TextMetrics {
id: widthMetrics
text: menuItem.modelData.text
}
MouseArea {
id: mouseArea
anchors.fill: parent
@@ -254,12 +233,15 @@ PanelWindow {
if ( !menuItem.modelData.hasChildren ) {
if ( menuItem.modelData.enabled ) {
menuItem.modelData.triggered();
root.menuActionTriggered();
root.visible = false;
}
} else {
root.menuStack.push(root.menu);
root.menu = menuItem.child;
root.menuStack.push(root.trayMenu);
menuChangeAnimation.start();
root.biggestWidth = 0;
root.trayMenu = menuItem.modelData;
listLayout.positionViewAtBeginning();
root.menuActionTriggered();
}
}
}