@moku-labs/game 0.15.7 → 0.15.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/page.d.mts +1 -1
- package/dist/app/system.d.mts +2 -2
- package/dist/app.d.mts +2 -2
- package/dist/app.mjs +1 -1
- package/dist/{commands-C8aWbThW.d.mts → commands-DWyII1jn.d.mts} +10 -2
- package/dist/control.d.mts +1 -1
- package/dist/control.mjs +1 -1
- package/dist/{index-CU4Oc2Qv.d.mts → index-B3dfnsUZ.d.mts} +6 -5
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/inspect.d.mts +2 -2
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{run-DlxDjgOO.mjs → run-BqYmcNo7.mjs} +38 -5
- package/dist/{runtime-Bm_guAJR.d.mts → runtime-gnMApclv.d.mts} +1 -1
- package/dist/{src-nJYt0LMr.mjs → src-DNVAJHwd.mjs} +905 -173
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs +1 -1
- package/dist/{types-MocH_igX.d.mts → types-BcEtn9XV.d.mts} +3 -3
- package/dist/{types-BkcgFOoB.d.mts → types-CPxx3NSG.d.mts} +58 -6
- package/dist/{types-CZGuMhV8.d.mts → types-CYoGvBkh.d.mts} +113 -5
- package/dist/{visual-BV2A3kpr.d.mts → visual-CxQ4Iqd3.d.mts} +3 -3
- package/dist/visual.d.mts +1 -1
- package/dist/visual.mjs +1 -1
- package/llms.txt +4 -4
- package/package.json +1 -1
|
@@ -3337,7 +3337,7 @@ function deliver$1(state, answer) {
|
|
|
3337
3337
|
* @param ctx - Domain context of the flow plugin.
|
|
3338
3338
|
* @param answer - The answer the closed gate refused.
|
|
3339
3339
|
*/
|
|
3340
|
-
function hold(ctx, answer) {
|
|
3340
|
+
function hold$1(ctx, answer) {
|
|
3341
3341
|
if (ctx.state.fx.mode === "fast") return;
|
|
3342
3342
|
if (!ctx.deps.time.isRunning()) return;
|
|
3343
3343
|
if (ctx.config.holdMs <= 0 && ctx.state.gate.held !== void 0) return;
|
|
@@ -3376,7 +3376,7 @@ function createGateApi(ctx) {
|
|
|
3376
3376
|
return {
|
|
3377
3377
|
answer: (answer) => {
|
|
3378
3378
|
if (ctx.state.gate.open === void 0) {
|
|
3379
|
-
hold(ctx, answer);
|
|
3379
|
+
hold$1(ctx, answer);
|
|
3380
3380
|
return false;
|
|
3381
3381
|
}
|
|
3382
3382
|
if (!accepts(ctx.state.gate, answer)) return false;
|
|
@@ -5506,7 +5506,7 @@ function startDeadline(timeoutMs) {
|
|
|
5506
5506
|
* @param running - The promise of `run()`.
|
|
5507
5507
|
* @returns `true` when the loop settled, `false` when the deadline won and the loop still runs.
|
|
5508
5508
|
*/
|
|
5509
|
-
async function settle$
|
|
5509
|
+
async function settle$2(ctx, running) {
|
|
5510
5510
|
const quiet = running.then(noop, noop);
|
|
5511
5511
|
const deadline = startDeadline(ctx.config.settleTimeoutMs);
|
|
5512
5512
|
try {
|
|
@@ -5535,7 +5535,7 @@ async function stopRunner(ctx) {
|
|
|
5535
5535
|
stop.abort("stop");
|
|
5536
5536
|
state.abort = stop;
|
|
5537
5537
|
}
|
|
5538
|
-
const settled = running === void 0 || await settle$
|
|
5538
|
+
const settled = running === void 0 || await settle$2(ctx, running);
|
|
5539
5539
|
state.running = void 0;
|
|
5540
5540
|
ctx.state.gate.held = void 0;
|
|
5541
5541
|
if (settled) state.abort = void 0;
|
|
@@ -24372,7 +24372,7 @@ function destroyPools(state) {
|
|
|
24372
24372
|
* view with `applyPose`.
|
|
24373
24373
|
*/
|
|
24374
24374
|
/** How deep a parent chain is followed; deeper is treated as a loop. */
|
|
24375
|
-
const MAX_DEPTH$
|
|
24375
|
+
const MAX_DEPTH$6 = 32;
|
|
24376
24376
|
/**
|
|
24377
24377
|
* The parent an entity moves with.
|
|
24378
24378
|
*
|
|
@@ -24394,7 +24394,7 @@ function parentOf$1(ecs, entity) {
|
|
|
24394
24394
|
*/
|
|
24395
24395
|
function isLeaving(ecs, entity) {
|
|
24396
24396
|
let current = entity;
|
|
24397
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
24397
|
+
for (let depth = 0; depth < MAX_DEPTH$6 && current !== 0; depth += 1) {
|
|
24398
24398
|
if (ecs.has(current, Exiting)) return true;
|
|
24399
24399
|
current = parentOf$1(ecs, current);
|
|
24400
24400
|
}
|
|
@@ -24500,7 +24500,7 @@ function rootPoseInto(ecs, entity, out, own) {
|
|
|
24500
24500
|
out.pivot.x = start.pivot.x;
|
|
24501
24501
|
out.pivot.y = start.pivot.y;
|
|
24502
24502
|
let current = entity;
|
|
24503
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
24503
|
+
for (let depth = 0; depth < MAX_DEPTH$6; depth += 1) {
|
|
24504
24504
|
const parent = parentOf$1(ecs, current);
|
|
24505
24505
|
if (parent === 0) break;
|
|
24506
24506
|
const above = ecs.get(parent, Transform) ?? Transform.defaults;
|
|
@@ -24841,7 +24841,7 @@ const RETAKE_STREAK = 3;
|
|
|
24841
24841
|
/** Frames in a row with no mark in it before a live range is kept as a picture again (spec 23's `CALM_AFTER`). */
|
|
24842
24842
|
const CALM_FRAMES = 8;
|
|
24843
24843
|
/** How deep a walk up the tree goes. */
|
|
24844
|
-
const MAX_DEPTH$
|
|
24844
|
+
const MAX_DEPTH$5 = 32;
|
|
24845
24845
|
/** CSS px a view may fall short of the viewport frame and still cover it: the reference frame rounds by a fraction. */
|
|
24846
24846
|
const COVER_SLACK = 1;
|
|
24847
24847
|
/** The whole-frame reasons after which a kept picture is taken again: the screen changed whole. */
|
|
@@ -25005,7 +25005,7 @@ function sameList$1(left, right) {
|
|
|
25005
25005
|
*/
|
|
25006
25006
|
function inRange(set, node, root) {
|
|
25007
25007
|
let current = node;
|
|
25008
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
25008
|
+
for (let depth = 0; depth < MAX_DEPTH$5 && current !== null && current !== root; depth += 1) {
|
|
25009
25009
|
if (set.has(current)) return true;
|
|
25010
25010
|
current = current.parent;
|
|
25011
25011
|
}
|
|
@@ -27963,7 +27963,7 @@ function installPassCounting(pixi, sinks) {
|
|
|
27963
27963
|
//#endregion
|
|
27964
27964
|
//#region src/plugins/renderer/sync/hit-test.ts
|
|
27965
27965
|
/** How deep a parent chain or a container tree is followed. */
|
|
27966
|
-
const MAX_DEPTH$
|
|
27966
|
+
const MAX_DEPTH$4 = 32;
|
|
27967
27967
|
/** Below this effective alpha a view is treated as not there. */
|
|
27968
27968
|
const MIN_ALPHA = .01;
|
|
27969
27969
|
/**
|
|
@@ -28045,7 +28045,7 @@ function clipBoxOf(sctx, entity) {
|
|
|
28045
28045
|
function clippedOut(sctx, entity, x, y) {
|
|
28046
28046
|
const ecs = sctx.ctx.deps.world.ecs;
|
|
28047
28047
|
let current = parentOf$1(ecs, entity);
|
|
28048
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
28048
|
+
for (let depth = 0; depth < MAX_DEPTH$4 && current !== 0; depth += 1) {
|
|
28049
28049
|
const box = clipBoxOf(sctx, current);
|
|
28050
28050
|
if (box !== void 0 && !inBox(box, localPoint(sctx, current, x, y))) return true;
|
|
28051
28051
|
current = parentOf$1(ecs, current);
|
|
@@ -28064,7 +28064,7 @@ function clippedOut(sctx, entity, x, y) {
|
|
|
28064
28064
|
function effectiveAlpha(object, root, off) {
|
|
28065
28065
|
let alpha = 1;
|
|
28066
28066
|
let current = object;
|
|
28067
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
28067
|
+
for (let depth = 0; depth < MAX_DEPTH$4 && current !== void 0; depth += 1) {
|
|
28068
28068
|
if (current === root) return alpha;
|
|
28069
28069
|
if (!current.visible || off?.has(current) === true) return 0;
|
|
28070
28070
|
alpha *= current.alpha;
|
|
@@ -28094,7 +28094,7 @@ function isShown$1(object, root, off) {
|
|
|
28094
28094
|
* @returns The same array.
|
|
28095
28095
|
*/
|
|
28096
28096
|
function topFirst(container, out, depth = 0) {
|
|
28097
|
-
if (depth >= MAX_DEPTH$
|
|
28097
|
+
if (depth >= MAX_DEPTH$4) return out;
|
|
28098
28098
|
const ordered = container.children.toSorted((left, right) => left.zIndex - right.zIndex);
|
|
28099
28099
|
for (let index = ordered.length - 1; index >= 0; index -= 1) {
|
|
28100
28100
|
const child = ordered[index];
|
|
@@ -28183,7 +28183,7 @@ function hitAll(sctx, x, y) {
|
|
|
28183
28183
|
* hit test walks, so a box the doors report is the box a click reaches.
|
|
28184
28184
|
*/
|
|
28185
28185
|
/** How deep a parent chain is followed; deeper is treated as a loop. */
|
|
28186
|
-
const MAX_DEPTH$
|
|
28186
|
+
const MAX_DEPTH$3 = 32;
|
|
28187
28187
|
/**
|
|
28188
28188
|
* The four corners of a box, clockwise from the top-left one.
|
|
28189
28189
|
*
|
|
@@ -28277,7 +28277,7 @@ function boundsOf(sctx, entity) {
|
|
|
28277
28277
|
function layerOf(sctx, entity) {
|
|
28278
28278
|
const ecs = sctx.ctx.deps.world.ecs;
|
|
28279
28279
|
let top = entity;
|
|
28280
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
28280
|
+
for (let depth = 0; depth < MAX_DEPTH$3; depth += 1) {
|
|
28281
28281
|
const parent = parentOf$1(ecs, top);
|
|
28282
28282
|
if (parent === 0) break;
|
|
28283
28283
|
top = parent;
|
|
@@ -28692,7 +28692,7 @@ function stepParts(sctx) {
|
|
|
28692
28692
|
//#endregion
|
|
28693
28693
|
//#region src/plugins/renderer/sync/overlay.ts
|
|
28694
28694
|
/** How deep a `Parent` chain or a walk up the tree goes. */
|
|
28695
|
-
const MAX_DEPTH$
|
|
28695
|
+
const MAX_DEPTH$2 = 32;
|
|
28696
28696
|
/** The owner `ui` gives the entities it spawns: the only overlays honoured (O4.8). */
|
|
28697
28697
|
const UI_OWNER_NAME = "ui";
|
|
28698
28698
|
/**
|
|
@@ -28761,7 +28761,7 @@ function clampInto(sctx, entity, into, raw) {
|
|
|
28761
28761
|
function rootOf(sctx, entity) {
|
|
28762
28762
|
const ecs = sctx.ctx.deps.world.ecs;
|
|
28763
28763
|
let current = entity;
|
|
28764
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
28764
|
+
for (let depth = 0; depth < MAX_DEPTH$2; depth += 1) {
|
|
28765
28765
|
const parent = parentOf$1(ecs, current);
|
|
28766
28766
|
if (parent === 0) return current;
|
|
28767
28767
|
current = parent;
|
|
@@ -28974,7 +28974,7 @@ function drawnAlpha(sctx, record) {
|
|
|
28974
28974
|
const alphaFilter = sctx.deps.host.pixi()?.AlphaFilter;
|
|
28975
28975
|
let alpha = 1;
|
|
28976
28976
|
let node = view.wrapper ?? view.object;
|
|
28977
|
-
for (let depth = 0; depth < MAX_DEPTH$
|
|
28977
|
+
for (let depth = 0; depth < MAX_DEPTH$2 && node !== null; depth += 1) {
|
|
28978
28978
|
if (node === root) return alpha;
|
|
28979
28979
|
if (!node.visible) return 0;
|
|
28980
28980
|
const owner = state.viewOfTop.get(node);
|
|
@@ -36162,7 +36162,7 @@ async function settledRecord(state, bundle) {
|
|
|
36162
36162
|
* @param size - The size of the decoded image.
|
|
36163
36163
|
* @param mips - True for a texture uploaded with its full mip chain.
|
|
36164
36164
|
*/
|
|
36165
|
-
function resize(target, size, mips) {
|
|
36165
|
+
function resize$1(target, size, mips) {
|
|
36166
36166
|
if (target === void 0 || size === void 0) return;
|
|
36167
36167
|
target.width = size.width;
|
|
36168
36168
|
target.height = size.height;
|
|
@@ -36184,7 +36184,7 @@ function storeFont(io, record, owner, font, sizes) {
|
|
|
36184
36184
|
const old = record.fonts.get(file.key);
|
|
36185
36185
|
record.fonts.set(file.key, font);
|
|
36186
36186
|
for (const page of old?.pages ?? []) io.destroyTexture(page);
|
|
36187
|
-
for (const [at, texture] of font.pages.entries()) resize(pages[at], sizes.get(texture), false);
|
|
36187
|
+
for (const [at, texture] of font.pages.entries()) resize$1(pages[at], sizes.get(texture), false);
|
|
36188
36188
|
file.mb = sumMb(pages);
|
|
36189
36189
|
}
|
|
36190
36190
|
/**
|
|
@@ -36209,7 +36209,7 @@ function store(io, record, fresh, sizes) {
|
|
|
36209
36209
|
const old = record.textures.get(file.key);
|
|
36210
36210
|
record.textures.set(file.key, fresh.texture);
|
|
36211
36211
|
if (old !== void 0) io.destroyTexture(old);
|
|
36212
|
-
resize(file, sizes.get(fresh.texture), file.mips === true);
|
|
36212
|
+
resize$1(file, sizes.get(fresh.texture), file.mips === true);
|
|
36213
36213
|
}
|
|
36214
36214
|
/**
|
|
36215
36215
|
* Makes the numbers and the picture follow the files that were stored: the megabytes of the
|
|
@@ -41376,7 +41376,7 @@ function finish(ectx, run, definition, passed) {
|
|
|
41376
41376
|
* @param definition - The definition.
|
|
41377
41377
|
* @param messages - What the compiler said.
|
|
41378
41378
|
*/
|
|
41379
|
-
function settle(ectx, run, definition, messages) {
|
|
41379
|
+
function settle$1(ectx, run, definition, messages) {
|
|
41380
41380
|
const { checks } = ectx.state;
|
|
41381
41381
|
if (checks.run !== run || checks.status.get(definition) !== "pending") return;
|
|
41382
41382
|
const errors = messages.filter((message) => message.type === "error");
|
|
@@ -41402,7 +41402,7 @@ function checkWgsl(ectx, run, definition) {
|
|
|
41402
41402
|
finish(ectx, run, definition, true);
|
|
41403
41403
|
return;
|
|
41404
41404
|
}
|
|
41405
|
-
device.createShaderModule({ code: definition.source }).getCompilationInfo().then((info) => settle(ectx, run, definition, info.messages), () => settle(ectx, run, definition, []));
|
|
41405
|
+
device.createShaderModule({ code: definition.source }).getCompilationInfo().then((info) => settle$1(ectx, run, definition, info.messages), () => settle$1(ectx, run, definition, []));
|
|
41406
41406
|
}
|
|
41407
41407
|
/**
|
|
41408
41408
|
* Reads the errors of a WebGL info log, one per `ERROR: 0:<line>: <message>` line; a summary line
|
|
@@ -43032,7 +43032,7 @@ const EFFECTS_OWNER = Object.freeze({
|
|
|
43032
43032
|
name: "effects"
|
|
43033
43033
|
});
|
|
43034
43034
|
/** How deep a parent chain is followed, as `rootPoseOf` does; deeper is treated as a loop. */
|
|
43035
|
-
const MAX_DEPTH = 32;
|
|
43035
|
+
const MAX_DEPTH$1 = 32;
|
|
43036
43036
|
/**
|
|
43037
43037
|
* The one dynamic set of an emitter container (P9): positions, frames, rotations and colours
|
|
43038
43038
|
* upload every frame; `uvs` stays static, because a frame is set at birth and the same `update()`
|
|
@@ -43075,7 +43075,7 @@ function bornFields(size) {
|
|
|
43075
43075
|
*/
|
|
43076
43076
|
function topOf(ecs, host) {
|
|
43077
43077
|
let top = host;
|
|
43078
|
-
for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
|
|
43078
|
+
for (let depth = 0; depth < MAX_DEPTH$1; depth += 1) {
|
|
43079
43079
|
const parent = parentOf$1(ecs, top);
|
|
43080
43080
|
if (parent === 0) break;
|
|
43081
43081
|
top = parent;
|
|
@@ -45182,6 +45182,10 @@ function submit(ctx, entity, answer) {
|
|
|
45182
45182
|
}
|
|
45183
45183
|
//#endregion
|
|
45184
45184
|
//#region src/plugins/input/components.ts
|
|
45185
|
+
const scrollableDefaults = {
|
|
45186
|
+
axis: "y",
|
|
45187
|
+
moving: false
|
|
45188
|
+
};
|
|
45185
45189
|
/**
|
|
45186
45190
|
* Fresh defaults for a component that names an intent. A function, so each component type gets
|
|
45187
45191
|
* its own payload object instead of sharing one.
|
|
@@ -45281,6 +45285,13 @@ const Pressed = /*#__PURE__*/ tag("Pressed");
|
|
|
45281
45285
|
*/
|
|
45282
45286
|
const PointerOver = /*#__PURE__*/ tag("PointerOver");
|
|
45283
45287
|
/**
|
|
45288
|
+
* On every ui scroll container, internal: ui writes it, input reads it, a game never sees it. A
|
|
45289
|
+
* press that leaves the tap slop along `axis` goes from the pressed view to the innermost scroll
|
|
45290
|
+
* of that axis the view hangs in by its `Parent` chain; a finger that lands on a view inside a
|
|
45291
|
+
* scroll that is `moving` stops it and answers nothing.
|
|
45292
|
+
*/
|
|
45293
|
+
const Scrollable = /*#__PURE__*/ component("Scrollable", scrollableDefaults);
|
|
45294
|
+
/**
|
|
45284
45295
|
* Where the one pointer is, in reference units. Written once per frame by the frame step. `id`
|
|
45285
45296
|
* names the pointer it follows, `-1` at rest.
|
|
45286
45297
|
*/
|
|
@@ -45290,8 +45301,12 @@ const Pointer = /*#__PURE__*/ resource("Pointer", pointerDefaults);
|
|
|
45290
45301
|
/**
|
|
45291
45302
|
* @file input plugin — who is under the finger. Every lookup goes through `renderer.sync.hitTest`,
|
|
45292
45303
|
* which answers `undefined` without a DOM, and every accept function refuses a view that is
|
|
45293
|
-
* already playing its exit. A trace takes a cell only inside a circle smaller than its box.
|
|
45304
|
+
* already playing its exit. A trace takes a cell only inside a circle smaller than its box. The
|
|
45305
|
+
* scroll a press belongs to is found up the pressed view's own `Parent` chain, never under the
|
|
45306
|
+
* point: a view drawn over a scroll from another root keeps its press.
|
|
45294
45307
|
*/
|
|
45308
|
+
/** How deep a `Parent` chain is followed; deeper is treated as a loop. */
|
|
45309
|
+
const MAX_DEPTH = 32;
|
|
45295
45310
|
/**
|
|
45296
45311
|
* Resolves what `app.input.*` was pointed at. A `{ projection, key }` goes through
|
|
45297
45312
|
* `world.projection.entityOf`, which answers for live views only, so a view in the despawn queue
|
|
@@ -45374,6 +45389,44 @@ function findPressed(ctx, x, y) {
|
|
|
45374
45389
|
return ctx.deps.renderer.sync.hitTest(x, y, (entity) => acceptPress(ctx, entity));
|
|
45375
45390
|
}
|
|
45376
45391
|
/**
|
|
45392
|
+
* The innermost scroll a view sits in that moves now: the view itself or the nearest ancestor of
|
|
45393
|
+
* its `Parent` chain that carries a `Scrollable` with `moving` true and is not on its way out. A
|
|
45394
|
+
* finger that lands on the view stops that scroll. A view of another root drawn over the scroll,
|
|
45395
|
+
* a HUD button or a popup, hangs in no chain of it, so it keeps its press. Reads only, allocates
|
|
45396
|
+
* nothing.
|
|
45397
|
+
*
|
|
45398
|
+
* @param ctx - Domain context of the input plugin.
|
|
45399
|
+
* @param entity - The view the hit test found under the finger.
|
|
45400
|
+
* @returns The scroll, or `undefined` when no scroll the view sits in moves.
|
|
45401
|
+
*/
|
|
45402
|
+
function movingScrollOf(ctx, entity) {
|
|
45403
|
+
const { ecs } = ctx.deps.world;
|
|
45404
|
+
let current = entity;
|
|
45405
|
+
for (let depth = 0; depth < MAX_DEPTH && current !== 0; depth += 1) {
|
|
45406
|
+
if (ecs.get(current, Scrollable)?.moving === true && !ecs.has(current, Exiting)) return current;
|
|
45407
|
+
current = parentOf$1(ecs, current);
|
|
45408
|
+
}
|
|
45409
|
+
}
|
|
45410
|
+
/**
|
|
45411
|
+
* The innermost scroll of an axis a pressed view sits in: the view itself or the nearest ancestor
|
|
45412
|
+
* of its `Parent` chain that carries a `Scrollable` of that axis and is not on its way out. A list
|
|
45413
|
+
* under a modal is in no chain of the modal's button, so a drift on that button never pans it.
|
|
45414
|
+
* Reads only, allocates nothing.
|
|
45415
|
+
*
|
|
45416
|
+
* @param ctx - Domain context of the input plugin.
|
|
45417
|
+
* @param entity - The pressed view.
|
|
45418
|
+
* @param axis - The dominant axis of the pan.
|
|
45419
|
+
* @returns The scroll, or `undefined` when the view sits in no scroll of that axis.
|
|
45420
|
+
*/
|
|
45421
|
+
function scrollOnAxisOf(ctx, entity, axis) {
|
|
45422
|
+
const { ecs } = ctx.deps.world;
|
|
45423
|
+
let current = entity;
|
|
45424
|
+
for (let depth = 0; depth < MAX_DEPTH && current !== 0; depth += 1) {
|
|
45425
|
+
if (ecs.get(current, Scrollable)?.axis === axis && !ecs.has(current, Exiting)) return current;
|
|
45426
|
+
current = parentOf$1(ecs, current);
|
|
45427
|
+
}
|
|
45428
|
+
}
|
|
45429
|
+
/**
|
|
45377
45430
|
* The topmost drop target at a point.
|
|
45378
45431
|
*
|
|
45379
45432
|
* @param ctx - Domain context of the input plugin.
|
|
@@ -46397,6 +46450,20 @@ function direction(from, to) {
|
|
|
46397
46450
|
return dy >= 0 ? "down" : "up";
|
|
46398
46451
|
}
|
|
46399
46452
|
/**
|
|
46453
|
+
* The axis a pan moves on: the dominant one, horizontal on an exact diagonal.
|
|
46454
|
+
*
|
|
46455
|
+
* @param from - Where the press started.
|
|
46456
|
+
* @param to - Where the finger is.
|
|
46457
|
+
* @returns The axis of the larger move.
|
|
46458
|
+
* @example
|
|
46459
|
+
* ```ts
|
|
46460
|
+
* axisOf({ x: 0, y: 0 }, { x: 20, y: -5 }); // "x"
|
|
46461
|
+
* ```
|
|
46462
|
+
*/
|
|
46463
|
+
function axisOf$1(from, to) {
|
|
46464
|
+
return Math.abs(to.x - from.x) >= Math.abs(to.y - from.y) ? "x" : "y";
|
|
46465
|
+
}
|
|
46466
|
+
/**
|
|
46400
46467
|
* Takes or releases the pointer capture of the canvas. A pointer that is already gone — a down
|
|
46401
46468
|
* and an up inside one frame — cannot be captured; the browser throws and the gesture goes on.
|
|
46402
46469
|
*
|
|
@@ -46415,7 +46482,7 @@ function capture(ctx, on) {
|
|
|
46415
46482
|
}
|
|
46416
46483
|
/**
|
|
46417
46484
|
* Ends the gesture: the press tag goes, the capture is released and the machine forgets the
|
|
46418
|
-
* pointer, whether it moved, the entity and its key.
|
|
46485
|
+
* pointer, whether it moved or caught a scroll, the entity and its key.
|
|
46419
46486
|
*
|
|
46420
46487
|
* @param ctx - Domain context of the input plugin.
|
|
46421
46488
|
*/
|
|
@@ -46426,16 +46493,30 @@ function toIdle(ctx) {
|
|
|
46426
46493
|
ctx.state.phase = "idle";
|
|
46427
46494
|
ctx.state.pointerId = void 0;
|
|
46428
46495
|
ctx.state.moved = false;
|
|
46496
|
+
ctx.state.caught = false;
|
|
46429
46497
|
ctx.state.entity = void 0;
|
|
46430
46498
|
ctx.state.key = void 0;
|
|
46431
46499
|
ctx.state.pressedMs = 0;
|
|
46432
46500
|
}
|
|
46433
46501
|
/**
|
|
46502
|
+
* Makes a view the pressed one: it is remembered with its projection key and tagged `Pressed`.
|
|
46503
|
+
*
|
|
46504
|
+
* @param ctx - Domain context of the input plugin.
|
|
46505
|
+
* @param entity - The view that takes the press.
|
|
46506
|
+
*/
|
|
46507
|
+
function press(ctx, entity) {
|
|
46508
|
+
ctx.state.entity = entity;
|
|
46509
|
+
ctx.state.key = ctx.deps.world.projection.keyOf(entity);
|
|
46510
|
+
ctx.deps.world.ecs.tag(entity, Pressed);
|
|
46511
|
+
}
|
|
46512
|
+
/**
|
|
46434
46513
|
* Takes the pointer and looks for the view under it. The pointer is taken even where no view is.
|
|
46435
46514
|
* While the finger rests (`rests`) the next finger that goes down takes the pointer over and this
|
|
46436
46515
|
* press is cancelled; once it starts a gesture — a drag, a move past the tap slop, a long press, a
|
|
46437
|
-
* trace — a second finger stays ignored.
|
|
46438
|
-
*
|
|
46516
|
+
* trace — a second finger stays ignored. When the view under the finger sits in a moving scroll,
|
|
46517
|
+
* that scroll takes the press: the finger stops it, and the press is caught. A view drawn over a
|
|
46518
|
+
* moving scroll from another root, a HUD or a popup button, keeps its press. A view that carries
|
|
46519
|
+
* `Traceable` starts a trace instead of a press: it is not tagged `Pressed`.
|
|
46439
46520
|
*
|
|
46440
46521
|
* @param ctx - Domain context of the input plugin.
|
|
46441
46522
|
* @param pointer - The `Pointer` resource of this frame.
|
|
@@ -46443,9 +46524,10 @@ function toIdle(ctx) {
|
|
|
46443
46524
|
* @param sample - The raw sample.
|
|
46444
46525
|
*/
|
|
46445
46526
|
function onDown(ctx, pointer, point, sample) {
|
|
46446
|
-
const { ecs
|
|
46527
|
+
const { ecs } = ctx.deps.world;
|
|
46447
46528
|
ctx.state.pointerId = sample.pointerId;
|
|
46448
46529
|
ctx.state.moved = false;
|
|
46530
|
+
ctx.state.caught = false;
|
|
46449
46531
|
ctx.state.start = {
|
|
46450
46532
|
x: point.x,
|
|
46451
46533
|
y: point.y
|
|
@@ -46457,16 +46539,42 @@ function onDown(ctx, pointer, point, sample) {
|
|
|
46457
46539
|
capture(ctx, true);
|
|
46458
46540
|
const entity = findPressed(ctx, point.x, point.y);
|
|
46459
46541
|
if (entity === void 0) return;
|
|
46542
|
+
const scroll = movingScrollOf(ctx, entity);
|
|
46543
|
+
if (scroll !== void 0) {
|
|
46544
|
+
ctx.state.caught = true;
|
|
46545
|
+
press(ctx, scroll);
|
|
46546
|
+
return;
|
|
46547
|
+
}
|
|
46460
46548
|
if (ecs.has(entity, Traceable)) {
|
|
46461
46549
|
beginTrace(ctx, entity, point);
|
|
46462
46550
|
return;
|
|
46463
46551
|
}
|
|
46464
|
-
ctx
|
|
46465
|
-
ctx.state.key = projection.keyOf(entity);
|
|
46466
|
-
ecs.tag(entity, Pressed);
|
|
46552
|
+
press(ctx, entity);
|
|
46467
46553
|
if (ecs.has(entity, Draggable) && ecs.has(entity, Swipeable)) ctx.log.warn("input: Draggable wins over Swipeable", { view: ctx.state.key });
|
|
46468
46554
|
}
|
|
46469
46555
|
/**
|
|
46556
|
+
* The press just left the tap slop: it goes to the scroll it pans, the innermost `Scrollable` of
|
|
46557
|
+
* the pressed view's own `Parent` chain (the view itself included) that moves on the dominant axis
|
|
46558
|
+
* of the move. The view loses `Pressed`, and the press is marked caught: it is a scroll gesture
|
|
46559
|
+
* now, so its release answers nothing, also when the finger came back inside the slop. A
|
|
46560
|
+
* draggable or swipeable view keeps its gesture, a caught press keeps its scroll, and a view that
|
|
46561
|
+
* sits in no scroll of that axis keeps its press, also a popup button drawn over a list.
|
|
46562
|
+
*
|
|
46563
|
+
* @param ctx - Domain context of the input plugin.
|
|
46564
|
+
* @param point - Where the finger is, in reference units.
|
|
46565
|
+
*/
|
|
46566
|
+
function handOff(ctx, point) {
|
|
46567
|
+
const { caught, entity, start } = ctx.state;
|
|
46568
|
+
const { ecs } = ctx.deps.world;
|
|
46569
|
+
if (entity === void 0 || caught) return;
|
|
46570
|
+
if (ecs.has(entity, Draggable) || ecs.has(entity, Swipeable)) return;
|
|
46571
|
+
const scroll = scrollOnAxisOf(ctx, entity, axisOf$1(start, point));
|
|
46572
|
+
if (scroll === void 0 || scroll === entity) return;
|
|
46573
|
+
ecs.untag(entity, Pressed);
|
|
46574
|
+
ctx.state.caught = true;
|
|
46575
|
+
press(ctx, scroll);
|
|
46576
|
+
}
|
|
46577
|
+
/**
|
|
46470
46578
|
* Turns a press on a draggable view into a drag once the finger left the start by more than
|
|
46471
46579
|
* `dragStartPx`. A draggable view with no projection key cannot be carried: `mute`, `lift` and
|
|
46472
46580
|
* `settle` belong to a projection, so the gesture is reported and given up.
|
|
@@ -46492,13 +46600,15 @@ function onMove(ctx, point) {
|
|
|
46492
46600
|
* Answers the release of a press: a tap when the finger stayed inside the slop, a swipe when it
|
|
46493
46601
|
* went far enough and fast enough. Neither: nothing happened, and nothing has to be undone. Every
|
|
46494
46602
|
* release inside the slop runs the `onTap` listeners first, so a view that carries only
|
|
46495
|
-
* `Touchable` is heard although it answers nothing.
|
|
46603
|
+
* `Touchable` is heard although it answers nothing. A caught press belongs to a scroll — it
|
|
46604
|
+
* stopped a moving one or was handed to one — and answers nothing and runs no listener.
|
|
46496
46605
|
*
|
|
46497
46606
|
* @param ctx - Domain context of the input plugin.
|
|
46498
46607
|
* @param entity - The pressed view.
|
|
46499
46608
|
* @param point - Where the finger let go, in reference units.
|
|
46500
46609
|
*/
|
|
46501
46610
|
function answerRelease(ctx, entity, point) {
|
|
46611
|
+
if (ctx.state.caught) return;
|
|
46502
46612
|
const { ecs } = ctx.deps.world;
|
|
46503
46613
|
const travelled = distance(ctx.state.start, point);
|
|
46504
46614
|
if (travelled <= ctx.config.tapSlopPx) {
|
|
@@ -46565,13 +46675,24 @@ function rests(ctx) {
|
|
|
46565
46675
|
return ctx.state.phase === "pressed" && !ctx.state.moved;
|
|
46566
46676
|
}
|
|
46567
46677
|
/**
|
|
46678
|
+
* Tells whether a sample takes the press past the tap slop for the first time: the press has not
|
|
46679
|
+
* moved yet and the finger is farther than `tapSlopPx` from the start.
|
|
46680
|
+
*
|
|
46681
|
+
* @param ctx - Domain context of the input plugin.
|
|
46682
|
+
* @param point - Where the finger is, in reference units.
|
|
46683
|
+
* @returns True in the one sample that turns the press into a moved one.
|
|
46684
|
+
*/
|
|
46685
|
+
function leavesSlop(ctx, point) {
|
|
46686
|
+
return ctx.state.phase === "pressed" && !ctx.state.moved && distance(ctx.state.start, point) > ctx.config.tapSlopPx;
|
|
46687
|
+
}
|
|
46688
|
+
/**
|
|
46568
46689
|
* Feeds one raw sample to the machine. A leave does nothing here. A sample of another pointer is
|
|
46569
46690
|
* dropped while a pointer is followed: the second finger is ignored. One exception: a press while
|
|
46570
46691
|
* the followed pointer rests takes the pointer over and cancels the resting press, so a resting
|
|
46571
46692
|
* thumb or palm never blocks the finger that plays. A move of an idle mouse or pen hit-tests
|
|
46572
46693
|
* nothing: its point is returned, so the drain moves the hover once per frame. A move of a press
|
|
46573
|
-
* past the tap slop marks the press as moved for good
|
|
46574
|
-
* cancel.
|
|
46694
|
+
* past the tap slop marks the press as moved for good and, in that one sample, hands it to the
|
|
46695
|
+
* scroll it pans. A lost capture ends the gesture like a cancel.
|
|
46575
46696
|
*
|
|
46576
46697
|
* @param ctx - Domain context of the input plugin.
|
|
46577
46698
|
* @param pointer - The `Pointer` resource of this frame.
|
|
@@ -46594,7 +46715,10 @@ function handleSample(ctx, pointer, sample) {
|
|
|
46594
46715
|
break;
|
|
46595
46716
|
case "move":
|
|
46596
46717
|
if (isHover(ctx, pointer, sample)) return point;
|
|
46597
|
-
if (
|
|
46718
|
+
if (leavesSlop(ctx, point)) {
|
|
46719
|
+
ctx.state.moved = true;
|
|
46720
|
+
handOff(ctx, point);
|
|
46721
|
+
}
|
|
46598
46722
|
if (ctx.state.phase === "tracing") extendTrace(ctx, point);
|
|
46599
46723
|
else onMove(ctx, point);
|
|
46600
46724
|
break;
|
|
@@ -46767,6 +46891,7 @@ function createInputState(_ctx) {
|
|
|
46767
46891
|
phase: "idle",
|
|
46768
46892
|
pointerId: void 0,
|
|
46769
46893
|
moved: false,
|
|
46894
|
+
caught: false,
|
|
46770
46895
|
entity: void 0,
|
|
46771
46896
|
key: void 0,
|
|
46772
46897
|
start: {
|
|
@@ -50176,6 +50301,31 @@ const Scroll = /*#__PURE__*/ component("Scroll", {
|
|
|
50176
50301
|
min: 0
|
|
50177
50302
|
});
|
|
50178
50303
|
/**
|
|
50304
|
+
* On every scroll container: where it stands and whether it moves, for a game system and the
|
|
50305
|
+
* read door. `ui` writes it in a frame where the offset or `max` changed, and once more with
|
|
50306
|
+
* `moving: false` when it stops; a game reads it and never writes it.
|
|
50307
|
+
*
|
|
50308
|
+
* @example
|
|
50309
|
+
* ```ts
|
|
50310
|
+
* // A board system dims the events of the order strip while it rests on the orders.
|
|
50311
|
+
* export const stripMist = system({
|
|
50312
|
+
* name: "board.stripMist",
|
|
50313
|
+
* phase: "animate",
|
|
50314
|
+
* query: [ScrollState, OrderStrip],
|
|
50315
|
+
* run: entities => {
|
|
50316
|
+
* for (const [entity, scroll] of entities) {
|
|
50317
|
+
* // scroll: { offset: 318, max: 390, moving: false }
|
|
50318
|
+
* }
|
|
50319
|
+
* }
|
|
50320
|
+
* });
|
|
50321
|
+
* ```
|
|
50322
|
+
*/
|
|
50323
|
+
const ScrollState = /*#__PURE__*/ component("ScrollState", {
|
|
50324
|
+
offset: 0,
|
|
50325
|
+
max: 0,
|
|
50326
|
+
moving: false
|
|
50327
|
+
});
|
|
50328
|
+
/**
|
|
50179
50329
|
* On the root entity of a popup kept beneath another popup (`popup(..., { over })`). Internal to
|
|
50180
50330
|
* `ui`: not exported from the package root. Every element of that root resolves `is.covered`.
|
|
50181
50331
|
*/
|
|
@@ -51908,8 +52058,9 @@ function windowFormOf(node) {
|
|
|
51908
52058
|
}
|
|
51909
52059
|
/**
|
|
51910
52060
|
* Refuses the props of a scroll its windowed form cannot take: some but not all of `rows`,
|
|
51911
|
-
* `rowHeight` and `row`,
|
|
51912
|
-
* patched, so a refused
|
|
52061
|
+
* `rowHeight` and `row`, a row height that is not above 0, or `axis: "x"`, since the rows of a
|
|
52062
|
+
* window stand one below the other. Called before anything is spawned or patched, so a refused
|
|
52063
|
+
* scroll leaves no half-made element.
|
|
51913
52064
|
*
|
|
51914
52065
|
* @param node - The node being placed.
|
|
51915
52066
|
* @throws {Error} For a windowed scroll whose props do not hold.
|
|
@@ -51919,7 +52070,50 @@ function windowFormOf(node) {
|
|
|
51919
52070
|
* ```
|
|
51920
52071
|
*/
|
|
51921
52072
|
function checkWindow(node) {
|
|
51922
|
-
if (node.type
|
|
52073
|
+
if (node.type !== "scroll") return;
|
|
52074
|
+
if (windowFormOf(node) !== void 0 && axisOf(node) === "x") throw new Error("[game] A windowed scroll moves on y only.\n Drop axis \"x\" or use the children form.");
|
|
52075
|
+
}
|
|
52076
|
+
/**
|
|
52077
|
+
* The axis a scroll moves on: `"x"` when its props say so, else `"y"`.
|
|
52078
|
+
*
|
|
52079
|
+
* @param node - The node of a scroll.
|
|
52080
|
+
* @returns The axis.
|
|
52081
|
+
* @example
|
|
52082
|
+
* ```ts
|
|
52083
|
+
* axisOf({ type: "scroll", props: { axis: "x" }, children: [] }); // "x"
|
|
52084
|
+
* ```
|
|
52085
|
+
*/
|
|
52086
|
+
function axisOf(node) {
|
|
52087
|
+
return node.props.axis === "x" ? "x" : "y";
|
|
52088
|
+
}
|
|
52089
|
+
/**
|
|
52090
|
+
* The motion record a scroll spawns with: its axis, still at the start, not placed yet. The
|
|
52091
|
+
* first solve puts it at its `offset` prop.
|
|
52092
|
+
*
|
|
52093
|
+
* @param node - The node of the scroll that enters.
|
|
52094
|
+
* @returns A new record.
|
|
52095
|
+
* @example
|
|
52096
|
+
* ```ts
|
|
52097
|
+
* scrollMotionOf({ type: "scroll", props: { axis: "x" }, children: [] }).mode; // "still"
|
|
52098
|
+
* ```
|
|
52099
|
+
*/
|
|
52100
|
+
function scrollMotionOf(node) {
|
|
52101
|
+
return {
|
|
52102
|
+
axis: axisOf(node),
|
|
52103
|
+
mode: "still",
|
|
52104
|
+
offset: 0,
|
|
52105
|
+
max: 0,
|
|
52106
|
+
velocity: 0,
|
|
52107
|
+
from: 0,
|
|
52108
|
+
to: 0,
|
|
52109
|
+
duration: 0,
|
|
52110
|
+
elapsed: 0,
|
|
52111
|
+
pointer: 0,
|
|
52112
|
+
start: 0,
|
|
52113
|
+
seen: 0,
|
|
52114
|
+
placed: false,
|
|
52115
|
+
atProp: false
|
|
52116
|
+
};
|
|
51923
52117
|
}
|
|
51924
52118
|
/**
|
|
51925
52119
|
* Builds a spacer: an empty node of a fixed height that never shrinks.
|
|
@@ -54345,15 +54539,23 @@ const VISUALS = [
|
|
|
54345
54539
|
];
|
|
54346
54540
|
/**
|
|
54347
54541
|
* The input components of an element and the Escape marker; a disabled or covered button drops
|
|
54348
|
-
* the answering one and the marker.
|
|
54542
|
+
* the answering one and the marker. A scroll's three hold its runtime state.
|
|
54349
54543
|
*/
|
|
54350
54544
|
const INPUTS = [
|
|
54351
54545
|
Tappable,
|
|
54352
54546
|
Touchable,
|
|
54353
54547
|
LocalWrite,
|
|
54354
54548
|
Scroll,
|
|
54549
|
+
Scrollable,
|
|
54550
|
+
ScrollState,
|
|
54355
54551
|
Escapable
|
|
54356
54552
|
];
|
|
54553
|
+
/** The components of a scroll that hold where it stands: added once, never patched by a render. */
|
|
54554
|
+
const SCROLL_STATE = namesOf$1([
|
|
54555
|
+
Scroll,
|
|
54556
|
+
Scrollable,
|
|
54557
|
+
ScrollState
|
|
54558
|
+
]);
|
|
54357
54559
|
/**
|
|
54358
54560
|
* One list of the visual and the input types. Its own function, because lint rule L5 refuses a
|
|
54359
54561
|
* collection built in a module-scope declaration.
|
|
@@ -54368,6 +54570,14 @@ function concatTypes(...lists) {
|
|
|
54368
54570
|
const LIVE_TYPES = concatTypes(VISUALS, INPUTS);
|
|
54369
54571
|
/** The owned fields of a type that gives none to a plugin: one list, never a new one. */
|
|
54370
54572
|
const NO_OWNED = [];
|
|
54573
|
+
/** The time of a reconcile a test steps by hand: no ms pass, so no scroll moves. */
|
|
54574
|
+
const NO_TIME = Object.freeze({
|
|
54575
|
+
delta: 0,
|
|
54576
|
+
elapsed: 0,
|
|
54577
|
+
scale: 1,
|
|
54578
|
+
frame: 0,
|
|
54579
|
+
idle: false
|
|
54580
|
+
});
|
|
54371
54581
|
/**
|
|
54372
54582
|
* Tells whether a list of component values holds one of a type.
|
|
54373
54583
|
*
|
|
@@ -54408,6 +54618,11 @@ const ELEMENT_OWNED = namesOf$1([
|
|
|
54408
54618
|
]);
|
|
54409
54619
|
/** The style of a scroll's content: one object, so a render never reads as a new style. */
|
|
54410
54620
|
const CONTENT_STYLE = Object.freeze({ origin: "topLeft" });
|
|
54621
|
+
/** The style of an x scroll's content: its children stand side by side. */
|
|
54622
|
+
const CONTENT_ROW_STYLE = Object.freeze({
|
|
54623
|
+
origin: "topLeft",
|
|
54624
|
+
direction: "row"
|
|
54625
|
+
});
|
|
54411
54626
|
/**
|
|
54412
54627
|
* The props, besides `style`, `state` and `content`, that a live element is written from: `bind`,
|
|
54413
54628
|
* `texture`, `name` and `fit` reach its visual, `intent`, `payload`, `local` and `escape` its
|
|
@@ -54445,23 +54660,24 @@ function writesSame(previous, next) {
|
|
|
54445
54660
|
/**
|
|
54446
54661
|
* The children one element is diffed against. A scroll container holds exactly one of them: the
|
|
54447
54662
|
* content the finger moves, which keeps the rects of its own children and carries them along. Its
|
|
54448
|
-
* pivot is its top-left corner, so the scroll step writes its offset straight into `y`; it
|
|
54449
|
-
* its children out as a column. The content of a windowed scroll
|
|
54450
|
-
* the two spacers and the rows, instead of the markup's children.
|
|
54663
|
+
* pivot is its top-left corner, so the scroll step writes its offset straight into `x` or `y`; it
|
|
54664
|
+
* lays its children out as a column, or as a row on the `x` axis. The content of a windowed scroll
|
|
54665
|
+
* holds the slots of its window, the two spacers and the rows, instead of the markup's children.
|
|
54451
54666
|
*
|
|
54452
54667
|
* @param node - The node the markup wrote.
|
|
54453
54668
|
* @param slots - The slots of a windowed scroll, top spacer to bottom spacer.
|
|
54669
|
+
* @param axis - The axis of a scroll, the one it spawned with.
|
|
54454
54670
|
* @returns The children of the element.
|
|
54455
54671
|
* @example
|
|
54456
54672
|
* ```ts
|
|
54457
54673
|
* childrenOf({ type: "scroll", props: {}, children: [] }).length; // 1
|
|
54458
54674
|
* ```
|
|
54459
54675
|
*/
|
|
54460
|
-
function childrenOf(node, slots) {
|
|
54676
|
+
function childrenOf(node, slots, axis = "y") {
|
|
54461
54677
|
if (node.type !== "scroll") return node.children;
|
|
54462
54678
|
return [{
|
|
54463
54679
|
type: CONTENT$1,
|
|
54464
|
-
props: { style: CONTENT_STYLE },
|
|
54680
|
+
props: { style: axis === "x" ? CONTENT_ROW_STYLE : CONTENT_STYLE },
|
|
54465
54681
|
children: slots ?? node.children
|
|
54466
54682
|
}];
|
|
54467
54683
|
}
|
|
@@ -54540,16 +54756,15 @@ function styleOf(node) {
|
|
|
54540
54756
|
return typeof style === "object" && style !== null ? style : void 0;
|
|
54541
54757
|
}
|
|
54542
54758
|
/**
|
|
54543
|
-
* Refuses the tags that cannot be placed: a text field that names no local field, a
|
|
54544
|
-
* scroll
|
|
54759
|
+
* Refuses the tags that cannot be placed: a text field that names no local field, and a windowed
|
|
54760
|
+
* scroll whose props do not hold.
|
|
54545
54761
|
*
|
|
54546
54762
|
* @param node - The node being placed.
|
|
54547
|
-
* @throws {Error} For an `input` tag without `local`, for a
|
|
54548
|
-
*
|
|
54763
|
+
* @throws {Error} For an `input` tag without `local`, and for a windowed scroll whose props do
|
|
54764
|
+
* not hold.
|
|
54549
54765
|
*/
|
|
54550
54766
|
function checkTag(node) {
|
|
54551
54767
|
if (node.type === "input" && typeof node.props.local !== "string") throw new Error("[game] An input needs a local field.\n Write <input local=\"name\" />.");
|
|
54552
|
-
if (node.type === "scroll" && node.props.axis === "x") throw new Error("[game] Horizontal scroll arrives after V3.\n Use axis \"y\".");
|
|
54553
54768
|
checkWindow(node);
|
|
54554
54769
|
}
|
|
54555
54770
|
/**
|
|
@@ -54572,16 +54787,28 @@ function answerOf(node) {
|
|
|
54572
54787
|
/**
|
|
54573
54788
|
* The input components of an element: a button answers the gate, writes local state, or only
|
|
54574
54789
|
* swallows the tap; a panel swallows the tap too, so nothing under it answers; a text field takes
|
|
54575
|
-
* the tap that starts its editing; a scroll container takes the press that moves its content
|
|
54576
|
-
*
|
|
54577
|
-
*
|
|
54790
|
+
* the tap that starts its editing; a scroll container takes the press that moves its content and
|
|
54791
|
+
* carries where it stands, on the axis it spawned with. A button of a covered popup answers
|
|
54792
|
+
* nothing, so only the top popup answers the gate. A button with the `escape` prop that answers
|
|
54793
|
+
* also carries `Escapable`, the control the Escape key taps.
|
|
54578
54794
|
*
|
|
54579
54795
|
* @param element - The element to make touchable.
|
|
54580
54796
|
* @returns The component values.
|
|
54581
54797
|
*/
|
|
54582
54798
|
function inputOf(element) {
|
|
54583
54799
|
const { type, node, is } = element;
|
|
54584
|
-
if (type === "scroll")
|
|
54800
|
+
if (type === "scroll") {
|
|
54801
|
+
const axis = element.scroll?.axis ?? "y";
|
|
54802
|
+
return [
|
|
54803
|
+
Touchable(),
|
|
54804
|
+
Scroll({ axis }),
|
|
54805
|
+
Scrollable({
|
|
54806
|
+
axis,
|
|
54807
|
+
moving: false
|
|
54808
|
+
}),
|
|
54809
|
+
ScrollState()
|
|
54810
|
+
];
|
|
54811
|
+
}
|
|
54585
54812
|
if (type === "panel" || type === "input") return [Touchable()];
|
|
54586
54813
|
if (type !== "button") return [];
|
|
54587
54814
|
if (is.disabled || is.covered) return [Touchable()];
|
|
@@ -54800,6 +55027,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
54800
55027
|
let slotsVersion = -1;
|
|
54801
55028
|
let slotsRoots = -1;
|
|
54802
55029
|
let placedUnder;
|
|
55030
|
+
let rendered = 0;
|
|
54803
55031
|
/**
|
|
54804
55032
|
* Registers the key of an element under its root, so `entityOf` and `find` answer for it. A key
|
|
54805
55033
|
* `ui` gave a node of its own, a spacer's, is never registered.
|
|
@@ -54896,13 +55124,16 @@ function createReconciler(ctx, modules, fields) {
|
|
|
54896
55124
|
trackHost(state, element);
|
|
54897
55125
|
registerKey(root, element);
|
|
54898
55126
|
if (element.type === "input") fields.enter(element);
|
|
54899
|
-
if (element.type === "scroll")
|
|
55127
|
+
if (element.type === "scroll") {
|
|
55128
|
+
state.scrolls.add(entity);
|
|
55129
|
+
element.scroll = scrollMotionOf(node);
|
|
55130
|
+
}
|
|
54900
55131
|
modules.layout.attach(element);
|
|
54901
55132
|
element.basis = {
|
|
54902
55133
|
epoch: state.epoch,
|
|
54903
55134
|
rowHeight: range.rowHeightOf(parent, node)
|
|
54904
55135
|
};
|
|
54905
|
-
diffChildren(root, element, childrenOf(node, range.slotsFor(element)), instance);
|
|
55136
|
+
diffChildren(root, element, childrenOf(node, range.slotsFor(element), element.scroll?.axis), instance);
|
|
54906
55137
|
root.needsSolve = true;
|
|
54907
55138
|
return entity;
|
|
54908
55139
|
}
|
|
@@ -55039,11 +55270,12 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55039
55270
|
/**
|
|
55040
55271
|
* Writes the visual and input components of a live element again. A visual or an input the
|
|
55041
55272
|
* element no longer carries is removed, so a variant can trade the rectangle for a nine-slice and
|
|
55042
|
-
* back, and a button that became disabled stops answering the gate. `Scroll`
|
|
55043
|
-
*
|
|
55044
|
-
*
|
|
55045
|
-
* the
|
|
55046
|
-
*
|
|
55273
|
+
* back, and a button that became disabled stops answering the gate. `Scroll`, `Scrollable` and
|
|
55274
|
+
* `ScrollState` are only ever added: where a scroll stands belongs to the finger and the scroll
|
|
55275
|
+
* step, and a new look or a new rect never resets it. The look written is recorded as the rest
|
|
55276
|
+
* of the visual, so the rest the world holds is always the one the element is drawn with: a
|
|
55277
|
+
* restore brings it back to exactly that. The extras of the `components` prop are diffed last.
|
|
55278
|
+
* Every call is one write pass of `UiCounters`.
|
|
55047
55279
|
*
|
|
55048
55280
|
* @param element - The element that changed.
|
|
55049
55281
|
*/
|
|
@@ -55060,7 +55292,8 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55060
55292
|
}
|
|
55061
55293
|
/**
|
|
55062
55294
|
* Writes one visual or input value onto a live element: added when the entity lacks it, else
|
|
55063
|
-
* its fields patched, but never a tag or
|
|
55295
|
+
* its fields patched, but never a tag or the state of a scroll, and never a field its type gives
|
|
55296
|
+
* to a plugin.
|
|
55064
55297
|
*
|
|
55065
55298
|
* @param element - The live element.
|
|
55066
55299
|
* @param value - The value of this render.
|
|
@@ -55070,7 +55303,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55070
55303
|
ecs.add(element.entity, value);
|
|
55071
55304
|
return;
|
|
55072
55305
|
}
|
|
55073
|
-
if (value.value === true || value.type
|
|
55306
|
+
if (value.value === true || SCROLL_STATE.has(value.type.componentName)) return;
|
|
55074
55307
|
const owned = ecs.typeOf(value.type.componentName)?.owned ?? NO_OWNED;
|
|
55075
55308
|
ecs.set(element.entity, asHandle(value.type), livePatch(value.value, owned));
|
|
55076
55309
|
}
|
|
@@ -55149,6 +55382,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55149
55382
|
*/
|
|
55150
55383
|
function patch(root, element, node, instance) {
|
|
55151
55384
|
checkWindow(node);
|
|
55385
|
+
warnAxis(element, node);
|
|
55152
55386
|
const change = compare(root, element, node);
|
|
55153
55387
|
const motion = node.props.motion;
|
|
55154
55388
|
const loopChanged = !sameLoop(element.motion?.loop, motion?.loop);
|
|
@@ -55169,7 +55403,22 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55169
55403
|
rowHeight: change.rowHeight
|
|
55170
55404
|
};
|
|
55171
55405
|
}
|
|
55172
|
-
diffChildren(root, element, childrenOf(node, range.slotsFor(element)), instance);
|
|
55406
|
+
diffChildren(root, element, childrenOf(node, range.slotsFor(element), element.scroll?.axis), instance);
|
|
55407
|
+
}
|
|
55408
|
+
/**
|
|
55409
|
+
* Warns once when a render gives a live scroll another `axis`: it keeps the one it spawned with.
|
|
55410
|
+
*
|
|
55411
|
+
* @param element - The element being patched.
|
|
55412
|
+
* @param node - The node of this render.
|
|
55413
|
+
*/
|
|
55414
|
+
function warnAxis(element, node) {
|
|
55415
|
+
const axis = element.scroll?.axis;
|
|
55416
|
+
if (axis === void 0 || axis === axisOf(node) || element.warned.has("scroll-axis-fixed")) return;
|
|
55417
|
+
element.warned.add("scroll-axis-fixed");
|
|
55418
|
+
ctx.log.warn("ui:scroll-axis-fixed", {
|
|
55419
|
+
key: element.key ?? element.identity,
|
|
55420
|
+
axis
|
|
55421
|
+
});
|
|
55173
55422
|
}
|
|
55174
55423
|
/**
|
|
55175
55424
|
* Counts a patch that brings a loop of another reference that is not really another loop: the
|
|
@@ -55826,8 +56075,10 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55826
56075
|
* again, the roots a scene placed again, the popups that may leave, the pull of the text being
|
|
55827
56076
|
* typed, the scroll offsets and the windowed ranges, the bound texts that show a string of
|
|
55828
56077
|
* another size, the viewport, the dirty roots and the hosted views.
|
|
56078
|
+
*
|
|
56079
|
+
* @param time - The frame's `Time`: the scrolls move by its `delta`.
|
|
55829
56080
|
*/
|
|
55830
|
-
function reconcile() {
|
|
56081
|
+
function reconcile(time = NO_TIME) {
|
|
55831
56082
|
sweep();
|
|
55832
56083
|
state.reconciles += 1;
|
|
55833
56084
|
if (state.remount) remountRoots();
|
|
@@ -55835,13 +56086,14 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55835
56086
|
settlePopups(ctx, state, unmountRoot);
|
|
55836
56087
|
fields.pull();
|
|
55837
56088
|
const containers = scrollContainers();
|
|
55838
|
-
modules.layout.scroll(containers, lookup);
|
|
56089
|
+
modules.layout.scroll(containers, lookup, time);
|
|
55839
56090
|
range.step(containers);
|
|
55840
56091
|
markResizedTexts(ctx, state, modules.layout);
|
|
55841
56092
|
markResizedIcons(ctx, state, modules.layout);
|
|
55842
56093
|
const viewportChanged = modules.styles.useViewport(ctx.deps.renderer.viewport.size());
|
|
55843
56094
|
if (viewportChanged) state.epoch += 1;
|
|
55844
56095
|
const count = dirtyRoots(viewportChanged);
|
|
56096
|
+
rendered = count;
|
|
55845
56097
|
for (let index = 0; index < count; index += 1) {
|
|
55846
56098
|
const root = dirtyList[index];
|
|
55847
56099
|
if (root !== void 0) reconcileDirtyRoot(root);
|
|
@@ -55867,16 +56119,22 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55867
56119
|
/**
|
|
55868
56120
|
* The second system of phase `layout`: every root takes its slot, then one solve per marked
|
|
55869
56121
|
* root, a root in a slot after the root of its slot and in its content box, then the rects, the
|
|
55870
|
-
* rest poses and the hooks,
|
|
55871
|
-
* the
|
|
55872
|
-
*
|
|
56122
|
+
* rest poses and the hooks, the scrolls settled on what the solve and the render gave them, and
|
|
56123
|
+
* the hosting of the hosts that came. The hints released since the last frame step are dropped
|
|
56124
|
+
* at its end, so the `change.Box` and `change.Transform` hooks the solve plays get the hint the
|
|
56125
|
+
* reconcile routed, and the next frame starts with none.
|
|
56126
|
+
*
|
|
56127
|
+
* @param time - The frame's `Time`: a scroll left moving asks for a frame only while it runs.
|
|
55873
56128
|
*/
|
|
55874
|
-
function solve() {
|
|
56129
|
+
function solve(time = NO_TIME) {
|
|
55875
56130
|
const viewport = modules.styles.viewport();
|
|
55876
56131
|
viewportBox.w = viewport?.width ?? 0;
|
|
55877
56132
|
viewportBox.h = viewport?.height ?? 0;
|
|
55878
56133
|
assignSlots();
|
|
55879
|
-
|
|
56134
|
+
const changed = layOutRoots(viewportBox);
|
|
56135
|
+
if (changed) ctx.deps.time.wake();
|
|
56136
|
+
if (changed || rendered > 0) modules.layout.settleScroll(scrollContainers(), lookup, time);
|
|
56137
|
+
rendered = 0;
|
|
55880
56138
|
settleOverlays(ctx, state, modules.layout);
|
|
55881
56139
|
if (state.risen.size > 0) {
|
|
55882
56140
|
hostViews(ctx, state, state.risen);
|
|
@@ -56053,7 +56311,8 @@ function sceneLayersOf(ctx) {
|
|
|
56053
56311
|
*
|
|
56054
56312
|
* @param ctx - Domain context of the ui plugin.
|
|
56055
56313
|
* @param modules - The styles and layout modules, injected in that order.
|
|
56056
|
-
* @returns The module: `tree`, `find` and `
|
|
56314
|
+
* @returns The module: `tree`, `find`, `lint`, `fill` and `scroll` reach the game, the rest the
|
|
56315
|
+
* frame.
|
|
56057
56316
|
*/
|
|
56058
56317
|
function createJsxApi(ctx, modules) {
|
|
56059
56318
|
const state = ctx.state.jsx;
|
|
@@ -56070,6 +56329,7 @@ function createJsxApi(ctx, modules) {
|
|
|
56070
56329
|
const frame = createReconciler(ctx, modules, fields);
|
|
56071
56330
|
const focus = createFocus(ctx, frame.markPointer, topRoot, fields);
|
|
56072
56331
|
const resolvedOf = (entity) => ctx.deps.world.ecs.get(entity, Text)?.resolved;
|
|
56332
|
+
const lookup = (entity) => state.elements.get(entity);
|
|
56073
56333
|
const find = (key) => {
|
|
56074
56334
|
for (const root of sortedRoots(state, layerNames())) {
|
|
56075
56335
|
const entity = state.byKey.get(root.entity)?.get(key);
|
|
@@ -56090,12 +56350,25 @@ function createJsxApi(ctx, modules) {
|
|
|
56090
56350
|
fields.fill(entity, value);
|
|
56091
56351
|
return true;
|
|
56092
56352
|
},
|
|
56353
|
+
scroll: (key, to) => {
|
|
56354
|
+
const entity = find(key);
|
|
56355
|
+
const element = entity === void 0 ? void 0 : lookup(entity);
|
|
56356
|
+
if (element?.scroll === void 0) {
|
|
56357
|
+
ctx.log.warn("ui:scroll-without-scroll", { key });
|
|
56358
|
+
return false;
|
|
56359
|
+
}
|
|
56360
|
+
if (!modules.layout.scrollTo(element, to, lookup)) {
|
|
56361
|
+
ctx.log.warn("ui:scroll-before-solve", { key });
|
|
56362
|
+
return false;
|
|
56363
|
+
}
|
|
56364
|
+
return true;
|
|
56365
|
+
},
|
|
56093
56366
|
open: fields.open,
|
|
56094
56367
|
pointer: (sample) => fields.pointer(sample),
|
|
56095
56368
|
tapped: (entity) => focus.tapped(entity),
|
|
56096
56369
|
reconcile: frame.reconcile,
|
|
56097
|
-
solve: () => {
|
|
56098
|
-
frame.solve();
|
|
56370
|
+
solve: (time) => {
|
|
56371
|
+
frame.solve(time);
|
|
56099
56372
|
focus.refresh();
|
|
56100
56373
|
fields.place();
|
|
56101
56374
|
},
|
|
@@ -57200,25 +57473,63 @@ function createPopupHandler(ctx, state, jsx) {
|
|
|
57200
57473
|
//#endregion
|
|
57201
57474
|
//#region src/plugins/ui/layout/scroll.ts
|
|
57202
57475
|
/**
|
|
57203
|
-
* @file ui/layout —
|
|
57204
|
-
*
|
|
57205
|
-
*
|
|
57206
|
-
*
|
|
57476
|
+
* @file ui/layout — scroll. A scroll container moves its content on its axis: with the finger,
|
|
57477
|
+
* then in a fling that slows down, or in a glide to a `snap` stop; it starts at its `offset`
|
|
57478
|
+
* prop, glides when the prop changes, and stays inside the range every solve gives it, with hard
|
|
57479
|
+
* edges. The content moves by one `Transform` write on the axis: no solve, no motion, the children
|
|
57480
|
+
* keep their rects. `Scroll` holds the offset the way the windowed range is cut with (0 and below),
|
|
57481
|
+
* `ScrollState` the way a game reads it (0 to `max`), `Scrollable` whether it moves, for `input`.
|
|
57482
|
+
* The exact numbers live on the element's `scroll` record and the samples of the press in the
|
|
57483
|
+
* layout state, written in place; the components get whole reference units. A frame allocates
|
|
57484
|
+
* nothing.
|
|
57207
57485
|
*/
|
|
57486
|
+
/** How far ahead a release carries its velocity before the nearest `snap` stop is taken, in ms. */
|
|
57487
|
+
const PROJECT_MS = 260;
|
|
57488
|
+
/** What a fling keeps of its velocity per ms. */
|
|
57489
|
+
const DECAY_PER_MS = .998;
|
|
57490
|
+
/** The speed under which a fling stops, in reference units per ms. */
|
|
57491
|
+
const STOP_SPEED = .02;
|
|
57492
|
+
/** The shortest glide, in ms. */
|
|
57493
|
+
const GLIDE_MS = 260;
|
|
57494
|
+
/** The ms each reference unit of distance adds to a glide. */
|
|
57495
|
+
const GLIDE_MS_PER_UNIT = 1.1;
|
|
57496
|
+
/** The longest glide, in ms. */
|
|
57497
|
+
const GLIDE_MAX_MS = 620;
|
|
57498
|
+
/** The samples of the last ms before a release give its velocity. */
|
|
57499
|
+
const VELOCITY_MS = 100;
|
|
57500
|
+
/** A finger that stood still for longer than this before its release lets go with no velocity, in ms. */
|
|
57501
|
+
const STILL_MS = 50;
|
|
57502
|
+
/** The numbers of one sample in the ring: where the finger was on the axis, and when. */
|
|
57503
|
+
const SAMPLE_STRIDE = 2;
|
|
57504
|
+
/** The fewest samples a release velocity is read from. */
|
|
57505
|
+
const MIN_SAMPLES = 2;
|
|
57208
57506
|
/**
|
|
57209
|
-
* Keeps an offset
|
|
57210
|
-
* the top.
|
|
57507
|
+
* Keeps an offset from the start inside 0 to `max`.
|
|
57211
57508
|
*
|
|
57212
|
-
* @param offset - Where the content would
|
|
57213
|
-
* @param
|
|
57214
|
-
* @returns The offset
|
|
57509
|
+
* @param offset - Where the content would stand, from the start.
|
|
57510
|
+
* @param max - The largest offset.
|
|
57511
|
+
* @returns The offset it stands at.
|
|
57215
57512
|
* @example
|
|
57216
57513
|
* ```ts
|
|
57217
|
-
*
|
|
57514
|
+
* within(420, 390); // 390
|
|
57218
57515
|
* ```
|
|
57219
57516
|
*/
|
|
57220
|
-
function
|
|
57221
|
-
return Math.min(
|
|
57517
|
+
function within(offset, max) {
|
|
57518
|
+
return Math.min(max, Math.max(0, offset));
|
|
57519
|
+
}
|
|
57520
|
+
/**
|
|
57521
|
+
* How far a length runs past a window, in whole reference units: 0 when it fits.
|
|
57522
|
+
*
|
|
57523
|
+
* @param length - What is scrolled.
|
|
57524
|
+
* @param window - What shows of it.
|
|
57525
|
+
* @returns The largest offset.
|
|
57526
|
+
* @example
|
|
57527
|
+
* ```ts
|
|
57528
|
+
* extent(696, 306); // 390
|
|
57529
|
+
* ```
|
|
57530
|
+
*/
|
|
57531
|
+
function extent(length, window) {
|
|
57532
|
+
return Math.max(0, Math.round(length - window));
|
|
57222
57533
|
}
|
|
57223
57534
|
/**
|
|
57224
57535
|
* The content entity of a scroll container: its single child.
|
|
@@ -57232,106 +57543,491 @@ function contentOf(container, lookup) {
|
|
|
57232
57543
|
return first === void 0 ? void 0 : lookup(first);
|
|
57233
57544
|
}
|
|
57234
57545
|
/**
|
|
57235
|
-
* The
|
|
57236
|
-
*
|
|
57546
|
+
* The largest offset of a scroll on its axis: its content's rect past its own, or
|
|
57547
|
+
* `rows × rowHeight` past its height for a windowed scroll.
|
|
57237
57548
|
*
|
|
57238
|
-
* @param container - The scroll element.
|
|
57549
|
+
* @param container - The scroll element, with a solved rect.
|
|
57239
57550
|
* @param content - Its content element.
|
|
57240
|
-
* @
|
|
57551
|
+
* @param axis - The axis it moves on.
|
|
57552
|
+
* @returns The largest offset, in whole reference units.
|
|
57241
57553
|
*/
|
|
57242
|
-
function
|
|
57554
|
+
function maxOf(container, content, axis) {
|
|
57555
|
+
if (axis === "x") return extent(content.rect.w, container.rect.w);
|
|
57243
57556
|
const window = container.window;
|
|
57244
|
-
return window === void 0 ? content.rect.h : window.rows * window.rowHeight;
|
|
57557
|
+
return extent(window === void 0 ? content.rect.h : window.rows * window.rowHeight, container.rect.h);
|
|
57558
|
+
}
|
|
57559
|
+
/**
|
|
57560
|
+
* The `offset` prop of the scroll's last render: 0 when it is left out or is no finite number.
|
|
57561
|
+
*
|
|
57562
|
+
* @param container - The scroll element.
|
|
57563
|
+
* @returns The offset it asks for, from the start.
|
|
57564
|
+
*/
|
|
57565
|
+
function askedOffsetOf(container) {
|
|
57566
|
+
const offset = container.node.props.offset;
|
|
57567
|
+
return typeof offset === "number" && Number.isFinite(offset) ? offset : 0;
|
|
57568
|
+
}
|
|
57569
|
+
/**
|
|
57570
|
+
* The `snap` prop of the scroll's last render.
|
|
57571
|
+
*
|
|
57572
|
+
* @param container - The scroll element.
|
|
57573
|
+
* @returns The stops, or `undefined` when it names none.
|
|
57574
|
+
*/
|
|
57575
|
+
function snapOf(container) {
|
|
57576
|
+
const snap = container.node.props.snap;
|
|
57577
|
+
return Array.isArray(snap) && snap.length > 0 ? snap : void 0;
|
|
57245
57578
|
}
|
|
57246
57579
|
/**
|
|
57247
|
-
*
|
|
57580
|
+
* The stop nearest to an offset, each stop kept inside 0 to `max`; the first of two as near.
|
|
57581
|
+
*
|
|
57582
|
+
* @param snap - The stops the scroll names.
|
|
57583
|
+
* @param offset - Where the release would carry it.
|
|
57584
|
+
* @param max - The largest offset.
|
|
57585
|
+
* @param fallback - Where it stays when no stop is a number.
|
|
57586
|
+
* @returns The stop.
|
|
57587
|
+
* @example
|
|
57588
|
+
* ```ts
|
|
57589
|
+
* nearestStop([0, 318, 390], 300, 390, 300); // 318
|
|
57590
|
+
* ```
|
|
57591
|
+
*/
|
|
57592
|
+
function nearestStop(snap, offset, max, fallback) {
|
|
57593
|
+
let nearest = fallback;
|
|
57594
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
57595
|
+
for (const candidate of snap) {
|
|
57596
|
+
if (typeof candidate !== "number") continue;
|
|
57597
|
+
const kept = within(candidate, max);
|
|
57598
|
+
if (Math.abs(kept - offset) < distance) {
|
|
57599
|
+
nearest = kept;
|
|
57600
|
+
distance = Math.abs(kept - offset);
|
|
57601
|
+
}
|
|
57602
|
+
}
|
|
57603
|
+
return nearest;
|
|
57604
|
+
}
|
|
57605
|
+
/**
|
|
57606
|
+
* Stops a scroll where it stands.
|
|
57607
|
+
*
|
|
57608
|
+
* @param motion - Its motion.
|
|
57609
|
+
*/
|
|
57610
|
+
function stop(motion) {
|
|
57611
|
+
motion.mode = "still";
|
|
57612
|
+
motion.velocity = 0;
|
|
57613
|
+
}
|
|
57614
|
+
/**
|
|
57615
|
+
* Starts a glide to an offset, kept inside the range, on the curve `1 − (1 − p)³` over
|
|
57616
|
+
* `min(620, 260 + distance × 1.1)` ms. A scroll already there stops instead.
|
|
57617
|
+
*
|
|
57618
|
+
* @param motion - Its motion.
|
|
57619
|
+
* @param target - Where it goes, from the start.
|
|
57620
|
+
*/
|
|
57621
|
+
function glideTo(motion, target) {
|
|
57622
|
+
const to = within(target, motion.max);
|
|
57623
|
+
const distance = Math.abs(to - motion.offset);
|
|
57624
|
+
if (distance === 0) {
|
|
57625
|
+
stop(motion);
|
|
57626
|
+
return;
|
|
57627
|
+
}
|
|
57628
|
+
motion.mode = "glide";
|
|
57629
|
+
motion.velocity = 0;
|
|
57630
|
+
motion.from = motion.offset;
|
|
57631
|
+
motion.to = to;
|
|
57632
|
+
motion.elapsed = 0;
|
|
57633
|
+
motion.duration = Math.min(GLIDE_MAX_MS, GLIDE_MS + distance * GLIDE_MS_PER_UNIT);
|
|
57634
|
+
}
|
|
57635
|
+
/**
|
|
57636
|
+
* Where a sample of the press starts in the ring: `back` samples before the newest one, 0 being
|
|
57637
|
+
* the newest. The position is at the index, the time right after it.
|
|
57638
|
+
*
|
|
57639
|
+
* @param state - The layout state, which keeps the samples of the press.
|
|
57640
|
+
* @param back - How many samples before the newest; 0 to the number of slots minus 1.
|
|
57641
|
+
* @returns The index of the sample in `scrollRing`.
|
|
57642
|
+
*/
|
|
57643
|
+
function slotBefore(state, back) {
|
|
57644
|
+
const slots = state.scrollRing.length / SAMPLE_STRIDE;
|
|
57645
|
+
return (state.scrollHead + slots - 1 - back) % slots * SAMPLE_STRIDE;
|
|
57646
|
+
}
|
|
57647
|
+
/**
|
|
57648
|
+
* Follows the finger on a held scroll. The first frame of a press, a hand-off included, takes the
|
|
57649
|
+
* finger where it is as the start, stops a fling or a glide where it stands, and ends the scroll's
|
|
57650
|
+
* standing at its `offset` prop; the offset then moves against the finger, inside 0 to `max`.
|
|
57651
|
+
* Every frame adds the finger's sample to the ring.
|
|
57652
|
+
*
|
|
57653
|
+
* @param state - The layout state, which keeps the samples of the press.
|
|
57654
|
+
* @param container - The held scroll.
|
|
57655
|
+
* @param motion - Its motion.
|
|
57656
|
+
* @param pointer - The `Pointer` resource of this frame.
|
|
57657
|
+
*/
|
|
57658
|
+
function hold(state, container, motion, pointer) {
|
|
57659
|
+
const at = motion.axis === "x" ? pointer.x : pointer.y;
|
|
57660
|
+
const ring = state.scrollRing;
|
|
57661
|
+
const slots = ring.length / SAMPLE_STRIDE;
|
|
57662
|
+
if (motion.mode !== "held" || state.scrolling !== container.entity) {
|
|
57663
|
+
motion.mode = "held";
|
|
57664
|
+
motion.velocity = 0;
|
|
57665
|
+
motion.pointer = at;
|
|
57666
|
+
motion.start = motion.offset;
|
|
57667
|
+
motion.atProp = false;
|
|
57668
|
+
state.scrolling = container.entity;
|
|
57669
|
+
state.scrollSamples = 0;
|
|
57670
|
+
state.scrollMoved = state.scrollClock;
|
|
57671
|
+
}
|
|
57672
|
+
motion.offset = within(motion.start - (at - motion.pointer), motion.max);
|
|
57673
|
+
if (state.scrollSamples > 0 && ring[slotBefore(state, 0)] !== at) state.scrollMoved = state.scrollClock;
|
|
57674
|
+
ring[state.scrollHead * SAMPLE_STRIDE] = at;
|
|
57675
|
+
ring[state.scrollHead * SAMPLE_STRIDE + 1] = state.scrollClock;
|
|
57676
|
+
state.scrollHead = (state.scrollHead + 1) % slots;
|
|
57677
|
+
state.scrollSamples = Math.min(slots, state.scrollSamples + 1);
|
|
57678
|
+
}
|
|
57679
|
+
/**
|
|
57680
|
+
* The velocity of the offset at a release, in units a ms, positive toward `max`: the finger's
|
|
57681
|
+
* move over its samples of the last 100 ms, against the finger. 0 when the finger stood still for
|
|
57682
|
+
* more than 50 ms before it let go, or left a single sample.
|
|
57683
|
+
*
|
|
57684
|
+
* @param state - The layout state, which keeps the samples of the press.
|
|
57685
|
+
* @returns The velocity.
|
|
57686
|
+
*/
|
|
57687
|
+
function releaseVelocity(state) {
|
|
57688
|
+
const ring = state.scrollRing;
|
|
57689
|
+
const now = state.scrollClock;
|
|
57690
|
+
if (state.scrollSamples < MIN_SAMPLES || now - state.scrollMoved > STILL_MS) return 0;
|
|
57691
|
+
const newest = slotBefore(state, 0);
|
|
57692
|
+
let oldest = newest;
|
|
57693
|
+
for (let back = 1; back < state.scrollSamples; back += 1) {
|
|
57694
|
+
const slot = slotBefore(state, back);
|
|
57695
|
+
if ((ring[slot + 1] ?? 0) < now - VELOCITY_MS) break;
|
|
57696
|
+
oldest = slot;
|
|
57697
|
+
}
|
|
57698
|
+
const span = (ring[newest + 1] ?? 0) - (ring[oldest + 1] ?? 0);
|
|
57699
|
+
return span > 0 ? ((ring[oldest] ?? 0) - (ring[newest] ?? 0)) / span : 0;
|
|
57700
|
+
}
|
|
57701
|
+
/**
|
|
57702
|
+
* Lets go of a scroll the finger held. With `snap` it glides to the stop nearest to where its
|
|
57703
|
+
* velocity carries in 260 ms; without, it flings, or stands when it was let go too slowly. A
|
|
57704
|
+
* press whose samples a later press took lets go with no velocity.
|
|
57705
|
+
*
|
|
57706
|
+
* @param state - The layout state, which keeps the samples of the press.
|
|
57707
|
+
* @param container - The scroll that was held.
|
|
57708
|
+
* @param motion - Its motion.
|
|
57709
|
+
* @param owned - Whether the samples are this press's.
|
|
57710
|
+
*/
|
|
57711
|
+
function letGo(state, container, motion, owned) {
|
|
57712
|
+
const velocity = owned ? releaseVelocity(state) : 0;
|
|
57713
|
+
const snap = snapOf(container);
|
|
57714
|
+
if (snap !== void 0) {
|
|
57715
|
+
glideTo(motion, nearestStop(snap, motion.offset + velocity * PROJECT_MS, motion.max, motion.offset));
|
|
57716
|
+
return;
|
|
57717
|
+
}
|
|
57718
|
+
if (Math.abs(velocity) < STOP_SPEED) {
|
|
57719
|
+
stop(motion);
|
|
57720
|
+
return;
|
|
57721
|
+
}
|
|
57722
|
+
motion.mode = "fling";
|
|
57723
|
+
motion.velocity = velocity;
|
|
57724
|
+
}
|
|
57725
|
+
/**
|
|
57726
|
+
* Moves a flinging scroll by the frame: the offset by its velocity, the velocity down by 0.998 a
|
|
57727
|
+
* ms. It stops under 0.02 units a ms, or on an edge: the edges are hard and nothing passes the
|
|
57728
|
+
* rest of the fling to a parent.
|
|
57729
|
+
*
|
|
57730
|
+
* @param motion - Its motion.
|
|
57731
|
+
* @param time - The frame's `Time`.
|
|
57732
|
+
*/
|
|
57733
|
+
function fling(motion, time) {
|
|
57734
|
+
const delta = time.delta;
|
|
57735
|
+
const next = motion.offset + motion.velocity * delta;
|
|
57736
|
+
motion.offset = within(next, motion.max);
|
|
57737
|
+
motion.velocity *= DECAY_PER_MS ** delta;
|
|
57738
|
+
if (motion.offset !== next || Math.abs(motion.velocity) < STOP_SPEED) stop(motion);
|
|
57739
|
+
}
|
|
57740
|
+
/**
|
|
57741
|
+
* Moves a gliding scroll by the frame along `1 − (1 − p)³`; at its end it stands on its target.
|
|
57742
|
+
*
|
|
57743
|
+
* @param motion - Its motion.
|
|
57744
|
+
* @param time - The frame's `Time`.
|
|
57745
|
+
*/
|
|
57746
|
+
function glide(motion, time) {
|
|
57747
|
+
motion.elapsed += time.delta;
|
|
57748
|
+
const progress = Math.min(1, motion.elapsed / motion.duration);
|
|
57749
|
+
if (progress === 1) {
|
|
57750
|
+
motion.offset = motion.to;
|
|
57751
|
+
stop(motion);
|
|
57752
|
+
return;
|
|
57753
|
+
}
|
|
57754
|
+
const left = 1 - progress;
|
|
57755
|
+
motion.offset = motion.from + (motion.to - motion.from) * (1 - left * left * left);
|
|
57756
|
+
}
|
|
57757
|
+
/**
|
|
57758
|
+
* Moves the content to a whole offset on the scroll's axis, from where the solve put it: one
|
|
57759
|
+
* `Transform` write, none when it stands there already. The rects of the solve are whole numbers
|
|
57760
|
+
* that Yoga hands out as doubles; rounded, the pose is a small integer, which `Object.assign`
|
|
57761
|
+
* copies into the world without a box.
|
|
57248
57762
|
*
|
|
57249
57763
|
* @param ctx - Domain context of the ui plugin.
|
|
57764
|
+
* @param state - The layout state, which keeps the patches.
|
|
57250
57765
|
* @param container - The scroll element.
|
|
57251
57766
|
* @param content - Its content element.
|
|
57252
|
-
* @param
|
|
57767
|
+
* @param motion - Its motion.
|
|
57253
57768
|
*/
|
|
57254
|
-
function moveContent(ctx, container, content,
|
|
57769
|
+
function moveContent(ctx, state, container, content, motion) {
|
|
57255
57770
|
const ecs = ctx.deps.world.ecs;
|
|
57256
|
-
ecs.
|
|
57257
|
-
|
|
57258
|
-
|
|
57259
|
-
|
|
57260
|
-
|
|
57771
|
+
const pose = ecs.get(content.entity, Transform);
|
|
57772
|
+
const offset = Math.round(motion.offset);
|
|
57773
|
+
if (pose === void 0) return;
|
|
57774
|
+
if (motion.axis === "x") {
|
|
57775
|
+
const x = Math.round(content.rect.x - container.rect.x) - offset;
|
|
57776
|
+
if (pose.x === x) return;
|
|
57777
|
+
state.scrollPatches.x.x = x;
|
|
57778
|
+
ecs.set(content.entity, Transform, state.scrollPatches.x);
|
|
57779
|
+
return;
|
|
57780
|
+
}
|
|
57781
|
+
const y = Math.round(content.rect.y - container.rect.y) - offset;
|
|
57782
|
+
if (pose.y === y) return;
|
|
57783
|
+
state.scrollPatches.y.y = y;
|
|
57784
|
+
ecs.set(content.entity, Transform, state.scrollPatches.y);
|
|
57261
57785
|
}
|
|
57262
57786
|
/**
|
|
57263
|
-
*
|
|
57264
|
-
* when
|
|
57787
|
+
* Writes what changed of a scroll, in whole units: `Scroll` when its offset or range moved,
|
|
57788
|
+
* `ScrollState` when the offset, `max` or `moving` changed, `Scrollable` when `moving` did, and
|
|
57789
|
+
* the content's pose. A frame where nothing changed writes nothing.
|
|
57265
57790
|
*
|
|
57266
57791
|
* @param ctx - Domain context of the ui plugin.
|
|
57267
|
-
* @param
|
|
57792
|
+
* @param state - The layout state, which keeps the patches.
|
|
57793
|
+
* @param container - The scroll element.
|
|
57268
57794
|
* @param content - Its content element.
|
|
57269
|
-
* @param
|
|
57270
|
-
|
|
57271
|
-
|
|
57272
|
-
|
|
57273
|
-
const
|
|
57274
|
-
|
|
57275
|
-
const
|
|
57276
|
-
const
|
|
57277
|
-
|
|
57278
|
-
|
|
57279
|
-
|
|
57280
|
-
|
|
57281
|
-
|
|
57282
|
-
|
|
57795
|
+
* @param motion - Its motion.
|
|
57796
|
+
*/
|
|
57797
|
+
function show(ctx, state, container, content, motion) {
|
|
57798
|
+
const ecs = ctx.deps.world.ecs;
|
|
57799
|
+
const patches = state.scrollPatches;
|
|
57800
|
+
const offset = Math.round(motion.offset);
|
|
57801
|
+
const moving = motion.mode !== "still";
|
|
57802
|
+
const scroll = ecs.get(container.entity, Scroll);
|
|
57803
|
+
const shown = ecs.get(container.entity, ScrollState);
|
|
57804
|
+
const scrollable = ecs.get(container.entity, Scrollable);
|
|
57805
|
+
const rangeMoved = scroll !== void 0 && (scroll.offset !== 0 - offset || scroll.min !== 0 - motion.max);
|
|
57806
|
+
const stateMoved = shown !== void 0 && (shown.offset !== offset || shown.max !== motion.max || shown.moving !== moving);
|
|
57807
|
+
if (rangeMoved) {
|
|
57808
|
+
patches.scroll.offset = 0 - offset;
|
|
57809
|
+
patches.scroll.min = 0 - motion.max;
|
|
57810
|
+
ecs.set(container.entity, Scroll, patches.scroll);
|
|
57811
|
+
}
|
|
57812
|
+
if (stateMoved) {
|
|
57813
|
+
patches.state.offset = offset;
|
|
57814
|
+
patches.state.max = motion.max;
|
|
57815
|
+
patches.state.moving = moving;
|
|
57816
|
+
ecs.set(container.entity, ScrollState, patches.state);
|
|
57817
|
+
}
|
|
57818
|
+
if (scrollable !== void 0 && scrollable.moving !== moving) {
|
|
57819
|
+
patches.scrollable.moving = moving;
|
|
57820
|
+
ecs.set(container.entity, Scrollable, patches.scrollable);
|
|
57821
|
+
}
|
|
57822
|
+
moveContent(ctx, state, container, content, motion);
|
|
57283
57823
|
}
|
|
57284
57824
|
/**
|
|
57285
|
-
*
|
|
57286
|
-
*
|
|
57825
|
+
* Tells whether a scroll has nothing to do this frame: its first solve has not placed it yet, or
|
|
57826
|
+
* it stands still and no finger is on it.
|
|
57827
|
+
*
|
|
57828
|
+
* @param motion - Its motion.
|
|
57829
|
+
* @param pressed - Whether a finger is on it.
|
|
57830
|
+
* @returns True when the frame leaves it alone.
|
|
57831
|
+
*/
|
|
57832
|
+
function isIdle(motion, pressed) {
|
|
57833
|
+
return !motion.placed || !pressed && motion.mode === "still";
|
|
57834
|
+
}
|
|
57835
|
+
/**
|
|
57836
|
+
* Moves one scroll by the frame: the finger while it is pressed, the release when it lets go,
|
|
57837
|
+
* then a fling or a glide, and writes what changed. A still scroll no finger is on costs a tag
|
|
57838
|
+
* check.
|
|
57287
57839
|
*
|
|
57288
57840
|
* @param ctx - Domain context of the ui plugin.
|
|
57841
|
+
* @param state - The layout state.
|
|
57289
57842
|
* @param container - The live scroll element.
|
|
57290
|
-
* @param contentHeight - `rows × rowHeight` of this render.
|
|
57291
57843
|
* @param lookup - How a child entity becomes its element.
|
|
57292
|
-
* @
|
|
57844
|
+
* @param pointer - The `Pointer` resource of this frame.
|
|
57845
|
+
* @param time - The frame's `Time`.
|
|
57846
|
+
* @returns True while the scroll moves.
|
|
57293
57847
|
*/
|
|
57294
|
-
function
|
|
57848
|
+
function stepOne(ctx, state, container, lookup, pointer, time) {
|
|
57849
|
+
const ecs = ctx.deps.world.ecs;
|
|
57850
|
+
const pressed = ecs.has(container.entity, Pressed);
|
|
57851
|
+
const released = !pressed && state.scrolling === container.entity;
|
|
57852
|
+
if (released) state.scrolling = void 0;
|
|
57853
|
+
const motion = container.scroll;
|
|
57854
|
+
if (motion === void 0 || isIdle(motion, pressed)) return false;
|
|
57295
57855
|
const content = contentOf(container, lookup);
|
|
57296
|
-
if (content === void 0
|
|
57297
|
-
|
|
57856
|
+
if (content === void 0 || !ecs.has(container.entity, Scroll)) return false;
|
|
57857
|
+
if (pressed) hold(state, container, motion, pointer);
|
|
57858
|
+
else if (motion.mode === "held") letGo(state, container, motion, released);
|
|
57859
|
+
if (motion.mode === "fling") fling(motion, time);
|
|
57860
|
+
else if (motion.mode === "glide") glide(motion, time);
|
|
57861
|
+
show(ctx, state, container, content, motion);
|
|
57862
|
+
return motion.mode !== "still";
|
|
57298
57863
|
}
|
|
57299
57864
|
/**
|
|
57300
|
-
* Moves
|
|
57301
|
-
*
|
|
57865
|
+
* Moves every scroll container by the frame: with the finger while it is pressed, then in a
|
|
57866
|
+
* fling or a glide after it lets go. Time is the frame's `delta`, so a time scale and a pause
|
|
57867
|
+
* apply. While a scroll moves, `time.wake()` keeps the frames coming; at a time scale of 0 no
|
|
57868
|
+
* frame is asked for: a fling or a glide stands where it is and goes on once time runs again,
|
|
57869
|
+
* and a finger's samples wake the loop themselves.
|
|
57302
57870
|
*
|
|
57303
57871
|
* @param ctx - Domain context of the ui plugin.
|
|
57304
|
-
* @param state - The layout state, which
|
|
57872
|
+
* @param state - The layout state, which keeps the samples of the press.
|
|
57305
57873
|
* @param containers - The live scroll elements.
|
|
57306
57874
|
* @param lookup - How a child entity becomes its element.
|
|
57875
|
+
* @param time - The frame's `Time`.
|
|
57876
|
+
*/
|
|
57877
|
+
function stepScroll(ctx, state, containers, lookup, time) {
|
|
57878
|
+
const pointer = ctx.deps.world.ecs.resource(Pointer);
|
|
57879
|
+
let moving = false;
|
|
57880
|
+
state.scrollClock += time.delta;
|
|
57881
|
+
for (const container of containers) if (stepOne(ctx, state, container, lookup, pointer, time)) moving = true;
|
|
57882
|
+
if (moving && time.delta > 0) ctx.deps.time.wake();
|
|
57883
|
+
}
|
|
57884
|
+
/**
|
|
57885
|
+
* Takes a new range: the offset keeps inside it at once, and so does a running glide. A scroll
|
|
57886
|
+
* that follows its `offset` prop goes on following it in the new range: standing, it stands at
|
|
57887
|
+
* the prop at once; gliding to it, it glides on to it from where it is. So a prop the first range
|
|
57888
|
+
* was too short for is reached once the content grows, a text measured again when its font
|
|
57889
|
+
* arrives.
|
|
57890
|
+
*
|
|
57891
|
+
* @param motion - The motion of the scroll.
|
|
57892
|
+
* @param max - The largest offset the solve gives it now.
|
|
57893
|
+
*/
|
|
57894
|
+
function resize(motion, max) {
|
|
57895
|
+
if (max === motion.max) return;
|
|
57896
|
+
motion.max = max;
|
|
57897
|
+
motion.offset = within(motion.offset, max);
|
|
57898
|
+
motion.from = within(motion.from, max);
|
|
57899
|
+
motion.to = within(motion.to, max);
|
|
57900
|
+
if (!motion.atProp) return;
|
|
57901
|
+
if (motion.mode === "glide") glideTo(motion, motion.seen);
|
|
57902
|
+
else motion.offset = within(motion.seen, max);
|
|
57903
|
+
}
|
|
57904
|
+
/**
|
|
57905
|
+
* Puts a scroll the solve just spawned at its `offset` prop, inside its range, and lets it follow
|
|
57906
|
+
* the prop from there. Its spawn waits in the queue of the phase: these values replace the queued
|
|
57907
|
+
* ones at the flush, before the first draw.
|
|
57908
|
+
*
|
|
57909
|
+
* @param ctx - Domain context of the ui plugin.
|
|
57910
|
+
* @param container - The scroll element, live since this solve.
|
|
57911
|
+
* @param content - Its content element.
|
|
57912
|
+
* @param motion - Its motion, not placed yet.
|
|
57307
57913
|
*/
|
|
57308
|
-
function
|
|
57914
|
+
function place$1(ctx, container, content, motion) {
|
|
57309
57915
|
const ecs = ctx.deps.world.ecs;
|
|
57310
|
-
const
|
|
57916
|
+
const rest = content.rest;
|
|
57917
|
+
motion.placed = true;
|
|
57918
|
+
motion.atProp = true;
|
|
57919
|
+
motion.max = maxOf(container, content, motion.axis);
|
|
57920
|
+
motion.seen = askedOffsetOf(container);
|
|
57921
|
+
motion.offset = within(motion.seen, motion.max);
|
|
57922
|
+
const offset = Math.round(motion.offset);
|
|
57923
|
+
const pose = motion.axis === "x" ? {
|
|
57924
|
+
...rest,
|
|
57925
|
+
x: content.rect.x - container.rect.x - offset
|
|
57926
|
+
} : {
|
|
57927
|
+
...rest,
|
|
57928
|
+
y: content.rect.y - container.rect.y - offset
|
|
57929
|
+
};
|
|
57930
|
+
ecs.add(container.entity, Scroll({
|
|
57931
|
+
axis: motion.axis,
|
|
57932
|
+
offset: 0 - offset,
|
|
57933
|
+
min: 0 - motion.max
|
|
57934
|
+
}));
|
|
57935
|
+
ecs.add(container.entity, ScrollState({
|
|
57936
|
+
offset,
|
|
57937
|
+
max: motion.max,
|
|
57938
|
+
moving: false
|
|
57939
|
+
}));
|
|
57940
|
+
ecs.add(content.entity, Transform(pose));
|
|
57941
|
+
}
|
|
57942
|
+
/**
|
|
57943
|
+
* Settles a placed scroll on what the solve and the render of this frame gave it: a new range at
|
|
57944
|
+
* once, then a glide to a new `offset` prop, which the scroll follows from then on. A scroll the
|
|
57945
|
+
* finger holds only remembers the prop.
|
|
57946
|
+
*
|
|
57947
|
+
* @param ctx - Domain context of the ui plugin.
|
|
57948
|
+
* @param state - The layout state, which keeps the patches.
|
|
57949
|
+
* @param container - The scroll element.
|
|
57950
|
+
* @param content - Its content element.
|
|
57951
|
+
* @param motion - Its motion, placed.
|
|
57952
|
+
*/
|
|
57953
|
+
function settle(ctx, state, container, content, motion) {
|
|
57954
|
+
resize(motion, maxOf(container, content, motion.axis));
|
|
57955
|
+
const asked = askedOffsetOf(container);
|
|
57956
|
+
if (asked !== motion.seen) {
|
|
57957
|
+
motion.seen = asked;
|
|
57958
|
+
if (motion.mode !== "held") {
|
|
57959
|
+
glideTo(motion, asked);
|
|
57960
|
+
motion.atProp = true;
|
|
57961
|
+
}
|
|
57962
|
+
}
|
|
57963
|
+
show(ctx, state, container, content, motion);
|
|
57964
|
+
}
|
|
57965
|
+
/**
|
|
57966
|
+
* Settles every scroll on what the solve and the render of this frame gave it. A scroll the solve
|
|
57967
|
+
* just spawned takes its place at its `offset` prop. A placed one keeps inside a new range at
|
|
57968
|
+
* once, a running glide's target too, and glides to a new `offset` prop; the same value on a
|
|
57969
|
+
* re-render does nothing, and a scroll the finger holds only remembers it. A scroll left moving
|
|
57970
|
+
* wakes the loop the way `stepScroll` does: not at a time scale of 0, where it stands.
|
|
57971
|
+
*
|
|
57972
|
+
* @param ctx - Domain context of the ui plugin.
|
|
57973
|
+
* @param state - The layout state, which keeps the patches.
|
|
57974
|
+
* @param containers - The live scroll elements.
|
|
57975
|
+
* @param lookup - How a child entity becomes its element.
|
|
57976
|
+
* @param time - The frame's `Time`.
|
|
57977
|
+
*/
|
|
57978
|
+
function settleScroll(ctx, state, containers, lookup, time) {
|
|
57979
|
+
let moving = false;
|
|
57311
57980
|
for (const container of containers) {
|
|
57312
|
-
const
|
|
57981
|
+
const motion = container.scroll;
|
|
57313
57982
|
const content = contentOf(container, lookup);
|
|
57314
|
-
if (
|
|
57315
|
-
if (
|
|
57316
|
-
|
|
57317
|
-
|
|
57318
|
-
continue;
|
|
57319
|
-
}
|
|
57320
|
-
if (state.scrolling !== container.entity) {
|
|
57321
|
-
state.scrolling = container.entity;
|
|
57322
|
-
state.scrollStart = {
|
|
57323
|
-
pointerY: pointer.y,
|
|
57324
|
-
offset: scroll.offset
|
|
57325
|
-
};
|
|
57326
|
-
}
|
|
57327
|
-
const min = Math.min(0, container.rect.h - contentHeightOf(container, content));
|
|
57328
|
-
const offset = clampOffset(state.scrollStart.offset + (pointer.y - state.scrollStart.pointerY), min);
|
|
57329
|
-
moveContent(ctx, container, content, {
|
|
57330
|
-
...scroll,
|
|
57331
|
-
offset,
|
|
57332
|
-
min
|
|
57333
|
-
});
|
|
57983
|
+
if (motion === void 0 || content === void 0) continue;
|
|
57984
|
+
if (motion.placed) settle(ctx, state, container, content, motion);
|
|
57985
|
+
else place$1(ctx, container, content, motion);
|
|
57986
|
+
if (motion.mode !== "still") moving = true;
|
|
57334
57987
|
}
|
|
57988
|
+
if (moving && time.delta > 0) ctx.deps.time.wake();
|
|
57989
|
+
}
|
|
57990
|
+
/**
|
|
57991
|
+
* Re-clamps a live windowed scroll to the content height of its new rows before its range is
|
|
57992
|
+
* cut, so a list that shrank below the window moves up in the same frame.
|
|
57993
|
+
*
|
|
57994
|
+
* @param ctx - Domain context of the ui plugin.
|
|
57995
|
+
* @param state - The layout state, which keeps the patches.
|
|
57996
|
+
* @param container - The live scroll element.
|
|
57997
|
+
* @param contentHeight - `rows × rowHeight` of this render.
|
|
57998
|
+
* @param lookup - How a child entity becomes its element.
|
|
57999
|
+
* @returns The offset the content stands at, 0 and below, as `Scroll` holds it.
|
|
58000
|
+
*/
|
|
58001
|
+
function clampScroll(ctx, state, container, contentHeight, lookup) {
|
|
58002
|
+
const motion = container.scroll;
|
|
58003
|
+
const content = contentOf(container, lookup);
|
|
58004
|
+
if (motion === void 0 || !motion.placed || content === void 0) return 0;
|
|
58005
|
+
resize(motion, extent(contentHeight, container.rect.h));
|
|
58006
|
+
show(ctx, state, container, content, motion);
|
|
58007
|
+
return 0 - Math.round(motion.offset);
|
|
58008
|
+
}
|
|
58009
|
+
/**
|
|
58010
|
+
* Puts a placed scroll at an offset at once, inside its range: a fling or a glide stops, the
|
|
58011
|
+
* scroll no longer follows its `offset` prop, and `ScrollState` says it stands. A finger that
|
|
58012
|
+
* holds it goes on from there.
|
|
58013
|
+
*
|
|
58014
|
+
* @param ctx - Domain context of the ui plugin.
|
|
58015
|
+
* @param state - The layout state, which keeps the patches.
|
|
58016
|
+
* @param container - The scroll element.
|
|
58017
|
+
* @param to - Where it goes, from the start; `NaN` leaves it where it is.
|
|
58018
|
+
* @param lookup - How a child entity becomes its element.
|
|
58019
|
+
* @returns True when the scroll took it; false before its first solve placed it.
|
|
58020
|
+
*/
|
|
58021
|
+
function scrollTo(ctx, state, container, to, lookup) {
|
|
58022
|
+
const motion = container.scroll;
|
|
58023
|
+
const content = contentOf(container, lookup);
|
|
58024
|
+
if (motion === void 0 || !motion.placed || content === void 0) return false;
|
|
58025
|
+
motion.offset = within(Number.isNaN(to) ? motion.offset : to, motion.max);
|
|
58026
|
+
motion.atProp = false;
|
|
58027
|
+
stop(motion);
|
|
58028
|
+
show(ctx, state, container, content, motion);
|
|
58029
|
+
ctx.deps.time.wake();
|
|
58030
|
+
return true;
|
|
57335
58031
|
}
|
|
57336
58032
|
//#endregion
|
|
57337
58033
|
//#region src/plugins/ui/layout/solve.ts
|
|
@@ -57546,7 +58242,9 @@ function textSourceOf(ctx) {
|
|
|
57546
58242
|
};
|
|
57547
58243
|
}
|
|
57548
58244
|
/**
|
|
57549
|
-
* Writes the style of an element onto its node and keeps its measure function in step.
|
|
58245
|
+
* Writes the style of an element onto its node and keeps its measure function in step. An `x`
|
|
58246
|
+
* scroll lays its content out the way a row does: along `x`, as wide as it is, as tall as the
|
|
58247
|
+
* scroll.
|
|
57550
58248
|
*
|
|
57551
58249
|
* @param state - The layout state.
|
|
57552
58250
|
* @param source - What a text element is measured through.
|
|
@@ -57555,7 +58253,8 @@ function textSourceOf(ctx) {
|
|
|
57555
58253
|
function writeStyle(state, source, element) {
|
|
57556
58254
|
const node = state.byEntity.get(element.entity);
|
|
57557
58255
|
if (node === void 0 || state.yoga === void 0) return;
|
|
57558
|
-
|
|
58256
|
+
const tag = element.scroll?.axis === "x" ? "row" : element.type;
|
|
58257
|
+
applyStyleToNode(state.yoga, node, element.style, tag, element.parentType);
|
|
57559
58258
|
installMeasure(state, node, element, source);
|
|
57560
58259
|
}
|
|
57561
58260
|
/**
|
|
@@ -57618,8 +58317,10 @@ function createLayoutApi(ctx) {
|
|
|
57618
58317
|
play(ctx, element, hook);
|
|
57619
58318
|
},
|
|
57620
58319
|
settled: (element) => canDespawn(element),
|
|
57621
|
-
scroll: (containers, lookup) => stepScroll(ctx, state, containers, lookup),
|
|
57622
|
-
|
|
58320
|
+
scroll: (containers, lookup, time) => stepScroll(ctx, state, containers, lookup, time),
|
|
58321
|
+
settleScroll: (containers, lookup, time) => settleScroll(ctx, state, containers, lookup, time),
|
|
58322
|
+
clampScroll: (container, contentHeight, lookup) => clampScroll(ctx, state, container, contentHeight, lookup),
|
|
58323
|
+
scrollTo: (container, to, lookup) => scrollTo(ctx, state, container, to, lookup),
|
|
57623
58324
|
windowOf,
|
|
57624
58325
|
popupHandler: (jsx) => createPopupHandler(ctx, state, jsx),
|
|
57625
58326
|
guideHandler: () => createGuideHandler(ctx),
|
|
@@ -58195,12 +58896,12 @@ function openRegistrations(ctx, jsx, layout) {
|
|
|
58195
58896
|
name: "ui.reconcile",
|
|
58196
58897
|
phase: "layout",
|
|
58197
58898
|
query: [],
|
|
58198
|
-
run: jsx.reconcile
|
|
58899
|
+
run: (_rows, frame) => jsx.reconcile(frame.time)
|
|
58199
58900
|
})), ecs.system(system({
|
|
58200
58901
|
name: "ui.solve",
|
|
58201
58902
|
phase: "layout",
|
|
58202
58903
|
query: [],
|
|
58203
|
-
run: jsx.solve
|
|
58904
|
+
run: (_rows, frame) => jsx.solve(frame.time)
|
|
58204
58905
|
})), ecs.onAdded(Tree, (entity) => {
|
|
58205
58906
|
const place = projection.keyOf(entity);
|
|
58206
58907
|
jsx.mountRoot(entity, place?.projection ?? "ui", ecs.get(entity, Layer)?.name ?? "ui");
|
|
@@ -58247,6 +58948,7 @@ function stopUi(state) {
|
|
|
58247
58948
|
state.layout.nodes = 0;
|
|
58248
58949
|
state.layout.yoga = void 0;
|
|
58249
58950
|
state.layout.scrolling = void 0;
|
|
58951
|
+
state.layout.scrollSamples = 0;
|
|
58250
58952
|
state.layout.iconLine = void 0;
|
|
58251
58953
|
state.jsx.components.clear();
|
|
58252
58954
|
state.jsx.roots.clear();
|
|
@@ -58537,7 +59239,7 @@ function createModules(ctx) {
|
|
|
58537
59239
|
};
|
|
58538
59240
|
}
|
|
58539
59241
|
/**
|
|
58540
|
-
* Creates the ui API: the
|
|
59242
|
+
* Creates the ui API: the five members a game calls, flat.
|
|
58541
59243
|
*
|
|
58542
59244
|
* @param ctx - Kernel context of the ui plugin.
|
|
58543
59245
|
* @returns The plugin API.
|
|
@@ -58548,7 +59250,8 @@ function createUiApi(ctx) {
|
|
|
58548
59250
|
tree: jsx.tree,
|
|
58549
59251
|
find: jsx.find,
|
|
58550
59252
|
lint: jsx.lint,
|
|
58551
|
-
fill: jsx.fill
|
|
59253
|
+
fill: jsx.fill,
|
|
59254
|
+
scroll: jsx.scroll
|
|
58552
59255
|
};
|
|
58553
59256
|
}
|
|
58554
59257
|
//#endregion
|
|
@@ -58650,8 +59353,8 @@ function createJsxState() {
|
|
|
58650
59353
|
//#endregion
|
|
58651
59354
|
//#region src/plugins/ui/layout/state.ts
|
|
58652
59355
|
/**
|
|
58653
|
-
* Creates an empty map. Its own non-exported function, because lint rule L5 refuses a
|
|
58654
|
-
* built inside an exported declaration.
|
|
59356
|
+
* Creates an empty map. Its own non-exported function, because lint rule L5 refuses a
|
|
59357
|
+
* collection built inside an exported declaration.
|
|
58655
59358
|
*
|
|
58656
59359
|
* @returns A map with nothing in it.
|
|
58657
59360
|
*/
|
|
@@ -58659,13 +59362,39 @@ function emptyMap() {
|
|
|
58659
59362
|
return /* @__PURE__ */ new Map();
|
|
58660
59363
|
}
|
|
58661
59364
|
/**
|
|
59365
|
+
* Creates the ring of pointer samples: a position and a time for each of `SAMPLES`. Its own
|
|
59366
|
+
* non-exported function, for the same rule.
|
|
59367
|
+
*
|
|
59368
|
+
* @returns The ring, zeroed.
|
|
59369
|
+
*/
|
|
59370
|
+
function sampleRing() {
|
|
59371
|
+
return /* @__PURE__ */ new Float64Array(10);
|
|
59372
|
+
}
|
|
59373
|
+
/**
|
|
59374
|
+
* Creates the patches the scroll step writes with, every field 0 or false.
|
|
59375
|
+
*
|
|
59376
|
+
* @returns The patches.
|
|
59377
|
+
*/
|
|
59378
|
+
function scrollPatches() {
|
|
59379
|
+
return {
|
|
59380
|
+
scroll: {
|
|
59381
|
+
offset: 0,
|
|
59382
|
+
min: 0
|
|
59383
|
+
},
|
|
59384
|
+
state: {
|
|
59385
|
+
offset: 0,
|
|
59386
|
+
max: 0,
|
|
59387
|
+
moving: false
|
|
59388
|
+
},
|
|
59389
|
+
scrollable: { moving: false },
|
|
59390
|
+
x: { x: 0 },
|
|
59391
|
+
y: { y: 0 }
|
|
59392
|
+
};
|
|
59393
|
+
}
|
|
59394
|
+
/**
|
|
58662
59395
|
* Creates the layout branch of the ui state. Everything starts empty: Yoga arrives in `onStart`.
|
|
58663
59396
|
*
|
|
58664
59397
|
* @returns The module state.
|
|
58665
|
-
* @example
|
|
58666
|
-
* ```ts
|
|
58667
|
-
* createLayoutState().nodes; // 0
|
|
58668
|
-
* ```
|
|
58669
59398
|
*/
|
|
58670
59399
|
function createLayoutState() {
|
|
58671
59400
|
return {
|
|
@@ -58675,10 +59404,12 @@ function createLayoutState() {
|
|
|
58675
59404
|
measured: 0,
|
|
58676
59405
|
solves: 0,
|
|
58677
59406
|
scrolling: void 0,
|
|
58678
|
-
|
|
58679
|
-
|
|
58680
|
-
|
|
58681
|
-
|
|
59407
|
+
scrollClock: 0,
|
|
59408
|
+
scrollRing: sampleRing(),
|
|
59409
|
+
scrollHead: 0,
|
|
59410
|
+
scrollSamples: 0,
|
|
59411
|
+
scrollMoved: 0,
|
|
59412
|
+
scrollPatches: scrollPatches(),
|
|
58682
59413
|
iconLine: void 0,
|
|
58683
59414
|
order: 0,
|
|
58684
59415
|
cleanups: []
|
|
@@ -58740,9 +59471,10 @@ function createUiState(_ctx) {
|
|
|
58740
59471
|
};
|
|
58741
59472
|
}
|
|
58742
59473
|
/**
|
|
58743
|
-
* Ui plugin: `app.ui.tree()`, `app.ui.find(...)`, `app.ui.lint()`, `app.ui.fill(...)`,
|
|
58744
|
-
* text field `<input local>`, the components `Box`, `LocalWrite
|
|
58745
|
-
* `DebugTrigger`, and the helpers `defineComponent`, `defineStyle`,
|
|
59474
|
+
* Ui plugin: `app.ui.tree()`, `app.ui.find(...)`, `app.ui.lint()`, `app.ui.fill(...)`,
|
|
59475
|
+
* `app.ui.scroll(...)`, the text field `<input local>`, the components `Box`, `LocalWrite`,
|
|
59476
|
+
* `DebugTrigger` and `ScrollState`, and the helpers `defineComponent`, `defineStyle`,
|
|
59477
|
+
* `defineTokens` and `popup`.
|
|
58746
59478
|
*
|
|
58747
59479
|
* @example
|
|
58748
59480
|
* ```ts
|
|
@@ -60313,4 +61045,4 @@ const screen = [
|
|
|
60313
61045
|
uiPlugin
|
|
60314
61046
|
];
|
|
60315
61047
|
//#endregion
|
|
60316
|
-
export {
|
|
61048
|
+
export { haptic as $, PERF_LOG_VERSION as $t, types_exports$11 as A, Emitter as At, Box as B, audioPlugin as Bt, types_exports$4 as C, Touchable as Ct, types_exports$8 as D, messageArgument as Dt, types_exports$7 as E, i18nPlugin as Et, types_exports$16 as F, Glow as Ft, defineTokens as G, Animation as Gt, LocalWrite as H, assetsPlugin as Ht, types_exports$17 as I, Noise as It, platformPlugin as J, PASS_KINDS as Jt, defineStyle as K, Frames as Kt, uiPlugin as L, Outline as Lt, types_exports$13 as M, Blur as Mt, types_exports$14 as N, ColorMatrix as Nt, types_exports$9 as O, messageDuration as Ot, types_exports$15 as P, Displacement as Pt, frames as Q, PERF_GAP_BUCKETS as Qt, resolve as R, defineFilter as Rt, types_exports$3 as S, Tappable as St, types_exports$6 as T, Traced as Tt, ScrollState as U, animPlugin as Ut, DebugTrigger as V, scenesPlugin as Vt, popup as W, AnimPlayer as Wt, defineAnimation as X, MONITOR_SECTIONS as Xt, HAPTIC_KINDS as Y, frameAt as Yt, external as Z, PERF_CAUSES as Zt, defineBundles as _, Pointer as _t, cubicBezier as a, refuseStart as an, sfx as at, types_exports$1 as b, Pressed as bt, hint as c, to as cn, stagger as ct, defineScene as d, SaveUnreadableError as dn, wait as dt, PROBLEM_RULES as en, mark as et, tr as f, lifecyclePlugin as fn, inputPlugin as ft, music as g, Hovered as gt, defineEmitter as h, Held as ht, screen as i, flowPlugin as in, set as it, types_exports$12 as j, Alpha as jt, types_exports$10 as k, effectsPlugin as kt, schedule as l, type as ln, tween as lt, flattenFeatures as m, clockPlugin as mn, DropTarget as mt, createPlugin as n, DEMAND_REASONS as nn, play$1 as nt, defineEffect as o, exit as on, spawn as ot, defineFeature as p, timePlugin as pn, Draggable as pt, textPlugin as q, rendererPlugin as qt, defineGame as r, worldPlugin as rn, sequence as rt, guide as s, slot as sn, spawned as st, createApp as t, SHADER_WHATS as tn, parallel as tt, projection as u, modelPlugin as un, use as ut, load as v, PointerOver as vt, types_exports$5 as w, Traceable as wt, types_exports$2 as x, Swipeable as xt, types_exports as y, Pressable as yt, defineMotion as z, debugPlugin as zt };
|