@datamoc/mw_games 0.38.0 → 0.39.1
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/CHANGELOG.md +55 -0
- package/api-surface.json +500 -2
- package/dist/assets/SvgParts.d.ts +31 -0
- package/dist/assets/SvgParts.d.ts.map +1 -0
- package/dist/assets/SvgParts.js +54 -0
- package/dist/assets/SvgParts.js.map +1 -0
- package/dist/assets/SvgSource.d.ts +3 -0
- package/dist/assets/SvgSource.d.ts.map +1 -0
- package/dist/assets/SvgSource.js +22 -0
- package/dist/assets/SvgSource.js.map +1 -0
- package/dist/assets/index.d.ts +2 -0
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +1 -0
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/svg.d.ts.map +1 -1
- package/dist/assets/svg.js +9 -5
- package/dist/assets/svg.js.map +1 -1
- package/dist/mw_games.global.js +110 -110
- package/dist/mwl/presentation.d.ts.map +1 -1
- package/dist/mwl/presentation.js +25 -18
- package/dist/mwl/presentation.js.map +1 -1
- package/dist/physics/PhysicsWorld.d.ts +49 -17
- package/dist/physics/PhysicsWorld.d.ts.map +1 -1
- package/dist/physics/PhysicsWorld.js +297 -59
- package/dist/physics/PhysicsWorld.js.map +1 -1
- package/dist/physics/XpbdWorld.d.ts +1 -1
- package/dist/physics/XpbdWorld.js +1 -1
- package/dist/physics/cloth/index.d.ts +98 -0
- package/dist/physics/cloth/index.d.ts.map +1 -0
- package/dist/physics/cloth/index.js +207 -0
- package/dist/physics/cloth/index.js.map +1 -0
- package/dist/physics/index.d.ts +4 -0
- package/dist/physics/index.d.ts.map +1 -1
- package/dist/physics/index.js +2 -0
- package/dist/physics/index.js.map +1 -1
- package/dist/physics/spring/index.d.ts +90 -0
- package/dist/physics/spring/index.d.ts.map +1 -0
- package/dist/physics/spring/index.js +97 -0
- package/dist/physics/spring/index.js.map +1 -0
- package/dist/skeleton/index.d.ts +94 -13
- package/dist/skeleton/index.d.ts.map +1 -1
- package/dist/skeleton/index.js +192 -73
- package/dist/skeleton/index.js.map +1 -1
- package/dist/two-d/presentation/MwlPresentation.d.ts +12 -0
- package/dist/two-d/presentation/MwlPresentation.d.ts.map +1 -1
- package/dist/two-d/presentation/MwlPresentation.js +204 -153
- package/dist/two-d/presentation/MwlPresentation.js.map +1 -1
- package/dist/two-d/render/ClothSprite.d.ts +83 -0
- package/dist/two-d/render/ClothSprite.d.ts.map +1 -0
- package/dist/two-d/render/ClothSprite.js +105 -0
- package/dist/two-d/render/ClothSprite.js.map +1 -0
- package/dist/two-d/render/HazeLayer.d.ts +112 -0
- package/dist/two-d/render/HazeLayer.d.ts.map +1 -0
- package/dist/two-d/render/HazeLayer.js +185 -0
- package/dist/two-d/render/HazeLayer.js.map +1 -0
- package/dist/two-d/render/NeonGlow.d.ts +100 -0
- package/dist/two-d/render/NeonGlow.d.ts.map +1 -0
- package/dist/two-d/render/NeonGlow.js +183 -0
- package/dist/two-d/render/NeonGlow.js.map +1 -0
- package/dist/two-d/render/SkeletalSprite.d.ts +5 -2
- package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -1
- package/dist/two-d/render/SkeletalSprite.js +27 -3
- package/dist/two-d/render/SkeletalSprite.js.map +1 -1
- package/dist/two-d/render/SpringSprite.d.ts +40 -0
- package/dist/two-d/render/SpringSprite.d.ts.map +1 -0
- package/dist/two-d/render/SpringSprite.js +39 -0
- package/dist/two-d/render/SpringSprite.js.map +1 -0
- package/dist/two-d/render/SpriteDeform.d.ts +156 -0
- package/dist/two-d/render/SpriteDeform.d.ts.map +1 -0
- package/dist/two-d/render/SpriteDeform.js +278 -0
- package/dist/two-d/render/SpriteDeform.js.map +1 -0
- package/dist/two-d/render/SvgSkeleton.d.ts +54 -0
- package/dist/two-d/render/SvgSkeleton.d.ts.map +1 -0
- package/dist/two-d/render/SvgSkeleton.js +112 -0
- package/dist/two-d/render/SvgSkeleton.js.map +1 -0
- package/dist/two-d/render/index.d.ts +12 -0
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +6 -0
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -7
- package/sbom.cdx.json +6 -58
- package/tools/asset-report.d.mts +37 -0
- package/tools/asset-report.mjs +157 -0
- package/tools/compile-resources.d.mts +2 -0
- package/tools/compile-resources.mjs +23 -2
- package/tools/emit-page.d.mts +2 -0
- package/tools/emit-page.mjs +11 -1
- package/tools/vite.mjs +4 -1
|
@@ -9,6 +9,12 @@ import { Window } from '../ui/Window.js';
|
|
|
9
9
|
import { WindowStack } from '../ui/WindowStack.js';
|
|
10
10
|
import { Grid, flowLayout, resolveAnchor } from '../ui/Layout.js';
|
|
11
11
|
import { setTheme, theme } from '../ui/theme.js';
|
|
12
|
+
/** Fallback widget sizes, shared by construction and layout so the two cannot drift. */
|
|
13
|
+
const DEFAULT_BUTTON_WIDTH = 160;
|
|
14
|
+
const DEFAULT_BUTTON_HEIGHT = 32;
|
|
15
|
+
const DEFAULT_BAR_HEIGHT = 12;
|
|
16
|
+
const DEFAULT_WINDOW_WIDTH = 300;
|
|
17
|
+
const DEFAULT_WINDOW_HEIGHT = 200;
|
|
12
18
|
/**
|
|
13
19
|
* Instantiates compiled MWL presentation using existing Pixi widgets. The controller owns state
|
|
14
20
|
* and actions; context switches restyle the same views without rebuilding procedural adapters.
|
|
@@ -109,15 +115,15 @@ export class MwlPresentation {
|
|
|
109
115
|
const context = this.data.contexts[this.context];
|
|
110
116
|
const definition = this.data.themes[context?.theme ?? this.baseTheme];
|
|
111
117
|
this.tokens = { ...definition.tokens, ...context?.tokens };
|
|
112
|
-
const
|
|
118
|
+
const props = definition.properties;
|
|
113
119
|
setTheme({
|
|
114
120
|
...this.previousTheme,
|
|
115
|
-
padding: Number(
|
|
116
|
-
spacing: Number(
|
|
121
|
+
padding: Number(props.padding ?? this.previousTheme.padding),
|
|
122
|
+
spacing: Number(props.spacing ?? this.previousTheme.spacing),
|
|
117
123
|
font: {
|
|
118
124
|
...this.previousTheme.font,
|
|
119
|
-
family:
|
|
120
|
-
size: Number(
|
|
125
|
+
family: props.font_family ?? this.previousTheme.font.family,
|
|
126
|
+
size: Number(props.font_size ?? this.previousTheme.font.size),
|
|
121
127
|
},
|
|
122
128
|
color: {
|
|
123
129
|
...this.previousTheme.color,
|
|
@@ -224,45 +230,19 @@ export class MwlPresentation {
|
|
|
224
230
|
this.mount(node);
|
|
225
231
|
changed = true;
|
|
226
232
|
}
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
const view = this.viewOf(node);
|
|
234
|
+
if (view.visible !== visible) {
|
|
235
|
+
view.visible = visible;
|
|
229
236
|
changed = true;
|
|
230
237
|
}
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
const selected = this.read(node, 'selected', false);
|
|
234
|
-
if (disabled !== node.view.disabled) {
|
|
235
|
-
node.view.setDisabled(disabled);
|
|
236
|
-
changed = true;
|
|
237
|
-
}
|
|
238
|
-
if (selected !== node.view.selected) {
|
|
239
|
-
node.view.setSelected(selected);
|
|
240
|
-
changed = true;
|
|
241
|
-
}
|
|
242
|
-
if (disabled)
|
|
243
|
-
node.state = 'disabled';
|
|
244
|
-
else if (selected)
|
|
245
|
-
node.state = 'selected';
|
|
246
|
-
else if (node === this.focused)
|
|
247
|
-
node.state = 'focused';
|
|
248
|
-
else if (['disabled', 'selected', 'focused'].includes(node.state))
|
|
249
|
-
node.state = 'idle';
|
|
250
|
-
}
|
|
238
|
+
if (view instanceof Button)
|
|
239
|
+
changed = this.refreshButton(node, view) || changed;
|
|
251
240
|
const properties = this.resolveProperties(node);
|
|
252
241
|
if (!equalProperties(node.properties, properties)) {
|
|
253
242
|
node.properties = properties;
|
|
254
243
|
changed = true;
|
|
255
244
|
}
|
|
256
|
-
const values =
|
|
257
|
-
if (node.view instanceof Button || node.view instanceof Label)
|
|
258
|
-
values.text = this.read(node, 'text', '');
|
|
259
|
-
if (node.view instanceof Window)
|
|
260
|
-
values.title = this.read(node, 'title', '');
|
|
261
|
-
if (node.view instanceof Bar) {
|
|
262
|
-
values.value = this.read(node, 'value', 1);
|
|
263
|
-
values.max = this.read(node, 'max', 1);
|
|
264
|
-
values.color = this.read(node, 'color', this.token(properties.color_token, 'accent'));
|
|
265
|
-
}
|
|
245
|
+
const values = this.readValues(node, properties, visible);
|
|
266
246
|
if (!equalProperties(node.values, values)) {
|
|
267
247
|
node.values = values;
|
|
268
248
|
changed = true;
|
|
@@ -277,34 +257,80 @@ export class MwlPresentation {
|
|
|
277
257
|
}
|
|
278
258
|
return changed;
|
|
279
259
|
}
|
|
260
|
+
refreshButton(node, view) {
|
|
261
|
+
let changed = false;
|
|
262
|
+
const disabled = this.read(node, 'disabled', false);
|
|
263
|
+
const selected = this.read(node, 'selected', false);
|
|
264
|
+
if (disabled !== view.disabled) {
|
|
265
|
+
view.setDisabled(disabled);
|
|
266
|
+
changed = true;
|
|
267
|
+
}
|
|
268
|
+
if (selected !== view.selected) {
|
|
269
|
+
view.setSelected(selected);
|
|
270
|
+
changed = true;
|
|
271
|
+
}
|
|
272
|
+
if (disabled)
|
|
273
|
+
node.state = 'disabled';
|
|
274
|
+
else if (selected)
|
|
275
|
+
node.state = 'selected';
|
|
276
|
+
else if (node === this.focused)
|
|
277
|
+
node.state = 'focused';
|
|
278
|
+
else if (['disabled', 'selected', 'focused'].includes(node.state))
|
|
279
|
+
node.state = 'idle';
|
|
280
|
+
return changed;
|
|
281
|
+
}
|
|
282
|
+
readValues(node, properties, visible) {
|
|
283
|
+
const view = this.viewOf(node);
|
|
284
|
+
const values = { visible, alpha: Number(properties.alpha ?? 1) };
|
|
285
|
+
if (view instanceof Button || view instanceof Label)
|
|
286
|
+
values.text = this.read(node, 'text', '');
|
|
287
|
+
if (view instanceof Window)
|
|
288
|
+
values.title = this.read(node, 'title', '');
|
|
289
|
+
if (view instanceof Bar) {
|
|
290
|
+
values.value = this.read(node, 'value', 1);
|
|
291
|
+
values.max = this.read(node, 'max', 1);
|
|
292
|
+
values.color = this.read(node, 'color', this.token(properties.color_token, 'accent'));
|
|
293
|
+
}
|
|
294
|
+
return values;
|
|
295
|
+
}
|
|
296
|
+
viewOf(node) {
|
|
297
|
+
const view = node.view;
|
|
298
|
+
if (!view)
|
|
299
|
+
throw new Error(`${node.path}: missing view`);
|
|
300
|
+
return view;
|
|
301
|
+
}
|
|
280
302
|
mount(node) {
|
|
281
|
-
const
|
|
282
|
-
const width = Number(
|
|
303
|
+
const props = this.resolveProperties(node);
|
|
304
|
+
const width = Number(props.width ?? DEFAULT_BUTTON_WIDTH), height = Number(props.height ?? DEFAULT_BUTTON_HEIGHT);
|
|
283
305
|
switch (node.definition.kind) {
|
|
284
306
|
case 'ui_label':
|
|
285
|
-
node.view = new Label({ text: '', color: this.token(
|
|
307
|
+
node.view = new Label({ text: '', color: this.token(props.color_token, 'text') });
|
|
286
308
|
break;
|
|
287
309
|
case 'ui_button':
|
|
288
310
|
node.view = new Button({ width, height, text: '' });
|
|
289
311
|
break;
|
|
290
312
|
case 'ui_bar':
|
|
291
|
-
node.view = new Bar({ width, height: Number(
|
|
313
|
+
node.view = new Bar({ width, height: Number(props.height ?? DEFAULT_BAR_HEIGHT) });
|
|
292
314
|
break;
|
|
293
315
|
case 'ui_window':
|
|
294
316
|
node.view = new Window({
|
|
295
|
-
width,
|
|
296
|
-
height: Number(
|
|
317
|
+
width: Number(props.width ?? DEFAULT_WINDOW_WIDTH),
|
|
318
|
+
height: Number(props.height ?? DEFAULT_WINDOW_HEIGHT),
|
|
297
319
|
title: '',
|
|
298
|
-
modal:
|
|
299
|
-
closable:
|
|
320
|
+
modal: props.modal !== 'false',
|
|
321
|
+
closable: props.closable !== 'false',
|
|
300
322
|
closeButton: true,
|
|
301
323
|
blocker: true,
|
|
302
|
-
anchor: (
|
|
324
|
+
anchor: (props.anchor ?? 'center'),
|
|
303
325
|
});
|
|
304
326
|
break;
|
|
305
327
|
default: {
|
|
306
|
-
if (
|
|
307
|
-
|
|
328
|
+
if (props.adapter) {
|
|
329
|
+
const adapters = this.options.adapters;
|
|
330
|
+
const create = adapters && Object.hasOwn(adapters, props.adapter) ? adapters[props.adapter] : undefined;
|
|
331
|
+
if (!create)
|
|
332
|
+
throw new Error(`${node.path}: missing presentation adapter "${props.adapter}"`);
|
|
333
|
+
node.adapter = create({ tokens: this.tokens, properties: props });
|
|
308
334
|
node.view = node.adapter.view;
|
|
309
335
|
}
|
|
310
336
|
else
|
|
@@ -316,43 +342,45 @@ export class MwlPresentation {
|
|
|
316
342
|
if (node.definition.kind !== 'ui_repeat')
|
|
317
343
|
for (const child of node.definition.children)
|
|
318
344
|
node.children.push(this.createRecord(child, `${node.path}/${child.id}`, childParent, node.item));
|
|
319
|
-
if (node.view instanceof Button)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
]
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
node.
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
if (node.view instanceof Window) {
|
|
344
|
-
const window = node.view;
|
|
345
|
-
window.delegate = { handleAction: (action) => this.navigate(action, node) };
|
|
346
|
-
const close = () => {
|
|
347
|
-
node.closed = true;
|
|
348
|
-
this.unmount(node, false);
|
|
349
|
-
this.invoke(node.definition.properties.close_action, node.item);
|
|
345
|
+
if (node.view instanceof Button)
|
|
346
|
+
this.wireButton(node, node.view);
|
|
347
|
+
if (node.view instanceof Window)
|
|
348
|
+
this.wireWindow(node, node.view);
|
|
349
|
+
}
|
|
350
|
+
wireButton(node, button) {
|
|
351
|
+
const click = () => {
|
|
352
|
+
this.setFocus(node);
|
|
353
|
+
this.invoke(node.definition.properties.action, node.item);
|
|
354
|
+
if (!this.destroyed)
|
|
355
|
+
this.update();
|
|
356
|
+
};
|
|
357
|
+
button.onClick.add(click);
|
|
358
|
+
node.cleanups.push(() => button.onClick.remove(click));
|
|
359
|
+
for (const [event, state] of [
|
|
360
|
+
['pointerover', 'hover'],
|
|
361
|
+
['pointerout', 'idle'],
|
|
362
|
+
['pointerdown', 'pressed'],
|
|
363
|
+
['pointerup', 'hover'],
|
|
364
|
+
]) {
|
|
365
|
+
const listener = () => {
|
|
366
|
+
node.state = state;
|
|
367
|
+
this.update();
|
|
350
368
|
};
|
|
351
|
-
|
|
352
|
-
node.cleanups.push(() =>
|
|
353
|
-
this.windows.push(window);
|
|
369
|
+
button.on(event, listener);
|
|
370
|
+
node.cleanups.push(() => button.off(event, listener));
|
|
354
371
|
}
|
|
355
372
|
}
|
|
373
|
+
wireWindow(node, window) {
|
|
374
|
+
window.delegate = { handleAction: (action) => this.navigate(action, node) };
|
|
375
|
+
const close = () => {
|
|
376
|
+
node.closed = true;
|
|
377
|
+
this.unmount(node, false);
|
|
378
|
+
this.invoke(node.definition.properties.close_action, node.item);
|
|
379
|
+
};
|
|
380
|
+
window.onClose.add(close);
|
|
381
|
+
node.cleanups.push(() => window.onClose.remove(close));
|
|
382
|
+
this.windows.push(window);
|
|
383
|
+
}
|
|
356
384
|
reconcile(node) {
|
|
357
385
|
const p = node.definition.properties;
|
|
358
386
|
const items = this.state[p.items_binding];
|
|
@@ -375,7 +403,8 @@ export class MwlPresentation {
|
|
|
375
403
|
for (const item of items) {
|
|
376
404
|
const prefix = `${node.path}/${encodeURIComponent(String(item[p.key]))}`;
|
|
377
405
|
const template = node.definition.children[0];
|
|
378
|
-
const child = existing.get(prefix) ??
|
|
406
|
+
const child = existing.get(prefix) ??
|
|
407
|
+
this.createRecord(template, `${prefix}/${template.id}`, this.viewOf(node), item);
|
|
379
408
|
if (!existing.has(prefix))
|
|
380
409
|
changed = true;
|
|
381
410
|
existing.delete(prefix);
|
|
@@ -395,16 +424,16 @@ export class MwlPresentation {
|
|
|
395
424
|
return this.tokens[name ?? fallback];
|
|
396
425
|
}
|
|
397
426
|
applyProperties(node) {
|
|
398
|
-
const view = node
|
|
427
|
+
const view = this.viewOf(node), props = node.properties, values = node.values;
|
|
399
428
|
view.alpha = values.alpha;
|
|
400
429
|
if (node.definition.kind === 'scene_layer' && !node.adapter) {
|
|
401
|
-
if (
|
|
430
|
+
if (props.image) {
|
|
402
431
|
if (!node.image) {
|
|
403
|
-
node.image = new Sprite(texture(
|
|
432
|
+
node.image = new Sprite(texture(props.image));
|
|
404
433
|
view.addChildAt(node.image, 0);
|
|
405
434
|
}
|
|
406
435
|
else
|
|
407
|
-
node.image.texture = texture(
|
|
436
|
+
node.image.texture = texture(props.image);
|
|
408
437
|
}
|
|
409
438
|
else {
|
|
410
439
|
node.image?.destroy();
|
|
@@ -412,11 +441,11 @@ export class MwlPresentation {
|
|
|
412
441
|
}
|
|
413
442
|
}
|
|
414
443
|
if (['ui_container', 'ui_row', 'ui_column', 'ui_grid', 'ui_mount', 'ui_repeat'].includes(node.definition.kind)) {
|
|
415
|
-
if (
|
|
444
|
+
if (props.background_token && !node.background) {
|
|
416
445
|
node.background = new Graphics();
|
|
417
446
|
view.addChildAt(node.background, 0);
|
|
418
447
|
}
|
|
419
|
-
else if (!
|
|
448
|
+
else if (!props.background_token && node.background) {
|
|
420
449
|
node.background.destroy();
|
|
421
450
|
node.background = undefined;
|
|
422
451
|
}
|
|
@@ -424,34 +453,37 @@ export class MwlPresentation {
|
|
|
424
453
|
const translate = this.options.translate ?? ((text) => text);
|
|
425
454
|
if (view instanceof Label) {
|
|
426
455
|
view.setText(translate(values.text));
|
|
427
|
-
view.setColor(this.token(
|
|
456
|
+
view.setColor(this.token(props.color_token, 'text'));
|
|
428
457
|
}
|
|
429
458
|
if (view instanceof Button) {
|
|
430
459
|
view.setText(translate(values.text));
|
|
431
460
|
view.setColors({
|
|
432
|
-
text: this.token(
|
|
433
|
-
disabledText: this.token(
|
|
434
|
-
background: this.token(
|
|
435
|
-
border: this.token(
|
|
461
|
+
text: this.token(props.color_token, 'text'),
|
|
462
|
+
disabledText: this.token(props.color_token, 'text'),
|
|
463
|
+
background: this.token(props.background_token, 'panel'),
|
|
464
|
+
border: this.token(props.border_token, 'accent'),
|
|
436
465
|
selection: node.definition.states[node.state]?.background_token
|
|
437
|
-
? this.token(
|
|
466
|
+
? this.token(props.background_token, 'selection')
|
|
438
467
|
: this.tokens.selection,
|
|
439
468
|
});
|
|
440
469
|
}
|
|
441
470
|
if (view instanceof Bar) {
|
|
442
471
|
view.setValue(values.value, values.max);
|
|
443
472
|
view.setColor(values.color);
|
|
444
|
-
view.setBackground(this.token(
|
|
473
|
+
view.setBackground(this.token(props.background_token, 'panel'));
|
|
445
474
|
}
|
|
446
475
|
if (view instanceof Window) {
|
|
447
476
|
view.setTitle(translate(values.title));
|
|
448
477
|
view.setColors({
|
|
449
|
-
background: this.token(
|
|
450
|
-
border: this.token(
|
|
451
|
-
title: this.token(
|
|
478
|
+
background: this.token(props.background_token, 'panel'),
|
|
479
|
+
border: this.token(props.border_token, 'accent'),
|
|
480
|
+
title: this.token(props.color_token, 'accent'),
|
|
452
481
|
});
|
|
453
482
|
}
|
|
454
|
-
node.adapter?.restyle?.({ tokens: this.tokens, properties:
|
|
483
|
+
node.adapter?.restyle?.({ tokens: this.tokens, properties: props });
|
|
484
|
+
}
|
|
485
|
+
isVisible(node) {
|
|
486
|
+
return Boolean(node.view?.visible);
|
|
455
487
|
}
|
|
456
488
|
layout() {
|
|
457
489
|
const bounds = { x: 0, y: 0, ...this.viewport };
|
|
@@ -465,63 +497,79 @@ export class MwlPresentation {
|
|
|
465
497
|
const view = node.view;
|
|
466
498
|
if (!view || !view.visible)
|
|
467
499
|
return;
|
|
468
|
-
const
|
|
469
|
-
const
|
|
470
|
-
const
|
|
471
|
-
let width = Math.min(Number(p.width ?? defaultWidth), bounds.width);
|
|
472
|
-
let height = Number(p.height ??
|
|
473
|
-
(view instanceof Button
|
|
474
|
-
? 32
|
|
475
|
-
: view instanceof Bar
|
|
476
|
-
? 12
|
|
477
|
-
: view instanceof Label
|
|
478
|
-
? view.height
|
|
479
|
-
: bounds.height));
|
|
480
|
-
if (view instanceof Window) {
|
|
481
|
-
width = Math.max(0, Math.min(Number(p.width ?? 300), this.viewport.width - theme().padding * 2));
|
|
482
|
-
height = Math.max(0, Math.min(Number(p.height ?? 200), this.viewport.height - theme().padding * 2));
|
|
483
|
-
view.resize(width, height);
|
|
484
|
-
}
|
|
485
|
-
else if (view instanceof Button || view instanceof Bar)
|
|
486
|
-
view.resize(width, height);
|
|
487
|
-
else if (view instanceof Label) {
|
|
488
|
-
view.setWrapWidth(width);
|
|
489
|
-
height = view.height;
|
|
490
|
-
}
|
|
491
|
-
const padding = Number(p.padding ?? 0), gap = Number(p.gap ?? 0);
|
|
500
|
+
const { width, height: measured } = this.measureNode(node, view, bounds);
|
|
501
|
+
const props = node.properties;
|
|
502
|
+
const padding = Number(props.padding ?? 0), gap = Number(props.gap ?? 0);
|
|
492
503
|
const inner = {
|
|
493
504
|
x: 0,
|
|
494
505
|
y: 0,
|
|
495
506
|
width: Math.max(0, width - padding * 2),
|
|
496
|
-
height: Math.max(0,
|
|
507
|
+
height: Math.max(0, measured - padding * 2),
|
|
497
508
|
};
|
|
498
509
|
if (view instanceof Window) {
|
|
499
510
|
inner.width = view.contentWidth;
|
|
500
511
|
inner.height = view.contentHeight;
|
|
501
512
|
}
|
|
502
|
-
const visible = node.children.filter((child) => child
|
|
513
|
+
const visible = node.children.filter((child) => this.isVisible(child));
|
|
503
514
|
for (const child of visible)
|
|
504
515
|
this.layoutNode(child, inner);
|
|
516
|
+
const auto = this.placeChildren(node, visible, inner, padding, gap);
|
|
517
|
+
const height = auto ?? measured;
|
|
518
|
+
node.width = width;
|
|
519
|
+
node.height = height;
|
|
520
|
+
this.paintNode(node, width, height);
|
|
521
|
+
this.anchorNode(node, view, bounds, width, height);
|
|
522
|
+
node.adapter?.resize?.(width, height);
|
|
523
|
+
}
|
|
524
|
+
measureNode(node, view, bounds) {
|
|
525
|
+
const props = node.properties;
|
|
526
|
+
const defaultWidth = view instanceof Button || view instanceof Bar ? DEFAULT_BUTTON_WIDTH : bounds.width;
|
|
527
|
+
let width = Math.min(Number(props.width ?? defaultWidth), bounds.width);
|
|
528
|
+
let height = Number(props.height ?? this.defaultHeight(view, bounds));
|
|
529
|
+
if (view instanceof Window) {
|
|
530
|
+
width = Math.max(0, Math.min(Number(props.width ?? DEFAULT_WINDOW_WIDTH), this.viewport.width - theme().padding * 2));
|
|
531
|
+
height = Math.max(0, Math.min(Number(props.height ?? DEFAULT_WINDOW_HEIGHT), this.viewport.height - theme().padding * 2));
|
|
532
|
+
view.resize(width, height);
|
|
533
|
+
}
|
|
534
|
+
else if (view instanceof Button || view instanceof Bar)
|
|
535
|
+
view.resize(width, height);
|
|
536
|
+
else if (view instanceof Label) {
|
|
537
|
+
view.setWrapWidth(width);
|
|
538
|
+
height = view.height;
|
|
539
|
+
}
|
|
540
|
+
return { width, height };
|
|
541
|
+
}
|
|
542
|
+
defaultHeight(view, bounds) {
|
|
543
|
+
if (view instanceof Button)
|
|
544
|
+
return DEFAULT_BUTTON_HEIGHT;
|
|
545
|
+
if (view instanceof Bar)
|
|
546
|
+
return DEFAULT_BAR_HEIGHT;
|
|
547
|
+
if (view instanceof Label)
|
|
548
|
+
return view.height;
|
|
549
|
+
return bounds.height;
|
|
550
|
+
}
|
|
551
|
+
/** Positions visible children; returns the content height when the node sizes to fit. */
|
|
552
|
+
placeChildren(node, visible, inner, padding, gap) {
|
|
553
|
+
const props = node.properties;
|
|
554
|
+
const kind = node.definition.kind;
|
|
505
555
|
if (['ui_row', 'ui_repeat'].includes(kind)) {
|
|
506
556
|
const total = visible.reduce((sum, child) => sum + child.width, 0) + gap * Math.max(0, visible.length - 1);
|
|
507
|
-
const result = flowLayout(visible, kind === 'ui_row' &&
|
|
557
|
+
const result = flowLayout(visible, kind === 'ui_row' && props.wrap === 'false' ? Math.max(total, inner.width) : inner.width, { gap, align: props.align });
|
|
508
558
|
visible.forEach((child, i) => {
|
|
509
559
|
child.view.position.set(padding + result.positions[i].x, padding + result.positions[i].y);
|
|
510
560
|
});
|
|
511
|
-
|
|
512
|
-
height = result.height + padding * 2;
|
|
561
|
+
return props.height === undefined ? result.height + padding * 2 : undefined;
|
|
513
562
|
}
|
|
514
|
-
|
|
563
|
+
if (kind === 'ui_column') {
|
|
515
564
|
let y = padding;
|
|
516
565
|
for (const child of visible) {
|
|
517
566
|
child.view.position.set(padding, y);
|
|
518
567
|
y += child.height + gap;
|
|
519
568
|
}
|
|
520
|
-
|
|
521
|
-
height = Math.max(0, y - gap + padding);
|
|
569
|
+
return props.height === undefined ? Math.max(0, y - gap + padding) : undefined;
|
|
522
570
|
}
|
|
523
|
-
|
|
524
|
-
const columns = Number(
|
|
571
|
+
if (kind === 'ui_grid') {
|
|
572
|
+
const columns = Number(props.columns), rows = Math.ceil(visible.length / columns);
|
|
525
573
|
const grid = new Grid({
|
|
526
574
|
columns: Array.from({ length: columns }, () => ({ grow: 1 })),
|
|
527
575
|
rows: Array.from({ length: rows }, () => ({
|
|
@@ -533,35 +581,38 @@ export class MwlPresentation {
|
|
|
533
581
|
const cell = grid.rect(Math.floor(i / columns), i % columns, inner);
|
|
534
582
|
this.layoutNode(child, { ...cell, x: cell.x + padding, y: cell.y + padding });
|
|
535
583
|
});
|
|
536
|
-
if (
|
|
537
|
-
height
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
584
|
+
if (props.height === undefined)
|
|
585
|
+
return (grid.rowSizes(inner.height).reduce((sum, size) => sum + size, 0) +
|
|
586
|
+
gap * Math.max(0, rows - 1) +
|
|
587
|
+
padding * 2);
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
if (['ui_container', 'ui_mount'].includes(kind) && props.height === undefined)
|
|
591
|
+
return Math.max(0, ...visible.map((child) => child.view.y + child.height)) + padding * 2;
|
|
592
|
+
return undefined;
|
|
593
|
+
}
|
|
594
|
+
paintNode(node, width, height) {
|
|
595
|
+
const props = node.properties;
|
|
547
596
|
if (node.background) {
|
|
548
|
-
node.background.clear().rect(0, 0, width, height).fill(this.token(
|
|
549
|
-
if (
|
|
550
|
-
node.background.stroke({ color: this.token(
|
|
597
|
+
node.background.clear().rect(0, 0, width, height).fill(this.token(props.background_token, 'panel'));
|
|
598
|
+
if (props.border_token)
|
|
599
|
+
node.background.stroke({ color: this.token(props.border_token, 'accent'), width: 1 });
|
|
551
600
|
}
|
|
601
|
+
}
|
|
602
|
+
anchorNode(node, view, bounds, width, height) {
|
|
603
|
+
const props = node.properties;
|
|
552
604
|
if (node.parent === this.camera.world)
|
|
553
|
-
view.position.set(Number(
|
|
605
|
+
view.position.set(Number(props.x ?? 0), Number(props.y ?? 0));
|
|
554
606
|
else if (!(view instanceof Window)) {
|
|
555
607
|
const placed = resolveAnchor({
|
|
556
|
-
anchor: (
|
|
608
|
+
anchor: (props.anchor ?? 'top-left'),
|
|
557
609
|
width,
|
|
558
610
|
height,
|
|
559
|
-
offsetX: Number(
|
|
560
|
-
offsetY: Number(
|
|
611
|
+
offsetX: Number(props.x ?? 0),
|
|
612
|
+
offsetY: Number(props.y ?? 0),
|
|
561
613
|
}, bounds);
|
|
562
614
|
view.position.set(placed.x, placed.y);
|
|
563
615
|
}
|
|
564
|
-
node.adapter?.resize?.(width, height);
|
|
565
616
|
}
|
|
566
617
|
invoke(action, item) {
|
|
567
618
|
if (action)
|