diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-06-06 22:38:15 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-06-06 22:38:15 +1000 |
| commit | 17fb60f216e3f6979a44144677a7d1d8bb9e9fe4 (patch) | |
| tree | ca79775a5cc31d58ae2f024dc5c905b0c86adc89 /modules/bar/popouts/TrayMenu.qml | |
| parent | popouts: transition between popout content (diff) | |
| download | caelestia-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.qml | 235 |
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 {} + } +} |