@typecad/ui 1.0.0-alpha.6 → 1.0.0-alpha.7
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/dist/engine-index.js +2 -0
- package/dist/ui-engine/runtime-header/canvas-helpers.js +12 -6
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.d.ts +1 -0
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +533 -0
- package/dist/ui-engine/runtime-header/forward-decls.js +5 -5
- package/dist/ui-engine/runtime-header/keyboard.js +45 -4
- package/dist/ui-engine/runtime-header/node-decoration.js +0 -1
- package/dist/ui-engine/runtime-header/scroll-physics.js +0 -4
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +42 -7
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +4 -1
- package/dist/ui-engine/runtime-header.d.ts +10 -1
- package/dist/ui-engine/runtime-header.js +5 -1
- package/dist/ui-engine/ui-lowering.js +14 -1
- package/dist/ui-engine/ui-registry.js +2 -1
- package/package.json +2 -2
- package/src/engine-index.ts +2 -0
- package/src/ui-engine/runtime-header/canvas-helpers.ts +12 -6
- package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +536 -0
- package/src/ui-engine/runtime-header/forward-decls.ts +5 -5
- package/src/ui-engine/runtime-header/keyboard.ts +45 -4
- package/src/ui-engine/runtime-header/node-decoration.ts +0 -1
- package/src/ui-engine/runtime-header/scroll-physics.ts +0 -4
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +42 -7
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +4 -1
- package/src/ui-engine/runtime-header.ts +15 -1
- package/src/ui-engine/ui-lowering.ts +16 -1
- package/src/ui-engine/ui-registry.ts +2 -1
|
@@ -328,15 +328,56 @@ static inline void ui_kb_draw_text_row() {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
// Draw the full keyboard overlay (background + text row + all keys).
|
|
331
|
+
// Renders into a dedicated offscreen canvas first, then pushes in a single
|
|
332
|
+
// SPI transaction — avoids the per-key/per-rect display writes that show up
|
|
333
|
+
// as visible row-by-row painting on SPI TFTs.
|
|
334
|
+
// (__ui_kb_canvas is forward-declared in the touch-keyboard-fwd slice.)
|
|
331
335
|
static inline void ui_kb_draw() {
|
|
332
|
-
|
|
333
|
-
|
|
336
|
+
int16_t kw = __ui_kb_box.w;
|
|
337
|
+
int16_t kh = __ui_kb_box.h;
|
|
338
|
+
if (kw <= 0 || kh <= 0) return;
|
|
339
|
+
// (Re)allocate the keyboard canvas to exact size (see repair-canvas comment
|
|
340
|
+
// — stride mismatch corrupts the push when reusing a differently-sized canvas).
|
|
341
|
+
if (!__ui_kb_canvas || !display_canvasBuffer(__ui_kb_canvas) ||
|
|
342
|
+
display_canvasWidth(__ui_kb_canvas) != kw ||
|
|
343
|
+
display_canvasHeight(__ui_kb_canvas) != kh) {
|
|
344
|
+
display_deleteCanvas(__ui_kb_canvas);
|
|
345
|
+
__ui_kb_canvas = display_createCanvas(kw, kh);
|
|
346
|
+
}
|
|
347
|
+
if (!__ui_kb_canvas || !display_canvasBuffer(__ui_kb_canvas)) {
|
|
348
|
+
// Canvas alloc failed — fall back to direct draw (slow but correct).
|
|
349
|
+
ui_display_fill_rect(__ui_kb_box.x, __ui_kb_box.y, kw, kh, __ui_kb_bg);
|
|
350
|
+
ui_kb_draw_text_row();
|
|
351
|
+
for (uint8_t i = 0; i < __ui_kb_keyCount; i++) {
|
|
352
|
+
if (__ui_kb_keys[i].special == 255) continue;
|
|
353
|
+
ui_kb_draw_key(i);
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
// Redirect drawing into the canvas. All ui_display_* calls below go to RAM.
|
|
358
|
+
CuttlefishDisplayTarget* __kb_prev_target = ui_display_get_target();
|
|
359
|
+
ui_display_set_target(__ui_kb_canvas);
|
|
360
|
+
// Opaque background over the keyboard box (canvas-local 0,0).
|
|
361
|
+
ui_display_fill_rect(0, 0, kw, kh, __ui_kb_bg);
|
|
362
|
+
// Text row + keys: temporarily shift box origin to canvas-local so the
|
|
363
|
+
// existing draw_text_row / draw_key functions compute coords at (0,0).
|
|
364
|
+
int16_t saveBoxX = __ui_kb_box.x;
|
|
365
|
+
int16_t saveBoxY = __ui_kb_box.y;
|
|
366
|
+
__ui_kb_box.x = 0;
|
|
367
|
+
__ui_kb_box.y = 0;
|
|
334
368
|
ui_kb_draw_text_row();
|
|
335
|
-
// Keys: one rect per key, label centered.
|
|
336
369
|
for (uint8_t i = 0; i < __ui_kb_keyCount; i++) {
|
|
337
|
-
if (__ui_kb_keys[i].special == 255) continue;
|
|
370
|
+
if (__ui_kb_keys[i].special == 255) continue;
|
|
338
371
|
ui_kb_draw_key(i);
|
|
339
372
|
}
|
|
373
|
+
__ui_kb_box.x = saveBoxX;
|
|
374
|
+
__ui_kb_box.y = saveBoxY;
|
|
375
|
+
// Restore display target and push the canvas in one transaction.
|
|
376
|
+
ui_display_set_target(__kb_prev_target);
|
|
377
|
+
display_startWrite();
|
|
378
|
+
display_setAddrWindow(saveBoxX, saveBoxY, kw, kh);
|
|
379
|
+
display_writePixels(display_canvasBuffer(__ui_kb_canvas), (uint32_t)kw * kh);
|
|
380
|
+
display_endWrite();
|
|
340
381
|
}
|
|
341
382
|
`;
|
|
342
383
|
}
|
|
@@ -176,7 +176,6 @@ static inline void ui_draw_node_border(uint16_t i, int16_t drawX, int16_t drawY,
|
|
|
176
176
|
if (__ui_nodes[i].hasPerSideBorder) {
|
|
177
177
|
int16_t w = __ui_nodes[i].box.w;
|
|
178
178
|
int16_t h = __ui_nodes[i].box.h;
|
|
179
|
-
uint8_t st = __ui_nodes[i].borderStyle;
|
|
180
179
|
// Top edge
|
|
181
180
|
if (__ui_nodes[i].borderTopWidth > 0) {
|
|
182
181
|
ui_display_fill_rect(drawX, drawY, w, __ui_nodes[i].borderTopWidth, color);
|
|
@@ -7,7 +7,6 @@ export function emitScrollPhysics() {
|
|
|
7
7
|
// raw touch sample → smoothed delta (dy). Capacitive: passthrough 1:1.
|
|
8
8
|
// Resistive: deadband suppresses sub-N-px jitter (steady drag still 1:1).
|
|
9
9
|
// 'none' tier compiles drag scroll out entirely.
|
|
10
|
-
static int16_t __ui_scroll_prev_dy = 0; // last smoothed delta (low-pass state)
|
|
11
10
|
|
|
12
11
|
static inline int16_t ui_scroll_scale_dy(int16_t dy) {
|
|
13
12
|
int32_t scaled = (int32_t)dy * (int32_t)UI_SCROLL_DRAG_SCALE_X10;
|
|
@@ -16,17 +15,14 @@ static inline int16_t ui_scroll_scale_dy(int16_t dy) {
|
|
|
16
15
|
|
|
17
16
|
static inline int16_t ui_scroll_smooth_dy(int16_t dy) {
|
|
18
17
|
#if UI_SCROLL_INPUT_TIER_CAPACITIVE
|
|
19
|
-
__ui_scroll_prev_dy = dy;
|
|
20
18
|
return ui_scroll_scale_dy(dy);
|
|
21
19
|
#elif UI_SCROLL_INPUT_TIER_RESISTIVE
|
|
22
20
|
int16_t db = (int16_t)UI_SCROLL_DEADBAND_PX;
|
|
23
21
|
if (dy >= -db && dy <= db) {
|
|
24
22
|
// Deadband: kill per-sample jitter around zero. Steady drag (|dy|>db) below
|
|
25
23
|
// passes through unchanged, so steady-state is 1:1 (spec Q1).
|
|
26
|
-
__ui_scroll_prev_dy = 0;
|
|
27
24
|
return 0;
|
|
28
25
|
}
|
|
29
|
-
__ui_scroll_prev_dy = dy;
|
|
30
26
|
return ui_scroll_scale_dy(dy);
|
|
31
27
|
#else
|
|
32
28
|
(void)dy;
|
|
@@ -27,9 +27,48 @@ export function emitTickDirtyDrawPhase() {
|
|
|
27
27
|
if (__ui_kb_dirty == 1) {
|
|
28
28
|
ui_kb_draw();
|
|
29
29
|
} else if (__ui_kb_dirty == 2) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
// Buffer the targeted update through the keyboard canvas too — drawing
|
|
31
|
+
// the text row directly to the display (fill_rect clear + text redraw)
|
|
32
|
+
// flashes on SPI TFTs because the clear is visible for one frame.
|
|
33
|
+
// Re-render into the canvas and push in one transaction. The canvas
|
|
34
|
+
// already holds the last full-keyboard frame, so we only need to redraw
|
|
35
|
+
// the text row + the single changed key on top of it.
|
|
36
|
+
// (__ui_kb_canvas is declared static in the keyboard slice, earlier in
|
|
37
|
+
// this same translation unit — no extern needed.)
|
|
38
|
+
if (__ui_kb_canvas && display_canvasBuffer(__ui_kb_canvas)) {
|
|
39
|
+
int16_t kw = __ui_kb_box.w;
|
|
40
|
+
int16_t saveBoxX = __ui_kb_box.x;
|
|
41
|
+
int16_t saveBoxY = __ui_kb_box.y;
|
|
42
|
+
CuttlefishDisplayTarget* __kb_prev = ui_display_get_target();
|
|
43
|
+
ui_display_set_target(__ui_kb_canvas);
|
|
44
|
+
__ui_kb_box.x = 0;
|
|
45
|
+
__ui_kb_box.y = 0;
|
|
46
|
+
ui_kb_draw_text_row();
|
|
47
|
+
uint8_t has_key_repaint = (__ui_kb_repaint_key >= 0 && __ui_kb_keys[__ui_kb_repaint_key].special != 255) ? 1 : 0;
|
|
48
|
+
if (has_key_repaint) {
|
|
49
|
+
ui_kb_draw_key((uint8_t)__ui_kb_repaint_key);
|
|
50
|
+
}
|
|
51
|
+
__ui_kb_box.x = saveBoxX;
|
|
52
|
+
__ui_kb_box.y = saveBoxY;
|
|
53
|
+
ui_display_set_target(__kb_prev);
|
|
54
|
+
// Push only the changed region: text row alone (UI_KB_TEXT_H) when no
|
|
55
|
+
// key highlight changed, or the full canvas when a key was repainted
|
|
56
|
+
// (the key could be anywhere in the keyboard grid).
|
|
57
|
+
display_startWrite();
|
|
58
|
+
if (has_key_repaint) {
|
|
59
|
+
display_setAddrWindow(saveBoxX, saveBoxY, kw, __ui_kb_box.h);
|
|
60
|
+
display_writePixels(display_canvasBuffer(__ui_kb_canvas), (uint32_t)kw * __ui_kb_box.h);
|
|
61
|
+
} else {
|
|
62
|
+
display_setAddrWindow(saveBoxX, saveBoxY, kw, UI_KB_TEXT_H);
|
|
63
|
+
display_writePixels(display_canvasBuffer(__ui_kb_canvas), (uint32_t)kw * UI_KB_TEXT_H);
|
|
64
|
+
}
|
|
65
|
+
display_endWrite();
|
|
66
|
+
} else {
|
|
67
|
+
// No canvas — fall back to direct draw (slow but correct).
|
|
68
|
+
ui_kb_draw_text_row();
|
|
69
|
+
if (__ui_kb_repaint_key >= 0 && __ui_kb_keys[__ui_kb_repaint_key].special != 255) {
|
|
70
|
+
ui_kb_draw_key((uint8_t)__ui_kb_repaint_key);
|
|
71
|
+
}
|
|
33
72
|
}
|
|
34
73
|
}
|
|
35
74
|
__ui_kb_dirty = 0;
|
|
@@ -272,10 +311,6 @@ export function emitTickDirtyDrawPhase() {
|
|
|
272
311
|
|
|
273
312
|
UIRect paintRect;
|
|
274
313
|
ui_node_paint_rect(i, baseDrawX, baseDrawY, drawX, drawY, paintTextW, paintTextH, &paintRect);
|
|
275
|
-
int16_t cullX = paintRect.x;
|
|
276
|
-
int16_t cullY = paintRect.y;
|
|
277
|
-
int16_t cullW = paintRect.w;
|
|
278
|
-
int16_t cullH = paintRect.h;
|
|
279
314
|
if (drawingBufferedScroll) {
|
|
280
315
|
// Canvas-local clip: skip nodes fully outside the viewport (0..vw, 0..vh).
|
|
281
316
|
// Use the node's face rect (box.w/h), NOT the paint rect — the paint rect
|
|
@@ -51,6 +51,7 @@ static UIKeyStyle __ui_kb_styles[UI_KB_MAX];
|
|
|
51
51
|
static UI_COLOR_T __ui_kb_bg = 0x0000; // keyboard background (resolved from CSS)
|
|
52
52
|
static uint8_t __ui_kb_bs_held = 0;
|
|
53
53
|
static uint8_t __ui_kb_dirty = 0; // 0=clean, 1=full redraw, 2=text row + single key
|
|
54
|
+
// (__ui_kb_canvas is declared in forward-decls.ts alongside the other persistent canvases.)
|
|
54
55
|
// Forward-declared here (defined in the keyboard subsystem block below) so the
|
|
55
56
|
// UI_HIDE_OSK caret/blink paths in ui_tick and the input draw can reference it.
|
|
56
57
|
static int16_t __ui_kb_target;
|
|
@@ -268,7 +269,9 @@ static void ui_touch_up() {
|
|
|
268
269
|
// including holds (released above) and taps on empty space (__ui_touch_node
|
|
269
270
|
// == -1), which is what makes "wake on any touch" work for display-sleep.
|
|
270
271
|
// Placed AFTER the click/release dispatch so onClick always fires first.
|
|
271
|
-
|
|
272
|
+
// Note: explicit '+ 1' instead of '++' — GCC 13+ (IDF v6) deprecates '++'
|
|
273
|
+
// on volatile-qualified types under -Werror=volatile.
|
|
274
|
+
__ui_tap_seq = __ui_tap_seq + 1;
|
|
272
275
|
__ui_tap_node = __ui_touch_node;
|
|
273
276
|
// Virtualized list item tap: if the touch was inside a list, compute the item
|
|
274
277
|
// index from the touch Y. Use total movement (not drag flag) to distinguish
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface EmitRuntimeHeaderOptions {
|
|
2
|
+
/**
|
|
3
|
+
* True when a native display adapter (AVR, ESP32) is in use. Kept for
|
|
4
|
+
* backwards compatibility — the CuttlefishGFX slice is now emitted
|
|
5
|
+
* separately via emitCuttlefishGfx() so it can land BEFORE display adapter
|
|
6
|
+
* declarations that instantiate CuttlefishGFX by value.
|
|
7
|
+
*/
|
|
8
|
+
nativeDisplayActive?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function emitRuntimeHeader(opts?: EmitRuntimeHeaderOptions): string;
|
|
@@ -40,7 +40,11 @@ import { emitTick } from "./runtime-header/tick.js";
|
|
|
40
40
|
import { emitAntialiasing } from "./runtime-header/antialiasing.js";
|
|
41
41
|
import { emitKeyboard } from "./runtime-header/keyboard.js";
|
|
42
42
|
import { emitGuardClose } from "./runtime-header/guard-close.js";
|
|
43
|
-
export function emitRuntimeHeader() {
|
|
43
|
+
export function emitRuntimeHeader(opts) {
|
|
44
|
+
// Note: the CuttlefishGFX slice (emitCuttlefishGfx) is emitted separately
|
|
45
|
+
// by ui-emitter.ts BEFORE the display adapter declaration. It is NOT
|
|
46
|
+
// emitted here — passing nativeDisplayActive has no effect.
|
|
47
|
+
void opts;
|
|
44
48
|
return [
|
|
45
49
|
emitTypesDefines(),
|
|
46
50
|
emitStructs(),
|
|
@@ -145,7 +145,20 @@ function emitKeyboardLoader(name, kb, rules, colorFormat) {
|
|
|
145
145
|
}
|
|
146
146
|
/** Emit one key's keys[] + styles[] lines, resolving CSS classes to colors. */
|
|
147
147
|
function emitKeyLine(key, kbClasses, rules, colorFormat) {
|
|
148
|
-
|
|
148
|
+
// The C++ UIKey.ch field is a single `char`. Special keys (shift=1, bs=2,
|
|
149
|
+
// ok=3, page=4) use the `special` field for their behavior and label — the
|
|
150
|
+
// `ch` value is never read. Their label strings ("OK", "123", "⇧", "⌫") are
|
|
151
|
+
// multi-char or multi-byte and would overflow `char` if emitted as literals.
|
|
152
|
+
// Emit a space placeholder for any key whose ch isn't a single ASCII byte.
|
|
153
|
+
const isSingleAscii = key.special === 0 && key.ch.length === 1 && key.ch.charCodeAt(0) < 128;
|
|
154
|
+
if (key.special === 0 && !isSingleAscii) {
|
|
155
|
+
console.warn(`[cuttlefish] keyboard key ch="${key.ch}" is non-ASCII and will be emitted as ` +
|
|
156
|
+
`a space placeholder. The native C++ UIKey.ch field is a single char and ` +
|
|
157
|
+
`cannot hold multi-byte codepoints. Use a custom keyboard layout or the ` +
|
|
158
|
+
`Adafruit path for full Unicode key labels.`);
|
|
159
|
+
}
|
|
160
|
+
const chRaw = isSingleAscii ? key.ch : ' ';
|
|
161
|
+
const chEsc = chRaw === "\\" ? "\\\\" : chRaw === "'" ? "\\'" : chRaw;
|
|
149
162
|
const style = resolveKeyStyle(key.classes, kbClasses, rules);
|
|
150
163
|
const bg = style.background ? resolveColor(style.background, colorFormat) : DEFAULT_KEY_BG;
|
|
151
164
|
const fg = style.color ? resolveColor(style.color, colorFormat) : DEFAULT_KEY_FG;
|
|
@@ -145,7 +145,8 @@ export function lowerOnMount(htmlPath, opts) {
|
|
|
145
145
|
const keyframeSets = buildKeyframeSets(mod.rawKeyframes || [], opts.colorFormat);
|
|
146
146
|
// Load image assets before lowering so imgDataId can be set.
|
|
147
147
|
const imageAssets = loadImageAssets(allStyled.length > 0 ? allStyled : [mod.styled], path.dirname(abs));
|
|
148
|
-
const
|
|
148
|
+
const _p = getDisplayProfile();
|
|
149
|
+
const scrollBudget = resolveScrollConfig(_p, { buildTarget: _p._buildTarget, psram: _p._psram }).scrollCanvasBudgetBytes;
|
|
149
150
|
const result = lowerUIToCpp(mod.styled, allBoxes, opts.colorFormat, opts.storage, mod.keyboards, mod.rules, getDisplayProfile(), mod.fontAssets, allStyled, imageAssets.nodeIdToAssetIndex, keyframeSets, scrollBudget);
|
|
150
151
|
for (const d of result.scrollMemoryDiagnostics) {
|
|
151
152
|
mod.mountDiagnostics.push({ ...d, source: d.source ?? path.basename(mod.htmlPath) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecad/ui",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.7",
|
|
4
4
|
"description": "TypeCAD UI authoring library — HTML/CSS-driven graphics for microcontrollers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"src"
|
|
34
34
|
],
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@typecad/cuttlefish": "1.0.0-alpha.
|
|
36
|
+
"@typecad/cuttlefish": "1.0.0-alpha.7"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"css-tree": "^3.2.1",
|
package/src/engine-index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "./ui-engine/ui-registry.js";
|
|
20
20
|
import { resolveColor, resolveColorInternal } from "./ui-engine/color.js";
|
|
21
21
|
import { emitRuntimeHeader } from "./ui-engine/runtime-header.js";
|
|
22
|
+
import { emitCuttlefishGfx } from "./ui-engine/runtime-header/cuttlefish-gfx.js";
|
|
22
23
|
import { splitUiFile } from "./ui-engine/ui-file-splitter.js";
|
|
23
24
|
import { analyzeScrollMemory } from "./ui-engine/scroll-memory-diagnostics.js";
|
|
24
25
|
|
|
@@ -44,6 +45,7 @@ export function registerTranspilerUI(): TranspilerUIHook {
|
|
|
44
45
|
resolveColor,
|
|
45
46
|
resolveColorInternal,
|
|
46
47
|
emitRuntimeHeader,
|
|
48
|
+
emitCuttlefishGfx,
|
|
47
49
|
splitUiFile,
|
|
48
50
|
generateProjectUITypeDeclarations,
|
|
49
51
|
analyzeScrollMemory,
|
|
@@ -10,6 +10,7 @@ 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_kb_canvas); __ui_kb_canvas = nullptr;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
// Lazily allocate/reuse a viewport-sized canvas for a scroll container. Resizes
|
|
@@ -62,7 +63,7 @@ static inline void ui_warn_scroll_memory(uint16_t nodeIdx, uint8_t reason) {
|
|
|
62
63
|
if (reason == 1) reasonText = "scroll viewport exceeds compile-time canvas budget";
|
|
63
64
|
else if (reason == 2) reasonText = "using Mode C strip fallback (smooth scroll canvas unavailable)";
|
|
64
65
|
|
|
65
|
-
#if defined(ESP32)
|
|
66
|
+
#if defined(ESP32) && defined(ARDUINO)
|
|
66
67
|
uint32_t freeHeap = ESP.getFreeHeap();
|
|
67
68
|
uint32_t maxAlloc = ESP.getMaxAllocHeap();
|
|
68
69
|
Serial.printf(
|
|
@@ -71,7 +72,7 @@ static inline void ui_warn_scroll_memory(uint16_t nodeIdx, uint8_t reason) {
|
|
|
71
72
|
"Shrink the scroll viewport in CSS, trim fonts/images, or use PSRAM.\\n",
|
|
72
73
|
label, vw, vh, (unsigned long)need, reasonText,
|
|
73
74
|
(unsigned long)freeHeap, (unsigned long)maxAlloc, UI_SCROLL_CANVAS_BUDGET_BYTES);
|
|
74
|
-
#elif defined(ESP8266)
|
|
75
|
+
#elif defined(ESP8266) && defined(ARDUINO)
|
|
75
76
|
uint32_t freeHeap = ESP.getFreeHeap();
|
|
76
77
|
Serial.printf(
|
|
77
78
|
"[cuttlefish] WARNING: #%s (%dx%d) needs %lu bytes for accurate scroll — %s. "
|
|
@@ -139,13 +140,18 @@ static inline void ui_shift_container_canvas(CuttlefishCanvas16* canvas, int16_t
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
// Repair canvas: parent-seeded background repaints + exposed-strip redraws.
|
|
142
|
-
//
|
|
143
|
-
//
|
|
143
|
+
// Reused across navigations when size matches; reallocated on exact-size
|
|
144
|
+
// mismatch (a reused larger canvas would keep its old stride, corrupting the
|
|
145
|
+
// pushed pixels — see the inline comment below).
|
|
144
146
|
static inline CuttlefishCanvas16* ui_get_repair_canvas(int16_t w, int16_t h) {
|
|
145
147
|
if (w <= 0 || h <= 0) return nullptr;
|
|
148
|
+
// Reallocate when size differs at all (not just when growing). A reused
|
|
149
|
+
// larger canvas keeps its old stride, and ui_push_canvas_rect uses that
|
|
150
|
+
// stride for row offsets — a mismatch with the caller's expected (w,h)
|
|
151
|
+
// corrupts the pushed pixels. Exact-size reallocation guarantees stride == w.
|
|
146
152
|
if (!__ui_repair_canvas || !display_canvasBuffer(__ui_repair_canvas) ||
|
|
147
|
-
display_canvasWidth(__ui_repair_canvas)
|
|
148
|
-
display_canvasHeight(__ui_repair_canvas)
|
|
153
|
+
display_canvasWidth(__ui_repair_canvas) != w ||
|
|
154
|
+
display_canvasHeight(__ui_repair_canvas) != h) {
|
|
149
155
|
display_deleteCanvas(__ui_repair_canvas);
|
|
150
156
|
__ui_repair_canvas = ui_create_canvas_best(w, h);
|
|
151
157
|
}
|