quickshell: fling the worm when a panel pops out on top of him

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-08-04 11:14:09 +01:00
parent 25819bd901
commit bdb3ab0c75

View file

@ -1029,7 +1029,7 @@ in
// Snapshot last, so a rect is always compared against
// where it was exactly one tick ago. vector4d is a
// value type, so this copies numbers, not handles.
prevRects = pushers.slice();
prevRects = pushers.map(p => p.rect);
}
}
@ -1060,12 +1060,18 @@ in
}
// Shoved by the panels
// Bound to the very rects the frame shader melts into the
// chrome (cx, cy, hw, hh; hw <= 0 means "not on screen"), so
// what sweeps him aside is the panel outline you can actually
// see growing, not the cards riding inside it.
// Each pusher is { rect, push }: `rect` is the very vector4d
// the frame shader melts into the chrome (cx, cy, hw, hh;
// hw <= 0 means "not on screen"), so what sweeps him aside is
// the panel outline you can actually see. `push` is the unit
// direction that panel slides out in a constant of the
// design, and not something worth deriving: the morph is
// 280ms of OutExpo, nearly all of it landing inside a single
// 33ms tick, so there is rarely a partly-grown rect to read a
// direction off of.
property var pushers: []
property var prevRects: []
readonly property real shoveSpeed: 18
function inRect(p, r) {
return Math.abs(p.x - r.x) <= r.z + 6 && Math.abs(p.y - r.y) <= r.w + 6;
@ -1074,16 +1080,28 @@ in
function checkShove() {
let head = body[0], tail = body[segments - 1];
for (let i = 0; i < pushers.length; i++) {
let r = pushers[i], q = prevRects[i];
let r = pushers[i].rect, q = prevRects[i];
if (!r || r.z < 0.5) continue;
if (!inRect(head, r)) continue;
if (!q || q.z < 0.5) { q = r; } // appeared already overlapping him
// Only an edge that is travelling outward carries him,
// and it carries him the way it is going: the dropdown
// grows rightward out of the bar, the launcher grows
// up out of the floor. A panel closing just lets go.
let dx = Math.max(0, (r.x + r.z) - (q.x + q.z)) + Math.min(0, (r.x - r.z) - (q.x - q.z));
let dy = Math.max(0, (r.y + r.w) - (q.y + q.w)) + Math.min(0, (r.y - r.w) - (q.y - q.w));
// Any part of him in the way is enough to be swept;
// only the head decides a turn-around.
let touched = false;
for (let s = 0; s < body.length && !touched; s++)
touched = inRect(body[s], r);
if (!touched) continue;
// Either it was not there a tick ago the pop-out,
// and the usual case given how fast the morph is or
// an edge has swept outward over him since. Both mean
// the panel arrived where he was standing.
let appeared = !q || q.z < 0.5;
let grew = !appeared && (
(r.x + r.z) - (q.x + q.z) > 1 || (q.x - q.z) - (r.x - r.z) > 1
|| (r.y + r.w) - (q.y + q.w) > 1 || (q.y - q.w) - (r.y - r.w) > 1);
if (appeared || grew) {
worm.loose = true;
worm.vx = pushers[i].push.x * shoveSpeed;
worm.vy = pushers[i].push.y * shoveSpeed - 3; // -3: a little air
return;
}
// Sitting still, it is simply solid: he walks into the
// panel and turns around rather than crawling through
// it, which is what made an open dropdown feel like it
@ -1092,15 +1110,7 @@ in
// inside (he landed there) means walking on is the way
// out, and reversing on that would just buzz him in
// place against both edges.
if (Math.abs(dx) < 1 && Math.abs(dy) < 1) {
if (!inRect(tail, r)) reverse();
return;
}
worm.loose = true;
// Clamped: the panel's first frame of OutExpo covers
// most of its width, which would fire him off screen.
worm.vx = Math.max(-22, Math.min(22, dx * 1.4));
worm.vy = Math.max(-22, Math.min(22, dy * 1.4)) - 3; // -3: a little air
if (inRect(head, r) && !inRect(tail, r)) reverse();
return;
}
}
@ -6752,9 +6762,16 @@ in
// Last child, so he crawls over the panels rather than under
// them. Purely decorative, see Weird.qml. The pushers are the
// same rects the frame shader draws, so what shoves him is the
// panel outline itself as it grows.
Weird { pushers: [chromeSdf.panel, chromeSdf.launcher] }
// same rects the frame shader draws, paired with the way each
// one slides out: dropdowns and toasts rightward out of the
// bar column, the launcher up out of the bottom frame band.
Weird {
pushers: [
{ rect: chromeSdf.panel, push: Qt.point(1, 0) },
{ rect: chromeSdf.toast, push: Qt.point(1, 0) },
{ rect: chromeSdf.launcher, push: Qt.point(0, -1) }
]
}
}
'';
};