@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,105 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 1449-1547).
|
|
2
|
+
// Canvas push/draw helpers, buffered scroll canvas push, scrollbar direct.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitCanvasScrollbar(): string {
|
|
5
|
+
return `
|
|
6
|
+
static inline void ui_push_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, int16_t y, int16_t w, int16_t h) {
|
|
7
|
+
if (!canvas || !display_canvasBuffer(canvas)) return;
|
|
8
|
+
UI_COLOR_T* pixels = display_canvasBuffer(canvas);
|
|
9
|
+
int16_t stride = display_canvasWidth(canvas);
|
|
10
|
+
// The canvas is viewport-sized: buffer row 0 = the first row of the viewport.
|
|
11
|
+
// The display destination is (x, y) but the source buffer starts at (0, 0).
|
|
12
|
+
// When a full-screen framebuffer is the active draw target, composite into it
|
|
13
|
+
// via __ui_gfx (so the single bulk push at frame end captures everything).
|
|
14
|
+
// Otherwise write straight to the display in one SPI transaction.
|
|
15
|
+
if (__ui_fb) {
|
|
16
|
+
if (w == stride) {
|
|
17
|
+
ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (int16_t row = 0; row < h; row++) {
|
|
21
|
+
ui_display_draw_rgb_bitmap(x, y + row, pixels + (int32_t)row * stride, w, 1);
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
display_startWrite();
|
|
26
|
+
display_setAddrWindow(x, y, w, h);
|
|
27
|
+
if (w == stride) {
|
|
28
|
+
// Full-width: contiguous in buffer, single write.
|
|
29
|
+
display_writePixels(pixels, (uint32_t)w * h);
|
|
30
|
+
display_endWrite();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (int16_t row = 0; row < h; row++) {
|
|
34
|
+
display_writePixels(pixels + (int32_t)row * stride, w);
|
|
35
|
+
}
|
|
36
|
+
display_endWrite();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static inline void ui_draw_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, int16_t y, int16_t w, int16_t h) {
|
|
40
|
+
if (!canvas || !display_canvasBuffer(canvas)) return;
|
|
41
|
+
UI_COLOR_T* pixels = display_canvasBuffer(canvas);
|
|
42
|
+
int16_t stride = display_canvasWidth(canvas);
|
|
43
|
+
if (w == stride && h == display_canvasHeight(canvas)) {
|
|
44
|
+
ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
for (int16_t row = 0; row < h; row++) {
|
|
48
|
+
ui_display_draw_rgb_bitmap(x, y + row, pixels + (int32_t)row * stride, w, 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static inline void ui_push_buffered_scroll_canvas(CuttlefishCanvas16* bufferedScrollCanvas,
|
|
53
|
+
CuttlefishCanvas16* bufferedScrollRepaintCanvas,
|
|
54
|
+
int16_t bufferedScrollNode,
|
|
55
|
+
int16_t bufferedScrollVX,
|
|
56
|
+
int16_t bufferedScrollVY,
|
|
57
|
+
int16_t bufferedScrollRepaintY,
|
|
58
|
+
int16_t bufferedScrollRepaintH,
|
|
59
|
+
CuttlefishDisplayTarget* drawTarget) {
|
|
60
|
+
if (bufferedScrollNode < 0 || !bufferedScrollCanvas || !display_canvasBuffer(bufferedScrollCanvas)) return;
|
|
61
|
+
// Draw scrollbar into the canvas (canvas-local coords: 0,0 = viewport top-left).
|
|
62
|
+
ui_display_set_target(bufferedScrollCanvas);
|
|
63
|
+
int16_t si = bufferedScrollNode;
|
|
64
|
+
int16_t vw = __ui_nodes[si].box.w;
|
|
65
|
+
int16_t vh = __ui_nodes[si].box.h;
|
|
66
|
+
if (bufferedScrollRepaintCanvas && bufferedScrollRepaintH > 0) {
|
|
67
|
+
ui_draw_canvas_rect(bufferedScrollRepaintCanvas, 0, bufferedScrollRepaintY, vw, bufferedScrollRepaintH);
|
|
68
|
+
}
|
|
69
|
+
int16_t tx = vw - 4;
|
|
70
|
+
uint16_t thumbH = (uint32_t)vh * vh / __ui_nodes[si].contentHeight;
|
|
71
|
+
if (thumbH < 8) thumbH = 8;
|
|
72
|
+
int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
|
|
73
|
+
uint16_t thumbY = (uint32_t)(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
|
|
74
|
+
UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
|
|
75
|
+
ui_display_fill_rect(tx, 0, 3, vh, dimFg);
|
|
76
|
+
ui_display_fill_rect(tx, thumbY, 3, thumbH, __ui_nodes[si].fg);
|
|
77
|
+
// Record the scrollY this canvas now reflects, so the next scroll frame can
|
|
78
|
+
// compute its shift delta (Mode B) from scrollY - lastPaintedScrollY.
|
|
79
|
+
__ui_nodes[si].lastPaintedScrollY = __ui_nodes[si].scrollY;
|
|
80
|
+
// Push the canvas to the draw target at the viewport position (the
|
|
81
|
+
// framebuffer when active, else the display directly).
|
|
82
|
+
ui_display_set_target(drawTarget);
|
|
83
|
+
ui_push_canvas_rect(bufferedScrollCanvas,
|
|
84
|
+
bufferedScrollVX, bufferedScrollVY,
|
|
85
|
+
vw, vh);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Draw a scroll container's scrollbar directly on the display (Mode C).
|
|
89
|
+
static inline void ui_draw_scrollbar_direct(int16_t si, int16_t vox, int16_t voy) {
|
|
90
|
+
if (si < 0 || si >= (int16_t)__ui_node_count) return;
|
|
91
|
+
int16_t vw = __ui_nodes[si].box.w;
|
|
92
|
+
int16_t vh = __ui_nodes[si].box.h;
|
|
93
|
+
if (__ui_nodes[si].contentHeight <= vh) return;
|
|
94
|
+
int16_t tx = vox + vw - 4;
|
|
95
|
+
uint16_t thumbH = (uint32_t)vh * vh / __ui_nodes[si].contentHeight;
|
|
96
|
+
if (thumbH < 8) thumbH = 8;
|
|
97
|
+
int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
|
|
98
|
+
uint16_t thumbY = (uint32_t)(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
|
|
99
|
+
UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
|
|
100
|
+
ui_display_fill_rect(tx, voy, 3, vh, dimFg);
|
|
101
|
+
ui_display_fill_rect(tx, (int16_t)(voy + thumbY), 3, thumbH, __ui_nodes[si].fg);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Per-node dirty marker (called by press handlers and binding evaluation).`;
|
|
105
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 304-451).
|
|
2
|
+
// lerp/blend fwd-decls, color-depth selection, mono snap, per-frame refresh dispatch.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitColorMonoRefresh(): string {
|
|
5
|
+
return `
|
|
6
|
+
|
|
7
|
+
// ── Color depth → blend/lerp selection ───────────────────────────────────────
|
|
8
|
+
// UI_COLOR_DEPTH is emitted by the UI emitter from the display profile's
|
|
9
|
+
// colorFormat (565 for TFT byte-identity, 888 for rgb666+). The value depth
|
|
10
|
+
// and the blend math MUST switch together: on 565, node fields hold 565 values
|
|
11
|
+
// and ui_blend565 is correct; on 888, node fields hold 888 values and
|
|
12
|
+
// ui_blend888 is correct. Forward-declare the four functions and define the
|
|
13
|
+
// ui_blend/UI_LERP_COLOR macros here (before any call site) so they resolve
|
|
14
|
+
// everywhere; the function bodies are defined later in this header.
|
|
15
|
+
#ifndef UI_COLOR_DEPTH
|
|
16
|
+
#define UI_COLOR_DEPTH 565
|
|
17
|
+
#endif
|
|
18
|
+
// Color value type tracks the depth: 888 holds 24-bit RGB (R<<16|G<<8|B),
|
|
19
|
+
// 565 holds 16-bit. Draw wrappers and locals use UI_COLOR_T so 888 is not
|
|
20
|
+
// narrowed before reaching the HAL. Under 565/mono this is uint16_t and the
|
|
21
|
+
// emitted code is byte-identical with the pre-widening runtime.
|
|
22
|
+
#if UI_COLOR_DEPTH == 888
|
|
23
|
+
#ifndef UI_COLOR_T
|
|
24
|
+
#define UI_COLOR_T uint32_t
|
|
25
|
+
#endif
|
|
26
|
+
#ifndef UI_DIM_MASK
|
|
27
|
+
#define UI_DIM_MASK 0x7F7F7Fu // halve each 8-bit channel independently
|
|
28
|
+
#endif
|
|
29
|
+
#else
|
|
30
|
+
#ifndef UI_COLOR_T
|
|
31
|
+
#define UI_COLOR_T uint16_t
|
|
32
|
+
#endif
|
|
33
|
+
#ifndef UI_DIM_MASK
|
|
34
|
+
#define UI_DIM_MASK 0x7BEFu // 565 dim mask (top bit clear per channel)
|
|
35
|
+
#endif
|
|
36
|
+
#endif
|
|
37
|
+
static inline uint16_t ui_blend565(uint16_t fg, uint16_t bg, uint8_t opacity);
|
|
38
|
+
static inline uint32_t ui_blend888(uint32_t fg, uint32_t bg, uint8_t opacity);
|
|
39
|
+
static inline uint16_t lerp_color(uint16_t a, uint16_t b, uint8_t k100);
|
|
40
|
+
static inline uint32_t lerp_color_888(uint32_t a, uint32_t b, uint8_t k100);
|
|
41
|
+
#if UI_COLOR_DEPTH == 888
|
|
42
|
+
#define ui_blend(fg, bg, op) ui_blend888((uint32_t)(fg), (uint32_t)(bg), (op))
|
|
43
|
+
#define UI_LERP_COLOR(a, b, k) lerp_color_888((uint32_t)(a), (uint32_t)(b), (k))
|
|
44
|
+
#else
|
|
45
|
+
#define ui_blend(fg, bg, op) ui_blend565((uint16_t)(fg), (uint16_t)(bg), (op))
|
|
46
|
+
#define UI_LERP_COLOR(a, b, k) lerp_color((uint16_t)(a), (uint16_t)(b), (k))
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
// ── 1-bit mono snap (UI_NATIVE_MONO) ─────────────────────────────────────────
|
|
50
|
+
// On a B&W e-ink panel, any color value must resolve to black or white. Node
|
|
51
|
+
// fields are pre-snapped at transpile, but blended/lerped runtime values
|
|
52
|
+
// (opacity, shadows, gradients) need a defensive snap in the draw path. The
|
|
53
|
+
// macro is a no-op on color targets so TFT output is byte-identical.
|
|
54
|
+
#ifdef UI_NATIVE_MONO
|
|
55
|
+
// Snap an RGB888 value to 1-bit mono (white/black) by luminance.
|
|
56
|
+
static inline uint32_t ui_snap_mono(uint32_t c) {
|
|
57
|
+
uint8_t r = (c >> 16) & 0xff, g = (c >> 8) & 0xff, b = c & 0xff;
|
|
58
|
+
// Match the transpile-time toMono threshold: (0.299r + 0.587g + 0.114b)/255 >= 0.27.
|
|
59
|
+
// Integer form: 299r+587g+114b >= 68850 (= 0.27*255*1000). Verified 0 mismatches.
|
|
60
|
+
return ((uint32_t)(299 * r + 587 * g + 114 * b) >= 68850u) ? 0xffffffu : 0x000000u;
|
|
61
|
+
}
|
|
62
|
+
// Snap an RGB565 value to 1-bit mono. Mono panels run at UI_COLOR_DEPTH 565, so
|
|
63
|
+
// node fields hold 565 values; reconstruct 8-bit channels then apply the threshold.
|
|
64
|
+
// Pre-snapped values (0x0000/0x0001/0xffff) pass through directly — the
|
|
65
|
+
// transpiler resolves #ffffff/#000000 to 0x0001/0x0000 on mono targets.
|
|
66
|
+
static inline uint16_t ui_snap_mono565(uint16_t c) {
|
|
67
|
+
if (c == 0x0000u) return 0x0000u;
|
|
68
|
+
if (c == 0x0001u || c == 0xffffu) return 0xffffu;
|
|
69
|
+
uint8_t r5 = (c >> 11) & 0x1f, g6 = (c >> 5) & 0x3f, b5 = c & 0x1f;
|
|
70
|
+
uint8_t r = (r5 << 3) | (r5 >> 2), g = (g6 << 2) | (g6 >> 4), b = (b5 << 3) | (b5 >> 2);
|
|
71
|
+
return ((uint32_t)(299 * r + 587 * g + 114 * b) >= 68850u) ? 0xffffu : 0x0000u;
|
|
72
|
+
}
|
|
73
|
+
#define UI_MAYBE_SNAP_MONO(c) (ui_snap_mono((uint32_t)(c)))
|
|
74
|
+
#define UI_MAYBE_SNAP_MONO565(c) (ui_snap_mono565((uint16_t)(c)))
|
|
75
|
+
#else
|
|
76
|
+
#define UI_MAYBE_SNAP_MONO(c) (c)
|
|
77
|
+
#define UI_MAYBE_SNAP_MONO565(c) (c)
|
|
78
|
+
#endif
|
|
79
|
+
|
|
80
|
+
// ── Per-frame refresh dispatch ──────────────────────────────────────────────
|
|
81
|
+
// Three mutually-exclusive compile-time paths, in priority order:
|
|
82
|
+
// 1. UI_REQUIRES_BACKING_STORE (e-ink): dirty-rect accumulator + partial refresh.
|
|
83
|
+
// 2. UI_BATCH_SPI_WRITES (TFT immediate): one startWrite/endWrite per frame.
|
|
84
|
+
// 3. default (SDL native host): no-ops.
|
|
85
|
+
// Exactly one branch ever compiles — the emitter's guards ensure the first two
|
|
86
|
+
// are never both defined (UI_REQUIRES_BACKING_STORE ⟹ requiresBackingStore,
|
|
87
|
+
// UI_BATCH_SPI_WRITES ⟹ immediate && !requiresBackingStore).
|
|
88
|
+
#if defined(UI_REQUIRES_BACKING_STORE)
|
|
89
|
+
// e-ink / deferred-partial: dirty-rect accumulator. Each painted node reports
|
|
90
|
+
// its paint rect; at frame end the union is refreshed as one partial update
|
|
91
|
+
// via display_partial_refresh.
|
|
92
|
+
#define UI_REFRESH_MAX_RECTS 16
|
|
93
|
+
struct UIRect16 { int16_t x, y, w, h; };
|
|
94
|
+
static UIRect16 __ui_refresh_rects[UI_REFRESH_MAX_RECTS];
|
|
95
|
+
static uint8_t __ui_refresh_rect_n = 0;
|
|
96
|
+
static inline void ui_refresh_begin_frame() { __ui_refresh_rect_n = 0; }
|
|
97
|
+
static inline void ui_refresh_add_rect(int16_t x, int16_t y, int16_t w, int16_t h) {
|
|
98
|
+
if (w <= 0 || h <= 0) return;
|
|
99
|
+
if (__ui_refresh_rect_n < UI_REFRESH_MAX_RECTS) {
|
|
100
|
+
__ui_refresh_rects[__ui_refresh_rect_n].x = x;
|
|
101
|
+
__ui_refresh_rects[__ui_refresh_rect_n].y = y;
|
|
102
|
+
__ui_refresh_rects[__ui_refresh_rect_n].w = w;
|
|
103
|
+
__ui_refresh_rects[__ui_refresh_rect_n].h = h;
|
|
104
|
+
__ui_refresh_rect_n++;
|
|
105
|
+
}
|
|
106
|
+
// TODO Phase 5: coalesce overlapping rects into a tighter union; cap by
|
|
107
|
+
// refresh budget; trigger a periodic full refresh for ghost clearing.
|
|
108
|
+
}
|
|
109
|
+
// Union all accumulated rects and issue one partial refresh of the bounding
|
|
110
|
+
// region via the shim's display_partial_refresh entry point.
|
|
111
|
+
static inline void ui_refresh_flush() {
|
|
112
|
+
if (__ui_refresh_rect_n == 0) return;
|
|
113
|
+
int16_t x0 = 32767, y0 = 32767, x1 = -32768, y1 = -32768;
|
|
114
|
+
for (uint8_t i = 0; i < __ui_refresh_rect_n; i++) {
|
|
115
|
+
const UIRect16& r = __ui_refresh_rects[i];
|
|
116
|
+
if (r.x < x0) x0 = r.x;
|
|
117
|
+
if (r.y < y0) y0 = r.y;
|
|
118
|
+
int16_t rx1 = (int16_t)(r.x + r.w), ry1 = (int16_t)(r.y + r.h);
|
|
119
|
+
if (rx1 > x1) x1 = rx1;
|
|
120
|
+
if (ry1 > y1) y1 = ry1;
|
|
121
|
+
}
|
|
122
|
+
if (x0 < 0) x0 = 0;
|
|
123
|
+
if (y0 < 0) y0 = 0;
|
|
124
|
+
if (x1 > display_width()) x1 = display_width();
|
|
125
|
+
if (y1 > display_height()) y1 = display_height();
|
|
126
|
+
if (x1 > x0 && y1 > y0) {
|
|
127
|
+
display_partial_refresh(x0, y0, (int16_t)(x1 - x0), (int16_t)(y1 - y0));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
#elif defined(UI_BATCH_SPI_WRITES)
|
|
131
|
+
// TFT immediate-refresh batching (CURRENTLY UNUSED — see note below).
|
|
132
|
+
// Wraps the frame's draws in ONE SPI transaction so all per-node writes share
|
|
133
|
+
// a single CS-asserted burst. add_rect is a no-op: TFT has no partial-refresh
|
|
134
|
+
// concept; the per-node draws already target the right pixels.
|
|
135
|
+
//
|
|
136
|
+
// NOTE: this branch is left in place but the emitter does NOT define
|
|
137
|
+
// UI_BATCH_SPI_WRITES by default. The design assumed Adafruit_SPITFT's
|
|
138
|
+
// startWrite/endWrite are reference-counted (nested calls = no-op for CS),
|
|
139
|
+
// but the Adafruit_GFX version in this repo is NOT — every endWrite raises
|
|
140
|
+
// CS unconditionally. So an outer frame startWrite gets closed by the first
|
|
141
|
+
// inner draw's endWrite → black screen. Re-enable only after either (a)
|
|
142
|
+
// upgrading to a ref-counted Adafruit_GFX or (b) adding a runtime flag the
|
|
143
|
+
// inner draw primitives check to skip their own startWrite/endWrite.
|
|
144
|
+
// See docs/superpowers/specs/2026-07-06-tft-spi-write-batching-design.md.
|
|
145
|
+
static inline void ui_refresh_begin_frame() { display_startWrite(); }
|
|
146
|
+
static inline void ui_refresh_add_rect(int16_t x, int16_t y, int16_t w, int16_t h) { (void)x; (void)y; (void)w; (void)h; }
|
|
147
|
+
static inline void ui_refresh_flush() { display_endWrite(); }
|
|
148
|
+
#else
|
|
149
|
+
// No batching (e.g. SDL native host render): true no-ops.
|
|
150
|
+
#define ui_refresh_begin_frame() ((void)0)
|
|
151
|
+
#define ui_refresh_add_rect(x, y, w, h) ((void)0)
|
|
152
|
+
#define ui_refresh_flush() ((void)0)
|
|
153
|
+
#endif`;
|
|
154
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 1548-1710).
|
|
2
|
+
// mark_dirty, subtree dirty, scroll direct prepare/compositor, scroll view dirty/overlap/invalidate.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitDirtyScrollMutators(): string {
|
|
5
|
+
return `
|
|
6
|
+
static inline void ui_mark_dirty(uint16_t nodeIdx) {
|
|
7
|
+
if (nodeIdx >= __ui_node_count) return;
|
|
8
|
+
__ui_nodes[nodeIdx].dirty = 1;
|
|
9
|
+
if (ui_is_effectively_visible(nodeIdx) &&
|
|
10
|
+
__ui_nodes[nodeIdx].screenId == __ui_active_screen) {
|
|
11
|
+
UIRect r;
|
|
12
|
+
ui_node_current_paint_rect(nodeIdx, &r);
|
|
13
|
+
if (r.w <= 0 || r.h <= 0) {
|
|
14
|
+
ui_mark_overlapping_higher_layers_dirty(nodeIdx);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
18
|
+
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
19
|
+
if (__ui_nodes[p].scrollable &&
|
|
20
|
+
!__ui_nodes[p].virtualized &&
|
|
21
|
+
__ui_nodes[p].contentHeight > __ui_nodes[p].box.h) {
|
|
22
|
+
UIRect clip = {
|
|
23
|
+
__ui_nodes[p].box.x,
|
|
24
|
+
__ui_nodes[p].box.y,
|
|
25
|
+
__ui_nodes[p].box.w,
|
|
26
|
+
__ui_nodes[p].box.h
|
|
27
|
+
};
|
|
28
|
+
if (!ui_rects_intersect(r.x, r.y, r.w, r.h, clip.x, clip.y, clip.w, clip.h)) {
|
|
29
|
+
__ui_nodes[nodeIdx].dirty = 0;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (r.x < clip.x ||
|
|
33
|
+
r.x + r.w > clip.x + clip.w ||
|
|
34
|
+
r.y < clip.y ||
|
|
35
|
+
r.y + r.h > clip.y + clip.h) {
|
|
36
|
+
ui_mark_scroll_view_dirty(p);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Fully inside the viewport, but overflow scroll content is canvas-
|
|
40
|
+
// composited. Promote to a scroll repaint — otherwise the defer path
|
|
41
|
+
// drops the child's dirty flag without drawing (e.g. :pressed buttons).
|
|
42
|
+
ui_mark_scroll_view_dirty(p);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
p = __ui_nodes[p].parent;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
ui_mark_overlapping_higher_layers_dirty(nodeIdx);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Set dirty=1 across a scroll subtree WITHOUT the per-child O(n) overlap repair.
|
|
52
|
+
// During scroll the subtree repaints into a freshly-cleared off-screen canvas, so
|
|
53
|
+
// intra-subtree overlap repair is pointless, and scroll children are draw-clipped
|
|
54
|
+
// to the container's viewport box — so all external higher-z neighbors of the
|
|
55
|
+
// viewport are covered by ONE overlap check at the container's paint rect (done by
|
|
56
|
+
// the caller). This drops scroll marking from O(K·n) to O(K + n).
|
|
57
|
+
static inline void ui_mark_subtree_dirty_local(uint16_t scrollNode) {
|
|
58
|
+
if (scrollNode >= __ui_node_count) return;
|
|
59
|
+
for (uint16_t c = scrollNode + 1; c < __ui_nodes[scrollNode].subtreeEnd; c++) {
|
|
60
|
+
__ui_nodes[c].dirty = 1;
|
|
61
|
+
}
|
|
62
|
+
__ui_nodes[scrollNode].dirty = 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Mode C strip-only: direct-partial scroll when no viewport canvas fits and the
|
|
66
|
+
// scroll delta is small. Fills only the exposed strip on the display, then marks
|
|
67
|
+
// visible descendants dirty for direct draw — never clears the whole viewport.
|
|
68
|
+
static inline void ui_scroll_direct_prepare(uint16_t s, int16_t* outVX, int16_t* outVY) {
|
|
69
|
+
int16_t vw = __ui_nodes[s].box.w;
|
|
70
|
+
int16_t vh = __ui_nodes[s].box.h;
|
|
71
|
+
int16_t vox = __ui_nodes[s].box.x;
|
|
72
|
+
int16_t voy = __ui_nodes[s].box.y;
|
|
73
|
+
UI_COLOR_T scrollBg = __ui_nodes[s].hasBg ? __ui_nodes[s].bg : __ui_nodes[s].clearColor;
|
|
74
|
+
int16_t deltaY = __ui_nodes[s].scrollY - __ui_nodes[s].lastPaintedScrollY;
|
|
75
|
+
int16_t absDelta = deltaY < 0 ? -deltaY : deltaY;
|
|
76
|
+
int16_t stripY = deltaY > 0 ? (int16_t)(vh - absDelta) : 0;
|
|
77
|
+
ui_display_fill_rect(vox, (int16_t)(voy + stripY), vw, absDelta, scrollBg);
|
|
78
|
+
for (uint16_t c = s + 1; c < __ui_nodes[s].subtreeEnd; c++) {
|
|
79
|
+
if (!ui_is_effectively_visible(c) || __ui_nodes[c].screenId != __ui_active_screen) {
|
|
80
|
+
__ui_nodes[c].dirty = 0;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
__ui_nodes[c].dirty = 1;
|
|
84
|
+
if (__ui_nodes[c].kind == NODE_PROGRESS || __ui_nodes[c].kind == NODE_RANGE) {
|
|
85
|
+
__ui_nodes[c].lastTextWidth = -1;
|
|
86
|
+
}
|
|
87
|
+
__ui_nodes[c].lastTextHeight = 0;
|
|
88
|
+
}
|
|
89
|
+
__ui_nodes[s].dirty = 0;
|
|
90
|
+
if (outVX) *outVX = vox;
|
|
91
|
+
if (outVY) *outVY = voy;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Nearest overflow scroll container owning nodeIdx (or nodeIdx itself).
|
|
95
|
+
static inline int16_t ui_overflow_scroll_compositor(uint16_t nodeIdx) {
|
|
96
|
+
if (nodeIdx >= __ui_node_count) return -1;
|
|
97
|
+
if (__ui_nodes[nodeIdx].scrollable && !__ui_nodes[nodeIdx].virtualized &&
|
|
98
|
+
__ui_nodes[nodeIdx].contentHeight > __ui_nodes[nodeIdx].box.h) {
|
|
99
|
+
return (int16_t)nodeIdx;
|
|
100
|
+
}
|
|
101
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
102
|
+
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
103
|
+
if (__ui_nodes[p].scrollable && !__ui_nodes[p].virtualized &&
|
|
104
|
+
__ui_nodes[p].contentHeight > __ui_nodes[p].box.h) {
|
|
105
|
+
return (int16_t)p;
|
|
106
|
+
}
|
|
107
|
+
p = __ui_nodes[p].parent;
|
|
108
|
+
}
|
|
109
|
+
return -1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static inline void ui_mark_scroll_view_overlaps_dirty(uint16_t scrollNode) {
|
|
113
|
+
if (scrollNode >= __ui_node_count) return;
|
|
114
|
+
if (!ui_is_effectively_visible(scrollNode)) return;
|
|
115
|
+
if (__ui_nodes[scrollNode].screenId != __ui_active_screen) return;
|
|
116
|
+
UIRect r;
|
|
117
|
+
ui_node_current_paint_rect(scrollNode, &r);
|
|
118
|
+
if (r.w <= 0 || r.h <= 0) return;
|
|
119
|
+
for (uint16_t c = 0; c < __ui_node_count; c++) {
|
|
120
|
+
if (c == scrollNode) continue;
|
|
121
|
+
if (c > scrollNode && c < __ui_nodes[scrollNode].subtreeEnd) continue;
|
|
122
|
+
if (__ui_nodes[c].dirty) continue;
|
|
123
|
+
if (!ui_is_effectively_visible(c)) continue;
|
|
124
|
+
if (__ui_nodes[c].screenId != __ui_active_screen) continue;
|
|
125
|
+
if (!ui_node_draws_before(scrollNode, c)) continue;
|
|
126
|
+
UIRect cr;
|
|
127
|
+
ui_node_current_paint_rect(c, &cr);
|
|
128
|
+
if (cr.w <= 0 || cr.h <= 0) continue;
|
|
129
|
+
if (ui_rects_intersect(r.x, r.y, r.w, r.h, cr.x, cr.y, cr.w, cr.h)) {
|
|
130
|
+
__ui_nodes[c].dirty = 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static inline void ui_mark_scroll_subtree_dirty(uint16_t scrollNode) {
|
|
136
|
+
ui_mark_subtree_dirty_local(scrollNode);
|
|
137
|
+
// Single overlap check at the container's paint rect covers every external
|
|
138
|
+
// higher-z neighbor of the viewport. The container index is the right one to
|
|
139
|
+
// pass: scroll content lives within the container's stacking context, so
|
|
140
|
+
// ui_node_draws_before(scrollNode, c) selects exactly the external layers that
|
|
141
|
+
// should be repaired when the viewport is repainted.
|
|
142
|
+
ui_mark_overlapping_higher_layers_dirty(scrollNode);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static inline void ui_mark_scroll_view_dirty(uint16_t scrollNode) {
|
|
146
|
+
if (scrollNode >= __ui_node_count) return;
|
|
147
|
+
__ui_nodes[scrollNode].dirty = 1;
|
|
148
|
+
ui_mark_scroll_view_overlaps_dirty(scrollNode);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static inline int16_t ui_scroll_ancestor_for_node(uint16_t nodeIdx) {
|
|
152
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
153
|
+
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
154
|
+
if (__ui_nodes[p].scrollable) return (int16_t)p;
|
|
155
|
+
p = __ui_nodes[p].parent;
|
|
156
|
+
}
|
|
157
|
+
return -1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static inline void ui_invalidate_scroll_canvas_for_node(uint16_t nodeIdx) {
|
|
161
|
+
int16_t scrollParent = ui_scroll_ancestor_for_node(nodeIdx);
|
|
162
|
+
if (scrollParent < 0) return;
|
|
163
|
+
if (__ui_nodes[scrollParent].virtualized) return;
|
|
164
|
+
if (__ui_nodes[scrollParent].contentHeight <= __ui_nodes[scrollParent].box.h) return;
|
|
165
|
+
int16_t span = __ui_nodes[scrollParent].box.h > 0 ? __ui_nodes[scrollParent].box.h : 1;
|
|
166
|
+
__ui_nodes[scrollParent].lastPaintedScrollY = __ui_nodes[scrollParent].scrollY - span;
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 902-999).
|
|
2
|
+
// ui_display_* target/pixel/bitmap/rect/cursor/text shim layer.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitDisplayShim(): string {
|
|
5
|
+
return `
|
|
6
|
+
|
|
7
|
+
static inline CuttlefishDisplayTarget* ui_display_get_target() { return __ui_gfx; }
|
|
8
|
+
static inline void ui_display_set_target(CuttlefishDisplayTarget* target) {
|
|
9
|
+
__ui_gfx = target ? target : display_defaultTarget();
|
|
10
|
+
}
|
|
11
|
+
static inline void ui_display_use_default_target() {
|
|
12
|
+
__ui_gfx = display_defaultTarget();
|
|
13
|
+
}
|
|
14
|
+
static inline uint8_t ui_display_is_default_target() {
|
|
15
|
+
return __ui_gfx == display_defaultTarget();
|
|
16
|
+
}
|
|
17
|
+
static inline int16_t ui_display_target_width() {
|
|
18
|
+
return display_targetWidth(__ui_gfx);
|
|
19
|
+
}
|
|
20
|
+
static inline int16_t ui_display_target_height() {
|
|
21
|
+
return display_targetHeight(__ui_gfx);
|
|
22
|
+
}
|
|
23
|
+
static inline int16_t ui_clamp_i16(int16_t value, int16_t lo, int16_t hi) {
|
|
24
|
+
if (value < lo) return lo;
|
|
25
|
+
if (value > hi) return hi;
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
static inline void ui_display_target_bounds(int16_t* left, int16_t* top, int16_t* right, int16_t* bottom) {
|
|
29
|
+
if (left) *left = (int16_t)(-__ui_draw_off_x);
|
|
30
|
+
if (top) *top = (int16_t)(-__ui_draw_off_y);
|
|
31
|
+
if (right) *right = (int16_t)(ui_display_target_width() - __ui_draw_off_x);
|
|
32
|
+
if (bottom) *bottom = (int16_t)(ui_display_target_height() - __ui_draw_off_y);
|
|
33
|
+
}
|
|
34
|
+
static inline uint8_t ui_clip_rect_to_display_target(int16_t* x, int16_t* y, int16_t* w, int16_t* h) {
|
|
35
|
+
if (!x || !y || !w || !h || *w <= 0 || *h <= 0) return 0;
|
|
36
|
+
int16_t left, top, right, bottom;
|
|
37
|
+
ui_display_target_bounds(&left, &top, &right, &bottom);
|
|
38
|
+
int16_t x0 = *x > left ? *x : left;
|
|
39
|
+
int16_t y0 = *y > top ? *y : top;
|
|
40
|
+
int16_t x1 = (int16_t)(*x + *w);
|
|
41
|
+
int16_t y1 = (int16_t)(*y + *h);
|
|
42
|
+
if (x1 > right) x1 = right;
|
|
43
|
+
if (y1 > bottom) y1 = bottom;
|
|
44
|
+
if (x0 >= x1 || y0 >= y1) return 0;
|
|
45
|
+
*x = x0;
|
|
46
|
+
*y = y0;
|
|
47
|
+
*w = (int16_t)(x1 - x0);
|
|
48
|
+
*h = (int16_t)(y1 - y0);
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
static inline void ui_display_draw_pixel(int16_t x, int16_t y, UI_COLOR_T color) {
|
|
52
|
+
display_targetDrawPixel(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, UI_MAYBE_SNAP_MONO565(color));
|
|
53
|
+
}
|
|
54
|
+
static inline void ui_display_draw_rgb_bitmap(int16_t x, int16_t y, const UI_COLOR_T* bitmap, int16_t w, int16_t h) {
|
|
55
|
+
display_targetDrawRGBBitmap(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, bitmap, w, h);
|
|
56
|
+
}
|
|
57
|
+
static inline void ui_display_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, UI_COLOR_T color) {
|
|
58
|
+
display_targetFillRect(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, w, h, UI_MAYBE_SNAP_MONO565(color));
|
|
59
|
+
}
|
|
60
|
+
static inline void ui_display_draw_fast_hline(int16_t x, int16_t y, int16_t w, UI_COLOR_T color) {
|
|
61
|
+
display_targetDrawFastHLine(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, w, UI_MAYBE_SNAP_MONO565(color));
|
|
62
|
+
}
|
|
63
|
+
static inline void ui_display_draw_fast_vline(int16_t x, int16_t y, int16_t h, UI_COLOR_T color) {
|
|
64
|
+
display_targetDrawFastVLine(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, h, UI_MAYBE_SNAP_MONO565(color));
|
|
65
|
+
}
|
|
66
|
+
static inline void ui_display_fill_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, UI_COLOR_T color) {
|
|
67
|
+
display_targetFillRoundRect(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, w, h, r, UI_MAYBE_SNAP_MONO565(color));
|
|
68
|
+
}
|
|
69
|
+
static inline void ui_display_draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, UI_COLOR_T color) {
|
|
70
|
+
display_targetDrawRect(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, w, h, UI_MAYBE_SNAP_MONO565(color));
|
|
71
|
+
}
|
|
72
|
+
static inline void ui_display_draw_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, UI_COLOR_T color) {
|
|
73
|
+
display_targetDrawRoundRect(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, w, h, r, UI_MAYBE_SNAP_MONO565(color));
|
|
74
|
+
}
|
|
75
|
+
static inline void ui_display_draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, UI_COLOR_T color) {
|
|
76
|
+
display_targetDrawLine(__ui_gfx, x0 + __ui_draw_off_x, y0 + __ui_draw_off_y,
|
|
77
|
+
x1 + __ui_draw_off_x, y1 + __ui_draw_off_y, UI_MAYBE_SNAP_MONO565(color));
|
|
78
|
+
}
|
|
79
|
+
static inline void ui_display_fill_circle(int16_t x, int16_t y, int16_t r, UI_COLOR_T color) {
|
|
80
|
+
display_targetFillCircle(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, r, UI_MAYBE_SNAP_MONO565(color));
|
|
81
|
+
}
|
|
82
|
+
static inline void ui_display_draw_circle(int16_t x, int16_t y, int16_t r, UI_COLOR_T color) {
|
|
83
|
+
display_targetDrawCircle(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y, r, UI_MAYBE_SNAP_MONO565(color));
|
|
84
|
+
}
|
|
85
|
+
static inline void ui_display_set_cursor(int16_t x, int16_t y) {
|
|
86
|
+
display_targetSetCursor(__ui_gfx, x + __ui_draw_off_x, y + __ui_draw_off_y);
|
|
87
|
+
}
|
|
88
|
+
static inline void ui_display_set_text_color(UI_COLOR_T fg, UI_COLOR_T bg) {
|
|
89
|
+
display_targetSetTextColorBg(__ui_gfx, fg, bg);
|
|
90
|
+
}
|
|
91
|
+
static inline void ui_display_set_text_color_solid(UI_COLOR_T fg) {
|
|
92
|
+
display_targetSetTextColor(__ui_gfx, fg);
|
|
93
|
+
}
|
|
94
|
+
static inline void ui_display_set_text_size(uint8_t size) {
|
|
95
|
+
display_targetSetTextSize(__ui_gfx, size);
|
|
96
|
+
}
|
|
97
|
+
static inline void ui_display_set_text_wrap(bool wrap) {
|
|
98
|
+
display_targetSetTextWrap(__ui_gfx, wrap);
|
|
99
|
+
}
|
|
100
|
+
static inline void ui_display_print(const char* text) {
|
|
101
|
+
display_targetPrint(__ui_gfx, text);
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 622-752).
|
|
2
|
+
// navigate, canvas/scroll/text/paint/clip/AA/image forward declarations, persistent canvas globals.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitForwardDecls(): string {
|
|
5
|
+
return `
|
|
6
|
+
static uint8_t __ui_fade_opacity = 100; // fade-in animation (0=transparent, 100=full)
|
|
7
|
+
static uint16_t __ui_fade_elapsed = 0;
|
|
8
|
+
static uint16_t __ui_fade_duration = 200; // ms
|
|
9
|
+
|
|
10
|
+
// Early forward declaration: ui_navigate (below) calls ui_release_canvas_state
|
|
11
|
+
// and ui_set_pressed (defined later) during screen changes. Needed on native
|
|
12
|
+
// (single TU, no Arduino auto-prototyper).
|
|
13
|
+
static inline void ui_release_canvas_state();
|
|
14
|
+
static inline void ui_set_pressed(uint16_t nodeIdx, uint8_t pressed);
|
|
15
|
+
static inline void ui_refresh_active_screen_bg_node();
|
|
16
|
+
|
|
17
|
+
// Navigate to a screen by index. Marks the new screen's nodes dirty + starts fade.
|
|
18
|
+
static inline void ui_navigate(uint8_t screenIdx) {
|
|
19
|
+
if (screenIdx >= __ui_screen_count || screenIdx == __ui_active_screen) return;
|
|
20
|
+
__ui_active_screen = screenIdx;
|
|
21
|
+
ui_refresh_active_screen_bg_node();
|
|
22
|
+
// Reset scroll/touch state so the old screen's scroll container doesn't
|
|
23
|
+
// interfere with the new screen. Release any currently-pressed button FIRST:
|
|
24
|
+
// navigation fires from a button's click handler during touch-down, so the
|
|
25
|
+
// matching touch-up release would otherwise be suppressed by the reset below
|
|
26
|
+
// and the button would stay stuck in its :pressed color.
|
|
27
|
+
if (__ui_touch_node >= 0 && __ui_touch_node < __ui_node_count &&
|
|
28
|
+
__ui_nodes[__ui_touch_node].kind == NODE_BUTTON && __ui_nodes[__ui_touch_node].value != 0) {
|
|
29
|
+
ui_set_pressed((uint16_t)__ui_touch_node, 0);
|
|
30
|
+
}
|
|
31
|
+
__ui_scroll_node = -1;
|
|
32
|
+
__ui_touch_node = -1;
|
|
33
|
+
__ui_touch_state = 0;
|
|
34
|
+
__ui_kb_visible = 0;
|
|
35
|
+
// Free every persistent canvas so the new screen allocates into a clean,
|
|
36
|
+
// unfragmented heap. Without this, the previous screen's canvas buffer stays
|
|
37
|
+
// resident and fragments the heap, so the new screen's buffer can't get a
|
|
38
|
+
// contiguous block (the "works first, then blanks until reset" symptom).
|
|
39
|
+
ui_release_canvas_state();
|
|
40
|
+
// Clear the entire display so old screen content doesn't show. On deferred-
|
|
41
|
+
// refresh panels (e-ink) a full clear flashes, so skip it — the all-nodes-
|
|
42
|
+
// dirty marking below drives a full repaint via partial refresh instead.
|
|
43
|
+
#ifndef UI_REFRESH_DEFERRED
|
|
44
|
+
display_fillScreen(0x0000);
|
|
45
|
+
#endif
|
|
46
|
+
// Mark all nodes dirty so the new screen fully redraws.
|
|
47
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
48
|
+
__ui_nodes[i].dirty = 1;
|
|
49
|
+
__ui_nodes[i].lastTextHeight = 0;
|
|
50
|
+
__ui_nodes[i].layoutCacheKey = 0;
|
|
51
|
+
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) __ui_nodes[i].lastTextWidth = -1;
|
|
52
|
+
else __ui_nodes[i].lastTextWidth = 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
extern const uint16_t __ui_font_face_count;
|
|
56
|
+
|
|
57
|
+
#define UI_NO_PARENT 0xFFFF // sentinel: out of uint16_t node-index range
|
|
58
|
+
|
|
59
|
+
// Forward declarations suppress Arduino's auto-prototyper, which would insert
|
|
60
|
+
// prototypes before UIFontFace/UIFontGlyph are declared.
|
|
61
|
+
static inline const UIFontFace* ui_font_face(uint8_t id);
|
|
62
|
+
static inline const UIFontGlyph* ui_font_glyph(const UIFontFace* face, uint16_t codepoint);
|
|
63
|
+
static inline uint8_t ui_font_alpha_at(const UIFontFace* face, const UIFontGlyph* glyph, uint16_t pixelIndex);
|
|
64
|
+
static inline uint16_t ui_asset_text_width(const char* text, const UIFontFace* face);
|
|
65
|
+
static inline uint8_t ui_asset_text_height(const UIFontFace* face);
|
|
66
|
+
static inline uint8_t ui_draw_asset_text(const char* text, int16_t x, int16_t y, UI_COLOR_T fg, UI_COLOR_T bg, uint8_t antialias, uint8_t fontFace);
|
|
67
|
+
static inline uint16_t ui_text_width(const char* text, uint8_t ts, uint8_t fontFace, int8_t letterSpacing);
|
|
68
|
+
struct UITextLine;
|
|
69
|
+
static inline uint8_t ui_text_next_line(const char** cursor, uint16_t maxWidth, uint8_t whiteSpaceMode, uint8_t ts, uint8_t fontFace, int8_t letterSpacing, UITextLine* out);
|
|
70
|
+
static inline void ui_text_layout_metrics(const char* text, uint16_t maxWidth, uint8_t whiteSpaceMode, uint8_t ts, uint8_t fontFace, int8_t letterSpacing, uint8_t lineHeight, uint16_t* outW, uint16_t* outH);
|
|
71
|
+
static inline void ui_invalidate_text_layout_cache(uint16_t nodeIdx);
|
|
72
|
+
static inline uint16_t ui_node_text_max_width(uint16_t nodeIdx);
|
|
73
|
+
static inline void ui_node_text_layout_metrics(uint16_t nodeIdx, uint16_t textMaxW, uint16_t* outW, uint16_t* outH);
|
|
74
|
+
static inline uint8_t ui_rects_intersect(int16_t ax, int16_t ay, int16_t aw, int16_t ah, int16_t bx, int16_t by, int16_t bw, int16_t bh);
|
|
75
|
+
static inline uint8_t ui_is_effectively_visible(uint16_t nodeIdx);
|
|
76
|
+
static inline uint8_t ui_node_draws_before(uint16_t a, uint16_t b);
|
|
77
|
+
static inline void ui_node_paint_rect(uint16_t nodeIdx, int16_t baseX, int16_t baseY, int16_t drawX, int16_t drawY, uint16_t textW, uint16_t textH, UIRect* out);
|
|
78
|
+
static inline void ui_node_current_paint_rect(uint16_t nodeIdx, UIRect* out);
|
|
79
|
+
static inline uint8_t ui_subtree_current_paint_rect(uint16_t nodeIdx, UIRect* out);
|
|
80
|
+
static inline void ui_mark_overlapping_higher_layers_dirty(uint16_t nodeIdx);
|
|
81
|
+
static inline void ui_mark_overlapping_higher_layers_dirty_for_rect(uint16_t nodeIdx, const UIRect* r);
|
|
82
|
+
static inline void ui_mark_scroll_view_dirty(uint16_t scrollNode);
|
|
83
|
+
static inline void ui_release_canvas_state();
|
|
84
|
+
static inline void ui_set_visible(uint16_t nodeIdx, uint8_t visible);
|
|
85
|
+
static inline void ui_invalidate_scroll_canvas_for_node(uint16_t nodeIdx);
|
|
86
|
+
static inline uint8_t ui_clip_rect_to_rect(UIRect* r, const UIRect* clip);
|
|
87
|
+
static inline void ui_fill_rect_clipped(int16_t x, int16_t y, int16_t w, int16_t h, const UIRect* clip, UI_COLOR_T color);
|
|
88
|
+
static inline void ui_hline_clipped(int16_t x, int16_t y, int16_t w, const UIRect* clip, UI_COLOR_T color);
|
|
89
|
+
static inline void ui_vline_clipped(int16_t x, int16_t y, int16_t h, const UIRect* clip, UI_COLOR_T color);
|
|
90
|
+
static inline void ui_draw_rect_outline_clipped(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t style, uint8_t width, const UIRect* clip, UI_COLOR_T color);
|
|
91
|
+
static inline void ui_draw_node_decoration_clipped(uint16_t nodeIdx, int16_t drawY, const UIRect* clip);
|
|
92
|
+
// Forward declarations for functions used before their definition in the
|
|
93
|
+
// single native translation unit (Arduino's auto-prototyper hides this;
|
|
94
|
+
// native emits one TU so explicit forwards are needed).
|
|
95
|
+
static inline int8_t ui_rich_link_hit(uint16_t nodeIdx, int16_t px, int16_t py);
|
|
96
|
+
static inline CuttlefishCanvas16* ui_aa_begin(int16_t w, int16_t h, UI_COLOR_T bg);
|
|
97
|
+
static inline void ui_aa_end(CuttlefishCanvas16* c);
|
|
98
|
+
static inline void ui_aa_push(CuttlefishCanvas16* c, int16_t dx, int16_t dy);
|
|
99
|
+
static inline void ui_aa_line(CuttlefishCanvas16* c, float x0, float y0, float x1, float y1, UI_COLOR_T color);
|
|
100
|
+
static inline void ui_aa_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, float r, UI_COLOR_T color);
|
|
101
|
+
static inline void ui_aa_fill_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, float r, UI_COLOR_T color);
|
|
102
|
+
static inline uint8_t ui_repair_current_node_paint_with_parent(uint16_t nodeIdx, UIRect* r);
|
|
103
|
+
static inline void ui_clear_node_paint_rect(uint16_t nodeIdx, const UIRect* paintRect);
|
|
104
|
+
static inline uint8_t ui_try_repair_geometry_fill(uint16_t nodeIdx, const UIRect* oldRect);
|
|
105
|
+
static inline void ui_draw_node_border(uint16_t i, int16_t drawX, int16_t drawY, UI_COLOR_T color);
|
|
106
|
+
static inline void ui_draw_node_outline(uint16_t i, int16_t drawX, int16_t drawY);
|
|
107
|
+
static inline void ui_draw_gradient_fill(uint16_t i, int16_t drawY);
|
|
108
|
+
static inline uint8_t ui_rotation_quadrant(int16_t deg);
|
|
109
|
+
static inline int16_t ui_rotated_face_w(uint16_t nodeIdx, int16_t w, int16_t h);
|
|
110
|
+
static inline int16_t ui_rotated_face_h(uint16_t nodeIdx, int16_t w, int16_t h);
|
|
111
|
+
static inline void ui_draw_image_rotated(const UIImage* img, int16_t x, int16_t y, int16_t rotateDeg);
|
|
112
|
+
static inline void ui_draw_image_with_fit(const UIImage* img, int16_t x, int16_t y, int16_t rotateDeg,
|
|
113
|
+
uint8_t fitMode, int16_t targetW, int16_t targetH);
|
|
114
|
+
static inline void ui_draw_scaled_image(const UIImage* img, int16_t x, int16_t y, int16_t rotateDeg,
|
|
115
|
+
int16_t drawW, int16_t drawH);
|
|
116
|
+
|
|
117
|
+
static CuttlefishDisplayTarget* __ui_gfx = display_defaultTarget();
|
|
118
|
+
// Persistent canvas slots, all freed on screen change (ui_navigate) so each
|
|
119
|
+
// screen starts with a clean heap. Without this, the first screen's canvas
|
|
120
|
+
// buffer stays resident and fragments the heap, so a later screen's buffer
|
|
121
|
+
// can't get a contiguous block — manifesting as that element blanking until a
|
|
122
|
+
// hard reset. File-scoped (not function-static) so ui_release_canvas_state can
|
|
123
|
+
// reach them.
|
|
124
|
+
static CuttlefishCanvas16* __ui_container_canvas = nullptr; // Mode B shift-and-repair
|
|
125
|
+
static CuttlefishCanvas16* __ui_list_canvas = nullptr; // virtualized <list> viewport
|
|
126
|
+
static int16_t __ui_list_canvas_node = -1; // node currently represented by __ui_list_canvas
|
|
127
|
+
static CuttlefishCanvas16* __ui_node_canvas = nullptr; // <canvas> element offscreen
|
|
128
|
+
static CuttlefishCanvas16* __ui_repair_canvas = nullptr; // buffered-paint / exposed-strip
|
|
129
|
+
static int16_t __ui_canvas_fallback_w = 0; // dimensions for direct <canvas> fallback
|
|
130
|
+
static int16_t __ui_canvas_fallback_h = 0;
|
|
131
|
+
// Draw offset: normally zero. The <canvas> allocation fallback sets it so
|
|
132
|
+
// callback-local coordinates draw into the node's current display/canvas target.
|
|
133
|
+
static int16_t __ui_draw_off_x = 0;
|
|
134
|
+
static int16_t __ui_draw_off_y = 0;
|
|
135
|
+
|
|
136
|
+
// Release every persistent canvas so the next screen allocates into a clean`;
|
|
137
|
+
}
|