@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,342 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 5434-5769).
|
|
2
|
+
// On-screen keyboard: add_key/insert/delete/compute_box/open/close/rect/touch/tick/tap/draw.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitKeyboard() {
|
|
5
|
+
return `
|
|
6
|
+
// ── On-screen keyboard subsystem ───────────────────────────────────────────
|
|
7
|
+
// (UIKey struct, UI_KB_* defines, __ui_kb_box, __ui_kb_visible, __ui_kb_bs_held,
|
|
8
|
+
// __ui_last_touch_x/y are declared earlier near the touch state machine so
|
|
9
|
+
// the touch functions can reference them.)
|
|
10
|
+
|
|
11
|
+
// Populated by the per-keyboard loader function (emitted by the lowering).
|
|
12
|
+
// (__ui_kb_keys is forward-declared earlier near the touch state machine.)
|
|
13
|
+
static uint8_t __ui_kb_keyCount;
|
|
14
|
+
static uint8_t __ui_kb_rows;
|
|
15
|
+
static uint8_t __ui_kb_cols;
|
|
16
|
+
static char __ui_kb_buffer[UI_TEXT_BUF + 1];
|
|
17
|
+
static uint8_t __ui_kb_len;
|
|
18
|
+
static uint8_t __ui_kb_maxlen;
|
|
19
|
+
static uint8_t __ui_kb_shift;
|
|
20
|
+
// __ui_kb_visible, __ui_kb_bs_held, __ui_last_touch_x/y are forward-declared
|
|
21
|
+
// earlier (near the touch state machine) because ui_touch_up references them.
|
|
22
|
+
// __ui_kb_target is also forward-declared there (the UI_HIDE_OSK caret/blink
|
|
23
|
+
// paths in ui_tick and the input draw reference it before this point).
|
|
24
|
+
static uint32_t __ui_kb_bs_repeat; // last auto-repeat deletion time
|
|
25
|
+
// __ui_kb_dirty is forward-declared earlier (near the touch state machine).
|
|
26
|
+
static void (*__ui_kb_onchange)();
|
|
27
|
+
// Dispatch table: one loader per input node. Indexed by input position.
|
|
28
|
+
extern void (*__ui_kb_loaders[])();
|
|
29
|
+
extern const uint16_t __ui_kb_loader_count;
|
|
30
|
+
|
|
31
|
+
static inline void ui_kb_add_key(char ch, uint8_t special, UI_COLOR_T bg, UI_COLOR_T fg, UI_COLOR_T borderColor) {
|
|
32
|
+
if (__ui_kb_keyCount >= UI_KB_MAX) return;
|
|
33
|
+
__ui_kb_keys[__ui_kb_keyCount] = { ch, special };
|
|
34
|
+
__ui_kb_styles[__ui_kb_keyCount] = { bg, fg, borderColor };
|
|
35
|
+
__ui_kb_keyCount++;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Insert a character into the buffer (if space permits).
|
|
39
|
+
static inline void ui_kb_insert(char c) {
|
|
40
|
+
if (__ui_kb_maxlen > 0 && __ui_kb_len >= __ui_kb_maxlen) return;
|
|
41
|
+
if (__ui_kb_len >= UI_TEXT_BUF) return;
|
|
42
|
+
__ui_kb_buffer[__ui_kb_len++] = c;
|
|
43
|
+
__ui_kb_buffer[__ui_kb_len] = 0;
|
|
44
|
+
#if defined(UI_HIDE_OSK)
|
|
45
|
+
// Desktop target: the OSK grid isn't drawn, so the input field itself is the
|
|
46
|
+
// only place the in-progress text appears. Sync the buffer into the target
|
|
47
|
+
// node's textBuffer and mark it dirty so the field repaints on the next tick
|
|
48
|
+
// — without this, typing appears to do nothing until Enter commits at close.
|
|
49
|
+
if (__ui_kb_target >= 0) {
|
|
50
|
+
strncpy(__ui_nodes[__ui_kb_target].textBuffer, __ui_kb_buffer, UI_TEXT_BUF);
|
|
51
|
+
__ui_nodes[__ui_kb_target].textBuffer[UI_TEXT_BUF] = 0;
|
|
52
|
+
ui_mark_dirty((uint16_t)__ui_kb_target);
|
|
53
|
+
}
|
|
54
|
+
#endif
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Delete one character from the buffer.
|
|
58
|
+
static inline void ui_kb_delete() {
|
|
59
|
+
if (__ui_kb_len == 0) return;
|
|
60
|
+
__ui_kb_buffer[--__ui_kb_len] = 0;
|
|
61
|
+
#if defined(UI_HIDE_OSK)
|
|
62
|
+
if (__ui_kb_target >= 0) {
|
|
63
|
+
strncpy(__ui_nodes[__ui_kb_target].textBuffer, __ui_kb_buffer, UI_TEXT_BUF);
|
|
64
|
+
__ui_nodes[__ui_kb_target].textBuffer[UI_TEXT_BUF] = 0;
|
|
65
|
+
ui_mark_dirty((uint16_t)__ui_kb_target);
|
|
66
|
+
}
|
|
67
|
+
#endif
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Compute the keyboard box on open from display dimensions + grid shape.
|
|
71
|
+
// Alpha (wide grid) docks to the bottom 75%; number (narrow grid) centers at 60%.
|
|
72
|
+
// Uses the display profile dimensions if available, else 320×240.
|
|
73
|
+
#ifndef __ui_display_w
|
|
74
|
+
#define __ui_display_w 320
|
|
75
|
+
#endif
|
|
76
|
+
#ifndef __ui_display_h
|
|
77
|
+
#define __ui_display_h 240
|
|
78
|
+
#endif
|
|
79
|
+
static inline void ui_kb_compute_box() {
|
|
80
|
+
uint8_t isNumber = (__ui_kb_cols <= 4);
|
|
81
|
+
uint16_t h = isNumber ? (__ui_display_h * 60 / 100) : (__ui_display_h * 75 / 100);
|
|
82
|
+
__ui_kb_box.w = isNumber ? (__ui_display_w * 50 / 100) : __ui_display_w;
|
|
83
|
+
__ui_kb_box.h = h;
|
|
84
|
+
__ui_kb_box.x = isNumber ? (__ui_display_w - __ui_kb_box.w) / 2 : 0;
|
|
85
|
+
__ui_kb_box.y = __ui_display_h - h;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Open the keyboard for an input node.
|
|
89
|
+
static inline void ui_kb_open(uint16_t nodeIdx, uint8_t inputPosition) {
|
|
90
|
+
__ui_kb_target = nodeIdx;
|
|
91
|
+
strncpy(__ui_kb_buffer, __ui_nodes[nodeIdx].textBuffer, UI_TEXT_BUF);
|
|
92
|
+
__ui_kb_buffer[UI_TEXT_BUF] = 0;
|
|
93
|
+
__ui_kb_len = strlen(__ui_kb_buffer);
|
|
94
|
+
uint16_t ml = __ui_nodes[nodeIdx].maxlen;
|
|
95
|
+
__ui_kb_maxlen = (ml > 0 && ml <= UI_TEXT_BUF) ? (uint8_t)ml : UI_TEXT_BUF;
|
|
96
|
+
__ui_kb_shift = 0;
|
|
97
|
+
__ui_kb_bs_held = 0;
|
|
98
|
+
// Load the key set via the dispatch table.
|
|
99
|
+
if (inputPosition < __ui_kb_loader_count) __ui_kb_loaders[inputPosition]();
|
|
100
|
+
__ui_kb_set_onchange();
|
|
101
|
+
ui_kb_compute_box();
|
|
102
|
+
__ui_kb_visible = 1;
|
|
103
|
+
__ui_kb_dirty = 1; // redraw on the first visible frame
|
|
104
|
+
// Do NOT pre-mark the tree dirty here. While the keyboard is visible the
|
|
105
|
+
// draw pass is skipped (its opaque background covers app nodes), so any
|
|
106
|
+
// dirty flags set now are never consumed/cleared — they survive until close,
|
|
107
|
+
// and the first post-close frame then repaints every dirty node = full-screen
|
|
108
|
+
// flash on SPI TFTs. The close path scopes the repaint to nodes whose paint
|
|
109
|
+
// rect intersects __ui_kb_box, which is the only region that needs restoring.
|
|
110
|
+
#if defined(UI_HIDE_OSK)
|
|
111
|
+
// Desktop target: the OSK isn't drawn, so the draw pass runs normally and the
|
|
112
|
+
// input field must repaint on focus to show the caret BEFORE the first
|
|
113
|
+
// keystroke. (The full-screen-flash concern above doesn't apply — there's no
|
|
114
|
+
// opaque overlay being skipped.) Seed the blink phase so the caret is ON for
|
|
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);
|
|
117
|
+
__ui_kb_blink = 0x20;
|
|
118
|
+
#endif
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Close the keyboard: commit buffer back to the input node.
|
|
122
|
+
static inline void ui_kb_close() {
|
|
123
|
+
if (__ui_kb_target >= 0) {
|
|
124
|
+
strncpy(__ui_nodes[__ui_kb_target].textBuffer, __ui_kb_buffer, UI_TEXT_BUF);
|
|
125
|
+
__ui_nodes[__ui_kb_target].textBuffer[UI_TEXT_BUF] = 0;
|
|
126
|
+
if (__ui_kb_onchange) __ui_kb_onchange();
|
|
127
|
+
}
|
|
128
|
+
// Capture the keyboard box before clearing visibility — it identifies the
|
|
129
|
+
// screen region the opaque overlay covered and that now needs restoring.
|
|
130
|
+
UIRect kbBox = __ui_kb_box;
|
|
131
|
+
__ui_kb_visible = 0;
|
|
132
|
+
__ui_kb_target = -1;
|
|
133
|
+
__ui_kb_bs_held = 0;
|
|
134
|
+
// The <screen> root's paint rect spans the whole display, so it always
|
|
135
|
+
// intersects kbBox. Routing it through ui_mark_dirty would cascade via
|
|
136
|
+
// ui_mark_overlapping_higher_layers_dirty into marking every node on the
|
|
137
|
+
// active screen dirty (its rect overlaps everything), which is the
|
|
138
|
+
// full-screen flash this function exists to avoid. Repaint just the
|
|
139
|
+
// keyboard-box slice of its background directly instead.
|
|
140
|
+
int16_t screenRoot = -1;
|
|
141
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
142
|
+
if (__ui_nodes[i].parent == UI_NO_PARENT && __ui_nodes[i].screenId == __ui_active_screen) {
|
|
143
|
+
screenRoot = (int16_t)i;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (screenRoot >= 0) {
|
|
148
|
+
UI_COLOR_T rootBg = (UI_COLOR_T)(__ui_nodes[screenRoot].hasBg
|
|
149
|
+
? __ui_nodes[screenRoot].bg
|
|
150
|
+
: __ui_nodes[screenRoot].clearColor);
|
|
151
|
+
ui_display_fill_rect(kbBox.x, kbBox.y, kbBox.w, kbBox.h, rootBg);
|
|
152
|
+
}
|
|
153
|
+
// Repaint everything else the keyboard overlay actually overwrote: nodes
|
|
154
|
+
// whose paint rect intersects the keyboard box, plus the edited input
|
|
155
|
+
// itself (its text just changed). ui_mark_dirty handles overlap repair +
|
|
156
|
+
// scroll clipping — safe here since these nodes are bounded in size, unlike
|
|
157
|
+
// the screen root.
|
|
158
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
159
|
+
if ((int16_t)i == screenRoot) continue;
|
|
160
|
+
UIRect r;
|
|
161
|
+
ui_node_current_paint_rect(i, &r);
|
|
162
|
+
if (r.w > 0 && r.h > 0 &&
|
|
163
|
+
ui_rects_intersect(r.x, r.y, r.w, r.h, kbBox.x, kbBox.y, kbBox.w, kbBox.h)) {
|
|
164
|
+
ui_mark_dirty(i);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Compute a key's rect from its index, given the grid + box.
|
|
170
|
+
// The top UI_KB_TEXT_H pixels of the box are reserved for the preview text row;
|
|
171
|
+
// keys fill the area below it.
|
|
172
|
+
static inline void ui_kb_key_rect(uint8_t idx, UIRect* out) {
|
|
173
|
+
uint8_t col = idx % __ui_kb_cols;
|
|
174
|
+
uint8_t row = idx / __ui_kb_cols;
|
|
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;
|
|
178
|
+
out->w = __ui_kb_box.w / __ui_kb_cols;
|
|
179
|
+
out->h = keysH / __ui_kb_rows;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Handle a touch-down inside the keyboard box. tx,ty are display coords.
|
|
183
|
+
// Handle a touch-down inside the keyboard box. Only fires on the initial
|
|
184
|
+
// down edge (tracked by __ui_touch_state in the modal path), NOT every poll.
|
|
185
|
+
// Records WHICH key is under the finger — the same key is activated on release
|
|
186
|
+
// (ui_kb_handle_tap), avoiding mis-targeting from coordinate drift on release.
|
|
187
|
+
static inline void ui_kb_handle_touch(int16_t tx, int16_t ty) {
|
|
188
|
+
__ui_kb_pressed_key = -1;
|
|
189
|
+
for (uint8_t i = 0; i < __ui_kb_keyCount; i++) {
|
|
190
|
+
UIKey k = __ui_kb_keys[i];
|
|
191
|
+
if (k.special == 255) continue; // padding cell, skip
|
|
192
|
+
UIRect r;
|
|
193
|
+
ui_kb_key_rect(i, &r);
|
|
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;
|
|
197
|
+
__ui_kb_dirty = 2; // targeted: redraw just this key's highlight
|
|
198
|
+
// Backspace starts deleting immediately + arms auto-repeat.
|
|
199
|
+
if (k.special == 2) {
|
|
200
|
+
__ui_kb_bs_held = 1;
|
|
201
|
+
__ui_kb_bs_repeat = millis();
|
|
202
|
+
ui_kb_delete();
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Called each frame while the keyboard is visible + a touch is held.
|
|
210
|
+
// Handles ⌫ auto-repeat.
|
|
211
|
+
static inline void ui_kb_tick(uint32_t now) {
|
|
212
|
+
if (!__ui_kb_bs_held) return;
|
|
213
|
+
if (now - __ui_kb_bs_repeat >= UI_KB_REPEAT_MS) {
|
|
214
|
+
ui_kb_delete();
|
|
215
|
+
__ui_kb_bs_repeat = now;
|
|
216
|
+
__ui_kb_repaint_key = __ui_kb_pressed_key; // ⌫ key stays highlighted
|
|
217
|
+
__ui_kb_dirty = 2; // targeted: text row + ⌫ key
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Handle a tap release. Activates the key that was under the finger on
|
|
222
|
+
// touch-down (__ui_kb_pressed_key) — NOT a fresh hit-test, which would
|
|
223
|
+
// mis-target due to coordinate drift on a resistive panel at release.
|
|
224
|
+
static inline void ui_kb_handle_tap(int16_t tx, int16_t ty) {
|
|
225
|
+
(void)tx; (void)ty; // key was recorded on touch-down; no re-hit-test
|
|
226
|
+
if (__ui_kb_pressed_key < 0) return;
|
|
227
|
+
UIKey k = __ui_kb_keys[__ui_kb_pressed_key];
|
|
228
|
+
int8_t releasedKey = __ui_kb_pressed_key;
|
|
229
|
+
__ui_kb_pressed_key = -1; // clear pressed state → highlight reverts
|
|
230
|
+
switch (k.special) {
|
|
231
|
+
case 0: { // char
|
|
232
|
+
char c = k.ch;
|
|
233
|
+
uint8_t wasShift = __ui_kb_shift;
|
|
234
|
+
if (wasShift && c >= 'a' && c <= 'z') c -= 32;
|
|
235
|
+
ui_kb_insert(c);
|
|
236
|
+
__ui_kb_shift = 0; // shift resets after one char
|
|
237
|
+
// Text row changes; if shift was active, repaint shift key too.
|
|
238
|
+
__ui_kb_repaint_key = wasShift ? -1 : releasedKey;
|
|
239
|
+
__ui_kb_dirty = 2;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case 1: // shift toggle — all letter keys change case, full redraw
|
|
243
|
+
__ui_kb_shift = !__ui_kb_shift;
|
|
244
|
+
__ui_kb_dirty = 1;
|
|
245
|
+
break;
|
|
246
|
+
case 2: // backspace: handled on down + repeat; nothing on tap-up
|
|
247
|
+
__ui_kb_repaint_key = releasedKey; // revert highlight
|
|
248
|
+
__ui_kb_dirty = 2;
|
|
249
|
+
break;
|
|
250
|
+
case 3: // OK
|
|
251
|
+
ui_kb_close();
|
|
252
|
+
break;
|
|
253
|
+
case 4: { // page-swap — new key layout, full redraw
|
|
254
|
+
extern void __ui_kb_load_default_alpha();
|
|
255
|
+
extern void __ui_kb_load_default_number();
|
|
256
|
+
if (__ui_kb_cols <= 4) __ui_kb_load_default_alpha();
|
|
257
|
+
else __ui_kb_load_default_number();
|
|
258
|
+
ui_kb_compute_box();
|
|
259
|
+
__ui_kb_dirty = 1;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Draw a single key by index. Shared by the full draw + targeted redraw.
|
|
266
|
+
static inline void ui_kb_draw_key(uint8_t i) {
|
|
267
|
+
UIKey k = __ui_kb_keys[i];
|
|
268
|
+
UIKeyStyle ks = __ui_kb_styles[i];
|
|
269
|
+
UIRect r;
|
|
270
|
+
ui_kb_key_rect(i, &r);
|
|
271
|
+
UI_COLOR_T bg = ks.bg;
|
|
272
|
+
UI_COLOR_T fg = ks.fg;
|
|
273
|
+
UI_COLOR_T border = ks.borderColor;
|
|
274
|
+
// Shift-active highlight: brighten the shift key's background — but only
|
|
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) {
|
|
277
|
+
#if UI_COLOR_DEPTH == 888
|
|
278
|
+
bg = 0xBDEFFF;
|
|
279
|
+
#else
|
|
280
|
+
bg = 0xBDF7;
|
|
281
|
+
#endif
|
|
282
|
+
}
|
|
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; }
|
|
285
|
+
ui_display_fill_rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, bg);
|
|
286
|
+
ui_display_draw_rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, border);
|
|
287
|
+
ui_display_set_text_color(fg, bg);
|
|
288
|
+
ui_display_set_text_size(1);
|
|
289
|
+
// Derive the label string + its length for centering.
|
|
290
|
+
const char* labelStr;
|
|
291
|
+
char single[2];
|
|
292
|
+
switch (k.special) {
|
|
293
|
+
case 1: labelStr = "Aa"; break;
|
|
294
|
+
case 2: labelStr = "DEL"; break;
|
|
295
|
+
case 3: labelStr = "OK"; break;
|
|
296
|
+
case 4: labelStr = __ui_kb_cols <= 4 ? "ABC" : "123"; break;
|
|
297
|
+
default:
|
|
298
|
+
single[0] = (__ui_kb_shift && k.ch >= 'a' && k.ch <= 'z') ? (char)(k.ch - 32) : k.ch;
|
|
299
|
+
single[1] = 0;
|
|
300
|
+
labelStr = single;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
// Center: textW = len * 6px, textH = 8px. Position inside the key rect.
|
|
304
|
+
uint8_t len = strlen(labelStr);
|
|
305
|
+
int16_t textW = (int16_t)len * 6;
|
|
306
|
+
int16_t textH = 8;
|
|
307
|
+
int16_t cx = r.x + (r.w - textW) / 2;
|
|
308
|
+
int16_t cy = r.y + (r.h - textH) / 2;
|
|
309
|
+
if (cx < r.x + 1) cx = r.x + 1; // clamp if label wider than key
|
|
310
|
+
ui_display_set_cursor(cx, cy);
|
|
311
|
+
ui_display_print(labelStr);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Redraw only the text display row (top of keyboard box). Used when a char is
|
|
315
|
+
// inserted/deleted without changing key highlights.
|
|
316
|
+
static inline void ui_kb_draw_text_row() {
|
|
317
|
+
// Clear the text row area (top UI_KB_TEXT_H px of the keyboard box).
|
|
318
|
+
ui_display_fill_rect(__ui_kb_box.x, __ui_kb_box.y, __ui_kb_box.w, UI_KB_TEXT_H, __ui_kb_bg);
|
|
319
|
+
ui_display_set_cursor(__ui_kb_box.x + 4, __ui_kb_box.y + 4);
|
|
320
|
+
#if UI_COLOR_DEPTH == 888
|
|
321
|
+
ui_display_set_text_color(0xFFFFFF, 0x000000);
|
|
322
|
+
#else
|
|
323
|
+
ui_display_set_text_color(0xFFFF, 0x0000);
|
|
324
|
+
#endif
|
|
325
|
+
ui_display_set_text_size(2);
|
|
326
|
+
ui_display_print(__ui_kb_buffer);
|
|
327
|
+
ui_display_print("_"); // cursor
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Draw the full keyboard overlay (background + text row + all keys).
|
|
331
|
+
static inline void ui_kb_draw() {
|
|
332
|
+
// Opaque background over the keyboard box.
|
|
333
|
+
ui_display_fill_rect(__ui_kb_box.x, __ui_kb_box.y, __ui_kb_box.w, __ui_kb_box.h, __ui_kb_bg);
|
|
334
|
+
ui_kb_draw_text_row();
|
|
335
|
+
// Keys: one rect per key, label centered.
|
|
336
|
+
for (uint8_t i = 0; i < __ui_kb_keyCount; i++) {
|
|
337
|
+
if (__ui_kb_keys[i].special == 255) continue; // padding cell, skip
|
|
338
|
+
ui_kb_draw_key(i);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
`;
|
|
342
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function emitNodeDecoration(): string;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 3704-3909).
|
|
2
|
+
// Gradient, shadow, round-rect, border, outline drawing.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitNodeDecoration() {
|
|
5
|
+
return `
|
|
6
|
+
static inline void ui_draw_gradient_fill(uint16_t i, int16_t drawY) {
|
|
7
|
+
int16_t bx = __ui_nodes[i].box.x;
|
|
8
|
+
int16_t by = drawY;
|
|
9
|
+
int16_t bw = __ui_nodes[i].box.w;
|
|
10
|
+
int16_t bh = __ui_nodes[i].box.h;
|
|
11
|
+
int16_t clipX = bx;
|
|
12
|
+
int16_t clipY = by;
|
|
13
|
+
int16_t clipW = bw;
|
|
14
|
+
int16_t clipH = bh;
|
|
15
|
+
if (!ui_clip_rect_to_display_target(&clipX, &clipY, &clipW, &clipH)) return;
|
|
16
|
+
UI_COLOR_T c1 = __ui_nodes[i].gradientColor1;
|
|
17
|
+
UI_COLOR_T c2 = __ui_nodes[i].gradientColor2;
|
|
18
|
+
uint8_t dir = __ui_nodes[i].gradientEnabled; // 1=vertical, 2=horizontal
|
|
19
|
+
if (dir == 1) {
|
|
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);
|
|
23
|
+
for (int16_t y = yStart; y < yEnd; y++) {
|
|
24
|
+
uint8_t op = (uint8_t)((uint16_t)y * 100 / (bh > 1 ? bh - 1 : 1));
|
|
25
|
+
uint32_t col = ui_blend(c1, c2, op);
|
|
26
|
+
ui_display_draw_fast_hline(clipX, by + y, clipW, col);
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
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);
|
|
32
|
+
for (int16_t x = xStart; x < xEnd; x++) {
|
|
33
|
+
uint8_t op = (uint8_t)((uint16_t)x * 100 / (bw > 1 ? bw - 1 : 1));
|
|
34
|
+
uint32_t col = ui_blend(c1, c2, op);
|
|
35
|
+
ui_display_draw_fast_vline(bx + x, clipY, clipH, col);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static inline void ui_draw_shadow(uint16_t i, int16_t drawY, uint8_t insetOnly) {
|
|
41
|
+
if (__ui_nodes[i].shadowCount == 0) return;
|
|
42
|
+
int16_t bx = __ui_nodes[i].box.x;
|
|
43
|
+
int16_t by = drawY;
|
|
44
|
+
int16_t bw = __ui_nodes[i].box.w;
|
|
45
|
+
int16_t bh = __ui_nodes[i].box.h;
|
|
46
|
+
UI_COLOR_T clearCol = __ui_nodes[i].clearColor;
|
|
47
|
+
uint8_t radius = __ui_nodes[i].borderRadius;
|
|
48
|
+
|
|
49
|
+
for (uint8_t s = 0; s < __ui_nodes[i].shadowCount && s < 4; s++) {
|
|
50
|
+
if (s >= __ui_nodes[i].shadowCount) continue; // use count, not color check (0 is valid black)
|
|
51
|
+
UI_COLOR_T shadowCol = __ui_nodes[i].shadowColor[s];
|
|
52
|
+
int8_t ox = __ui_nodes[i].shadowOffsetX[s];
|
|
53
|
+
int8_t oy = __ui_nodes[i].shadowOffsetY[s];
|
|
54
|
+
uint8_t rawBlur = __ui_nodes[i].shadowBlur[s];
|
|
55
|
+
uint8_t blur = rawBlur;
|
|
56
|
+
if (blur == 0) blur = 1; // at least 1 pass for a hard shadow
|
|
57
|
+
uint8_t baseAlpha = __ui_nodes[i].shadowAlpha[s];
|
|
58
|
+
uint8_t inset = __ui_nodes[i].shadowInset[s];
|
|
59
|
+
if (insetOnly && !inset) continue;
|
|
60
|
+
if (!insetOnly && inset) continue;
|
|
61
|
+
|
|
62
|
+
if (inset && rawBlur == 0) {
|
|
63
|
+
UI_COLOR_T insetBg = __ui_nodes[i].hasBg ? __ui_nodes[i].bg : __ui_nodes[i].clearColor;
|
|
64
|
+
uint32_t col = ui_blend(shadowCol, insetBg, baseAlpha);
|
|
65
|
+
if (oy > 0) {
|
|
66
|
+
ui_display_fill_rect(bx, by, bw, oy, col);
|
|
67
|
+
} else if (oy < 0) {
|
|
68
|
+
ui_display_fill_rect(bx, by + bh + oy, bw, -oy, col);
|
|
69
|
+
}
|
|
70
|
+
if (ox > 0) {
|
|
71
|
+
ui_display_fill_rect(bx, by, ox, bh, col);
|
|
72
|
+
} else if (ox < 0) {
|
|
73
|
+
ui_display_fill_rect(bx + bw + ox, by, -ox, bh, col);
|
|
74
|
+
}
|
|
75
|
+
if (ox == 0 && oy == 0) {
|
|
76
|
+
ui_display_draw_rect(bx, by, bw, bh, col);
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (int8_t pass = blur; pass >= 1; pass--) {
|
|
82
|
+
uint8_t opacity = (uint8_t)((uint16_t)baseAlpha / (pass + 1));
|
|
83
|
+
uint32_t col = ui_blend(shadowCol, inset ? (__ui_nodes[i].hasBg ? __ui_nodes[i].bg : clearCol) : clearCol, opacity);
|
|
84
|
+
if (inset) {
|
|
85
|
+
// Inset: draw inside the element, shrinking inward by pass.
|
|
86
|
+
int16_t ix = bx + pass;
|
|
87
|
+
int16_t iy = by + pass;
|
|
88
|
+
int16_t iw = bw - 2 * pass;
|
|
89
|
+
int16_t ih = bh - 2 * pass;
|
|
90
|
+
if (iw <= 0 || ih <= 0) continue;
|
|
91
|
+
// Offset the inset by the shadow's x/y (e.g. top highlight).
|
|
92
|
+
iy += oy;
|
|
93
|
+
ix += ox;
|
|
94
|
+
// Draw only the edge ring (4 thin rects), not a full fill — the
|
|
95
|
+
// element's own background will cover the center anyway.
|
|
96
|
+
ui_display_fill_rect(ix, iy, iw, 1, col); // top edge
|
|
97
|
+
ui_display_fill_rect(ix, iy + ih - 1, iw, 1, col); // bottom edge
|
|
98
|
+
ui_display_fill_rect(ix, iy, 1, ih, col); // left edge
|
|
99
|
+
ui_display_fill_rect(ix + iw - 1, iy, 1, ih, col); // right edge
|
|
100
|
+
} else {
|
|
101
|
+
// Outset: expand outward from box + offset.
|
|
102
|
+
int16_t sx = bx + ox - pass;
|
|
103
|
+
int16_t sy = by + oy - pass;
|
|
104
|
+
int16_t sw = bw + 2 * pass;
|
|
105
|
+
int16_t sh_ = bh + 2 * pass;
|
|
106
|
+
if (radius > 0) {
|
|
107
|
+
uint8_t r = radius + (uint8_t)pass;
|
|
108
|
+
if (r > sw / 2) r = sw / 2;
|
|
109
|
+
if (r > sh_ / 2) r = sh_ / 2;
|
|
110
|
+
ui_display_fill_round_rect(sx, sy, sw, sh_, r, col);
|
|
111
|
+
} else {
|
|
112
|
+
ui_display_fill_rect(sx, sy, sw, sh_, col);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static inline void ui_draw_closed_round_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t radius, UI_COLOR_T color) {
|
|
120
|
+
if (w <= 0 || h <= 0) return;
|
|
121
|
+
uint8_t r = radius;
|
|
122
|
+
if (r > w / 2) r = w / 2;
|
|
123
|
+
if (r > h / 2) r = h / 2;
|
|
124
|
+
if (r == 0) {
|
|
125
|
+
ui_display_draw_rect(x, y, w, h, color);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
ui_display_draw_round_rect(x, y, w, h, r, color);
|
|
129
|
+
// Adafruit_GFX's circle helper omits the cardinal tangent pixels. Fill them
|
|
130
|
+
// so straight edges and corner arcs meet without visible pinholes.
|
|
131
|
+
ui_display_draw_pixel(x + r, y, color);
|
|
132
|
+
ui_display_draw_pixel(x + w - r - 1, y, color);
|
|
133
|
+
ui_display_draw_pixel(x + r, y + h - 1, color);
|
|
134
|
+
ui_display_draw_pixel(x + w - r - 1, y + h - 1, color);
|
|
135
|
+
ui_display_draw_pixel(x, y + r, color);
|
|
136
|
+
ui_display_draw_pixel(x + w - 1, y + r, color);
|
|
137
|
+
ui_display_draw_pixel(x, y + h - r - 1, color);
|
|
138
|
+
ui_display_draw_pixel(x + w - 1, y + h - r - 1, color);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static inline void ui_draw_rect_outline(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t radius, uint8_t style, uint8_t width, UI_COLOR_T color) {
|
|
142
|
+
if (style == 0 || width == 0 || w <= 0 || h <= 0) return;
|
|
143
|
+
for (uint8_t b = 0; b < width; b++) {
|
|
144
|
+
int16_t rx = x + b;
|
|
145
|
+
int16_t ry = y + b;
|
|
146
|
+
int16_t rw = w - 2 * b;
|
|
147
|
+
int16_t rh = h - 2 * b;
|
|
148
|
+
if (rw <= 0 || rh <= 0) return;
|
|
149
|
+
uint8_t r = radius > b ? radius - b : 0;
|
|
150
|
+
if (style == 1) {
|
|
151
|
+
if (r > 0) ui_draw_closed_round_rect(rx, ry, rw, rh, r, color);
|
|
152
|
+
else ui_display_draw_rect(rx, ry, rw, rh, color);
|
|
153
|
+
} else {
|
|
154
|
+
for (int16_t dx = 0; dx < rw; dx += 8) {
|
|
155
|
+
int16_t seg = (dx + 4 <= rw) ? 4 : (rw - dx);
|
|
156
|
+
if (seg > 0) {
|
|
157
|
+
ui_display_draw_fast_hline(rx + dx, ry, seg, color);
|
|
158
|
+
ui_display_draw_fast_hline(rx + dx, ry + rh - 1, seg, color);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (int16_t dy = 0; dy < rh; dy += 8) {
|
|
162
|
+
int16_t seg = (dy + 4 <= rh) ? 4 : (rh - dy);
|
|
163
|
+
if (seg > 0) {
|
|
164
|
+
ui_display_draw_fast_vline(rx, ry + dy, seg, color);
|
|
165
|
+
ui_display_draw_fast_vline(rx + rw - 1, ry + dy, seg, color);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static inline void ui_draw_node_border(uint16_t i, int16_t drawX, int16_t drawY, UI_COLOR_T color) {
|
|
173
|
+
// Per-side borders: when any side's width differs from the uniform width,
|
|
174
|
+
// draw each side as an independent filled rect. The uniform path (one
|
|
175
|
+
// ui_draw_rect_outline call) is the common case and stays unchanged.
|
|
176
|
+
if (__ui_nodes[i].hasPerSideBorder) {
|
|
177
|
+
int16_t w = __ui_nodes[i].box.w;
|
|
178
|
+
int16_t h = __ui_nodes[i].box.h;
|
|
179
|
+
uint8_t st = __ui_nodes[i].borderStyle;
|
|
180
|
+
// Top edge
|
|
181
|
+
if (__ui_nodes[i].borderTopWidth > 0) {
|
|
182
|
+
ui_display_fill_rect(drawX, drawY, w, __ui_nodes[i].borderTopWidth, color);
|
|
183
|
+
}
|
|
184
|
+
// Bottom edge
|
|
185
|
+
if (__ui_nodes[i].borderBottomWidth > 0) {
|
|
186
|
+
ui_display_fill_rect(drawX, drawY + h - __ui_nodes[i].borderBottomWidth, w, __ui_nodes[i].borderBottomWidth, color);
|
|
187
|
+
}
|
|
188
|
+
// Left edge (between top and bottom borders)
|
|
189
|
+
if (__ui_nodes[i].borderLeftWidth > 0) {
|
|
190
|
+
ui_display_fill_rect(drawX, drawY + __ui_nodes[i].borderTopWidth, __ui_nodes[i].borderLeftWidth,
|
|
191
|
+
h - __ui_nodes[i].borderTopWidth - __ui_nodes[i].borderBottomWidth, color);
|
|
192
|
+
}
|
|
193
|
+
// Right edge
|
|
194
|
+
if (__ui_nodes[i].borderRightWidth > 0) {
|
|
195
|
+
ui_display_fill_rect(drawX + w - __ui_nodes[i].borderRightWidth, drawY + __ui_nodes[i].borderTopWidth,
|
|
196
|
+
__ui_nodes[i].borderRightWidth, h - __ui_nodes[i].borderTopWidth - __ui_nodes[i].borderBottomWidth, color);
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
ui_draw_rect_outline(drawX, drawY, __ui_nodes[i].box.w, __ui_nodes[i].box.h,
|
|
201
|
+
__ui_nodes[i].borderRadius, __ui_nodes[i].borderStyle, __ui_nodes[i].borderWidth, color);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static inline void ui_draw_node_outline(uint16_t i, int16_t drawX, int16_t drawY) {
|
|
205
|
+
if (__ui_nodes[i].outlineStyle == 0 || __ui_nodes[i].outlineWidth == 0) return;
|
|
206
|
+
uint8_t w = __ui_nodes[i].outlineWidth;
|
|
207
|
+
ui_draw_rect_outline(drawX - w, drawY - w,
|
|
208
|
+
__ui_nodes[i].box.w + 2 * w, __ui_nodes[i].box.h + 2 * w,
|
|
209
|
+
__ui_nodes[i].borderRadius + w, __ui_nodes[i].outlineStyle, w, __ui_nodes[i].outlineColor);
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function emitPaintOrderCoords(): string;
|