diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-05-11 23:55:05 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-05-11 23:55:05 +1000 |
| commit | 60858f6f02fb7dc04e727db89090e7b83399803a (patch) | |
| tree | 0a6300271786a38a468ba4bb6df0c392eed82043 | |
| parent | osd: fix show on hover (diff) | |
| download | caelestia-shell-60858f6f02fb7dc04e727db89090e7b83399803a.tar.gz caelestia-shell-60858f6f02fb7dc04e727db89090e7b83399803a.tar.bz2 caelestia-shell-60858f6f02fb7dc04e727db89090e7b83399803a.zip | |
feat: session menu
| -rw-r--r-- | assets/kurukuru.gif | bin | 0 -> 99006 bytes | |||
| -rw-r--r-- | config/SessionConfig.qml | 12 | ||||
| -rw-r--r-- | modules/osd/Content.qml | 1 | ||||
| -rw-r--r-- | modules/osd/Osd.qml | 71 | ||||
| -rw-r--r-- | modules/session/Background.qml | 70 | ||||
| -rw-r--r-- | modules/session/Content.qml | 118 | ||||
| -rw-r--r-- | modules/session/Session.qml | 87 | ||||
| -rw-r--r-- | modules/session/Wrapper.qml | 62 | ||||
| -rw-r--r-- | services/Drawers.qml | 1 | ||||
| -rw-r--r-- | shell.qml | 2 | ||||
| -rw-r--r-- | widgets/StateLayer.qml | 3 |
11 files changed, 396 insertions, 31 deletions
diff --git a/assets/kurukuru.gif b/assets/kurukuru.gif Binary files differnew file mode 100644 index 0000000..38d203d --- /dev/null +++ b/assets/kurukuru.gif diff --git a/config/SessionConfig.qml b/config/SessionConfig.qml new file mode 100644 index 0000000..ba4db0c --- /dev/null +++ b/config/SessionConfig.qml @@ -0,0 +1,12 @@ +pragma Singleton + +import Quickshell +import QtQuick + +Singleton { + readonly property Sizes sizes: Sizes {} + + component Sizes: QtObject { + readonly property int button: 80 + } +} diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml index 25ba7b8..6814966 100644 --- a/modules/osd/Content.qml +++ b/modules/osd/Content.qml @@ -1,7 +1,6 @@ import "root:/widgets" import "root:/services" import "root:/config" -import Quickshell import QtQuick Column { diff --git a/modules/osd/Osd.qml b/modules/osd/Osd.qml index 8f93ae9..7774bab 100644 --- a/modules/osd/Osd.qml +++ b/modules/osd/Osd.qml @@ -80,49 +80,60 @@ Variants { Component.onCompleted: root.winHeight = height - Background { - id: bg - - visible: false - + Item { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right + anchors.rightMargin: Drawers.rightExclusion - wrapperWidth: Math.min(wrapper.width, content.width) - wrapperHeight: wrapper.height - } + clip: true + visible: width > 0 + implicitWidth: wrapper.width + implicitHeight: wrapper.height - LayerShadow { - source: bg - } + Background { + id: bg - Wrapper { - id: wrapper + visible: false - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right - implicitHeight: content.height + bg.rounding * 2 + wrapperWidth: Math.min(wrapper.width, content.width) + wrapperHeight: wrapper.height + } - osdVisible: root.osdVisible - contentWidth: content.width + LayerShadow { + source: bg + } + + Wrapper { + id: wrapper + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right - Content { - id: content + implicitHeight: content.height + bg.rounding * 2 - monitor: root.monitor + osdVisible: root.osdVisible + contentWidth: content.width + + Content { + id: content + + monitor: root.monitor + } } - } - HoverHandler { - id: hoverHandler + HoverHandler { + id: hoverHandler - onHoveredChanged: { - root.hovered = hovered; - if (hovered) - timer.stop(); - else - root.osdVisible = false; + onHoveredChanged: { + root.hovered = hovered; + if (hovered) + timer.stop(); + else + root.osdVisible = false; + } } } } diff --git a/modules/session/Background.qml b/modules/session/Background.qml new file mode 100644 index 0000000..4b4b92e --- /dev/null +++ b/modules/session/Background.qml @@ -0,0 +1,70 @@ +import "root:/services" +import "root:/config" +import QtQuick +import QtQuick.Shapes + +Shape { + id: root + + required property real wrapperWidth + required property real wrapperHeight + readonly property real rounding: BorderConfig.rounding + readonly property bool flatten: wrapperWidth < rounding * 2 + readonly property real roundingX: flatten ? wrapperWidth / 2 : rounding + + preferredRendererType: Shape.CurveRenderer + opacity: Colours.transparency.enabled ? Colours.transparency.base : 1 + + ShapePath { + strokeWidth: -1 + fillColor: BorderConfig.colour + + startX: root.wrapperWidth - 1 + + PathArc { + relativeX: -root.roundingX + relativeY: root.rounding + radiusX: Math.min(root.rounding, root.wrapperWidth) + radiusY: root.rounding + } + PathLine { + x: root.roundingX + relativeY: 0 + } + PathArc { + relativeX: -root.roundingX + relativeY: root.rounding + radiusX: Math.min(root.rounding, root.wrapperWidth) + radiusY: root.rounding + direction: PathArc.Counterclockwise + } + PathLine { + y: root.wrapperHeight - root.rounding * 2 + } + PathArc { + relativeX: root.roundingX + relativeY: root.rounding + radiusX: Math.min(root.rounding, root.wrapperWidth) + radiusY: root.rounding + direction: PathArc.Counterclockwise + } + PathLine { + x: (root.flatten ? root.roundingX : root.wrapperWidth - root.rounding) - 1 + relativeY: 0 + } + PathArc { + relativeX: root.roundingX + relativeY: root.rounding + radiusX: Math.min(root.rounding, root.wrapperWidth) + radiusY: root.rounding + } + + Behavior on fillColor { + ColorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } +} diff --git a/modules/session/Content.qml b/modules/session/Content.qml new file mode 100644 index 0000000..d1a2c38 --- /dev/null +++ b/modules/session/Content.qml @@ -0,0 +1,118 @@ +pragma ComponentBehavior: Bound + +import "root:/widgets" +import "root:/services" +import "root:/config" +import Quickshell +import Quickshell.Io +import QtQuick + +Column { + id: root + + required property Scope session + + padding: Appearance.padding.large + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + + spacing: Appearance.spacing.large + + SessionButton { + id: logout + + icon: "logout" + command: ["uwsm", "stop"] + + KeyNavigation.down: shutdown + + Connections { + target: session + + function onSessionVisibleChanged(): void { + if (session.sessionVisible) + logout.focus = true; + } + } + } + + SessionButton { + id: shutdown + + icon: "power_settings_new" + command: ["systemctl", "poweroff"] + + KeyNavigation.up: logout + KeyNavigation.down: hibernate + } + + AnimatedImage { + width: SessionConfig.sizes.button + height: SessionConfig.sizes.button + sourceSize.width: width + sourceSize.height: height + + playing: session.sessionVisible + asynchronous: true + speed: 0.7 + source: "root:/assets/kurukuru.gif" + } + + SessionButton { + id: hibernate + + icon: "downloading" + command: ["systemctl", "hibernate"] + + KeyNavigation.up: shutdown + KeyNavigation.down: reboot + } + + SessionButton { + id: reboot + + icon: "cached" + command: ["systemctl", "reboot"] + + KeyNavigation.up: hibernate + } + + component SessionButton: StyledRect { + id: button + + required property string icon + required property list<string> command + + implicitWidth: SessionConfig.sizes.button + implicitHeight: SessionConfig.sizes.button + + radius: Appearance.rounding.large + color: button.activeFocus ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainer + + Keys.onEnterPressed: proc.startDetached() + Keys.onEscapePressed: root.session.sessionVisible = false + + Process { + id: proc + + command: button.command + } + + StateLayer { + radius: parent.radius + + function onClicked(): void { + proc.startDetached(); + } + } + + MaterialIcon { + anchors.centerIn: parent + + text: button.icon + color: button.activeFocus ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.extraLarge + } + } +} diff --git a/modules/session/Session.qml b/modules/session/Session.qml new file mode 100644 index 0000000..0f8a0d5 --- /dev/null +++ b/modules/session/Session.qml @@ -0,0 +1,87 @@ +import "root:/widgets" +import "root:/services" +import "root:/config" +import Quickshell +import Quickshell.Wayland +import QtQuick + +Scope { + id: root + + property int winHeight + property bool sessionVisible + + Connections { + target: Drawers + + function onPosChanged(screen: ShellScreen, x: int, y: int): void { + if (x > screen.width - BorderConfig.thickness && y > (screen.height - root.winHeight) / 2 && y < (screen.height + root.winHeight) / 2) + root.sessionVisible = true; + } + } + + LazyLoader { + loading: true + + StyledWindow { + id: win + + name: "osd" + keyboardFocus: root.sessionVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + visible: wrapper.shouldBeVisible + + mask: Region { + item: wrapper + } + + anchors.left: true + anchors.right: true + height: wrapper.height + + Component.onCompleted: { + root.winHeight = height; + Drawers.rightExclusion = Qt.binding(() => bg.width); + } + + Background { + id: bg + + visible: false + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + + wrapperWidth: Math.min(wrapper.width, content.width) + wrapperHeight: wrapper.height + } + + LayerShadow { + source: bg + } + + Wrapper { + id: wrapper + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + + implicitHeight: content.height + bg.rounding * 2 + + sessionVisible: root.sessionVisible + contentWidth: content.width + + Content { + id: content + + session: root + } + } + } + } + + CustomShortcut { + name: "session" + description: "Toggle session menu" + onPressed: root.sessionVisible = !root.sessionVisible + } +} diff --git a/modules/session/Wrapper.qml b/modules/session/Wrapper.qml new file mode 100644 index 0000000..5806972 --- /dev/null +++ b/modules/session/Wrapper.qml @@ -0,0 +1,62 @@ +import "root:/config" +import QtQuick + +Item { + id: root + + required property bool sessionVisible + required property real contentWidth + property bool shouldBeVisible + + visible: width > 0 + width: 0 + + states: State { + name: "visible" + when: root.sessionVisible + + PropertyChanges { + root.width: contentWidth + root.shouldBeVisible: true + } + } + + transitions: [ + Transition { + from: "" + to: "visible" + + SequentialAnimation { + PropertyAction { + target: root + property: "shouldBeVisible" + } + NumberAnimation { + target: root + property: "width" + duration: Appearance.anim.durations.large + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.emphasizedDecel + } + } + }, + Transition { + from: "visible" + to: "" + + SequentialAnimation { + NumberAnimation { + target: root + property: "width" + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.emphasizedAccel + } + PropertyAction { + target: root + property: "shouldBeVisible" + } + } + } + ] +} diff --git a/services/Drawers.qml b/services/Drawers.qml index 1f524ab..ac88b6c 100644 --- a/services/Drawers.qml +++ b/services/Drawers.qml @@ -6,6 +6,7 @@ Singleton { id: root property var positions: ({}) + property int rightExclusion signal posChanged(screen: ShellScreen, x: int, y: int) @@ -2,6 +2,7 @@ import "modules/bar" import "modules/launcher" import "modules/osd" import "modules/notifications" +import "modules/session" import "modules/drawers" import "modules/background" import Quickshell @@ -13,4 +14,5 @@ ShellRoot { Background {} Drawers {} Notifications {} + Session {} } diff --git a/widgets/StateLayer.qml b/widgets/StateLayer.qml index de15b16..bebf230 100644 --- a/widgets/StateLayer.qml +++ b/widgets/StateLayer.qml @@ -6,6 +6,9 @@ import QtQuick Rectangle { id: root + readonly property alias hovered: mouse.hovered + readonly property alias pressed: mouse.pressed + function onClicked(event: MouseEvent): void { } |