efx 0.6.1

Rust XML templating engine for building egui UIs with procedural macros
Documentation
# EFx

efx — declarative UI template engine in Rust
`efx!` is a procedural macro that transforms compact XML-like markup into method calls to your UI (e.g. wrappers over `egui/eframe`).

## Minimal example

```rust
use efx_core::doc_prelude::*;
use efx::*;

efx!(Ui::default(), r#"
    <Column>
        <Label>Hello</Label>
        <Separator/>
        <Row><Label>Row</Label></Row>
    </Column>
"#);
```
**Key Features 0.5**
- Tags: `Column`, `Row`, `Label`, `Separator`, `Button`.
- Insert expressions: `{expr}` within text.
- Escaping: `{{``{`, `}}``}`.
- Tag attributes are **parsed**.

---

### EFx Sandbox (local playground)

`efx-sandbox` is a helper binary crate kept in this repository. It’s used for manual testing of tags and as a “live” example of how to use the templating macro in a real `egui` app.

**Why use it**

* Quickly verify tag behavior in a native window (`eframe/egui`).
* Keep rich examples and “scenes” outside doctests (no test harness limitations).
* Demonstrate how `efx!` integrates with application state.

**Where it lives**

`/efx-sandbox`

This crate is part of the workspace and is **not published**.

**How to run**

```bash
cargo run -p efx-sandbox
```

> Make sure `eframe/egui` versions match those used by EFx (we pin `eframe = "0.32"` for `egui 0.32.x`).

**Minimal `main.rs` example**

```rust,ignore
use eframe::{egui, NativeOptions};
use efx::*;                    // the efx! macro
use efx_core::doc_prelude::*;  // convenient egui prelude

fn main() -> eframe::Result<()> {
    eframe::run_native(
        "EFx Sandbox",
        NativeOptions::default(),
        Box::new(|_cc| Box::new(App::default())),
    )
}

#[derive(Default)]
struct App {
    counter: i32,
    input: String,
}

impl eframe::App for App {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            // Header
            let _ = efx!(ui, r#"
                <Column gap="8">
                  <Label size="20" bold="true">EFx sandbox</Label>
                  <Separator/>
                </Column>
            "#);

            // Buttons returning Response
            ui.horizontal(|ui| {
                let inc = efx!(ui, r#"<Button tooltip="Increment">+1</Button>"#);
                if inc.clicked() { self.counter += 1; }

                let dec = efx!(ui, r#"<Button tooltip="Decrement">-1</Button>"#);
                if dec.clicked() { self.counter -= 1; }
            });

            // Dynamic text
            let _ = efx!(ui, r#"<Label>Counter: {self.counter}</Label>"#);

            // Text input
            let _ = efx!(ui, r#"<TextField value="self.input" hint="type here…"/>"#);

            // Scroll + links + styled buttons
            let _ = efx!(ui, r#"
                <ScrollArea axis="vertical" max_height="160" always_show="true" id="demo-log">
                  <Column gap="6">
                    <Label monospace="true">You typed: {self.input.clone()}</Label>
                    <Row gap="8">
                      <Hyperlink url="https://efxui.com" tooltip="Project site"/>
                      <Hyperlink url="help:about" open_external="false">About</Hyperlink>
                    </Row>
                    <Separator/>
                    <Row gap="10" wrap="true">
                      <Button fill="#333333AA" rounding="8">A</Button>
                      <Button frame="false">B</Button>
                      <Button min_width="100" tooltip="Wide">Wide</Button>
                    </Row>
                  </Column>
                </ScrollArea>
            "#);
        });
    }
}
```

**Tips**

* Keep several example “scenes” as `&'static str` and switch them via a `ComboBox` to test different tag sets.
* Prefer **snake\_case** attributes (`max_height`, `always_show`, `stroke_width`, …). If a tag supports kebab-case aliases, the tag’s section will mention it.
* Colors are `#RRGGBB` or `#RRGGBBAA` (short `#RGB/#RGBA` is not supported yet).

**Why sandbox instead of doctests**

Doctests are great for syntax and error messages, but `egui` requires a proper render loop (`Context::run()`), which doctests don’t provide. The sandbox runs a real app, while examples in this documentation are marked `rust,ignore` to avoid execution.

---

For more information, see the sections below: **Supported Tags** and **Syntax Guide**.