@typecad/ui 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +1443 -0
- package/dist/engine-index.d.ts +7 -0
- package/dist/engine-index.js +41 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +42 -0
- package/dist/preview/build-program.d.ts +7 -0
- package/dist/preview/build-program.js +665 -0
- package/dist/preview/host-gfx.d.ts +90 -0
- package/dist/preview/host-gfx.js +552 -0
- package/dist/preview/host-ui-runtime.d.ts +241 -0
- package/dist/preview/host-ui-runtime.js +3236 -0
- package/dist/preview/types.d.ts +83 -0
- package/dist/preview/types.js +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.js +10 -0
- package/dist/ui-engine/block-layout.d.ts +8 -0
- package/dist/ui-engine/block-layout.js +95 -0
- package/dist/ui-engine/color.d.ts +49 -0
- package/dist/ui-engine/color.js +273 -0
- package/dist/ui-engine/css-parser.d.ts +155 -0
- package/dist/ui-engine/css-parser.js +1058 -0
- package/dist/ui-engine/default-keyboards.d.ts +3 -0
- package/dist/ui-engine/default-keyboards.js +53 -0
- package/dist/ui-engine/easing.d.ts +12 -0
- package/dist/ui-engine/easing.js +76 -0
- package/dist/ui-engine/font-assets.d.ts +50 -0
- package/dist/ui-engine/font-assets.js +431 -0
- package/dist/ui-engine/html-parser.d.ts +116 -0
- package/dist/ui-engine/html-parser.js +284 -0
- package/dist/ui-engine/image-assets.d.ts +21 -0
- package/dist/ui-engine/image-assets.js +125 -0
- package/dist/ui-engine/inline-parser.d.ts +30 -0
- package/dist/ui-engine/inline-parser.js +66 -0
- package/dist/ui-engine/keyframes.d.ts +3 -0
- package/dist/ui-engine/keyframes.js +39 -0
- package/dist/ui-engine/layout-engine.d.ts +35 -0
- package/dist/ui-engine/layout-engine.js +203 -0
- package/dist/ui-engine/model.d.ts +239 -0
- package/dist/ui-engine/model.js +987 -0
- package/dist/ui-engine/rich-layout.d.ts +51 -0
- package/dist/ui-engine/rich-layout.js +220 -0
- package/dist/ui-engine/run-types.d.ts +13 -0
- package/dist/ui-engine/run-types.js +1 -0
- package/dist/ui-engine/runtime-header/antialiasing.d.ts +1 -0
- package/dist/ui-engine/runtime-header/antialiasing.js +141 -0
- package/dist/ui-engine/runtime-header/blend-bodies.d.ts +1 -0
- package/dist/ui-engine/runtime-header/blend-bodies.js +39 -0
- package/dist/ui-engine/runtime-header/canvas-helpers.d.ts +1 -0
- package/dist/ui-engine/runtime-header/canvas-helpers.js +155 -0
- package/dist/ui-engine/runtime-header/canvas-scrollbar.d.ts +1 -0
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +105 -0
- package/dist/ui-engine/runtime-header/color-mono-refresh.d.ts +1 -0
- package/dist/ui-engine/runtime-header/color-mono-refresh.js +154 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.d.ts +1 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +169 -0
- package/dist/ui-engine/runtime-header/display-shim.d.ts +1 -0
- package/dist/ui-engine/runtime-header/display-shim.js +104 -0
- package/dist/ui-engine/runtime-header/forward-decls.d.ts +1 -0
- package/dist/ui-engine/runtime-header/forward-decls.js +137 -0
- package/dist/ui-engine/runtime-header/guard-close.d.ts +1 -0
- package/dist/ui-engine/runtime-header/guard-close.js +8 -0
- package/dist/ui-engine/runtime-header/image-drawing.d.ts +1 -0
- package/dist/ui-engine/runtime-header/image-drawing.js +300 -0
- package/dist/ui-engine/runtime-header/init-press-input.d.ts +1 -0
- package/dist/ui-engine/runtime-header/init-press-input.js +126 -0
- package/dist/ui-engine/runtime-header/keyboard.d.ts +1 -0
- package/dist/ui-engine/runtime-header/keyboard.js +342 -0
- package/dist/ui-engine/runtime-header/node-decoration.d.ts +1 -0
- package/dist/ui-engine/runtime-header/node-decoration.js +212 -0
- package/dist/ui-engine/runtime-header/paint-order-coords.d.ts +1 -0
- package/dist/ui-engine/runtime-header/paint-order-coords.js +190 -0
- package/dist/ui-engine/runtime-header/paint-rects-repair.d.ts +1 -0
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +553 -0
- package/dist/ui-engine/runtime-header/scroll-physics.d.ts +1 -0
- package/dist/ui-engine/runtime-header/scroll-physics.js +161 -0
- package/dist/ui-engine/runtime-header/state-bindings-nav.d.ts +1 -0
- package/dist/ui-engine/runtime-header/state-bindings-nav.js +176 -0
- package/dist/ui-engine/runtime-header/structs.d.ts +1 -0
- package/dist/ui-engine/runtime-header/structs.js +220 -0
- package/dist/ui-engine/runtime-header/text-rendering.d.ts +1 -0
- package/dist/ui-engine/runtime-header/text-rendering.js +715 -0
- package/dist/ui-engine/runtime-header/tick/bindings-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +103 -0
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +1051 -0
- package/dist/ui-engine/runtime-header/tick/flush-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/flush-phase.js +17 -0
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +15 -0
- package/dist/ui-engine/runtime-header/tick/transitions-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/transitions-phase.js +213 -0
- package/dist/ui-engine/runtime-header/tick-motion-helpers.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick-motion-helpers.js +26 -0
- package/dist/ui-engine/runtime-header/tick.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick.js +17 -0
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.d.ts +1 -0
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +406 -0
- package/dist/ui-engine/runtime-header/types-defines.d.ts +1 -0
- package/dist/ui-engine/runtime-header/types-defines.js +79 -0
- package/dist/ui-engine/runtime-header.d.ts +1 -0
- package/dist/ui-engine/runtime-header.js +69 -0
- package/dist/ui-engine/scroll-memory-diagnostics.d.ts +16 -0
- package/dist/ui-engine/scroll-memory-diagnostics.js +58 -0
- package/dist/ui-engine/select-engine.d.ts +4 -0
- package/dist/ui-engine/select-engine.js +19 -0
- package/dist/ui-engine/style-resolver.d.ts +77 -0
- package/dist/ui-engine/style-resolver.js +341 -0
- package/dist/ui-engine/text-layout.d.ts +19 -0
- package/dist/ui-engine/text-layout.js +92 -0
- package/dist/ui-engine/transpile-ui.d.ts +11 -0
- package/dist/ui-engine/transpile-ui.js +33 -0
- package/dist/ui-engine/ua-stylesheet.d.ts +3 -0
- package/dist/ui-engine/ua-stylesheet.js +56 -0
- package/dist/ui-engine/ui-file-splitter.d.ts +14 -0
- package/dist/ui-engine/ui-file-splitter.js +47 -0
- package/dist/ui-engine/ui-lowering.d.ts +33 -0
- package/dist/ui-engine/ui-lowering.js +390 -0
- package/dist/ui-engine/ui-registry.d.ts +81 -0
- package/dist/ui-engine/ui-registry.js +429 -0
- package/dist/ui-engine/yoga-layout.d.ts +12 -0
- package/dist/ui-engine/yoga-layout.js +314 -0
- package/package.json +86 -0
- package/src/engine-index.ts +51 -0
- package/src/index.ts +232 -0
- package/src/preview/build-program.ts +734 -0
- package/src/preview/host-gfx.ts +580 -0
- package/src/preview/host-ui-runtime.ts +3245 -0
- package/src/preview/types.ts +96 -0
- package/src/types.ts +127 -0
- package/src/ui-engine/block-layout.ts +104 -0
- package/src/ui-engine/color.ts +263 -0
- package/src/ui-engine/css-parser.ts +1003 -0
- package/src/ui-engine/default-keyboards.ts +58 -0
- package/src/ui-engine/easing.ts +53 -0
- package/src/ui-engine/font-assets.ts +512 -0
- package/src/ui-engine/html-parser.ts +397 -0
- package/src/ui-engine/image-assets.ts +170 -0
- package/src/ui-engine/inline-parser.ts +78 -0
- package/src/ui-engine/keyframes.ts +47 -0
- package/src/ui-engine/layout-engine.ts +228 -0
- package/src/ui-engine/model.ts +1225 -0
- package/src/ui-engine/rich-layout.ts +257 -0
- package/src/ui-engine/run-types.ts +14 -0
- package/src/ui-engine/runtime-header/antialiasing.ts +141 -0
- package/src/ui-engine/runtime-header/blend-bodies.ts +39 -0
- package/src/ui-engine/runtime-header/canvas-helpers.ts +155 -0
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +105 -0
- package/src/ui-engine/runtime-header/color-mono-refresh.ts +154 -0
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +169 -0
- package/src/ui-engine/runtime-header/display-shim.ts +104 -0
- package/src/ui-engine/runtime-header/forward-decls.ts +137 -0
- package/src/ui-engine/runtime-header/guard-close.ts +8 -0
- package/src/ui-engine/runtime-header/image-drawing.ts +300 -0
- package/src/ui-engine/runtime-header/init-press-input.ts +126 -0
- package/src/ui-engine/runtime-header/keyboard.ts +342 -0
- package/src/ui-engine/runtime-header/node-decoration.ts +212 -0
- package/src/ui-engine/runtime-header/paint-order-coords.ts +190 -0
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +553 -0
- package/src/ui-engine/runtime-header/scroll-physics.ts +161 -0
- package/src/ui-engine/runtime-header/state-bindings-nav.ts +176 -0
- package/src/ui-engine/runtime-header/structs.ts +220 -0
- package/src/ui-engine/runtime-header/text-rendering.ts +715 -0
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +103 -0
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +1051 -0
- package/src/ui-engine/runtime-header/tick/flush-phase.ts +17 -0
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +15 -0
- package/src/ui-engine/runtime-header/tick/transitions-phase.ts +213 -0
- package/src/ui-engine/runtime-header/tick-motion-helpers.ts +26 -0
- package/src/ui-engine/runtime-header/tick.ts +18 -0
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +406 -0
- package/src/ui-engine/runtime-header/types-defines.ts +79 -0
- package/src/ui-engine/runtime-header.ts +71 -0
- package/src/ui-engine/scroll-memory-diagnostics.ts +79 -0
- package/src/ui-engine/select-engine.ts +21 -0
- package/src/ui-engine/style-resolver.ts +416 -0
- package/src/ui-engine/text-layout.ts +114 -0
- package/src/ui-engine/transpile-ui.ts +45 -0
- package/src/ui-engine/ua-stylesheet.ts +60 -0
- package/src/ui-engine/ui-file-splitter.ts +59 -0
- package/src/ui-engine/ui-lowering.ts +459 -0
- package/src/ui-engine/ui-registry.ts +530 -0
- package/src/ui-engine/yoga-layout.ts +309 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 1000-1154).
|
|
2
|
+
// Scroll input + physics layer, framebuffer get/push.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitScrollPhysics(): string {
|
|
5
|
+
return `
|
|
6
|
+
// ── Scroll engine: Input layer ───────────────────────────────────────────────
|
|
7
|
+
// raw touch sample → smoothed delta (dy). Capacitive: passthrough 1:1.
|
|
8
|
+
// Resistive: deadband suppresses sub-N-px jitter (steady drag still 1:1).
|
|
9
|
+
// 'none' tier compiles drag scroll out entirely.
|
|
10
|
+
static int16_t __ui_scroll_prev_dy = 0; // last smoothed delta (low-pass state)
|
|
11
|
+
|
|
12
|
+
static inline int16_t ui_scroll_scale_dy(int16_t dy) {
|
|
13
|
+
int32_t scaled = (int32_t)dy * (int32_t)UI_SCROLL_DRAG_SCALE_X10;
|
|
14
|
+
return (int16_t)(scaled >= 0 ? (scaled + 5) / 10 : (scaled - 5) / 10);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static inline int16_t ui_scroll_smooth_dy(int16_t dy) {
|
|
18
|
+
#if UI_SCROLL_INPUT_TIER_CAPACITIVE
|
|
19
|
+
__ui_scroll_prev_dy = dy;
|
|
20
|
+
return ui_scroll_scale_dy(dy);
|
|
21
|
+
#elif UI_SCROLL_INPUT_TIER_RESISTIVE
|
|
22
|
+
int16_t db = (int16_t)UI_SCROLL_DEADBAND_PX;
|
|
23
|
+
if (dy >= -db && dy <= db) {
|
|
24
|
+
// Deadband: kill per-sample jitter around zero. Steady drag (|dy|>db) below
|
|
25
|
+
// passes through unchanged, so steady-state is 1:1 (spec Q1).
|
|
26
|
+
__ui_scroll_prev_dy = 0;
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
__ui_scroll_prev_dy = dy;
|
|
30
|
+
return ui_scroll_scale_dy(dy);
|
|
31
|
+
#else
|
|
32
|
+
(void)dy;
|
|
33
|
+
return 0;
|
|
34
|
+
#endif
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Scroll engine: Physics layer ─────────────────────────────────────────────
|
|
38
|
+
// 1:1 in bounds; rubber-band at edges; bounce-back/snap on release. No fling.
|
|
39
|
+
// On constrained render tiers (no elastic), overscroll is hard-clamped away.
|
|
40
|
+
|
|
41
|
+
static inline int16_t ui_scroll_max(int16_t node) {
|
|
42
|
+
if (node < 0) return 0;
|
|
43
|
+
int16_t m = __ui_nodes[node].contentHeight - __ui_nodes[node].box.h;
|
|
44
|
+
return m < 0 ? 0 : m;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Rubber-band excursion for d cumulative pixels dragged past a boundary.
|
|
48
|
+
// r = maxOverscroll * d / (d + stiffness). stiffness held as X10 fixed-point.
|
|
49
|
+
static inline int16_t ui_scroll_overscroll_for(int16_t d) {
|
|
50
|
+
if (d <= 0) return 0;
|
|
51
|
+
int16_t maxOv = (int16_t)UI_SCROLL_MAX_OVERSCROLL;
|
|
52
|
+
int16_t stiffX10 = (int16_t)UI_SCROLL_STIFFNESS_X10;
|
|
53
|
+
if (stiffX10 <= 0) stiffX10 = 1;
|
|
54
|
+
int32_t r = ((int32_t)maxOv * (int32_t)d) / ((int32_t)d + (int32_t)stiffX10);
|
|
55
|
+
return r > maxOv ? maxOv : (int16_t)r;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Apply a smoothed drag delta to the owning scroll node. Returns 1 if the view
|
|
59
|
+
// changed (needs redraw). Sets overscrollPx for rubber-band excursions; scrollY
|
|
60
|
+
// itself never leaves [0, maxScroll] so the committed position stays valid.
|
|
61
|
+
static inline uint8_t ui_apply_scroll_delta(int16_t node, int16_t dy) {
|
|
62
|
+
if (node < 0 || dy == 0) return 0;
|
|
63
|
+
int16_t sy = __ui_nodes[node].scrollY;
|
|
64
|
+
int16_t maxS = ui_scroll_max(node);
|
|
65
|
+
int16_t nextY = sy - dy;
|
|
66
|
+
int16_t prevOv = __ui_nodes[node].overscrollPx;
|
|
67
|
+
int16_t nextOv = prevOv;
|
|
68
|
+
if (nextY < 0) {
|
|
69
|
+
__ui_nodes[node].scrollY = 0;
|
|
70
|
+
// Cumulative drag past the top boundary since crossing it.
|
|
71
|
+
int16_t draggedPast = dy - sy; // how far past 0 this delta pushed
|
|
72
|
+
int16_t cum = prevOv + draggedPast;
|
|
73
|
+
if (cum < 0) cum = 0;
|
|
74
|
+
#if UI_SCROLL_ELASTIC
|
|
75
|
+
nextOv = ui_scroll_overscroll_for(cum);
|
|
76
|
+
#else
|
|
77
|
+
nextOv = 0;
|
|
78
|
+
#endif
|
|
79
|
+
} else if (nextY > maxS) {
|
|
80
|
+
__ui_nodes[node].scrollY = maxS;
|
|
81
|
+
int16_t draggedPast = nextY - maxS;
|
|
82
|
+
int16_t cum = (prevOv < 0 ? -prevOv : 0) + draggedPast; // prevOv<0 = bottom
|
|
83
|
+
if (cum < 0) cum = 0;
|
|
84
|
+
#if UI_SCROLL_ELASTIC
|
|
85
|
+
nextOv = -ui_scroll_overscroll_for(cum); // negative = bottom overshoot
|
|
86
|
+
#else
|
|
87
|
+
nextOv = 0;
|
|
88
|
+
#endif
|
|
89
|
+
} else {
|
|
90
|
+
__ui_nodes[node].scrollY = nextY;
|
|
91
|
+
nextOv = 0; // returned in-bounds → reset
|
|
92
|
+
}
|
|
93
|
+
__ui_nodes[node].overscrollPx = nextOv;
|
|
94
|
+
uint8_t changed = (__ui_nodes[node].scrollY != sy) || (nextOv != prevOv);
|
|
95
|
+
if (changed) ui_mark_scroll_view_dirty((uint16_t)node);
|
|
96
|
+
return changed;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// On release: arm a bounded settle animation — bounce overscroll back to 0, or
|
|
100
|
+
// edge-snap scrollY within edgeSnapPx. The animation runs in ui_tick.
|
|
101
|
+
static inline uint8_t ui_scroll_release(int16_t node) {
|
|
102
|
+
if (node < 0) return 0;
|
|
103
|
+
uint8_t changed = 0;
|
|
104
|
+
if (__ui_nodes[node].overscrollPx != 0) {
|
|
105
|
+
__ui_nodes[node].settling = 1;
|
|
106
|
+
__ui_settle_from_overscroll = __ui_nodes[node].overscrollPx;
|
|
107
|
+
__ui_settle_from_scrollY = 0;
|
|
108
|
+
__ui_settle_start_ms = millis();
|
|
109
|
+
changed = 1;
|
|
110
|
+
} else {
|
|
111
|
+
int16_t sy = __ui_nodes[node].scrollY;
|
|
112
|
+
int16_t maxS = ui_scroll_max(node);
|
|
113
|
+
int16_t snap = (int16_t)UI_SCROLL_EDGE_SNAP_PX;
|
|
114
|
+
if (sy > 0 && sy <= snap) {
|
|
115
|
+
__ui_nodes[node].settling = 1;
|
|
116
|
+
__ui_settle_from_scrollY = sy; // positive → snap toward 0
|
|
117
|
+
__ui_settle_from_overscroll = 0;
|
|
118
|
+
__ui_settle_start_ms = millis();
|
|
119
|
+
changed = 1;
|
|
120
|
+
} else if (maxS > 0 && sy < maxS && sy >= maxS - snap) {
|
|
121
|
+
__ui_nodes[node].settling = 1;
|
|
122
|
+
__ui_settle_from_scrollY = sy - maxS; // negative → snap toward max
|
|
123
|
+
__ui_settle_from_overscroll = 0;
|
|
124
|
+
__ui_settle_start_ms = millis();
|
|
125
|
+
changed = 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return changed;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Advance the settle animation for a node (called from ui_tick). Ease-out over
|
|
132
|
+
// UI_SCROLL_SETTLE_MS, terminating at the boundary. Bounded — always ends.
|
|
133
|
+
static inline void ui_scroll_advance_settle(uint8_t node, uint16_t deltaMs) {
|
|
134
|
+
(void)deltaMs;
|
|
135
|
+
if (node >= __ui_node_count || !__ui_nodes[node].settling) return;
|
|
136
|
+
uint32_t elapsed = millis() - __ui_settle_start_ms;
|
|
137
|
+
uint16_t dur = (uint16_t)UI_SCROLL_SETTLE_MS;
|
|
138
|
+
// ease-out: k = 1 - (1 - t)^2, t in [0,1]
|
|
139
|
+
uint32_t t = elapsed >= dur ? 100 : (elapsed * 100) / dur;
|
|
140
|
+
uint32_t k = 100 - ((100 - t) * (100 - t)) / 100;
|
|
141
|
+
if (__ui_nodes[node].overscrollPx != 0) {
|
|
142
|
+
int16_t from = __ui_settle_from_overscroll;
|
|
143
|
+
__ui_nodes[node].overscrollPx = (int16_t)(from - (int32_t)(from * k) / 100);
|
|
144
|
+
if (t >= 100) __ui_nodes[node].overscrollPx = 0;
|
|
145
|
+
} else if (__ui_settle_from_scrollY != 0) {
|
|
146
|
+
int16_t from = __ui_settle_from_scrollY; // +toward 0, -toward max
|
|
147
|
+
int16_t maxS = ui_scroll_max(node);
|
|
148
|
+
if (from > 0) {
|
|
149
|
+
__ui_nodes[node].scrollY = (int16_t)(from - (int32_t)(from * k) / 100);
|
|
150
|
+
if (t >= 100) __ui_nodes[node].scrollY = 0;
|
|
151
|
+
} else { // from < 0: snap toward maxS
|
|
152
|
+
int16_t target = maxS;
|
|
153
|
+
__ui_nodes[node].scrollY = target + (int16_t)((int32_t)from * (100 - k) / 100);
|
|
154
|
+
if (t >= 100) __ui_nodes[node].scrollY = target;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (t >= 100) __ui_nodes[node].settling = 0;
|
|
158
|
+
ui_mark_scroll_view_dirty(node);
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 452-621).
|
|
2
|
+
// Mixed slice: nav/touch/scroll/draw-order/kb state vars, image/keyframe/list/canvas/input binding structs, ease, fade. Kept contiguous to preserve byte-identity.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitStateBindingsNav(): string {
|
|
5
|
+
return `
|
|
6
|
+
|
|
7
|
+
// ── Multi-screen navigation ─────────────────────────────────────────────────
|
|
8
|
+
// Touch/scroll/keyboard state reset by navigation.
|
|
9
|
+
static uint8_t __ui_touch_state = 0;
|
|
10
|
+
static int16_t __ui_touch_node = -1; // int16: node index can exceed 127
|
|
11
|
+
// Unified scroll gesture: one owning scroll container per gesture, one baseline.
|
|
12
|
+
// overscrollPx/settling live on the node; only the settle-animation state is here.
|
|
13
|
+
static int16_t __ui_scroll_node = -1; // owning scroll container (int16: node index can exceed 127)
|
|
14
|
+
// Per-node flag: 1 if the scroll-container loop successfully allocated a Mode B
|
|
15
|
+
// canvas for this node on a recent frame. ui_apply_scroll_delta gates scrolling
|
|
16
|
+
// on this — containers whose canvas won't fit (fragmented heap / no PSRAM) are
|
|
17
|
+
// frozen-but-not-torn rather than allowed to scroll with unclipped children.
|
|
18
|
+
// Sized at runtime via __ui_node_count; defaults to all-zero (lock until proven).
|
|
19
|
+
static uint8_t* __ui_scroll_canvas_ok = nullptr;
|
|
20
|
+
// One-shot scroll memory warnings (indexed by node).
|
|
21
|
+
static uint8_t* __ui_scroll_mem_warned = nullptr;
|
|
22
|
+
// Precomputed draw order (lower z-index first, then source index). Built once in
|
|
23
|
+
// ui_init; zIndex is static after mount so this stays valid for the app lifetime.
|
|
24
|
+
static uint16_t* __ui_draw_order = nullptr;
|
|
25
|
+
// Non-virtualized scroll-container node indices (built once in ui_init).
|
|
26
|
+
static uint16_t* __ui_scroll_owners = nullptr;
|
|
27
|
+
static uint16_t __ui_scroll_owner_count = 0;
|
|
28
|
+
// First NODE_FILL on the active screen (for framebuffer bg seed).
|
|
29
|
+
static uint16_t __ui_active_screen_bg_node = 0xFFFF;
|
|
30
|
+
static uint32_t __ui_settle_start_ms = 0; // when the active settle animation began
|
|
31
|
+
static int16_t __ui_settle_from_overscroll = 0; // settle start value (bounce-back)
|
|
32
|
+
static int16_t __ui_settle_from_scrollY = 0; // settle start value (edge snap; sign: +toward 0, -toward max)
|
|
33
|
+
static uint8_t __ui_kb_visible = 0;
|
|
34
|
+
|
|
35
|
+
static uint8_t __ui_active_screen = 0; // which screen is visible/interactive
|
|
36
|
+
extern const uint16_t __ui_screen_count; // total number of screens (emitted by lowering)
|
|
37
|
+
|
|
38
|
+
// ── Image assets ────────────────────────────────────────────────────────────
|
|
39
|
+
struct UIImage { uint16_t w; uint16_t h; const UI_COLOR_T* data; };
|
|
40
|
+
extern const UIImage __ui_images[];
|
|
41
|
+
extern const uint16_t __ui_image_count;
|
|
42
|
+
|
|
43
|
+
// ── @keyframes animations ───────────────────────────────────────────────────
|
|
44
|
+
struct UIKeyframeStop {
|
|
45
|
+
uint8_t percent;
|
|
46
|
+
uint8_t props; // bitmask: 1=background, 2=color, 4=opacity, 8=transform, 16=size
|
|
47
|
+
uint32_t bg;
|
|
48
|
+
uint32_t fg;
|
|
49
|
+
uint8_t opacity;
|
|
50
|
+
int16_t transformOffsetX;
|
|
51
|
+
int16_t transformOffsetY;
|
|
52
|
+
int16_t translatePctX;
|
|
53
|
+
int16_t translatePctY;
|
|
54
|
+
int16_t scaleX;
|
|
55
|
+
int16_t scaleY;
|
|
56
|
+
int16_t rotateDeg;
|
|
57
|
+
int16_t width;
|
|
58
|
+
int16_t height;
|
|
59
|
+
};
|
|
60
|
+
#define UI_KF_BG 1
|
|
61
|
+
#define UI_KF_FG 2
|
|
62
|
+
#define UI_KF_OPACITY 4
|
|
63
|
+
#define UI_KF_TRANSFORM 8
|
|
64
|
+
#define UI_KF_SIZE 16
|
|
65
|
+
struct UIKeyframeSet {
|
|
66
|
+
uint8_t stopCount;
|
|
67
|
+
const UIKeyframeStop* stops;
|
|
68
|
+
};
|
|
69
|
+
struct UIAnimation {
|
|
70
|
+
uint16_t node;
|
|
71
|
+
uint8_t keyframeSet;
|
|
72
|
+
uint16_t durationMs;
|
|
73
|
+
uint16_t delayMs;
|
|
74
|
+
int16_t iterations;
|
|
75
|
+
int16_t baseWidth;
|
|
76
|
+
int16_t baseHeight;
|
|
77
|
+
int8_t originX;
|
|
78
|
+
int8_t originY;
|
|
79
|
+
uint8_t timingFunction; // UI_TIMING_* — applied to the lerp factor between stops
|
|
80
|
+
uint32_t elapsed;
|
|
81
|
+
uint8_t active;
|
|
82
|
+
uint32_t lastUpdateMs; // throttle: only redraw every ~100ms to avoid tearing
|
|
83
|
+
};
|
|
84
|
+
// animation-timing-function codes (kept in sync with model.ts TIMING_*).
|
|
85
|
+
#define UI_TIMING_LINEAR 0
|
|
86
|
+
#define UI_TIMING_EASE_IN_OUT 1
|
|
87
|
+
#define UI_TIMING_EASE 2
|
|
88
|
+
#define UI_TIMING_EASE_IN 3
|
|
89
|
+
#define UI_TIMING_EASE_OUT 4
|
|
90
|
+
|
|
91
|
+
// Apply an easing curve to a 0..100 linear lerp factor. Pure integer math
|
|
92
|
+
// (no floats on device). Uses Newton-Raphson to solve the cubic-bezier x axis
|
|
93
|
+
// for the input k, then returns the bezier's y — identical algorithm + control
|
|
94
|
+
// points to easeCurveLerpK in model.ts so preview and device agree.
|
|
95
|
+
// Control points are /1000 fixed point; bezierX/Y(t) = 3(1-t)²t·c1 + 3(1-t)t²·c2 + t³.
|
|
96
|
+
static inline uint8_t ui_ease_lerp_k(uint8_t timing, uint8_t k) {
|
|
97
|
+
if (timing == UI_TIMING_LINEAR || k == 0) return k;
|
|
98
|
+
if (k >= 100) return 100;
|
|
99
|
+
int32_t x1, y1, x2, y2;
|
|
100
|
+
switch (timing) {
|
|
101
|
+
case UI_TIMING_EASE_IN_OUT: x1 = 420; y1 = 0; x2 = 580; y2 = 1000; break;
|
|
102
|
+
case UI_TIMING_EASE: x1 = 250; y1 = 100; x2 = 250; y2 = 1000; break;
|
|
103
|
+
case UI_TIMING_EASE_IN: x1 = 420; y1 = 0; x2 = 1000; y2 = 1000; break;
|
|
104
|
+
case UI_TIMING_EASE_OUT: x1 = 0; y1 = 0; x2 = 580; y2 = 1000; break;
|
|
105
|
+
default: return k;
|
|
106
|
+
}
|
|
107
|
+
// Control points are /1000 (0..1000 == 0..1). t is parametric, also /1000.
|
|
108
|
+
// X(t) = 3(1-t)²t·x1 + 3(1-t)t²·x2 + t³. Solve X(t)=targetX for t by bisection,
|
|
109
|
+
// then return Y(t). Bisection (not Newton-Raphson): Newton diverges for curves
|
|
110
|
+
// whose x-derivative is ~0 near an endpoint (ease-out: x1=0), snapping the dot
|
|
111
|
+
// to the wrong stop. X(t) is monotonic for valid CSS points, so bisection always
|
|
112
|
+
// converges. Products of four /1000 values are /1e12; t³ is /1e9 (×1000 to align).
|
|
113
|
+
// int64 accumulation avoids overflow (3e12 > INT32_MAX). Identical algorithm +
|
|
114
|
+
// control points to easeCurveLerpK in model.ts — preview and device must agree.
|
|
115
|
+
int32_t targetX = (int32_t)k * 10; // input on the /1000 x axis
|
|
116
|
+
int32_t lo = 0, hi = 1000;
|
|
117
|
+
for (uint8_t i = 0; i < 14; i++) {
|
|
118
|
+
int32_t t = (lo + hi) >> 1;
|
|
119
|
+
int32_t mt = 1000 - t;
|
|
120
|
+
int64_t termX1 = (int64_t)3 * mt * mt * t * x1; // /1e12
|
|
121
|
+
int64_t termX2 = (int64_t)3 * mt * t * t * x2; // /1e12
|
|
122
|
+
int64_t termX3 = (int64_t)t * t * t * 1000; // /1e12 (t³ was /1e9)
|
|
123
|
+
int32_t X = (int32_t)((termX1 + termX2 + termX3) / 1000000000LL); // back to /1000
|
|
124
|
+
if (X < targetX) lo = t; else hi = t;
|
|
125
|
+
}
|
|
126
|
+
int32_t t = (lo + hi) >> 1;
|
|
127
|
+
int32_t mt = 1000 - t;
|
|
128
|
+
int64_t termY1 = (int64_t)3 * mt * mt * t * y1;
|
|
129
|
+
int64_t termY2 = (int64_t)3 * mt * t * t * y2;
|
|
130
|
+
int64_t termY3 = (int64_t)t * t * t * 1000;
|
|
131
|
+
int32_t Y = (int32_t)((termY1 + termY2 + termY3) / 1000000000LL); // /1000
|
|
132
|
+
return (uint8_t)(Y / 10); // back to /100
|
|
133
|
+
}
|
|
134
|
+
extern const UIKeyframeSet __ui_keyframe_sets[];
|
|
135
|
+
extern const uint16_t __ui_keyframe_set_count;
|
|
136
|
+
extern UIAnimation __ui_anims[];
|
|
137
|
+
extern const uint16_t __ui_anim_count;
|
|
138
|
+
|
|
139
|
+
// ── List bindings ───────────────────────────────────────────────────────────
|
|
140
|
+
struct UIListBinding {
|
|
141
|
+
uint16_t node;
|
|
142
|
+
uint16_t (*countFn)(void);
|
|
143
|
+
void (*itemFn)(uint16_t idx, char* buf, uint8_t size);
|
|
144
|
+
void (*tapFn)(uint16_t idx); // optional: called when an item is tapped
|
|
145
|
+
};
|
|
146
|
+
extern UIListBinding __ui_list_bindings[];
|
|
147
|
+
extern const uint16_t __ui_list_binding_count;
|
|
148
|
+
|
|
149
|
+
// ── Canvas bindings (ui.drawCanvas) ─────────────────────────────────────────
|
|
150
|
+
// Each canvas node's user-supplied draw function. Called each frame with the
|
|
151
|
+
// node's offscreen CuttlefishCanvas16 set as the active draw target, so the
|
|
152
|
+
// lowered callback body draws via the same ui_display_* wrappers as everything.
|
|
153
|
+
struct UICanvasBinding {
|
|
154
|
+
uint16_t node;
|
|
155
|
+
void (*fn)(CuttlefishCanvas16* canvas);
|
|
156
|
+
};
|
|
157
|
+
extern UICanvasBinding __ui_canvas_bindings[];
|
|
158
|
+
extern const uint16_t __ui_canvas_binding_count;
|
|
159
|
+
|
|
160
|
+
// ── Input bindings (two-way) ─────────────────────────────────────────────────
|
|
161
|
+
// ui.bindInput(node, cb) — cb fires with the node's current text whenever the
|
|
162
|
+
// bound <input>'s textBuffer changes (e.g. user typed via on-screen keyboard).
|
|
163
|
+
// lastSeen holds the previously-observed text so the runtime can detect change.
|
|
164
|
+
struct UIInputBinding {
|
|
165
|
+
uint16_t node;
|
|
166
|
+
void (*cb)(const char* text);
|
|
167
|
+
char lastSeen[UI_TEXT_BUF + 1];
|
|
168
|
+
};
|
|
169
|
+
extern UIInputBinding __ui_input_bindings[];
|
|
170
|
+
extern const uint16_t __ui_input_binding_count;
|
|
171
|
+
|
|
172
|
+
// List bindings are now carried ON each node (listCountFn/listItemFn/listTapFn).
|
|
173
|
+
// The UIListBinding table below is still emitted by the lowering for the
|
|
174
|
+
// node-initializer to read at static-init time; the runtime never indexes it.
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 90-303).
|
|
2
|
+
// Enums (UINodeKind, UIProperty) and all struct definitions + extern table decls.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitStructs(): string {
|
|
5
|
+
return `
|
|
6
|
+
enum UINodeKind { NODE_FILL, NODE_TEXT, NODE_BUTTON, NODE_CHECK, NODE_RADIO, NODE_PROGRESS, NODE_RANGE, NODE_INPUT, NODE_IMG, NODE_LIST, NODE_CANVAS };
|
|
7
|
+
enum UIProperty { PROP_BG, PROP_FG, PROP_TEXT, PROP_VISIBLE, PROP_BORDER_COLOR, PROP_VALUE };
|
|
8
|
+
|
|
9
|
+
struct UIRect { int16_t x, y, w, h; };
|
|
10
|
+
struct UIFontGlyph {
|
|
11
|
+
uint16_t codepoint;
|
|
12
|
+
int8_t xOffset;
|
|
13
|
+
int8_t yOffset;
|
|
14
|
+
uint8_t width;
|
|
15
|
+
uint8_t height;
|
|
16
|
+
uint8_t advance;
|
|
17
|
+
uint16_t dataOffset; // 4-bit alpha pixel offset
|
|
18
|
+
};
|
|
19
|
+
struct UIFontFace {
|
|
20
|
+
uint8_t id;
|
|
21
|
+
uint8_t glyphCount;
|
|
22
|
+
uint8_t lineHeight;
|
|
23
|
+
uint8_t baseline;
|
|
24
|
+
const UIFontGlyph* glyphs;
|
|
25
|
+
const uint8_t* alpha;
|
|
26
|
+
};
|
|
27
|
+
struct UINode {
|
|
28
|
+
UIRect box;
|
|
29
|
+
uint32_t bg;
|
|
30
|
+
uint32_t fg;
|
|
31
|
+
UINodeKind kind;
|
|
32
|
+
const char* text;
|
|
33
|
+
char textBuffer[UI_TEXT_BUF + 1]; // dynamic text — read only when hasTextBinding == 1
|
|
34
|
+
uint8_t hasTextBinding; // set by ui_init when a PROP_TEXT binding targets this node
|
|
35
|
+
const uint8_t* font;
|
|
36
|
+
uint8_t hasBg;
|
|
37
|
+
uint8_t textAlign; // 0=left, 1=center, 2=right
|
|
38
|
+
uint8_t textSize; // GFX text size: 1-4
|
|
39
|
+
uint8_t lineHeight; // px per text line (0 = font default)
|
|
40
|
+
int8_t letterSpacing; // px between chars (0 = default advance)
|
|
41
|
+
uint8_t fontAntialias; // 1 = smooth text edges when UI_AA is available
|
|
42
|
+
uint8_t fontFace; // 0 = classic GFX bitmap font; otherwise UIFontFace id
|
|
43
|
+
uint32_t borderColor; // resolved color for the border (0 = use fg)
|
|
44
|
+
uint8_t borderStyle; // 0=none, 1=solid, 2=dashed
|
|
45
|
+
uint8_t borderWidth; // px, 0=none (uniform fallback)
|
|
46
|
+
uint8_t borderTopWidth; // per-side; equals borderWidth when uniform
|
|
47
|
+
uint8_t borderRightWidth;
|
|
48
|
+
uint8_t borderBottomWidth;
|
|
49
|
+
uint8_t borderLeftWidth;
|
|
50
|
+
uint8_t hasPerSideBorder; // 1 when any per-side width differs from borderWidth
|
|
51
|
+
uint8_t borderRadius; // px, 0=square
|
|
52
|
+
uint8_t paddingTop;
|
|
53
|
+
uint8_t paddingRight;
|
|
54
|
+
uint8_t paddingBottom;
|
|
55
|
+
uint8_t paddingLeft;
|
|
56
|
+
uint8_t gradientEnabled; // 0=none, 1=vertical, 2=horizontal
|
|
57
|
+
uint32_t gradientColor1;
|
|
58
|
+
uint32_t gradientColor2;
|
|
59
|
+
uint32_t outlineColor;
|
|
60
|
+
uint8_t outlineStyle; // 0=none, 1=solid, 2=dashed
|
|
61
|
+
uint8_t outlineWidth;
|
|
62
|
+
int16_t zIndex; // effective draw layer; higher layers draw later
|
|
63
|
+
int16_t transformOffsetX; // draw-only transform: translate(...)
|
|
64
|
+
int16_t transformOffsetY;
|
|
65
|
+
int16_t rotateDeg;
|
|
66
|
+
int8_t pressedOffsetX; // draw-only :pressed offset, no Yoga relayout
|
|
67
|
+
int8_t pressedOffsetY;
|
|
68
|
+
uint8_t shadowCount; // 0-4 active shadows
|
|
69
|
+
int8_t shadowOffsetX[4];
|
|
70
|
+
int8_t shadowOffsetY[4];
|
|
71
|
+
uint8_t shadowBlur[4];
|
|
72
|
+
uint32_t shadowColor[4];
|
|
73
|
+
uint8_t shadowAlpha[4];
|
|
74
|
+
uint8_t shadowInset[4]; // 0=outset, 1=inset
|
|
75
|
+
uint8_t textShadowCount;
|
|
76
|
+
int8_t textShadowOffsetX;
|
|
77
|
+
int8_t textShadowOffsetY;
|
|
78
|
+
uint8_t textShadowBlur;
|
|
79
|
+
uint32_t textShadowColor;
|
|
80
|
+
uint8_t textShadowAlpha;
|
|
81
|
+
uint8_t underline; // text-decoration: 0=none,1=underline,2=line-through,3=both
|
|
82
|
+
uint8_t textOverflow; // text-overflow: 0=clip, 1=ellipsis (truncate + ...)
|
|
83
|
+
uint8_t nowrap; // 1 = no text wrapping (white-space: nowrap/pre)
|
|
84
|
+
uint8_t whiteSpaceMode; // 0=normal, 1=nowrap, 2=pre, 3=pre-line
|
|
85
|
+
uint8_t visible; // 0=hidden, 1=visible
|
|
86
|
+
uint8_t opacity; // 0-100
|
|
87
|
+
uint32_t clearColor; // ancestor's background — used to wipe transparent text before redraw
|
|
88
|
+
int16_t lastTextWidth;
|
|
89
|
+
int16_t lastTextHeight;
|
|
90
|
+
uint32_t layoutCacheKey; // 0 = invalid; non-zero hashes layout inputs
|
|
91
|
+
uint16_t layoutMetricsW; // cached ui_text_layout_metrics width
|
|
92
|
+
uint16_t layoutMetricsH; // cached ui_text_layout_metrics height
|
|
93
|
+
// scroll (unified: containers and virtualized lists share these)
|
|
94
|
+
uint8_t scrollable; // 1 = children offset by scrollY, clipped to this box
|
|
95
|
+
uint8_t virtualized; // 1 = children produced by list*Fn callbacks (<list>)
|
|
96
|
+
int16_t scrollY; // committed offset (always in [0, maxScroll]); draw subtracts it
|
|
97
|
+
int16_t contentHeight; // total child height (clamp bound + scrollbar ratio)
|
|
98
|
+
int16_t overscrollPx; // elastic excursion past a boundary (0 in-bounds; +top, -bottom)
|
|
99
|
+
uint8_t settling; // 1 while a bounce-back/snap animation runs
|
|
100
|
+
int16_t lastPaintedScrollY; // scrollY at last container repaint (Mode B shift delta)
|
|
101
|
+
uint16_t listCount; // virtualized: current item count (refreshed each frame)
|
|
102
|
+
uint16_t (*listCountFn)(void);
|
|
103
|
+
void (*listItemFn)(uint16_t idx, char* buf, uint8_t size);
|
|
104
|
+
void (*listTapFn)(uint16_t idx); // nullptr if no tap handler
|
|
105
|
+
uint16_t parent; // 0xFFFF = root/no parent (UI_NO_PARENT)
|
|
106
|
+
uint16_t subtreeEnd; // exclusive pre-order end index
|
|
107
|
+
uint8_t screenId; // which <screen> this node belongs to (for navigation)
|
|
108
|
+
uint8_t imgDataId; // index into __ui_images[] (255 = no image)
|
|
109
|
+
uint8_t objectFit; // 0=none, 1=fill, 2=contain, 3=cover, 4=scale-down
|
|
110
|
+
uint16_t listItemHeight; // px per item for <list> (0 = not a list)
|
|
111
|
+
int16_t rangeMin; // for <range>: minimum value
|
|
112
|
+
int16_t rangeMax; // for <range>: maximum value
|
|
113
|
+
int16_t maxlen; // for <input>: max character length (0 = UI_TEXT_BUF)
|
|
114
|
+
uint16_t canvasW; // canvas buffer width (for <canvas>)
|
|
115
|
+
uint16_t canvasH; // canvas buffer height (for <canvas>)
|
|
116
|
+
// Rich-text runs (runCount > 0 for text nodes with mixed inline content).
|
|
117
|
+
// The node references a contiguous slice of the global run / segment / line
|
|
118
|
+
// arrays; geometry is precomputed at transpile time (runs are static-only).
|
|
119
|
+
uint8_t runCount; // number of runs in this node (0 = plain single-string text)
|
|
120
|
+
uint8_t richLineCount; // number of wrapped lines
|
|
121
|
+
uint16_t runStart; // first index into __ui_runs[]
|
|
122
|
+
uint16_t richSegStart; // first index into __ui_rich_segs[]
|
|
123
|
+
uint16_t richSegCount; // total segments across all lines
|
|
124
|
+
uint16_t richLineStart; // first index into __ui_rich_lines[]
|
|
125
|
+
// runtime slot
|
|
126
|
+
uint8_t dirty;
|
|
127
|
+
int16_t value; // unified element state
|
|
128
|
+
};
|
|
129
|
+
// Rich-text run: one piece of styled inline text within a node's run list.
|
|
130
|
+
struct UIRichRun {
|
|
131
|
+
const char* text;
|
|
132
|
+
uint32_t fg;
|
|
133
|
+
uint8_t textSize;
|
|
134
|
+
uint8_t fontFace;
|
|
135
|
+
uint8_t underline; // 0=none,1=underline,2=line-through,3=both
|
|
136
|
+
int8_t letterSpacing;
|
|
137
|
+
int8_t linkTarget; // resolved screen index, -1 = not a link
|
|
138
|
+
};
|
|
139
|
+
// One laid-out segment of a run on one line (precomputed geometry).
|
|
140
|
+
struct UIRichSeg {
|
|
141
|
+
uint8_t runIndex; // index into the node's runs (0..runCount-1)
|
|
142
|
+
const char* text;
|
|
143
|
+
int16_t x; // offset from the line's left edge (pre-alignment)
|
|
144
|
+
uint16_t w; // measured width
|
|
145
|
+
uint8_t line; // which line (0..richLineCount-1) this segment is on
|
|
146
|
+
};
|
|
147
|
+
// One wrapped line of rich text (precomputed).
|
|
148
|
+
struct UIRichLine {
|
|
149
|
+
int16_t y; // top y relative to the node's text top
|
|
150
|
+
uint16_t h; // line height (tallest run on this line)
|
|
151
|
+
int16_t baseline; // baseline y (for mixed-size baseline alignment)
|
|
152
|
+
uint16_t w; // total line width (for alignment)
|
|
153
|
+
};
|
|
154
|
+
struct UITransition {
|
|
155
|
+
uint16_t node;
|
|
156
|
+
UIProperty prop;
|
|
157
|
+
uint16_t durationMs;
|
|
158
|
+
// The :pressed and base-state target colors. ui_on_press arms toward
|
|
159
|
+
// pressedTarget; ui_on_release arms toward baseTarget.
|
|
160
|
+
uint32_t pressedTarget;
|
|
161
|
+
uint32_t baseTarget;
|
|
162
|
+
// runtime
|
|
163
|
+
uint16_t elapsed;
|
|
164
|
+
uint32_t prevValue;
|
|
165
|
+
uint32_t targetValue;
|
|
166
|
+
uint8_t active;
|
|
167
|
+
};
|
|
168
|
+
struct UIBinding {
|
|
169
|
+
uint16_t node;
|
|
170
|
+
UIProperty prop;
|
|
171
|
+
uint32_t (*fn)(void); // for color/numeric bindings
|
|
172
|
+
void (*textFn)(char* buf, uint8_t size); // for text bindings (PROP_TEXT): fills buf
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Color lerp for transitions (rgb565). For mono, this collapses to a snap.
|
|
176
|
+
static inline uint16_t lerp_color(uint16_t a, uint16_t b, uint8_t k100) {
|
|
177
|
+
if (k100 >= 100) return b;
|
|
178
|
+
// Lerp in 888 internally for smoother color transitions (keyframe animation,
|
|
179
|
+
// :pressed transitions). Unpack 565→888, lerp at 8-bit, re-quantize to 565.
|
|
180
|
+
uint8_t ar5 = (a >> 11) & 0x1f, ag6 = (a >> 5) & 0x3f, ab5 = a & 0x1f;
|
|
181
|
+
uint8_t br5 = (b >> 11) & 0x1f, bg6 = (b >> 5) & 0x3f, bb5 = b & 0x1f;
|
|
182
|
+
uint16_t ar8 = (ar5 << 3) | (ar5 >> 2), ag8 = (ag6 << 2) | (ag6 >> 4), ab8 = (ab5 << 3) | (ab5 >> 2);
|
|
183
|
+
uint16_t br8 = (br5 << 3) | (br5 >> 2), bg8 = (bg6 << 2) | (bg6 >> 4), bb8 = (bb5 << 3) | (bb5 >> 2);
|
|
184
|
+
int16_t r = (int16_t)(ar8 + (int16_t)((br8 - ar8) * k100 / 100));
|
|
185
|
+
int16_t g = (int16_t)(ag8 + (int16_t)((bg8 - ag8) * k100 / 100));
|
|
186
|
+
int16_t bl = (int16_t)(ab8 + (int16_t)((bb8 - ab8) * k100 / 100));
|
|
187
|
+
return ((uint16_t)((r >> 3) & 0x1f) << 11) | ((uint16_t)((g >> 2) & 0x3f) << 5) | (uint16_t)((bl >> 3) & 0x1f);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// RGB888 lerp — for transitions on RGB888/RGB666 targets (Phase 2+). Unused in
|
|
191
|
+
// Phase 1; the 565 lerp_color above remains the active path for TFT targets,
|
|
192
|
+
// whose node colors are still emitted as 565 values stored in uint32_t fields.
|
|
193
|
+
static inline uint32_t lerp_color_888(uint32_t a, uint32_t b, uint8_t k100) {
|
|
194
|
+
if (k100 >= 100) return b;
|
|
195
|
+
uint8_t ar = (a >> 16) & 0xff, ag = (a >> 8) & 0xff, ab = a & 0xff;
|
|
196
|
+
uint8_t br = (b >> 16) & 0xff, bg = (b >> 8) & 0xff, bb = b & 0xff;
|
|
197
|
+
int16_t r = ar + (int16_t)(((int16_t)br - (int16_t)ar) * k100 / 100);
|
|
198
|
+
int16_t g = ag + (int16_t)(((int16_t)bg - (int16_t)ag) * k100 / 100);
|
|
199
|
+
int16_t bl = ab + (int16_t)(((int16_t)bb - (int16_t)ab) * k100 / 100);
|
|
200
|
+
return ((uint32_t)(r & 0xff) << 16) | ((uint32_t)(g & 0xff) << 8) | (uint32_t)(bl & 0xff);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Declared by the lowering output (the tables). Matches the mutable (non-const)
|
|
204
|
+
// definitions: ui_tick updates node bg/dirty and transition elapsed/active.
|
|
205
|
+
extern UINode __ui_nodes[];
|
|
206
|
+
extern UITransition __ui_trans[];
|
|
207
|
+
extern UIBinding __ui_bindings[];
|
|
208
|
+
extern const UIFontFace __ui_font_faces[];
|
|
209
|
+
// Rich-text run / segment / line tables (parallel arrays; nodes reference
|
|
210
|
+
// contiguous slices via runStart/richSegStart/richLineStart + counts).
|
|
211
|
+
extern UIRichRun __ui_runs[];
|
|
212
|
+
extern UIRichSeg __ui_rich_segs[];
|
|
213
|
+
extern UIRichLine __ui_rich_lines[];
|
|
214
|
+
extern const uint16_t __ui_node_count;
|
|
215
|
+
extern const uint16_t __ui_trans_count;
|
|
216
|
+
extern const uint16_t __ui_binding_count;
|
|
217
|
+
extern const uint16_t __ui_run_count;
|
|
218
|
+
extern const uint16_t __ui_rich_seg_count;
|
|
219
|
+
extern const uint16_t __ui_rich_line_count;`;
|
|
220
|
+
}
|