@typecad/ui 0.1.0-alpha.1 → 1.0.0-alpha.10
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 +7 -1
- 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 +86 -86
- 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
|
@@ -12,10 +12,18 @@ export function emitAntialiasing(): string {
|
|
|
12
12
|
// then pushed to the display. The ILI9341 has no efficient SPI read-back, so
|
|
13
13
|
// all blending happens in RAM.
|
|
14
14
|
#ifdef UI_AA
|
|
15
|
+
#include <math.h> // fabs, floor, ceil, sqrtf for AA coverage math
|
|
16
|
+
|
|
17
|
+
// AA is only useful on RAM-backed targets. Direct SPI targets have no safe
|
|
18
|
+
// read/modify/write surface for coverage blending; callers still get the
|
|
19
|
+
// regular primitive path instead of an allocation plus many tiny pushes.
|
|
20
|
+
static inline uint8_t ui_aa_target_is_ram() {
|
|
21
|
+
return !ui_display_is_default_target();
|
|
22
|
+
}
|
|
15
23
|
|
|
16
24
|
// Get (or allocate) a canvas sized to the element being drawn.
|
|
17
25
|
static inline CuttlefishCanvas16* ui_aa_begin(int16_t w, int16_t h, UI_COLOR_T bg) {
|
|
18
|
-
if (w <= 0 || h <= 0) return nullptr;
|
|
26
|
+
if (w <= 0 || h <= 0 || !ui_aa_target_is_ram()) return nullptr;
|
|
19
27
|
if (!__ui_aa_canvas || display_canvasWidth(__ui_aa_canvas) < w || display_canvasHeight(__ui_aa_canvas) < h) {
|
|
20
28
|
display_deleteCanvas(__ui_aa_canvas);
|
|
21
29
|
__ui_aa_canvas = display_createCanvas(w > 0 ? w : 1, h > 0 ? h : 1);
|
|
@@ -33,7 +41,7 @@ static inline void ui_aa_push(CuttlefishCanvas16* c, int16_t dx, int16_t dy) {
|
|
|
33
41
|
// or the display directly when not. Row-by-row drawRGBBitmap (no transparent
|
|
34
42
|
// alpha — the AA canvas already has the blended pixels).
|
|
35
43
|
for (int16_t row = 0; row < h; row++) {
|
|
36
|
-
ui_display_draw_rgb_bitmap(dx, dy + row, display_canvasBuffer(c) + (
|
|
44
|
+
ui_display_draw_rgb_bitmap(dx, dy + row, display_canvasBuffer(c) + static_cast<int32_t>(row) * w, w, 1);
|
|
37
45
|
}
|
|
38
46
|
}
|
|
39
47
|
|
|
@@ -44,16 +52,16 @@ static inline void ui_aa_pixel(CuttlefishCanvas16* c, int16_t x, int16_t y, UI_C
|
|
|
44
52
|
if (x < 0 || y < 0 || x >= display_canvasWidth(c) || y >= display_canvasHeight(c)) return;
|
|
45
53
|
if (cov >= 255) { display_targetDrawPixel((CuttlefishDisplayTarget*)c, x, y, color); return; }
|
|
46
54
|
UI_COLOR_T bg = display_canvasGetPixel(c, x, y);
|
|
47
|
-
uint8_t op =
|
|
55
|
+
uint8_t op = static_cast<uint8_t>(static_cast<uint16_t>(cov) * 100 / 255);
|
|
48
56
|
display_targetDrawPixel((CuttlefishDisplayTarget*)c, x, y, ui_blend(color, bg, op));
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
// Xiaolin Wu antialiased line. Coordinates are in canvas-local space.
|
|
52
60
|
static inline void ui_aa_line(CuttlefishCanvas16* c, float x0, float y0, float x1, float y1, UI_COLOR_T color) {
|
|
53
61
|
if (!c || !display_canvasBuffer(c)) return;
|
|
54
|
-
auto ipart = [](float f) { return (
|
|
55
|
-
auto round_f = [](float f) { return
|
|
56
|
-
auto fpart = [](float f) { return f -
|
|
62
|
+
auto ipart = [](float f) { return static_cast<int16_t>(f); };
|
|
63
|
+
auto round_f = [](float f) { return static_cast<int16_t>(f + 0.5f); };
|
|
64
|
+
auto fpart = [](float f) { return f - static_cast<float>(static_cast<int16_t>(f)); };
|
|
57
65
|
auto rfpart = [&](float f) { return 1.0f - fpart(f); };
|
|
58
66
|
|
|
59
67
|
bool steep = fabs(y1 - y0) > fabs(x1 - x0);
|
|
@@ -63,17 +71,29 @@ static inline void ui_aa_line(CuttlefishCanvas16* c, float x0, float y0, float x
|
|
|
63
71
|
float dx = x1 - x0, dy = y1 - y0;
|
|
64
72
|
float gradient = (dx == 0) ? 1.0f : dy / dx;
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
float xend =
|
|
68
|
-
float
|
|
74
|
+
// Handle first endpoint.
|
|
75
|
+
float xend = static_cast<float>(round_f(x0));
|
|
76
|
+
float yend = y0 + gradient * (xend - x0);
|
|
77
|
+
float xgap = rfpart(x0);
|
|
78
|
+
int16_t xpx1 = static_cast<int16_t>(xend);
|
|
79
|
+
int16_t ypx1 = ipart(yend);
|
|
80
|
+
if (steep) {
|
|
81
|
+
ui_aa_pixel(c, ypx1, xpx1, color, static_cast<uint8_t>(rfpart(yend) * xgap * 255));
|
|
82
|
+
ui_aa_pixel(c, ypx1 + 1, xpx1, color, static_cast<uint8_t>(fpart(yend) * xgap * 255));
|
|
83
|
+
} else {
|
|
84
|
+
ui_aa_pixel(c, xpx1, ypx1, color, static_cast<uint8_t>(rfpart(yend) * xgap * 255));
|
|
85
|
+
ui_aa_pixel(c, xpx1, ypx1 + 1, color, static_cast<uint8_t>(fpart(yend) * xgap * 255));
|
|
86
|
+
}
|
|
87
|
+
float intery = yend + gradient;
|
|
69
88
|
|
|
70
|
-
|
|
89
|
+
// Main loop.
|
|
90
|
+
for (int16_t x = xpx1 + 1; x <= static_cast<int16_t>(round_f(x1)) - 1; x++) {
|
|
71
91
|
if (steep) {
|
|
72
|
-
ui_aa_pixel(c, ipart(intery), x, color,
|
|
73
|
-
ui_aa_pixel(c, ipart(intery) + 1, x, color,
|
|
92
|
+
ui_aa_pixel(c, ipart(intery), x, color, static_cast<uint8_t>(rfpart(intery) * 255));
|
|
93
|
+
ui_aa_pixel(c, ipart(intery) + 1, x, color, static_cast<uint8_t>(fpart(intery) * 255));
|
|
74
94
|
} else {
|
|
75
|
-
ui_aa_pixel(c, x, ipart(intery), color,
|
|
76
|
-
ui_aa_pixel(c, x, ipart(intery) + 1, color,
|
|
95
|
+
ui_aa_pixel(c, x, ipart(intery), color, static_cast<uint8_t>(rfpart(intery) * 255));
|
|
96
|
+
ui_aa_pixel(c, x, ipart(intery) + 1, color, static_cast<uint8_t>(fpart(intery) * 255));
|
|
77
97
|
}
|
|
78
98
|
intery += gradient;
|
|
79
99
|
}
|
|
@@ -84,24 +104,24 @@ static inline void ui_aa_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, f
|
|
|
84
104
|
if (!c || !display_canvasBuffer(c)) return;
|
|
85
105
|
if (r <= 0) return;
|
|
86
106
|
// Walk each scanline from top to bottom of the bounding box.
|
|
87
|
-
int16_t y0 = (
|
|
88
|
-
int16_t y1 = (
|
|
107
|
+
int16_t y0 = static_cast<int16_t>(floor(cy - r));
|
|
108
|
+
int16_t y1 = static_cast<int16_t>(ceil(cy + r));
|
|
89
109
|
for (int16_t y = y0; y <= y1; y++) {
|
|
90
|
-
float dy = (
|
|
110
|
+
float dy = static_cast<float>(y) - cy;
|
|
91
111
|
float dx = r * r - dy * dy;
|
|
92
112
|
if (dx < 0) continue;
|
|
93
113
|
float halfW = sqrtf(dx);
|
|
94
|
-
float leftX = (
|
|
95
|
-
float rightX = (
|
|
114
|
+
float leftX = static_cast<float>(cx) - halfW;
|
|
115
|
+
float rightX = static_cast<float>(cx) + halfW;
|
|
96
116
|
// Left edge: blend two pixels at the coverage split.
|
|
97
|
-
int16_t lx = (
|
|
117
|
+
int16_t lx = static_cast<int16_t>(floor(leftX));
|
|
98
118
|
float lfrac = leftX - lx;
|
|
99
|
-
ui_aa_pixel(c, lx, y, color,
|
|
119
|
+
ui_aa_pixel(c, lx, y, color, static_cast<uint8_t>((1.0f - lfrac) * 255));
|
|
100
120
|
ui_aa_pixel(c, lx + 1, y, color, 0); // interior starts here (drawn solid below)
|
|
101
121
|
// Right edge.
|
|
102
|
-
int16_t rx = (
|
|
122
|
+
int16_t rx = static_cast<int16_t>(floor(rightX));
|
|
103
123
|
float rfrac = rightX - rx;
|
|
104
|
-
ui_aa_pixel(c, rx, y, color,
|
|
124
|
+
ui_aa_pixel(c, rx, y, color, static_cast<uint8_t>(rfrac * 255));
|
|
105
125
|
ui_aa_pixel(c, rx + 1, y, color, 0);
|
|
106
126
|
// Solid fill between edges (skip the edge pixels already blended).
|
|
107
127
|
for (int16_t x = lx + 1; x < rx; x++) {
|
|
@@ -114,21 +134,21 @@ static inline void ui_aa_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, f
|
|
|
114
134
|
static inline void ui_aa_fill_circle(CuttlefishCanvas16* c, int16_t cx, int16_t cy, float r, UI_COLOR_T color) {
|
|
115
135
|
if (!c || !display_canvasBuffer(c)) return;
|
|
116
136
|
if (r <= 0) return;
|
|
117
|
-
int16_t y0 = (
|
|
118
|
-
int16_t y1 = (
|
|
137
|
+
int16_t y0 = static_cast<int16_t>(floor(cy - r));
|
|
138
|
+
int16_t y1 = static_cast<int16_t>(ceil(cy + r));
|
|
119
139
|
for (int16_t y = y0; y <= y1; y++) {
|
|
120
|
-
float dy = (
|
|
140
|
+
float dy = static_cast<float>(y) - cy;
|
|
121
141
|
float dx = r * r - dy * dy;
|
|
122
142
|
if (dx < 0) continue;
|
|
123
143
|
float halfW = sqrtf(dx);
|
|
124
|
-
float leftX = (
|
|
125
|
-
float rightX = (
|
|
126
|
-
int16_t lx = (
|
|
127
|
-
int16_t rx = (
|
|
144
|
+
float leftX = static_cast<float>(cx) - halfW;
|
|
145
|
+
float rightX = static_cast<float>(cx) + halfW;
|
|
146
|
+
int16_t lx = static_cast<int16_t>(floor(leftX));
|
|
147
|
+
int16_t rx = static_cast<int16_t>(ceil(rightX));
|
|
128
148
|
// Blend left edge.
|
|
129
|
-
ui_aa_pixel(c, lx, y, color,
|
|
149
|
+
ui_aa_pixel(c, lx, y, color, static_cast<uint8_t>((1.0f - (leftX - lx)) * 255));
|
|
130
150
|
// Blend right edge.
|
|
131
|
-
ui_aa_pixel(c, rx, y, color,
|
|
151
|
+
ui_aa_pixel(c, rx, y, color, static_cast<uint8_t>((rightX - (rx - 1)) * 255));
|
|
132
152
|
// Solid interior.
|
|
133
153
|
for (int16_t x = lx + 1; x < rx; x++) {
|
|
134
154
|
if (x >= 0 && x < display_canvasWidth(c) && y >= 0 && y < display_canvasHeight(c)) display_targetDrawPixel((CuttlefishDisplayTarget*)c, x, y, color);
|
|
@@ -15,9 +15,9 @@ static inline uint16_t ui_blend565(uint16_t fg, uint16_t bg, uint8_t opacity) {
|
|
|
15
15
|
// Unpack to 8-bit (5-bit → 8-bit: (v << 3) | (v >> 2)).
|
|
16
16
|
uint16_t fr8 = (fr << 3) | (fr >> 2), fg8 = (fg5 << 2) | (fg5 >> 4), fb8 = (fb << 3) | (fb >> 2);
|
|
17
17
|
uint16_t br8 = (br << 3) | (br >> 2), bg8 = (bg5 << 2) | (bg5 >> 4), bb8 = (bb << 3) | (bb >> 2);
|
|
18
|
-
uint16_t r =
|
|
19
|
-
uint16_t g =
|
|
20
|
-
uint16_t b =
|
|
18
|
+
uint16_t r = static_cast<uint16_t>((fr8 * opacity + br8 * (100 - opacity)) / 100);
|
|
19
|
+
uint16_t g = static_cast<uint16_t>((fg8 * opacity + bg8 * (100 - opacity)) / 100);
|
|
20
|
+
uint16_t b = static_cast<uint16_t>((fb8 * opacity + bb8 * (100 - opacity)) / 100);
|
|
21
21
|
return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -33,7 +33,7 @@ static inline uint32_t ui_blend888(uint32_t fg, uint32_t bg, uint8_t opacity) {
|
|
|
33
33
|
uint8_t r = (fr * opacity + br * (100 - opacity)) / 100;
|
|
34
34
|
uint8_t g = (fg8 * opacity + bg8 * (100 - opacity)) / 100;
|
|
35
35
|
uint8_t b = (fb * opacity + bb * (100 - opacity)) / 100;
|
|
36
|
-
return ((
|
|
36
|
+
return (static_cast<uint32_t>(r) << 16) | (static_cast<uint32_t>(g) << 8) | b;
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
39
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Slice of the C++ runtime header (original source lines 753-901).
|
|
2
|
-
// Persistent canvas release/create/get/shift/repair
|
|
2
|
+
// Persistent canvas release/create/get/shift/repair helpers.
|
|
3
3
|
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
4
|
export function emitCanvasHelpers(): string {
|
|
5
5
|
return `
|
|
@@ -10,6 +10,8 @@ static inline void ui_release_canvas_state() {
|
|
|
10
10
|
__ui_list_canvas_node = -1;
|
|
11
11
|
display_deleteCanvas(__ui_node_canvas); __ui_node_canvas = nullptr;
|
|
12
12
|
display_deleteCanvas(__ui_repair_canvas); __ui_repair_canvas = nullptr;
|
|
13
|
+
display_deleteCanvas(__ui_band_canvas); __ui_band_canvas = nullptr;
|
|
14
|
+
display_deleteCanvas(__ui_kb_canvas); __ui_kb_canvas = nullptr;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
// Lazily allocate/reuse a viewport-sized canvas for a scroll container. Resizes
|
|
@@ -22,7 +24,18 @@ static inline CuttlefishCanvas16* ui_create_canvas_best(int16_t w, int16_t h) {
|
|
|
22
24
|
#if defined(ESP32) && defined(BOARD_HAS_PSRAM)
|
|
23
25
|
if (psramFound()) {
|
|
24
26
|
CuttlefishCanvas16* c = display_createCanvasPsram(w, h);
|
|
25
|
-
if (c && display_canvasBuffer(c))
|
|
27
|
+
if (c && display_canvasBuffer(c)) {
|
|
28
|
+
static uint8_t psram_logged = 0;
|
|
29
|
+
if (!psram_logged) {
|
|
30
|
+
psram_logged = 1;
|
|
31
|
+
#if defined(ESP32) && defined(ARDUINO)
|
|
32
|
+
Serial.printf("[psram] canvas %dx%d allocated in PSRAM (free=%u)\n", w, h, ESP.getFreePsram());
|
|
33
|
+
#else
|
|
34
|
+
printf("[psram] canvas %dx%d allocated in PSRAM\n", w, h);
|
|
35
|
+
#endif
|
|
36
|
+
}
|
|
37
|
+
return c;
|
|
38
|
+
}
|
|
26
39
|
// PSRAM allocation failed (rare — fragmented PSRAM) → fall through to SRAM.
|
|
27
40
|
}
|
|
28
41
|
#endif
|
|
@@ -30,63 +43,18 @@ static inline CuttlefishCanvas16* ui_create_canvas_best(int16_t w, int16_t h) {
|
|
|
30
43
|
}
|
|
31
44
|
|
|
32
45
|
static inline CuttlefishCanvas16* ui_get_container_canvas(int16_t w, int16_t h) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
static inline const char* __ui_scroll_node_id(uint16_t idx);
|
|
46
|
-
|
|
47
|
-
// Emit a one-time Serial warning when scroll rendering cannot be accurate due
|
|
48
|
-
// to heap limits. reason: 0=canvas alloc failed, 1=viewport exceeds budget,
|
|
49
|
-
// 2=Mode C strip fallback (reduced accuracy).
|
|
50
|
-
static inline void ui_warn_scroll_memory(uint16_t nodeIdx, uint8_t reason) {
|
|
51
|
-
if (nodeIdx >= __ui_node_count) return;
|
|
52
|
-
if (!__ui_scroll_mem_warned) return;
|
|
53
|
-
if (__ui_scroll_mem_warned[nodeIdx]) return;
|
|
54
|
-
__ui_scroll_mem_warned[nodeIdx] = 1;
|
|
55
|
-
|
|
56
|
-
int16_t vw = __ui_nodes[nodeIdx].box.w;
|
|
57
|
-
int16_t vh = __ui_nodes[nodeIdx].box.h;
|
|
58
|
-
uint32_t need = (uint32_t)(vw > 0 ? vw : 0) * (uint32_t)(vh > 0 ? vh : 0) * 2u;
|
|
59
|
-
const char* id = __ui_scroll_node_id(nodeIdx);
|
|
60
|
-
const char* label = (id && id[0]) ? id : "scroll viewport";
|
|
61
|
-
const char* reasonText = "scroll canvas allocation failed";
|
|
62
|
-
if (reason == 1) reasonText = "scroll viewport exceeds compile-time canvas budget";
|
|
63
|
-
else if (reason == 2) reasonText = "using Mode C strip fallback (smooth scroll canvas unavailable)";
|
|
64
|
-
|
|
65
|
-
#if defined(ESP32)
|
|
66
|
-
uint32_t freeHeap = ESP.getFreeHeap();
|
|
67
|
-
uint32_t maxAlloc = ESP.getMaxAllocHeap();
|
|
68
|
-
Serial.printf(
|
|
69
|
-
"[cuttlefish] WARNING: #%s (%dx%d) needs %lu bytes for accurate scroll — %s. "
|
|
70
|
-
"heap free=%lu max_alloc=%lu budget=%d. "
|
|
71
|
-
"Shrink the scroll viewport in CSS, trim fonts/images, or use PSRAM.\\n",
|
|
72
|
-
label, vw, vh, (unsigned long)need, reasonText,
|
|
73
|
-
(unsigned long)freeHeap, (unsigned long)maxAlloc, UI_SCROLL_CANVAS_BUDGET_BYTES);
|
|
74
|
-
#elif defined(ESP8266)
|
|
75
|
-
uint32_t freeHeap = ESP.getFreeHeap();
|
|
76
|
-
Serial.printf(
|
|
77
|
-
"[cuttlefish] WARNING: #%s (%dx%d) needs %lu bytes for accurate scroll — %s. "
|
|
78
|
-
"heap free=%lu budget=%d. "
|
|
79
|
-
"Shrink the scroll viewport in CSS, trim fonts/images, or reduce UI footprint.\\n",
|
|
80
|
-
label, vw, vh, (unsigned long)need, reasonText,
|
|
81
|
-
(unsigned long)freeHeap, UI_SCROLL_CANVAS_BUDGET_BYTES);
|
|
82
|
-
#else
|
|
83
|
-
// Non-Arduino target (e.g. SDL native): no Serial, so use standard-C printf.
|
|
84
|
-
// The native framework forces <cstdio> so printf is available here.
|
|
85
|
-
printf(
|
|
86
|
-
"[cuttlefish] WARNING: #%s (%dx%d) needs %lu bytes for accurate scroll — %s. "
|
|
87
|
-
"budget=%d. Shrink the scroll viewport in CSS or trim UI assets.\\n",
|
|
88
|
-
label, vw, vh, (unsigned long)need, reasonText, UI_SCROLL_CANVAS_BUDGET_BYTES);
|
|
89
|
-
#endif
|
|
46
|
+
// Intentionally returns nullptr: scroll containers always use the band
|
|
47
|
+
// renderer (ui_render_scroll_bands), not the Mode B viewport canvas.
|
|
48
|
+
// Mode B holds the whole viewport in one large canvas and pushes it in a
|
|
49
|
+
// single transaction every frame — on SPI TFTs that full-viewport push
|
|
50
|
+
// (especially from PSRAM) visibly flashes on child state changes like a
|
|
51
|
+
// button press. The band renderer composites into a small ~10KB SRAM canvas
|
|
52
|
+
// strip-by-strip, pushing each band only after it's complete — tear-free and
|
|
53
|
+
// flash-free regardless of target memory. PSRAM still benefits the other
|
|
54
|
+
// canvases (list, keyboard, repair); only the scroll viewport deliberately
|
|
55
|
+
// bypasses it for visual quality.
|
|
56
|
+
(void)w; (void)h;
|
|
57
|
+
return nullptr;
|
|
90
58
|
}
|
|
91
59
|
|
|
92
60
|
// Shift the canvas buffer vertically by deltaY (cheap memmove of existing
|
|
@@ -117,16 +85,16 @@ static inline void ui_shift_container_canvas(CuttlefishCanvas16* canvas, int16_t
|
|
|
117
85
|
// band at the TOP. (Matches the proven pre-rewrite direction.)
|
|
118
86
|
if (deltaY > 0) {
|
|
119
87
|
for (int16_t row = 0; row < h - shift; row++) {
|
|
120
|
-
memmove(pixels + (
|
|
121
|
-
pixels +
|
|
122
|
-
(
|
|
88
|
+
memmove(pixels + static_cast<int32_t>(row) * stride,
|
|
89
|
+
pixels + static_cast<int32_t>(row + shift) * stride,
|
|
90
|
+
static_cast<size_t>(contentW) * sizeof(UI_COLOR_T));
|
|
123
91
|
}
|
|
124
92
|
if (exposedY) *exposedY = h - shift;
|
|
125
93
|
} else {
|
|
126
94
|
for (int16_t row = h - shift - 1; row >= 0; row--) {
|
|
127
|
-
memmove(pixels +
|
|
128
|
-
pixels + (
|
|
129
|
-
(
|
|
95
|
+
memmove(pixels + static_cast<int32_t>(row + shift) * stride,
|
|
96
|
+
pixels + static_cast<int32_t>(row) * stride,
|
|
97
|
+
static_cast<size_t>(contentW) * sizeof(UI_COLOR_T));
|
|
130
98
|
}
|
|
131
99
|
if (exposedY) *exposedY = 0;
|
|
132
100
|
}
|
|
@@ -139,13 +107,18 @@ static inline void ui_shift_container_canvas(CuttlefishCanvas16* canvas, int16_t
|
|
|
139
107
|
}
|
|
140
108
|
|
|
141
109
|
// Repair canvas: parent-seeded background repaints + exposed-strip redraws.
|
|
142
|
-
//
|
|
143
|
-
//
|
|
110
|
+
// Reused across navigations when size matches; reallocated on exact-size
|
|
111
|
+
// mismatch (a reused larger canvas would keep its old stride, corrupting the
|
|
112
|
+
// pushed pixels — see the inline comment below).
|
|
144
113
|
static inline CuttlefishCanvas16* ui_get_repair_canvas(int16_t w, int16_t h) {
|
|
145
114
|
if (w <= 0 || h <= 0) return nullptr;
|
|
115
|
+
// Reallocate when size differs at all (not just when growing). A reused
|
|
116
|
+
// larger canvas keeps its old stride, and ui_push_canvas_rect uses that
|
|
117
|
+
// stride for row offsets — a mismatch with the caller's expected (w,h)
|
|
118
|
+
// corrupts the pushed pixels. Exact-size reallocation guarantees stride == w.
|
|
146
119
|
if (!__ui_repair_canvas || !display_canvasBuffer(__ui_repair_canvas) ||
|
|
147
|
-
display_canvasWidth(__ui_repair_canvas)
|
|
148
|
-
display_canvasHeight(__ui_repair_canvas)
|
|
120
|
+
display_canvasWidth(__ui_repair_canvas) != w ||
|
|
121
|
+
display_canvasHeight(__ui_repair_canvas) != h) {
|
|
149
122
|
display_deleteCanvas(__ui_repair_canvas);
|
|
150
123
|
__ui_repair_canvas = ui_create_canvas_best(w, h);
|
|
151
124
|
}
|
|
@@ -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)
|