quickshell: session menu card backing, keyboard nav with default selection
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a14ccd8c09
commit
516b8b6d5b
1 changed files with 75 additions and 44 deletions
|
|
@ -475,6 +475,8 @@ in
|
|||
required property var shellRoot
|
||||
screen: modelData
|
||||
WlrLayershell.namespace: "quickshell-bar"
|
||||
// Keyboard only while the session menu is open (arrow/Enter nav)
|
||||
WlrLayershell.keyboardFocus: sessionMenu.open ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
|
|
@ -528,25 +530,54 @@ in
|
|||
}
|
||||
|
||||
// ── Session menu: icon-only power controls morphing out of
|
||||
// the right frame column at screen centre (Super+L).
|
||||
// the right frame column at screen centre (Super+L). Keyboard:
|
||||
// arrows/Tab move the selection, Enter activates, Esc closes.
|
||||
Item {
|
||||
id: sessionMenu
|
||||
property bool open: false
|
||||
property real openW: open ? 56 : 0
|
||||
property int selIdx: 0
|
||||
property real openW: open ? 64 : 0
|
||||
Behavior on openW {
|
||||
NumberAnimation { duration: 280; easing.type: Easing.OutExpo }
|
||||
}
|
||||
|
||||
readonly property var actions: [
|
||||
{ icon: "lock", danger: false, act: "lock" },
|
||||
{ icon: "logout", danger: false, act: "logout" },
|
||||
{ icon: "restart_alt", danger: true, act: "reboot" },
|
||||
{ icon: "power_settings_new", danger: true, act: "poweroff" }
|
||||
]
|
||||
|
||||
function activate(act) {
|
||||
open = false;
|
||||
if (act === "lock") Quickshell.execDetached([Commands.hyprlock]);
|
||||
else if (act === "logout") Hyprland.dispatch("hl.dsp.exit()");
|
||||
else if (act === "reboot") Quickshell.execDetached([Commands.systemctl, "reboot"]);
|
||||
else Quickshell.execDetached([Commands.systemctl, "poweroff"]);
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
open = !open;
|
||||
if (open) {
|
||||
selIdx = 0;
|
||||
forceActiveFocus();
|
||||
_sessionAutoClose.restart();
|
||||
}
|
||||
}
|
||||
|
||||
x: bar.width - Theme.frameWidth - openW
|
||||
y: Math.round((bar.height - height) / 2)
|
||||
width: openW
|
||||
height: sessionCol.height + 24
|
||||
height: sessionCard.height + 24
|
||||
visible: openW > 0.5
|
||||
|
||||
function toggle() {
|
||||
open = !open;
|
||||
if (open) _sessionAutoClose.restart();
|
||||
}
|
||||
focus: open
|
||||
Keys.onEscapePressed: open = false
|
||||
Keys.onUpPressed: { selIdx = (selIdx + actions.length - 1) % actions.length; _sessionAutoClose.restart(); }
|
||||
Keys.onDownPressed: { selIdx = (selIdx + 1) % actions.length; _sessionAutoClose.restart(); }
|
||||
Keys.onTabPressed: { selIdx = (selIdx + 1) % actions.length; _sessionAutoClose.restart(); }
|
||||
Keys.onReturnPressed: activate(actions[selIdx].act)
|
||||
Keys.onEnterPressed: activate(actions[selIdx].act)
|
||||
|
||||
Timer {
|
||||
id: _sessionAutoClose
|
||||
|
|
@ -570,51 +601,51 @@ in
|
|||
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
Column {
|
||||
id: sessionCol
|
||||
// Card backing, matching the other dropdowns
|
||||
Rectangle {
|
||||
id: sessionCard
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
spacing: 4
|
||||
width: 48
|
||||
height: sessionCol.height + 8
|
||||
radius: 8
|
||||
color: Theme.base01
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{ icon: "lock", danger: false, act: "lock" },
|
||||
{ icon: "logout", danger: false, act: "logout" },
|
||||
{ icon: "restart_alt", danger: true, act: "reboot" },
|
||||
{ icon: "power_settings_new", danger: true, act: "poweroff" }
|
||||
]
|
||||
Column {
|
||||
id: sessionCol
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
Rectangle {
|
||||
id: sessBtn
|
||||
required property var modelData
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 8
|
||||
color: sessBtnMa.containsMouse ? Theme.base02 : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Repeater {
|
||||
model: sessionMenu.actions
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: sessBtn.modelData.icon
|
||||
color: sessBtn.modelData.danger && sessBtnMa.containsMouse
|
||||
? Theme.base08 : Theme.base05
|
||||
Rectangle {
|
||||
id: sessBtn
|
||||
required property var modelData
|
||||
required property int index
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 8
|
||||
color: sessionMenu.selIdx === index ? Theme.base02 : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: sessBtnMa
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
sessionMenu.open = false;
|
||||
if (sessBtn.modelData.act === "lock") Quickshell.execDetached([Commands.hyprlock]);
|
||||
else if (sessBtn.modelData.act === "logout") Hyprland.dispatch("hl.dsp.exit()");
|
||||
else if (sessBtn.modelData.act === "reboot") Quickshell.execDetached([Commands.systemctl, "reboot"]);
|
||||
else Quickshell.execDetached([Commands.systemctl, "poweroff"]);
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: sessBtn.modelData.icon
|
||||
color: sessBtn.modelData.danger && sessionMenu.selIdx === sessBtn.index
|
||||
? Theme.base08 : Theme.base05
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: sessionMenu.selIdx = sessBtn.index
|
||||
onClicked: sessionMenu.activate(sessBtn.modelData.act)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue