@datamoc/mw_games 0.7.6 → 0.7.7
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 +270 -0
- package/dist/actors/Inventory.d.ts +7 -1
- package/dist/actors/Inventory.d.ts.map +1 -1
- package/dist/actors/Inventory.js.map +1 -1
- package/dist/actors/craft.d.ts +35 -14
- package/dist/actors/craft.d.ts.map +1 -1
- package/dist/actors/craft.js +56 -24
- package/dist/actors/craft.js.map +1 -1
- package/dist/actors/index.d.ts +1 -1
- package/dist/actors/index.d.ts.map +1 -1
- package/dist/ai/Heuristics.d.ts +232 -0
- package/dist/ai/Heuristics.d.ts.map +1 -0
- package/dist/ai/Heuristics.js +298 -0
- package/dist/ai/Heuristics.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +2 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/score.d.ts +126 -0
- package/dist/ai/score.d.ts.map +1 -0
- package/dist/ai/score.js +121 -0
- package/dist/ai/score.js.map +1 -0
- package/dist/assets/loader.d.ts +18 -4
- package/dist/assets/loader.d.ts.map +1 -1
- package/dist/assets/loader.js +40 -11
- package/dist/assets/loader.js.map +1 -1
- package/dist/audio/Positional.d.ts +125 -0
- package/dist/audio/Positional.d.ts.map +1 -0
- package/dist/audio/Positional.js +135 -0
- package/dist/audio/Positional.js.map +1 -0
- package/dist/audio/Sound.d.ts +7 -1
- package/dist/audio/Sound.d.ts.map +1 -1
- package/dist/audio/Sound.js +8 -2
- package/dist/audio/Sound.js.map +1 -1
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +1 -0
- package/dist/audio/index.js.map +1 -1
- package/dist/battle/AttackDialog.d.ts +75 -0
- package/dist/battle/AttackDialog.d.ts.map +1 -0
- package/dist/battle/AttackDialog.js +115 -0
- package/dist/battle/AttackDialog.js.map +1 -0
- package/dist/battle/AttackPreview.d.ts +70 -0
- package/dist/battle/AttackPreview.d.ts.map +1 -0
- package/dist/battle/AttackPreview.js +78 -0
- package/dist/battle/AttackPreview.js.map +1 -0
- package/dist/battle/BattleStats.d.ts +45 -0
- package/dist/battle/BattleStats.d.ts.map +1 -0
- package/dist/battle/BattleStats.js +63 -0
- package/dist/battle/BattleStats.js.map +1 -0
- package/dist/battle/UnitSelector.d.ts +72 -0
- package/dist/battle/UnitSelector.d.ts.map +1 -0
- package/dist/battle/UnitSelector.js +117 -0
- package/dist/battle/UnitSelector.js.map +1 -0
- package/dist/battle/Whiteboard.d.ts +50 -0
- package/dist/battle/Whiteboard.d.ts.map +1 -0
- package/dist/battle/Whiteboard.js +94 -0
- package/dist/battle/Whiteboard.js.map +1 -0
- package/dist/battle/index.d.ts +10 -0
- package/dist/battle/index.d.ts.map +1 -1
- package/dist/battle/index.js +5 -0
- package/dist/battle/index.js.map +1 -1
- package/dist/board/FogOfWar.d.ts +32 -2
- package/dist/board/FogOfWar.d.ts.map +1 -1
- package/dist/board/FogOfWar.js +70 -6
- package/dist/board/FogOfWar.js.map +1 -1
- package/dist/core/Achievements.d.ts +40 -7
- package/dist/core/Achievements.d.ts.map +1 -1
- package/dist/core/Achievements.js +51 -7
- package/dist/core/Achievements.js.map +1 -1
- package/dist/core/Hex.d.ts +26 -6
- package/dist/core/Hex.d.ts.map +1 -1
- package/dist/core/Hex.js +40 -13
- package/dist/core/Hex.js.map +1 -1
- package/dist/core/Input.d.ts +36 -0
- package/dist/core/Input.d.ts.map +1 -1
- package/dist/core/Input.js +63 -1
- package/dist/core/Input.js.map +1 -1
- package/dist/core/Random.d.ts +93 -0
- package/dist/core/Random.d.ts.map +1 -1
- package/dist/core/Random.js +177 -0
- package/dist/core/Random.js.map +1 -1
- package/dist/core/SyncGuard.d.ts +57 -0
- package/dist/core/SyncGuard.d.ts.map +1 -0
- package/dist/core/SyncGuard.js +116 -0
- package/dist/core/SyncGuard.js.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/i18n/Po.d.ts +51 -0
- package/dist/i18n/Po.d.ts.map +1 -0
- package/dist/i18n/Po.js +176 -0
- package/dist/i18n/Po.js.map +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/index.js.map +1 -1
- package/dist/mw_games.global.js +104 -98
- package/dist/mwl/MapFile.d.ts +41 -0
- package/dist/mwl/MapFile.d.ts.map +1 -0
- package/dist/mwl/MapFile.js +43 -0
- package/dist/mwl/MapFile.js.map +1 -0
- package/dist/mwl/campaign.d.ts +51 -0
- package/dist/mwl/campaign.d.ts.map +1 -0
- package/dist/mwl/campaign.js +58 -0
- package/dist/mwl/campaign.js.map +1 -0
- package/dist/mwl/carryover.d.ts +79 -0
- package/dist/mwl/carryover.d.ts.map +1 -0
- package/dist/mwl/carryover.js +63 -0
- package/dist/mwl/carryover.js.map +1 -0
- package/dist/mwl/content.d.ts +10 -0
- package/dist/mwl/content.d.ts.map +1 -1
- package/dist/mwl/content.js +8 -0
- package/dist/mwl/content.js.map +1 -1
- package/dist/mwl/hooks.d.ts +6 -5
- package/dist/mwl/hooks.d.ts.map +1 -1
- package/dist/mwl/hooks.js.map +1 -1
- package/dist/mwl/index.d.ts +8 -2
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +4 -1
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/runtime.d.ts +140 -7
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +427 -33
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +97 -18
- package/dist/mwl/schema.js.map +1 -1
- package/dist/roguelike/MultiTurnBeam.d.ts +50 -12
- package/dist/roguelike/MultiTurnBeam.d.ts.map +1 -1
- package/dist/roguelike/MultiTurnBeam.js +100 -53
- package/dist/roguelike/MultiTurnBeam.js.map +1 -1
- package/dist/roguelike/Targeting.d.ts +94 -0
- package/dist/roguelike/Targeting.d.ts.map +1 -1
- package/dist/roguelike/Targeting.js +123 -0
- package/dist/roguelike/Targeting.js.map +1 -1
- package/dist/roguelike/index.d.ts +3 -1
- package/dist/roguelike/index.d.ts.map +1 -1
- package/dist/roguelike/index.js +1 -0
- package/dist/roguelike/index.js.map +1 -1
- package/dist/simulation/CampaignSave.d.ts +68 -0
- package/dist/simulation/CampaignSave.d.ts.map +1 -0
- package/dist/simulation/CampaignSave.js +54 -0
- package/dist/simulation/CampaignSave.js.map +1 -0
- package/dist/simulation/EventPresentation.d.ts +111 -0
- package/dist/simulation/EventPresentation.d.ts.map +1 -0
- package/dist/simulation/EventPresentation.js +120 -0
- package/dist/simulation/EventPresentation.js.map +1 -0
- package/dist/simulation/index.d.ts +4 -0
- package/dist/simulation/index.d.ts.map +1 -1
- package/dist/simulation/index.js +2 -0
- package/dist/simulation/index.js.map +1 -1
- package/dist/two-d/pixi-interop.d.ts +18 -6
- package/dist/two-d/pixi-interop.d.ts.map +1 -1
- package/dist/two-d/pixi-interop.js +18 -6
- package/dist/two-d/pixi-interop.js.map +1 -1
- package/dist/two-d/render/AnimatedSprite.d.ts +74 -7
- package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
- package/dist/two-d/render/AnimatedSprite.js +88 -26
- package/dist/two-d/render/AnimatedSprite.js.map +1 -1
- package/dist/two-d/render/Camera.d.ts +64 -4
- package/dist/two-d/render/Camera.d.ts.map +1 -1
- package/dist/two-d/render/Camera.js +132 -11
- package/dist/two-d/render/Camera.js.map +1 -1
- package/dist/two-d/render/Halo.d.ts +58 -0
- package/dist/two-d/render/Halo.d.ts.map +1 -0
- package/dist/two-d/render/Halo.js +48 -0
- package/dist/two-d/render/Halo.js.map +1 -0
- package/dist/two-d/render/ImageModifiers.d.ts +174 -0
- package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
- package/dist/two-d/render/ImageModifiers.js +438 -1
- package/dist/two-d/render/ImageModifiers.js.map +1 -1
- package/dist/two-d/render/LightningArc.d.ts +57 -0
- package/dist/two-d/render/LightningArc.d.ts.map +1 -0
- package/dist/two-d/render/LightningArc.js +91 -0
- package/dist/two-d/render/LightningArc.js.map +1 -0
- package/dist/two-d/render/PaletteRemap.d.ts +135 -0
- package/dist/two-d/render/PaletteRemap.d.ts.map +1 -0
- package/dist/two-d/render/PaletteRemap.js +181 -0
- package/dist/two-d/render/PaletteRemap.js.map +1 -0
- package/dist/two-d/render/Particles.d.ts +26 -0
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +23 -2
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/SpriteAttachment.d.ts +55 -0
- package/dist/two-d/render/SpriteAttachment.d.ts.map +1 -0
- package/dist/two-d/render/SpriteAttachment.js +60 -0
- package/dist/two-d/render/SpriteAttachment.js.map +1 -0
- package/dist/two-d/render/TerrainGraphics.d.ts +142 -0
- package/dist/two-d/render/TerrainGraphics.d.ts.map +1 -0
- package/dist/two-d/render/TerrainGraphics.js +161 -0
- package/dist/two-d/render/TerrainGraphics.js.map +1 -0
- package/dist/two-d/render/index.d.ts +13 -3
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +6 -1
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/two-d/stage/StoryScreen.d.ts +110 -0
- package/dist/two-d/stage/StoryScreen.d.ts.map +1 -0
- package/dist/two-d/stage/StoryScreen.js +178 -0
- package/dist/two-d/stage/StoryScreen.js.map +1 -0
- package/dist/two-d/stage/index.d.ts +2 -0
- package/dist/two-d/stage/index.d.ts.map +1 -1
- package/dist/two-d/stage/index.js +1 -0
- package/dist/two-d/stage/index.js.map +1 -1
- package/dist/two-d/ui/Checkbox.d.ts +44 -0
- package/dist/two-d/ui/Checkbox.d.ts.map +1 -0
- package/dist/two-d/ui/Checkbox.js +89 -0
- package/dist/two-d/ui/Checkbox.js.map +1 -0
- package/dist/two-d/ui/DataTable.d.ts +79 -0
- package/dist/two-d/ui/DataTable.d.ts.map +1 -0
- package/dist/two-d/ui/DataTable.js +164 -0
- package/dist/two-d/ui/DataTable.js.map +1 -0
- package/dist/two-d/ui/Dropdown.d.ts +72 -0
- package/dist/two-d/ui/Dropdown.d.ts.map +1 -0
- package/dist/two-d/ui/Dropdown.js +126 -0
- package/dist/two-d/ui/Dropdown.js.map +1 -0
- package/dist/two-d/ui/FloatingText.d.ts +43 -4
- package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingText.js +52 -5
- package/dist/two-d/ui/FloatingText.js.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.d.ts +86 -13
- package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.js +89 -18
- package/dist/two-d/ui/FloatingTextStack.js.map +1 -1
- package/dist/two-d/ui/Layout.d.ts +105 -0
- package/dist/two-d/ui/Layout.d.ts.map +1 -0
- package/dist/two-d/ui/Layout.js +174 -0
- package/dist/two-d/ui/Layout.js.map +1 -0
- package/dist/two-d/ui/ScrollBox.d.ts +67 -0
- package/dist/two-d/ui/ScrollBox.d.ts.map +1 -0
- package/dist/two-d/ui/ScrollBox.js +127 -0
- package/dist/two-d/ui/ScrollBox.js.map +1 -0
- package/dist/two-d/ui/Skins.d.ts +59 -0
- package/dist/two-d/ui/Skins.d.ts.map +1 -0
- package/dist/two-d/ui/Skins.js +77 -0
- package/dist/two-d/ui/Skins.js.map +1 -0
- package/dist/two-d/ui/Slider.d.ts +91 -0
- package/dist/two-d/ui/Slider.d.ts.map +1 -0
- package/dist/two-d/ui/Slider.js +170 -0
- package/dist/two-d/ui/Slider.js.map +1 -0
- package/dist/two-d/ui/Spinner.d.ts +67 -0
- package/dist/two-d/ui/Spinner.d.ts.map +1 -0
- package/dist/two-d/ui/Spinner.js +137 -0
- package/dist/two-d/ui/Spinner.js.map +1 -0
- package/dist/two-d/ui/TabbedList.d.ts +118 -0
- package/dist/two-d/ui/TabbedList.d.ts.map +1 -0
- package/dist/two-d/ui/TabbedList.js +221 -0
- package/dist/two-d/ui/TabbedList.js.map +1 -0
- package/dist/two-d/ui/TextModel.d.ts +65 -0
- package/dist/two-d/ui/TextModel.d.ts.map +1 -0
- package/dist/two-d/ui/TextModel.js +126 -0
- package/dist/two-d/ui/TextModel.js.map +1 -0
- package/dist/two-d/ui/TreeView.d.ts +68 -0
- package/dist/two-d/ui/TreeView.d.ts.map +1 -0
- package/dist/two-d/ui/TreeView.js +133 -0
- package/dist/two-d/ui/TreeView.js.map +1 -0
- package/dist/two-d/ui/Window.d.ts +13 -0
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +23 -0
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/WindowStack.d.ts.map +1 -1
- package/dist/two-d/ui/WindowStack.js +2 -0
- package/dist/two-d/ui/WindowStack.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +27 -3
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +14 -2
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/two-d/ui/markup.d.ts +127 -0
- package/dist/two-d/ui/markup.d.ts.map +1 -0
- package/dist/two-d/ui/markup.js +322 -0
- package/dist/two-d/ui/markup.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world/SideTurns.d.ts +115 -0
- package/dist/world/SideTurns.d.ts.map +1 -0
- package/dist/world/SideTurns.js +138 -0
- package/dist/world/SideTurns.js.map +1 -0
- package/dist/world/index.d.ts +2 -0
- package/dist/world/index.d.ts.map +1 -1
- package/dist/world/index.js +1 -0
- package/dist/world/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,32 +1,88 @@
|
|
|
1
1
|
import { Container } from 'pixi.js';
|
|
2
2
|
import { FloatingText } from './FloatingText.js';
|
|
3
|
+
/** The vertical gap left between two stacked lines - Java's `FloatingText.push()` uses `4`. */
|
|
4
|
+
export const FLOATING_TEXT_STACK_GAP = 4;
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
+
* The age a lifted line is forced to, in seconds, from how many lines sit below it - Java's
|
|
7
|
+
* `LIFESPAN - numBelow / 5f` read as a floor under the age rather than a cap on what is left, the
|
|
8
|
+
* same rule `FloatingText.ageAtLeast` applies. One second per five lines, a fifth of a second each,
|
|
9
|
+
* so a burst of numbers cannot become a column that outlives the fight that produced it.
|
|
10
|
+
*/
|
|
11
|
+
export function floatingTextStackLifePenalty(linesBelow) {
|
|
12
|
+
return Math.max(0, linesBelow) / 5;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The `y` `older` must sit at to clear `below`, or `older.y` when it already does.
|
|
16
|
+
*
|
|
17
|
+
* The direction is the point, and it is why this returns a *position* rather than an offset to add:
|
|
18
|
+
* Java's `FloatingText.push()` anchors the newcomer on the target and lifts the lines already there
|
|
19
|
+
* (`above.setPos(above.left(), below.top() - above.height() - 4)`), the opposite of moving the
|
|
20
|
+
* newcomer out of the way. Pop-ups rise, so the older ones belong above the newer ones - an offset
|
|
21
|
+
* the caller adds is direction-ambiguous, a position the caller assigns is not.
|
|
22
|
+
*
|
|
23
|
+
* Both boxes are anchored at their centre, so a line's top is `y - height / 2`; a line is clear when
|
|
24
|
+
* its bottom plus the gap no longer reaches the other's top.
|
|
6
25
|
*
|
|
7
26
|
* Exported and pure on purpose: the stacking rule is arithmetic, and this way it is checked as
|
|
8
27
|
* arithmetic rather than through a rendered label (`Label` needs a DOM to measure text).
|
|
9
|
-
|
|
10
|
-
|
|
28
|
+
*/
|
|
29
|
+
export function floatingTextStackLift(older, below, gap = FLOATING_TEXT_STACK_GAP) {
|
|
30
|
+
const belowTop = below.y - below.height / 2;
|
|
31
|
+
if (older.y + older.height / 2 + gap <= belowTop)
|
|
32
|
+
return older.y;
|
|
33
|
+
return belowTop - older.height / 2 - gap;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The whole stacking policy as arithmetic: which of `live` (oldest first) share the newcomer's key,
|
|
37
|
+
* where each of them ends up, and what age each is forced to.
|
|
38
|
+
*
|
|
39
|
+
* Newest first, stopping at the first line already clear of the one below it: a stack is ordered,
|
|
40
|
+
* an older line sits above a newer one, so a line that needs no move means nothing above it does
|
|
41
|
+
* either. That is Java's `break`, and the reason the walk runs backwards over `live`.
|
|
42
|
+
*
|
|
43
|
+
* Pure and exported for the same reason `floatingTextStackLift` is: applying a plan needs a
|
|
44
|
+
* `FloatingText`, which is a `Label`, which needs a DOM. The decision is therefore checked as
|
|
45
|
+
* arithmetic and the class is left with the assignments. `push` inlined this loop once, and the
|
|
46
|
+
* loop is where the direction, the order and the life floor were all wrong at the same time.
|
|
11
47
|
*
|
|
12
48
|
* @example
|
|
13
49
|
* ```ts
|
|
14
|
-
* import {
|
|
15
|
-
*
|
|
50
|
+
* import {
|
|
51
|
+
* FLOATING_TEXT_STACK_GAP,
|
|
52
|
+
* floatingTextStackLift,
|
|
53
|
+
* floatingTextStackLifePenalty,
|
|
54
|
+
* floatingTextStackMoves,
|
|
55
|
+
* } from '@datamoc/mw_games/two-d/ui';
|
|
56
|
+
*
|
|
57
|
+
* const older = { key: 'hero', x: 0, y: 0, height: 10 };
|
|
58
|
+
* const newcomer = { key: 'hero', x: 0, y: 0, height: 8 };
|
|
59
|
+
*
|
|
60
|
+
* console.log(FLOATING_TEXT_STACK_GAP); // 4, the gap Java's push leaves
|
|
61
|
+
* console.log(floatingTextStackLift(older, newcomer)); // -13, where the older line must sit
|
|
62
|
+
* console.log(floatingTextStackLifePenalty(1)); // 0.2, the age a first lifted line is forced to
|
|
63
|
+
* console.log(floatingTextStackMoves([older], newcomer)); // [{ index: 0, y: -13, ageAtLeast: 0.2 }]
|
|
16
64
|
* ```
|
|
17
65
|
*/
|
|
18
|
-
export function
|
|
66
|
+
export function floatingTextStackMoves(live, incoming, gap = FLOATING_TEXT_STACK_GAP) {
|
|
67
|
+
const moves = [];
|
|
19
68
|
if (incoming.key === undefined)
|
|
20
|
-
return
|
|
21
|
-
let
|
|
22
|
-
for (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (entry.x !== incoming.x || entry.y !== incoming.y)
|
|
69
|
+
return moves;
|
|
70
|
+
let below = incoming;
|
|
71
|
+
for (let index = live.length - 1; index >= 0; index--) {
|
|
72
|
+
const held = live[index];
|
|
73
|
+
if (held.key !== incoming.key)
|
|
26
74
|
continue;
|
|
27
|
-
|
|
75
|
+
const y = floatingTextStackLift(held, below, gap);
|
|
76
|
+
//already clear of the line below, so everything above it is too: Java stops here
|
|
77
|
+
if (y === held.y)
|
|
78
|
+
break;
|
|
79
|
+
moves.push({ index, y, ageAtLeast: floatingTextStackLifePenalty(moves.length + 1) });
|
|
80
|
+
//the next line up is measured against where this one is going, not where it was. The
|
|
81
|
+
//entries handed in are not mutated, so the move is carried forward on a copy: without this
|
|
82
|
+
//every line of a burst lands at the same y.
|
|
83
|
+
below = { ...held, y };
|
|
28
84
|
}
|
|
29
|
-
return
|
|
85
|
+
return moves;
|
|
30
86
|
}
|
|
31
87
|
/**
|
|
32
88
|
* Owns the pop-ups a game spawns over world points: one `push` per number, one `update(dt)` to
|
|
@@ -57,7 +113,14 @@ export class FloatingTextStack extends Container {
|
|
|
57
113
|
super(...arguments);
|
|
58
114
|
this.live = [];
|
|
59
115
|
}
|
|
60
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Spawns a pop-up at `options.x`/`options.y`, lifting the lines already there out of its way.
|
|
118
|
+
*
|
|
119
|
+
* The newcomer stays exactly where it was asked to appear; anything sharing its key that it
|
|
120
|
+
* would overlap moves up, newest first, each one measured against the line below it and each
|
|
121
|
+
* nudge costing it a little life - Java's order, so a stack grows upward and self-limits. The
|
|
122
|
+
* decision is `floatingTextStackMoves`; this method only assigns what that plan says.
|
|
123
|
+
*/
|
|
61
124
|
push(options) {
|
|
62
125
|
const popup = new FloatingText({
|
|
63
126
|
text: options.text,
|
|
@@ -67,9 +130,17 @@ export class FloatingTextStack extends Container {
|
|
|
67
130
|
rise: options.rise,
|
|
68
131
|
hold: options.hold,
|
|
69
132
|
});
|
|
133
|
+
//before the measurement below, never after: `height` includes the pop-up's own scale
|
|
134
|
+
if (options.scale !== undefined)
|
|
135
|
+
popup.scale.set(options.scale);
|
|
70
136
|
const entry = { key: options.key, x: options.x, y: options.y, height: popup.height };
|
|
71
|
-
|
|
72
|
-
|
|
137
|
+
for (const move of floatingTextStackMoves(this.live.map((held) => held.entry), entry)) {
|
|
138
|
+
const held = this.live[move.index];
|
|
139
|
+
held.entry.y = move.y;
|
|
140
|
+
held.popup.y = move.y;
|
|
141
|
+
held.popup.ageAtLeast(move.ageAtLeast);
|
|
142
|
+
}
|
|
143
|
+
popup.position.set(options.x, options.y);
|
|
73
144
|
this.addChild(popup);
|
|
74
145
|
this.live.push({ popup, entry });
|
|
75
146
|
return popup;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FloatingTextStack.js","sourceRoot":"","sources":["../../../src/two-d/ui/FloatingTextStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,YAAY,EAA4B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"FloatingTextStack.js","sourceRoot":"","sources":["../../../src/two-d/ui/FloatingTextStack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export type Anchor = 'top-left' | 'top' | 'top-right' | 'left' | 'center' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right' | 'fill';
|
|
2
|
+
export interface LayoutRect {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
export interface AnchorSpec {
|
|
9
|
+
/** which corner or edge to sit against; `center` when omitted */
|
|
10
|
+
anchor?: Anchor;
|
|
11
|
+
/** extra offset after anchoring, positive right and down */
|
|
12
|
+
offsetX?: number;
|
|
13
|
+
offsetY?: number;
|
|
14
|
+
/** 0..1 position across the bounds, overriding the named anchor's own alignment */
|
|
15
|
+
alignX?: number;
|
|
16
|
+
alignY?: number;
|
|
17
|
+
/** the placed size; the content's own size when omitted */
|
|
18
|
+
width?: number;
|
|
19
|
+
height?: number;
|
|
20
|
+
/** shrinks the bounds on every side before anchoring */
|
|
21
|
+
margin?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The horizontal (0, 0.5, 1) and vertical alignment a named anchor implies.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { anchorAlign } from '@datamoc/mw_games/two-d/ui';
|
|
29
|
+
*
|
|
30
|
+
* console.log(anchorAlign('bottom-right')); // { x: 1, y: 1 }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function anchorAlign(anchor: Anchor): {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Places a `size`-shaped child against the bounds - the anchor half of a data-driven shell, the
|
|
39
|
+
* part GUI2 spells as `[cell]` anchoring a widget inside its slot.
|
|
40
|
+
*
|
|
41
|
+
* A `fill` anchor takes the bounds (minus margins) whole; every other anchor lines the child up on
|
|
42
|
+
* the named edge and then applies the offsets, so `{ anchor: 'bottom-right', offsetX: -8 }` pins a
|
|
43
|
+
* button eight pixels in from the bottom-right corner.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { resolveAnchor } from '@datamoc/mw_games/two-d/ui';
|
|
48
|
+
*
|
|
49
|
+
* const bounds = { x: 0, y: 0, width: 800, height: 600 };
|
|
50
|
+
* console.log(resolveAnchor({ anchor: 'bottom-right', width: 100, height: 30, margin: 10 }, bounds));
|
|
51
|
+
* // { x: 690, y: 560, width: 100, height: 30 }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveAnchor(spec: AnchorSpec, bounds: LayoutRect, size?: {
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
}): LayoutRect;
|
|
58
|
+
export interface GridTrack {
|
|
59
|
+
/** a fixed size, in pixels */
|
|
60
|
+
size?: number;
|
|
61
|
+
/** share of what is left after the fixed tracks; a track with neither is 0 */
|
|
62
|
+
grow?: number;
|
|
63
|
+
}
|
|
64
|
+
export interface GridSpec {
|
|
65
|
+
columns: readonly GridTrack[];
|
|
66
|
+
rows: readonly GridTrack[];
|
|
67
|
+
/** space between tracks, in pixels */
|
|
68
|
+
gap?: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A column/row grid whose tracks are `size` or `grow`, the data-driven counterpart to GUI2's
|
|
72
|
+
* `[grid]`. Fixed tracks take their size; the rest of the space is divided between the `grow`
|
|
73
|
+
* tracks in proportion to their weight, so a sidebar can be `size: 200` and the content
|
|
74
|
+
* `grow: 1` without either side knowing the window's width.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { Grid } from '@datamoc/mw_games/two-d/ui';
|
|
79
|
+
*
|
|
80
|
+
* const grid = new Grid({ columns: [{ size: 200 }, { grow: 1 }], rows: [{ grow: 1 }], gap: 8 });
|
|
81
|
+
* const cell = grid.rect(0, 1, { x: 0, y: 0, width: 800, height: 600 });
|
|
82
|
+
* console.log(cell); // the content column: x 208, width 592
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare class Grid {
|
|
86
|
+
private spec;
|
|
87
|
+
constructor(spec: GridSpec);
|
|
88
|
+
get columns(): readonly GridTrack[];
|
|
89
|
+
get rows(): readonly GridTrack[];
|
|
90
|
+
/** the resolved pixel width of each column for a given total width */
|
|
91
|
+
columnSizes(totalWidth: number): number[];
|
|
92
|
+
/** the resolved pixel height of each row for a given total height */
|
|
93
|
+
rowSizes(totalHeight: number): number[];
|
|
94
|
+
/**
|
|
95
|
+
* The rectangle of a cell, spanning `columnSpan` columns and `rowSpan` rows. Out-of-range cells
|
|
96
|
+
* resolve to a zero-sized rect at the far edge rather than throwing, so a caller walking a
|
|
97
|
+
* changing table never has to bounds-check first.
|
|
98
|
+
*/
|
|
99
|
+
rect(row: number, column: number, bounds: LayoutRect, options?: {
|
|
100
|
+
rowSpan?: number;
|
|
101
|
+
columnSpan?: number;
|
|
102
|
+
}): LayoutRect;
|
|
103
|
+
private get gap();
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=Layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/two-d/ui/Layout.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GACjB,UAAU,GAAG,KAAK,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,cAAc,GAAG,MAAM,CAAC;AAErH,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IAC1B,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAA4B,GAC/D,UAAU,CA6BZ;AAED,MAAM,WAAW,SAAS;IACzB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3B,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,IAAI;IAChB,OAAO,CAAC,IAAI,CAAW;IAEvB,YAAY,IAAI,EAAE,QAAQ,EAEzB;IAED,IAAI,OAAO,IAAI,SAAS,SAAS,EAAE,CAElC;IAED,IAAI,IAAI,IAAI,SAAS,SAAS,EAAE,CAE/B;IAED,sEAAsE;IACtE,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAExC;IAED,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtC;IAED;;;;OAIG;IACH,IAAI,CACH,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,GACrD,UAAU,CAcZ;IAED,OAAO,KAAK,GAAG,GAEd;CACD"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The horizontal (0, 0.5, 1) and vertical alignment a named anchor implies.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { anchorAlign } from '@datamoc/mw_games/two-d/ui';
|
|
7
|
+
*
|
|
8
|
+
* console.log(anchorAlign('bottom-right')); // { x: 1, y: 1 }
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export function anchorAlign(anchor) {
|
|
12
|
+
switch (anchor) {
|
|
13
|
+
case 'top':
|
|
14
|
+
case 'bottom':
|
|
15
|
+
return { x: 0.5, y: anchor === 'top' ? 0 : 1 };
|
|
16
|
+
case 'left':
|
|
17
|
+
case 'right':
|
|
18
|
+
return { x: anchor === 'left' ? 0 : 1, y: 0.5 };
|
|
19
|
+
case 'top-left':
|
|
20
|
+
return { x: 0, y: 0 };
|
|
21
|
+
case 'top-right':
|
|
22
|
+
return { x: 1, y: 0 };
|
|
23
|
+
case 'bottom-left':
|
|
24
|
+
return { x: 0, y: 1 };
|
|
25
|
+
case 'bottom-right':
|
|
26
|
+
return { x: 1, y: 1 };
|
|
27
|
+
case 'fill':
|
|
28
|
+
case 'center':
|
|
29
|
+
default:
|
|
30
|
+
return { x: 0.5, y: 0.5 };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Places a `size`-shaped child against the bounds - the anchor half of a data-driven shell, the
|
|
35
|
+
* part GUI2 spells as `[cell]` anchoring a widget inside its slot.
|
|
36
|
+
*
|
|
37
|
+
* A `fill` anchor takes the bounds (minus margins) whole; every other anchor lines the child up on
|
|
38
|
+
* the named edge and then applies the offsets, so `{ anchor: 'bottom-right', offsetX: -8 }` pins a
|
|
39
|
+
* button eight pixels in from the bottom-right corner.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { resolveAnchor } from '@datamoc/mw_games/two-d/ui';
|
|
44
|
+
*
|
|
45
|
+
* const bounds = { x: 0, y: 0, width: 800, height: 600 };
|
|
46
|
+
* console.log(resolveAnchor({ anchor: 'bottom-right', width: 100, height: 30, margin: 10 }, bounds));
|
|
47
|
+
* // { x: 690, y: 560, width: 100, height: 30 }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function resolveAnchor(spec, bounds, size = { width: 0, height: 0 }) {
|
|
51
|
+
const margin = spec.margin ?? 0;
|
|
52
|
+
const left = bounds.x + margin;
|
|
53
|
+
const top = bounds.y + margin;
|
|
54
|
+
const usableWidth = Math.max(0, bounds.width - margin * 2);
|
|
55
|
+
const usableHeight = Math.max(0, bounds.height - margin * 2);
|
|
56
|
+
if (spec.anchor === 'fill') {
|
|
57
|
+
const x = left + (spec.offsetX ?? 0);
|
|
58
|
+
const y = top + (spec.offsetY ?? 0);
|
|
59
|
+
return {
|
|
60
|
+
x,
|
|
61
|
+
y,
|
|
62
|
+
width: spec.width ?? Math.max(0, left + usableWidth - x),
|
|
63
|
+
height: spec.height ?? Math.max(0, top + usableHeight - y),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const align = anchorAlign(spec.anchor ?? 'center');
|
|
67
|
+
const alignX = spec.alignX ?? align.x;
|
|
68
|
+
const alignY = spec.alignY ?? align.y;
|
|
69
|
+
const width = spec.width ?? size.width;
|
|
70
|
+
const height = spec.height ?? size.height;
|
|
71
|
+
return {
|
|
72
|
+
x: left + alignX * Math.max(0, usableWidth - width) + (spec.offsetX ?? 0),
|
|
73
|
+
y: top + alignY * Math.max(0, usableHeight - height) + (spec.offsetY ?? 0),
|
|
74
|
+
width,
|
|
75
|
+
height,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A column/row grid whose tracks are `size` or `grow`, the data-driven counterpart to GUI2's
|
|
80
|
+
* `[grid]`. Fixed tracks take their size; the rest of the space is divided between the `grow`
|
|
81
|
+
* tracks in proportion to their weight, so a sidebar can be `size: 200` and the content
|
|
82
|
+
* `grow: 1` without either side knowing the window's width.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { Grid } from '@datamoc/mw_games/two-d/ui';
|
|
87
|
+
*
|
|
88
|
+
* const grid = new Grid({ columns: [{ size: 200 }, { grow: 1 }], rows: [{ grow: 1 }], gap: 8 });
|
|
89
|
+
* const cell = grid.rect(0, 1, { x: 0, y: 0, width: 800, height: 600 });
|
|
90
|
+
* console.log(cell); // the content column: x 208, width 592
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export class Grid {
|
|
94
|
+
constructor(spec) {
|
|
95
|
+
this.spec = spec;
|
|
96
|
+
}
|
|
97
|
+
get columns() {
|
|
98
|
+
return this.spec.columns;
|
|
99
|
+
}
|
|
100
|
+
get rows() {
|
|
101
|
+
return this.spec.rows;
|
|
102
|
+
}
|
|
103
|
+
/** the resolved pixel width of each column for a given total width */
|
|
104
|
+
columnSizes(totalWidth) {
|
|
105
|
+
return resolveTracks(this.spec.columns, totalWidth, this.gap);
|
|
106
|
+
}
|
|
107
|
+
/** the resolved pixel height of each row for a given total height */
|
|
108
|
+
rowSizes(totalHeight) {
|
|
109
|
+
return resolveTracks(this.spec.rows, totalHeight, this.gap);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The rectangle of a cell, spanning `columnSpan` columns and `rowSpan` rows. Out-of-range cells
|
|
113
|
+
* resolve to a zero-sized rect at the far edge rather than throwing, so a caller walking a
|
|
114
|
+
* changing table never has to bounds-check first.
|
|
115
|
+
*/
|
|
116
|
+
rect(row, column, bounds, options = {}) {
|
|
117
|
+
const columns = this.columnSizes(bounds.width);
|
|
118
|
+
const rows = this.rowSizes(bounds.height);
|
|
119
|
+
const columnSpan = options.columnSpan ?? 1;
|
|
120
|
+
const rowSpan = options.rowSpan ?? 1;
|
|
121
|
+
const x = bounds.x + offsetOf(columns, column, this.gap);
|
|
122
|
+
const y = bounds.y + offsetOf(rows, row, this.gap);
|
|
123
|
+
return {
|
|
124
|
+
x,
|
|
125
|
+
y,
|
|
126
|
+
width: spanOf(columns, column, columnSpan, this.gap),
|
|
127
|
+
height: spanOf(rows, row, rowSpan, this.gap),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
get gap() {
|
|
131
|
+
return this.spec.gap ?? 0;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function resolveTracks(tracks, total, gap) {
|
|
135
|
+
const gaps = gap * Math.max(0, tracks.length - 1);
|
|
136
|
+
const available = Math.max(0, total - gaps);
|
|
137
|
+
const fixed = tracks.reduce((sum, track) => sum + (track.size ?? 0), 0);
|
|
138
|
+
const growTotal = tracks.reduce((sum, track) => sum + (track.grow ?? 0), 0);
|
|
139
|
+
const remaining = Math.max(0, available - fixed);
|
|
140
|
+
let assigned = 0;
|
|
141
|
+
return tracks.map((track, index) => {
|
|
142
|
+
if (track.size !== undefined)
|
|
143
|
+
return track.size;
|
|
144
|
+
if (!track.grow || growTotal === 0)
|
|
145
|
+
return 0;
|
|
146
|
+
//give the last grow track whatever rounding is left, so the tracks always fill the space
|
|
147
|
+
const isLast = index === lastGrowIndex(tracks);
|
|
148
|
+
const size = isLast ? remaining - assigned : Math.floor((remaining * track.grow) / growTotal);
|
|
149
|
+
assigned += size;
|
|
150
|
+
return size;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function lastGrowIndex(tracks) {
|
|
154
|
+
for (let index = tracks.length - 1; index >= 0; index--)
|
|
155
|
+
if (tracks[index].grow)
|
|
156
|
+
return index;
|
|
157
|
+
return -1;
|
|
158
|
+
}
|
|
159
|
+
function offsetOf(sizes, index, gap) {
|
|
160
|
+
let offset = 0;
|
|
161
|
+
for (let i = 0; i < index && i < sizes.length; i++)
|
|
162
|
+
offset += sizes[i] + gap;
|
|
163
|
+
return offset;
|
|
164
|
+
}
|
|
165
|
+
function spanOf(sizes, index, span, gap) {
|
|
166
|
+
let width = 0;
|
|
167
|
+
for (let i = index; i < index + span && i < sizes.length; i++) {
|
|
168
|
+
width += sizes[i];
|
|
169
|
+
if (i > index)
|
|
170
|
+
width += gap;
|
|
171
|
+
}
|
|
172
|
+
return Math.max(0, width);
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=Layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layout.js","sourceRoot":"","sources":["../../../src/two-d/ui/Layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Container } from 'pixi.js';
|
|
2
|
+
import { Signal } from '../../core/Signal.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Clamps a scroll offset so the content never pulls away from the top or leaves a gap past the
|
|
5
|
+
* bottom. When the content is shorter than the viewport the only offset is 0.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { scrollOffset } from '@datamoc/mw_games/two-d/ui';
|
|
10
|
+
*
|
|
11
|
+
* console.log(scrollOffset(-5, 500, 200)); // 0
|
|
12
|
+
* console.log(scrollOffset(900, 500, 200)); // 300
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function scrollOffset(offset: number, contentSize: number, viewportSize: number): number;
|
|
16
|
+
export interface ScrollBoxOptions {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
/** how tall the content is; set it again with `setContentHeight` as the content changes */
|
|
20
|
+
contentHeight?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A clipped viewport with a scrollbar: put rows in `content` and they scroll inside the box,
|
|
25
|
+
* everything outside it hidden by a mask. The scrollbar is drawn from the theme; the wheel,
|
|
26
|
+
* `scrollBy` and `scrollIntoView` all run through `scrollOffset`, so they clamp the same way.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { ScrollBox } from '@datamoc/mw_games/two-d/ui';
|
|
31
|
+
*
|
|
32
|
+
* const box = new ScrollBox({ width: 200, height: 120, contentHeight: 400 });
|
|
33
|
+
* box.scrollBy(60);
|
|
34
|
+
* console.log(box.offset); // 60
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class ScrollBox extends Container {
|
|
38
|
+
readonly onChange: Signal<number>;
|
|
39
|
+
/** add the rows to scroll here */
|
|
40
|
+
readonly content: Container<import("pixi.js").ContainerChild>;
|
|
41
|
+
private maskShape;
|
|
42
|
+
private track;
|
|
43
|
+
private thumb;
|
|
44
|
+
private width_;
|
|
45
|
+
private height_;
|
|
46
|
+
private contentHeight_;
|
|
47
|
+
private offset_;
|
|
48
|
+
private readonly themeListener;
|
|
49
|
+
constructor(options: ScrollBoxOptions);
|
|
50
|
+
get offset(): number;
|
|
51
|
+
get contentHeight(): number;
|
|
52
|
+
get viewportHeight(): number;
|
|
53
|
+
/** how far the content can scroll; 0 when it all fits */
|
|
54
|
+
get maxOffset(): number;
|
|
55
|
+
get scrollable(): boolean;
|
|
56
|
+
setContentHeight(height: number): void;
|
|
57
|
+
resize(width: number, height: number): void;
|
|
58
|
+
scrollBy(delta: number): void;
|
|
59
|
+
scrollTo(offset: number): void;
|
|
60
|
+
/** scrolls just enough to bring a `height`-tall item at `top` fully into view */
|
|
61
|
+
scrollIntoView(top: number, height: number): void;
|
|
62
|
+
private setOffset;
|
|
63
|
+
private readonly handleWheel;
|
|
64
|
+
private draw;
|
|
65
|
+
destroy(options?: Parameters<Container['destroy']>[0]): void;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=ScrollBox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScrollBox.d.ts","sourceRoot":"","sources":["../../../src/two-d/ui/ScrollBox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAG9C;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9F;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAU,SAAQ,SAAS;IACvC,QAAQ,CAAC,QAAQ,iBAAwB;IAEzC,kCAAkC;IAClC,QAAQ,CAAC,OAAO,8CAAmB;IAEnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,KAAK,CAAkB;IAE/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IAEnD,YAAY,OAAO,EAAE,gBAAgB,EAoBpC;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,yDAAyD;IACzD,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGrC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI1C;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5B;IAED,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,iFAAiF;IACjF,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGhD;IAED,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAE1B;IAEF,OAAO,CAAC,IAAI;IAmBH,OAAO,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAGpE;CACD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Container, Graphics } from 'pixi.js';
|
|
2
|
+
import { Signal } from '../../core/Signal.js';
|
|
3
|
+
import { theme, themeChanged } from './theme.js';
|
|
4
|
+
/**
|
|
5
|
+
* Clamps a scroll offset so the content never pulls away from the top or leaves a gap past the
|
|
6
|
+
* bottom. When the content is shorter than the viewport the only offset is 0.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { scrollOffset } from '@datamoc/mw_games/two-d/ui';
|
|
11
|
+
*
|
|
12
|
+
* console.log(scrollOffset(-5, 500, 200)); // 0
|
|
13
|
+
* console.log(scrollOffset(900, 500, 200)); // 300
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function scrollOffset(offset, contentSize, viewportSize) {
|
|
17
|
+
return Math.max(0, Math.min(Math.max(0, contentSize - viewportSize), offset));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A clipped viewport with a scrollbar: put rows in `content` and they scroll inside the box,
|
|
21
|
+
* everything outside it hidden by a mask. The scrollbar is drawn from the theme; the wheel,
|
|
22
|
+
* `scrollBy` and `scrollIntoView` all run through `scrollOffset`, so they clamp the same way.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { ScrollBox } from '@datamoc/mw_games/two-d/ui';
|
|
27
|
+
*
|
|
28
|
+
* const box = new ScrollBox({ width: 200, height: 120, contentHeight: 400 });
|
|
29
|
+
* box.scrollBy(60);
|
|
30
|
+
* console.log(box.offset); // 60
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class ScrollBox extends Container {
|
|
34
|
+
constructor(options) {
|
|
35
|
+
super();
|
|
36
|
+
this.onChange = new Signal();
|
|
37
|
+
/** add the rows to scroll here */
|
|
38
|
+
this.content = new Container();
|
|
39
|
+
this.maskShape = new Graphics();
|
|
40
|
+
this.track = new Graphics();
|
|
41
|
+
this.thumb = new Graphics();
|
|
42
|
+
this.themeListener = () => this.draw();
|
|
43
|
+
this.handleWheel = (event) => {
|
|
44
|
+
this.scrollBy(event.deltaY);
|
|
45
|
+
};
|
|
46
|
+
this.width_ = options.width;
|
|
47
|
+
this.height_ = options.height;
|
|
48
|
+
this.contentHeight_ = Math.max(0, options.contentHeight ?? 0);
|
|
49
|
+
this.offset_ = scrollOffset(options.offset ?? 0, this.contentHeight_, this.height_);
|
|
50
|
+
this.addChild(this.content);
|
|
51
|
+
this.addChild(this.maskShape);
|
|
52
|
+
this.addChild(this.track);
|
|
53
|
+
this.addChild(this.thumb);
|
|
54
|
+
this.content.mask = this.maskShape;
|
|
55
|
+
this.eventMode = 'static';
|
|
56
|
+
this.on('wheel', this.handleWheel);
|
|
57
|
+
this.draw();
|
|
58
|
+
themeChanged.add(this.themeListener);
|
|
59
|
+
}
|
|
60
|
+
get offset() {
|
|
61
|
+
return this.offset_;
|
|
62
|
+
}
|
|
63
|
+
get contentHeight() {
|
|
64
|
+
return this.contentHeight_;
|
|
65
|
+
}
|
|
66
|
+
get viewportHeight() {
|
|
67
|
+
return this.height_;
|
|
68
|
+
}
|
|
69
|
+
/** how far the content can scroll; 0 when it all fits */
|
|
70
|
+
get maxOffset() {
|
|
71
|
+
return Math.max(0, this.contentHeight_ - this.height_);
|
|
72
|
+
}
|
|
73
|
+
get scrollable() {
|
|
74
|
+
return this.maxOffset > 0;
|
|
75
|
+
}
|
|
76
|
+
setContentHeight(height) {
|
|
77
|
+
this.contentHeight_ = Math.max(0, height);
|
|
78
|
+
this.setOffset(scrollOffset(this.offset_, this.contentHeight_, this.height_));
|
|
79
|
+
}
|
|
80
|
+
resize(width, height) {
|
|
81
|
+
this.width_ = width;
|
|
82
|
+
this.height_ = height;
|
|
83
|
+
this.setOffset(scrollOffset(this.offset_, this.contentHeight_, this.height_));
|
|
84
|
+
}
|
|
85
|
+
scrollBy(delta) {
|
|
86
|
+
this.setOffset(scrollOffset(this.offset_ + delta, this.contentHeight_, this.height_));
|
|
87
|
+
}
|
|
88
|
+
scrollTo(offset) {
|
|
89
|
+
this.setOffset(scrollOffset(offset, this.contentHeight_, this.height_));
|
|
90
|
+
}
|
|
91
|
+
/** scrolls just enough to bring a `height`-tall item at `top` fully into view */
|
|
92
|
+
scrollIntoView(top, height) {
|
|
93
|
+
if (top < this.offset_)
|
|
94
|
+
this.scrollTo(top);
|
|
95
|
+
else if (top + height > this.offset_ + this.height_)
|
|
96
|
+
this.scrollTo(top + height - this.height_);
|
|
97
|
+
}
|
|
98
|
+
setOffset(offset) {
|
|
99
|
+
if (offset === this.offset_) {
|
|
100
|
+
this.draw();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.offset_ = offset;
|
|
104
|
+
this.draw();
|
|
105
|
+
this.onChange.dispatch(offset);
|
|
106
|
+
}
|
|
107
|
+
draw() {
|
|
108
|
+
const t = theme();
|
|
109
|
+
this.content.y = -this.offset_;
|
|
110
|
+
this.maskShape.clear().rect(0, 0, this.width_, this.height_).fill({ color: 0xffffff });
|
|
111
|
+
this.track.clear();
|
|
112
|
+
this.thumb.clear();
|
|
113
|
+
if (!this.scrollable)
|
|
114
|
+
return;
|
|
115
|
+
const barWidth = 6;
|
|
116
|
+
const x = this.width_ - barWidth;
|
|
117
|
+
this.track.roundRect(x, 0, barWidth, this.height_, barWidth / 2).fill({ color: t.color.panelFill });
|
|
118
|
+
const thumbHeight = Math.max(barWidth, (this.height_ / this.contentHeight_) * this.height_);
|
|
119
|
+
const thumbY = (this.offset_ / this.maxOffset) * (this.height_ - thumbHeight);
|
|
120
|
+
this.thumb.roundRect(x, thumbY, barWidth, thumbHeight, barWidth / 2).fill({ color: t.color.textHighlight });
|
|
121
|
+
}
|
|
122
|
+
destroy(options) {
|
|
123
|
+
themeChanged.remove(this.themeListener);
|
|
124
|
+
super.destroy(options);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=ScrollBox.js.map
|