@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,8 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 5770-5770).
|
|
2
|
+
// Closing #endif of the __TC_UI_RUNTIME guard.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitGuardClose(): string {
|
|
5
|
+
return `
|
|
6
|
+
#endif
|
|
7
|
+
`;
|
|
8
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 1155-1448).
|
|
2
|
+
// Tail of framebuffer block + image rotation/fit/scaled/rotated drawing.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitImageDrawing(): string {
|
|
5
|
+
return `
|
|
6
|
+
// ── Full-screen framebuffer (opt-in PSRAM perf experiment) ───────────────────
|
|
7
|
+
// Disabled by default: pushing a full 320×240 frame for a tiny dirty button can
|
|
8
|
+
// look like a global brightness flash on SPI TFTs. Enable only when a target
|
|
9
|
+
// has enough PSRAM and a full-frame repaint is preferable to partial updates.
|
|
10
|
+
static CuttlefishCanvas16* __ui_fb = nullptr;
|
|
11
|
+
static uint8_t __ui_fb_tried = 0; // 0 = not yet attempted, 1 = alloc attempted
|
|
12
|
+
|
|
13
|
+
// Get the framebuffer, allocating once (in PSRAM when available). Returns null
|
|
14
|
+
// when PSRAM is absent or the allocation failed — callers fall back to direct.
|
|
15
|
+
static inline CuttlefishCanvas16* ui_get_framebuffer() {
|
|
16
|
+
if (__ui_fb_tried) return __ui_fb; // one-time attempt; null means "none"
|
|
17
|
+
__ui_fb_tried = 1;
|
|
18
|
+
#if UI_USE_FULL_FRAMEBUFFER && defined(ESP32) && defined(BOARD_HAS_PSRAM)
|
|
19
|
+
if (psramFound()) {
|
|
20
|
+
int16_t fw = display_width();
|
|
21
|
+
int16_t fh = display_height();
|
|
22
|
+
// GFXcanvas16 uses ps_malloc when ESP32 PSRAM malloc is hooked in via the
|
|
23
|
+
// Arduino core; the allocation succeeds only if PSRAM is really present.
|
|
24
|
+
__ui_fb = display_createCanvasPsram(fw, fh);
|
|
25
|
+
if (!__ui_fb || !display_canvasBuffer(__ui_fb)) {
|
|
26
|
+
// Allocation failed (PSRAM too small / not really mapped) — give up.
|
|
27
|
+
__ui_fb = nullptr;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
#endif
|
|
31
|
+
return __ui_fb;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Push the entire framebuffer to the display in one bulk SPI transaction.
|
|
35
|
+
static inline void ui_push_framebuffer() {
|
|
36
|
+
if (!__ui_fb || !display_canvasBuffer(__ui_fb)) return;
|
|
37
|
+
int16_t w = display_canvasWidth(__ui_fb);
|
|
38
|
+
int16_t h = display_canvasHeight(__ui_fb);
|
|
39
|
+
display_startWrite();
|
|
40
|
+
display_setAddrWindow(0, 0, w, h);
|
|
41
|
+
display_writePixels(display_canvasBuffer(__ui_fb), (uint32_t)w * h);
|
|
42
|
+
display_endWrite();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static inline uint8_t ui_rotation_quadrant(int16_t deg) {
|
|
46
|
+
int16_t normalized = deg % 360;
|
|
47
|
+
if (normalized < 0) normalized += 360;
|
|
48
|
+
if (normalized == 90) return 1;
|
|
49
|
+
if (normalized == 180) return 2;
|
|
50
|
+
if (normalized == 270) return 3;
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static inline int16_t ui_rotated_face_w(uint16_t nodeIdx, int16_t w, int16_t h) {
|
|
55
|
+
if (__ui_nodes[nodeIdx].kind != NODE_IMG &&
|
|
56
|
+
!(__ui_nodes[nodeIdx].kind == NODE_FILL && __ui_nodes[nodeIdx].gradientEnabled == 0)) return w;
|
|
57
|
+
uint8_t q = ui_rotation_quadrant(__ui_nodes[nodeIdx].rotateDeg);
|
|
58
|
+
return (q == 1 || q == 3) ? h : w;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static inline int16_t ui_rotated_face_h(uint16_t nodeIdx, int16_t w, int16_t h) {
|
|
62
|
+
if (__ui_nodes[nodeIdx].kind != NODE_IMG &&
|
|
63
|
+
!(__ui_nodes[nodeIdx].kind == NODE_FILL && __ui_nodes[nodeIdx].gradientEnabled == 0)) return h;
|
|
64
|
+
uint8_t q = ui_rotation_quadrant(__ui_nodes[nodeIdx].rotateDeg);
|
|
65
|
+
return (q == 1 || q == 3) ? w : h;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Draw image with object-fit: 0=none, 1=fill, 2=contain, 3=cover, 4=scale-down.
|
|
69
|
+
// The sampler is bounded to the target box first, then quarter-turn rotated.
|
|
70
|
+
// This keeps cover cropped inside the element instead of overpainting siblings.
|
|
71
|
+
static inline void ui_draw_image_with_fit(const UIImage* img, int16_t x, int16_t y, int16_t rotateDeg,
|
|
72
|
+
uint8_t fitMode, int16_t targetW, int16_t targetH) {
|
|
73
|
+
if (!img || !img->data || img->w == 0 || img->h == 0 || targetW <= 0 || targetH <= 0) return;
|
|
74
|
+
int16_t srcW = img->w, srcH = img->h;
|
|
75
|
+
int16_t drawW = srcW, drawH = srcH;
|
|
76
|
+
int16_t offX = (targetW - drawW) / 2;
|
|
77
|
+
int16_t offY = (targetH - drawH) / 2;
|
|
78
|
+
|
|
79
|
+
if (fitMode == 1) {
|
|
80
|
+
drawW = targetW;
|
|
81
|
+
drawH = targetH;
|
|
82
|
+
offX = 0;
|
|
83
|
+
offY = 0;
|
|
84
|
+
} else if (fitMode == 2 || fitMode == 3 || fitMode == 4) {
|
|
85
|
+
int32_t scaleX = ((int32_t)targetW * 1000) / srcW;
|
|
86
|
+
int32_t scaleY = ((int32_t)targetH * 1000) / srcH;
|
|
87
|
+
if (scaleX < 1) scaleX = 1;
|
|
88
|
+
if (scaleY < 1) scaleY = 1;
|
|
89
|
+
int32_t scale = scaleX;
|
|
90
|
+
if (fitMode == 2) {
|
|
91
|
+
if (scaleY < scaleX) scale = scaleY;
|
|
92
|
+
} else if (fitMode == 3) {
|
|
93
|
+
if (scaleY > scaleX) scale = scaleY;
|
|
94
|
+
} else {
|
|
95
|
+
if (scaleY < scaleX) scale = scaleY;
|
|
96
|
+
if (scale > 1000) scale = 1000;
|
|
97
|
+
}
|
|
98
|
+
drawW = (int16_t)((int32_t)srcW * scale / 1000);
|
|
99
|
+
drawH = (int16_t)((int32_t)srcH * scale / 1000);
|
|
100
|
+
if (drawW < 1) drawW = 1;
|
|
101
|
+
if (drawH < 1) drawH = 1;
|
|
102
|
+
if (fitMode == 3) {
|
|
103
|
+
if (drawW < targetW) drawW = targetW;
|
|
104
|
+
if (drawH < targetH) drawH = targetH;
|
|
105
|
+
}
|
|
106
|
+
offX = (targetW - drawW) / 2;
|
|
107
|
+
offY = (targetH - drawH) / 2;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
uint8_t q = ui_rotation_quadrant(rotateDeg);
|
|
111
|
+
int16_t clipX = x;
|
|
112
|
+
int16_t clipY = y;
|
|
113
|
+
int16_t clipW = (q == 1 || q == 3) ? targetH : targetW;
|
|
114
|
+
int16_t clipH = (q == 1 || q == 3) ? targetW : targetH;
|
|
115
|
+
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
116
|
+
int16_t txStart = 0, txEnd = targetW, tyStart = 0, tyEnd = targetH;
|
|
117
|
+
if (q == 0) {
|
|
118
|
+
txStart = ui_clamp_i16((int16_t)(clipX - x), 0, targetW);
|
|
119
|
+
txEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, targetW);
|
|
120
|
+
tyStart = ui_clamp_i16((int16_t)(clipY - y), 0, targetH);
|
|
121
|
+
tyEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, targetH);
|
|
122
|
+
} else if (q == 1) {
|
|
123
|
+
txStart = ui_clamp_i16((int16_t)(clipY - y), 0, targetW);
|
|
124
|
+
txEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, targetW);
|
|
125
|
+
tyStart = ui_clamp_i16((int16_t)(x + targetH - (clipX + clipW)), 0, targetH);
|
|
126
|
+
tyEnd = ui_clamp_i16((int16_t)(x + targetH - clipX), 0, targetH);
|
|
127
|
+
} else if (q == 2) {
|
|
128
|
+
txStart = ui_clamp_i16((int16_t)(x + targetW - (clipX + clipW)), 0, targetW);
|
|
129
|
+
txEnd = ui_clamp_i16((int16_t)(x + targetW - clipX), 0, targetW);
|
|
130
|
+
tyStart = ui_clamp_i16((int16_t)(y + targetH - (clipY + clipH)), 0, targetH);
|
|
131
|
+
tyEnd = ui_clamp_i16((int16_t)(y + targetH - clipY), 0, targetH);
|
|
132
|
+
} else {
|
|
133
|
+
txStart = ui_clamp_i16((int16_t)(y + targetW - (clipY + clipH)), 0, targetW);
|
|
134
|
+
txEnd = ui_clamp_i16((int16_t)(y + targetW - clipY), 0, targetW);
|
|
135
|
+
tyStart = ui_clamp_i16((int16_t)(clipX - x), 0, targetH);
|
|
136
|
+
tyEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, targetH);
|
|
137
|
+
}
|
|
138
|
+
if (txStart >= txEnd || tyStart >= tyEnd) return;
|
|
139
|
+
for (int16_t ty = tyStart; ty < tyEnd; ty++) {
|
|
140
|
+
int16_t localY = ty - offY;
|
|
141
|
+
if (localY < 0 || localY >= drawH) continue;
|
|
142
|
+
int16_t srcY = ((int32_t)localY * srcH) / drawH;
|
|
143
|
+
if (srcY < 0) srcY = 0;
|
|
144
|
+
if (srcY >= srcH) srcY = srcH - 1;
|
|
145
|
+
for (int16_t tx = txStart; tx < txEnd; tx++) {
|
|
146
|
+
int16_t localX = tx - offX;
|
|
147
|
+
if (localX < 0 || localX >= drawW) continue;
|
|
148
|
+
int16_t srcX = ((int32_t)localX * srcW) / drawW;
|
|
149
|
+
if (srcX < 0) srcX = 0;
|
|
150
|
+
if (srcX >= srcW) srcX = srcW - 1;
|
|
151
|
+
UI_COLOR_T color = img->data[(int32_t)srcY * srcW + srcX];
|
|
152
|
+
int16_t dx = tx;
|
|
153
|
+
int16_t dy = ty;
|
|
154
|
+
if (q == 1) {
|
|
155
|
+
dx = targetH - 1 - ty;
|
|
156
|
+
dy = tx;
|
|
157
|
+
} else if (q == 2) {
|
|
158
|
+
dx = targetW - 1 - tx;
|
|
159
|
+
dy = targetH - 1 - ty;
|
|
160
|
+
} else if (q == 3) {
|
|
161
|
+
dx = ty;
|
|
162
|
+
dy = targetW - 1 - tx;
|
|
163
|
+
}
|
|
164
|
+
ui_display_draw_pixel(x + dx, y + dy, color);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static inline void ui_draw_scaled_image(const UIImage* img, int16_t x, int16_t y, int16_t rotateDeg,
|
|
170
|
+
int16_t drawW, int16_t drawH) {
|
|
171
|
+
if (!img || !img->data || img->w == 0 || img->h == 0 || drawW <= 0 || drawH <= 0) return;
|
|
172
|
+
uint8_t q = ui_rotation_quadrant(rotateDeg);
|
|
173
|
+
int16_t clipX = x;
|
|
174
|
+
int16_t clipY = y;
|
|
175
|
+
int16_t clipW = (q == 1 || q == 3) ? drawH : drawW;
|
|
176
|
+
int16_t clipH = (q == 1 || q == 3) ? drawW : drawH;
|
|
177
|
+
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
178
|
+
int16_t dxStart = 0, dxEnd = drawW, dyStart = 0, dyEnd = drawH;
|
|
179
|
+
if (q == 0) {
|
|
180
|
+
dxStart = ui_clamp_i16((int16_t)(clipX - x), 0, drawW);
|
|
181
|
+
dxEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, drawW);
|
|
182
|
+
dyStart = ui_clamp_i16((int16_t)(clipY - y), 0, drawH);
|
|
183
|
+
dyEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, drawH);
|
|
184
|
+
} else if (q == 1) {
|
|
185
|
+
dxStart = ui_clamp_i16((int16_t)(clipY - y), 0, drawW);
|
|
186
|
+
dxEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, drawW);
|
|
187
|
+
dyStart = ui_clamp_i16((int16_t)(x + drawH - (clipX + clipW)), 0, drawH);
|
|
188
|
+
dyEnd = ui_clamp_i16((int16_t)(x + drawH - clipX), 0, drawH);
|
|
189
|
+
} else if (q == 2) {
|
|
190
|
+
dxStart = ui_clamp_i16((int16_t)(x + drawW - (clipX + clipW)), 0, drawW);
|
|
191
|
+
dxEnd = ui_clamp_i16((int16_t)(x + drawW - clipX), 0, drawW);
|
|
192
|
+
dyStart = ui_clamp_i16((int16_t)(y + drawH - (clipY + clipH)), 0, drawH);
|
|
193
|
+
dyEnd = ui_clamp_i16((int16_t)(y + drawH - clipY), 0, drawH);
|
|
194
|
+
} else {
|
|
195
|
+
dxStart = ui_clamp_i16((int16_t)(y + drawW - (clipY + clipH)), 0, drawW);
|
|
196
|
+
dxEnd = ui_clamp_i16((int16_t)(y + drawW - clipY), 0, drawW);
|
|
197
|
+
dyStart = ui_clamp_i16((int16_t)(clipX - x), 0, drawH);
|
|
198
|
+
dyEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, drawH);
|
|
199
|
+
}
|
|
200
|
+
if (dxStart >= dxEnd || dyStart >= dyEnd) return;
|
|
201
|
+
if (q == 0) {
|
|
202
|
+
// Simple case: no rotation, draw with scaling
|
|
203
|
+
if (drawW == img->w && drawH == img->h) {
|
|
204
|
+
int16_t rows = (int16_t)(dyEnd - dyStart);
|
|
205
|
+
int16_t cols = (int16_t)(dxEnd - dxStart);
|
|
206
|
+
for (int16_t row = 0; row < rows; row++) {
|
|
207
|
+
ui_display_draw_rgb_bitmap((int16_t)(x + dxStart), (int16_t)(y + dyStart + row),
|
|
208
|
+
img->data + (int32_t)(dyStart + row) * img->w + dxStart, cols, 1);
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
// Scale using nearest-neighbor
|
|
212
|
+
for (int16_t dy = dyStart; dy < dyEnd; dy++) {
|
|
213
|
+
int16_t srcY = ((int32_t)dy * img->h) / drawH;
|
|
214
|
+
for (int16_t dx = dxStart; dx < dxEnd; dx++) {
|
|
215
|
+
int16_t srcX = ((int32_t)dx * img->w) / drawW;
|
|
216
|
+
UI_COLOR_T color = img->data[(int32_t)srcY * img->w + srcX];
|
|
217
|
+
ui_display_draw_pixel(x + dx, y + dy, color);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
for (int16_t dy = dyStart; dy < dyEnd; dy++) {
|
|
224
|
+
int16_t srcY = ((int32_t)dy * img->h) / drawH;
|
|
225
|
+
for (int16_t dx = dxStart; dx < dxEnd; dx++) {
|
|
226
|
+
int16_t srcX = ((int32_t)dx * img->w) / drawW;
|
|
227
|
+
UI_COLOR_T color = img->data[(int32_t)srcY * img->w + srcX];
|
|
228
|
+
int16_t rdx = 0, rdy = 0;
|
|
229
|
+
if (q == 1) {
|
|
230
|
+
rdx = drawH - 1 - dy;
|
|
231
|
+
rdy = dx;
|
|
232
|
+
} else if (q == 2) {
|
|
233
|
+
rdx = drawW - 1 - dx;
|
|
234
|
+
rdy = drawH - 1 - dy;
|
|
235
|
+
} else {
|
|
236
|
+
rdx = dy;
|
|
237
|
+
rdy = drawW - 1 - dx;
|
|
238
|
+
}
|
|
239
|
+
ui_display_draw_pixel(x + rdx, y + rdy, color);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static inline void ui_draw_image_rotated(const UIImage* img, int16_t x, int16_t y, int16_t rotateDeg) {
|
|
245
|
+
uint8_t q = ui_rotation_quadrant(rotateDeg);
|
|
246
|
+
int16_t clipX = x;
|
|
247
|
+
int16_t clipY = y;
|
|
248
|
+
int16_t clipW = (q == 1 || q == 3) ? img->h : img->w;
|
|
249
|
+
int16_t clipH = (q == 1 || q == 3) ? img->w : img->h;
|
|
250
|
+
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
251
|
+
if (q == 0) {
|
|
252
|
+
int16_t sx0 = ui_clamp_i16((int16_t)(clipX - x), 0, (int16_t)img->w);
|
|
253
|
+
int16_t sy0 = ui_clamp_i16((int16_t)(clipY - y), 0, (int16_t)img->h);
|
|
254
|
+
int16_t sw = ui_clamp_i16(clipW, 0, (int16_t)(img->w - sx0));
|
|
255
|
+
int16_t sh = ui_clamp_i16(clipH, 0, (int16_t)(img->h - sy0));
|
|
256
|
+
for (int16_t row = 0; row < sh; row++) {
|
|
257
|
+
ui_display_draw_rgb_bitmap((int16_t)(x + sx0), (int16_t)(y + sy0 + row),
|
|
258
|
+
img->data + (int32_t)(sy0 + row) * img->w + sx0, sw, 1);
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
uint16_t sxStart = 0, sxEnd = img->w, syStart = 0, syEnd = img->h;
|
|
263
|
+
if (q == 1) {
|
|
264
|
+
sxStart = (uint16_t)ui_clamp_i16((int16_t)(clipY - y), 0, (int16_t)img->w);
|
|
265
|
+
sxEnd = (uint16_t)ui_clamp_i16((int16_t)(clipY + clipH - y), 0, (int16_t)img->w);
|
|
266
|
+
syStart = (uint16_t)ui_clamp_i16((int16_t)(x + img->h - (clipX + clipW)), 0, (int16_t)img->h);
|
|
267
|
+
syEnd = (uint16_t)ui_clamp_i16((int16_t)(x + img->h - clipX), 0, (int16_t)img->h);
|
|
268
|
+
} else if (q == 2) {
|
|
269
|
+
sxStart = (uint16_t)ui_clamp_i16((int16_t)(x + img->w - (clipX + clipW)), 0, (int16_t)img->w);
|
|
270
|
+
sxEnd = (uint16_t)ui_clamp_i16((int16_t)(x + img->w - clipX), 0, (int16_t)img->w);
|
|
271
|
+
syStart = (uint16_t)ui_clamp_i16((int16_t)(y + img->h - (clipY + clipH)), 0, (int16_t)img->h);
|
|
272
|
+
syEnd = (uint16_t)ui_clamp_i16((int16_t)(y + img->h - clipY), 0, (int16_t)img->h);
|
|
273
|
+
} else {
|
|
274
|
+
sxStart = (uint16_t)ui_clamp_i16((int16_t)(y + img->w - (clipY + clipH)), 0, (int16_t)img->w);
|
|
275
|
+
sxEnd = (uint16_t)ui_clamp_i16((int16_t)(y + img->w - clipY), 0, (int16_t)img->w);
|
|
276
|
+
syStart = (uint16_t)ui_clamp_i16((int16_t)(clipX - x), 0, (int16_t)img->h);
|
|
277
|
+
syEnd = (uint16_t)ui_clamp_i16((int16_t)(clipX + clipW - x), 0, (int16_t)img->h);
|
|
278
|
+
}
|
|
279
|
+
if (sxStart >= sxEnd || syStart >= syEnd) return;
|
|
280
|
+
for (uint16_t sy = syStart; sy < syEnd; sy++) {
|
|
281
|
+
for (uint16_t sx = sxStart; sx < sxEnd; sx++) {
|
|
282
|
+
UI_COLOR_T color = img->data[(uint32_t)sy * img->w + sx];
|
|
283
|
+
int16_t dx = 0;
|
|
284
|
+
int16_t dy = 0;
|
|
285
|
+
if (q == 1) {
|
|
286
|
+
dx = (int16_t)(img->h - 1 - sy);
|
|
287
|
+
dy = (int16_t)sx;
|
|
288
|
+
} else if (q == 2) {
|
|
289
|
+
dx = (int16_t)(img->w - 1 - sx);
|
|
290
|
+
dy = (int16_t)(img->h - 1 - sy);
|
|
291
|
+
} else {
|
|
292
|
+
dx = (int16_t)sy;
|
|
293
|
+
dy = (int16_t)(img->w - 1 - sx);
|
|
294
|
+
}
|
|
295
|
+
ui_display_draw_pixel(x + dx, y + dy, color);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
`;
|
|
300
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 2442-2561).
|
|
2
|
+
// ui_init, press/release entry points, pin-watch polling.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitInitPressInput(): string {
|
|
5
|
+
return `
|
|
6
|
+
// Initial draw: mark all nodes dirty so the first ui_tick renders everything.
|
|
7
|
+
// Called once in setup() before the loop begins.
|
|
8
|
+
// Also seed each text-bound node's buffer from its flash literal so the first
|
|
9
|
+
// strcmp in ui_tick has a valid baseline (no spurious redraw on frame 1).
|
|
10
|
+
static inline void ui_init(void) {
|
|
11
|
+
// Allocate the per-node scroll-canvas-OK flag array (once; __ui_node_count
|
|
12
|
+
// is a compile-time constant known by this point). calloc zeroes it — all
|
|
13
|
+
// containers start locked until the scroll-container loop proves their
|
|
14
|
+
// canvas fits.
|
|
15
|
+
if (!__ui_scroll_canvas_ok && __ui_node_count > 0) {
|
|
16
|
+
__ui_scroll_canvas_ok = (uint8_t*)calloc(__ui_node_count, sizeof(uint8_t));
|
|
17
|
+
}
|
|
18
|
+
if (!__ui_scroll_mem_warned && __ui_node_count > 0) {
|
|
19
|
+
__ui_scroll_mem_warned = (uint8_t*)calloc(__ui_node_count, sizeof(uint8_t));
|
|
20
|
+
}
|
|
21
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
22
|
+
__ui_nodes[i].dirty = 1;
|
|
23
|
+
__ui_nodes[i].lastTextHeight = 0;
|
|
24
|
+
__ui_nodes[i].layoutCacheKey = 0;
|
|
25
|
+
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) {
|
|
26
|
+
__ui_nodes[i].lastTextWidth = -1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (uint16_t i = 0; i < __ui_binding_count; i++) {
|
|
30
|
+
if (__ui_bindings[i].prop == PROP_TEXT && __ui_bindings[i].textFn) {
|
|
31
|
+
uint16_t n = __ui_bindings[i].node;
|
|
32
|
+
__ui_nodes[n].hasTextBinding = 1;
|
|
33
|
+
strncpy(__ui_nodes[n].textBuffer, __ui_nodes[n].text ? __ui_nodes[n].text : "", UI_TEXT_BUF);
|
|
34
|
+
__ui_nodes[n].textBuffer[UI_TEXT_BUF] = '\\0';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Seed virtualized-list runtime state. The fn pointers can't be baked into
|
|
38
|
+
// the static node initializer: ui.bindList is resolved AFTER ui.mount lowers
|
|
39
|
+
// the HTML, so the lowering can't see the binding yet. Instead the lowering
|
|
40
|
+
// emits the UIListBinding table (the binding's fn bodies) and ui_init copies
|
|
41
|
+
// the pointers onto each <list> node here, then computes the initial count
|
|
42
|
+
// and contentHeight so the first paint and the scroll clamp bound are correct.
|
|
43
|
+
for (uint16_t b = 0; b < __ui_list_binding_count; b++) {
|
|
44
|
+
uint16_t n = __ui_list_bindings[b].node;
|
|
45
|
+
if (n >= __ui_node_count || !__ui_nodes[n].virtualized) continue;
|
|
46
|
+
__ui_nodes[n].listCountFn = __ui_list_bindings[b].countFn;
|
|
47
|
+
__ui_nodes[n].listItemFn = __ui_list_bindings[b].itemFn;
|
|
48
|
+
__ui_nodes[n].listTapFn = __ui_list_bindings[b].tapFn;
|
|
49
|
+
}
|
|
50
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
51
|
+
if (!__ui_nodes[i].virtualized || !__ui_nodes[i].listCountFn) continue;
|
|
52
|
+
uint16_t ih = __ui_nodes[i].listItemHeight > 0 ? __ui_nodes[i].listItemHeight : 24;
|
|
53
|
+
uint16_t cnt = __ui_nodes[i].listCountFn();
|
|
54
|
+
__ui_nodes[i].listCount = cnt;
|
|
55
|
+
__ui_nodes[i].contentHeight = (int16_t)((uint32_t)cnt * ih);
|
|
56
|
+
__ui_nodes[i].scrollY = 0;
|
|
57
|
+
__ui_nodes[i].overscrollPx = 0;
|
|
58
|
+
__ui_nodes[i].settling = 0;
|
|
59
|
+
__ui_nodes[i].lastPaintedScrollY = -(__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1);
|
|
60
|
+
}
|
|
61
|
+
ui_build_draw_order();
|
|
62
|
+
ui_build_scroll_owner_table();
|
|
63
|
+
ui_refresh_active_screen_bg_node();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Debounce: ignore press/release events within 50ms of the last edge.
|
|
67
|
+
// Mechanical switches bounce (multiple edges in ~5-20ms); without this, the
|
|
68
|
+
// transition gets armed/interrupted dozens of times per physical press.
|
|
69
|
+
static volatile uint32_t __ui_last_edge_time = 0;
|
|
70
|
+
#define UI_DEBOUNCE_MS 50
|
|
71
|
+
|
|
72
|
+
static inline void ui_set_pressed(uint16_t nodeIdx, uint8_t pressed) {
|
|
73
|
+
__ui_nodes[nodeIdx].value = pressed ? 1 : 0;
|
|
74
|
+
ui_mark_dirty(nodeIdx);
|
|
75
|
+
for (uint16_t i = 0; i < __ui_trans_count; i++) {
|
|
76
|
+
if (__ui_trans[i].node == nodeIdx) {
|
|
77
|
+
__ui_trans[i].prevValue = __ui_trans[i].prop == PROP_FG ? __ui_nodes[nodeIdx].fg : __ui_nodes[nodeIdx].bg;
|
|
78
|
+
__ui_trans[i].targetValue = pressed ? __ui_trans[i].pressedTarget : __ui_trans[i].baseTarget;
|
|
79
|
+
__ui_trans[i].elapsed = 0;
|
|
80
|
+
__ui_trans[i].active = 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Press / release entry points that node.onPress(pin) lowers to.
|
|
86
|
+
// On press, arm transitions toward the :pressed target color; on release,
|
|
87
|
+
// arm them back toward the base color (interrupt-and-re-lerp from current).
|
|
88
|
+
static inline void ui_on_press(uint16_t nodeIdx) {
|
|
89
|
+
uint32_t now = millis();
|
|
90
|
+
if (now - __ui_last_edge_time < UI_DEBOUNCE_MS) return;
|
|
91
|
+
__ui_last_edge_time = now;
|
|
92
|
+
ui_set_pressed(nodeIdx, 1);
|
|
93
|
+
}
|
|
94
|
+
static inline void ui_on_release(uint16_t nodeIdx) {
|
|
95
|
+
uint32_t now = millis();
|
|
96
|
+
if (now - __ui_last_edge_time < UI_DEBOUNCE_MS) return;
|
|
97
|
+
__ui_last_edge_time = now;
|
|
98
|
+
ui_set_pressed(nodeIdx, 0);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Pin-watch callback type: void fn(void)
|
|
102
|
+
typedef void (*PinWatchCallback)(void);
|
|
103
|
+
|
|
104
|
+
struct UIPinWatch {
|
|
105
|
+
uint8_t pin;
|
|
106
|
+
uint8_t lastState; // for edge detection
|
|
107
|
+
PinWatchCallback cb; // fires on falling edge
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Populated by the emit layer from ui.watchPin() calls.
|
|
111
|
+
extern UIPinWatch __ui_pin_watches[];
|
|
112
|
+
extern const uint16_t __ui_pin_watch_count;
|
|
113
|
+
|
|
114
|
+
// Poll all configured pin-watchers. Called at the start of ui_tick each frame.
|
|
115
|
+
// Detects falling edges with natural debounce from the ~16ms frame rate.
|
|
116
|
+
static inline void ui_poll_inputs() {
|
|
117
|
+
for (uint16_t i = 0; i < __ui_pin_watch_count; i++) {
|
|
118
|
+
uint8_t val = digitalRead(__ui_pin_watches[i].pin);
|
|
119
|
+
if (val == LOW && __ui_pin_watches[i].lastState == HIGH) {
|
|
120
|
+
if (__ui_pin_watches[i].cb) __ui_pin_watches[i].cb();
|
|
121
|
+
}
|
|
122
|
+
__ui_pin_watches[i].lastState = val;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
}
|