updated design
This commit is contained in:
@@ -80,7 +80,13 @@ contentPad * 2`), which keeps the shape maths out of binding loops.
|
||||
| CPU/MEM/GPU | bar | btop | | | | resource dashboard |
|
||||
|
||||
Popouts open on a short dwell and stay while the pointer is over the trigger or
|
||||
the panel. Clicking pins one open.
|
||||
the panel. Clicking pins one open. A shell-wide coordinator keeps only one
|
||||
transient surface mapped at a time, including tray context menus, so sweeping
|
||||
across the rail cannot stack several animated panels over one another.
|
||||
|
||||
The power menu is fully keyboard-driven as well as clickable: `Up`/`Down` or
|
||||
`J`/`K` moves the active slab, `Enter`/`Space` confirms it, and `Escape`
|
||||
dismisses the screen.
|
||||
|
||||
## Keybinds
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@ Item {
|
||||
property color accent: Theme.primary
|
||||
property color hoverFill: Theme.accent
|
||||
property bool destructive: false
|
||||
property bool selected: false
|
||||
property real lean: Theme.skew
|
||||
property real lunge: 6
|
||||
property bool wide: false
|
||||
|
||||
signal clicked()
|
||||
|
||||
readonly property bool hovered: hover.hovered
|
||||
readonly property bool hovered: hover.hovered || root.selected
|
||||
readonly property bool pressed: tap.pressed
|
||||
|
||||
implicitWidth: wide ? 260 : (row.implicitWidth + panel.contentPad * 2)
|
||||
|
||||
@@ -103,6 +103,7 @@ PopupWindow {
|
||||
}
|
||||
|
||||
function open() {
|
||||
PopupCoordinator.claim(root);
|
||||
exitAnim.stop();
|
||||
shown = true;
|
||||
enterAnim.restart();
|
||||
@@ -183,6 +184,7 @@ PopupWindow {
|
||||
root.pinned = false;
|
||||
contentHolder.x = root.bleed;
|
||||
contentHolder.y = root.bleed;
|
||||
PopupCoordinator.release(root);
|
||||
root.dismissedFully();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
// One transient surface at a time. Without a shared owner, a quick sweep over
|
||||
// the bar/rail can leave several independently animated windows mapped on top
|
||||
// of each other. Every coordinated surface exposes dismiss().
|
||||
Singleton {
|
||||
property var active: null
|
||||
|
||||
function claim(surface: var) {
|
||||
if (active && active !== surface)
|
||||
active.dismiss();
|
||||
active = surface;
|
||||
}
|
||||
|
||||
function release(surface: var) {
|
||||
if (active === surface)
|
||||
active = null;
|
||||
}
|
||||
|
||||
function dismissAll() {
|
||||
if (active)
|
||||
active.dismiss();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ Item {
|
||||
property string status: ""
|
||||
property bool failed: false
|
||||
property bool busy: pam.active
|
||||
readonly property real uiScale: Math.max(0.72, Math.min(1.15,
|
||||
Math.min(width / 1920, height / 1080)))
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
@@ -137,7 +139,9 @@ Item {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: parent.height * 0.2
|
||||
spacing: Theme.padM
|
||||
width: 300
|
||||
width: Math.min(300, parent.width * 0.28)
|
||||
scale: surface.uiScale
|
||||
transformOrigin: Item.TopRight
|
||||
|
||||
RailRow {
|
||||
width: parent.width
|
||||
@@ -181,8 +185,10 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: parent.height * 0.14
|
||||
width: 460
|
||||
width: Math.min(460, parent.width - Theme.padXL * 2)
|
||||
height: 150
|
||||
scale: surface.uiScale
|
||||
transformOrigin: Item.Bottom
|
||||
|
||||
transform: Translate { id: shakeShift }
|
||||
|
||||
@@ -206,7 +212,7 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: prompt.bottom
|
||||
anchors.topMargin: Theme.padL
|
||||
width: 380
|
||||
width: Math.min(380, authBlock.width - Theme.padL * 2)
|
||||
height: 56
|
||||
lean: 0.12
|
||||
chop: 16
|
||||
@@ -320,7 +326,7 @@ Item {
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Quickshell.env("USER") || "narl"
|
||||
text: Quickshell.env("USER") || "USER"
|
||||
color: Theme.muted
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: Theme.fsSmall
|
||||
@@ -349,7 +355,7 @@ Item {
|
||||
id: pam
|
||||
|
||||
config: "hyprlock"
|
||||
user: Quickshell.env("USER") || "narl"
|
||||
user: Quickshell.env("USER") || "USER"
|
||||
|
||||
onPamMessage: {
|
||||
if (pam.responseRequired) pam.respond(surface.entry);
|
||||
|
||||
@@ -26,9 +26,21 @@ PanelWindow {
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
readonly property bool open: Actions.powerMenuOpen
|
||||
property int selectedAction: 0
|
||||
|
||||
readonly property var actionModel: [
|
||||
{ label: "Lock", icon: "", danger: false, act: "lock" },
|
||||
{ label: "Suspend", icon: "", danger: false, act: "suspend" },
|
||||
{ label: "Hibernate", icon: "", danger: false, act: "hibernate" },
|
||||
{ label: "Log Out", icon: "", danger: true, act: "logout" },
|
||||
{ label: "Reboot", icon: "", danger: true, act: "reboot" },
|
||||
{ label: "Shut Down", icon: "", danger: true, act: "shutdown" }
|
||||
]
|
||||
|
||||
onOpenChanged: {
|
||||
if (open) {
|
||||
PopupCoordinator.dismissAll();
|
||||
selectedAction = 0;
|
||||
openAnim.restart();
|
||||
} else {
|
||||
closeAnim.restart();
|
||||
@@ -122,14 +134,7 @@ PanelWindow {
|
||||
Repeater {
|
||||
id: buttonRepeater
|
||||
|
||||
model: [
|
||||
{ label: "Lock", icon: "", danger: false, act: "lock" },
|
||||
{ label: "Suspend", icon: "", danger: false, act: "suspend" },
|
||||
{ label: "Hibernate", icon: "", danger: false, act: "hibernate" },
|
||||
{ label: "Log Out", icon: "", danger: true, act: "logout" },
|
||||
{ label: "Reboot", icon: "", danger: true, act: "reboot" },
|
||||
{ label: "Shut Down", icon: "", danger: true, act: "shutdown" }
|
||||
]
|
||||
model: root.actionModel
|
||||
|
||||
// A carrier item owns the staggered slide so the button keeps its
|
||||
// own hover transform.
|
||||
@@ -159,10 +164,12 @@ PanelWindow {
|
||||
text: entry.modelData.label
|
||||
icon: entry.modelData.icon
|
||||
destructive: entry.modelData.danger
|
||||
selected: root.selectedAction === entry.index
|
||||
accent: entry.modelData.danger ? Theme.accent : Theme.primary
|
||||
lean: Theme.skew
|
||||
lunge: -10
|
||||
onClicked: root.invoke(entry.modelData.act)
|
||||
onHoveredChanged: if (hovered) root.selectedAction = entry.index
|
||||
}
|
||||
|
||||
// Staggered entry, one button after another.
|
||||
@@ -211,7 +218,7 @@ PanelWindow {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 40
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "ESC TO CANCEL"
|
||||
text: "↑ ↓ SELECT ENTER CONFIRM ESC CANCEL"
|
||||
color: Theme.muted
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: Theme.fsMicro
|
||||
@@ -237,6 +244,21 @@ PanelWindow {
|
||||
anchors.fill: parent
|
||||
focus: root.open
|
||||
Keys.onEscapePressed: Actions.closePowerMenu()
|
||||
Keys.onPressed: event => {
|
||||
if (!root.open) return;
|
||||
if (event.key === Qt.Key_Up || event.key === Qt.Key_K) {
|
||||
root.selectedAction = (root.selectedAction + root.actionModel.length - 1)
|
||||
% root.actionModel.length;
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_J) {
|
||||
root.selectedAction = (root.selectedAction + 1) % root.actionModel.length;
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
|
||||
|| event.key === Qt.Key_Space) {
|
||||
root.invoke(root.actionModel[root.selectedAction].act);
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
|
||||
@@ -53,6 +53,7 @@ PopupWindow {
|
||||
|
||||
function open() {
|
||||
if (!menuHandle) return;
|
||||
PopupCoordinator.claim(root);
|
||||
closeAnim.stop();
|
||||
shown = true;
|
||||
openAnim.restart();
|
||||
@@ -68,6 +69,11 @@ PopupWindow {
|
||||
if (shown) close(); else open();
|
||||
}
|
||||
|
||||
// Shared name used by PopupCoordinator for every transient surface.
|
||||
function dismiss() {
|
||||
close();
|
||||
}
|
||||
|
||||
// The grab can only take hold once the popup is actually mapped. Asked for
|
||||
// in the same tick as `shown`, Hyprland has no surface to hand it and
|
||||
// clears it immediately — which closed the menu the instant it opened.
|
||||
@@ -145,6 +151,7 @@ PopupWindow {
|
||||
onFinished: {
|
||||
root.shown = false;
|
||||
holder.x = root.bleed;
|
||||
PopupCoordinator.release(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: row.entry.hasChildren ? 18 : 0
|
||||
visible: row.entry.hasChildren
|
||||
text: row.expanded ? "" : ""
|
||||
text: row.expanded ? "" : ""
|
||||
color: row.hovered ? Theme.ink : Theme.subtext
|
||||
font.family: Theme.fontIcon
|
||||
font.pixelSize: Theme.fsMicro
|
||||
|
||||
Reference in New Issue
Block a user