@typecad/ui 1.0.0-alpha.7 → 1.0.0-alpha.9
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/README.md +6 -0
- package/dist/engine-index.js +0 -2
- package/dist/preview/host-ui-runtime.d.ts +1 -0
- package/dist/preview/host-ui-runtime.js +56 -11
- package/dist/ui-engine/font-assets.d.ts +8 -0
- package/dist/ui-engine/font-assets.js +13 -0
- package/dist/ui-engine/layout-engine.js +19 -6
- package/dist/ui-engine/model.d.ts +1 -1
- package/dist/ui-engine/model.js +10 -4
- package/dist/ui-engine/runtime-header/antialiasing.js +52 -32
- package/dist/ui-engine/runtime-header/blend-bodies.js +4 -4
- package/dist/ui-engine/runtime-header/canvas-helpers.js +32 -65
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +22 -10
- package/dist/ui-engine/runtime-header/color-mono-refresh.js +10 -10
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +59 -29
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +26 -10
- package/dist/ui-engine/runtime-header/display-shim.js +8 -8
- package/dist/ui-engine/runtime-header/forward-decls.js +48 -6
- package/dist/ui-engine/runtime-header/image-drawing.js +154 -84
- package/dist/ui-engine/runtime-header/init-press-input.js +20 -5
- package/dist/ui-engine/runtime-header/keyboard.js +19 -16
- package/dist/ui-engine/runtime-header/node-decoration.js +8 -8
- package/dist/ui-engine/runtime-header/node-draw-body.d.ts +1 -0
- package/dist/ui-engine/runtime-header/node-draw-body.js +1402 -0
- package/dist/ui-engine/runtime-header/paint-order-coords.js +48 -18
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +38 -17
- package/dist/ui-engine/runtime-header/scroll-physics.js +15 -14
- package/dist/ui-engine/runtime-header/state-bindings-nav.js +23 -13
- package/dist/ui-engine/runtime-header/structs.js +12 -10
- package/dist/ui-engine/runtime-header/text-rendering.js +132 -71
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +29 -10
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +299 -747
- package/dist/ui-engine/runtime-header/tick/flush-phase.js +7 -3
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +19 -3
- package/dist/ui-engine/runtime-header/tick/transitions-phase.js +31 -24
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +42 -40
- package/dist/ui-engine/runtime-header/types-defines.js +23 -3
- package/dist/ui-engine/runtime-header.js +2 -0
- package/dist/ui-engine/transpile-ui.js +2 -2
- package/dist/ui-engine/ua-stylesheet.js +46 -3
- package/dist/ui-engine/ui-lowering.d.ts +1 -3
- package/dist/ui-engine/ui-lowering.js +4 -23
- package/dist/ui-engine/ui-registry.js +1 -7
- package/package.json +2 -2
- package/src/engine-index.ts +0 -2
- package/src/preview/host-ui-runtime.ts +56 -12
- package/src/ui-engine/font-assets.ts +13 -0
- package/src/ui-engine/layout-engine.ts +19 -6
- package/src/ui-engine/model.ts +10 -5
- package/src/ui-engine/runtime-header/antialiasing.ts +52 -32
- package/src/ui-engine/runtime-header/blend-bodies.ts +4 -4
- package/src/ui-engine/runtime-header/canvas-helpers.ts +32 -65
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +22 -10
- package/src/ui-engine/runtime-header/color-mono-refresh.ts +10 -10
- package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +59 -29
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +26 -10
- package/src/ui-engine/runtime-header/display-shim.ts +8 -8
- package/src/ui-engine/runtime-header/forward-decls.ts +48 -6
- package/src/ui-engine/runtime-header/image-drawing.ts +154 -84
- package/src/ui-engine/runtime-header/init-press-input.ts +20 -5
- package/src/ui-engine/runtime-header/keyboard.ts +19 -16
- package/src/ui-engine/runtime-header/node-decoration.ts +8 -8
- package/src/ui-engine/runtime-header/node-draw-body.ts +1402 -0
- package/src/ui-engine/runtime-header/paint-order-coords.ts +48 -18
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +38 -17
- package/src/ui-engine/runtime-header/scroll-physics.ts +15 -14
- package/src/ui-engine/runtime-header/state-bindings-nav.ts +23 -13
- package/src/ui-engine/runtime-header/structs.ts +12 -10
- package/src/ui-engine/runtime-header/text-rendering.ts +132 -71
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +29 -10
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +299 -747
- package/src/ui-engine/runtime-header/tick/flush-phase.ts +7 -3
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +19 -3
- package/src/ui-engine/runtime-header/tick/transitions-phase.ts +31 -24
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +42 -40
- package/src/ui-engine/runtime-header/types-defines.ts +23 -3
- package/src/ui-engine/runtime-header.ts +2 -0
- package/src/ui-engine/transpile-ui.ts +2 -2
- package/src/ui-engine/ua-stylesheet.ts +46 -3
- package/src/ui-engine/ui-lowering.ts +3 -25
- package/src/ui-engine/ui-registry.ts +1 -8
- package/dist/ui-engine/scroll-memory-diagnostics.d.ts +0 -16
- package/dist/ui-engine/scroll-memory-diagnostics.js +0 -58
- package/src/ui-engine/scroll-memory-diagnostics.ts +0 -79
|
@@ -13,12 +13,16 @@ static inline void ui_push_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, in
|
|
|
13
13
|
// via __ui_gfx (so the single bulk push at frame end captures everything).
|
|
14
14
|
// Otherwise write straight to the display in one SPI transaction.
|
|
15
15
|
if (__ui_fb) {
|
|
16
|
+
// The framebuffer is the composition target. Track this publish so a
|
|
17
|
+
// geometry repair or scroll-canvas repair that does not leave a node dirty
|
|
18
|
+
// still reaches the panel during the frame-end flush.
|
|
19
|
+
ui_fb_add_rect(x, y, w, h);
|
|
16
20
|
if (w == stride) {
|
|
17
21
|
ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
|
|
18
22
|
return;
|
|
19
23
|
}
|
|
20
24
|
for (int16_t row = 0; row < h; row++) {
|
|
21
|
-
ui_display_draw_rgb_bitmap(x, y + row, pixels + (
|
|
25
|
+
ui_display_draw_rgb_bitmap(x, y + row, pixels + static_cast<int32_t>(row) * stride, w, 1);
|
|
22
26
|
}
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
@@ -26,12 +30,12 @@ static inline void ui_push_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, in
|
|
|
26
30
|
display_setAddrWindow(x, y, w, h);
|
|
27
31
|
if (w == stride) {
|
|
28
32
|
// Full-width: contiguous in buffer, single write.
|
|
29
|
-
display_writePixels(pixels, (
|
|
33
|
+
display_writePixels(pixels, static_cast<uint32_t>(w) * h);
|
|
30
34
|
display_endWrite();
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
37
|
for (int16_t row = 0; row < h; row++) {
|
|
34
|
-
display_writePixels(pixels + (
|
|
38
|
+
display_writePixels(pixels + static_cast<int32_t>(row) * stride, w);
|
|
35
39
|
}
|
|
36
40
|
display_endWrite();
|
|
37
41
|
}
|
|
@@ -45,7 +49,7 @@ static inline void ui_draw_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, in
|
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
47
51
|
for (int16_t row = 0; row < h; row++) {
|
|
48
|
-
ui_display_draw_rgb_bitmap(x, y + row, pixels + (
|
|
52
|
+
ui_display_draw_rgb_bitmap(x, y + row, pixels + static_cast<int32_t>(row) * stride, w, 1);
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -67,10 +71,14 @@ static inline void ui_push_buffered_scroll_canvas(CuttlefishCanvas16* bufferedSc
|
|
|
67
71
|
ui_draw_canvas_rect(bufferedScrollRepaintCanvas, 0, bufferedScrollRepaintY, vw, bufferedScrollRepaintH);
|
|
68
72
|
}
|
|
69
73
|
int16_t tx = vw - 4;
|
|
70
|
-
uint16_t thumbH = (
|
|
74
|
+
uint16_t thumbH = static_cast<uint32_t>(vh) * vh / __ui_nodes[si].contentHeight;
|
|
71
75
|
if (thumbH < 8) thumbH = 8;
|
|
76
|
+
// Clamp to vh so (vh - thumbH) below can't underflow on a degenerate sub-8px
|
|
77
|
+
// viewport (which would cast a negative value to a huge uint32_t and place the
|
|
78
|
+
// thumb off-screen). Matches the guard in ui_render_scroll_bands.
|
|
79
|
+
if (thumbH > static_cast<uint16_t>(vh)) thumbH = static_cast<uint16_t>(vh);
|
|
72
80
|
int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
|
|
73
|
-
uint16_t thumbY =
|
|
81
|
+
uint16_t thumbY = static_cast<uint32_t>(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
|
|
74
82
|
UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
|
|
75
83
|
ui_display_fill_rect(tx, 0, 3, vh, dimFg);
|
|
76
84
|
ui_display_fill_rect(tx, thumbY, 3, thumbH, __ui_nodes[si].fg);
|
|
@@ -87,18 +95,22 @@ static inline void ui_push_buffered_scroll_canvas(CuttlefishCanvas16* bufferedSc
|
|
|
87
95
|
|
|
88
96
|
// Draw a scroll container's scrollbar directly on the display (Mode C).
|
|
89
97
|
static inline void ui_draw_scrollbar_direct(int16_t si, int16_t vox, int16_t voy) {
|
|
90
|
-
if (si < 0 || si >= (
|
|
98
|
+
if (si < 0 || si >= static_cast<int16_t>(__ui_node_count)) return;
|
|
91
99
|
int16_t vw = __ui_nodes[si].box.w;
|
|
92
100
|
int16_t vh = __ui_nodes[si].box.h;
|
|
93
101
|
if (__ui_nodes[si].contentHeight <= vh) return;
|
|
94
102
|
int16_t tx = vox + vw - 4;
|
|
95
|
-
uint16_t thumbH = (
|
|
103
|
+
uint16_t thumbH = static_cast<uint32_t>(vh) * vh / __ui_nodes[si].contentHeight;
|
|
96
104
|
if (thumbH < 8) thumbH = 8;
|
|
105
|
+
// Clamp to vh so (vh - thumbH) below can't underflow on a degenerate sub-8px
|
|
106
|
+
// viewport (which would cast a negative value to a huge uint32_t and place the
|
|
107
|
+
// thumb off-screen). Matches the guard in ui_render_scroll_bands.
|
|
108
|
+
if (thumbH > static_cast<uint16_t>(vh)) thumbH = static_cast<uint16_t>(vh);
|
|
97
109
|
int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
|
|
98
|
-
uint16_t thumbY =
|
|
110
|
+
uint16_t thumbY = static_cast<uint32_t>(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
|
|
99
111
|
UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
|
|
100
112
|
ui_display_fill_rect(tx, voy, 3, vh, dimFg);
|
|
101
|
-
ui_display_fill_rect(tx,
|
|
113
|
+
ui_display_fill_rect(tx, static_cast<int16_t>(voy + thumbY), 3, thumbH, __ui_nodes[si].fg);
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
// Per-node dirty marker (called by press handlers and binding evaluation).`;
|
|
@@ -39,11 +39,11 @@ static inline uint32_t ui_blend888(uint32_t fg, uint32_t bg, uint8_t opacity);
|
|
|
39
39
|
static inline uint16_t lerp_color(uint16_t a, uint16_t b, uint8_t k100);
|
|
40
40
|
static inline uint32_t lerp_color_888(uint32_t a, uint32_t b, uint8_t k100);
|
|
41
41
|
#if UI_COLOR_DEPTH == 888
|
|
42
|
-
#define ui_blend(fg, bg, op) ui_blend888(
|
|
43
|
-
#define UI_LERP_COLOR(a, b, k) lerp_color_888(
|
|
42
|
+
#define ui_blend(fg, bg, op) ui_blend888(static_cast<uint32_t>(fg), static_cast<uint32_t>(bg), (op))
|
|
43
|
+
#define UI_LERP_COLOR(a, b, k) lerp_color_888(static_cast<uint32_t>(a), static_cast<uint32_t>(b), (k))
|
|
44
44
|
#else
|
|
45
|
-
#define ui_blend(fg, bg, op) ui_blend565(
|
|
46
|
-
#define UI_LERP_COLOR(a, b, k) lerp_color(
|
|
45
|
+
#define ui_blend(fg, bg, op) ui_blend565(static_cast<uint16_t>(fg), static_cast<uint16_t>(bg), (op))
|
|
46
|
+
#define UI_LERP_COLOR(a, b, k) lerp_color(static_cast<uint16_t>(a), static_cast<uint16_t>(b), (k))
|
|
47
47
|
#endif
|
|
48
48
|
|
|
49
49
|
// ── 1-bit mono snap (UI_NATIVE_MONO) ─────────────────────────────────────────
|
|
@@ -57,7 +57,7 @@ static inline uint32_t ui_snap_mono(uint32_t c) {
|
|
|
57
57
|
uint8_t r = (c >> 16) & 0xff, g = (c >> 8) & 0xff, b = c & 0xff;
|
|
58
58
|
// Match the transpile-time toMono threshold: (0.299r + 0.587g + 0.114b)/255 >= 0.27.
|
|
59
59
|
// Integer form: 299r+587g+114b >= 68850 (= 0.27*255*1000). Verified 0 mismatches.
|
|
60
|
-
return (
|
|
60
|
+
return (static_cast<uint32_t>(299 * r + 587 * g + 114 * b) >= 68850u) ? 0xffffffu : 0x000000u;
|
|
61
61
|
}
|
|
62
62
|
// Snap an RGB565 value to 1-bit mono. Mono panels run at UI_COLOR_DEPTH 565, so
|
|
63
63
|
// node fields hold 565 values; reconstruct 8-bit channels then apply the threshold.
|
|
@@ -68,10 +68,10 @@ static inline uint16_t ui_snap_mono565(uint16_t c) {
|
|
|
68
68
|
if (c == 0x0001u || c == 0xffffu) return 0xffffu;
|
|
69
69
|
uint8_t r5 = (c >> 11) & 0x1f, g6 = (c >> 5) & 0x3f, b5 = c & 0x1f;
|
|
70
70
|
uint8_t r = (r5 << 3) | (r5 >> 2), g = (g6 << 2) | (g6 >> 4), b = (b5 << 3) | (b5 >> 2);
|
|
71
|
-
return (
|
|
71
|
+
return (static_cast<uint32_t>(299 * r + 587 * g + 114 * b) >= 68850u) ? 0xffffu : 0x0000u;
|
|
72
72
|
}
|
|
73
|
-
#define UI_MAYBE_SNAP_MONO(c) (ui_snap_mono(
|
|
74
|
-
#define UI_MAYBE_SNAP_MONO565(c) (ui_snap_mono565(
|
|
73
|
+
#define UI_MAYBE_SNAP_MONO(c) (ui_snap_mono(static_cast<uint32_t>(c)))
|
|
74
|
+
#define UI_MAYBE_SNAP_MONO565(c) (ui_snap_mono565(static_cast<uint16_t>(c)))
|
|
75
75
|
#else
|
|
76
76
|
#define UI_MAYBE_SNAP_MONO(c) (c)
|
|
77
77
|
#define UI_MAYBE_SNAP_MONO565(c) (c)
|
|
@@ -115,7 +115,7 @@ static inline uint16_t ui_snap_mono565(uint16_t c) {
|
|
|
115
115
|
const UIRect16& r = __ui_refresh_rects[i];
|
|
116
116
|
if (r.x < x0) x0 = r.x;
|
|
117
117
|
if (r.y < y0) y0 = r.y;
|
|
118
|
-
int16_t rx1 =
|
|
118
|
+
int16_t rx1 = static_cast<int16_t>(r.x + r.w), ry1 = static_cast<int16_t>(r.y + r.h);
|
|
119
119
|
if (rx1 > x1) x1 = rx1;
|
|
120
120
|
if (ry1 > y1) y1 = ry1;
|
|
121
121
|
}
|
|
@@ -124,7 +124,7 @@ static inline uint16_t ui_snap_mono565(uint16_t c) {
|
|
|
124
124
|
if (x1 > display_width()) x1 = display_width();
|
|
125
125
|
if (y1 > display_height()) y1 = display_height();
|
|
126
126
|
if (x1 > x0 && y1 > y0) {
|
|
127
|
-
display_partial_refresh(x0, y0,
|
|
127
|
+
display_partial_refresh(x0, y0, static_cast<int16_t>(x1 - x0), static_cast<int16_t>(y1 - y0));
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
#elif defined(UI_BATCH_SPI_WRITES)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// Slice of the C++ runtime header. Defines the CuttlefishGFX class — the
|
|
3
|
-
// shared geometry/canvas/text base used by native
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// shared geometry/canvas/text base used by native display adapters that
|
|
4
|
+
// instantiate CuttlefishGFX by value. NOT emitted on macro-alias paths
|
|
5
|
+
// (Adafruit `CuttlefishCanvas16==GFXcanvas16`, SDL
|
|
6
|
+
// `CuttlefishCanvas16==SdlGfxCanvas`), which own the canvas type.
|
|
6
7
|
//
|
|
7
8
|
// ──── BSD-3-Clause attribution ────────────────────────────────────────────
|
|
8
9
|
// The geometry algorithm implementations emitted by this slice (drawLine,
|
|
@@ -56,13 +57,16 @@ export function emitCuttlefishGfx(active: boolean): string {
|
|
|
56
57
|
|
|
57
58
|
return `
|
|
58
59
|
// ── CuttlefishGFX (native display GFX base) ────────────────────────────────
|
|
59
|
-
// Emitted only
|
|
60
|
+
// Emitted only for native adapters that instantiate CuttlefishGFX by value;
|
|
61
|
+
// suppressed on macro-alias paths (Adafruit CuttlefishCanvas16==GFXcanvas16,
|
|
62
|
+
// SDL CuttlefishCanvas16==SdlGfxCanvas). Gated in ui-emitter.ts.
|
|
60
63
|
#ifndef CUTTLEFISH_GFX_DEFINED
|
|
61
64
|
#define CUTTLEFISH_GFX_DEFINED
|
|
62
65
|
|
|
63
66
|
#include <stdint.h>
|
|
64
|
-
#include <
|
|
65
|
-
#include <
|
|
67
|
+
#include <cstdlib>
|
|
68
|
+
#include <cstring>
|
|
69
|
+
#include <new> // std::nothrow for canvas buffer allocation
|
|
66
70
|
|
|
67
71
|
// ── Panel-ops interface ────────────────────────────────────────────────────
|
|
68
72
|
// Each native adapter fills a CuttlefishPanelOps with function pointers that
|
|
@@ -121,7 +125,7 @@ class CuttlefishGFX {
|
|
|
121
125
|
// Text
|
|
122
126
|
virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size);
|
|
123
127
|
virtual void write(uint8_t c);
|
|
124
|
-
void print(const char* s) { while (*s) write((
|
|
128
|
+
void print(const char* s) { while (*s) write(static_cast<uint8_t>(*s++)); }
|
|
125
129
|
void setCursor(int16_t x, int16_t y) { cursor_x_ = x; cursor_y_ = y; }
|
|
126
130
|
void setTextColor(uint16_t c) { textcolor_ = c; }
|
|
127
131
|
void setTextColor(uint16_t c, uint16_t bg) { textcolor_ = c; textbgcolor_ = bg; }
|
|
@@ -153,9 +157,18 @@ class CuttlefishGFX {
|
|
|
153
157
|
// display_deleteCanvas (the runtime reuses persistent canvas slots — see
|
|
154
158
|
// AGENTS.md: __ui_container_canvas, __ui_repair_canvas, __ui_list_canvas,
|
|
155
159
|
// __ui_node_canvas).
|
|
156
|
-
class CuttlefishCanvas16 : public CuttlefishGFX {
|
|
160
|
+
class CuttlefishCanvas16 final : public CuttlefishGFX {
|
|
157
161
|
public:
|
|
162
|
+
// Noncopyable (owns a heap buffer; a value-copy would double-free).
|
|
163
|
+
CuttlefishCanvas16(const CuttlefishCanvas16&) = delete;
|
|
164
|
+
CuttlefishCanvas16& operator=(const CuttlefishCanvas16&) = delete;
|
|
165
|
+
// Owns its buffer: mallocs w*h*2 bytes and frees it in the dtor.
|
|
158
166
|
CuttlefishCanvas16(int16_t w, int16_t h);
|
|
167
|
+
// Adopts an externally-allocated buffer (e.g. PSRAM via display_createCanvasPsram).
|
|
168
|
+
// If takeOwnership is true, the dtor frees the buffer with free() (works for both
|
|
169
|
+
// SRAM malloc and the ESP unified heap, which includes PSRAM). If false, the
|
|
170
|
+
// caller owns the buffer's lifetime.
|
|
171
|
+
CuttlefishCanvas16(int16_t w, int16_t h, uint16_t* externalBuffer, uint8_t takeOwnership = 0);
|
|
159
172
|
virtual ~CuttlefishCanvas16();
|
|
160
173
|
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
|
|
161
174
|
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
|
|
@@ -174,13 +187,16 @@ class CuttlefishCanvas16 : public CuttlefishGFX {
|
|
|
174
187
|
private:
|
|
175
188
|
uint16_t* buffer_;
|
|
176
189
|
int16_t canvas_w_, canvas_h_;
|
|
190
|
+
uint8_t owns_buffer_; // 1 = dtor frees buffer_ (malloc'd), 0 = externally owned
|
|
177
191
|
};
|
|
178
192
|
|
|
179
193
|
// ── 1-bit mono canvas (SSD1309/SSD1680 backing stores) ──────────────────────
|
|
180
194
|
// Mirrors Adafruit_GFXcanvasMono. (w+7)/8 bytes per row. The buffer is the
|
|
181
195
|
// panel backing store on SSD1309 — flush via display_partial_refresh.
|
|
182
|
-
class CuttlefishCanvasMono : public CuttlefishGFX {
|
|
196
|
+
class CuttlefishCanvasMono final : public CuttlefishGFX {
|
|
183
197
|
public:
|
|
198
|
+
CuttlefishCanvasMono(const CuttlefishCanvasMono&) = delete;
|
|
199
|
+
CuttlefishCanvasMono& operator=(const CuttlefishCanvasMono&) = delete;
|
|
184
200
|
CuttlefishCanvasMono(int16_t w, int16_t h);
|
|
185
201
|
virtual ~CuttlefishCanvasMono();
|
|
186
202
|
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
|
|
@@ -373,8 +389,8 @@ void CuttlefishGFX::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
|
|
|
373
389
|
if (a > b) { int16_t t = a; a = b; b = t; }
|
|
374
390
|
drawFastHLine(a, y, b - a + 1, color);
|
|
375
391
|
}
|
|
376
|
-
sa = (
|
|
377
|
-
sb = (
|
|
392
|
+
sa = static_cast<int32_t>(dx12) * (last - y1);
|
|
393
|
+
sb = static_cast<int32_t>(dx02) * (last - y0);
|
|
378
394
|
for (; y <= y2; y++) {
|
|
379
395
|
a = x1 + sa / dy12;
|
|
380
396
|
b = x0 + sb / dy02;
|
|
@@ -392,7 +408,7 @@ void CuttlefishGFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t* bitmap,
|
|
|
392
408
|
if (!bitmap || w <= 0 || h <= 0) return;
|
|
393
409
|
for (int16_t j = 0; j < h; j++) {
|
|
394
410
|
for (int16_t i = 0; i < w; i++) {
|
|
395
|
-
drawPixel(x + i, y + j, bitmap[(
|
|
411
|
+
drawPixel(x + i, y + j, bitmap[static_cast<int32_t>(j) * w + i]);
|
|
396
412
|
}
|
|
397
413
|
}
|
|
398
414
|
}
|
|
@@ -406,7 +422,7 @@ void CuttlefishGFX::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t col
|
|
|
406
422
|
return;
|
|
407
423
|
if (c >= 176) c++; // Adafruit '±' fixup
|
|
408
424
|
for (int8_t i = 0; i < 5; i++) {
|
|
409
|
-
uint8_t line = cuttlefish_glcdfont[(
|
|
425
|
+
uint8_t line = cuttlefish_glcdfont[static_cast<size_t>(c) * 5 + i];
|
|
410
426
|
for (int8_t j = 0; j < 8; j++, line >>= 1) {
|
|
411
427
|
if (line & 1) {
|
|
412
428
|
if (size == 1) drawPixel(x + i, y + j, color);
|
|
@@ -442,22 +458,36 @@ void CuttlefishGFX::write(uint8_t c) {
|
|
|
442
458
|
|
|
443
459
|
CuttlefishCanvas16::CuttlefishCanvas16(int16_t w, int16_t h)
|
|
444
460
|
: CuttlefishGFX(nullptr, nullptr),
|
|
445
|
-
buffer_(nullptr), canvas_w_(w), canvas_h_(h) {
|
|
461
|
+
buffer_(nullptr), canvas_w_(w), canvas_h_(h), owns_buffer_(1) {
|
|
446
462
|
if ((w > 0) && (h > 0)) {
|
|
447
|
-
size_t bytes = (
|
|
448
|
-
|
|
463
|
+
size_t bytes = static_cast<size_t>(w) * static_cast<size_t>(h) * sizeof(uint16_t);
|
|
464
|
+
// malloc (not new): on targets with CONFIG_REQUIRES_FULL_LIBCPP but without
|
|
465
|
+
// CONFIG_CPP_EXCEPTIONS, operator new throws std::bad_alloc on OOM and the
|
|
466
|
+
// nothrow wrapper's internal catch can't unwind → std::terminate → abort.
|
|
467
|
+
// malloc returns NULL on failure with no exception path, which is exactly
|
|
468
|
+
// what the canvas-failure self-heal logic expects.
|
|
469
|
+
buffer_ = static_cast<uint16_t*>(malloc(bytes));
|
|
449
470
|
if (buffer_) memset(buffer_, 0, bytes);
|
|
450
471
|
}
|
|
451
472
|
}
|
|
452
473
|
|
|
474
|
+
CuttlefishCanvas16::CuttlefishCanvas16(int16_t w, int16_t h, uint16_t* externalBuffer, uint8_t takeOwnership)
|
|
475
|
+
: CuttlefishGFX(nullptr, nullptr),
|
|
476
|
+
buffer_(externalBuffer), canvas_w_(w), canvas_h_(h), owns_buffer_(takeOwnership) {
|
|
477
|
+
// Zero the buffer so the canvas starts clean whether or not we own it.
|
|
478
|
+
if (externalBuffer && (w > 0) && (h > 0)) {
|
|
479
|
+
memset(externalBuffer, 0, static_cast<size_t>(w) * static_cast<size_t>(h) * sizeof(uint16_t));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
453
483
|
CuttlefishCanvas16::~CuttlefishCanvas16() {
|
|
454
|
-
if (buffer_) free(buffer_);
|
|
484
|
+
if (buffer_ && owns_buffer_) free(buffer_);
|
|
455
485
|
}
|
|
456
486
|
|
|
457
487
|
void CuttlefishCanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
458
488
|
if (!buffer_) return;
|
|
459
489
|
if ((x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return;
|
|
460
|
-
buffer_[(
|
|
490
|
+
buffer_[static_cast<size_t>(y) * static_cast<size_t>(canvas_w_) + static_cast<size_t>(x)] = color;
|
|
461
491
|
}
|
|
462
492
|
|
|
463
493
|
void CuttlefishCanvas16::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
@@ -469,23 +499,23 @@ void CuttlefishCanvas16::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, ui
|
|
|
469
499
|
// Cull before the inner loop (AGENTS.md: clip before expensive work).
|
|
470
500
|
if (x2 <= x1 || y2 <= y1) return;
|
|
471
501
|
for (int16_t j = y1; j < y2; j++) {
|
|
472
|
-
uint16_t* row = buffer_ + (
|
|
502
|
+
uint16_t* row = buffer_ + static_cast<size_t>(j) * static_cast<size_t>(canvas_w_);
|
|
473
503
|
for (int16_t i = x1; i < x2; i++) row[i] = color;
|
|
474
504
|
}
|
|
475
505
|
}
|
|
476
506
|
|
|
477
507
|
uint16_t CuttlefishCanvas16::getPixel(int16_t x, int16_t y) const {
|
|
478
508
|
if (!buffer_ || (x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return 0;
|
|
479
|
-
return buffer_[(
|
|
509
|
+
return buffer_[static_cast<size_t>(y) * static_cast<size_t>(canvas_w_) + static_cast<size_t>(x)];
|
|
480
510
|
}
|
|
481
511
|
|
|
482
512
|
CuttlefishCanvasMono::CuttlefishCanvasMono(int16_t w, int16_t h)
|
|
483
513
|
: CuttlefishGFX(nullptr, nullptr),
|
|
484
514
|
buffer_(nullptr), canvas_w_(w), canvas_h_(h) {
|
|
485
515
|
if ((w > 0) && (h > 0)) {
|
|
486
|
-
size_t row_bytes = ((
|
|
487
|
-
size_t bytes = row_bytes * (
|
|
488
|
-
buffer_ = (
|
|
516
|
+
size_t row_bytes = (static_cast<size_t>(w) + 7u) / 8u;
|
|
517
|
+
size_t bytes = row_bytes * static_cast<size_t>(h);
|
|
518
|
+
buffer_ = static_cast<uint8_t*>(malloc(bytes));
|
|
489
519
|
if (buffer_) memset(buffer_, 0, bytes);
|
|
490
520
|
}
|
|
491
521
|
}
|
|
@@ -497,8 +527,8 @@ CuttlefishCanvasMono::~CuttlefishCanvasMono() {
|
|
|
497
527
|
void CuttlefishCanvasMono::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
498
528
|
if (!buffer_) return;
|
|
499
529
|
if ((x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return;
|
|
500
|
-
size_t row_bytes = ((
|
|
501
|
-
uint8_t* row = buffer_ + (
|
|
530
|
+
size_t row_bytes = (static_cast<size_t>(canvas_w_) + 7u) / 8u;
|
|
531
|
+
uint8_t* row = buffer_ + static_cast<size_t>(y) * row_bytes;
|
|
502
532
|
uint8_t mask = 0x80u >> (x & 7);
|
|
503
533
|
if (color) row[x >> 3] |= mask;
|
|
504
534
|
else row[x >> 3] &= ~mask;
|
|
@@ -514,8 +544,8 @@ void CuttlefishCanvasMono::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
|
|
514
544
|
uint8_t fill = color ? 0xFFu : 0x00u;
|
|
515
545
|
for (int16_t j = y1; j < y2; j++) {
|
|
516
546
|
for (int16_t i = x1; i < x2; i++) {
|
|
517
|
-
size_t row_bytes = ((
|
|
518
|
-
uint8_t* row = buffer_ + (
|
|
547
|
+
size_t row_bytes = (static_cast<size_t>(canvas_w_) + 7u) / 8u;
|
|
548
|
+
uint8_t* row = buffer_ + static_cast<size_t>(j) * row_bytes;
|
|
519
549
|
uint8_t mask = 0x80u >> (i & 7);
|
|
520
550
|
if (color) row[i >> 3] |= mask;
|
|
521
551
|
else row[i >> 3] &= ~mask;
|
|
@@ -526,8 +556,8 @@ void CuttlefishCanvasMono::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
|
|
526
556
|
|
|
527
557
|
uint16_t CuttlefishCanvasMono::getPixel(int16_t x, int16_t y) const {
|
|
528
558
|
if (!buffer_ || (x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return 0;
|
|
529
|
-
size_t row_bytes = ((
|
|
530
|
-
const uint8_t* row = buffer_ + (
|
|
559
|
+
size_t row_bytes = (static_cast<size_t>(canvas_w_) + 7u) / 8u;
|
|
560
|
+
const uint8_t* row = buffer_ + static_cast<size_t>(y) * row_bytes;
|
|
531
561
|
return (row[x >> 3] & (0x80u >> (x & 7))) ? 0xFFFFu : 0x0000u;
|
|
532
562
|
}
|
|
533
563
|
|
|
@@ -64,7 +64,10 @@ static inline void ui_mark_subtree_dirty_local(uint16_t scrollNode) {
|
|
|
64
64
|
|
|
65
65
|
// Mode C strip-only: direct-partial scroll when no viewport canvas fits and the
|
|
66
66
|
// scroll delta is small. Fills only the exposed strip on the display, then marks
|
|
67
|
-
//
|
|
67
|
+
// only the descendants that intersect that strip dirty for direct draw — mirroring
|
|
68
|
+
// the Mode B canvas repair path. Never clears the whole viewport, and never marks
|
|
69
|
+
// the whole subtree dirty (AGENTS.md: keep scroll drag invalidation small — a
|
|
70
|
+
// 1-pixel drag must not repaint every visible child).
|
|
68
71
|
static inline void ui_scroll_direct_prepare(uint16_t s, int16_t* outVX, int16_t* outVY) {
|
|
69
72
|
int16_t vw = __ui_nodes[s].box.w;
|
|
70
73
|
int16_t vh = __ui_nodes[s].box.h;
|
|
@@ -73,18 +76,31 @@ static inline void ui_scroll_direct_prepare(uint16_t s, int16_t* outVX, int16_t*
|
|
|
73
76
|
UI_COLOR_T scrollBg = __ui_nodes[s].hasBg ? __ui_nodes[s].bg : __ui_nodes[s].clearColor;
|
|
74
77
|
int16_t deltaY = __ui_nodes[s].scrollY - __ui_nodes[s].lastPaintedScrollY;
|
|
75
78
|
int16_t absDelta = deltaY < 0 ? -deltaY : deltaY;
|
|
76
|
-
int16_t stripY = deltaY > 0 ?
|
|
77
|
-
ui_display_fill_rect(vox,
|
|
79
|
+
int16_t stripY = deltaY > 0 ? static_cast<int16_t>(vh - absDelta) : 0;
|
|
80
|
+
ui_display_fill_rect(vox, static_cast<int16_t>(voy + stripY), vw, absDelta, scrollBg);
|
|
81
|
+
// Only descendants whose paint rect intersects the exposed strip need
|
|
82
|
+
// repainting; the rest keep their last-painted pixels (the strip background
|
|
83
|
+
// fill already covered the vacated band). Clear stale dirty flags on the
|
|
84
|
+
// others so a flag set by a prior frame (or a sibling promotion) doesn't
|
|
85
|
+
// trigger a needless repaint.
|
|
86
|
+
UIRect exposed = { vox, static_cast<int16_t>(voy + stripY), vw, absDelta };
|
|
78
87
|
for (uint16_t c = s + 1; c < __ui_nodes[s].subtreeEnd; c++) {
|
|
79
88
|
if (!ui_is_effectively_visible(c) || __ui_nodes[c].screenId != __ui_active_screen) {
|
|
80
89
|
__ui_nodes[c].dirty = 0;
|
|
81
90
|
continue;
|
|
82
91
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
UIRect cr;
|
|
93
|
+
ui_node_current_paint_rect(c, &cr);
|
|
94
|
+
if (cr.w > 0 && cr.h > 0 &&
|
|
95
|
+
ui_rects_intersect(cr.x, cr.y, cr.w, cr.h, exposed.x, exposed.y, exposed.w, exposed.h)) {
|
|
96
|
+
__ui_nodes[c].dirty = 1;
|
|
97
|
+
if (__ui_nodes[c].kind == NODE_PROGRESS || __ui_nodes[c].kind == NODE_RANGE) {
|
|
98
|
+
__ui_nodes[c].lastTextWidth = -1;
|
|
99
|
+
}
|
|
100
|
+
__ui_nodes[c].lastTextHeight = 0;
|
|
101
|
+
} else {
|
|
102
|
+
__ui_nodes[c].dirty = 0;
|
|
86
103
|
}
|
|
87
|
-
__ui_nodes[c].lastTextHeight = 0;
|
|
88
104
|
}
|
|
89
105
|
__ui_nodes[s].dirty = 0;
|
|
90
106
|
if (outVX) *outVX = vox;
|
|
@@ -96,13 +112,13 @@ static inline int16_t ui_overflow_scroll_compositor(uint16_t nodeIdx) {
|
|
|
96
112
|
if (nodeIdx >= __ui_node_count) return -1;
|
|
97
113
|
if (__ui_nodes[nodeIdx].scrollable && !__ui_nodes[nodeIdx].virtualized &&
|
|
98
114
|
__ui_nodes[nodeIdx].contentHeight > __ui_nodes[nodeIdx].box.h) {
|
|
99
|
-
return (
|
|
115
|
+
return static_cast<int16_t>(nodeIdx);
|
|
100
116
|
}
|
|
101
117
|
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
102
118
|
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
103
119
|
if (__ui_nodes[p].scrollable && !__ui_nodes[p].virtualized &&
|
|
104
120
|
__ui_nodes[p].contentHeight > __ui_nodes[p].box.h) {
|
|
105
|
-
return (
|
|
121
|
+
return static_cast<int16_t>(p);
|
|
106
122
|
}
|
|
107
123
|
p = __ui_nodes[p].parent;
|
|
108
124
|
}
|
|
@@ -151,7 +167,7 @@ static inline void ui_mark_scroll_view_dirty(uint16_t scrollNode) {
|
|
|
151
167
|
static inline int16_t ui_scroll_ancestor_for_node(uint16_t nodeIdx) {
|
|
152
168
|
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
153
169
|
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
154
|
-
if (__ui_nodes[p].scrollable) return (
|
|
170
|
+
if (__ui_nodes[p].scrollable) return static_cast<int16_t>(p);
|
|
155
171
|
p = __ui_nodes[p].parent;
|
|
156
172
|
}
|
|
157
173
|
return -1;
|
|
@@ -26,10 +26,10 @@ static inline int16_t ui_clamp_i16(int16_t value, int16_t lo, int16_t hi) {
|
|
|
26
26
|
return value;
|
|
27
27
|
}
|
|
28
28
|
static inline void ui_display_target_bounds(int16_t* left, int16_t* top, int16_t* right, int16_t* bottom) {
|
|
29
|
-
if (left) *left =
|
|
30
|
-
if (top) *top =
|
|
31
|
-
if (right) *right =
|
|
32
|
-
if (bottom) *bottom =
|
|
29
|
+
if (left) *left = static_cast<int16_t>(-__ui_draw_off_x);
|
|
30
|
+
if (top) *top = static_cast<int16_t>(-__ui_draw_off_y);
|
|
31
|
+
if (right) *right = static_cast<int16_t>(ui_display_target_width() - __ui_draw_off_x);
|
|
32
|
+
if (bottom) *bottom = static_cast<int16_t>(ui_display_target_height() - __ui_draw_off_y);
|
|
33
33
|
}
|
|
34
34
|
static inline uint8_t ui_clip_rect_to_display_target(int16_t* x, int16_t* y, int16_t* w, int16_t* h) {
|
|
35
35
|
if (!x || !y || !w || !h || *w <= 0 || *h <= 0) return 0;
|
|
@@ -37,15 +37,15 @@ static inline uint8_t ui_clip_rect_to_display_target(int16_t* x, int16_t* y, int
|
|
|
37
37
|
ui_display_target_bounds(&left, &top, &right, &bottom);
|
|
38
38
|
int16_t x0 = *x > left ? *x : left;
|
|
39
39
|
int16_t y0 = *y > top ? *y : top;
|
|
40
|
-
int16_t x1 =
|
|
41
|
-
int16_t y1 =
|
|
40
|
+
int16_t x1 = static_cast<int16_t>(*x + *w);
|
|
41
|
+
int16_t y1 = static_cast<int16_t>(*y + *h);
|
|
42
42
|
if (x1 > right) x1 = right;
|
|
43
43
|
if (y1 > bottom) y1 = bottom;
|
|
44
44
|
if (x0 >= x1 || y0 >= y1) return 0;
|
|
45
45
|
*x = x0;
|
|
46
46
|
*y = y0;
|
|
47
|
-
*w =
|
|
48
|
-
*h =
|
|
47
|
+
*w = static_cast<int16_t>(x1 - x0);
|
|
48
|
+
*h = static_cast<int16_t>(y1 - y0);
|
|
49
49
|
return 1;
|
|
50
50
|
}
|
|
51
51
|
static inline void ui_display_draw_pixel(int16_t x, int16_t y, UI_COLOR_T color) {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// Slice of the C++ runtime header (original source lines 622-752).
|
|
2
2
|
// navigate, canvas/scroll/text/paint/clip/AA/image forward declarations, persistent canvas globals.
|
|
3
3
|
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
//
|
|
5
|
+
// EMIT BOUNDARY: This file is the structural head of the UI runtime header
|
|
6
|
+
// surface (C) — every partial emitter in this directory produces bytes that are
|
|
7
|
+
// assembled into the C++ header inlined into user sketches. The emitted bytes
|
|
8
|
+
// are covered by the TypeCAD Runtime Exception (see RUNTIME_EXCEPTION.md at the
|
|
9
|
+
// repository root) and are not subject to the license of this tool source.
|
|
4
10
|
export function emitForwardDecls(): string {
|
|
5
11
|
return `
|
|
6
12
|
// Early forward declaration: ui_navigate (below) calls ui_release_canvas_state
|
|
@@ -9,9 +15,17 @@ export function emitForwardDecls(): string {
|
|
|
9
15
|
static inline void ui_release_canvas_state();
|
|
10
16
|
static inline void ui_set_pressed(uint16_t nodeIdx, uint8_t pressed);
|
|
11
17
|
static inline void ui_refresh_active_screen_bg_node();
|
|
18
|
+
static inline CuttlefishCanvas16* ui_get_framebuffer();
|
|
19
|
+
// Set when the next frame must be composed from the active screen background;
|
|
20
|
+
// declared before ui_navigate because navigation can request that composition.
|
|
21
|
+
static uint8_t __ui_fb_needs_compose = 1;
|
|
12
22
|
|
|
13
23
|
// Navigate to a screen by index. Marks the new screen's nodes dirty, releases
|
|
14
|
-
// persistent canvas state, and
|
|
24
|
+
// persistent canvas state, and requests a complete off-screen composition when
|
|
25
|
+
// the full-frame path is available. The physical panel is never cleared here on
|
|
26
|
+
// that path: navigation must not expose an intermediate frame while the next
|
|
27
|
+
// frame is built. Without a full-frame buffer, the fallback remains best-effort;
|
|
28
|
+
// no-TE TFT hardware cannot provide a strict tear-free guarantee.
|
|
15
29
|
static inline void ui_navigate(uint8_t screenIdx) {
|
|
16
30
|
if (screenIdx >= __ui_screen_count || screenIdx == __ui_active_screen) return;
|
|
17
31
|
__ui_active_screen = screenIdx;
|
|
@@ -23,7 +37,7 @@ static inline void ui_navigate(uint8_t screenIdx) {
|
|
|
23
37
|
// and the button would stay stuck in its :pressed color.
|
|
24
38
|
if (__ui_touch_node >= 0 && __ui_touch_node < __ui_node_count &&
|
|
25
39
|
__ui_nodes[__ui_touch_node].kind == NODE_BUTTON && __ui_nodes[__ui_touch_node].value != 0) {
|
|
26
|
-
ui_set_pressed((
|
|
40
|
+
ui_set_pressed(static_cast<uint16_t>(__ui_touch_node), 0);
|
|
27
41
|
}
|
|
28
42
|
__ui_scroll_node = -1;
|
|
29
43
|
__ui_touch_node = -1;
|
|
@@ -34,17 +48,34 @@ static inline void ui_navigate(uint8_t screenIdx) {
|
|
|
34
48
|
// resident and fragments the heap, so the new screen's buffer can't get a
|
|
35
49
|
// contiguous block (the "works first, then blanks until reset" symptom).
|
|
36
50
|
ui_release_canvas_state();
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
51
|
+
// A framebuffer-capable target keeps the old frame visible until the new one
|
|
52
|
+
// is complete. Targets without enough memory retain the legacy clear fallback
|
|
53
|
+
// so stale pixels from the old screen cannot remain; this constrained path is
|
|
54
|
+
// best-effort because the panel has no TE/vblank synchronization.
|
|
55
|
+
if (ui_get_framebuffer()) {
|
|
56
|
+
__ui_fb_needs_compose = 1;
|
|
57
|
+
} else {
|
|
40
58
|
#ifndef UI_REFRESH_DEFERRED
|
|
41
|
-
|
|
59
|
+
UI_COLOR_T navBg = (UI_COLOR_T)0;
|
|
60
|
+
if (__ui_active_screen_bg_node < __ui_node_count) {
|
|
61
|
+
navBg = __ui_nodes[__ui_active_screen_bg_node].hasBg
|
|
62
|
+
? __ui_nodes[__ui_active_screen_bg_node].bg
|
|
63
|
+
: __ui_nodes[__ui_active_screen_bg_node].clearColor;
|
|
64
|
+
}
|
|
65
|
+
display_fillScreen(navBg);
|
|
42
66
|
#endif
|
|
67
|
+
}
|
|
43
68
|
// Mark all nodes dirty so the new screen fully redraws.
|
|
44
69
|
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
45
70
|
__ui_nodes[i].dirty = 1;
|
|
46
71
|
__ui_nodes[i].lastTextHeight = 0;
|
|
47
72
|
__ui_nodes[i].layoutCacheKey = 0;
|
|
73
|
+
if (__ui_nodes[i].scrollable) {
|
|
74
|
+
// The next screen needs a complete scroll-canvas seed; subsequent local
|
|
75
|
+
// child updates can use retained-canvas repairs.
|
|
76
|
+
__ui_nodes[i].lastPaintedScrollY = static_cast<int16_t>(
|
|
77
|
+
__ui_nodes[i].scrollY - (__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1));
|
|
78
|
+
}
|
|
48
79
|
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) __ui_nodes[i].lastTextWidth = -1;
|
|
49
80
|
else __ui_nodes[i].lastTextWidth = 0;
|
|
50
81
|
}
|
|
@@ -77,6 +108,7 @@ static inline uint8_t ui_subtree_current_paint_rect(uint16_t nodeIdx, UIRect* ou
|
|
|
77
108
|
static inline void ui_mark_overlapping_higher_layers_dirty(uint16_t nodeIdx);
|
|
78
109
|
static inline void ui_mark_overlapping_higher_layers_dirty_for_rect(uint16_t nodeIdx, const UIRect* r);
|
|
79
110
|
static inline void ui_mark_scroll_view_dirty(uint16_t scrollNode);
|
|
111
|
+
static inline UI_COLOR_T ui_parent_clear_color(uint16_t nodeIdx);
|
|
80
112
|
static inline void ui_release_canvas_state();
|
|
81
113
|
static inline void ui_set_visible(uint16_t nodeIdx, uint8_t visible);
|
|
82
114
|
static inline void ui_invalidate_scroll_canvas_for_node(uint16_t nodeIdx);
|
|
@@ -101,6 +133,11 @@ static inline void ui_aa_fill_circle(CuttlefishCanvas16* c, int16_t cx, int16_t
|
|
|
101
133
|
static inline uint8_t ui_repair_current_node_paint_with_parent(uint16_t nodeIdx, UIRect* r);
|
|
102
134
|
static inline void ui_clear_node_paint_rect(uint16_t nodeIdx, const UIRect* paintRect);
|
|
103
135
|
static inline uint8_t ui_try_repair_geometry_fill(uint16_t nodeIdx, const UIRect* oldRect);
|
|
136
|
+
// Band renderers (defined in node-draw-body; forward-declared so the NODE_LIST
|
|
137
|
+
// case can fall back to ui_render_list_bands before its definition site).
|
|
138
|
+
static inline uint8_t ui_render_node_bands(uint16_t nodeIdx, int16_t prX, int16_t prY, int16_t prW, int16_t prH);
|
|
139
|
+
static inline uint8_t ui_render_list_bands(uint16_t i);
|
|
140
|
+
static inline uint8_t ui_render_list_direct(uint16_t i);
|
|
104
141
|
static inline void ui_draw_node_border(uint16_t i, int16_t drawX, int16_t drawY, UI_COLOR_T color);
|
|
105
142
|
static inline void ui_draw_node_outline(uint16_t i, int16_t drawX, int16_t drawY);
|
|
106
143
|
static inline void ui_draw_gradient_fill(uint16_t i, int16_t drawY);
|
|
@@ -114,6 +151,10 @@ static inline void ui_draw_scaled_image(const UIImage* img, int16_t x, int16_t y
|
|
|
114
151
|
int16_t drawW, int16_t drawH);
|
|
115
152
|
|
|
116
153
|
static CuttlefishDisplayTarget* __ui_gfx = display_defaultTarget();
|
|
154
|
+
// Full-frame composition is required for the first frame and after navigation.
|
|
155
|
+
// Once composed, subsequent dirty updates can safely modify the retained
|
|
156
|
+
// framebuffer without rebuilding unchanged nodes. The flags are declared
|
|
157
|
+
// before ui_navigate above because navigation sets the compose request.
|
|
117
158
|
// Persistent canvas slots, all freed on screen change (ui_navigate) so each
|
|
118
159
|
// screen starts with a clean heap. Without this, the first screen's canvas
|
|
119
160
|
// buffer stays resident and fragments the heap, so a later screen's buffer
|
|
@@ -125,6 +166,7 @@ static CuttlefishCanvas16* __ui_list_canvas = nullptr; // virtualized <lis
|
|
|
125
166
|
static int16_t __ui_list_canvas_node = -1; // node currently represented by __ui_list_canvas
|
|
126
167
|
static CuttlefishCanvas16* __ui_node_canvas = nullptr; // <canvas> element offscreen
|
|
127
168
|
static CuttlefishCanvas16* __ui_repair_canvas = nullptr; // buffered-paint / exposed-strip
|
|
169
|
+
static CuttlefishCanvas16* __ui_band_canvas = nullptr; // band renderer (no-PSRAM scroll)
|
|
128
170
|
static CuttlefishCanvas16* __ui_kb_canvas = nullptr; // on-screen keyboard overlay
|
|
129
171
|
static int16_t __ui_canvas_fallback_w = 0; // dimensions for direct <canvas> fallback
|
|
130
172
|
static int16_t __ui_canvas_fallback_h = 0;
|