summaryrefslogtreecommitdiff
path: root/modules/bar/popouts/TrayMenu.qml
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-06-06 22:38:15 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-06-06 22:38:15 +1000
commit17fb60f216e3f6979a44144677a7d1d8bb9e9fe4 (patch)
treeca79775a5cc31d58ae2f024dc5c905b0c86adc89 /modules/bar/popouts/TrayMenu.qml
parentpopouts: transition between popout content (diff)
downloadcaelestia-shell-17fb60f216e3f6979a44144677a7d1d8bb9e9fe4.tar.gz
caelestia-shell-17fb60f216e3f6979a44144677a7d1d8bb9e9fe4.tar.bz2
caelestia-shell-17fb60f216e3f6979a44144677a7d1d8bb9e9fe4.zip
feat: tray item menus
Diffstat (limited to 'modules/bar/popouts/TrayMenu.qml')
-rw-r--r--modules/bar/popouts/TrayMenu.qml235
1 files changed, 235 insertions, 0 deletions
diff --git a/modules/bar/popouts/TrayMenu.qml b/modules/bar/popouts/TrayMenu.qml
new file mode 100644
index 0000000..f7d0928
--- /dev/null
+++ b/modules/bar/popouts/TrayMenu.qml
@@ -0,0 +1,235 @@
+pragma ComponentBehavior: Bound
+
+import "root:/widgets"
+import "root:/services"
+import "root:/config"
+import Quickshell
+import Quickshell.Widgets
+import QtQuick
+import QtQuick.Controls
+
+StackView {
+ id: root
+
+ required property QsMenuHandle trayItem
+
+ implicitWidth: currentItem.implicitWidth
+ implicitHeight: currentItem.implicitHeight
+
+ initialItem: SubMenu {
+ handle: root.trayItem
+ }
+
+ pushEnter: Anim {}
+ pushExit: Anim {}
+ popEnter: Anim {}
+ popExit: Anim {}
+
+ component Anim: Transition {
+ NumberAnimation {
+ duration: 0 // Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ }
+
+ component SubMenu: Column {
+ id: menu
+
+ required property QsMenuHandle handle
+ property bool isSubMenu
+ property bool shown
+
+ padding: Appearance.padding.smaller
+ spacing: Appearance.spacing.small
+
+ opacity: shown ? 1 : 0
+ scale: shown ? 1 : 0.8
+
+ Component.onCompleted: shown = true
+ StackView.onActivating: shown = true
+ StackView.onDeactivating: shown = false
+ StackView.onRemoved: destroy()
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ }
+
+ Behavior on scale {
+ NumberAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ }
+
+ QsMenuOpener {
+ id: menuOpener
+
+ menu: menu.handle
+ }
+
+ Repeater {
+ model: menuOpener.children
+
+ StyledRect {
+ id: item
+
+ required property QsMenuEntry modelData
+
+ implicitWidth: BarConfig.sizes.trayMenuWidth
+ implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
+
+ radius: Appearance.rounding.full
+ color: modelData.isSeparator ? Colours.palette.m3outlineVariant : "transparent"
+
+ Loader {
+ id: children
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ active: !item.modelData.isSeparator
+ asynchronous: true
+
+ sourceComponent: Item {
+ implicitHeight: label.implicitHeight
+
+ StateLayer {
+ anchors.margins: -Appearance.padding.small / 2
+ anchors.leftMargin: -Appearance.padding.smaller
+ anchors.rightMargin: -Appearance.padding.smaller
+
+ radius: item.radius
+ disabled: !item.modelData.enabled
+
+ function onClicked(): void {
+ const entry = item.modelData;
+ if (entry.hasChildren)
+ root.push(subMenuComp.createObject(null, {
+ handle: entry,
+ isSubMenu: true
+ }));
+ else
+ item.modelData.triggered();
+ }
+ }
+
+ Loader {
+ id: icon
+
+ anchors.left: parent.left
+
+ active: item.modelData.icon !== ""
+ asynchronous: true
+
+ sourceComponent: IconImage {
+ implicitSize: label.implicitHeight
+
+ source: item.modelData.icon
+ }
+ }
+
+ StyledText {
+ id: label
+
+ anchors.left: icon.right
+ anchors.leftMargin: icon.active ? Appearance.spacing.smaller : 0
+
+ text: labelMetrics.elidedText
+ color: item.modelData.enabled ? Colours.palette.m3onSurface : Colours.palette.m3outline
+ }
+
+ TextMetrics {
+ id: labelMetrics
+
+ text: item.modelData.text
+ font.pointSize: label.font.pointSize
+ font.family: label.font.family
+
+ elide: Text.ElideRight
+ elideWidth: BarConfig.sizes.trayMenuWidth - (icon.active ? icon.implicitWidth + label.anchors.leftMargin : 0) - (expand.active ? expand.implicitWidth + Appearance.spacing.normal : 0)
+ }
+
+ Loader {
+ id: expand
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+
+ active: item.modelData.hasChildren
+ asynchronous: true
+
+ sourceComponent: MaterialIcon {
+ text: "chevron_right"
+ color: item.modelData.enabled ? Colours.palette.m3onSurface : Colours.palette.m3outline
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Loader {
+ active: menu.isSubMenu
+ asynchronous: true
+
+ sourceComponent: Item {
+ implicitWidth: back.implicitWidth
+ implicitHeight: back.implicitHeight + Appearance.spacing.small / 2
+
+ Item {
+ anchors.bottom: parent.bottom
+ implicitWidth: back.implicitWidth
+ implicitHeight: back.implicitHeight
+
+ StyledRect {
+ anchors.fill: parent
+ anchors.margins: -Appearance.padding.small / 2
+ anchors.leftMargin: -Appearance.padding.smaller
+ anchors.rightMargin: -Appearance.padding.smaller * 2
+
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3secondaryContainer
+
+ StateLayer {
+ radius: parent.radius
+
+ function onClicked(): void {
+ root.pop();
+ }
+ }
+ }
+
+ Row {
+ id: back
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ MaterialIcon {
+ anchors.verticalCenter: parent.verticalCenter
+ text: "chevron_left"
+ color: Colours.palette.m3onSecondaryContainer
+ }
+
+ StyledText {
+ anchors.verticalCenter: parent.verticalCenter
+ text: qsTr("Back")
+ color: Colours.palette.m3onSecondaryContainer
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: subMenuComp
+
+ SubMenu {}
+ }
+}