Review everything shipped this weekend, and fix what the reviewers caught

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 13:29:42 -04:00
parent ffce48964e
commit 07db1068f1
42 changed files with 959 additions and 219 deletions
+90 -16
View File
@@ -129,16 +129,34 @@ PanelWindow {
onTriggered: root.revealed = false
}
// Other modules (the bar, the capture overlay) read this. It is one
// shared flag but there is one Dock per monitor, so only the instance on
// the currently-focused monitor is allowed to write it -- otherwise
// whichever instance last changed reveal state would stomp the others,
// and a reader would see an arbitrary monitor's value. This scopes the
// flag to mean "is the dock revealed on the monitor the user is on",
// which is what a capture overlay or the bar actually care about.
// (A true per-monitor flag would need ShellState.dockRevealed itself to
// become keyed by screen, which is out of scope here -- see the report.)
readonly property bool isFocusedMonitorInstance: root.monitor === null || root.monitor === Hyprland.focusedMonitor
// ShellState.dockRevealed is one shared flag and there is one Dock per
// monitor, so exactly one instance may write it -- otherwise whichever
// instance last changed reveal state stomps the others and a reader gets an
// arbitrary monitor's answer. Scoped this way the flag means "is the dock
// revealed on the monitor the user is on", which is the only question a
// reader outside the dock can sensibly ask of it. (A genuinely per-monitor
// answer would need the property itself keyed by screen.)
//
// Two things disqualify an instance. One is not being on the focused
// monitor, which is the whole point. The other is not being on screen at
// all: a Dock whose screen is not in `dockScreens` is invisible, and an
// invisible dock reporting itself as revealed is a lie a reader acts on. An
// instance whose monitor could not be resolved -- created standalone, or
// asked before Hyprland has reported the screen -- knows nothing about
// which monitor the user is on, so it writes only when there is no focused
// monitor to be wrong about rather than stomping the instance that knows.
//
// Nothing reads the flag today: the bar and the capture overlay it was
// written for both stopped. It is written correctly rather than left
// half-wrong, and when ShellState is next opened the property, this
// arbitration and _syncShellState should go together.
readonly property bool isFocusedMonitorInstance: {
if (!root.onThisScreen)
return false;
if (root.monitor !== null)
return root.monitor === Hyprland.focusedMonitor;
return Hyprland.focusedMonitor === null;
}
onRevealedChanged: root._syncShellState()
onIsFocusedMonitorInstanceChanged: root._syncShellState()
@@ -183,6 +201,33 @@ PanelWindow {
Item {
id: maskItem
// Where the body comes to rest once the slide finishes, mirroring
// DockBody's own x/y bindings in their revealed case.
//
// The revealed region is built from these rather than from the
// body's live position. The body slides in over a couple of hundred
// milliseconds, and a region that follows it in is a region that is
// a few pixels tall on the frame the dock is summoned -- the exact
// frame the pointer that summoned it needs to be inside, and the
// exact frame it drops the hover and sends the dock back. Following
// the animation also means an input-region commit to the compositor
// on every one of those frames, for a rectangle that is only right
// on the last of them.
//
// Clamped rather than assigned outright so the mask still tracks
// the body if anything else ever moves it: the reveal only ever
// approaches these values from outside the screen edge.
readonly property real settledX: {
if (!root.vertical)
return body.x;
return root.position === "left"
? Math.max(body.x, root.tooltipSpace)
: Math.min(body.x, surface.width - body.width - root.tooltipSpace);
}
readonly property real settledY: root.vertical
? body.y
: Math.min(body.y, root.tooltipSpace)
x: {
if (!root.revealed)
return root.position === "right" ? surface.width - root.revealStripHeight : 0;
@@ -194,25 +239,27 @@ PanelWindow {
// to its own edge, which is x 0 on the left and the body on
// the right.
if (!root.vertical)
return body.x;
return root.position === "right" ? body.x : 0;
return maskItem.settledX;
return root.position === "right" ? maskItem.settledX : 0;
}
y: {
if (!root.revealed)
return root.vertical ? 0 : surface.height - root.revealStripHeight;
return body.y;
return maskItem.settledY;
}
width: {
if (!root.revealed)
return root.vertical ? root.revealStripHeight : surface.width;
return root.vertical
? (root.position === "right" ? surface.width - body.x : body.x + body.width)
? (root.position === "right"
? surface.width - maskItem.settledX
: maskItem.settledX + body.width)
: body.width;
}
height: {
if (!root.revealed)
return root.vertical ? surface.height : root.revealStripHeight;
return root.vertical ? body.height : surface.height - body.y;
return root.vertical ? body.height : surface.height - maskItem.settledY;
}
}
@@ -226,7 +273,7 @@ PanelWindow {
body.dismissPreview();
dockContextMenu.anchorItem = anchorItem;
dockContextMenu.app = app;
dockContextMenu.visible = true;
dockContextMenu.requested = true;
}
vertical: root.vertical
@@ -284,6 +331,33 @@ PanelWindow {
id: dockContextMenu
}
// The safety net for a latched interaction flag.
//
// Every term of `interactionHeld` is cleared by an event that can go
// missing: a drag by its release, which a stolen grab eats; a preview by
// the pointer leaving, which a destroyed anchor never reports; a menu by a
// row being chosen, which a menu whose anchor died is never offered. Any
// one of them left set holds the dock revealed with the pointer nowhere
// near it, and there is no gesture that gets it back -- the flag is stuck,
// so the dock is out until the shell restarts.
//
// Eight seconds of held-but-untouched is not a gesture. The pointer being
// on the previews or on the menu counts as touched, because both are their
// own surfaces and leaving the dock is how you reach them.
Timer {
id: interactionWatchdog
interval: 8000
running: root.interactionHeld && !pointer.hovered
&& !dockPreviews.hovered && !dockContextMenu.hovered
onTriggered: {
body.cancelDrag();
body.dismissPreview();
dockContextMenu.requested = false;
}
}
// Its own surface rather than something drawn inside the dock: the dock's
// input mask is a thin strip when hidden and the bar's own rectangle when
// shown, and widening it to cover a preview would hand the dock every