@typecad/ui 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +1443 -0
- package/dist/engine-index.d.ts +7 -0
- package/dist/engine-index.js +41 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +42 -0
- package/dist/preview/build-program.d.ts +7 -0
- package/dist/preview/build-program.js +665 -0
- package/dist/preview/host-gfx.d.ts +90 -0
- package/dist/preview/host-gfx.js +552 -0
- package/dist/preview/host-ui-runtime.d.ts +241 -0
- package/dist/preview/host-ui-runtime.js +3236 -0
- package/dist/preview/types.d.ts +83 -0
- package/dist/preview/types.js +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.js +10 -0
- package/dist/ui-engine/block-layout.d.ts +8 -0
- package/dist/ui-engine/block-layout.js +95 -0
- package/dist/ui-engine/color.d.ts +49 -0
- package/dist/ui-engine/color.js +273 -0
- package/dist/ui-engine/css-parser.d.ts +155 -0
- package/dist/ui-engine/css-parser.js +1058 -0
- package/dist/ui-engine/default-keyboards.d.ts +3 -0
- package/dist/ui-engine/default-keyboards.js +53 -0
- package/dist/ui-engine/easing.d.ts +12 -0
- package/dist/ui-engine/easing.js +76 -0
- package/dist/ui-engine/font-assets.d.ts +50 -0
- package/dist/ui-engine/font-assets.js +431 -0
- package/dist/ui-engine/html-parser.d.ts +116 -0
- package/dist/ui-engine/html-parser.js +284 -0
- package/dist/ui-engine/image-assets.d.ts +21 -0
- package/dist/ui-engine/image-assets.js +125 -0
- package/dist/ui-engine/inline-parser.d.ts +30 -0
- package/dist/ui-engine/inline-parser.js +66 -0
- package/dist/ui-engine/keyframes.d.ts +3 -0
- package/dist/ui-engine/keyframes.js +39 -0
- package/dist/ui-engine/layout-engine.d.ts +35 -0
- package/dist/ui-engine/layout-engine.js +203 -0
- package/dist/ui-engine/model.d.ts +239 -0
- package/dist/ui-engine/model.js +987 -0
- package/dist/ui-engine/rich-layout.d.ts +51 -0
- package/dist/ui-engine/rich-layout.js +220 -0
- package/dist/ui-engine/run-types.d.ts +13 -0
- package/dist/ui-engine/run-types.js +1 -0
- package/dist/ui-engine/runtime-header/antialiasing.d.ts +1 -0
- package/dist/ui-engine/runtime-header/antialiasing.js +141 -0
- package/dist/ui-engine/runtime-header/blend-bodies.d.ts +1 -0
- package/dist/ui-engine/runtime-header/blend-bodies.js +39 -0
- package/dist/ui-engine/runtime-header/canvas-helpers.d.ts +1 -0
- package/dist/ui-engine/runtime-header/canvas-helpers.js +155 -0
- package/dist/ui-engine/runtime-header/canvas-scrollbar.d.ts +1 -0
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +105 -0
- package/dist/ui-engine/runtime-header/color-mono-refresh.d.ts +1 -0
- package/dist/ui-engine/runtime-header/color-mono-refresh.js +154 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.d.ts +1 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +169 -0
- package/dist/ui-engine/runtime-header/display-shim.d.ts +1 -0
- package/dist/ui-engine/runtime-header/display-shim.js +104 -0
- package/dist/ui-engine/runtime-header/forward-decls.d.ts +1 -0
- package/dist/ui-engine/runtime-header/forward-decls.js +137 -0
- package/dist/ui-engine/runtime-header/guard-close.d.ts +1 -0
- package/dist/ui-engine/runtime-header/guard-close.js +8 -0
- package/dist/ui-engine/runtime-header/image-drawing.d.ts +1 -0
- package/dist/ui-engine/runtime-header/image-drawing.js +300 -0
- package/dist/ui-engine/runtime-header/init-press-input.d.ts +1 -0
- package/dist/ui-engine/runtime-header/init-press-input.js +126 -0
- package/dist/ui-engine/runtime-header/keyboard.d.ts +1 -0
- package/dist/ui-engine/runtime-header/keyboard.js +342 -0
- package/dist/ui-engine/runtime-header/node-decoration.d.ts +1 -0
- package/dist/ui-engine/runtime-header/node-decoration.js +212 -0
- package/dist/ui-engine/runtime-header/paint-order-coords.d.ts +1 -0
- package/dist/ui-engine/runtime-header/paint-order-coords.js +190 -0
- package/dist/ui-engine/runtime-header/paint-rects-repair.d.ts +1 -0
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +553 -0
- package/dist/ui-engine/runtime-header/scroll-physics.d.ts +1 -0
- package/dist/ui-engine/runtime-header/scroll-physics.js +161 -0
- package/dist/ui-engine/runtime-header/state-bindings-nav.d.ts +1 -0
- package/dist/ui-engine/runtime-header/state-bindings-nav.js +176 -0
- package/dist/ui-engine/runtime-header/structs.d.ts +1 -0
- package/dist/ui-engine/runtime-header/structs.js +220 -0
- package/dist/ui-engine/runtime-header/text-rendering.d.ts +1 -0
- package/dist/ui-engine/runtime-header/text-rendering.js +715 -0
- package/dist/ui-engine/runtime-header/tick/bindings-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +103 -0
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +1051 -0
- package/dist/ui-engine/runtime-header/tick/flush-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/flush-phase.js +17 -0
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +15 -0
- package/dist/ui-engine/runtime-header/tick/transitions-phase.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick/transitions-phase.js +213 -0
- package/dist/ui-engine/runtime-header/tick-motion-helpers.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick-motion-helpers.js +26 -0
- package/dist/ui-engine/runtime-header/tick.d.ts +1 -0
- package/dist/ui-engine/runtime-header/tick.js +17 -0
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.d.ts +1 -0
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +406 -0
- package/dist/ui-engine/runtime-header/types-defines.d.ts +1 -0
- package/dist/ui-engine/runtime-header/types-defines.js +79 -0
- package/dist/ui-engine/runtime-header.d.ts +1 -0
- package/dist/ui-engine/runtime-header.js +69 -0
- package/dist/ui-engine/scroll-memory-diagnostics.d.ts +16 -0
- package/dist/ui-engine/scroll-memory-diagnostics.js +58 -0
- package/dist/ui-engine/select-engine.d.ts +4 -0
- package/dist/ui-engine/select-engine.js +19 -0
- package/dist/ui-engine/style-resolver.d.ts +77 -0
- package/dist/ui-engine/style-resolver.js +341 -0
- package/dist/ui-engine/text-layout.d.ts +19 -0
- package/dist/ui-engine/text-layout.js +92 -0
- package/dist/ui-engine/transpile-ui.d.ts +11 -0
- package/dist/ui-engine/transpile-ui.js +33 -0
- package/dist/ui-engine/ua-stylesheet.d.ts +3 -0
- package/dist/ui-engine/ua-stylesheet.js +56 -0
- package/dist/ui-engine/ui-file-splitter.d.ts +14 -0
- package/dist/ui-engine/ui-file-splitter.js +47 -0
- package/dist/ui-engine/ui-lowering.d.ts +33 -0
- package/dist/ui-engine/ui-lowering.js +390 -0
- package/dist/ui-engine/ui-registry.d.ts +81 -0
- package/dist/ui-engine/ui-registry.js +429 -0
- package/dist/ui-engine/yoga-layout.d.ts +12 -0
- package/dist/ui-engine/yoga-layout.js +314 -0
- package/package.json +86 -0
- package/src/engine-index.ts +51 -0
- package/src/index.ts +232 -0
- package/src/preview/build-program.ts +734 -0
- package/src/preview/host-gfx.ts +580 -0
- package/src/preview/host-ui-runtime.ts +3245 -0
- package/src/preview/types.ts +96 -0
- package/src/types.ts +127 -0
- package/src/ui-engine/block-layout.ts +104 -0
- package/src/ui-engine/color.ts +263 -0
- package/src/ui-engine/css-parser.ts +1003 -0
- package/src/ui-engine/default-keyboards.ts +58 -0
- package/src/ui-engine/easing.ts +53 -0
- package/src/ui-engine/font-assets.ts +512 -0
- package/src/ui-engine/html-parser.ts +397 -0
- package/src/ui-engine/image-assets.ts +170 -0
- package/src/ui-engine/inline-parser.ts +78 -0
- package/src/ui-engine/keyframes.ts +47 -0
- package/src/ui-engine/layout-engine.ts +228 -0
- package/src/ui-engine/model.ts +1225 -0
- package/src/ui-engine/rich-layout.ts +257 -0
- package/src/ui-engine/run-types.ts +14 -0
- package/src/ui-engine/runtime-header/antialiasing.ts +141 -0
- package/src/ui-engine/runtime-header/blend-bodies.ts +39 -0
- package/src/ui-engine/runtime-header/canvas-helpers.ts +155 -0
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +105 -0
- package/src/ui-engine/runtime-header/color-mono-refresh.ts +154 -0
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +169 -0
- package/src/ui-engine/runtime-header/display-shim.ts +104 -0
- package/src/ui-engine/runtime-header/forward-decls.ts +137 -0
- package/src/ui-engine/runtime-header/guard-close.ts +8 -0
- package/src/ui-engine/runtime-header/image-drawing.ts +300 -0
- package/src/ui-engine/runtime-header/init-press-input.ts +126 -0
- package/src/ui-engine/runtime-header/keyboard.ts +342 -0
- package/src/ui-engine/runtime-header/node-decoration.ts +212 -0
- package/src/ui-engine/runtime-header/paint-order-coords.ts +190 -0
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +553 -0
- package/src/ui-engine/runtime-header/scroll-physics.ts +161 -0
- package/src/ui-engine/runtime-header/state-bindings-nav.ts +176 -0
- package/src/ui-engine/runtime-header/structs.ts +220 -0
- package/src/ui-engine/runtime-header/text-rendering.ts +715 -0
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +103 -0
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +1051 -0
- package/src/ui-engine/runtime-header/tick/flush-phase.ts +17 -0
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +15 -0
- package/src/ui-engine/runtime-header/tick/transitions-phase.ts +213 -0
- package/src/ui-engine/runtime-header/tick-motion-helpers.ts +26 -0
- package/src/ui-engine/runtime-header/tick.ts +18 -0
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +406 -0
- package/src/ui-engine/runtime-header/types-defines.ts +79 -0
- package/src/ui-engine/runtime-header.ts +71 -0
- package/src/ui-engine/scroll-memory-diagnostics.ts +79 -0
- package/src/ui-engine/select-engine.ts +21 -0
- package/src/ui-engine/style-resolver.ts +416 -0
- package/src/ui-engine/text-layout.ts +114 -0
- package/src/ui-engine/transpile-ui.ts +45 -0
- package/src/ui-engine/ua-stylesheet.ts +60 -0
- package/src/ui-engine/ui-file-splitter.ts +59 -0
- package/src/ui-engine/ui-lowering.ts +459 -0
- package/src/ui-engine/ui-registry.ts +530 -0
- package/src/ui-engine/yoga-layout.ts +309 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 1895-2441).
|
|
2
|
+
// Paint rects, overlapping layers dirty, buffer decisions, seed/repair, scroll-clip tests, clipped primitives, clear/repair, set_visible.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitPaintRectsRepair(): string {
|
|
5
|
+
return `
|
|
6
|
+
static inline void ui_node_paint_rect(uint16_t nodeIdx, int16_t baseX, int16_t baseY, int16_t drawX, int16_t drawY, uint16_t textW, uint16_t textH, UIRect* out) {
|
|
7
|
+
int16_t shadowL, shadowT, shadowR, shadowB;
|
|
8
|
+
ui_shadow_extents(nodeIdx, &shadowL, &shadowT, &shadowR, &shadowB);
|
|
9
|
+
|
|
10
|
+
int16_t faceW = __ui_nodes[nodeIdx].box.w;
|
|
11
|
+
int16_t faceH = __ui_nodes[nodeIdx].box.h;
|
|
12
|
+
if (__ui_nodes[nodeIdx].kind == NODE_TEXT || __ui_nodes[nodeIdx].kind == NODE_CHECK || __ui_nodes[nodeIdx].kind == NODE_RADIO) {
|
|
13
|
+
if (__ui_nodes[nodeIdx].lastTextWidth > faceW) faceW = __ui_nodes[nodeIdx].lastTextWidth;
|
|
14
|
+
if (__ui_nodes[nodeIdx].lastTextHeight > faceH) faceH = __ui_nodes[nodeIdx].lastTextHeight;
|
|
15
|
+
if ((int16_t)textW > faceW) faceW = (int16_t)textW;
|
|
16
|
+
if ((int16_t)textH > faceH) faceH = (int16_t)textH;
|
|
17
|
+
}
|
|
18
|
+
int16_t unrotatedFaceW = faceW;
|
|
19
|
+
int16_t unrotatedFaceH = faceH;
|
|
20
|
+
faceW = ui_rotated_face_w(nodeIdx, unrotatedFaceW, unrotatedFaceH);
|
|
21
|
+
faceH = ui_rotated_face_h(nodeIdx, unrotatedFaceW, unrotatedFaceH);
|
|
22
|
+
|
|
23
|
+
int16_t shadowX0 = baseX - shadowL;
|
|
24
|
+
int16_t shadowY0 = baseY - shadowT;
|
|
25
|
+
int16_t shadowX1 = baseX + faceW + shadowR;
|
|
26
|
+
int16_t shadowY1 = baseY + faceH + shadowB;
|
|
27
|
+
int16_t faceX0 = drawX;
|
|
28
|
+
int16_t faceY0 = drawY;
|
|
29
|
+
int16_t faceX1 = drawX + faceW;
|
|
30
|
+
int16_t faceY1 = drawY + faceH;
|
|
31
|
+
int16_t x0 = shadowX0 < faceX0 ? shadowX0 : faceX0;
|
|
32
|
+
int16_t y0 = shadowY0 < faceY0 ? shadowY0 : faceY0;
|
|
33
|
+
int16_t x1 = shadowX1 > faceX1 ? shadowX1 : faceX1;
|
|
34
|
+
int16_t y1 = shadowY1 > faceY1 ? shadowY1 : faceY1;
|
|
35
|
+
if (__ui_nodes[nodeIdx].outlineStyle != 0 && __ui_nodes[nodeIdx].outlineWidth > 0) {
|
|
36
|
+
int16_t o = __ui_nodes[nodeIdx].outlineWidth;
|
|
37
|
+
ui_expand_rect(&x0, &y0, &x1, &y1, drawX - o, drawY - o, drawX + faceW + o, drawY + faceH + o);
|
|
38
|
+
}
|
|
39
|
+
out->x = x0;
|
|
40
|
+
out->y = y0;
|
|
41
|
+
out->w = x1 - x0;
|
|
42
|
+
out->h = y1 - y0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static inline void ui_node_current_paint_rect(uint16_t nodeIdx, UIRect* out) {
|
|
46
|
+
uint16_t tw = 0;
|
|
47
|
+
uint16_t th = 0;
|
|
48
|
+
uint16_t textMaxW = ui_node_text_max_width(nodeIdx);
|
|
49
|
+
ui_node_text_layout_metrics(nodeIdx, textMaxW, &tw, &th);
|
|
50
|
+
if (__ui_nodes[nodeIdx].kind == NODE_CHECK || __ui_nodes[nodeIdx].kind == NODE_RADIO) {
|
|
51
|
+
tw += 22;
|
|
52
|
+
if (th < 16) th = 16;
|
|
53
|
+
}
|
|
54
|
+
ui_node_paint_rect(nodeIdx,
|
|
55
|
+
ui_base_draw_x_for_node(nodeIdx),
|
|
56
|
+
ui_base_draw_y_for_node(nodeIdx),
|
|
57
|
+
ui_draw_x_for_node(nodeIdx),
|
|
58
|
+
ui_draw_y_for_node(nodeIdx),
|
|
59
|
+
tw,
|
|
60
|
+
th,
|
|
61
|
+
out);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static inline uint8_t ui_subtree_current_paint_rect(uint16_t nodeIdx, UIRect* out) {
|
|
65
|
+
if (nodeIdx >= __ui_node_count) return 0;
|
|
66
|
+
int16_t end = __ui_nodes[nodeIdx].subtreeEnd;
|
|
67
|
+
if (end > __ui_node_count) end = __ui_node_count;
|
|
68
|
+
uint8_t hasRect = 0;
|
|
69
|
+
int16_t x0 = 0;
|
|
70
|
+
int16_t y0 = 0;
|
|
71
|
+
int16_t x1 = 0;
|
|
72
|
+
int16_t y1 = 0;
|
|
73
|
+
for (uint16_t c = nodeIdx; c < end; c++) {
|
|
74
|
+
if (__ui_nodes[c].screenId != __ui_active_screen) continue;
|
|
75
|
+
if (!ui_is_effectively_visible(c)) continue;
|
|
76
|
+
UIRect r;
|
|
77
|
+
ui_node_current_paint_rect(c, &r);
|
|
78
|
+
if (r.w <= 0 || r.h <= 0) continue;
|
|
79
|
+
if (!hasRect) {
|
|
80
|
+
x0 = r.x;
|
|
81
|
+
y0 = r.y;
|
|
82
|
+
x1 = r.x + r.w;
|
|
83
|
+
y1 = r.y + r.h;
|
|
84
|
+
hasRect = 1;
|
|
85
|
+
} else {
|
|
86
|
+
ui_expand_rect(&x0, &y0, &x1, &y1, r.x, r.y, r.x + r.w, r.y + r.h);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!hasRect) return 0;
|
|
90
|
+
out->x = x0;
|
|
91
|
+
out->y = y0;
|
|
92
|
+
out->w = x1 - x0;
|
|
93
|
+
out->h = y1 - y0;
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static inline void ui_mark_overlapping_higher_layers_dirty(uint16_t nodeIdx) {
|
|
98
|
+
if (nodeIdx >= __ui_node_count) return;
|
|
99
|
+
if (!ui_is_effectively_visible(nodeIdx)) return;
|
|
100
|
+
if (__ui_nodes[nodeIdx].screenId != __ui_active_screen) return;
|
|
101
|
+
UIRect r;
|
|
102
|
+
ui_node_current_paint_rect(nodeIdx, &r);
|
|
103
|
+
ui_mark_overlapping_higher_layers_dirty_for_rect(nodeIdx, &r);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static inline void ui_mark_overlapping_higher_layers_dirty_for_rect(uint16_t nodeIdx, const UIRect* r) {
|
|
107
|
+
if (nodeIdx >= __ui_node_count || !r) return;
|
|
108
|
+
if (!ui_is_effectively_visible(nodeIdx)) return;
|
|
109
|
+
if (__ui_nodes[nodeIdx].screenId != __ui_active_screen) return;
|
|
110
|
+
if (r->w <= 0 || r->h <= 0) return;
|
|
111
|
+
for (uint16_t c = 0; c < __ui_node_count; c++) {
|
|
112
|
+
if (c == nodeIdx) continue;
|
|
113
|
+
if (__ui_nodes[c].dirty) continue;
|
|
114
|
+
if (!ui_is_effectively_visible(c)) continue;
|
|
115
|
+
if (__ui_nodes[c].screenId != __ui_active_screen) continue;
|
|
116
|
+
if (!ui_node_draws_before(nodeIdx, c)) continue;
|
|
117
|
+
UIRect cr;
|
|
118
|
+
ui_node_current_paint_rect(c, &cr);
|
|
119
|
+
if (cr.w <= 0 || cr.h <= 0) continue;
|
|
120
|
+
if (ui_rects_intersect(r->x, r->y, r->w, r->h, cr.x, cr.y, cr.w, cr.h)) {
|
|
121
|
+
__ui_nodes[c].dirty = 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static inline void ui_clear_press_offset_area(uint16_t nodeIdx, int16_t baseX, int16_t baseY, int16_t drawX, int16_t drawY, uint16_t textW, uint16_t textH) {
|
|
127
|
+
if (__ui_nodes[nodeIdx].pressedOffsetX == 0 && __ui_nodes[nodeIdx].pressedOffsetY == 0) return;
|
|
128
|
+
UIRect r;
|
|
129
|
+
ui_node_paint_rect(nodeIdx, baseX, baseY, drawX, drawY, textW, textH, &r);
|
|
130
|
+
int16_t x0 = r.x;
|
|
131
|
+
int16_t y0 = r.y;
|
|
132
|
+
int16_t x1 = r.x + r.w;
|
|
133
|
+
int16_t y1 = r.y + r.h;
|
|
134
|
+
if (ui_display_is_default_target() && ui_repair_current_node_paint_with_parent(nodeIdx, &r)) return;
|
|
135
|
+
ui_display_fill_rect(x0, y0, x1 - x0, y1 - y0, ui_parent_clear_color(nodeIdx));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Generated-font / AA text and images draw per-pixel when sent straight to SPI.
|
|
139
|
+
// Prefer the RAM paint canvas for these kinds (within the pixel budget).
|
|
140
|
+
static inline uint8_t ui_pixel_heavy_node(uint16_t nodeIdx) {
|
|
141
|
+
if (nodeIdx >= __ui_node_count) return 0;
|
|
142
|
+
if (__ui_nodes[nodeIdx].kind == NODE_IMG) return 1;
|
|
143
|
+
if (__ui_nodes[nodeIdx].kind == NODE_TEXT &&
|
|
144
|
+
(__ui_nodes[nodeIdx].fontFace || __ui_nodes[nodeIdx].fontAntialias)) return 1;
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static inline uint8_t ui_should_buffer_paint(uint16_t nodeIdx, int16_t w, int16_t h) {
|
|
149
|
+
if (w <= 0 || h <= 0) return 0;
|
|
150
|
+
if (__ui_nodes[nodeIdx].kind == NODE_LIST) return 0;
|
|
151
|
+
// <canvas> elements batch via __ui_node_canvas when drawing to the display.
|
|
152
|
+
if (__ui_nodes[nodeIdx].kind == NODE_CANVAS) return 0;
|
|
153
|
+
if ((uint32_t)w * (uint32_t)h > UI_MAX_BUFFERED_PAINT_PIXELS) return 0;
|
|
154
|
+
if (ui_pixel_heavy_node(nodeIdx)) return 1;
|
|
155
|
+
if (__ui_nodes[nodeIdx].kind == NODE_FILL &&
|
|
156
|
+
__ui_nodes[nodeIdx].hasBg &&
|
|
157
|
+
__ui_nodes[nodeIdx].gradientEnabled == 0 &&
|
|
158
|
+
__ui_nodes[nodeIdx].borderRadius == 0 &&
|
|
159
|
+
__ui_nodes[nodeIdx].borderStyle == 0 &&
|
|
160
|
+
__ui_nodes[nodeIdx].outlineStyle == 0 &&
|
|
161
|
+
__ui_nodes[nodeIdx].shadowCount == 0) return 0;
|
|
162
|
+
if (__ui_nodes[nodeIdx].kind == NODE_FILL &&
|
|
163
|
+
!__ui_nodes[nodeIdx].hasBg &&
|
|
164
|
+
__ui_nodes[nodeIdx].borderStyle == 0 &&
|
|
165
|
+
__ui_nodes[nodeIdx].outlineStyle == 0 &&
|
|
166
|
+
__ui_nodes[nodeIdx].shadowCount == 0) return 0;
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static inline void ui_seed_paint_canvas_for_node(uint16_t nodeIdx, CuttlefishCanvas16* canvas, int16_t canvasX, int16_t canvasY) {
|
|
171
|
+
if (!canvas || !display_canvasBuffer(canvas)) return;
|
|
172
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
173
|
+
if (p == UI_NO_PARENT || p >= __ui_node_count) {
|
|
174
|
+
display_canvasFillScreen(canvas, __ui_nodes[nodeIdx].clearColor);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
uint16_t parentDecorated =
|
|
179
|
+
(__ui_nodes[p].hasBg && (__ui_nodes[p].borderRadius > 0 || __ui_nodes[p].gradientEnabled > 0)) ||
|
|
180
|
+
__ui_nodes[p].borderStyle != 0 ||
|
|
181
|
+
__ui_nodes[p].outlineStyle != 0;
|
|
182
|
+
if (!parentDecorated) {
|
|
183
|
+
display_canvasFillScreen(canvas, ui_parent_clear_color(nodeIdx));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
display_canvasFillScreen(canvas, ui_parent_clear_color(p));
|
|
188
|
+
int16_t parentDrawX = ui_draw_x_for_node(p);
|
|
189
|
+
int16_t parentDrawY = ui_draw_y_for_node(p);
|
|
190
|
+
int16_t origParentX = __ui_nodes[p].box.x;
|
|
191
|
+
CuttlefishDisplayTarget* previousGfx = ui_display_get_target();
|
|
192
|
+
ui_display_set_target(canvas);
|
|
193
|
+
__ui_nodes[p].box.x = parentDrawX - canvasX;
|
|
194
|
+
int16_t localY = parentDrawY - canvasY;
|
|
195
|
+
|
|
196
|
+
// Parent fill color: blended toward the parent's backdrop when the parent is
|
|
197
|
+
// translucent (matching the main NODE_FILL draw, which uses fillBg). Without
|
|
198
|
+
// this, scroll repair seeds the canvas with the parent's RAW bg while the
|
|
199
|
+
// main draw used the blended color → shearing on translucent nodes during scroll.
|
|
200
|
+
UI_COLOR_T parentFillBg = __ui_nodes[p].bg;
|
|
201
|
+
if (__ui_nodes[p].opacity < 100) {
|
|
202
|
+
parentFillBg = ui_blend(__ui_nodes[p].bg, ui_parent_clear_color(p), __ui_nodes[p].opacity);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (__ui_nodes[p].gradientEnabled > 0) {
|
|
206
|
+
ui_draw_gradient_fill(p, localY);
|
|
207
|
+
} else if (__ui_nodes[p].borderRadius > 0 && __ui_nodes[p].hasBg) {
|
|
208
|
+
ui_display_fill_round_rect(__ui_nodes[p].box.x, localY, __ui_nodes[p].box.w, __ui_nodes[p].box.h,
|
|
209
|
+
__ui_nodes[p].borderRadius, parentFillBg);
|
|
210
|
+
} else if (__ui_nodes[p].hasBg) {
|
|
211
|
+
ui_display_fill_rect(__ui_nodes[p].box.x, localY, __ui_nodes[p].box.w, __ui_nodes[p].box.h, parentFillBg);
|
|
212
|
+
}
|
|
213
|
+
if (__ui_nodes[p].borderStyle != 0) {
|
|
214
|
+
UI_COLOR_T bColor = __ui_nodes[p].borderColor ? __ui_nodes[p].borderColor : __ui_nodes[p].fg;
|
|
215
|
+
ui_draw_node_border(p, __ui_nodes[p].box.x, localY, bColor);
|
|
216
|
+
}
|
|
217
|
+
ui_draw_node_outline(p, __ui_nodes[p].box.x, localY);
|
|
218
|
+
|
|
219
|
+
__ui_nodes[p].box.x = origParentX;
|
|
220
|
+
ui_display_set_target(previousGfx);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static inline uint8_t ui_repair_current_node_paint_with_parent(uint16_t nodeIdx, UIRect* r) {
|
|
224
|
+
if (!r || r->w <= 0 || r->h <= 0) return 0;
|
|
225
|
+
if ((uint32_t)r->w * (uint32_t)r->h > UI_MAX_BUFFERED_PAINT_PIXELS) return 0;
|
|
226
|
+
CuttlefishCanvas16* repairCanvas = ui_get_repair_canvas(r->w, r->h);
|
|
227
|
+
if (!repairCanvas) return 0;
|
|
228
|
+
ui_seed_paint_canvas_for_node(nodeIdx, repairCanvas, r->x, r->y);
|
|
229
|
+
ui_display_use_default_target();
|
|
230
|
+
ui_push_canvas_rect(repairCanvas, r->x, r->y, r->w, r->h);
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static inline uint8_t ui_is_rect_clipped_by_scroll(uint16_t nodeIdx, int16_t drawX, int16_t drawY, int16_t drawW, int16_t drawH) {
|
|
235
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
236
|
+
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
237
|
+
if (__ui_nodes[p].scrollable) {
|
|
238
|
+
if (drawX < __ui_nodes[p].box.x ||
|
|
239
|
+
drawX + drawW > __ui_nodes[p].box.x + __ui_nodes[p].box.w ||
|
|
240
|
+
drawY < __ui_nodes[p].box.y ||
|
|
241
|
+
drawY + drawH > __ui_nodes[p].box.y + __ui_nodes[p].box.h) {
|
|
242
|
+
return 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
p = __ui_nodes[p].parent;
|
|
246
|
+
}
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static inline uint8_t ui_is_clipped_by_scroll(uint16_t nodeIdx, int16_t drawX, int16_t drawY) {
|
|
251
|
+
return ui_is_rect_clipped_by_scroll(nodeIdx, drawX, drawY, __ui_nodes[nodeIdx].box.w, __ui_nodes[nodeIdx].box.h);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Point-in-viewport test for hit-testing. A tap point is tappable if it lies
|
|
255
|
+
// within EVERY scrollable ancestor's viewport (logical AND, matching the
|
|
256
|
+
// preview's intersected scroll clip). This differs from ui_is_clipped_by_scroll,
|
|
257
|
+
// which tests the node's whole bounding box — a tall node (e.g. a wrapped
|
|
258
|
+
// rich-text paragraph) can overflow below the fold yet have a tappable link in
|
|
259
|
+
// its visible portion. Use this in the hit-test path; keep the whole-box check
|
|
260
|
+
// for draw culling, where a partially-visible node still needs repainting.
|
|
261
|
+
static inline uint8_t ui_is_point_clipped_by_scroll(uint16_t nodeIdx, int16_t px, int16_t py) {
|
|
262
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
263
|
+
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
264
|
+
if (__ui_nodes[p].scrollable) {
|
|
265
|
+
if (px < __ui_nodes[p].box.x ||
|
|
266
|
+
px >= __ui_nodes[p].box.x + __ui_nodes[p].box.w ||
|
|
267
|
+
py < __ui_nodes[p].box.y ||
|
|
268
|
+
py >= __ui_nodes[p].box.y + __ui_nodes[p].box.h) {
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
p = __ui_nodes[p].parent;
|
|
273
|
+
}
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
static inline uint8_t ui_clip_rect_to_rect(UIRect* r, const UIRect* clip) {
|
|
278
|
+
int16_t x0 = r->x > clip->x ? r->x : clip->x;
|
|
279
|
+
int16_t y0 = r->y > clip->y ? r->y : clip->y;
|
|
280
|
+
int16_t x1 = r->x + r->w < clip->x + clip->w ? r->x + r->w : clip->x + clip->w;
|
|
281
|
+
int16_t y1 = r->y + r->h < clip->y + clip->h ? r->y + r->h : clip->y + clip->h;
|
|
282
|
+
if (x1 <= x0 || y1 <= y0) return 0;
|
|
283
|
+
r->x = x0;
|
|
284
|
+
r->y = y0;
|
|
285
|
+
r->w = x1 - x0;
|
|
286
|
+
r->h = y1 - y0;
|
|
287
|
+
return 1;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
static inline void ui_fill_rect_clipped(int16_t x, int16_t y, int16_t w, int16_t h, const UIRect* clip, UI_COLOR_T color) {
|
|
291
|
+
UIRect r = { x, y, w, h };
|
|
292
|
+
if (!ui_clip_rect_to_rect(&r, clip)) return;
|
|
293
|
+
ui_display_fill_rect(r.x, r.y, r.w, r.h, color);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static inline void ui_hline_clipped(int16_t x, int16_t y, int16_t w, const UIRect* clip, UI_COLOR_T color) {
|
|
297
|
+
if (w <= 0 || y < clip->y || y >= clip->y + clip->h) return;
|
|
298
|
+
int16_t x0 = x > clip->x ? x : clip->x;
|
|
299
|
+
int16_t x1 = x + w < clip->x + clip->w ? x + w : clip->x + clip->w;
|
|
300
|
+
if (x1 <= x0) return;
|
|
301
|
+
ui_display_draw_fast_hline(x0, y, x1 - x0, color);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
static inline void ui_vline_clipped(int16_t x, int16_t y, int16_t h, const UIRect* clip, UI_COLOR_T color) {
|
|
305
|
+
if (h <= 0 || x < clip->x || x >= clip->x + clip->w) return;
|
|
306
|
+
int16_t y0 = y > clip->y ? y : clip->y;
|
|
307
|
+
int16_t y1 = y + h < clip->y + clip->h ? y + h : clip->y + clip->h;
|
|
308
|
+
if (y1 <= y0) return;
|
|
309
|
+
ui_display_draw_fast_vline(x, y0, y1 - y0, color);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static inline void ui_draw_rect_outline_clipped(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t style, uint8_t width, const UIRect* clip, UI_COLOR_T color) {
|
|
313
|
+
if (style == 0 || width == 0 || w <= 0 || h <= 0) return;
|
|
314
|
+
for (uint8_t b = 0; b < width; b++) {
|
|
315
|
+
int16_t rx = x + b;
|
|
316
|
+
int16_t ry = y + b;
|
|
317
|
+
int16_t rw = w - 2 * b;
|
|
318
|
+
int16_t rh = h - 2 * b;
|
|
319
|
+
if (rw <= 0 || rh <= 0) return;
|
|
320
|
+
if (style == 1) {
|
|
321
|
+
ui_hline_clipped(rx, ry, rw, clip, color);
|
|
322
|
+
ui_hline_clipped(rx, ry + rh - 1, rw, clip, color);
|
|
323
|
+
ui_vline_clipped(rx, ry, rh, clip, color);
|
|
324
|
+
ui_vline_clipped(rx + rw - 1, ry, rh, clip, color);
|
|
325
|
+
} else {
|
|
326
|
+
for (int16_t dx = 0; dx < rw; dx += 8) {
|
|
327
|
+
int16_t seg = (dx + 4 <= rw) ? 4 : (rw - dx);
|
|
328
|
+
ui_hline_clipped(rx + dx, ry, seg, clip, color);
|
|
329
|
+
ui_hline_clipped(rx + dx, ry + rh - 1, seg, clip, color);
|
|
330
|
+
}
|
|
331
|
+
for (int16_t dy = 0; dy < rh; dy += 8) {
|
|
332
|
+
int16_t seg = (dy + 4 <= rh) ? 4 : (rh - dy);
|
|
333
|
+
ui_vline_clipped(rx, ry + dy, seg, clip, color);
|
|
334
|
+
ui_vline_clipped(rx + rw - 1, ry + dy, seg, clip, color);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
static inline void ui_draw_node_decoration_clipped(uint16_t nodeIdx, int16_t drawY, const UIRect* clip) {
|
|
341
|
+
if (nodeIdx >= __ui_node_count) return;
|
|
342
|
+
if (__ui_nodes[nodeIdx].borderStyle != 0) {
|
|
343
|
+
UI_COLOR_T bColor = __ui_nodes[nodeIdx].borderColor ? __ui_nodes[nodeIdx].borderColor : __ui_nodes[nodeIdx].fg;
|
|
344
|
+
int16_t x = __ui_nodes[nodeIdx].box.x;
|
|
345
|
+
int16_t y = drawY;
|
|
346
|
+
int16_t w = __ui_nodes[nodeIdx].box.w;
|
|
347
|
+
int16_t h = __ui_nodes[nodeIdx].box.h;
|
|
348
|
+
if (__ui_nodes[nodeIdx].borderRadius > 0) {
|
|
349
|
+
if (x >= clip->x && y >= clip->y && x + w <= clip->x + clip->w && y + h <= clip->y + clip->h) {
|
|
350
|
+
ui_draw_node_border(nodeIdx, x, drawY, bColor);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
ui_draw_rect_outline_clipped(x, y, w, h,
|
|
354
|
+
__ui_nodes[nodeIdx].borderStyle, __ui_nodes[nodeIdx].borderWidth, clip, bColor);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (__ui_nodes[nodeIdx].outlineStyle != 0 && __ui_nodes[nodeIdx].outlineWidth > 0) {
|
|
358
|
+
uint8_t w = __ui_nodes[nodeIdx].outlineWidth;
|
|
359
|
+
int16_t x = __ui_nodes[nodeIdx].box.x - w;
|
|
360
|
+
int16_t y = drawY - w;
|
|
361
|
+
int16_t ow = __ui_nodes[nodeIdx].box.w + 2 * w;
|
|
362
|
+
int16_t oh = __ui_nodes[nodeIdx].box.h + 2 * w;
|
|
363
|
+
if (__ui_nodes[nodeIdx].borderRadius > 0) {
|
|
364
|
+
if (x >= clip->x && y >= clip->y && x + ow <= clip->x + clip->w && y + oh <= clip->y + clip->h) {
|
|
365
|
+
ui_draw_node_outline(nodeIdx, __ui_nodes[nodeIdx].box.x, drawY);
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
ui_draw_rect_outline_clipped(x, y, ow, oh,
|
|
369
|
+
__ui_nodes[nodeIdx].outlineStyle, w, clip, __ui_nodes[nodeIdx].outlineColor);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static inline void ui_clear_node_paint_rect(uint16_t nodeIdx, const UIRect* paintRect) {
|
|
375
|
+
if (nodeIdx >= __ui_node_count || !paintRect || paintRect->w <= 0 || paintRect->h <= 0) return;
|
|
376
|
+
int16_t scrollParent = ui_scroll_ancestor_for_node(nodeIdx);
|
|
377
|
+
UIRect r = *paintRect;
|
|
378
|
+
ui_display_use_default_target();
|
|
379
|
+
if (scrollParent >= 0) {
|
|
380
|
+
UIRect clip = {
|
|
381
|
+
__ui_nodes[scrollParent].box.x,
|
|
382
|
+
__ui_nodes[scrollParent].box.y,
|
|
383
|
+
__ui_nodes[scrollParent].box.w,
|
|
384
|
+
__ui_nodes[scrollParent].box.h
|
|
385
|
+
};
|
|
386
|
+
UIRect clipped = r;
|
|
387
|
+
if (!ui_clip_rect_to_rect(&clipped, &clip)) return;
|
|
388
|
+
if (ui_repair_current_node_paint_with_parent(nodeIdx, &clipped)) return;
|
|
389
|
+
ui_fill_rect_clipped(clipped.x, clipped.y, clipped.w, clipped.h, &clip, ui_parent_clear_color(nodeIdx));
|
|
390
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
391
|
+
if (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
392
|
+
ui_draw_node_decoration_clipped(p, ui_draw_y_for_node(p), &clip);
|
|
393
|
+
}
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (ui_is_rect_clipped_by_scroll(nodeIdx, r.x, r.y, r.w, r.h)) return;
|
|
397
|
+
ui_display_fill_rect(r.x, r.y, r.w, r.h, ui_parent_clear_color(nodeIdx));
|
|
398
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
399
|
+
if (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
400
|
+
int16_t parentDrawY = ui_draw_y_for_node(p);
|
|
401
|
+
if (__ui_nodes[p].borderStyle != 0) {
|
|
402
|
+
UI_COLOR_T bColor = __ui_nodes[p].borderColor ? __ui_nodes[p].borderColor : __ui_nodes[p].fg;
|
|
403
|
+
ui_draw_node_border(p, ui_draw_x_for_node(p), parentDrawY, bColor);
|
|
404
|
+
}
|
|
405
|
+
ui_draw_node_outline(p, ui_draw_x_for_node(p), parentDrawY);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
static inline void ui_clear_current_node_paint(uint16_t nodeIdx) {
|
|
410
|
+
if (nodeIdx >= __ui_node_count) return;
|
|
411
|
+
uint16_t tw = 0;
|
|
412
|
+
uint16_t th = 0;
|
|
413
|
+
uint16_t textMaxW = ui_node_text_max_width(nodeIdx);
|
|
414
|
+
ui_node_text_layout_metrics(nodeIdx, textMaxW, &tw, &th);
|
|
415
|
+
if (__ui_nodes[nodeIdx].kind == NODE_CHECK || __ui_nodes[nodeIdx].kind == NODE_RADIO) tw += 22;
|
|
416
|
+
int16_t baseDrawX = ui_base_draw_x_for_node(nodeIdx);
|
|
417
|
+
int16_t baseDrawY = ui_base_draw_y_for_node(nodeIdx);
|
|
418
|
+
int16_t drawX = ui_draw_x_for_node(nodeIdx);
|
|
419
|
+
int16_t drawY = ui_draw_y_for_node(nodeIdx);
|
|
420
|
+
UIRect r;
|
|
421
|
+
ui_node_paint_rect(nodeIdx, baseDrawX, baseDrawY, drawX, drawY, tw, th, &r);
|
|
422
|
+
ui_clear_node_paint_rect(nodeIdx, &r);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
static inline uint8_t ui_try_repair_geometry_fill(uint16_t nodeIdx, const UIRect* oldRect) {
|
|
426
|
+
if (nodeIdx >= __ui_node_count || !oldRect || oldRect->w <= 0 || oldRect->h <= 0) return 0;
|
|
427
|
+
if (__ui_nodes[nodeIdx].kind != NODE_FILL) return 0;
|
|
428
|
+
if (!__ui_nodes[nodeIdx].hasBg) return 0;
|
|
429
|
+
if (__ui_nodes[nodeIdx].gradientEnabled != 0) return 0;
|
|
430
|
+
if (__ui_nodes[nodeIdx].borderRadius != 0 ||
|
|
431
|
+
__ui_nodes[nodeIdx].borderStyle != 0 ||
|
|
432
|
+
__ui_nodes[nodeIdx].outlineStyle != 0 ||
|
|
433
|
+
__ui_nodes[nodeIdx].shadowCount != 0) return 0;
|
|
434
|
+
|
|
435
|
+
UIRect newRect;
|
|
436
|
+
ui_node_current_paint_rect(nodeIdx, &newRect);
|
|
437
|
+
if (newRect.w <= 0 || newRect.h <= 0) return 0;
|
|
438
|
+
int16_t x0 = oldRect->x < newRect.x ? oldRect->x : newRect.x;
|
|
439
|
+
int16_t y0 = oldRect->y < newRect.y ? oldRect->y : newRect.y;
|
|
440
|
+
int16_t x1 = oldRect->x + oldRect->w > newRect.x + newRect.w ? oldRect->x + oldRect->w : newRect.x + newRect.w;
|
|
441
|
+
int16_t y1 = oldRect->y + oldRect->h > newRect.y + newRect.h ? oldRect->y + oldRect->h : newRect.y + newRect.h;
|
|
442
|
+
UIRect repair = { x0, y0, (int16_t)(x1 - x0), (int16_t)(y1 - y0) };
|
|
443
|
+
int16_t scrollParent = ui_scroll_ancestor_for_node(nodeIdx);
|
|
444
|
+
if (scrollParent >= 0) {
|
|
445
|
+
UIRect clip = {
|
|
446
|
+
__ui_nodes[scrollParent].box.x,
|
|
447
|
+
__ui_nodes[scrollParent].box.y,
|
|
448
|
+
__ui_nodes[scrollParent].box.w,
|
|
449
|
+
__ui_nodes[scrollParent].box.h
|
|
450
|
+
};
|
|
451
|
+
if (!ui_clip_rect_to_rect(&repair, &clip)) {
|
|
452
|
+
ui_invalidate_scroll_canvas_for_node(nodeIdx);
|
|
453
|
+
return 1;
|
|
454
|
+
}
|
|
455
|
+
} else if (ui_is_rect_clipped_by_scroll(nodeIdx, repair.x, repair.y, repair.w, repair.h)) {
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
if (repair.w <= 0 || repair.h <= 0) return 0;
|
|
459
|
+
if ((uint32_t)repair.w * (uint32_t)repair.h > UI_MAX_BUFFERED_PAINT_PIXELS) return 0;
|
|
460
|
+
CuttlefishCanvas16* repairCanvas = ui_get_repair_canvas(repair.w, repair.h);
|
|
461
|
+
if (!repairCanvas) return 0;
|
|
462
|
+
|
|
463
|
+
ui_seed_paint_canvas_for_node(nodeIdx, repairCanvas, repair.x, repair.y);
|
|
464
|
+
CuttlefishDisplayTarget* previousGfx = ui_display_get_target();
|
|
465
|
+
ui_display_set_target(repairCanvas);
|
|
466
|
+
UI_COLOR_T fillBg = __ui_nodes[nodeIdx].bg;
|
|
467
|
+
if (__ui_nodes[nodeIdx].opacity < 100) {
|
|
468
|
+
fillBg = ui_blend(__ui_nodes[nodeIdx].bg, ui_parent_clear_color(nodeIdx), __ui_nodes[nodeIdx].opacity);
|
|
469
|
+
}
|
|
470
|
+
int16_t drawX = ui_draw_x_for_node(nodeIdx) - repair.x;
|
|
471
|
+
int16_t drawY = ui_draw_y_for_node(nodeIdx) - repair.y;
|
|
472
|
+
int16_t fillW = ui_rotated_face_w(nodeIdx, __ui_nodes[nodeIdx].box.w, __ui_nodes[nodeIdx].box.h);
|
|
473
|
+
int16_t fillH = ui_rotated_face_h(nodeIdx, __ui_nodes[nodeIdx].box.w, __ui_nodes[nodeIdx].box.h);
|
|
474
|
+
ui_display_fill_rect(drawX, drawY, fillW, fillH, fillBg);
|
|
475
|
+
ui_display_set_target(previousGfx);
|
|
476
|
+
ui_display_use_default_target();
|
|
477
|
+
ui_push_canvas_rect(repairCanvas, repair.x, repair.y, repair.w, repair.h);
|
|
478
|
+
if (scrollParent >= 0) ui_invalidate_scroll_canvas_for_node(nodeIdx);
|
|
479
|
+
ui_mark_overlapping_higher_layers_dirty_for_rect(nodeIdx, &repair);
|
|
480
|
+
return 1;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
static inline void ui_clear_subtree_current_paint(uint16_t nodeIdx) {
|
|
484
|
+
if (nodeIdx >= __ui_node_count) return;
|
|
485
|
+
UIRect r;
|
|
486
|
+
if (!ui_subtree_current_paint_rect(nodeIdx, &r)) return;
|
|
487
|
+
ui_display_use_default_target();
|
|
488
|
+
int16_t scrollParent = ui_scroll_ancestor_for_node(nodeIdx);
|
|
489
|
+
if (scrollParent >= 0) {
|
|
490
|
+
UIRect clip = {
|
|
491
|
+
__ui_nodes[scrollParent].box.x,
|
|
492
|
+
__ui_nodes[scrollParent].box.y,
|
|
493
|
+
__ui_nodes[scrollParent].box.w,
|
|
494
|
+
__ui_nodes[scrollParent].box.h
|
|
495
|
+
};
|
|
496
|
+
UIRect clipped = r;
|
|
497
|
+
if (!ui_clip_rect_to_rect(&clipped, &clip)) return;
|
|
498
|
+
if (ui_repair_current_node_paint_with_parent(nodeIdx, &clipped)) return;
|
|
499
|
+
ui_fill_rect_clipped(clipped.x, clipped.y, clipped.w, clipped.h, &clip, ui_parent_clear_color(nodeIdx));
|
|
500
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
501
|
+
if (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
502
|
+
ui_draw_node_decoration_clipped(p, ui_draw_y_for_node(p), &clip);
|
|
503
|
+
}
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (ui_repair_current_node_paint_with_parent(nodeIdx, &r)) return;
|
|
507
|
+
ui_display_fill_rect(r.x, r.y, r.w, r.h, ui_parent_clear_color(nodeIdx));
|
|
508
|
+
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
509
|
+
if (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
510
|
+
int16_t parentDrawY = ui_draw_y_for_node(p);
|
|
511
|
+
if (__ui_nodes[p].borderStyle != 0) {
|
|
512
|
+
UI_COLOR_T bColor = __ui_nodes[p].borderColor ? __ui_nodes[p].borderColor : __ui_nodes[p].fg;
|
|
513
|
+
ui_draw_node_border(p, ui_draw_x_for_node(p), parentDrawY, bColor);
|
|
514
|
+
}
|
|
515
|
+
ui_draw_node_outline(p, ui_draw_x_for_node(p), parentDrawY);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
static inline void ui_set_visible(uint16_t nodeIdx, uint8_t visible) {
|
|
520
|
+
if (nodeIdx >= __ui_node_count) return;
|
|
521
|
+
visible = visible ? 1 : 0;
|
|
522
|
+
if (__ui_nodes[nodeIdx].visible == visible) return;
|
|
523
|
+
|
|
524
|
+
if (!visible) {
|
|
525
|
+
// Clear the whole subtree in one clipped repair. This removes child pixels
|
|
526
|
+
// even when the container itself is transparent, without separate display
|
|
527
|
+
// writes for each descendant.
|
|
528
|
+
UIRect subtreeRect;
|
|
529
|
+
uint8_t hasSubtreeRect = ui_subtree_current_paint_rect(nodeIdx, &subtreeRect);
|
|
530
|
+
ui_clear_subtree_current_paint(nodeIdx);
|
|
531
|
+
int16_t end = __ui_nodes[nodeIdx].subtreeEnd;
|
|
532
|
+
if (end > __ui_node_count) end = __ui_node_count;
|
|
533
|
+
for (int16_t c = end - 1; c >= (int16_t)nodeIdx; c--) {
|
|
534
|
+
if (__ui_nodes[c].screenId != __ui_active_screen) continue;
|
|
535
|
+
__ui_nodes[c].dirty = 0;
|
|
536
|
+
}
|
|
537
|
+
if (hasSubtreeRect) ui_mark_overlapping_higher_layers_dirty_for_rect(nodeIdx, &subtreeRect);
|
|
538
|
+
__ui_nodes[nodeIdx].visible = 0;
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
__ui_nodes[nodeIdx].visible = 1;
|
|
543
|
+
int16_t end = __ui_nodes[nodeIdx].subtreeEnd;
|
|
544
|
+
if (end > __ui_node_count) end = __ui_node_count;
|
|
545
|
+
for (uint16_t c = nodeIdx; c < end; c++) {
|
|
546
|
+
if (__ui_nodes[c].screenId == __ui_active_screen && ui_is_effectively_visible(c)) {
|
|
547
|
+
__ui_nodes[c].dirty = 1;
|
|
548
|
+
ui_mark_overlapping_higher_layers_dirty(c);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
`;
|
|
553
|
+
}
|