@typecad/ui 1.0.0-alpha.6 → 1.0.0-alpha.8
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 +2 -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 +42 -69
- 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.d.ts +1 -0
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +563 -0
- 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 +52 -10
- 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 +62 -18
- package/dist/ui-engine/runtime-header/node-decoration.js +8 -9
- 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 -18
- 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 +336 -749
- 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 +46 -41
- package/dist/ui-engine/runtime-header/types-defines.js +23 -3
- package/dist/ui-engine/runtime-header.d.ts +10 -1
- package/dist/ui-engine/runtime-header.js +7 -1
- 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 +18 -24
- package/dist/ui-engine/ui-registry.js +1 -6
- package/package.json +2 -2
- package/src/engine-index.ts +2 -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 +42 -69
- 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 +566 -0
- 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 +52 -10
- 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 +62 -18
- package/src/ui-engine/runtime-header/node-decoration.ts +8 -9
- 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 -18
- 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 +336 -749
- 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 +46 -41
- package/src/ui-engine/runtime-header/types-defines.ts +23 -3
- package/src/ui-engine/runtime-header.ts +17 -1
- 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 +19 -26
- package/src/ui-engine/ui-registry.ts +1 -7
- 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
|
@@ -1,20 +1,31 @@
|
|
|
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() {
|
|
5
11
|
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
12
|
// Early forward declaration: ui_navigate (below) calls ui_release_canvas_state
|
|
11
13
|
// and ui_set_pressed (defined later) during screen changes. Needed on native
|
|
12
14
|
// (single TU, no Arduino auto-prototyper).
|
|
13
15
|
static inline void ui_release_canvas_state();
|
|
14
16
|
static inline void ui_set_pressed(uint16_t nodeIdx, uint8_t pressed);
|
|
15
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;
|
|
16
22
|
|
|
17
|
-
// Navigate to a screen by index. Marks the new screen's nodes dirty
|
|
23
|
+
// Navigate to a screen by index. Marks the new screen's nodes dirty, releases
|
|
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.
|
|
18
29
|
static inline void ui_navigate(uint8_t screenIdx) {
|
|
19
30
|
if (screenIdx >= __ui_screen_count || screenIdx == __ui_active_screen) return;
|
|
20
31
|
__ui_active_screen = screenIdx;
|
|
@@ -26,7 +37,7 @@ static inline void ui_navigate(uint8_t screenIdx) {
|
|
|
26
37
|
// and the button would stay stuck in its :pressed color.
|
|
27
38
|
if (__ui_touch_node >= 0 && __ui_touch_node < __ui_node_count &&
|
|
28
39
|
__ui_nodes[__ui_touch_node].kind == NODE_BUTTON && __ui_nodes[__ui_touch_node].value != 0) {
|
|
29
|
-
ui_set_pressed((
|
|
40
|
+
ui_set_pressed(static_cast<uint16_t>(__ui_touch_node), 0);
|
|
30
41
|
}
|
|
31
42
|
__ui_scroll_node = -1;
|
|
32
43
|
__ui_touch_node = -1;
|
|
@@ -37,17 +48,34 @@ static inline void ui_navigate(uint8_t screenIdx) {
|
|
|
37
48
|
// resident and fragments the heap, so the new screen's buffer can't get a
|
|
38
49
|
// contiguous block (the "works first, then blanks until reset" symptom).
|
|
39
50
|
ui_release_canvas_state();
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
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 {
|
|
43
58
|
#ifndef UI_REFRESH_DEFERRED
|
|
44
|
-
|
|
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);
|
|
45
66
|
#endif
|
|
67
|
+
}
|
|
46
68
|
// Mark all nodes dirty so the new screen fully redraws.
|
|
47
69
|
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
48
70
|
__ui_nodes[i].dirty = 1;
|
|
49
71
|
__ui_nodes[i].lastTextHeight = 0;
|
|
50
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
|
+
}
|
|
51
79
|
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) __ui_nodes[i].lastTextWidth = -1;
|
|
52
80
|
else __ui_nodes[i].lastTextWidth = 0;
|
|
53
81
|
}
|
|
@@ -80,6 +108,7 @@ static inline uint8_t ui_subtree_current_paint_rect(uint16_t nodeIdx, UIRect* ou
|
|
|
80
108
|
static inline void ui_mark_overlapping_higher_layers_dirty(uint16_t nodeIdx);
|
|
81
109
|
static inline void ui_mark_overlapping_higher_layers_dirty_for_rect(uint16_t nodeIdx, const UIRect* r);
|
|
82
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);
|
|
83
112
|
static inline void ui_release_canvas_state();
|
|
84
113
|
static inline void ui_set_visible(uint16_t nodeIdx, uint8_t visible);
|
|
85
114
|
static inline void ui_invalidate_scroll_canvas_for_node(uint16_t nodeIdx);
|
|
@@ -93,15 +122,22 @@ static inline void ui_draw_node_decoration_clipped(uint16_t nodeIdx, int16_t dra
|
|
|
93
122
|
// single native translation unit (Arduino's auto-prototyper hides this;
|
|
94
123
|
// native emits one TU so explicit forwards are needed).
|
|
95
124
|
static inline int8_t ui_rich_link_hit(uint16_t nodeIdx, int16_t px, int16_t py);
|
|
125
|
+
#ifdef UI_AA
|
|
96
126
|
static inline CuttlefishCanvas16* ui_aa_begin(int16_t w, int16_t h, UI_COLOR_T bg);
|
|
97
127
|
static inline void ui_aa_end(CuttlefishCanvas16* c);
|
|
98
128
|
static inline void ui_aa_push(CuttlefishCanvas16* c, int16_t dx, int16_t dy);
|
|
99
129
|
static inline void ui_aa_line(CuttlefishCanvas16* c, float x0, float y0, float x1, float y1, UI_COLOR_T color);
|
|
100
130
|
static inline void ui_aa_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, float r, UI_COLOR_T color);
|
|
101
131
|
static inline void ui_aa_fill_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, float r, UI_COLOR_T color);
|
|
132
|
+
#endif
|
|
102
133
|
static inline uint8_t ui_repair_current_node_paint_with_parent(uint16_t nodeIdx, UIRect* r);
|
|
103
134
|
static inline void ui_clear_node_paint_rect(uint16_t nodeIdx, const UIRect* paintRect);
|
|
104
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);
|
|
105
141
|
static inline void ui_draw_node_border(uint16_t i, int16_t drawX, int16_t drawY, UI_COLOR_T color);
|
|
106
142
|
static inline void ui_draw_node_outline(uint16_t i, int16_t drawX, int16_t drawY);
|
|
107
143
|
static inline void ui_draw_gradient_fill(uint16_t i, int16_t drawY);
|
|
@@ -115,6 +151,10 @@ static inline void ui_draw_scaled_image(const UIImage* img, int16_t x, int16_t y
|
|
|
115
151
|
int16_t drawW, int16_t drawH);
|
|
116
152
|
|
|
117
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.
|
|
118
158
|
// Persistent canvas slots, all freed on screen change (ui_navigate) so each
|
|
119
159
|
// screen starts with a clean heap. Without this, the first screen's canvas
|
|
120
160
|
// buffer stays resident and fragments the heap, so a later screen's buffer
|
|
@@ -126,6 +166,8 @@ static CuttlefishCanvas16* __ui_list_canvas = nullptr; // virtualized <lis
|
|
|
126
166
|
static int16_t __ui_list_canvas_node = -1; // node currently represented by __ui_list_canvas
|
|
127
167
|
static CuttlefishCanvas16* __ui_node_canvas = nullptr; // <canvas> element offscreen
|
|
128
168
|
static CuttlefishCanvas16* __ui_repair_canvas = nullptr; // buffered-paint / exposed-strip
|
|
169
|
+
static CuttlefishCanvas16* __ui_band_canvas = nullptr; // band renderer (no-PSRAM scroll)
|
|
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;
|
|
131
173
|
// Draw offset: normally zero. The <canvas> allocation fallback sets it so
|
|
@@ -3,12 +3,54 @@
|
|
|
3
3
|
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
4
|
export function emitImageDrawing() {
|
|
5
5
|
return `
|
|
6
|
-
// ── Full-screen framebuffer (opt-in PSRAM
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
6
|
+
// ── Full-screen framebuffer (opt-in PSRAM composition path) ──────────────────
|
|
7
|
+
// Composes a complete frame off-screen so navigation and dirty-node updates do
|
|
8
|
+
// not expose intermediate clears/primitive writes. The panel still receives a
|
|
9
|
+
// sequential SPI transfer; without a TE/vblank signal this is a strong tearing
|
|
10
|
+
// reduction, not a strict hardware-level tear-free guarantee.
|
|
10
11
|
static CuttlefishCanvas16* __ui_fb = nullptr;
|
|
11
12
|
static uint8_t __ui_fb_tried = 0; // 0 = not yet attempted, 1 = alloc attempted
|
|
13
|
+
// Dirty bounds for the retained framebuffer. A local touch update should not
|
|
14
|
+
// retransmit the entire panel: the full-frame burst is long enough to look like
|
|
15
|
+
// a brightness dip on SPI TFTs even though the framebuffer itself is coherent.
|
|
16
|
+
static uint8_t __ui_fb_dirty = 0;
|
|
17
|
+
static int16_t __ui_fb_dirty_x0 = 0;
|
|
18
|
+
static int16_t __ui_fb_dirty_y0 = 0;
|
|
19
|
+
static int16_t __ui_fb_dirty_x1 = 0;
|
|
20
|
+
static int16_t __ui_fb_dirty_y1 = 0;
|
|
21
|
+
|
|
22
|
+
static inline void ui_fb_begin_frame() {
|
|
23
|
+
__ui_fb_dirty = 0;
|
|
24
|
+
__ui_fb_dirty_x0 = display_width();
|
|
25
|
+
__ui_fb_dirty_y0 = display_height();
|
|
26
|
+
__ui_fb_dirty_x1 = 0;
|
|
27
|
+
__ui_fb_dirty_y1 = 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static inline void ui_fb_add_rect(int16_t x, int16_t y, int16_t w, int16_t h) {
|
|
31
|
+
if (w <= 0 || h <= 0) return;
|
|
32
|
+
int16_t x0 = x < 0 ? 0 : x;
|
|
33
|
+
int16_t y0 = y < 0 ? 0 : y;
|
|
34
|
+
int16_t x1 = x + w;
|
|
35
|
+
int16_t y1 = y + h;
|
|
36
|
+
int16_t dw = display_width();
|
|
37
|
+
int16_t dh = display_height();
|
|
38
|
+
if (x1 > dw) x1 = dw;
|
|
39
|
+
if (y1 > dh) y1 = dh;
|
|
40
|
+
if (x0 >= x1 || y0 >= y1) return;
|
|
41
|
+
if (!__ui_fb_dirty) {
|
|
42
|
+
__ui_fb_dirty_x0 = x0;
|
|
43
|
+
__ui_fb_dirty_y0 = y0;
|
|
44
|
+
__ui_fb_dirty_x1 = x1;
|
|
45
|
+
__ui_fb_dirty_y1 = y1;
|
|
46
|
+
__ui_fb_dirty = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (x0 < __ui_fb_dirty_x0) __ui_fb_dirty_x0 = x0;
|
|
50
|
+
if (y0 < __ui_fb_dirty_y0) __ui_fb_dirty_y0 = y0;
|
|
51
|
+
if (x1 > __ui_fb_dirty_x1) __ui_fb_dirty_x1 = x1;
|
|
52
|
+
if (y1 > __ui_fb_dirty_y1) __ui_fb_dirty_y1 = y1;
|
|
53
|
+
}
|
|
12
54
|
|
|
13
55
|
// Get the framebuffer, allocating once (in PSRAM when available). Returns null
|
|
14
56
|
// when PSRAM is absent or the allocation failed — callers fall back to direct.
|
|
@@ -31,14 +73,42 @@ static inline CuttlefishCanvas16* ui_get_framebuffer() {
|
|
|
31
73
|
return __ui_fb;
|
|
32
74
|
}
|
|
33
75
|
|
|
34
|
-
// Push the
|
|
76
|
+
// Push only the changed framebuffer bounds. Rows are sent separately when the
|
|
77
|
+
// dirty union is narrower than the framebuffer because the source canvas has a
|
|
78
|
+
// wider stride than the panel window. Each row is complete before it reaches
|
|
79
|
+
// the panel, and the display adapter can synchronize each window to scanline.
|
|
35
80
|
static inline void ui_push_framebuffer() {
|
|
36
|
-
if (!__ui_fb || !display_canvasBuffer(__ui_fb)) return;
|
|
37
|
-
int16_t
|
|
38
|
-
int16_t
|
|
81
|
+
if (!__ui_fb || !display_canvasBuffer(__ui_fb) || !__ui_fb_dirty) return;
|
|
82
|
+
int16_t fw = display_canvasWidth(__ui_fb);
|
|
83
|
+
int16_t fh = display_canvasHeight(__ui_fb);
|
|
84
|
+
int16_t x = __ui_fb_dirty_x0;
|
|
85
|
+
int16_t y = __ui_fb_dirty_y0;
|
|
86
|
+
int16_t w = static_cast<int16_t>(__ui_fb_dirty_x1 - x);
|
|
87
|
+
int16_t h = static_cast<int16_t>(__ui_fb_dirty_y1 - y);
|
|
88
|
+
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x >= fw || y >= fh) return;
|
|
89
|
+
if (x + w > fw) w = static_cast<int16_t>(fw - x);
|
|
90
|
+
if (y + h > fh) h = static_cast<int16_t>(fh - y);
|
|
91
|
+
// Use the panel's native bulk-pixel path, not drawRGBBitmap(). The native
|
|
92
|
+
// CuttlefishGFX implementation of drawRGBBitmap is intentionally generic and
|
|
93
|
+
// emits one writePixel transaction per pixel; using it for a retained-frame
|
|
94
|
+
// flush turns a small local update into hundreds of visible SPI flashes.
|
|
95
|
+
UI_COLOR_T* pixels = display_canvasBuffer(__ui_fb);
|
|
39
96
|
display_startWrite();
|
|
40
|
-
|
|
41
|
-
|
|
97
|
+
if (x == 0 && w == fw) {
|
|
98
|
+
// Full-width rows are contiguous and can use one bulk write.
|
|
99
|
+
display_setAddrWindow(x, y, w, h);
|
|
100
|
+
display_writePixels(pixels + static_cast<int32_t>(y) * fw,
|
|
101
|
+
static_cast<uint32_t>(w) * h);
|
|
102
|
+
} else {
|
|
103
|
+
// Narrow dirty bounds require one row at a time because the source stride
|
|
104
|
+
// is fw while each destination window is only w pixels wide.
|
|
105
|
+
for (int16_t row = 0; row < h; row++) {
|
|
106
|
+
display_setAddrWindow(x, static_cast<int16_t>(y + row), w, 1);
|
|
107
|
+
display_writePixels(
|
|
108
|
+
pixels + static_cast<int32_t>(y + row) * fw + x,
|
|
109
|
+
static_cast<uint32_t>(w));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
42
112
|
display_endWrite();
|
|
43
113
|
}
|
|
44
114
|
|
|
@@ -82,8 +152,8 @@ static inline void ui_draw_image_with_fit(const UIImage* img, int16_t x, int16_t
|
|
|
82
152
|
offX = 0;
|
|
83
153
|
offY = 0;
|
|
84
154
|
} else if (fitMode == 2 || fitMode == 3 || fitMode == 4) {
|
|
85
|
-
int32_t scaleX = ((
|
|
86
|
-
int32_t scaleY = ((
|
|
155
|
+
int32_t scaleX = (static_cast<int32_t>(targetW) * 1000) / srcW;
|
|
156
|
+
int32_t scaleY = (static_cast<int32_t>(targetH) * 1000) / srcH;
|
|
87
157
|
if (scaleX < 1) scaleX = 1;
|
|
88
158
|
if (scaleY < 1) scaleY = 1;
|
|
89
159
|
int32_t scale = scaleX;
|
|
@@ -95,8 +165,8 @@ static inline void ui_draw_image_with_fit(const UIImage* img, int16_t x, int16_t
|
|
|
95
165
|
if (scaleY < scaleX) scale = scaleY;
|
|
96
166
|
if (scale > 1000) scale = 1000;
|
|
97
167
|
}
|
|
98
|
-
drawW =
|
|
99
|
-
drawH =
|
|
168
|
+
drawW = static_cast<int16_t>(static_cast<int32_t>(srcW) * scale / 1000);
|
|
169
|
+
drawH = static_cast<int16_t>(static_cast<int32_t>(srcH) * scale / 1000);
|
|
100
170
|
if (drawW < 1) drawW = 1;
|
|
101
171
|
if (drawH < 1) drawH = 1;
|
|
102
172
|
if (fitMode == 3) {
|
|
@@ -115,40 +185,40 @@ static inline void ui_draw_image_with_fit(const UIImage* img, int16_t x, int16_t
|
|
|
115
185
|
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
116
186
|
int16_t txStart = 0, txEnd = targetW, tyStart = 0, tyEnd = targetH;
|
|
117
187
|
if (q == 0) {
|
|
118
|
-
txStart = ui_clamp_i16(
|
|
119
|
-
txEnd = ui_clamp_i16(
|
|
120
|
-
tyStart = ui_clamp_i16(
|
|
121
|
-
tyEnd = ui_clamp_i16(
|
|
188
|
+
txStart = ui_clamp_i16(static_cast<int16_t>(clipX - x), 0, targetW);
|
|
189
|
+
txEnd = ui_clamp_i16(static_cast<int16_t>(clipX + clipW - x), 0, targetW);
|
|
190
|
+
tyStart = ui_clamp_i16(static_cast<int16_t>(clipY - y), 0, targetH);
|
|
191
|
+
tyEnd = ui_clamp_i16(static_cast<int16_t>(clipY + clipH - y), 0, targetH);
|
|
122
192
|
} else if (q == 1) {
|
|
123
|
-
txStart = ui_clamp_i16(
|
|
124
|
-
txEnd = ui_clamp_i16(
|
|
125
|
-
tyStart = ui_clamp_i16(
|
|
126
|
-
tyEnd = ui_clamp_i16(
|
|
193
|
+
txStart = ui_clamp_i16(static_cast<int16_t>(clipY - y), 0, targetW);
|
|
194
|
+
txEnd = ui_clamp_i16(static_cast<int16_t>(clipY + clipH - y), 0, targetW);
|
|
195
|
+
tyStart = ui_clamp_i16(static_cast<int16_t>(x + targetH - (clipX + clipW)), 0, targetH);
|
|
196
|
+
tyEnd = ui_clamp_i16(static_cast<int16_t>(x + targetH - clipX), 0, targetH);
|
|
127
197
|
} else if (q == 2) {
|
|
128
|
-
txStart = ui_clamp_i16(
|
|
129
|
-
txEnd = ui_clamp_i16(
|
|
130
|
-
tyStart = ui_clamp_i16(
|
|
131
|
-
tyEnd = ui_clamp_i16(
|
|
198
|
+
txStart = ui_clamp_i16(static_cast<int16_t>(x + targetW - (clipX + clipW)), 0, targetW);
|
|
199
|
+
txEnd = ui_clamp_i16(static_cast<int16_t>(x + targetW - clipX), 0, targetW);
|
|
200
|
+
tyStart = ui_clamp_i16(static_cast<int16_t>(y + targetH - (clipY + clipH)), 0, targetH);
|
|
201
|
+
tyEnd = ui_clamp_i16(static_cast<int16_t>(y + targetH - clipY), 0, targetH);
|
|
132
202
|
} else {
|
|
133
|
-
txStart = ui_clamp_i16(
|
|
134
|
-
txEnd = ui_clamp_i16(
|
|
135
|
-
tyStart = ui_clamp_i16(
|
|
136
|
-
tyEnd = ui_clamp_i16(
|
|
203
|
+
txStart = ui_clamp_i16(static_cast<int16_t>(y + targetW - (clipY + clipH)), 0, targetW);
|
|
204
|
+
txEnd = ui_clamp_i16(static_cast<int16_t>(y + targetW - clipY), 0, targetW);
|
|
205
|
+
tyStart = ui_clamp_i16(static_cast<int16_t>(clipX - x), 0, targetH);
|
|
206
|
+
tyEnd = ui_clamp_i16(static_cast<int16_t>(clipX + clipW - x), 0, targetH);
|
|
137
207
|
}
|
|
138
208
|
if (txStart >= txEnd || tyStart >= tyEnd) return;
|
|
139
209
|
for (int16_t ty = tyStart; ty < tyEnd; ty++) {
|
|
140
210
|
int16_t localY = ty - offY;
|
|
141
211
|
if (localY < 0 || localY >= drawH) continue;
|
|
142
|
-
int16_t srcY = ((
|
|
212
|
+
int16_t srcY = (static_cast<int32_t>(localY) * srcH) / drawH;
|
|
143
213
|
if (srcY < 0) srcY = 0;
|
|
144
214
|
if (srcY >= srcH) srcY = srcH - 1;
|
|
145
215
|
for (int16_t tx = txStart; tx < txEnd; tx++) {
|
|
146
216
|
int16_t localX = tx - offX;
|
|
147
217
|
if (localX < 0 || localX >= drawW) continue;
|
|
148
|
-
int16_t srcX = ((
|
|
218
|
+
int16_t srcX = (static_cast<int32_t>(localX) * srcW) / drawW;
|
|
149
219
|
if (srcX < 0) srcX = 0;
|
|
150
220
|
if (srcX >= srcW) srcX = srcW - 1;
|
|
151
|
-
UI_COLOR_T color = img->data[(
|
|
221
|
+
UI_COLOR_T color = img->data[static_cast<int32_t>(srcY) * srcW + srcX];
|
|
152
222
|
int16_t dx = tx;
|
|
153
223
|
int16_t dy = ty;
|
|
154
224
|
if (q == 1) {
|
|
@@ -177,43 +247,43 @@ static inline void ui_draw_scaled_image(const UIImage* img, int16_t x, int16_t y
|
|
|
177
247
|
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
178
248
|
int16_t dxStart = 0, dxEnd = drawW, dyStart = 0, dyEnd = drawH;
|
|
179
249
|
if (q == 0) {
|
|
180
|
-
dxStart = ui_clamp_i16(
|
|
181
|
-
dxEnd = ui_clamp_i16(
|
|
182
|
-
dyStart = ui_clamp_i16(
|
|
183
|
-
dyEnd = ui_clamp_i16(
|
|
250
|
+
dxStart = ui_clamp_i16(static_cast<int16_t>(clipX - x), 0, drawW);
|
|
251
|
+
dxEnd = ui_clamp_i16(static_cast<int16_t>(clipX + clipW - x), 0, drawW);
|
|
252
|
+
dyStart = ui_clamp_i16(static_cast<int16_t>(clipY - y), 0, drawH);
|
|
253
|
+
dyEnd = ui_clamp_i16(static_cast<int16_t>(clipY + clipH - y), 0, drawH);
|
|
184
254
|
} else if (q == 1) {
|
|
185
|
-
dxStart = ui_clamp_i16(
|
|
186
|
-
dxEnd = ui_clamp_i16(
|
|
187
|
-
dyStart = ui_clamp_i16(
|
|
188
|
-
dyEnd = ui_clamp_i16(
|
|
255
|
+
dxStart = ui_clamp_i16(static_cast<int16_t>(clipY - y), 0, drawW);
|
|
256
|
+
dxEnd = ui_clamp_i16(static_cast<int16_t>(clipY + clipH - y), 0, drawW);
|
|
257
|
+
dyStart = ui_clamp_i16(static_cast<int16_t>(x + drawH - (clipX + clipW)), 0, drawH);
|
|
258
|
+
dyEnd = ui_clamp_i16(static_cast<int16_t>(x + drawH - clipX), 0, drawH);
|
|
189
259
|
} else if (q == 2) {
|
|
190
|
-
dxStart = ui_clamp_i16(
|
|
191
|
-
dxEnd = ui_clamp_i16(
|
|
192
|
-
dyStart = ui_clamp_i16(
|
|
193
|
-
dyEnd = ui_clamp_i16(
|
|
260
|
+
dxStart = ui_clamp_i16(static_cast<int16_t>(x + drawW - (clipX + clipW)), 0, drawW);
|
|
261
|
+
dxEnd = ui_clamp_i16(static_cast<int16_t>(x + drawW - clipX), 0, drawW);
|
|
262
|
+
dyStart = ui_clamp_i16(static_cast<int16_t>(y + drawH - (clipY + clipH)), 0, drawH);
|
|
263
|
+
dyEnd = ui_clamp_i16(static_cast<int16_t>(y + drawH - clipY), 0, drawH);
|
|
194
264
|
} else {
|
|
195
|
-
dxStart = ui_clamp_i16(
|
|
196
|
-
dxEnd = ui_clamp_i16(
|
|
197
|
-
dyStart = ui_clamp_i16(
|
|
198
|
-
dyEnd = ui_clamp_i16(
|
|
265
|
+
dxStart = ui_clamp_i16(static_cast<int16_t>(y + drawW - (clipY + clipH)), 0, drawW);
|
|
266
|
+
dxEnd = ui_clamp_i16(static_cast<int16_t>(y + drawW - clipY), 0, drawW);
|
|
267
|
+
dyStart = ui_clamp_i16(static_cast<int16_t>(clipX - x), 0, drawH);
|
|
268
|
+
dyEnd = ui_clamp_i16(static_cast<int16_t>(clipX + clipW - x), 0, drawH);
|
|
199
269
|
}
|
|
200
270
|
if (dxStart >= dxEnd || dyStart >= dyEnd) return;
|
|
201
271
|
if (q == 0) {
|
|
202
272
|
// Simple case: no rotation, draw with scaling
|
|
203
273
|
if (drawW == img->w && drawH == img->h) {
|
|
204
|
-
int16_t rows =
|
|
205
|
-
int16_t cols =
|
|
274
|
+
int16_t rows = static_cast<int16_t>(dyEnd - dyStart);
|
|
275
|
+
int16_t cols = static_cast<int16_t>(dxEnd - dxStart);
|
|
206
276
|
for (int16_t row = 0; row < rows; row++) {
|
|
207
|
-
ui_display_draw_rgb_bitmap(
|
|
208
|
-
img->data +
|
|
277
|
+
ui_display_draw_rgb_bitmap(static_cast<int16_t>(x + dxStart), static_cast<int16_t>(y + dyStart + row),
|
|
278
|
+
img->data + static_cast<int32_t>(dyStart + row) * img->w + dxStart, cols, 1);
|
|
209
279
|
}
|
|
210
280
|
} else {
|
|
211
281
|
// Scale using nearest-neighbor
|
|
212
282
|
for (int16_t dy = dyStart; dy < dyEnd; dy++) {
|
|
213
|
-
int16_t srcY = ((
|
|
283
|
+
int16_t srcY = (static_cast<int32_t>(dy) * img->h) / drawH;
|
|
214
284
|
for (int16_t dx = dxStart; dx < dxEnd; dx++) {
|
|
215
|
-
int16_t srcX = ((
|
|
216
|
-
UI_COLOR_T color = img->data[(
|
|
285
|
+
int16_t srcX = (static_cast<int32_t>(dx) * img->w) / drawW;
|
|
286
|
+
UI_COLOR_T color = img->data[static_cast<int32_t>(srcY) * img->w + srcX];
|
|
217
287
|
ui_display_draw_pixel(x + dx, y + dy, color);
|
|
218
288
|
}
|
|
219
289
|
}
|
|
@@ -221,10 +291,10 @@ static inline void ui_draw_scaled_image(const UIImage* img, int16_t x, int16_t y
|
|
|
221
291
|
return;
|
|
222
292
|
}
|
|
223
293
|
for (int16_t dy = dyStart; dy < dyEnd; dy++) {
|
|
224
|
-
int16_t srcY = ((
|
|
294
|
+
int16_t srcY = (static_cast<int32_t>(dy) * img->h) / drawH;
|
|
225
295
|
for (int16_t dx = dxStart; dx < dxEnd; dx++) {
|
|
226
|
-
int16_t srcX = ((
|
|
227
|
-
UI_COLOR_T color = img->data[(
|
|
296
|
+
int16_t srcX = (static_cast<int32_t>(dx) * img->w) / drawW;
|
|
297
|
+
UI_COLOR_T color = img->data[static_cast<int32_t>(srcY) * img->w + srcX];
|
|
228
298
|
int16_t rdx = 0, rdy = 0;
|
|
229
299
|
if (q == 1) {
|
|
230
300
|
rdx = drawH - 1 - dy;
|
|
@@ -249,48 +319,48 @@ static inline void ui_draw_image_rotated(const UIImage* img, int16_t x, int16_t
|
|
|
249
319
|
int16_t clipH = (q == 1 || q == 3) ? img->w : img->h;
|
|
250
320
|
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
251
321
|
if (q == 0) {
|
|
252
|
-
int16_t sx0 = ui_clamp_i16(
|
|
253
|
-
int16_t sy0 = ui_clamp_i16(
|
|
254
|
-
int16_t sw = ui_clamp_i16(clipW, 0,
|
|
255
|
-
int16_t sh = ui_clamp_i16(clipH, 0,
|
|
322
|
+
int16_t sx0 = ui_clamp_i16(static_cast<int16_t>(clipX - x), 0, static_cast<int16_t>(img->w));
|
|
323
|
+
int16_t sy0 = ui_clamp_i16(static_cast<int16_t>(clipY - y), 0, static_cast<int16_t>(img->h));
|
|
324
|
+
int16_t sw = ui_clamp_i16(clipW, 0, static_cast<int16_t>(img->w - sx0));
|
|
325
|
+
int16_t sh = ui_clamp_i16(clipH, 0, static_cast<int16_t>(img->h - sy0));
|
|
256
326
|
for (int16_t row = 0; row < sh; row++) {
|
|
257
|
-
ui_display_draw_rgb_bitmap(
|
|
258
|
-
img->data +
|
|
327
|
+
ui_display_draw_rgb_bitmap(static_cast<int16_t>(x + sx0), static_cast<int16_t>(y + sy0 + row),
|
|
328
|
+
img->data + static_cast<int32_t>(sy0 + row) * img->w + sx0, sw, 1);
|
|
259
329
|
}
|
|
260
330
|
return;
|
|
261
331
|
}
|
|
262
332
|
uint16_t sxStart = 0, sxEnd = img->w, syStart = 0, syEnd = img->h;
|
|
263
333
|
if (q == 1) {
|
|
264
|
-
sxStart = (
|
|
265
|
-
sxEnd = (
|
|
266
|
-
syStart = (
|
|
267
|
-
syEnd = (
|
|
334
|
+
sxStart = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(clipY - y), 0, static_cast<int16_t>(img->w)));
|
|
335
|
+
sxEnd = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(clipY + clipH - y), 0, static_cast<int16_t>(img->w)));
|
|
336
|
+
syStart = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(x + img->h - (clipX + clipW)), 0, static_cast<int16_t>(img->h)));
|
|
337
|
+
syEnd = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(x + img->h - clipX), 0, static_cast<int16_t>(img->h)));
|
|
268
338
|
} else if (q == 2) {
|
|
269
|
-
sxStart = (
|
|
270
|
-
sxEnd = (
|
|
271
|
-
syStart = (
|
|
272
|
-
syEnd = (
|
|
339
|
+
sxStart = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(x + img->w - (clipX + clipW)), 0, static_cast<int16_t>(img->w)));
|
|
340
|
+
sxEnd = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(x + img->w - clipX), 0, static_cast<int16_t>(img->w)));
|
|
341
|
+
syStart = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(y + img->h - (clipY + clipH)), 0, static_cast<int16_t>(img->h)));
|
|
342
|
+
syEnd = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(y + img->h - clipY), 0, static_cast<int16_t>(img->h)));
|
|
273
343
|
} else {
|
|
274
|
-
sxStart = (
|
|
275
|
-
sxEnd = (
|
|
276
|
-
syStart = (
|
|
277
|
-
syEnd = (
|
|
344
|
+
sxStart = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(y + img->w - (clipY + clipH)), 0, static_cast<int16_t>(img->w)));
|
|
345
|
+
sxEnd = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(y + img->w - clipY), 0, static_cast<int16_t>(img->w)));
|
|
346
|
+
syStart = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(clipX - x), 0, static_cast<int16_t>(img->h)));
|
|
347
|
+
syEnd = static_cast<uint16_t>(ui_clamp_i16(static_cast<int16_t>(clipX + clipW - x), 0, static_cast<int16_t>(img->h)));
|
|
278
348
|
}
|
|
279
349
|
if (sxStart >= sxEnd || syStart >= syEnd) return;
|
|
280
350
|
for (uint16_t sy = syStart; sy < syEnd; sy++) {
|
|
281
351
|
for (uint16_t sx = sxStart; sx < sxEnd; sx++) {
|
|
282
|
-
UI_COLOR_T color = img->data[(
|
|
352
|
+
UI_COLOR_T color = img->data[static_cast<uint32_t>(sy) * img->w + sx];
|
|
283
353
|
int16_t dx = 0;
|
|
284
354
|
int16_t dy = 0;
|
|
285
355
|
if (q == 1) {
|
|
286
|
-
dx =
|
|
287
|
-
dy = (
|
|
356
|
+
dx = static_cast<int16_t>(img->h - 1 - sy);
|
|
357
|
+
dy = static_cast<int16_t>(sx);
|
|
288
358
|
} else if (q == 2) {
|
|
289
|
-
dx =
|
|
290
|
-
dy =
|
|
359
|
+
dx = static_cast<int16_t>(img->w - 1 - sx);
|
|
360
|
+
dy = static_cast<int16_t>(img->h - 1 - sy);
|
|
291
361
|
} else {
|
|
292
|
-
dx = (
|
|
293
|
-
dy =
|
|
362
|
+
dx = static_cast<int16_t>(sy);
|
|
363
|
+
dy = static_cast<int16_t>(img->w - 1 - sx);
|
|
294
364
|
}
|
|
295
365
|
ui_display_draw_pixel(x + dx, y + dy, color);
|
|
296
366
|
}
|
|
@@ -13,15 +13,22 @@ static inline void ui_init(void) {
|
|
|
13
13
|
// containers start locked until the scroll-container loop proves their
|
|
14
14
|
// canvas fits.
|
|
15
15
|
if (!__ui_scroll_canvas_ok && __ui_node_count > 0) {
|
|
16
|
-
__ui_scroll_canvas_ok = (uint8_t
|
|
16
|
+
__ui_scroll_canvas_ok = new (std::nothrow) uint8_t[__ui_node_count]();
|
|
17
17
|
}
|
|
18
|
-
if (!
|
|
19
|
-
|
|
18
|
+
if (!__ui_scroll_render_locked && __ui_node_count > 0) {
|
|
19
|
+
__ui_scroll_render_locked = new (std::nothrow) uint8_t[__ui_node_count]();
|
|
20
20
|
}
|
|
21
21
|
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
22
22
|
__ui_nodes[i].dirty = 1;
|
|
23
23
|
__ui_nodes[i].lastTextHeight = 0;
|
|
24
24
|
__ui_nodes[i].layoutCacheKey = 0;
|
|
25
|
+
if (__ui_nodes[i].scrollable) {
|
|
26
|
+
// Force the first scroll-canvas composition to seed every pixel. After
|
|
27
|
+
// that, a dirty child can be repaired in place without repainting the
|
|
28
|
+
// entire viewport.
|
|
29
|
+
__ui_nodes[i].lastPaintedScrollY = static_cast<int16_t>(
|
|
30
|
+
__ui_nodes[i].scrollY - (__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1));
|
|
31
|
+
}
|
|
25
32
|
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) {
|
|
26
33
|
__ui_nodes[i].lastTextWidth = -1;
|
|
27
34
|
}
|
|
@@ -32,6 +39,11 @@ static inline void ui_init(void) {
|
|
|
32
39
|
__ui_nodes[n].hasTextBinding = 1;
|
|
33
40
|
strncpy(__ui_nodes[n].textBuffer, __ui_nodes[n].text ? __ui_nodes[n].text : "", UI_TEXT_BUF);
|
|
34
41
|
__ui_nodes[n].textBuffer[UI_TEXT_BUF] = '\\0';
|
|
42
|
+
} else if (__ui_bindings[i].fn) {
|
|
43
|
+
// Prime numeric binding caches once at setup; the first tick can then skip
|
|
44
|
+
// redundant assignments and dirty propagation when values are unchanged.
|
|
45
|
+
__ui_bindings[i].lastValue = __ui_bindings[i].fn();
|
|
46
|
+
__ui_bindings[i].initialized = 1;
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
// Seed virtualized-list runtime state. The fn pointers can't be baked into
|
|
@@ -52,7 +64,7 @@ static inline void ui_init(void) {
|
|
|
52
64
|
uint16_t ih = __ui_nodes[i].listItemHeight > 0 ? __ui_nodes[i].listItemHeight : 24;
|
|
53
65
|
uint16_t cnt = __ui_nodes[i].listCountFn();
|
|
54
66
|
__ui_nodes[i].listCount = cnt;
|
|
55
|
-
__ui_nodes[i].contentHeight =
|
|
67
|
+
__ui_nodes[i].contentHeight = static_cast<int16_t>(static_cast<uint32_t>(cnt) * ih);
|
|
56
68
|
__ui_nodes[i].scrollY = 0;
|
|
57
69
|
__ui_nodes[i].overscrollPx = 0;
|
|
58
70
|
__ui_nodes[i].settling = 0;
|
|
@@ -61,6 +73,9 @@ static inline void ui_init(void) {
|
|
|
61
73
|
ui_build_draw_order();
|
|
62
74
|
ui_build_scroll_owner_table();
|
|
63
75
|
ui_refresh_active_screen_bg_node();
|
|
76
|
+
if (!__ui_scroll_candidates && __ui_node_count > 0) {
|
|
77
|
+
__ui_scroll_candidates = new (std::nothrow) UIScrollPaintCandidate[__ui_node_count];
|
|
78
|
+
}
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
// Debounce: ignore press/release events within 50ms of the last edge.
|
|
@@ -99,7 +114,7 @@ static inline void ui_on_release(uint16_t nodeIdx) {
|
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
// Pin-watch callback type: void fn(void)
|
|
102
|
-
|
|
117
|
+
using PinWatchCallback = void (*)(void);
|
|
103
118
|
|
|
104
119
|
struct UIPinWatch {
|
|
105
120
|
uint8_t pin;
|