Commit aa790e7e33a0
tarball2 files changed+47-19
@@ -86,8 +86,9 @@ Running log of settled decisions and open questions. Research backing lives in `
8686
- Shared-separator rule, plans of 1 to 4 phases, the 4-phase grid guarantees a plan for any input. Pinch and lerp are safety nets only.8787
- Window moves and block moves both exist as commands. Tag reordering plans with rigid blocks.8888
- One curve per motion group.89
-- Floating to tiled is phased. The slot opens concurrently, the floater travels one axis per phase, then lands.90
-- Open. Seed shape, whether landing may resize both axes at once, lane extent, and the layout model for interspersed tags.89
+- Floating windows travel above the tiled layer, exempt from the overlap rule.90
+- Seeds start as a line on the old boundary between neighbours.91
+- Open. Floating choreography (slot opens last, direct glide, slot opens first), whether landing may resize both axes at once, lane extent, and the layout model for interspersed tags.9192
- Input hit-tests against final layout geometry, never animated geometry.9293
- smithay is pinned by git rev. The repo uses jj.9394
@@ -141,7 +141,12 @@
141141
<div class="demo" id="d7"></div>142142
143143
<h2>12. Floating to tiled</h2>144
-<p>A floating window joining the tiled layout is phased, unlike a spawn. The tiled windows open a slot for it, concurrently and following the same rules as an insertion. Meanwhile the floating window travels above them, one axis per phase, until it sits over the slot. In the last phase it falls into place. Lifting a tiled window back out runs the same steps in reverse. The floating window is drawn above the tiled layer and is exempt from the overlap rule while it travels.</p>144
+<p>A floating window joining the tiled layout travels above the tiled layer and is exempt from the overlap rule while it does. The mode button cycles three choreographies. Lifting out plays each one in reverse.</p>145
+<ul>146
+ <li><strong>Slot opens last</strong> (default). F travels one axis per phase until it hovers over where its slot will be. Only then does the gap open, and F lands into it as it widens, in the same final phase.</li>147
+ <li><strong>Direct glide.</strong> F lerps straight into the slot while the gap opens, in one phase.</li>148
+ <li><strong>Slot opens first.</strong> The gap opens while F is still travelling, then F lands. This was draft 2 behaviour.</li>149
+</ul>145150
<div class="demo" id="d10"></div>146151
147152
<h2>13. Fallbacks</h2>@@ -211,11 +216,11 @@
211216
<li>Orthogonal is the default and the flagship look. Direct is a toggle.</li>212217
<li>Window moves and block moves both exist as commands. Tag reordering moves rigid blocks.</li>213218
<li>One curve per motion group, bounded by phase time rather than amount of motion.</li>214
- <li>Floating to tiled is phased, travel one axis per phase, then land.</li>219
+ <li>Seeds start as a line on the old boundary.</li>215220
<li>The planner must never need a fallback. Pinch and lerp stay as safety nets to try.</li>216221
</ul>217222
<h3>Open</h3>218
-<div class="q"><strong>Seed shape.</strong> Line on the old boundary, point on the old boundary, or target centre (section 4). Line is always one phase. Point looks more like your "grow from centre" but costs a phase in orthogonal style.</div>223
+<div class="q"><strong>Floating choreography.</strong> Slot opens last, direct glide, or slot opens first (section 12).</div>219224
<div class="q"><strong>Landing in orthogonal style.</strong> The final "falls into place" step resizes both width and height at once. Is that an accepted exception, or must it also split into two phases?</div>220225
<div class="q"><strong>Lane extent.</strong> Strips span only the band of the involved windows. Spanning the whole output gives thicker lanes for small windows, but disturbs more of the screen.</div>221226
<div class="q"><strong>The layout model (section 16).</strong> One tree per output with tags as a filter, or something else. You raised the case of windows being closed and moved while others are hidden. In the one-tree model, hidden windows keep their slot, closing removes only that leaf, and moves are made relative to visible neighbours, so hidden siblings stay attached to whichever visible neighbour they were next to. That rule is what needs discussing.</div>@@ -773,6 +778,7 @@ function tagLayout(I, view2, order) {
773778
774779
{ // d6 interrupts775780
const W = 640, H = 260, d = new Demo('d6', W, H, { graph: true }), I = inner(W, H);781
+ const names = ["A B C", "C A B", "A wide", "B C A", "close B"];776782
const targets = [777783
() => splitX(I, ['A', 'B', 'C']),778784
() => splitX(I, ['C', 'A', 'B']),@@ -783,7 +789,7 @@ function tagLayout(I, view2, order) {
783789
const withB = L => { if (!L.B) L.B = R(I.x, I.y, 0, 0); return L; };784790
let idx = 0;785791
d.set(withB(targets[0]()));786
- const next = () => { idx = (idx + 1) % targets.length; d.go(withB(targets[idx]())); };792
+ const next = () => { idx = (idx + 1) % targets.length; d.go(withB(targets[idx]())); d.lastKind += ` · target "${names[idx]}"`; };787793
d.button('Next target', next);788794
d.button('Storm (8 rapid interrupts)', () => {789795
let n = 0;@@ -889,7 +895,8 @@ for (const [id, names] of [
889895
{ // d10 floating to tiled890896
const W = 640, H = 300, d = new Demo('d10', W, H, { floating: ['F'] }), I = inner(W, H);891897
const FL = R(220, 40, 220, 130);892
- let tiled = false;898
+ const modes = ['slot opens last', 'direct glide', 'slot opens first'];899
+ let tiled = false, mode = 0;893900
const tiledL = () => splitX(I, ['A', 'B', 'F', 'C']);894901
const floatL = () => ({ ...splitX(I, ['A', 'B', 'C']), F: FL });895902
const ortho = () => STYLE === 'orthogonal';@@ -898,28 +905,48 @@ for (const [id, names] of [
898905
for (let i = 0; i <= n; i++) { const t = tiledAt(i); keys.push({ A: t.A, B: t.B, C: t.C, F: fAt(i) }); }899906
return { kind, keys, durs: keys.slice(1).map(() => PHASE_MS) };900907
};908
+ const slotPlan = (from, to, opening) => {909
+ const fromBx = (from.B.x + from.B.w + from.C.x) / 2, toBx = (to.B.x + to.B.w + to.C.x) / 2;910
+ return opening911
+ ? plan({ A: from.A, B: from.B, C: from.C, P: R(fromBx, I.y, 0, I.h) }, { A: to.A, B: to.B, C: to.C, P: to.F })912
+ : plan({ A: from.A, B: from.B, C: from.C, P: from.F }, { A: to.A, B: to.B, C: to.C, P: R(toBx, I.y, 0, I.h) });913
+ };914
+ const at = (tp, i) => tp.keys[Math.max(0, Math.min(i, tp.durs.length))];901915
const floatIn = from => {902
- const slot = tiledL().F, to = tiledL();903
- const bx = (from.B.x + from.B.w + from.C.x) / 2;904
- const tp = plan({ A: from.A, B: from.B, C: from.C, P: R(bx, I.y, 0, I.h) }, { A: to.A, B: to.B, C: to.C, P: slot });905
- const k = tp.durs.length, f = from.F;916
+ const to = tiledL(), slot = to.F, tp = slotPlan(from, to, true), k = tp.durs.length, f = from.F;917
+ if (modes[mode] === 'direct glide') {918
+ const n = Math.max(1, k);919
+ return assemble(n, i => at(tp, i), i => i === 0 ? f : slot, `glide in, slot ${tp.kind}`);920
+ }906921
const cx = slot.x + slot.w / 2 - f.w / 2, cy = slot.y + slot.h / 2 - f.h / 2;907922
const path = ortho() ? [f, R(cx, f.y, f.w, f.h), R(cx, cy, f.w, f.h)] : [f, R(cx, cy, f.w, f.h)];908
- const n = Math.max(path.length - 1, k) + 1;909
- return assemble(n, i => tp.keys[Math.min(i, k)], i => i === n ? slot : path[Math.min(i, path.length - 1)], `float in, slot ${tp.kind}`);923
+ const travel = path.length - 1;924
+ if (modes[mode] === 'slot opens last') {925
+ const n = travel + Math.max(1, k);926
+ return assemble(n, i => at(tp, i - (n - k)), i => i === n ? slot : path[Math.min(i, travel)], `float in, slot ${tp.kind}`);927
+ }928
+ const n = Math.max(travel, k) + 1;929
+ return assemble(n, i => at(tp, i), i => i === n ? slot : path[Math.min(i, travel)], `float in, slot ${tp.kind}`);910930
};911931
const floatOut = from => {912
- const slot = from.F, to = floatL();913
- const bx = (to.B.x + to.B.w + to.C.x) / 2;914
- const tp = plan({ A: from.A, B: from.B, C: from.C, P: slot }, { A: to.A, B: to.B, C: to.C, P: R(bx, I.y, 0, I.h) });915
- const k = tp.durs.length;932
+ const to = floatL(), slot = from.F, tp = slotPlan(from, to, false), k = tp.durs.length;933
+ if (modes[mode] === 'direct glide') {934
+ const n = Math.max(1, k);935
+ return assemble(n, i => at(tp, i), i => i === 0 ? slot : FL, `glide out, slot ${tp.kind}`);936
+ }916937
const lift = R(slot.x + slot.w / 2 - FL.w / 2, slot.y + slot.h / 2 - FL.h / 2, FL.w, FL.h);917938
const path = ortho() ? [slot, lift, R(lift.x, FL.y, FL.w, FL.h), FL] : [slot, lift, FL];918
- const n = Math.max(path.length - 1, k + 1);919
- return assemble(n, i => tp.keys[Math.max(0, Math.min(i - 1, k))], i => path[Math.min(i, path.length - 1)], `lift out, slot ${tp.kind}`);939
+ const travel = path.length - 1;940
+ if (modes[mode] === 'slot opens last') {941
+ const n = Math.max(travel, k);942
+ return assemble(n, i => at(tp, i), i => path[Math.min(i, travel)], `lift out, slot ${tp.kind}`);943
+ }944
+ const n = Math.max(travel, k + 1);945
+ return assemble(n, i => at(tp, i - 1), i => path[Math.min(i, travel)], `lift out, slot ${tp.kind}`);920946
};921947
d.set(floatL());922948
d.button('Tile F / float F', () => { tiled = !tiled; d.go(tiled ? tiledL() : floatL(), { planFn: tiled ? floatIn : floatOut }); });949
+ const mb = d.button('mode: ' + modes[0], () => { mode = (mode + 1) % modes.length; mb.textContent = 'mode: ' + modes[mode]; });923950
}924951
925952
document.getElementById('phaseMs').oninput = e => { PHASE_MS = +e.target.value; document.getElementById('phaseMsOut').textContent = PHASE_MS + ' ms'; };