Watch
1
0
Fork
You've already forked souveraine
0

sheet: let zoom reach a window

pose has been in the wire since 2026-08-05 with no caller — poseActiveZone was
the only one, and TASK-60 deleted it. Zoom composes pixels the client already
drew; Resize places a new rectangle and the client relays out inside it.
This commit is contained in:
Fimeg 2026-08-16 10:31:18 -04:00
commit 75cb392924

View file

@ -59,6 +59,20 @@ Scope {
// tapping Resize twice in two visits should continue the cycle, not restart
// it and appear to do nothing.
property int sizeStep: 0
// Which pose Zoom last applied, on the scope for the same reason as
// `sizeStep`: the cycle has to survive the sheet closing.
property int poseStep: 0
// Whether the subject has left its zone's confinement, as the compositor
// last reported it. Read, never remembered a shell that kept its own idea
// of this would draw Unfloat on a window something else had already put
// back.
readonly property bool targetFloating: {
for (const w of ViewtopControl.windows)
if (w.id === scope.target)
return w.floating === true;
return false;
}
// How long a hold on Close means kill. Longer than a tap could ever be,
// short enough that a stuck app does not feel like a negotiation.
@ -364,6 +378,59 @@ Scope {
}
}
SheetButton {
icon: "zoom_out_map"
label: "Zoom"
// The `pose` verb, reaching a window for the first time.
//
// `pose` has existed in the wire and the compositor since
// 2026-08-05 it genuinely scales content, not only moves
// it and **nothing in the shell has ever called it**.
// `poseActiveZone` did, and TASK-60 deleted that wrapper as
// the shell's half of the two-writer bug, which took the
// only caller with it. Casey, 2026-08-16: *"I can move
// windows but the zooming them in and out didn't work
// yet."* It did not work because nobody asked.
//
// Distinct from Resize, and the difference is load-bearing:
// Resize `place`s a different *rectangle* and the client
// relays out inside it. Zoom composes the pixels it already
// drew the client is never told, its layout does not
// move, and the compositor maps input back through the
// inverse so a shrunken window is still touchable where it
// is drawn.
//
// Identity is a step in the cycle rather than a separate
// Reset: a pose the user cannot get out of with the button
// they got into it with is a trap.
onTapped: {
scope.poseStep = (scope.poseStep + 1) % 3;
const scale = [1.0, 0.85, 0.7][scope.poseStep];
ViewtopControl.pose(scope.target, scale, 0.0, 0.5, 0.5);
}
}
SheetButton {
icon: scope.targetFloating ? "picture_in_picture_off"
: "picture_in_picture"
label: scope.targetFloating ? "Unfloat" : "Float"
// A float leaves the zone's confinement it stops being
// the zone's business, so the strip no longer counts it
// when deciding whether a zone still holds anything.
//
// Which is exactly why the label has to flip: a float is
// the one window you can no longer find by remembering
// which zone you left it on, so the way back out must be
// the same button, in the same place, saying so.
onTapped: {
if (scope.targetFloating)
ViewtopControl.unfloat(scope.target);
else
ViewtopControl.float(scope.target);
ViewtopControl.refreshWindows();
}
}
SheetButton {
icon: "splitscreen"
label: "Split"