diff --git a/Modules/SidePanel/Cards/ProfileCard.qml b/Modules/SidePanel/Cards/ProfileCard.qml index 81c1f67..23f19df 100644 --- a/Modules/SidePanel/Cards/ProfileCard.qml +++ b/Modules/SidePanel/Cards/ProfileCard.qml @@ -6,6 +6,7 @@ import Quickshell.Io import Quickshell.Widgets import qs.Services import qs.Widgets +import qs.Modules.SidePanel // Header card with avatar, user and quick actions NBox { @@ -66,13 +67,18 @@ NBox { id: powerButton icon: "power_settings_new" onClicked: { - //settingsPanel.isLoaded = !settingsPanel.isLoaded powerMenu.show() } } } } + PowerMenu { + id: powerMenu + anchors.top: powerButton.bottom + anchors.right: powerButton.right + } + // ---------------------------------- // Uptime Timer { @@ -108,376 +114,7 @@ NBox { } } - // ---------------------------------- - // Logout menu - function logout() { - if (WorkspaceManager.isNiri) { - logoutProcessNiri.running = true - } else if (WorkspaceManager.isHyprland) { - logoutProcessHyprland.running = true - } else { - console.warn("No supported compositor detected for logout") - } - } - - function suspend() { - suspendProcess.running = true - } - - function shutdown() { - shutdownProcess.running = true - } - - function reboot() { - rebootProcess.running = true - } - function updateSystemInfo() { uptimeProcess.running = true } - - Process { - id: shutdownProcess - - command: ["shutdown", "-h", "now"] - running: false - } - - Process { - id: rebootProcess - - command: ["reboot"] - running: false - } - - Process { - id: suspendProcess - - command: ["systemctl", "suspend"] - running: false - } - - Process { - id: logoutProcessNiri - - command: ["niri", "msg", "action", "quit", "--skip-confirmation"] - running: false - } - - Process { - id: logoutProcessHyprland - - command: ["hyprctl", "dispatch", "exit"] - running: false - } - - Process { - id: logoutProcess - - command: ["loginctl", "terminate-user", Quickshell.env("USER")] - running: false - } - - NPanel { - id: powerMenu - - anchors.top: powerButton.bottom - anchors.right: powerButton.right - - Rectangle { - width: 160 * scaling - height: 220 * scaling - color: Colors.surface - radius: 8 * scaling - border.color: Colors.outline - border.width: 1 * scaling - visible: true - z: 9999 - anchors.top: parent.top - anchors.right: parent.right - anchors.rightMargin: 32 * scaling - anchors.topMargin: powerButton.y + powerButton.height + 48 * scaling - - // Prevent closing when clicking in the panel bg - MouseArea { - anchors.fill: parent - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: 8 * scaling - spacing: 4 * scaling - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 36 * scaling - radius: 6 * scaling - color: lockButtonArea.containsMouse ? Colors.accentPrimary : "transparent" - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 12 * scaling - anchors.rightMargin: 12 * scaling - - Row { - id: lockRow - spacing: 8 * scaling - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - Text { - text: "lock_outline" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 * scaling - color: lockButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - - Text { - text: "Lock Screen" - font.pixelSize: 14 * scaling - color: lockButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - } - } - - MouseArea { - id: lockButtonArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - lockScreen.locked = true - systemMenu.visible = false - } - } - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 36 * scaling - radius: 6 * scaling - color: suspendButtonArea.containsMouse ? Colors.accentPrimary : "transparent" - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 12 * scaling - anchors.rightMargin: 12 * scaling - - Row { - id: suspendRow - spacing: 8 * scaling - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - Text { - text: "bedtime" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 * scaling - color: suspendButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - - Text { - text: "Suspend" - font.pixelSize: 14 * scaling - color: suspendButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - } - } - - MouseArea { - id: suspendButtonArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - suspend() - systemMenu.visible = false - } - } - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 36 * scaling - radius: 6 * scaling - color: rebootButtonArea.containsMouse ? Colors.accentPrimary : "transparent" - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 12 * scaling - anchors.rightMargin: 12 * scaling - - Row { - id: rebootRow - spacing: 8 * scaling - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - Text { - text: "refresh" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 * scaling - color: rebootButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - - Text { - text: "Reboot" - font.pixelSize: 14 * scaling - color: rebootButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - } - } - - MouseArea { - id: rebootButtonArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - reboot() - systemMenu.visible = false - } - } - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 36 * scaling - radius: 6 * scaling - color: logoutButtonArea.containsMouse ? Colors.accentPrimary : "transparent" - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 12 * scaling - anchors.rightMargin: 12 * scaling - - Row { - id: logoutRow - spacing: 8 * scaling - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - Text { - text: "exit_to_app" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 * scaling - color: logoutButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - - Text { - text: "Logout" - font.pixelSize: 14 * scaling - color: logoutButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - } - } - - MouseArea { - id: logoutButtonArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - logout() - systemMenu.visible = false - } - } - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 36 * scaling - radius: 6 * scaling - color: shutdownButtonArea.containsMouse ? Colors.accentPrimary : "transparent" - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 12 * scaling - anchors.rightMargin: 12 * scaling - - Row { - id: shutdownRow - spacing: 8 * scaling - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - Text { - text: "power_settings_new" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 * scaling - color: shutdownButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - - Text { - text: "Shutdown" - font.pixelSize: 14 * scaling - color: shutdownButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 * scaling - } - } - } - - MouseArea { - id: shutdownButtonArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - shutdown() - systemMenu.visible = false - } - } - } - } - } - } } diff --git a/Modules/SidePanel/PowerMenu.qml b/Modules/SidePanel/PowerMenu.qml new file mode 100644 index 0000000..9fcdb6d --- /dev/null +++ b/Modules/SidePanel/PowerMenu.qml @@ -0,0 +1,400 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import qs.Services +import qs.Widgets + +NPanel { + id: powerMenu + visible: false + + // Anchors will be set by the parent component + + function show() { + visible = true + } + + function hide() { + visible = false + } + + // Close menu when clicking outside + Connections { + target: Quickshell + function onMousePressed() { + if (powerMenu.visible && !powerMenu.contains(Quickshell.mousePosition)) { + powerMenu.hide() + } + } + } + + Rectangle { + width: 160 * scaling + height: 220 * scaling + color: Colors.surface + radius: 8 * scaling + border.color: Colors.outline + border.width: 1 * scaling + visible: true + z: 9999 + anchors.top: parent.top + anchors.right: parent.right + anchors.rightMargin: 32 * scaling + anchors.topMargin: 86 * scaling + + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + onClicked: { + + // Prevent event bubbling + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 * scaling + spacing: 4 * scaling + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: lockButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: lockRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "lock_outline" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: lockButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Lock Screen" + font.pixelSize: 14 * scaling + color: lockButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: lockButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // TODO: Implement lock screen functionality + console.log("Lock screen requested") + powerMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: suspendButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: suspendRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "bedtime" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: suspendButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Suspend" + font.pixelSize: 14 * scaling + color: suspendButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: suspendButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + suspend() + powerMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: rebootButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: rebootRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "refresh" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: rebootButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Reboot" + font.pixelSize: 14 * scaling + color: rebootButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: rebootButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + reboot() + powerMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: logoutButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: logoutRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "exit_to_app" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: logoutButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Logout" + font.pixelSize: 14 * scaling + color: logoutButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: logoutButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + logout() + powerMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: shutdownButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: shutdownRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "power_settings_new" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: shutdownButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Shutdown" + font.pixelSize: 14 * scaling + color: shutdownButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: shutdownButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + shutdown() + powerMenu.visible = false + } + } + } + } + } + + // ---------------------------------- + // System functions + function logout() { + if (Workspaces.isNiri) { + logoutProcessNiri.running = true + } else if (Workspaces.isHyprland) { + logoutProcessHyprland.running = true + } else { + console.warn("No supported compositor detected for logout") + } + } + + function suspend() { + suspendProcess.running = true + } + + function shutdown() { + shutdownProcess.running = true + } + + function reboot() { + rebootProcess.running = true + } + + Process { + id: shutdownProcess + + command: ["shutdown", "-h", "now"] + running: false + } + + Process { + id: rebootProcess + + command: ["reboot"] + running: false + } + + Process { + id: suspendProcess + + command: ["systemctl", "suspend"] + running: false + } + + Process { + id: logoutProcessNiri + + command: ["niri", "msg", "action", "quit", "--skip-confirmation"] + running: false + } + + Process { + id: logoutProcessHyprland + + command: ["hyprctl", "dispatch", "exit"] + running: false + } + + Process { + id: logoutProcess + + command: ["loginctl", "terminate-user", Quickshell.env("USER")] + running: false + } +} \ No newline at end of file