#![allow(unused)]
use std::path::Path;
const MAX_DEPTH: usize = 7;
#[cfg(feature = "backend")]
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not set");
let dst = Path::new(&out_dir).join("a2ui_generated.slint");
let generated = generate();
std::fs::write(&dst, generated).expect("write generated .slint");
slint_build::compile(dst.to_str().expect("utf8 path"))
.expect("Slint UI compilation failed");
}
#[cfg(not(feature = "backend"))]
fn main() {}
fn generate() -> String {
let mut out = String::new();
out.push_str("// AUTO-GENERATED by crates/slint/build.rs — do not edit.\n");
out.push_str("// Bounded-depth unrolling of the A2UI component tree (Slint cannot recurse).\n\n");
out.push_str(
"struct LiveNode {\n\
\x20 id: string,\n\
\x20 kind: string,\n\
\x20 text: string,\n\
\x20 label: string,\n\
\x20 variant: string,\n\
\x20 checked: bool,\n\
\x20 number: float,\n\
\x20 extra: string,\n\
\x20 focused: bool,\n\
\x20 children: [int],\n\
}\n\n",
);
out.push_str(
"struct SampleEntry {\n\
\x20 name: string,\n\
}\n\n",
);
out.push_str(
"global Events {\n\
\x20 // Fired when an interactive node is activated (button press, etc.).\n\
\x20 // Carries the node's A2UI id so Rust can dispatch its handle_event.\n\
\x20 callback activate(string);\n\
}\n\n\
export { Events }\n\n",
);
for k in 0..=MAX_DEPTH {
let child = if k == 0 { None } else { Some(format!("Node{}", k - 1)) };
out.push_str(&node_body(k, child.as_deref()));
out.push('\n');
}
out.push_str(&format!(
"export component Surface inherits Window {{\n\
\x20 title: \"A2UI Slint Gallery\";\n\
\x20 preferred-width: 1000px;\n\
\x20 preferred-height: 700px;\n\
\x20 in property <[LiveNode]> nodes;\n\
\x20 in property <[SampleEntry]> samples;\n\
\x20 in property <int> selected-sample;\n\
\x20 callback select-sample(int);\n\
\n\
\x20 HorizontalLayout {{\n\
\x20 // Left: sample browser.\n\
\x20 Rectangle {{\n\
\x20 width: 220px;\n\
\x20 background: #f5f5f5;\n\
\x20 VerticalLayout {{\n\
\x20 padding: 8px;\n\
\x20 spacing: 6px;\n\
\x20 Text {{ text: \"Samples\"; font-weight: 800; }}\n\
\x20 Flickable {{\n\
\x20 VerticalLayout {{\n\
\x20 for s[i] in root.samples : Rectangle {{\n\
\x20 height: 28px;\n\
\x20 background: i == root.selected-sample ? #2563eb : transparent;\n\
\x20 HorizontalLayout {{\n\
\x20 padding-left: 8px;\n\
\x20 Text {{\n\
\x20 text: s.name;\n\
\x20 color: i == root.selected-sample ? #ffffff : #222222;\n\
\x20 vertical-alignment: center;\n\
\x20 }}\n\
\x20 }}\n\
\x20 TouchArea {{ clicked => {{ root.select-sample(i); }} }}\n\
\x20 }}\n\
\x20 }}\n\
\x20 }}\n\
\x20 }}\n\
\x20 }}\n\
\x20 // Divider.\n\
\x20 Rectangle {{ width: 1px; background: #ddd; }}\n\
\x20 // Right: the rendered A2UI surface.\n\
\x20 Node{MAX_DEPTH} {{ all: nodes; idx: 0; }}\n\
\x20 }}\n\
}}\n",
));
out
}
fn node_body(k: usize, child_comp: Option<&str>) -> String {
let name = format!("Node{k}");
let render_children = match child_comp {
Some(c) => format!("for c in me.children : {c} {{ all: all; idx: c; }}"),
None => "Text { text: \"…\"; color: #888; }".to_string(),
};
format!(
"component {name} inherits Rectangle {{\n\
\x20 in property <[LiveNode]> all;\n\
\x20 in property <int> idx;\n\
\x20 property <LiveNode> me: all[idx];\n\
\n\
\x20 // Column — vertical stack.\n\
\x20 if me.kind == \"Column\" : VerticalLayout {{\n\
\x20 spacing: 4px;\n\
\x20 {render_children}\n\
\x20 }}\n\
\n\
\x20 // Row — horizontal stack.\n\
\x20 if me.kind == \"Row\" : HorizontalLayout {{\n\
\x20 spacing: 4px;\n\
\x20 {render_children}\n\
\x20 }}\n\
\n\
\x20 // Card — bordered panel wrapping one child.\n\
\x20 if me.kind == \"Card\" : Rectangle {{\n\
\x20 background: #fff;\n\
\x20 border-radius: 8px;\n\
\x20 border-width: 1px;\n\
\x20 border-color: #d0d0d0;\n\
\x20 VerticalLayout {{ padding: 10px; {render_children} }}\n\
\x20 }}\n\
\n\
\x20 // Text — styled paragraph; variant selects heading/caption styles.\n\
\x20 if me.kind == \"Text\" : Text {{\n\
\x20 text: me.text;\n\
\x20 font-weight: me.variant == \"h1\" || me.variant == \"h2\" || me.variant == \"h3\" ? 800 : 400;\n\
\x20 color: me.focused ? #1d4ed8 : #111;\n\
\x20 }}\n\
\n\
\x20 // Button — labeled press target; child node is its label.\n\
\x20 if me.kind == \"Button\" : Rectangle {{\n\
\x20 background: me.variant == \"primary\" ? #2563eb : #f3f4f6;\n\
\x20 border-radius: 4px;\n\
\x20 border-width: me.focused ? 2px : 0px;\n\
\x20 border-color: #1d4ed8;\n\
\x20 HorizontalLayout {{ padding: 6px; {render_children} }}\n\
\x20 TouchArea {{ clicked => {{ Events.activate(me.id); }} }}\n\
\x20 }}\n\
\n\
\x20 // TextField — labeled input showing its bound value; focus ring when focused.\n\
\x20 if me.kind == \"TextField\" : Rectangle {{\n\
\x20 border-width: 1px;\n\
\x20 border-color: me.focused ? #eab308 : #ccc;\n\
\x20 border-radius: 4px;\n\
\x20 VerticalLayout {{\n\
\x20 padding: 4px;\n\
\x20 Text {{ text: me.label; color: #666; font-size: 11px; }}\n\
\x20 Text {{ text: me.text; }}\n\
\x20 }}\n\
\x20 }}\n\
\n\
\x20 // Divider — thin full-width horizontal rule.\n\
\x20 if me.kind == \"Divider\" : Rectangle {{\n\
\x20 height: 1px;\n\
\x20 background: #d0d0d0;\n\
\x20 }}\n\
\n\
\x20 // List — vertical stack of children (same layout as Column).\n\
\x20 if me.kind == \"List\" : VerticalLayout {{\n\
\x20 spacing: 2px;\n\
\x20 {render_children}\n\
\x20 }}\n\
\n\
\x20 // CheckBox — indicator + label; Enter toggles via core dispatch.\n\
\x20 if me.kind == \"CheckBox\" : HorizontalLayout {{\n\
\x20 spacing: 6px;\n\
\x20 Text {{ text: me.checked ? \"\u{2611}\" : \"\u{2610}\"; }}\n\
\x20 Text {{ text: me.text; }}\n\
\x20 TouchArea {{ clicked => {{ Events.activate(me.id); }} }}\n\
\x20 }}\n\
\n\
\x20 // Slider — display-only track showing the current value.\n\
\x20 if me.kind == \"Slider\" : HorizontalLayout {{\n\
\x20 Text {{ text: \"\u{25ae}\u{2500}\u{2500} (slider) \" + me.number; }}\n\
\x20 }}\n\
\n\
\x20 // Icon — labeled box (no icon font available).\n\
\x20 if me.kind == \"Icon\" : Rectangle {{\n\
\x20 background: #f3f4f6;\n\
\x20 border-width: 1px; border-color: #ddd;\n\
\x20 HorizontalLayout {{ padding: 4px; Text {{ text: \"[icon: \" + me.extra + \"]\"; color: #666; }} }}\n\
\x20 }}\n\
\n\
\x20 // Tabs — active index label + active child panel.\n\
\x20 if me.kind == \"Tabs\" : VerticalLayout {{\n\
\x20 Text {{ text: \"Tabs (active: \" + me.number + \")\"; color: #666; font-size: 11px; }}\n\
\x20 {render_children}\n\
\x20 }}\n\
\n\
\x20 // Modal — elevated panel; renders nothing when closed.\n\
\x20 if me.kind == \"Modal\" && me.checked : Rectangle {{\n\
\x20 background: #fff;\n\
\x20 border-radius: 8px;\n\
\x20 border-width: 2px; border-color: #bbb;\n\
\x20 drop-shadow-blur: 6px; drop-shadow-color: #0003;\n\
\x20 VerticalLayout {{ padding: 12px; {render_children} }}\n\
\x20 }}\n\
\n\
\x20 // ChoicePicker — labeled box (display-only).\n\
\x20 if me.kind == \"ChoicePicker\" : Rectangle {{\n\
\x20 background: #fafafa;\n\
\x20 border-width: 1px; border-color: #ddd;\n\
\x20 HorizontalLayout {{ padding: 6px; Text {{ text: me.label; }} }}\n\
\x20 }}\n\
\n\
\x20 // DateTimeInput — bordered field showing label + datetime value.\n\
\x20 if me.kind == \"DateTimeInput\" : Rectangle {{\n\
\x20 border-width: 1px; border-color: me.focused ? #eab308 : #ccc;\n\
\x20 border-radius: 4px;\n\
\x20 VerticalLayout {{\n\
\x20 padding: 4px;\n\
\x20 Text {{ text: me.label; color: #666; font-size: 11px; }}\n\
\x20 Text {{ text: me.extra; }}\n\
\x20 }}\n\
\x20 }}\n\
\n\
\x20 // Image / Video / AudioPlayer — labeled placeholders (bytes not carried).\n\
\x20 if me.kind == \"Image\" : Rectangle {{\n\
\x20 background: #f3f4f6; border-width: 1px; border-color: #ddd;\n\
\x20 HorizontalLayout {{ padding: 6px; Text {{ text: \"[Image: \" + me.extra + \"]\"; color: #666; }} }}\n\
\x20 }}\n\
\x20 if me.kind == \"Video\" : Rectangle {{\n\
\x20 background: #f3f4f6; border-width: 1px; border-color: #ddd;\n\
\x20 HorizontalLayout {{ padding: 6px; Text {{ text: \"[Video: \" + me.extra + \"]\"; color: #666; }} }}\n\
\x20 }}\n\
\x20 if me.kind == \"AudioPlayer\" : Rectangle {{\n\
\x20 background: #f3f4f6; border-width: 1px; border-color: #ddd;\n\
\x20 HorizontalLayout {{ padding: 6px; Text {{ text: \"\u{25b6} [Audio: \" + me.extra + \"]\"; color: #666; }} }}\n\
\x20 }}\n\
\n\
\x20 // Unknown / not-yet-implemented kind — show the kind name so the tree is visible.\n\
\x20 if me.kind != \"Column\" && me.kind != \"Row\" && me.kind != \"Card\" &&\n\
\x20 me.kind != \"Text\" && me.kind != \"Button\" && me.kind != \"TextField\" &&\n\
\x20 me.kind != \"Divider\" && me.kind != \"List\" && me.kind != \"CheckBox\" &&\n\
\x20 me.kind != \"Slider\" && me.kind != \"Icon\" && me.kind != \"Tabs\" &&\n\
\x20 me.kind != \"Modal\" && me.kind != \"ChoicePicker\" && me.kind != \"DateTimeInput\" &&\n\
\x20 me.kind != \"Image\" && me.kind != \"Video\" && me.kind != \"AudioPlayer\" :\n\
\x20 Rectangle {{\n\
\x20 background: #fafafa;\n\
\x20 border-width: 1px; border-color: #ddd;\n\
\x20 VerticalLayout {{ padding: 6px; Text {{ text: \"[\" + me.kind + \"]\"; color: #888; {render_children} }} }}\n\
\x20 }}\n\
}}\n",
)
}