feat(quickshell): give the shell the polkit agent and one shared scrim
Moves PolkitPrompt from shell.qml into HyprChromeShell. Whether a prompt is open is shell state by the same rule as the screen and the layer pair: two surfaces read it. The prompt no longer carries a backdrop of its own. There is one ChromeBackdrop per output and a prompt raises them all, so a prompt over an already-expanded rail reuses the scrim that is there rather than stacking a second one on it, and a prompt over a collapsed rail expands that same scrim from its bar-height band to the whole output. The layer pair now keys off `scrimUp` (expanded OR prompting) rather than off the density, which keeps bar and backdrop exactly one level apart in every state. A prompt over a collapsed rail raises both: BACKGROUND sits under ordinary windows so a scrim there dims nothing, and the bar has to stay one above the scrim or the shell dims its own chrome. The rail is raised but stays collapsed — its layer answers to the scrim, its height to `expanded`. Outputs the rail does not live on get a scrim only while a prompt is up; a modal that dims one monitor and leaves the others lit does not read as modal. Expanding the rail still dims only the rail's screen, which is the existing behaviour and the right one. The dialog follows Hyprland.focusedMonitor rather than the rail's screen — a password prompt belongs where the user is looking — matched by name against Quickshell.screens, falling back to the rail's screen rather than to nothing. SUPER A is frozen while a prompt is open, and dropped rather than queued, so the rail does not spring open the moment the dialog goes. Verified on two monitors via hyprctl layers, both densities, plus the toggle block with an odd number of presses (two cancel out and prove nothing). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GAq2kKCLazZmrKvkd3akud
This commit is contained in:
@@ -40,6 +40,12 @@ import qs.HyprChrome.Theme
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
// Which output the dialog appears on. Driven by the shell, which puts it on
|
||||
// the focused monitor rather than on the rail's — a password prompt belongs
|
||||
// where the user is looking. Left unset it falls back to whatever screen
|
||||
// quickshell picks, which is right for a single-monitor session.
|
||||
property var screen: null
|
||||
|
||||
// Where the flow's identity list is currently pointed. Held here rather
|
||||
// than read back off the flow because the content addresses identities by
|
||||
// index and AuthFlow addresses them by object.
|
||||
@@ -50,6 +56,15 @@ Scope {
|
||||
// shows a dialog, which looks exactly like "no one asked for authorization".
|
||||
readonly property alias registered: agent.isRegistered
|
||||
|
||||
// Whether a request is being presented. Both surfaces read it, so it is
|
||||
// decided once here rather than each deriving it — the scrim and the dialog
|
||||
// must come and go on the same frame.
|
||||
//
|
||||
// isCompleted is checked as well as null: the flow reports its terminal
|
||||
// state before the agent drops it, and neither surface should linger for
|
||||
// those frames over a request that has already been decided.
|
||||
readonly property bool prompting: root.flow !== null && !root.flow.isCompleted
|
||||
|
||||
// Reset per REQUEST, not per window show.
|
||||
//
|
||||
// A second request supersedes the first by swapping `flow` while the dialog
|
||||
@@ -136,13 +151,16 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
// No scrim of its own. The shell owns the single ChromeBackdrop and raises
|
||||
// it for either cause — an expanded rail or an open prompt — so a prompt
|
||||
// arriving over an already-expanded rail reuses the scrim that is already
|
||||
// there instead of stacking a second one on top of it. `prompting` above is
|
||||
// what the shell reads to decide. See HyprChromeShell.
|
||||
PanelWindow {
|
||||
id: win
|
||||
|
||||
// isCompleted is checked as well as null: the flow reports its terminal
|
||||
// state before the agent drops it, and the dialog should not linger for
|
||||
// those frames showing a request that has already been decided.
|
||||
visible: root.flow !== null && !root.flow.isCompleted
|
||||
screen: root.screen
|
||||
visible: root.prompting
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// A real modal — unlike the rest of the rail, this one must take the
|
||||
@@ -158,14 +176,11 @@ Scope {
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// Scrim. No click-to-dismiss: a polkit request is answered or
|
||||
// explicitly cancelled, and losing one to a stray click on the
|
||||
// wallpaper would leave the caller waiting with no visible reason.
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.surface
|
||||
opacity: 0.72
|
||||
}
|
||||
// No scrim here — ChromeBackdrop above draws it. This surface stays
|
||||
// transparent but unmasked, so it still swallows clicks across the
|
||||
// whole output: a polkit request is answered or explicitly cancelled,
|
||||
// and losing one to a stray click on the wallpaper would leave the
|
||||
// caller waiting with no visible reason.
|
||||
|
||||
PolkitPromptContent {
|
||||
id: content
|
||||
|
||||
Reference in New Issue
Block a user