@typecad/ui 1.0.0-alpha.7 → 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.
Files changed (84) hide show
  1. package/README.md +6 -0
  2. package/dist/engine-index.js +0 -2
  3. package/dist/preview/host-ui-runtime.d.ts +1 -0
  4. package/dist/preview/host-ui-runtime.js +56 -11
  5. package/dist/ui-engine/font-assets.d.ts +8 -0
  6. package/dist/ui-engine/font-assets.js +13 -0
  7. package/dist/ui-engine/layout-engine.js +19 -6
  8. package/dist/ui-engine/model.d.ts +1 -1
  9. package/dist/ui-engine/model.js +10 -4
  10. package/dist/ui-engine/runtime-header/antialiasing.js +52 -32
  11. package/dist/ui-engine/runtime-header/blend-bodies.js +4 -4
  12. package/dist/ui-engine/runtime-header/canvas-helpers.js +32 -65
  13. package/dist/ui-engine/runtime-header/canvas-scrollbar.js +22 -10
  14. package/dist/ui-engine/runtime-header/color-mono-refresh.js +10 -10
  15. package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +59 -29
  16. package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +26 -10
  17. package/dist/ui-engine/runtime-header/display-shim.js +8 -8
  18. package/dist/ui-engine/runtime-header/forward-decls.js +48 -6
  19. package/dist/ui-engine/runtime-header/image-drawing.js +154 -84
  20. package/dist/ui-engine/runtime-header/init-press-input.js +20 -5
  21. package/dist/ui-engine/runtime-header/keyboard.js +19 -16
  22. package/dist/ui-engine/runtime-header/node-decoration.js +8 -8
  23. package/dist/ui-engine/runtime-header/node-draw-body.d.ts +1 -0
  24. package/dist/ui-engine/runtime-header/node-draw-body.js +1402 -0
  25. package/dist/ui-engine/runtime-header/paint-order-coords.js +48 -18
  26. package/dist/ui-engine/runtime-header/paint-rects-repair.js +38 -17
  27. package/dist/ui-engine/runtime-header/scroll-physics.js +15 -14
  28. package/dist/ui-engine/runtime-header/state-bindings-nav.js +23 -13
  29. package/dist/ui-engine/runtime-header/structs.js +12 -10
  30. package/dist/ui-engine/runtime-header/text-rendering.js +132 -71
  31. package/dist/ui-engine/runtime-header/tick/bindings-phase.js +29 -10
  32. package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +299 -747
  33. package/dist/ui-engine/runtime-header/tick/flush-phase.js +7 -3
  34. package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +19 -3
  35. package/dist/ui-engine/runtime-header/tick/transitions-phase.js +31 -24
  36. package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +42 -40
  37. package/dist/ui-engine/runtime-header/types-defines.js +23 -3
  38. package/dist/ui-engine/runtime-header.js +2 -0
  39. package/dist/ui-engine/transpile-ui.js +2 -2
  40. package/dist/ui-engine/ua-stylesheet.js +46 -3
  41. package/dist/ui-engine/ui-lowering.d.ts +1 -3
  42. package/dist/ui-engine/ui-lowering.js +4 -23
  43. package/dist/ui-engine/ui-registry.js +1 -7
  44. package/package.json +2 -2
  45. package/src/engine-index.ts +0 -2
  46. package/src/preview/host-ui-runtime.ts +56 -12
  47. package/src/ui-engine/font-assets.ts +13 -0
  48. package/src/ui-engine/layout-engine.ts +19 -6
  49. package/src/ui-engine/model.ts +10 -5
  50. package/src/ui-engine/runtime-header/antialiasing.ts +52 -32
  51. package/src/ui-engine/runtime-header/blend-bodies.ts +4 -4
  52. package/src/ui-engine/runtime-header/canvas-helpers.ts +32 -65
  53. package/src/ui-engine/runtime-header/canvas-scrollbar.ts +22 -10
  54. package/src/ui-engine/runtime-header/color-mono-refresh.ts +10 -10
  55. package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +59 -29
  56. package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +26 -10
  57. package/src/ui-engine/runtime-header/display-shim.ts +8 -8
  58. package/src/ui-engine/runtime-header/forward-decls.ts +48 -6
  59. package/src/ui-engine/runtime-header/image-drawing.ts +154 -84
  60. package/src/ui-engine/runtime-header/init-press-input.ts +20 -5
  61. package/src/ui-engine/runtime-header/keyboard.ts +19 -16
  62. package/src/ui-engine/runtime-header/node-decoration.ts +8 -8
  63. package/src/ui-engine/runtime-header/node-draw-body.ts +1402 -0
  64. package/src/ui-engine/runtime-header/paint-order-coords.ts +48 -18
  65. package/src/ui-engine/runtime-header/paint-rects-repair.ts +38 -17
  66. package/src/ui-engine/runtime-header/scroll-physics.ts +15 -14
  67. package/src/ui-engine/runtime-header/state-bindings-nav.ts +23 -13
  68. package/src/ui-engine/runtime-header/structs.ts +12 -10
  69. package/src/ui-engine/runtime-header/text-rendering.ts +132 -71
  70. package/src/ui-engine/runtime-header/tick/bindings-phase.ts +29 -10
  71. package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +299 -747
  72. package/src/ui-engine/runtime-header/tick/flush-phase.ts +7 -3
  73. package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +19 -3
  74. package/src/ui-engine/runtime-header/tick/transitions-phase.ts +31 -24
  75. package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +42 -40
  76. package/src/ui-engine/runtime-header/types-defines.ts +23 -3
  77. package/src/ui-engine/runtime-header.ts +2 -0
  78. package/src/ui-engine/transpile-ui.ts +2 -2
  79. package/src/ui-engine/ua-stylesheet.ts +46 -3
  80. package/src/ui-engine/ui-lowering.ts +3 -25
  81. package/src/ui-engine/ui-registry.ts +1 -8
  82. package/dist/ui-engine/scroll-memory-diagnostics.d.ts +0 -16
  83. package/dist/ui-engine/scroll-memory-diagnostics.js +0 -58
  84. package/src/ui-engine/scroll-memory-diagnostics.ts +0 -79
@@ -3,12 +3,54 @@
3
3
  // See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
4
4
  export function emitImageDrawing(): string {
5
5
  return `
6
- // ── Full-screen framebuffer (opt-in PSRAM perf experiment) ───────────────────
7
- // Disabled by default: pushing a full 320×240 frame for a tiny dirty button can
8
- // look like a global brightness flash on SPI TFTs. Enable only when a target
9
- // has enough PSRAM and a full-frame repaint is preferable to partial updates.
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 entire framebuffer to the display in one bulk SPI transaction.
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 w = display_canvasWidth(__ui_fb);
38
- int16_t h = display_canvasHeight(__ui_fb);
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
- display_setAddrWindow(0, 0, w, h);
41
- display_writePixels(display_canvasBuffer(__ui_fb), (uint32_t)w * h);
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 = ((int32_t)targetW * 1000) / srcW;
86
- int32_t scaleY = ((int32_t)targetH * 1000) / srcH;
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 = (int16_t)((int32_t)srcW * scale / 1000);
99
- drawH = (int16_t)((int32_t)srcH * scale / 1000);
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((int16_t)(clipX - x), 0, targetW);
119
- txEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, targetW);
120
- tyStart = ui_clamp_i16((int16_t)(clipY - y), 0, targetH);
121
- tyEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, targetH);
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((int16_t)(clipY - y), 0, targetW);
124
- txEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, targetW);
125
- tyStart = ui_clamp_i16((int16_t)(x + targetH - (clipX + clipW)), 0, targetH);
126
- tyEnd = ui_clamp_i16((int16_t)(x + targetH - clipX), 0, targetH);
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((int16_t)(x + targetW - (clipX + clipW)), 0, targetW);
129
- txEnd = ui_clamp_i16((int16_t)(x + targetW - clipX), 0, targetW);
130
- tyStart = ui_clamp_i16((int16_t)(y + targetH - (clipY + clipH)), 0, targetH);
131
- tyEnd = ui_clamp_i16((int16_t)(y + targetH - clipY), 0, targetH);
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((int16_t)(y + targetW - (clipY + clipH)), 0, targetW);
134
- txEnd = ui_clamp_i16((int16_t)(y + targetW - clipY), 0, targetW);
135
- tyStart = ui_clamp_i16((int16_t)(clipX - x), 0, targetH);
136
- tyEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, targetH);
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 = ((int32_t)localY * srcH) / drawH;
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 = ((int32_t)localX * srcW) / drawW;
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[(int32_t)srcY * srcW + srcX];
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((int16_t)(clipX - x), 0, drawW);
181
- dxEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, drawW);
182
- dyStart = ui_clamp_i16((int16_t)(clipY - y), 0, drawH);
183
- dyEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, drawH);
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((int16_t)(clipY - y), 0, drawW);
186
- dxEnd = ui_clamp_i16((int16_t)(clipY + clipH - y), 0, drawW);
187
- dyStart = ui_clamp_i16((int16_t)(x + drawH - (clipX + clipW)), 0, drawH);
188
- dyEnd = ui_clamp_i16((int16_t)(x + drawH - clipX), 0, drawH);
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((int16_t)(x + drawW - (clipX + clipW)), 0, drawW);
191
- dxEnd = ui_clamp_i16((int16_t)(x + drawW - clipX), 0, drawW);
192
- dyStart = ui_clamp_i16((int16_t)(y + drawH - (clipY + clipH)), 0, drawH);
193
- dyEnd = ui_clamp_i16((int16_t)(y + drawH - clipY), 0, drawH);
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((int16_t)(y + drawW - (clipY + clipH)), 0, drawW);
196
- dxEnd = ui_clamp_i16((int16_t)(y + drawW - clipY), 0, drawW);
197
- dyStart = ui_clamp_i16((int16_t)(clipX - x), 0, drawH);
198
- dyEnd = ui_clamp_i16((int16_t)(clipX + clipW - x), 0, drawH);
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 = (int16_t)(dyEnd - dyStart);
205
- int16_t cols = (int16_t)(dxEnd - dxStart);
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((int16_t)(x + dxStart), (int16_t)(y + dyStart + row),
208
- img->data + (int32_t)(dyStart + row) * img->w + dxStart, cols, 1);
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 = ((int32_t)dy * img->h) / drawH;
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 = ((int32_t)dx * img->w) / drawW;
216
- UI_COLOR_T color = img->data[(int32_t)srcY * img->w + srcX];
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 = ((int32_t)dy * img->h) / drawH;
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 = ((int32_t)dx * img->w) / drawW;
227
- UI_COLOR_T color = img->data[(int32_t)srcY * img->w + srcX];
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((int16_t)(clipX - x), 0, (int16_t)img->w);
253
- int16_t sy0 = ui_clamp_i16((int16_t)(clipY - y), 0, (int16_t)img->h);
254
- int16_t sw = ui_clamp_i16(clipW, 0, (int16_t)(img->w - sx0));
255
- int16_t sh = ui_clamp_i16(clipH, 0, (int16_t)(img->h - sy0));
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((int16_t)(x + sx0), (int16_t)(y + sy0 + row),
258
- img->data + (int32_t)(sy0 + row) * img->w + sx0, sw, 1);
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 = (uint16_t)ui_clamp_i16((int16_t)(clipY - y), 0, (int16_t)img->w);
265
- sxEnd = (uint16_t)ui_clamp_i16((int16_t)(clipY + clipH - y), 0, (int16_t)img->w);
266
- syStart = (uint16_t)ui_clamp_i16((int16_t)(x + img->h - (clipX + clipW)), 0, (int16_t)img->h);
267
- syEnd = (uint16_t)ui_clamp_i16((int16_t)(x + img->h - clipX), 0, (int16_t)img->h);
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 = (uint16_t)ui_clamp_i16((int16_t)(x + img->w - (clipX + clipW)), 0, (int16_t)img->w);
270
- sxEnd = (uint16_t)ui_clamp_i16((int16_t)(x + img->w - clipX), 0, (int16_t)img->w);
271
- syStart = (uint16_t)ui_clamp_i16((int16_t)(y + img->h - (clipY + clipH)), 0, (int16_t)img->h);
272
- syEnd = (uint16_t)ui_clamp_i16((int16_t)(y + img->h - clipY), 0, (int16_t)img->h);
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 = (uint16_t)ui_clamp_i16((int16_t)(y + img->w - (clipY + clipH)), 0, (int16_t)img->w);
275
- sxEnd = (uint16_t)ui_clamp_i16((int16_t)(y + img->w - clipY), 0, (int16_t)img->w);
276
- syStart = (uint16_t)ui_clamp_i16((int16_t)(clipX - x), 0, (int16_t)img->h);
277
- syEnd = (uint16_t)ui_clamp_i16((int16_t)(clipX + clipW - x), 0, (int16_t)img->h);
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[(uint32_t)sy * img->w + sx];
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 = (int16_t)(img->h - 1 - sy);
287
- dy = (int16_t)sx;
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 = (int16_t)(img->w - 1 - sx);
290
- dy = (int16_t)(img->h - 1 - sy);
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 = (int16_t)sy;
293
- dy = (int16_t)(img->w - 1 - sx);
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*)calloc(__ui_node_count, sizeof(uint8_t));
16
+ __ui_scroll_canvas_ok = new (std::nothrow) uint8_t[__ui_node_count]();
17
17
  }
18
- if (!__ui_scroll_mem_warned && __ui_node_count > 0) {
19
- __ui_scroll_mem_warned = (uint8_t*)calloc(__ui_node_count, sizeof(uint8_t));
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 = (int16_t)((uint32_t)cnt * ih);
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
- typedef void (*PinWatchCallback)(void);
117
+ using PinWatchCallback = void (*)(void);
103
118
 
104
119
  struct UIPinWatch {
105
120
  uint8_t pin;
@@ -49,7 +49,7 @@ static inline void ui_kb_insert(char c) {
49
49
  if (__ui_kb_target >= 0) {
50
50
  strncpy(__ui_nodes[__ui_kb_target].textBuffer, __ui_kb_buffer, UI_TEXT_BUF);
51
51
  __ui_nodes[__ui_kb_target].textBuffer[UI_TEXT_BUF] = 0;
52
- ui_mark_dirty((uint16_t)__ui_kb_target);
52
+ ui_mark_dirty(static_cast<uint16_t>(__ui_kb_target));
53
53
  }
54
54
  #endif
55
55
  }
@@ -62,7 +62,7 @@ static inline void ui_kb_delete() {
62
62
  if (__ui_kb_target >= 0) {
63
63
  strncpy(__ui_nodes[__ui_kb_target].textBuffer, __ui_kb_buffer, UI_TEXT_BUF);
64
64
  __ui_nodes[__ui_kb_target].textBuffer[UI_TEXT_BUF] = 0;
65
- ui_mark_dirty((uint16_t)__ui_kb_target);
65
+ ui_mark_dirty(static_cast<uint16_t>(__ui_kb_target));
66
66
  }
67
67
  #endif
68
68
  }
@@ -92,7 +92,7 @@ static inline void ui_kb_open(uint16_t nodeIdx, uint8_t inputPosition) {
92
92
  __ui_kb_buffer[UI_TEXT_BUF] = 0;
93
93
  __ui_kb_len = strlen(__ui_kb_buffer);
94
94
  uint16_t ml = __ui_nodes[nodeIdx].maxlen;
95
- __ui_kb_maxlen = (ml > 0 && ml <= UI_TEXT_BUF) ? (uint8_t)ml : UI_TEXT_BUF;
95
+ __ui_kb_maxlen = (ml > 0 && ml <= UI_TEXT_BUF) ? static_cast<uint8_t>(ml) : UI_TEXT_BUF;
96
96
  __ui_kb_shift = 0;
97
97
  __ui_kb_bs_held = 0;
98
98
  // Load the key set via the dispatch table.
@@ -113,7 +113,7 @@ static inline void ui_kb_open(uint16_t nodeIdx, uint8_t inputPosition) {
113
113
  // keystroke. (The full-screen-flash concern above doesn't apply — there's no
114
114
  // opaque overlay being skipped.) Seed the blink phase so the caret is ON for
115
115
  // the first ~530ms (bit 0x20 set → visible), so focus feels immediate.
116
- if (__ui_kb_target >= 0) ui_mark_dirty((uint16_t)__ui_kb_target);
116
+ if (__ui_kb_target >= 0) ui_mark_dirty(static_cast<uint16_t>(__ui_kb_target));
117
117
  __ui_kb_blink = 0x20;
118
118
  #endif
119
119
  }
@@ -140,7 +140,7 @@ static inline void ui_kb_close() {
140
140
  int16_t screenRoot = -1;
141
141
  for (uint16_t i = 0; i < __ui_node_count; i++) {
142
142
  if (__ui_nodes[i].parent == UI_NO_PARENT && __ui_nodes[i].screenId == __ui_active_screen) {
143
- screenRoot = (int16_t)i;
143
+ screenRoot = static_cast<int16_t>(i);
144
144
  break;
145
145
  }
146
146
  }
@@ -156,7 +156,7 @@ static inline void ui_kb_close() {
156
156
  // scroll clipping — safe here since these nodes are bounded in size, unlike
157
157
  // the screen root.
158
158
  for (uint16_t i = 0; i < __ui_node_count; i++) {
159
- if ((int16_t)i == screenRoot) continue;
159
+ if (static_cast<int16_t>(i) == screenRoot) continue;
160
160
  UIRect r;
161
161
  ui_node_current_paint_rect(i, &r);
162
162
  if (r.w > 0 && r.h > 0 &&
@@ -173,8 +173,8 @@ static inline void ui_kb_key_rect(uint8_t idx, UIRect* out) {
173
173
  uint8_t col = idx % __ui_kb_cols;
174
174
  uint8_t row = idx / __ui_kb_cols;
175
175
  int16_t keysH = __ui_kb_box.h - UI_KB_TEXT_H; // key area height (below text row)
176
- out->x = __ui_kb_box.x + (int16_t)col * __ui_kb_box.w / __ui_kb_cols;
177
- out->y = __ui_kb_box.y + UI_KB_TEXT_H + (int16_t)row * keysH / __ui_kb_rows;
176
+ out->x = __ui_kb_box.x + static_cast<int16_t>(col) * __ui_kb_box.w / __ui_kb_cols;
177
+ out->y = __ui_kb_box.y + UI_KB_TEXT_H + static_cast<int16_t>(row) * keysH / __ui_kb_rows;
178
178
  out->w = __ui_kb_box.w / __ui_kb_cols;
179
179
  out->h = keysH / __ui_kb_rows;
180
180
  }
@@ -192,8 +192,8 @@ static inline void ui_kb_handle_touch(int16_t tx, int16_t ty) {
192
192
  UIRect r;
193
193
  ui_kb_key_rect(i, &r);
194
194
  if (tx >= r.x && tx < r.x + r.w && ty >= r.y && ty < r.y + r.h) {
195
- __ui_kb_pressed_key = (int8_t)i; // remember for release
196
- __ui_kb_repaint_key = (int8_t)i;
195
+ __ui_kb_pressed_key = static_cast<int8_t>(i); // remember for release
196
+ __ui_kb_repaint_key = static_cast<int8_t>(i);
197
197
  __ui_kb_dirty = 2; // targeted: redraw just this key's highlight
198
198
  // Backspace starts deleting immediately + arms auto-repeat.
199
199
  if (k.special == 2) {
@@ -273,7 +273,7 @@ static inline void ui_kb_draw_key(uint8_t i) {
273
273
  UI_COLOR_T border = ks.borderColor;
274
274
  // Shift-active highlight: brighten the shift key's background — but only
275
275
  // when not pressed, so the press inversion stays high-contrast.
276
- if (k.special == 1 && __ui_kb_shift && (int8_t)i != __ui_kb_pressed_key) {
276
+ if (k.special == 1 && __ui_kb_shift && static_cast<int8_t>(i) != __ui_kb_pressed_key) {
277
277
  #if UI_COLOR_DEPTH == 888
278
278
  bg = 0xBDEFFF;
279
279
  #else
@@ -281,7 +281,7 @@ static inline void ui_kb_draw_key(uint8_t i) {
281
281
  #endif
282
282
  }
283
283
  // Pressed key: invert colors for clear tap feedback.
284
- if ((int8_t)i == __ui_kb_pressed_key) { UI_COLOR_T t = bg; bg = fg; fg = t; }
284
+ if (static_cast<int8_t>(i) == __ui_kb_pressed_key) { UI_COLOR_T t = bg; bg = fg; fg = t; }
285
285
  ui_display_fill_rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, bg);
286
286
  ui_display_draw_rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, border);
287
287
  ui_display_set_text_color(fg, bg);
@@ -295,14 +295,14 @@ static inline void ui_kb_draw_key(uint8_t i) {
295
295
  case 3: labelStr = "OK"; break;
296
296
  case 4: labelStr = __ui_kb_cols <= 4 ? "ABC" : "123"; break;
297
297
  default:
298
- single[0] = (__ui_kb_shift && k.ch >= 'a' && k.ch <= 'z') ? (char)(k.ch - 32) : k.ch;
298
+ single[0] = (__ui_kb_shift && k.ch >= 'a' && k.ch <= 'z') ? static_cast<char>(k.ch - 32) : k.ch;
299
299
  single[1] = 0;
300
300
  labelStr = single;
301
301
  break;
302
302
  }
303
303
  // Center: textW = len * 6px, textH = 8px. Position inside the key rect.
304
304
  uint8_t len = strlen(labelStr);
305
- int16_t textW = (int16_t)len * 6;
305
+ int16_t textW = static_cast<int16_t>(len) * 6;
306
306
  int16_t textH = 8;
307
307
  int16_t cx = r.x + (r.w - textW) / 2;
308
308
  int16_t cy = r.y + (r.h - textH) / 2;
@@ -338,11 +338,14 @@ static inline void ui_kb_draw() {
338
338
  if (kw <= 0 || kh <= 0) return;
339
339
  // (Re)allocate the keyboard canvas to exact size (see repair-canvas comment
340
340
  // — stride mismatch corrupts the push when reusing a differently-sized canvas).
341
+ // ui_create_canvas_best prefers PSRAM when available (a full-width keyboard
342
+ // canvas can be ~30KB, too big for no-PSRAM internal SRAM) and falls back to
343
+ // SRAM — same policy as the list/scroll viewport canvases.
341
344
  if (!__ui_kb_canvas || !display_canvasBuffer(__ui_kb_canvas) ||
342
345
  display_canvasWidth(__ui_kb_canvas) != kw ||
343
346
  display_canvasHeight(__ui_kb_canvas) != kh) {
344
347
  display_deleteCanvas(__ui_kb_canvas);
345
- __ui_kb_canvas = display_createCanvas(kw, kh);
348
+ __ui_kb_canvas = ui_create_canvas_best(kw, kh);
346
349
  }
347
350
  if (!__ui_kb_canvas || !display_canvasBuffer(__ui_kb_canvas)) {
348
351
  // Canvas alloc failed — fall back to direct draw (slow but correct).
@@ -376,7 +379,7 @@ static inline void ui_kb_draw() {
376
379
  ui_display_set_target(__kb_prev_target);
377
380
  display_startWrite();
378
381
  display_setAddrWindow(saveBoxX, saveBoxY, kw, kh);
379
- display_writePixels(display_canvasBuffer(__ui_kb_canvas), (uint32_t)kw * kh);
382
+ display_writePixels(display_canvasBuffer(__ui_kb_canvas), static_cast<uint32_t>(kw) * kh);
380
383
  display_endWrite();
381
384
  }
382
385
  `;
@@ -18,19 +18,19 @@ static inline void ui_draw_gradient_fill(uint16_t i, int16_t drawY) {
18
18
  uint8_t dir = __ui_nodes[i].gradientEnabled; // 1=vertical, 2=horizontal
19
19
  if (dir == 1) {
20
20
  // Vertical: top=c1, bottom=c2. Draw row by row.
21
- int16_t yStart = (int16_t)(clipY - by);
22
- int16_t yEnd = (int16_t)(clipY + clipH - by);
21
+ int16_t yStart = static_cast<int16_t>(clipY - by);
22
+ int16_t yEnd = static_cast<int16_t>(clipY + clipH - by);
23
23
  for (int16_t y = yStart; y < yEnd; y++) {
24
- uint8_t op = (uint8_t)((uint16_t)y * 100 / (bh > 1 ? bh - 1 : 1));
24
+ uint8_t op = static_cast<uint8_t>(static_cast<uint16_t>(y) * 100 / (bh > 1 ? bh - 1 : 1));
25
25
  uint32_t col = ui_blend(c1, c2, op);
26
26
  ui_display_draw_fast_hline(clipX, by + y, clipW, col);
27
27
  }
28
28
  } else {
29
29
  // Horizontal: left=c1, right=c2. Draw column by column.
30
- int16_t xStart = (int16_t)(clipX - bx);
31
- int16_t xEnd = (int16_t)(clipX + clipW - bx);
30
+ int16_t xStart = static_cast<int16_t>(clipX - bx);
31
+ int16_t xEnd = static_cast<int16_t>(clipX + clipW - bx);
32
32
  for (int16_t x = xStart; x < xEnd; x++) {
33
- uint8_t op = (uint8_t)((uint16_t)x * 100 / (bw > 1 ? bw - 1 : 1));
33
+ uint8_t op = static_cast<uint8_t>(static_cast<uint16_t>(x) * 100 / (bw > 1 ? bw - 1 : 1));
34
34
  uint32_t col = ui_blend(c1, c2, op);
35
35
  ui_display_draw_fast_vline(bx + x, clipY, clipH, col);
36
36
  }
@@ -79,7 +79,7 @@ static inline void ui_draw_shadow(uint16_t i, int16_t drawY, uint8_t insetOnly)
79
79
  }
80
80
 
81
81
  for (int8_t pass = blur; pass >= 1; pass--) {
82
- uint8_t opacity = (uint8_t)((uint16_t)baseAlpha / (pass + 1));
82
+ uint8_t opacity = static_cast<uint8_t>(static_cast<uint16_t>(baseAlpha) / (pass + 1));
83
83
  uint32_t col = ui_blend(shadowCol, inset ? (__ui_nodes[i].hasBg ? __ui_nodes[i].bg : clearCol) : clearCol, opacity);
84
84
  if (inset) {
85
85
  // Inset: draw inside the element, shrinking inward by pass.
@@ -104,7 +104,7 @@ static inline void ui_draw_shadow(uint16_t i, int16_t drawY, uint8_t insetOnly)
104
104
  int16_t sw = bw + 2 * pass;
105
105
  int16_t sh_ = bh + 2 * pass;
106
106
  if (radius > 0) {
107
- uint8_t r = radius + (uint8_t)pass;
107
+ uint8_t r = radius + static_cast<uint8_t>(pass);
108
108
  if (r > sw / 2) r = sw / 2;
109
109
  if (r > sh_ / 2) r = sh_ / 2;
110
110
  ui_display_fill_round_rect(sx, sy, sw, sh_, r, col);