import QtQuick import Quickshell import "." as M import "../services" as S M.HoverPanel { id: menuWindow popupMode: true contentWidth: 180 signal runCommand(var cmd) readonly property bool _isNiri: Quickshell.env("NIRI_SOCKET") !== "" // Confirmation state: null = normal menu, object = pending confirm property var _confirmItem: null function _run(cmd) { runCommand(cmd); dismiss(); } function _requestAction(item) { if (item.confirm) { _confirmItem = item; } else { _run(item.cmd); } } function _cancelConfirm() { _confirmItem = null; } // Normal menu entries Column { visible: !menuWindow._confirmItem width: menuWindow.contentWidth Repeater { model: [ { label: "Lock", icon: "\uF023", cmd: ["loginctl", "lock-session"], color: S.Theme.base0D, confirm: false }, { label: "Suspend", icon: "\uF186", cmd: ["systemctl", "suspend"], color: S.Theme.base0E, confirm: false }, { label: "Logout", icon: "\uF2F5", cmd: menuWindow._isNiri ? ["niri", "msg", "action", "quit"] : ["loginctl", "terminate-user", ""], color: S.Theme.base0A, confirm: false }, { label: "Reboot", icon: "\uF021", cmd: ["systemctl", "reboot"], color: S.Theme.base09, confirm: true }, { label: "Shutdown", icon: "\uF011", cmd: ["systemctl", "poweroff"], color: S.Theme.base08, confirm: true } ] delegate: Item { id: entry required property var modelData required property int index width: menuWindow.contentWidth height: 32 Rectangle { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 color: entryHover.hovered ? S.Theme.base02 : "transparent" radius: S.Theme.radius } Text { id: entryIcon anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 12 text: entry.modelData.icon color: entry.modelData.color font.pixelSize: S.Theme.fontSize + 1 font.family: S.Theme.iconFontFamily } Text { anchors.verticalCenter: parent.verticalCenter anchors.left: entryIcon.right anchors.leftMargin: 10 text: entry.modelData.label color: S.Theme.base05 font.pixelSize: S.Theme.fontSize font.family: S.Theme.fontFamily } HoverHandler { id: entryHover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: menuWindow._requestAction(entry.modelData) } } } } // Confirmation view Column { visible: !!menuWindow._confirmItem width: menuWindow.contentWidth spacing: 4 Item { width: parent.width height: 28 Text { anchors.centerIn: parent text: menuWindow._confirmItem ? menuWindow._confirmItem.label + "?" : "" color: menuWindow._confirmItem ? menuWindow._confirmItem.color : S.Theme.base05 font.pixelSize: S.Theme.fontSize font.family: S.Theme.fontFamily font.bold: true } } Row { anchors.horizontalCenter: parent.horizontalCenter spacing: 8 // Cancel Item { width: 72 height: 28 Rectangle { anchors.fill: parent color: cancelHover.hovered ? S.Theme.base02 : S.Theme.base01 radius: S.Theme.radius border.width: 1 border.color: S.Theme.base03 } Text { anchors.centerIn: parent text: "Cancel" color: S.Theme.base05 font.pixelSize: S.Theme.fontSize font.family: S.Theme.fontFamily } HoverHandler { id: cancelHover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: menuWindow._cancelConfirm() } } // Confirm Item { width: 72 height: 28 Rectangle { anchors.fill: parent color: { if (!menuWindow._confirmItem) return S.Theme.base02; const c = menuWindow._confirmItem.color; return confirmHover.hovered ? Qt.rgba(c.r, c.g, c.b, 0.3) : Qt.rgba(c.r, c.g, c.b, 0.15); } radius: S.Theme.radius border.width: 1 border.color: menuWindow._confirmItem ? menuWindow._confirmItem.color : S.Theme.base03 } Text { anchors.centerIn: parent text: "Confirm" color: menuWindow._confirmItem ? menuWindow._confirmItem.color : S.Theme.base05 font.pixelSize: S.Theme.fontSize font.family: S.Theme.fontFamily font.bold: true } HoverHandler { id: confirmHover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: { if (menuWindow._confirmItem) menuWindow._run(menuWindow._confirmItem.cmd); } } } } Item { width: 1 height: 4 } } }