imxc 0.2.0 → 0.3.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/dist/compile.d.ts +10 -0
- package/dist/compile.js +169 -0
- package/dist/components.js +249 -1
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.js +23 -0
- package/dist/emitter.d.ts +3 -3
- package/dist/emitter.js +814 -84
- package/dist/index.js +39 -111
- package/dist/init.d.ts +1 -0
- package/dist/init.js +202 -17
- package/dist/ir.d.ts +157 -3
- package/dist/lowering.d.ts +1 -0
- package/dist/lowering.js +400 -63
- package/dist/validator.js +3 -5
- package/dist/watch.d.ts +4 -0
- package/dist/watch.js +66 -0
- package/package.json +2 -2
package/dist/emitter.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
const INDENT = ' ';
|
|
2
|
+
let currentCompName = '';
|
|
3
|
+
function emitLocComment(loc, tag, lines, indent) {
|
|
4
|
+
if (loc) {
|
|
5
|
+
lines.push(`${indent}// ${loc.file}:${loc.line} <${tag}>`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
2
8
|
function cppType(t) {
|
|
3
9
|
switch (t) {
|
|
4
10
|
case 'int': return 'int';
|
|
@@ -28,15 +34,76 @@ function asCharPtr(expr) {
|
|
|
28
34
|
// Expression — assume std::string, add .c_str()
|
|
29
35
|
return `${expr}.c_str()`;
|
|
30
36
|
}
|
|
37
|
+
function emitImVec4(arrayStr) {
|
|
38
|
+
const parts = arrayStr.split(',').map(s => {
|
|
39
|
+
const v = s.trim();
|
|
40
|
+
return v.includes('.') ? `${v}f` : `${v}.0f`;
|
|
41
|
+
});
|
|
42
|
+
return `ImVec4(${parts.join(', ')})`;
|
|
43
|
+
}
|
|
44
|
+
function emitImVec2(arrayStr) {
|
|
45
|
+
const parts = arrayStr.split(',').map(s => {
|
|
46
|
+
const v = s.trim();
|
|
47
|
+
return v.includes('.') ? `${v}f` : `${v}.0f`;
|
|
48
|
+
});
|
|
49
|
+
return `ImVec2(${parts.join(', ')})`;
|
|
50
|
+
}
|
|
51
|
+
function emitFloat(val) {
|
|
52
|
+
return val.includes('.') ? `${val}F` : `${val}.0F`;
|
|
53
|
+
}
|
|
54
|
+
function findDockLayout(nodes) {
|
|
55
|
+
for (const node of nodes) {
|
|
56
|
+
if (node.kind === 'dock_layout')
|
|
57
|
+
return node;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function emitDockSetupFunction(layout, compName, lines) {
|
|
62
|
+
lines.push(`void ${compName}_setup_dock_layout(ImGuiID dockspace_id) {`);
|
|
63
|
+
lines.push(`${INDENT}ImGui::DockBuilderRemoveNode(dockspace_id);`);
|
|
64
|
+
lines.push(`${INDENT}ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_None);`);
|
|
65
|
+
lines.push(`${INDENT}ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->WorkSize);`);
|
|
66
|
+
lines.push('');
|
|
67
|
+
let counter = 0;
|
|
68
|
+
function emitDockNode(node, parentVar) {
|
|
69
|
+
if (node.kind === 'dock_panel') {
|
|
70
|
+
for (const title of node.windows) {
|
|
71
|
+
lines.push(`${INDENT}ImGui::DockBuilderDockWindow(${title}, ${parentVar});`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const dirRaw = node.direction.replace(/"/g, '');
|
|
76
|
+
const dir = dirRaw === 'horizontal' ? 'ImGuiDir_Left' : 'ImGuiDir_Up';
|
|
77
|
+
const sizeF = emitFloat(node.size);
|
|
78
|
+
const firstVar = `dock_${counter++}`;
|
|
79
|
+
const secondVar = `dock_${counter++}`;
|
|
80
|
+
lines.push(`${INDENT}ImGuiID ${firstVar}, ${secondVar};`);
|
|
81
|
+
lines.push(`${INDENT}ImGui::DockBuilderSplitNode(${parentVar}, ${dir}, ${sizeF}, &${firstVar}, &${secondVar});`);
|
|
82
|
+
if (node.children.length >= 1)
|
|
83
|
+
emitDockNode(node.children[0], firstVar);
|
|
84
|
+
if (node.children.length >= 2)
|
|
85
|
+
emitDockNode(node.children[1], secondVar);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const child of layout.children) {
|
|
89
|
+
emitDockNode(child, 'dockspace_id');
|
|
90
|
+
}
|
|
91
|
+
lines.push(`${INDENT}ImGui::DockBuilderFinish(dockspace_id);`);
|
|
92
|
+
lines.push('}');
|
|
93
|
+
lines.push('');
|
|
94
|
+
}
|
|
31
95
|
/**
|
|
32
96
|
* Emit a .gen.h header for a component that has props.
|
|
33
97
|
* Contains the props struct and function forward declaration.
|
|
34
98
|
*/
|
|
35
|
-
export function emitComponentHeader(comp) {
|
|
99
|
+
export function emitComponentHeader(comp, sourceFile) {
|
|
36
100
|
const lines = [];
|
|
101
|
+
if (sourceFile) {
|
|
102
|
+
lines.push(`// Generated from ${sourceFile} by imxc`);
|
|
103
|
+
}
|
|
37
104
|
lines.push('#pragma once');
|
|
38
|
-
lines.push('#include <
|
|
39
|
-
lines.push('#include <
|
|
105
|
+
lines.push('#include <imx/runtime.h>');
|
|
106
|
+
lines.push('#include <imx/renderer.h>');
|
|
40
107
|
lines.push('#include <functional>');
|
|
41
108
|
lines.push('#include <string>');
|
|
42
109
|
lines.push('');
|
|
@@ -48,11 +115,11 @@ export function emitComponentHeader(comp) {
|
|
|
48
115
|
lines.push('};');
|
|
49
116
|
lines.push('');
|
|
50
117
|
// Function forward declaration
|
|
51
|
-
lines.push(`void ${comp.name}_render(
|
|
118
|
+
lines.push(`void ${comp.name}_render(imx::RenderContext& ctx, const ${comp.name}Props& props);`);
|
|
52
119
|
lines.push('');
|
|
53
120
|
return lines.join('\n');
|
|
54
121
|
}
|
|
55
|
-
export function emitComponent(comp, imports) {
|
|
122
|
+
export function emitComponent(comp, imports, sourceFile) {
|
|
56
123
|
const lines = [];
|
|
57
124
|
// Reset counters for each component
|
|
58
125
|
styleCounter = 0;
|
|
@@ -60,20 +127,34 @@ export function emitComponent(comp, imports) {
|
|
|
60
127
|
checkboxCounter = 0;
|
|
61
128
|
comboCounter = 0;
|
|
62
129
|
listBoxCounter = 0;
|
|
130
|
+
nativeWidgetCounter = 0;
|
|
131
|
+
plotCounter = 0;
|
|
132
|
+
dragDropSourceStack.length = 0;
|
|
133
|
+
dragDropTargetStack.length = 0;
|
|
134
|
+
currentCompName = comp.name;
|
|
63
135
|
const hasProps = comp.params.length > 0;
|
|
64
136
|
const hasColorType = comp.stateSlots.some(s => s.type === 'color');
|
|
137
|
+
// File banner
|
|
138
|
+
if (sourceFile) {
|
|
139
|
+
lines.push(`// Generated from ${sourceFile} by imxc`);
|
|
140
|
+
}
|
|
65
141
|
if (hasProps) {
|
|
66
142
|
// Component with props: include its own header instead of redeclaring struct
|
|
67
143
|
lines.push(`#include "${comp.name}.gen.h"`);
|
|
68
144
|
if (hasColorType) {
|
|
69
145
|
lines.push('#include <array>');
|
|
70
146
|
}
|
|
147
|
+
// Embed image includes
|
|
148
|
+
const embedKeysProps = collectEmbedKeys(comp.body);
|
|
149
|
+
for (const key of embedKeysProps) {
|
|
150
|
+
lines.push(`#include "${key}.embed.h"`);
|
|
151
|
+
}
|
|
71
152
|
lines.push('');
|
|
72
153
|
}
|
|
73
154
|
else {
|
|
74
155
|
// No props: standard headers
|
|
75
|
-
lines.push('#include <
|
|
76
|
-
lines.push('#include <
|
|
156
|
+
lines.push('#include <imx/runtime.h>');
|
|
157
|
+
lines.push('#include <imx/renderer.h>');
|
|
77
158
|
if (hasColorType) {
|
|
78
159
|
lines.push('#include <array>');
|
|
79
160
|
}
|
|
@@ -83,11 +164,29 @@ export function emitComponent(comp, imports) {
|
|
|
83
164
|
lines.push(`#include "${imp.headerFile}"`);
|
|
84
165
|
}
|
|
85
166
|
}
|
|
167
|
+
// Embed image includes
|
|
168
|
+
const embedKeys = collectEmbedKeys(comp.body);
|
|
169
|
+
for (const key of embedKeys) {
|
|
170
|
+
lines.push(`#include "${key}.embed.h"`);
|
|
171
|
+
}
|
|
172
|
+
lines.push('');
|
|
173
|
+
}
|
|
174
|
+
const dockLayout = findDockLayout(comp.body);
|
|
175
|
+
if (dockLayout) {
|
|
176
|
+
lines.push('#include <imgui_internal.h>');
|
|
177
|
+
lines.push('');
|
|
178
|
+
lines.push('static bool g_layout_applied = false;');
|
|
179
|
+
lines.push('static bool g_reset_layout = false;');
|
|
180
|
+
lines.push('');
|
|
181
|
+
lines.push('void imx_reset_layout() {');
|
|
182
|
+
lines.push(`${INDENT}g_reset_layout = true;`);
|
|
183
|
+
lines.push('}');
|
|
86
184
|
lines.push('');
|
|
185
|
+
emitDockSetupFunction(dockLayout, comp.name, lines);
|
|
87
186
|
}
|
|
88
187
|
// Function signature
|
|
89
188
|
const propsArg = hasProps ? `, const ${comp.name}Props& props` : '';
|
|
90
|
-
lines.push(`void ${comp.name}_render(
|
|
189
|
+
lines.push(`void ${comp.name}_render(imx::RenderContext& ctx${propsArg}) {`);
|
|
91
190
|
// State declarations
|
|
92
191
|
for (const slot of comp.stateSlots) {
|
|
93
192
|
const initVal = slot.type === 'string'
|
|
@@ -106,13 +205,16 @@ export function emitComponent(comp, imports) {
|
|
|
106
205
|
lines.push('');
|
|
107
206
|
return lines.join('\n');
|
|
108
207
|
}
|
|
109
|
-
export function emitRoot(rootName, stateCount, bufferCount) {
|
|
208
|
+
export function emitRoot(rootName, stateCount, bufferCount, sourceFile) {
|
|
110
209
|
const lines = [];
|
|
111
|
-
|
|
210
|
+
if (sourceFile) {
|
|
211
|
+
lines.push(`// Generated from ${sourceFile} by imxc`);
|
|
212
|
+
}
|
|
213
|
+
lines.push('#include <imx/runtime.h>');
|
|
112
214
|
lines.push('');
|
|
113
|
-
lines.push(`void ${rootName}_render(
|
|
215
|
+
lines.push(`void ${rootName}_render(imx::RenderContext& ctx);`);
|
|
114
216
|
lines.push('');
|
|
115
|
-
lines.push('namespace
|
|
217
|
+
lines.push('namespace imx {');
|
|
116
218
|
lines.push('void render_root(Runtime& runtime) {');
|
|
117
219
|
lines.push(`${INDENT}auto& ctx = runtime.begin_frame();`);
|
|
118
220
|
lines.push(`${INDENT}ctx.begin_instance("${rootName}", 0, ${stateCount}, ${bufferCount});`);
|
|
@@ -120,7 +222,7 @@ export function emitRoot(rootName, stateCount, bufferCount) {
|
|
|
120
222
|
lines.push(`${INDENT}ctx.end_instance();`);
|
|
121
223
|
lines.push(`${INDENT}runtime.end_frame();`);
|
|
122
224
|
lines.push('}');
|
|
123
|
-
lines.push('} // namespace
|
|
225
|
+
lines.push('} // namespace imx');
|
|
124
226
|
lines.push('');
|
|
125
227
|
return lines.join('\n');
|
|
126
228
|
}
|
|
@@ -151,17 +253,19 @@ function emitNode(node, lines, depth) {
|
|
|
151
253
|
emitCheckbox(node, lines, indent);
|
|
152
254
|
break;
|
|
153
255
|
case 'separator':
|
|
154
|
-
lines.push(`${indent}
|
|
256
|
+
lines.push(`${indent}imx::renderer::separator();`);
|
|
155
257
|
break;
|
|
156
258
|
case 'begin_popup':
|
|
157
|
-
|
|
259
|
+
emitLocComment(node.loc, 'Popup', lines, indent);
|
|
260
|
+
lines.push(`${indent}if (imx::renderer::begin_popup(${node.id})) {`);
|
|
158
261
|
break;
|
|
159
262
|
case 'end_popup':
|
|
160
|
-
lines.push(`${indent}
|
|
263
|
+
lines.push(`${indent}imx::renderer::end_popup();`);
|
|
161
264
|
lines.push(`${indent}}`);
|
|
162
265
|
break;
|
|
163
266
|
case 'open_popup':
|
|
164
|
-
|
|
267
|
+
emitLocComment(node.loc, 'OpenPopup', lines, indent);
|
|
268
|
+
lines.push(`${indent}imx::renderer::open_popup(${node.id});`);
|
|
165
269
|
break;
|
|
166
270
|
case 'conditional':
|
|
167
271
|
emitConditional(node, lines, indent, depth);
|
|
@@ -208,6 +312,64 @@ function emitNode(node, lines, depth) {
|
|
|
208
312
|
case 'tooltip':
|
|
209
313
|
emitTooltip(node, lines, indent);
|
|
210
314
|
break;
|
|
315
|
+
case 'bullet_text':
|
|
316
|
+
emitBulletText(node, lines, indent);
|
|
317
|
+
break;
|
|
318
|
+
case 'label_text':
|
|
319
|
+
emitLabelText(node, lines, indent);
|
|
320
|
+
break;
|
|
321
|
+
case 'selectable':
|
|
322
|
+
emitSelectable(node, lines, indent);
|
|
323
|
+
break;
|
|
324
|
+
case 'radio':
|
|
325
|
+
emitRadio(node, lines, indent);
|
|
326
|
+
break;
|
|
327
|
+
case 'input_text_multiline':
|
|
328
|
+
emitInputTextMultiline(node, lines, indent);
|
|
329
|
+
break;
|
|
330
|
+
case 'color_picker':
|
|
331
|
+
emitColorPicker(node, lines, indent);
|
|
332
|
+
break;
|
|
333
|
+
case 'plot_lines':
|
|
334
|
+
emitPlotLines(node, lines, indent);
|
|
335
|
+
break;
|
|
336
|
+
case 'plot_histogram':
|
|
337
|
+
emitPlotHistogram(node, lines, indent);
|
|
338
|
+
break;
|
|
339
|
+
case 'image':
|
|
340
|
+
emitImage(node, lines, indent);
|
|
341
|
+
break;
|
|
342
|
+
case 'draw_line':
|
|
343
|
+
emitDrawLine(node, lines, indent);
|
|
344
|
+
break;
|
|
345
|
+
case 'draw_rect':
|
|
346
|
+
emitDrawRect(node, lines, indent);
|
|
347
|
+
break;
|
|
348
|
+
case 'draw_circle':
|
|
349
|
+
emitDrawCircle(node, lines, indent);
|
|
350
|
+
break;
|
|
351
|
+
case 'draw_text':
|
|
352
|
+
emitDrawText(node, lines, indent);
|
|
353
|
+
break;
|
|
354
|
+
case 'native_widget':
|
|
355
|
+
emitNativeWidget(node, lines, indent);
|
|
356
|
+
break;
|
|
357
|
+
case 'dock_layout': {
|
|
358
|
+
lines.push(`${indent}{`);
|
|
359
|
+
lines.push(`${indent}${INDENT}ImGuiID dock_id = ImGui::GetID("MainDockSpace");`);
|
|
360
|
+
lines.push(`${indent}${INDENT}if (g_reset_layout) {`);
|
|
361
|
+
lines.push(`${indent}${INDENT}${INDENT}${currentCompName}_setup_dock_layout(dock_id);`);
|
|
362
|
+
lines.push(`${indent}${INDENT}${INDENT}g_reset_layout = false;`);
|
|
363
|
+
lines.push(`${indent}${INDENT}} else if (!g_layout_applied) {`);
|
|
364
|
+
lines.push(`${indent}${INDENT}${INDENT}g_layout_applied = true;`);
|
|
365
|
+
lines.push(`${indent}${INDENT}${INDENT}ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_id);`);
|
|
366
|
+
lines.push(`${indent}${INDENT}${INDENT}if (node == nullptr || !node->IsSplitNode()) {`);
|
|
367
|
+
lines.push(`${indent}${INDENT}${INDENT}${INDENT}${currentCompName}_setup_dock_layout(dock_id);`);
|
|
368
|
+
lines.push(`${indent}${INDENT}${INDENT}}`);
|
|
369
|
+
lines.push(`${indent}${INDENT}}`);
|
|
370
|
+
lines.push(`${indent}}`);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
211
373
|
}
|
|
212
374
|
}
|
|
213
375
|
let styleCounter = 0;
|
|
@@ -215,6 +377,91 @@ let customComponentCounter = 0;
|
|
|
215
377
|
let checkboxCounter = 0;
|
|
216
378
|
let comboCounter = 0;
|
|
217
379
|
let listBoxCounter = 0;
|
|
380
|
+
let nativeWidgetCounter = 0;
|
|
381
|
+
let plotCounter = 0;
|
|
382
|
+
const windowOpenStack = []; // tracks if begin_window used open prop
|
|
383
|
+
const modalOnCloseStack = []; // tracks modal onClose expressions
|
|
384
|
+
const dragDropSourceStack = [];
|
|
385
|
+
const dragDropTargetStack = [];
|
|
386
|
+
/**
|
|
387
|
+
* Build a Style variable from a raw style expression string for self-closing components.
|
|
388
|
+
* Handles JS-like object literals: { width: 300, height: 100 } -> imx::Style with assignments.
|
|
389
|
+
* Returns the variable name, or null if no style.
|
|
390
|
+
*/
|
|
391
|
+
/**
|
|
392
|
+
* Split a comma-separated list of key:value pairs in a style object literal,
|
|
393
|
+
* respecting brackets so that array values like [0.1, 0.1, 0.1, 1.0] are not split.
|
|
394
|
+
*/
|
|
395
|
+
function splitStylePairs(inner) {
|
|
396
|
+
const pairs = [];
|
|
397
|
+
let depth = 0;
|
|
398
|
+
let start = 0;
|
|
399
|
+
for (let i = 0; i < inner.length; i++) {
|
|
400
|
+
const ch = inner[i];
|
|
401
|
+
if (ch === '[' || ch === '(')
|
|
402
|
+
depth++;
|
|
403
|
+
else if (ch === ']' || ch === ')')
|
|
404
|
+
depth--;
|
|
405
|
+
else if (ch === ',' && depth === 0) {
|
|
406
|
+
const piece = inner.substring(start, i).trim();
|
|
407
|
+
if (piece)
|
|
408
|
+
pairs.push(piece);
|
|
409
|
+
start = i + 1;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const last = inner.substring(start).trim();
|
|
413
|
+
if (last)
|
|
414
|
+
pairs.push(last);
|
|
415
|
+
return pairs;
|
|
416
|
+
}
|
|
417
|
+
function buildStyleVar(styleExpr, indent, lines) {
|
|
418
|
+
if (!styleExpr)
|
|
419
|
+
return null;
|
|
420
|
+
// Check if it looks like an object literal: { key: value, ... }
|
|
421
|
+
const trimmed = styleExpr.trim();
|
|
422
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
423
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
424
|
+
if (!inner)
|
|
425
|
+
return null;
|
|
426
|
+
const varName = `style_${styleCounter++}`;
|
|
427
|
+
lines.push(`${indent}imx::Style ${varName};`);
|
|
428
|
+
// Parse key: value pairs (bracket-aware to handle array values)
|
|
429
|
+
const pairs = splitStylePairs(inner);
|
|
430
|
+
for (const pair of pairs) {
|
|
431
|
+
const colonIdx = pair.indexOf(':');
|
|
432
|
+
if (colonIdx === -1)
|
|
433
|
+
continue;
|
|
434
|
+
const key = pair.substring(0, colonIdx).trim();
|
|
435
|
+
const val = pair.substring(colonIdx + 1).trim();
|
|
436
|
+
// Map camelCase to snake_case
|
|
437
|
+
const cppKey = key === 'paddingHorizontal' ? 'padding_horizontal'
|
|
438
|
+
: key === 'paddingVertical' ? 'padding_vertical'
|
|
439
|
+
: key === 'minWidth' ? 'min_width'
|
|
440
|
+
: key === 'minHeight' ? 'min_height'
|
|
441
|
+
: key === 'backgroundColor' ? 'background_color'
|
|
442
|
+
: key === 'textColor' ? 'text_color'
|
|
443
|
+
: key === 'fontSize' ? 'font_size'
|
|
444
|
+
: key;
|
|
445
|
+
// ImVec4 fields (color arrays)
|
|
446
|
+
if (cppKey === 'background_color' || cppKey === 'text_color') {
|
|
447
|
+
// val is like [r, g, b, a] — convert to ImVec4(r, g, b, a)
|
|
448
|
+
const arrInner = val.trim().replace(/^\[/, '').replace(/\]$/, '');
|
|
449
|
+
const components = arrInner.split(',').map(c => {
|
|
450
|
+
const s = c.trim();
|
|
451
|
+
return s.includes('.') ? `${s}f` : `${s}.0f`;
|
|
452
|
+
});
|
|
453
|
+
lines.push(`${indent}${varName}.${cppKey} = ImVec4(${components.join(', ')});`);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
const floatVal = val.includes('.') ? `${val}F` : `${val}.0F`;
|
|
457
|
+
lines.push(`${indent}${varName}.${cppKey} = ${floatVal};`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return varName;
|
|
461
|
+
}
|
|
462
|
+
// Already a variable name or expression — return as-is
|
|
463
|
+
return styleExpr;
|
|
464
|
+
}
|
|
218
465
|
function buildStyleBlock(node, indent, lines) {
|
|
219
466
|
// Check for style-related props (gap, padding, width, height, etc.)
|
|
220
467
|
const styleProps = {};
|
|
@@ -229,17 +476,18 @@ function buildStyleBlock(node, indent, lines) {
|
|
|
229
476
|
styleProps[cppKey] = val;
|
|
230
477
|
}
|
|
231
478
|
}
|
|
232
|
-
// Also check node.style (explicit style prop)
|
|
479
|
+
// Also check node.style (explicit style prop) — route through buildStyleVar to handle
|
|
480
|
+
// object literals like { backgroundColor: [r,g,b,a] } safely
|
|
233
481
|
const explicitStyle = node.style ?? node.props['style'];
|
|
234
482
|
if (explicitStyle) {
|
|
235
|
-
return explicitStyle;
|
|
483
|
+
return buildStyleVar(explicitStyle, indent, lines);
|
|
236
484
|
}
|
|
237
485
|
if (Object.keys(styleProps).length === 0) {
|
|
238
486
|
return null;
|
|
239
487
|
}
|
|
240
488
|
// Generate MSVC-compatible style construction
|
|
241
489
|
const varName = `style_${styleCounter++}`;
|
|
242
|
-
lines.push(`${indent}
|
|
490
|
+
lines.push(`${indent}imx::Style ${varName};`);
|
|
243
491
|
for (const [key, val] of Object.entries(styleProps)) {
|
|
244
492
|
// Ensure the value is a float literal (e.g., 8 -> 8.0F, 8.5 -> 8.5F)
|
|
245
493
|
const floatVal = val.includes('.') ? `${val}F` : `${val}.0F`;
|
|
@@ -248,59 +496,88 @@ function buildStyleBlock(node, indent, lines) {
|
|
|
248
496
|
return varName;
|
|
249
497
|
}
|
|
250
498
|
function emitBeginContainer(node, lines, indent) {
|
|
499
|
+
emitLocComment(node.loc, node.tag, lines, indent);
|
|
251
500
|
switch (node.tag) {
|
|
252
501
|
case 'Window': {
|
|
253
502
|
const title = asCharPtr(node.props['title'] ?? '""');
|
|
254
|
-
|
|
503
|
+
const flagParts = [];
|
|
504
|
+
if (node.props['noTitleBar'] === 'true')
|
|
505
|
+
flagParts.push('ImGuiWindowFlags_NoTitleBar');
|
|
506
|
+
if (node.props['noResize'] === 'true')
|
|
507
|
+
flagParts.push('ImGuiWindowFlags_NoResize');
|
|
508
|
+
if (node.props['noMove'] === 'true')
|
|
509
|
+
flagParts.push('ImGuiWindowFlags_NoMove');
|
|
510
|
+
if (node.props['noCollapse'] === 'true')
|
|
511
|
+
flagParts.push('ImGuiWindowFlags_NoCollapse');
|
|
512
|
+
if (node.props['noDocking'] === 'true')
|
|
513
|
+
flagParts.push('ImGuiWindowFlags_NoDocking');
|
|
514
|
+
if (node.props['noScrollbar'] === 'true')
|
|
515
|
+
flagParts.push('ImGuiWindowFlags_NoScrollbar');
|
|
516
|
+
const flags = flagParts.length > 0 ? flagParts.join(' | ') : '0';
|
|
517
|
+
const openExpr = node.props['open'];
|
|
518
|
+
const onCloseExpr = node.props['onClose'];
|
|
519
|
+
if (openExpr) {
|
|
520
|
+
windowOpenStack.push(true);
|
|
521
|
+
lines.push(`${indent}{`);
|
|
522
|
+
lines.push(`${indent} bool win_open = ${openExpr};`);
|
|
523
|
+
lines.push(`${indent} imx::renderer::begin_window(${title}, ${flags}, &win_open);`);
|
|
524
|
+
if (onCloseExpr) {
|
|
525
|
+
lines.push(`${indent} if (!win_open) { ${onCloseExpr}; }`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
windowOpenStack.push(false);
|
|
530
|
+
lines.push(`${indent}imx::renderer::begin_window(${title}, ${flags});`);
|
|
531
|
+
}
|
|
255
532
|
break;
|
|
256
533
|
}
|
|
257
534
|
case 'Row': {
|
|
258
535
|
const style = buildStyleBlock(node, indent, lines);
|
|
259
536
|
if (style) {
|
|
260
|
-
lines.push(`${indent}
|
|
537
|
+
lines.push(`${indent}imx::renderer::begin_row(${style});`);
|
|
261
538
|
}
|
|
262
539
|
else {
|
|
263
|
-
lines.push(`${indent}
|
|
540
|
+
lines.push(`${indent}imx::renderer::begin_row();`);
|
|
264
541
|
}
|
|
265
542
|
break;
|
|
266
543
|
}
|
|
267
544
|
case 'Column': {
|
|
268
545
|
const style = buildStyleBlock(node, indent, lines);
|
|
269
546
|
if (style) {
|
|
270
|
-
lines.push(`${indent}
|
|
547
|
+
lines.push(`${indent}imx::renderer::begin_column(${style});`);
|
|
271
548
|
}
|
|
272
549
|
else {
|
|
273
|
-
lines.push(`${indent}
|
|
550
|
+
lines.push(`${indent}imx::renderer::begin_column();`);
|
|
274
551
|
}
|
|
275
552
|
break;
|
|
276
553
|
}
|
|
277
554
|
case 'View': {
|
|
278
555
|
const style = buildStyleBlock(node, indent, lines);
|
|
279
556
|
if (style) {
|
|
280
|
-
lines.push(`${indent}
|
|
557
|
+
lines.push(`${indent}imx::renderer::begin_view(${style});`);
|
|
281
558
|
}
|
|
282
559
|
else {
|
|
283
|
-
lines.push(`${indent}
|
|
560
|
+
lines.push(`${indent}imx::renderer::begin_view();`);
|
|
284
561
|
}
|
|
285
562
|
break;
|
|
286
563
|
}
|
|
287
564
|
case 'DockSpace': {
|
|
288
565
|
const style = buildStyleBlock(node, indent, lines);
|
|
289
566
|
if (style) {
|
|
290
|
-
lines.push(`${indent}
|
|
567
|
+
lines.push(`${indent}imx::renderer::begin_dockspace(${style});`);
|
|
291
568
|
}
|
|
292
569
|
else {
|
|
293
|
-
lines.push(`${indent}
|
|
570
|
+
lines.push(`${indent}imx::renderer::begin_dockspace();`);
|
|
294
571
|
}
|
|
295
572
|
break;
|
|
296
573
|
}
|
|
297
574
|
case 'MenuBar': {
|
|
298
|
-
lines.push(`${indent}if (
|
|
575
|
+
lines.push(`${indent}if (imx::renderer::begin_menu_bar()) {`);
|
|
299
576
|
break;
|
|
300
577
|
}
|
|
301
578
|
case 'Menu': {
|
|
302
579
|
const label = asCharPtr(node.props['label'] ?? '""');
|
|
303
|
-
lines.push(`${indent}if (
|
|
580
|
+
lines.push(`${indent}if (imx::renderer::begin_menu(${label})) {`);
|
|
304
581
|
break;
|
|
305
582
|
}
|
|
306
583
|
case 'Table': {
|
|
@@ -309,100 +586,343 @@ function emitBeginContainer(node, lines, indent) {
|
|
|
309
586
|
const count = columnNames.length;
|
|
310
587
|
const varName = `table_cols_${styleCounter++}`;
|
|
311
588
|
lines.push(`${indent}const char* ${varName}[] = {${columnNames.join(', ')}};`);
|
|
312
|
-
lines.push(`${indent}if (
|
|
589
|
+
lines.push(`${indent}if (imx::renderer::begin_table("##table", ${count}, ${varName})) {`);
|
|
313
590
|
break;
|
|
314
591
|
}
|
|
315
592
|
case 'TableRow': {
|
|
316
|
-
lines.push(`${indent}
|
|
593
|
+
lines.push(`${indent}imx::renderer::begin_table_row();`);
|
|
317
594
|
break;
|
|
318
595
|
}
|
|
319
596
|
case 'TabBar': {
|
|
320
|
-
lines.push(`${indent}if (
|
|
597
|
+
lines.push(`${indent}if (imx::renderer::begin_tab_bar()) {`);
|
|
321
598
|
break;
|
|
322
599
|
}
|
|
323
600
|
case 'TabItem': {
|
|
324
601
|
const label = asCharPtr(node.props['label'] ?? '""');
|
|
325
|
-
lines.push(`${indent}if (
|
|
602
|
+
lines.push(`${indent}if (imx::renderer::begin_tab_item(${label})) {`);
|
|
326
603
|
break;
|
|
327
604
|
}
|
|
328
605
|
case 'TreeNode': {
|
|
329
606
|
const label = asCharPtr(node.props['label'] ?? '""');
|
|
330
|
-
lines.push(`${indent}if (
|
|
607
|
+
lines.push(`${indent}if (imx::renderer::begin_tree_node(${label})) {`);
|
|
331
608
|
break;
|
|
332
609
|
}
|
|
333
610
|
case 'CollapsingHeader': {
|
|
334
611
|
const label = asCharPtr(node.props['label'] ?? '""');
|
|
335
|
-
lines.push(`${indent}if (
|
|
612
|
+
lines.push(`${indent}if (imx::renderer::begin_collapsing_header(${label})) {`);
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
case 'Theme': {
|
|
616
|
+
const preset = asCharPtr(node.props['preset'] ?? '"dark"');
|
|
617
|
+
const varName = `theme_${styleCounter++}`;
|
|
618
|
+
lines.push(`${indent}imx::ThemeConfig ${varName};`);
|
|
619
|
+
if (node.props['accentColor']) {
|
|
620
|
+
lines.push(`${indent}${varName}.accent_color = ${emitImVec4(node.props['accentColor'])};`);
|
|
621
|
+
}
|
|
622
|
+
if (node.props['backgroundColor']) {
|
|
623
|
+
lines.push(`${indent}${varName}.background_color = ${emitImVec4(node.props['backgroundColor'])};`);
|
|
624
|
+
}
|
|
625
|
+
if (node.props['textColor']) {
|
|
626
|
+
lines.push(`${indent}${varName}.text_color = ${emitImVec4(node.props['textColor'])};`);
|
|
627
|
+
}
|
|
628
|
+
if (node.props['borderColor']) {
|
|
629
|
+
lines.push(`${indent}${varName}.border_color = ${emitImVec4(node.props['borderColor'])};`);
|
|
630
|
+
}
|
|
631
|
+
if (node.props['surfaceColor']) {
|
|
632
|
+
lines.push(`${indent}${varName}.surface_color = ${emitImVec4(node.props['surfaceColor'])};`);
|
|
633
|
+
}
|
|
634
|
+
if (node.props['rounding']) {
|
|
635
|
+
lines.push(`${indent}${varName}.rounding = ${emitFloat(node.props['rounding'])};`);
|
|
636
|
+
}
|
|
637
|
+
if (node.props['borderSize']) {
|
|
638
|
+
lines.push(`${indent}${varName}.border_size = ${emitFloat(node.props['borderSize'])};`);
|
|
639
|
+
}
|
|
640
|
+
if (node.props['spacing']) {
|
|
641
|
+
lines.push(`${indent}${varName}.spacing = ${emitFloat(node.props['spacing'])};`);
|
|
642
|
+
}
|
|
643
|
+
lines.push(`${indent}imx::renderer::begin_theme(${preset}, ${varName});`);
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
case 'Modal': {
|
|
647
|
+
const title = asCharPtr(node.props['title'] ?? '""');
|
|
648
|
+
const openExpr = node.props['open'];
|
|
649
|
+
const onCloseExpr = node.props['onClose'];
|
|
650
|
+
if (openExpr) {
|
|
651
|
+
windowOpenStack.push(true);
|
|
652
|
+
// Extract onClose body for use in end emitter
|
|
653
|
+
let onCloseBody = null;
|
|
654
|
+
if (onCloseExpr) {
|
|
655
|
+
const lambdaMatch = onCloseExpr.match(/^\[&\]\(\)\s*\{\s*(.*?)\s*\}$/);
|
|
656
|
+
onCloseBody = lambdaMatch ? lambdaMatch[1] : `${onCloseExpr};`;
|
|
657
|
+
}
|
|
658
|
+
modalOnCloseStack.push(onCloseBody);
|
|
659
|
+
lines.push(`${indent}{`);
|
|
660
|
+
lines.push(`${indent} bool modal_closed = false;`);
|
|
661
|
+
lines.push(`${indent} if (imx::renderer::begin_modal(${title}, ${openExpr}, &modal_closed)) {`);
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
windowOpenStack.push(false);
|
|
665
|
+
modalOnCloseStack.push(null);
|
|
666
|
+
lines.push(`${indent}if (imx::renderer::begin_modal(${title}, true, nullptr)) {`);
|
|
667
|
+
}
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
case 'StyleColor': {
|
|
671
|
+
const varName = `sc_${styleCounter++}`;
|
|
672
|
+
lines.push(`${indent}imx::StyleColorOverrides ${varName};`);
|
|
673
|
+
const colorProps = [
|
|
674
|
+
['text', 'text'], ['textDisabled', 'text_disabled'],
|
|
675
|
+
['windowBg', 'window_bg'], ['frameBg', 'frame_bg'],
|
|
676
|
+
['frameBgHovered', 'frame_bg_hovered'], ['frameBgActive', 'frame_bg_active'],
|
|
677
|
+
['titleBg', 'title_bg'], ['titleBgActive', 'title_bg_active'],
|
|
678
|
+
['button', 'button'], ['buttonHovered', 'button_hovered'],
|
|
679
|
+
['buttonActive', 'button_active'], ['header', 'header'],
|
|
680
|
+
['headerHovered', 'header_hovered'], ['headerActive', 'header_active'],
|
|
681
|
+
['separator', 'separator'], ['checkMark', 'check_mark'],
|
|
682
|
+
['sliderGrab', 'slider_grab'], ['border', 'border'],
|
|
683
|
+
['popupBg', 'popup_bg'], ['tab', 'tab'],
|
|
684
|
+
];
|
|
685
|
+
for (const [tsName, cppName] of colorProps) {
|
|
686
|
+
if (node.props[tsName]) {
|
|
687
|
+
lines.push(`${indent}${varName}.${cppName} = ${emitImVec4(node.props[tsName])};`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
lines.push(`${indent}imx::renderer::begin_style_color(${varName});`);
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
case 'StyleVar': {
|
|
694
|
+
const varName = `sv_${styleCounter++}`;
|
|
695
|
+
lines.push(`${indent}imx::StyleVarOverrides ${varName};`);
|
|
696
|
+
const floatProps = [
|
|
697
|
+
['alpha', 'alpha'], ['windowRounding', 'window_rounding'],
|
|
698
|
+
['frameRounding', 'frame_rounding'], ['frameBorderSize', 'frame_border_size'],
|
|
699
|
+
['indentSpacing', 'indent_spacing'], ['tabRounding', 'tab_rounding'],
|
|
700
|
+
];
|
|
701
|
+
for (const [tsName, cppName] of floatProps) {
|
|
702
|
+
if (node.props[tsName]) {
|
|
703
|
+
lines.push(`${indent}${varName}.${cppName} = ${emitFloat(node.props[tsName])};`);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
const vec2Props = [
|
|
707
|
+
['windowPadding', 'window_padding'], ['framePadding', 'frame_padding'],
|
|
708
|
+
['itemSpacing', 'item_spacing'], ['itemInnerSpacing', 'item_inner_spacing'],
|
|
709
|
+
['cellPadding', 'cell_padding'],
|
|
710
|
+
];
|
|
711
|
+
for (const [tsName, cppName] of vec2Props) {
|
|
712
|
+
if (node.props[tsName]) {
|
|
713
|
+
lines.push(`${indent}${varName}.${cppName} = ${emitImVec2(node.props[tsName])};`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
lines.push(`${indent}imx::renderer::begin_style_var(${varName});`);
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
case 'Group': {
|
|
720
|
+
lines.push(`${indent}ImGui::BeginGroup();`);
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
case 'ID': {
|
|
724
|
+
const scope = node.props['scope'] ?? '""';
|
|
725
|
+
if (scope.startsWith('"')) {
|
|
726
|
+
lines.push(`${indent}ImGui::PushID(${scope});`);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
lines.push(`${indent}ImGui::PushID(static_cast<int>(${scope}));`);
|
|
730
|
+
}
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
case 'DragDropSource': {
|
|
734
|
+
dragDropSourceStack.push(node.props);
|
|
735
|
+
lines.push(`${indent}ImGui::BeginGroup();`);
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
case 'DragDropTarget': {
|
|
739
|
+
dragDropTargetStack.push(node.props);
|
|
740
|
+
lines.push(`${indent}ImGui::BeginGroup();`);
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
case 'Disabled': {
|
|
744
|
+
const disabled = node.props['disabled'] ?? 'true';
|
|
745
|
+
lines.push(`${indent}ImGui::BeginDisabled(${disabled});`);
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
case 'Child': {
|
|
749
|
+
const id = asCharPtr(node.props['id'] ?? '"##child"');
|
|
750
|
+
const width = emitFloat(node.props['width'] ?? '0');
|
|
751
|
+
const height = emitFloat(node.props['height'] ?? '0');
|
|
752
|
+
const border = node.props['border'] === 'true' ? 'true' : 'false';
|
|
753
|
+
lines.push(`${indent}ImGui::BeginChild(${id}, ImVec2(${width}, ${height}), ${border});`);
|
|
336
754
|
break;
|
|
337
755
|
}
|
|
756
|
+
case 'Canvas': {
|
|
757
|
+
const width = emitFloat(node.props['width'] ?? '0');
|
|
758
|
+
const height = emitFloat(node.props['height'] ?? '0');
|
|
759
|
+
const style = buildStyleBlock(node, indent, lines);
|
|
760
|
+
if (style) {
|
|
761
|
+
lines.push(`${indent}imx::renderer::begin_canvas(${width}, ${height}, ${style});`);
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
lines.push(`${indent}imx::renderer::begin_canvas(${width}, ${height});`);
|
|
765
|
+
}
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case 'DockLayout':
|
|
769
|
+
case 'DockSplit':
|
|
770
|
+
case 'DockPanel':
|
|
771
|
+
break;
|
|
338
772
|
}
|
|
339
773
|
}
|
|
340
774
|
function emitEndContainer(node, lines, indent) {
|
|
341
775
|
switch (node.tag) {
|
|
342
|
-
case 'Window':
|
|
343
|
-
lines.push(`${indent}
|
|
776
|
+
case 'Window': {
|
|
777
|
+
lines.push(`${indent}imx::renderer::end_window();`);
|
|
778
|
+
const hadOpen = windowOpenStack.pop() ?? false;
|
|
779
|
+
if (hadOpen) {
|
|
780
|
+
lines.push(`${indent}}`);
|
|
781
|
+
}
|
|
344
782
|
break;
|
|
783
|
+
}
|
|
345
784
|
case 'Row':
|
|
346
|
-
lines.push(`${indent}
|
|
785
|
+
lines.push(`${indent}imx::renderer::end_row();`);
|
|
347
786
|
break;
|
|
348
787
|
case 'Column':
|
|
349
|
-
lines.push(`${indent}
|
|
788
|
+
lines.push(`${indent}imx::renderer::end_column();`);
|
|
350
789
|
break;
|
|
351
790
|
case 'View':
|
|
352
|
-
lines.push(`${indent}
|
|
791
|
+
lines.push(`${indent}imx::renderer::end_view();`);
|
|
353
792
|
break;
|
|
354
793
|
case 'DockSpace':
|
|
355
|
-
lines.push(`${indent}
|
|
794
|
+
lines.push(`${indent}imx::renderer::end_dockspace();`);
|
|
356
795
|
break;
|
|
357
796
|
case 'MenuBar':
|
|
358
|
-
lines.push(`${indent}
|
|
797
|
+
lines.push(`${indent}imx::renderer::end_menu_bar();`);
|
|
359
798
|
lines.push(`${indent}}`);
|
|
360
799
|
break;
|
|
361
800
|
case 'Menu':
|
|
362
|
-
lines.push(`${indent}
|
|
801
|
+
lines.push(`${indent}imx::renderer::end_menu();`);
|
|
363
802
|
lines.push(`${indent}}`);
|
|
364
803
|
break;
|
|
365
804
|
case 'Table':
|
|
366
|
-
lines.push(`${indent}
|
|
805
|
+
lines.push(`${indent}imx::renderer::end_table();`);
|
|
367
806
|
lines.push(`${indent}}`);
|
|
368
807
|
break;
|
|
369
808
|
case 'TableRow':
|
|
370
|
-
lines.push(`${indent}
|
|
809
|
+
lines.push(`${indent}imx::renderer::end_table_row();`);
|
|
371
810
|
break;
|
|
372
811
|
case 'TabBar':
|
|
373
|
-
lines.push(`${indent}
|
|
812
|
+
lines.push(`${indent}imx::renderer::end_tab_bar();`);
|
|
374
813
|
lines.push(`${indent}}`);
|
|
375
814
|
break;
|
|
376
815
|
case 'TabItem':
|
|
377
|
-
lines.push(`${indent}
|
|
816
|
+
lines.push(`${indent}imx::renderer::end_tab_item();`);
|
|
378
817
|
lines.push(`${indent}}`);
|
|
379
818
|
break;
|
|
380
819
|
case 'TreeNode':
|
|
381
|
-
lines.push(`${indent}
|
|
820
|
+
lines.push(`${indent}imx::renderer::end_tree_node();`);
|
|
382
821
|
lines.push(`${indent}}`);
|
|
383
822
|
break;
|
|
384
823
|
case 'CollapsingHeader':
|
|
385
|
-
lines.push(`${indent}
|
|
824
|
+
lines.push(`${indent}imx::renderer::end_collapsing_header();`);
|
|
825
|
+
lines.push(`${indent}}`);
|
|
826
|
+
break;
|
|
827
|
+
case 'Theme':
|
|
828
|
+
lines.push(`${indent}imx::renderer::end_theme();`);
|
|
829
|
+
break;
|
|
830
|
+
case 'Modal': {
|
|
831
|
+
lines.push(`${indent}imx::renderer::end_modal();`);
|
|
832
|
+
lines.push(`${indent}}`); // close the if (begin_modal) block
|
|
833
|
+
const hadOpen = windowOpenStack.pop() ?? false;
|
|
834
|
+
const onCloseBody = modalOnCloseStack.pop() ?? null;
|
|
835
|
+
if (hadOpen && onCloseBody) {
|
|
836
|
+
// Check modal_closed OUTSIDE the if(begin_modal) block,
|
|
837
|
+
// because BeginPopupModal returns false when X is clicked
|
|
838
|
+
// (it calls EndPopup internally).
|
|
839
|
+
lines.push(`${indent}if (modal_closed) { ${onCloseBody} }`);
|
|
840
|
+
lines.push(`${indent}}`); // close the { bool modal_closed scope
|
|
841
|
+
}
|
|
842
|
+
else if (hadOpen) {
|
|
843
|
+
lines.push(`${indent}}`); // close scope without onClose
|
|
844
|
+
}
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
case 'StyleColor':
|
|
848
|
+
lines.push(`${indent}imx::renderer::end_style_color();`);
|
|
849
|
+
break;
|
|
850
|
+
case 'StyleVar':
|
|
851
|
+
lines.push(`${indent}imx::renderer::end_style_var();`);
|
|
852
|
+
break;
|
|
853
|
+
case 'Group':
|
|
854
|
+
lines.push(`${indent}ImGui::EndGroup();`);
|
|
855
|
+
break;
|
|
856
|
+
case 'ID':
|
|
857
|
+
lines.push(`${indent}ImGui::PopID();`);
|
|
858
|
+
break;
|
|
859
|
+
case 'DragDropSource': {
|
|
860
|
+
const props = dragDropSourceStack.pop() ?? {};
|
|
861
|
+
const typeStr = asCharPtr(props['type'] ?? '""');
|
|
862
|
+
const payload = props['payload'] ?? '0';
|
|
863
|
+
lines.push(`${indent}ImGui::EndGroup();`);
|
|
864
|
+
lines.push(`${indent}if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {`);
|
|
865
|
+
lines.push(`${indent} float _dd_payload = static_cast<float>(${payload});`);
|
|
866
|
+
lines.push(`${indent} ImGui::SetDragDropPayload(${typeStr}, &_dd_payload, sizeof(_dd_payload));`);
|
|
867
|
+
lines.push(`${indent} ImGui::Text("Dragging...");`);
|
|
868
|
+
lines.push(`${indent} ImGui::EndDragDropSource();`);
|
|
386
869
|
lines.push(`${indent}}`);
|
|
387
870
|
break;
|
|
871
|
+
}
|
|
872
|
+
case 'Disabled':
|
|
873
|
+
lines.push(`${indent}ImGui::EndDisabled();`);
|
|
874
|
+
break;
|
|
875
|
+
case 'Child':
|
|
876
|
+
lines.push(`${indent}ImGui::EndChild();`);
|
|
877
|
+
break;
|
|
878
|
+
case 'Canvas':
|
|
879
|
+
lines.push(`${indent}imx::renderer::end_canvas();`);
|
|
880
|
+
break;
|
|
881
|
+
case 'DragDropTarget': {
|
|
882
|
+
const props = dragDropTargetStack.pop() ?? {};
|
|
883
|
+
const typeStr = asCharPtr(props['type'] ?? '""');
|
|
884
|
+
const onDrop = props['onDrop'] ?? '';
|
|
885
|
+
lines.push(`${indent}ImGui::EndGroup();`);
|
|
886
|
+
lines.push(`${indent}if (ImGui::BeginDragDropTarget()) {`);
|
|
887
|
+
lines.push(`${indent} if (const ImGuiPayload* _dd_p = ImGui::AcceptDragDropPayload(${typeStr})) {`);
|
|
888
|
+
// Parse the structured callback: "cppType|paramName|bodyCode"
|
|
889
|
+
const parts = onDrop.split('|');
|
|
890
|
+
if (parts.length >= 3) {
|
|
891
|
+
const cppType = parts[0];
|
|
892
|
+
const paramName = parts[1];
|
|
893
|
+
const bodyCode = parts.slice(2).join('|'); // rejoin in case body contained |
|
|
894
|
+
lines.push(`${indent} ${cppType} ${paramName} = *(const ${cppType}*)_dd_p->Data;`);
|
|
895
|
+
lines.push(`${indent} ${bodyCode}`);
|
|
896
|
+
}
|
|
897
|
+
lines.push(`${indent} }`);
|
|
898
|
+
lines.push(`${indent} ImGui::EndDragDropTarget();`);
|
|
899
|
+
lines.push(`${indent}}`);
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
case 'DockLayout':
|
|
903
|
+
case 'DockSplit':
|
|
904
|
+
case 'DockPanel':
|
|
905
|
+
break;
|
|
388
906
|
}
|
|
389
907
|
}
|
|
390
908
|
function emitText(node, lines, indent) {
|
|
909
|
+
emitLocComment(node.loc, 'Text', lines, indent);
|
|
391
910
|
if (node.args.length === 0) {
|
|
392
|
-
lines.push(`${indent}
|
|
911
|
+
lines.push(`${indent}imx::renderer::text(${JSON.stringify(node.format)});`);
|
|
393
912
|
}
|
|
394
913
|
else {
|
|
395
914
|
const argsStr = node.args.join(', ');
|
|
396
|
-
lines.push(`${indent}
|
|
915
|
+
lines.push(`${indent}imx::renderer::text(${JSON.stringify(node.format)}, ${argsStr});`);
|
|
397
916
|
}
|
|
398
917
|
}
|
|
399
918
|
function emitButton(node, lines, indent, depth) {
|
|
919
|
+
emitLocComment(node.loc, 'Button', lines, indent);
|
|
400
920
|
const title = asCharPtr(node.title);
|
|
401
921
|
if (node.action.length === 0) {
|
|
402
|
-
lines.push(`${indent}
|
|
922
|
+
lines.push(`${indent}imx::renderer::button(${title});`);
|
|
403
923
|
}
|
|
404
924
|
else {
|
|
405
|
-
lines.push(`${indent}if (
|
|
925
|
+
lines.push(`${indent}if (imx::renderer::button(${title})) {`);
|
|
406
926
|
for (const stmt of node.action) {
|
|
407
927
|
lines.push(`${indent}${INDENT}${stmt}`);
|
|
408
928
|
}
|
|
@@ -410,22 +930,23 @@ function emitButton(node, lines, indent, depth) {
|
|
|
410
930
|
}
|
|
411
931
|
}
|
|
412
932
|
function emitMenuItem(node, lines, indent, depth) {
|
|
933
|
+
emitLocComment(node.loc, 'MenuItem', lines, indent);
|
|
413
934
|
const label = asCharPtr(node.label);
|
|
414
935
|
const shortcut = node.shortcut ? asCharPtr(node.shortcut) : undefined;
|
|
415
936
|
if (node.action.length === 0) {
|
|
416
937
|
if (shortcut) {
|
|
417
|
-
lines.push(`${indent}
|
|
938
|
+
lines.push(`${indent}imx::renderer::menu_item(${label}, ${shortcut});`);
|
|
418
939
|
}
|
|
419
940
|
else {
|
|
420
|
-
lines.push(`${indent}
|
|
941
|
+
lines.push(`${indent}imx::renderer::menu_item(${label});`);
|
|
421
942
|
}
|
|
422
943
|
}
|
|
423
944
|
else {
|
|
424
945
|
if (shortcut) {
|
|
425
|
-
lines.push(`${indent}if (
|
|
946
|
+
lines.push(`${indent}if (imx::renderer::menu_item(${label}, ${shortcut})) {`);
|
|
426
947
|
}
|
|
427
948
|
else {
|
|
428
|
-
lines.push(`${indent}if (
|
|
949
|
+
lines.push(`${indent}if (imx::renderer::menu_item(${label})) {`);
|
|
429
950
|
}
|
|
430
951
|
for (const stmt of node.action) {
|
|
431
952
|
lines.push(`${indent} ${stmt}`);
|
|
@@ -434,29 +955,31 @@ function emitMenuItem(node, lines, indent, depth) {
|
|
|
434
955
|
}
|
|
435
956
|
}
|
|
436
957
|
function emitTextInput(node, lines, indent) {
|
|
958
|
+
emitLocComment(node.loc, 'TextInput', lines, indent);
|
|
437
959
|
const label = asCharPtr(node.label && node.label !== '""' ? node.label : `"##textinput_${node.bufferIndex}"`);
|
|
438
960
|
if (node.stateVar) {
|
|
439
961
|
lines.push(`${indent}{`);
|
|
440
962
|
lines.push(`${indent}${INDENT}auto& buf = ctx.get_buffer(${node.bufferIndex});`);
|
|
441
963
|
lines.push(`${indent}${INDENT}buf.sync_from(${node.stateVar}.get());`);
|
|
442
|
-
lines.push(`${indent}${INDENT}if (
|
|
964
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::text_input(${label}, buf)) {`);
|
|
443
965
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(buf.value());`);
|
|
444
966
|
lines.push(`${indent}${INDENT}}`);
|
|
445
967
|
lines.push(`${indent}}`);
|
|
446
968
|
}
|
|
447
969
|
else {
|
|
448
970
|
lines.push(`${indent}auto& buf_${node.bufferIndex} = ctx.get_buffer(${node.bufferIndex});`);
|
|
449
|
-
lines.push(`${indent}
|
|
971
|
+
lines.push(`${indent}imx::renderer::text_input(${label}, buf_${node.bufferIndex});`);
|
|
450
972
|
}
|
|
451
973
|
}
|
|
452
974
|
function emitCheckbox(node, lines, indent) {
|
|
975
|
+
emitLocComment(node.loc, 'Checkbox', lines, indent);
|
|
453
976
|
const label = asCharPtr(node.label && node.label !== '""' ? node.label : `"##checkbox_${checkboxCounter}"`);
|
|
454
977
|
checkboxCounter++;
|
|
455
978
|
if (node.stateVar) {
|
|
456
979
|
// State-bound case
|
|
457
980
|
lines.push(`${indent}{`);
|
|
458
981
|
lines.push(`${indent}${INDENT}bool val = ${node.stateVar}.get();`);
|
|
459
|
-
lines.push(`${indent}${INDENT}if (
|
|
982
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::checkbox(${label}, &val)) {`);
|
|
460
983
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
461
984
|
lines.push(`${indent}${INDENT}}`);
|
|
462
985
|
lines.push(`${indent}}`);
|
|
@@ -465,7 +988,7 @@ function emitCheckbox(node, lines, indent) {
|
|
|
465
988
|
// Props-bound / expression-bound case
|
|
466
989
|
lines.push(`${indent}{`);
|
|
467
990
|
lines.push(`${indent}${INDENT}bool val = ${node.valueExpr};`);
|
|
468
|
-
lines.push(`${indent}${INDENT}if (
|
|
991
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::checkbox(${label}, &val)) {`);
|
|
469
992
|
if (node.onChangeExpr) {
|
|
470
993
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
471
994
|
}
|
|
@@ -473,10 +996,13 @@ function emitCheckbox(node, lines, indent) {
|
|
|
473
996
|
lines.push(`${indent}}`);
|
|
474
997
|
}
|
|
475
998
|
else {
|
|
476
|
-
lines.push(`${indent}
|
|
999
|
+
lines.push(`${indent}imx::renderer::checkbox(${label}, nullptr);`);
|
|
477
1000
|
}
|
|
478
1001
|
}
|
|
479
1002
|
function emitConditional(node, lines, indent, depth) {
|
|
1003
|
+
if (node.loc) {
|
|
1004
|
+
lines.push(`${indent}// ${node.loc.file}:${node.loc.line} conditional`);
|
|
1005
|
+
}
|
|
480
1006
|
lines.push(`${indent}if (${node.condition}) {`);
|
|
481
1007
|
emitNodes(node.body, lines, depth + 1);
|
|
482
1008
|
if (node.elseBody && node.elseBody.length > 0) {
|
|
@@ -486,6 +1012,9 @@ function emitConditional(node, lines, indent, depth) {
|
|
|
486
1012
|
lines.push(`${indent}}`);
|
|
487
1013
|
}
|
|
488
1014
|
function emitListMap(node, lines, indent, depth) {
|
|
1015
|
+
if (node.loc) {
|
|
1016
|
+
lines.push(`${indent}// ${node.loc.file}:${node.loc.line} .map()`);
|
|
1017
|
+
}
|
|
489
1018
|
lines.push(`${indent}for (size_t i = 0; i < ${node.array}.size(); i++) {`);
|
|
490
1019
|
lines.push(`${indent}${INDENT}auto& ${node.itemVar} = ${node.array}[i];`);
|
|
491
1020
|
lines.push(`${indent}${INDENT}ctx.begin_instance("${node.componentName}", i, ${node.stateCount}, ${node.bufferCount});`);
|
|
@@ -494,6 +1023,7 @@ function emitListMap(node, lines, indent, depth) {
|
|
|
494
1023
|
lines.push(`${indent}}`);
|
|
495
1024
|
}
|
|
496
1025
|
function emitCustomComponent(node, lines, indent) {
|
|
1026
|
+
emitLocComment(node.loc, node.name, lines, indent);
|
|
497
1027
|
const instanceIndex = customComponentCounter++;
|
|
498
1028
|
const propsEntries = Object.entries(node.props);
|
|
499
1029
|
lines.push(`${indent}ctx.begin_instance("${node.name}", ${instanceIndex}, ${node.stateCount}, ${node.bufferCount});`);
|
|
@@ -512,6 +1042,23 @@ function emitCustomComponent(node, lines, indent) {
|
|
|
512
1042
|
}
|
|
513
1043
|
lines.push(`${indent}ctx.end_instance();`);
|
|
514
1044
|
}
|
|
1045
|
+
function emitNativeWidget(node, lines, indent) {
|
|
1046
|
+
emitLocComment(node.loc, node.name, lines, indent);
|
|
1047
|
+
const idx = nativeWidgetCounter++;
|
|
1048
|
+
const label = `${node.name}##nw_${idx}`;
|
|
1049
|
+
lines.push(`${indent}{`);
|
|
1050
|
+
lines.push(`${indent}${INDENT}imx::WidgetArgs _wa("${label}");`);
|
|
1051
|
+
// Value props
|
|
1052
|
+
for (const [k, v] of Object.entries(node.props)) {
|
|
1053
|
+
lines.push(`${indent}${INDENT}_wa.set("${k}", ${v});`);
|
|
1054
|
+
}
|
|
1055
|
+
// Callback props — already lowered to [&](std::any _v) { ... } lambdas
|
|
1056
|
+
for (const [k, v] of Object.entries(node.callbackProps)) {
|
|
1057
|
+
lines.push(`${indent}${INDENT}_wa.set_callback("${k}", ${v});`);
|
|
1058
|
+
}
|
|
1059
|
+
lines.push(`${indent}${INDENT}imx::call_widget("${node.name}", _wa);`);
|
|
1060
|
+
lines.push(`${indent}}`);
|
|
1061
|
+
}
|
|
515
1062
|
function ensureFloatLiteral(val) {
|
|
516
1063
|
// If already has 'f' suffix or '.', leave as-is
|
|
517
1064
|
if (val.endsWith('f') || val.endsWith('F') || val.includes('.'))
|
|
@@ -520,12 +1067,13 @@ function ensureFloatLiteral(val) {
|
|
|
520
1067
|
return `${val}.0f`;
|
|
521
1068
|
}
|
|
522
1069
|
function emitSliderFloat(node, lines, indent) {
|
|
1070
|
+
emitLocComment(node.loc, 'SliderFloat', lines, indent);
|
|
523
1071
|
const min = ensureFloatLiteral(node.min);
|
|
524
1072
|
const max = ensureFloatLiteral(node.max);
|
|
525
1073
|
if (node.stateVar) {
|
|
526
1074
|
lines.push(`${indent}{`);
|
|
527
1075
|
lines.push(`${indent}${INDENT}float val = ${node.stateVar}.get();`);
|
|
528
|
-
lines.push(`${indent}${INDENT}if (
|
|
1076
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::slider_float(${node.label}, &val, ${min}, ${max})) {`);
|
|
529
1077
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
530
1078
|
lines.push(`${indent}${INDENT}}`);
|
|
531
1079
|
lines.push(`${indent}}`);
|
|
@@ -533,7 +1081,7 @@ function emitSliderFloat(node, lines, indent) {
|
|
|
533
1081
|
else if (node.valueExpr !== undefined) {
|
|
534
1082
|
lines.push(`${indent}{`);
|
|
535
1083
|
lines.push(`${indent}${INDENT}float val = ${node.valueExpr};`);
|
|
536
|
-
lines.push(`${indent}${INDENT}if (
|
|
1084
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::slider_float(${node.label}, &val, ${min}, ${max})) {`);
|
|
537
1085
|
if (node.onChangeExpr) {
|
|
538
1086
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
539
1087
|
}
|
|
@@ -542,10 +1090,11 @@ function emitSliderFloat(node, lines, indent) {
|
|
|
542
1090
|
}
|
|
543
1091
|
}
|
|
544
1092
|
function emitSliderInt(node, lines, indent) {
|
|
1093
|
+
emitLocComment(node.loc, 'SliderInt', lines, indent);
|
|
545
1094
|
if (node.stateVar) {
|
|
546
1095
|
lines.push(`${indent}{`);
|
|
547
1096
|
lines.push(`${indent}${INDENT}int val = ${node.stateVar}.get();`);
|
|
548
|
-
lines.push(`${indent}${INDENT}if (
|
|
1097
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::slider_int(${node.label}, &val, ${node.min}, ${node.max})) {`);
|
|
549
1098
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
550
1099
|
lines.push(`${indent}${INDENT}}`);
|
|
551
1100
|
lines.push(`${indent}}`);
|
|
@@ -553,7 +1102,7 @@ function emitSliderInt(node, lines, indent) {
|
|
|
553
1102
|
else if (node.valueExpr !== undefined) {
|
|
554
1103
|
lines.push(`${indent}{`);
|
|
555
1104
|
lines.push(`${indent}${INDENT}int val = ${node.valueExpr};`);
|
|
556
|
-
lines.push(`${indent}${INDENT}if (
|
|
1105
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::slider_int(${node.label}, &val, ${node.min}, ${node.max})) {`);
|
|
557
1106
|
if (node.onChangeExpr) {
|
|
558
1107
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
559
1108
|
}
|
|
@@ -562,11 +1111,12 @@ function emitSliderInt(node, lines, indent) {
|
|
|
562
1111
|
}
|
|
563
1112
|
}
|
|
564
1113
|
function emitDragFloat(node, lines, indent) {
|
|
1114
|
+
emitLocComment(node.loc, 'DragFloat', lines, indent);
|
|
565
1115
|
const speed = ensureFloatLiteral(node.speed);
|
|
566
1116
|
if (node.stateVar) {
|
|
567
1117
|
lines.push(`${indent}{`);
|
|
568
1118
|
lines.push(`${indent}${INDENT}float val = ${node.stateVar}.get();`);
|
|
569
|
-
lines.push(`${indent}${INDENT}if (
|
|
1119
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::drag_float(${node.label}, &val, ${speed})) {`);
|
|
570
1120
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
571
1121
|
lines.push(`${indent}${INDENT}}`);
|
|
572
1122
|
lines.push(`${indent}}`);
|
|
@@ -574,7 +1124,7 @@ function emitDragFloat(node, lines, indent) {
|
|
|
574
1124
|
else if (node.valueExpr !== undefined) {
|
|
575
1125
|
lines.push(`${indent}{`);
|
|
576
1126
|
lines.push(`${indent}${INDENT}float val = ${node.valueExpr};`);
|
|
577
|
-
lines.push(`${indent}${INDENT}if (
|
|
1127
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::drag_float(${node.label}, &val, ${speed})) {`);
|
|
578
1128
|
if (node.onChangeExpr) {
|
|
579
1129
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
580
1130
|
}
|
|
@@ -583,11 +1133,12 @@ function emitDragFloat(node, lines, indent) {
|
|
|
583
1133
|
}
|
|
584
1134
|
}
|
|
585
1135
|
function emitDragInt(node, lines, indent) {
|
|
1136
|
+
emitLocComment(node.loc, 'DragInt', lines, indent);
|
|
586
1137
|
const speed = ensureFloatLiteral(node.speed);
|
|
587
1138
|
if (node.stateVar) {
|
|
588
1139
|
lines.push(`${indent}{`);
|
|
589
1140
|
lines.push(`${indent}${INDENT}int val = ${node.stateVar}.get();`);
|
|
590
|
-
lines.push(`${indent}${INDENT}if (
|
|
1141
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::drag_int(${node.label}, &val, ${speed})) {`);
|
|
591
1142
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
592
1143
|
lines.push(`${indent}${INDENT}}`);
|
|
593
1144
|
lines.push(`${indent}}`);
|
|
@@ -595,7 +1146,7 @@ function emitDragInt(node, lines, indent) {
|
|
|
595
1146
|
else if (node.valueExpr !== undefined) {
|
|
596
1147
|
lines.push(`${indent}{`);
|
|
597
1148
|
lines.push(`${indent}${INDENT}int val = ${node.valueExpr};`);
|
|
598
|
-
lines.push(`${indent}${INDENT}if (
|
|
1149
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::drag_int(${node.label}, &val, ${speed})) {`);
|
|
599
1150
|
if (node.onChangeExpr) {
|
|
600
1151
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
601
1152
|
}
|
|
@@ -604,6 +1155,7 @@ function emitDragInt(node, lines, indent) {
|
|
|
604
1155
|
}
|
|
605
1156
|
}
|
|
606
1157
|
function emitCombo(node, lines, indent) {
|
|
1158
|
+
emitLocComment(node.loc, 'Combo', lines, indent);
|
|
607
1159
|
const itemsList = node.items.split(',').map(s => s.trim()).filter(s => s.length > 0);
|
|
608
1160
|
const count = itemsList.length;
|
|
609
1161
|
const varName = `combo_items_${comboCounter++}`;
|
|
@@ -611,7 +1163,7 @@ function emitCombo(node, lines, indent) {
|
|
|
611
1163
|
lines.push(`${indent}{`);
|
|
612
1164
|
lines.push(`${indent}${INDENT}const char* ${varName}[] = {${itemsList.join(', ')}};`);
|
|
613
1165
|
lines.push(`${indent}${INDENT}int val = ${node.stateVar}.get();`);
|
|
614
|
-
lines.push(`${indent}${INDENT}if (
|
|
1166
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::combo(${node.label}, &val, ${varName}, ${count})) {`);
|
|
615
1167
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
616
1168
|
lines.push(`${indent}${INDENT}}`);
|
|
617
1169
|
lines.push(`${indent}}`);
|
|
@@ -620,7 +1172,7 @@ function emitCombo(node, lines, indent) {
|
|
|
620
1172
|
lines.push(`${indent}{`);
|
|
621
1173
|
lines.push(`${indent}${INDENT}const char* ${varName}[] = {${itemsList.join(', ')}};`);
|
|
622
1174
|
lines.push(`${indent}${INDENT}int val = ${node.valueExpr};`);
|
|
623
|
-
lines.push(`${indent}${INDENT}if (
|
|
1175
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::combo(${node.label}, &val, ${varName}, ${count})) {`);
|
|
624
1176
|
if (node.onChangeExpr) {
|
|
625
1177
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
626
1178
|
}
|
|
@@ -629,10 +1181,11 @@ function emitCombo(node, lines, indent) {
|
|
|
629
1181
|
}
|
|
630
1182
|
}
|
|
631
1183
|
function emitInputInt(node, lines, indent) {
|
|
1184
|
+
emitLocComment(node.loc, 'InputInt', lines, indent);
|
|
632
1185
|
if (node.stateVar) {
|
|
633
1186
|
lines.push(`${indent}{`);
|
|
634
1187
|
lines.push(`${indent}${INDENT}int val = ${node.stateVar}.get();`);
|
|
635
|
-
lines.push(`${indent}${INDENT}if (
|
|
1188
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::input_int(${node.label}, &val)) {`);
|
|
636
1189
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
637
1190
|
lines.push(`${indent}${INDENT}}`);
|
|
638
1191
|
lines.push(`${indent}}`);
|
|
@@ -640,7 +1193,7 @@ function emitInputInt(node, lines, indent) {
|
|
|
640
1193
|
else if (node.valueExpr !== undefined) {
|
|
641
1194
|
lines.push(`${indent}{`);
|
|
642
1195
|
lines.push(`${indent}${INDENT}int val = ${node.valueExpr};`);
|
|
643
|
-
lines.push(`${indent}${INDENT}if (
|
|
1196
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::input_int(${node.label}, &val)) {`);
|
|
644
1197
|
if (node.onChangeExpr) {
|
|
645
1198
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
646
1199
|
}
|
|
@@ -649,10 +1202,11 @@ function emitInputInt(node, lines, indent) {
|
|
|
649
1202
|
}
|
|
650
1203
|
}
|
|
651
1204
|
function emitInputFloat(node, lines, indent) {
|
|
1205
|
+
emitLocComment(node.loc, 'InputFloat', lines, indent);
|
|
652
1206
|
if (node.stateVar) {
|
|
653
1207
|
lines.push(`${indent}{`);
|
|
654
1208
|
lines.push(`${indent}${INDENT}float val = ${node.stateVar}.get();`);
|
|
655
|
-
lines.push(`${indent}${INDENT}if (
|
|
1209
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::input_float(${node.label}, &val)) {`);
|
|
656
1210
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
657
1211
|
lines.push(`${indent}${INDENT}}`);
|
|
658
1212
|
lines.push(`${indent}}`);
|
|
@@ -660,7 +1214,7 @@ function emitInputFloat(node, lines, indent) {
|
|
|
660
1214
|
else if (node.valueExpr !== undefined) {
|
|
661
1215
|
lines.push(`${indent}{`);
|
|
662
1216
|
lines.push(`${indent}${INDENT}float val = ${node.valueExpr};`);
|
|
663
|
-
lines.push(`${indent}${INDENT}if (
|
|
1217
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::input_float(${node.label}, &val)) {`);
|
|
664
1218
|
if (node.onChangeExpr) {
|
|
665
1219
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
666
1220
|
}
|
|
@@ -669,16 +1223,18 @@ function emitInputFloat(node, lines, indent) {
|
|
|
669
1223
|
}
|
|
670
1224
|
}
|
|
671
1225
|
function emitColorEdit(node, lines, indent) {
|
|
1226
|
+
emitLocComment(node.loc, 'ColorEdit', lines, indent);
|
|
672
1227
|
if (node.stateVar) {
|
|
673
1228
|
lines.push(`${indent}{`);
|
|
674
1229
|
lines.push(`${indent}${INDENT}auto val = ${node.stateVar}.get();`);
|
|
675
|
-
lines.push(`${indent}${INDENT}if (
|
|
1230
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::color_edit(${node.label}, val.data())) {`);
|
|
676
1231
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
677
1232
|
lines.push(`${indent}${INDENT}}`);
|
|
678
1233
|
lines.push(`${indent}}`);
|
|
679
1234
|
}
|
|
680
1235
|
}
|
|
681
1236
|
function emitListBox(node, lines, indent) {
|
|
1237
|
+
emitLocComment(node.loc, 'ListBox', lines, indent);
|
|
682
1238
|
const itemsList = node.items.split(',').map(s => s.trim()).filter(s => s.length > 0);
|
|
683
1239
|
const count = itemsList.length;
|
|
684
1240
|
const varName = `listbox_items_${listBoxCounter++}`;
|
|
@@ -686,7 +1242,7 @@ function emitListBox(node, lines, indent) {
|
|
|
686
1242
|
lines.push(`${indent}{`);
|
|
687
1243
|
lines.push(`${indent}${INDENT}const char* ${varName}[] = {${itemsList.join(', ')}};`);
|
|
688
1244
|
lines.push(`${indent}${INDENT}int val = ${node.stateVar}.get();`);
|
|
689
|
-
lines.push(`${indent}${INDENT}if (
|
|
1245
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::list_box(${node.label}, &val, ${varName}, ${count})) {`);
|
|
690
1246
|
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
691
1247
|
lines.push(`${indent}${INDENT}}`);
|
|
692
1248
|
lines.push(`${indent}}`);
|
|
@@ -695,7 +1251,7 @@ function emitListBox(node, lines, indent) {
|
|
|
695
1251
|
lines.push(`${indent}{`);
|
|
696
1252
|
lines.push(`${indent}${INDENT}const char* ${varName}[] = {${itemsList.join(', ')}};`);
|
|
697
1253
|
lines.push(`${indent}${INDENT}int val = ${node.valueExpr};`);
|
|
698
|
-
lines.push(`${indent}${INDENT}if (
|
|
1254
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::list_box(${node.label}, &val, ${varName}, ${count})) {`);
|
|
699
1255
|
if (node.onChangeExpr) {
|
|
700
1256
|
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
701
1257
|
}
|
|
@@ -704,13 +1260,187 @@ function emitListBox(node, lines, indent) {
|
|
|
704
1260
|
}
|
|
705
1261
|
}
|
|
706
1262
|
function emitProgressBar(node, lines, indent) {
|
|
1263
|
+
emitLocComment(node.loc, 'ProgressBar', lines, indent);
|
|
707
1264
|
if (node.overlay) {
|
|
708
|
-
lines.push(`${indent}
|
|
1265
|
+
lines.push(`${indent}imx::renderer::progress_bar(${node.value}, ${node.overlay});`);
|
|
709
1266
|
}
|
|
710
1267
|
else {
|
|
711
|
-
lines.push(`${indent}
|
|
1268
|
+
lines.push(`${indent}imx::renderer::progress_bar(${node.value});`);
|
|
712
1269
|
}
|
|
713
1270
|
}
|
|
714
1271
|
function emitTooltip(node, lines, indent) {
|
|
715
|
-
|
|
1272
|
+
emitLocComment(node.loc, 'Tooltip', lines, indent);
|
|
1273
|
+
lines.push(`${indent}imx::renderer::tooltip(${node.text});`);
|
|
1274
|
+
}
|
|
1275
|
+
function emitBulletText(node, lines, indent) {
|
|
1276
|
+
emitLocComment(node.loc, 'BulletText', lines, indent);
|
|
1277
|
+
if (node.args.length === 0) {
|
|
1278
|
+
lines.push(`${indent}imx::renderer::bullet_text("${node.format}");`);
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
const fmtArgs = node.args.map(a => {
|
|
1282
|
+
if (a.startsWith('"'))
|
|
1283
|
+
return a;
|
|
1284
|
+
return `std::to_string(${a}).c_str()`;
|
|
1285
|
+
}).join(', ');
|
|
1286
|
+
lines.push(`${indent}imx::renderer::bullet_text("${node.format}", ${fmtArgs});`);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function emitLabelText(node, lines, indent) {
|
|
1290
|
+
emitLocComment(node.loc, 'LabelText', lines, indent);
|
|
1291
|
+
lines.push(`${indent}imx::renderer::label_text(${asCharPtr(node.label)}, ${asCharPtr(node.value)});`);
|
|
1292
|
+
}
|
|
1293
|
+
function emitSelectable(node, lines, indent) {
|
|
1294
|
+
emitLocComment(node.loc, 'Selectable', lines, indent);
|
|
1295
|
+
const label = asCharPtr(node.label);
|
|
1296
|
+
if (node.action.length > 0) {
|
|
1297
|
+
lines.push(`${indent}if (imx::renderer::selectable(${label}, ${node.selected})) {`);
|
|
1298
|
+
for (const stmt of node.action) {
|
|
1299
|
+
lines.push(`${indent}${INDENT}${stmt}`);
|
|
1300
|
+
}
|
|
1301
|
+
lines.push(`${indent}}`);
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
lines.push(`${indent}imx::renderer::selectable(${label}, ${node.selected});`);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function emitRadio(node, lines, indent) {
|
|
1308
|
+
emitLocComment(node.loc, 'Radio', lines, indent);
|
|
1309
|
+
const label = asCharPtr(node.label);
|
|
1310
|
+
if (node.stateVar) {
|
|
1311
|
+
lines.push(`${indent}{`);
|
|
1312
|
+
lines.push(`${indent}${INDENT}int val = ${node.stateVar}.get();`);
|
|
1313
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::radio(${label}, &val, ${node.index})) {`);
|
|
1314
|
+
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
1315
|
+
lines.push(`${indent}${INDENT}}`);
|
|
1316
|
+
lines.push(`${indent}}`);
|
|
1317
|
+
}
|
|
1318
|
+
else if (node.valueExpr !== undefined) {
|
|
1319
|
+
lines.push(`${indent}{`);
|
|
1320
|
+
lines.push(`${indent}${INDENT}int val = ${node.valueExpr};`);
|
|
1321
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::radio(${label}, &val, ${node.index})) {`);
|
|
1322
|
+
if (node.onChangeExpr) {
|
|
1323
|
+
lines.push(`${indent}${INDENT}${INDENT}${node.onChangeExpr};`);
|
|
1324
|
+
}
|
|
1325
|
+
lines.push(`${indent}${INDENT}}`);
|
|
1326
|
+
lines.push(`${indent}}`);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function emitInputTextMultiline(node, lines, indent) {
|
|
1330
|
+
emitLocComment(node.loc, 'InputTextMultiline', lines, indent);
|
|
1331
|
+
lines.push(`${indent}{`);
|
|
1332
|
+
const innerIndent = indent + INDENT;
|
|
1333
|
+
const styleVar = buildStyleVar(node.style, innerIndent, lines);
|
|
1334
|
+
lines.push(`${innerIndent}auto& buf = ctx.get_buffer(${node.bufferIndex});`);
|
|
1335
|
+
if (node.stateVar) {
|
|
1336
|
+
lines.push(`${innerIndent}buf.sync_from(${node.stateVar}.get());`);
|
|
1337
|
+
}
|
|
1338
|
+
const styleArg = styleVar ? `, ${styleVar}` : '';
|
|
1339
|
+
lines.push(`${innerIndent}if (imx::renderer::text_input_multiline(${node.label}, buf${styleArg})) {`);
|
|
1340
|
+
if (node.stateVar) {
|
|
1341
|
+
lines.push(`${innerIndent}${INDENT}${node.stateVar}.set(buf.value());`);
|
|
1342
|
+
}
|
|
1343
|
+
lines.push(`${innerIndent}}`);
|
|
1344
|
+
lines.push(`${indent}}`);
|
|
1345
|
+
}
|
|
1346
|
+
function emitColorPicker(node, lines, indent) {
|
|
1347
|
+
emitLocComment(node.loc, 'ColorPicker', lines, indent);
|
|
1348
|
+
if (node.stateVar) {
|
|
1349
|
+
lines.push(`${indent}{`);
|
|
1350
|
+
lines.push(`${indent}${INDENT}auto val = ${node.stateVar}.get();`);
|
|
1351
|
+
lines.push(`${indent}${INDENT}if (imx::renderer::color_picker(${node.label}, val.data())) {`);
|
|
1352
|
+
lines.push(`${indent}${INDENT}${INDENT}${node.stateVar}.set(val);`);
|
|
1353
|
+
lines.push(`${indent}${INDENT}}`);
|
|
1354
|
+
lines.push(`${indent}}`);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
function emitPlotLines(node, lines, indent) {
|
|
1358
|
+
emitLocComment(node.loc, 'PlotLines', lines, indent);
|
|
1359
|
+
const idx = plotCounter++;
|
|
1360
|
+
const varName = `_plot_${idx}`;
|
|
1361
|
+
const values = node.values.split(',').map(v => ensureFloatLiteral(v.trim()));
|
|
1362
|
+
const count = values.length;
|
|
1363
|
+
lines.push(`${indent}{`);
|
|
1364
|
+
const innerIndent = indent + INDENT;
|
|
1365
|
+
const styleVar = buildStyleVar(node.style, innerIndent, lines);
|
|
1366
|
+
lines.push(`${innerIndent}float ${varName}[] = {${values.join(', ')}};`);
|
|
1367
|
+
const overlay = node.overlay ? `, ${node.overlay}` : ', nullptr';
|
|
1368
|
+
const styleArg = styleVar ? `, ${styleVar}` : '';
|
|
1369
|
+
lines.push(`${innerIndent}imx::renderer::plot_lines(${node.label}, ${varName}, ${count}${overlay}${styleArg});`);
|
|
1370
|
+
lines.push(`${indent}}`);
|
|
1371
|
+
}
|
|
1372
|
+
function emitPlotHistogram(node, lines, indent) {
|
|
1373
|
+
emitLocComment(node.loc, 'PlotHistogram', lines, indent);
|
|
1374
|
+
const idx = plotCounter++;
|
|
1375
|
+
const varName = `_plot_${idx}`;
|
|
1376
|
+
const values = node.values.split(',').map(v => ensureFloatLiteral(v.trim()));
|
|
1377
|
+
const count = values.length;
|
|
1378
|
+
lines.push(`${indent}{`);
|
|
1379
|
+
const innerIndent = indent + INDENT;
|
|
1380
|
+
const styleVar = buildStyleVar(node.style, innerIndent, lines);
|
|
1381
|
+
lines.push(`${innerIndent}float ${varName}[] = {${values.join(', ')}};`);
|
|
1382
|
+
const overlay = node.overlay ? `, ${node.overlay}` : ', nullptr';
|
|
1383
|
+
const styleArg = styleVar ? `, ${styleVar}` : '';
|
|
1384
|
+
lines.push(`${innerIndent}imx::renderer::plot_histogram(${node.label}, ${varName}, ${count}${overlay}${styleArg});`);
|
|
1385
|
+
lines.push(`${indent}}`);
|
|
1386
|
+
}
|
|
1387
|
+
function emitImage(node, lines, indent) {
|
|
1388
|
+
emitLocComment(node.loc, 'Image', lines, indent);
|
|
1389
|
+
const width = node.width ? ensureFloatLiteral(node.width) : '0';
|
|
1390
|
+
const height = node.height ? ensureFloatLiteral(node.height) : '0';
|
|
1391
|
+
if (node.embed && node.embedKey) {
|
|
1392
|
+
// Embedded mode: reference the data from the .embed.h header
|
|
1393
|
+
lines.push(`${indent}imx::renderer::image_embedded("${node.embedKey}", ${node.embedKey}_data, ${node.embedKey}_size, ${width}, ${height});`);
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
// File mode: pass the path string
|
|
1397
|
+
lines.push(`${indent}imx::renderer::image(${node.src}, ${width}, ${height});`);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function emitDrawLine(node, lines, indent) {
|
|
1401
|
+
const p1Parts = node.p1.split(',').map((s) => emitFloat(s.trim()));
|
|
1402
|
+
const p2Parts = node.p2.split(',').map((s) => emitFloat(s.trim()));
|
|
1403
|
+
const color = emitImVec4(node.color);
|
|
1404
|
+
const thickness = emitFloat(node.thickness);
|
|
1405
|
+
lines.push(`${indent}imx::renderer::draw_line(${p1Parts.join(', ')}, ${p2Parts.join(', ')}, ${color}, ${thickness});`);
|
|
1406
|
+
}
|
|
1407
|
+
function emitDrawRect(node, lines, indent) {
|
|
1408
|
+
const minParts = node.min.split(',').map((s) => emitFloat(s.trim()));
|
|
1409
|
+
const maxParts = node.max.split(',').map((s) => emitFloat(s.trim()));
|
|
1410
|
+
const color = emitImVec4(node.color);
|
|
1411
|
+
const filled = node.filled;
|
|
1412
|
+
const thickness = emitFloat(node.thickness);
|
|
1413
|
+
const rounding = emitFloat(node.rounding);
|
|
1414
|
+
lines.push(`${indent}imx::renderer::draw_rect(${minParts.join(', ')}, ${maxParts.join(', ')}, ${color}, ${filled}, ${thickness}, ${rounding});`);
|
|
1415
|
+
}
|
|
1416
|
+
function emitDrawCircle(node, lines, indent) {
|
|
1417
|
+
const centerParts = node.center.split(',').map((s) => emitFloat(s.trim()));
|
|
1418
|
+
const radius = emitFloat(node.radius);
|
|
1419
|
+
const color = emitImVec4(node.color);
|
|
1420
|
+
const filled = node.filled;
|
|
1421
|
+
const thickness = emitFloat(node.thickness);
|
|
1422
|
+
lines.push(`${indent}imx::renderer::draw_circle(${centerParts.join(', ')}, ${radius}, ${color}, ${filled}, ${thickness});`);
|
|
1423
|
+
}
|
|
1424
|
+
function emitDrawText(node, lines, indent) {
|
|
1425
|
+
const posParts = node.pos.split(',').map((s) => emitFloat(s.trim()));
|
|
1426
|
+
const color = emitImVec4(node.color);
|
|
1427
|
+
const text = asCharPtr(node.text);
|
|
1428
|
+
lines.push(`${indent}imx::renderer::draw_text(${posParts.join(', ')}, ${color}, ${text});`);
|
|
1429
|
+
}
|
|
1430
|
+
function collectEmbedKeys(nodes) {
|
|
1431
|
+
const keys = [];
|
|
1432
|
+
for (const node of nodes) {
|
|
1433
|
+
if (node.kind === 'image' && node.embed && node.embedKey) {
|
|
1434
|
+
keys.push(node.embedKey);
|
|
1435
|
+
}
|
|
1436
|
+
else if (node.kind === 'conditional') {
|
|
1437
|
+
keys.push(...collectEmbedKeys(node.body));
|
|
1438
|
+
if (node.elseBody)
|
|
1439
|
+
keys.push(...collectEmbedKeys(node.elseBody));
|
|
1440
|
+
}
|
|
1441
|
+
else if (node.kind === 'list_map') {
|
|
1442
|
+
keys.push(...collectEmbedKeys(node.body));
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return keys;
|
|
716
1446
|
}
|