xui 0.1.0

Declarative UI toolkit for Rust — an ergonomic layer over GPUI
Documentation
# XUI

**Declarative UI toolkit for Rust — an ergonomic layer over [GPUI](https://crates.io/crates/gpui).**

XUI (Xi UI) cuts the boilerplate of GPUI applications down to a couple of attributes and a
JSX‑like `ui!` DSL, while keeping the full power of GPUI one import away through the
pinned, re‑exported `xui::gpui`.

> [!WARNING] **Early development.** The API is still evolving and may change without notice.

## Badges

[![Crates.io](https://img.shields.io/crates/v/xui.svg)](https://crates.io/crates/xui)
[![Downloads](https://img.shields.io/crates/d/xui.svg)](https://crates.io/crates/xui)
[![License](https://img.shields.io/crates/l/xui.svg)](https://crates.io/crates/xui)
[![Documentation](https://docs.rs/xui/badge.svg)](https://docs.rs/xui)

## Features

- **`#[app]`** — declare an application struct and get a fully wired `fn main()`:
  GPUI application, window creation and options, and state setup.
- **`ui!`** — a declarative, JSX‑like DSL that compiles straight to GPUI builder chains.
- **`#[render]`** — write `Render` implementations as plain functions.
- **`#[mutex]` / `#[async_mutex]`** — wrap struct fields in `std::sync::Mutex` /
  `tokio::sync::Mutex` with a single attribute.
- **`read_global!`** — one‑liner access to GPUI globals.
- **Batteries included** — a `prelude` with the most used GPUI items, ready‑made color
  constants and sizing helpers.

## Requirements

- Rust **1.88+** (edition 2024).
- `tokio` in your dependencies only if you use `#[async_mutex]`.

## Quick start

```toml
[dependencies]
xui = "0.1.0"   # or a git dependency
```

A complete counter application (a runnable version lives in [`example/`](example/)):

```rust
use xui::gpui::div;
use xui::prelude::*;

// Application state, stored as a GPUI global.
#[derive(Default)]
struct Counter {
    value: i32,
}
// `gpui` re-exported into the prelude
impl gpui::Global for Counter {}

// The application entry point. `#[app]` generates `fn main()` for you.
#[app(title = "Counter", size = (500, 400))]
struct App {}

impl App {
    fn new() -> Self { Self {} }

    // Called once at startup, before the window is opened.
    fn setup(cx: &mut xui::gpui::App) {
        cx.default_global::<Counter>();
    }
}

// Becomes `impl xui::gpui::Render for App`.
#[render]
fn App(_window: (), cx: ()) {
    let value = cx.read_global::<Counter, _>(|counter, _| counter.value);

    ui! {
        div {
            flex; flex_col; items_center; justify_center; size_full;
            gap: rems(1.5);
            bg: hsla(0.6, 0.1, 0.95, 1.0);

            div {
                text_size: rems(2.0);
                font_weight: FontWeight::BOLD;
                { format!("Counter: {value}") }
            }

            div {
                flex; flex_row; gap: rems(1.0);

                div {
                    bg: RED; text_color: WHITE;
                    px: rems(1.5); py: rems(0.5);
                    rounded: rems(0.25); cursor_pointer;
                    on_click: |_event, _window, cx| {
                        cx.update_global::<Counter, _>(|counter, _| counter.value -= 1);
                    };
                    "-"
                }

                div {
                    bg: GREEN; text_color: WHITE;
                    px: rems(1.5); py: rems(0.5);
                    rounded: rems(0.25); cursor_pointer;
                    on_click: |_event, _window, cx| {
                        cx.update_global::<Counter, _>(|counter, _| counter.value += 1);
                    };
                    "+"
                }
            }
        }
    }
}
```

## The `#[app]` macro

Applied to a struct, `#[app]` turns it into the program entry point. It emits the
struct unchanged and generates a `fn main()` that:

1. creates the GPUI `Application`,
2. calls `YourStruct::setup(cx)` so you can register globals and services,
3. opens a window configured by the attribute arguments,
4. instantiates the root view via `YourStruct::new()`.

The struct **must not be generic** and must provide:

```rust
impl App {
    fn new() -> Self { ... }                        // constructs the root view
    fn setup(cx: &mut xui::gpui::App) { ... }       // one-time initialization
}
```

### Window options

| Argument                    | Example                        | Description                                  |
| --------------------------- | ------------------------------ | -------------------------------------------- |
| `title`                     | `title = "My App"`             | Window title.                                |
| `size`                      | `size = (800, 600)`            | Initial window size in pixels.               |
| `min_size`                  | `min_size = (320, 240)`        | Minimum window size in pixels.               |
| `resizable` / `movable`     | `resizable = true`             | Standard window behavior flags.              |
| `decorations`               | `decorations = server`         | `server` or `client` window decorations.     |
| `background`                | `background = blurred`         | `opaque`, `transparent` or `blurred`.        |
| `focus`                     | `focus = true`                 | Whether the window receives focus on open.   |

All arguments are optional and can be combined freely:

```rust
#[app(
    title = "My App",
    size = (800, 600),
    min_size = (320, 240),
    resizable = true,
    background = transparent,
)]
struct App { /* fields become your root view's state */ }
```

Fields annotated with `#[mutex]` / `#[async_mutex]` inside an `#[app]` struct are
automatically wrapped in the corresponding mutex type (see [State helpers](#state-helpers)).

## The `ui!` DSL

`ui!` builds GPUI element trees with a declarative syntax. It is re‑exported by the prelude.

```rust
ui! {
    div {
        // flags - zero-argument builder calls
        flex; flex_col; size_full; cursor_pointer;

        // setters - `name: value;` becomes `.name(value)`
        gap: rems(1.0);
        bg: BLUE;

        // handlers - closures are passed through as-is
        on_click: |_event, window, cx| { /* ... */ };
        // ...or with the arrow form, for visual separation
        on_mouse_down => |_event, window, cx| { /* ... */ };

        // children
        "plain text becomes a child element";
        { format!("so does any Rust expression: {}", 42) }
        div { "nested elements work as expected" }
    }
}
```

How it maps to GPUI:

| DSL construct                    | Generated code                    |
| -------------------------------- | --------------------------------- |
| `element { ... }`                | `element()...`                    |
| `flag;`                          | `.flag()`                         |
| `name: expr;`                    | `.name(expr)`                     |
| `name => expr;`                  | `.name(expr)`                     |
| `"literal"`                      | `xui::gpui::div().child("literal")` |
| `{ expr }`                       | `expr` (spliced in as a child)    |
| nested `element { ... }`         | `.child(element()...)`            |

Notes:

- Element names are **paths resolved in your scope** — import what you need
  (`use xui::gpui::div;`), and any custom element constructor works too
  (`my_widgets::card { ... }`).
- Properties must be terminated with `;`.
- If the block contains **multiple root nodes**, they are automatically wrapped in a
  `div().flex().flex_col()` container; an **empty** block evaluates to `()`.

## The `#[render]` macro

`#[render]` converts a free function into an `impl xui::gpui::Render` for the type with
the **same name as the function**. The function itself is consumed — only the impl is
emitted.

```rust
#[render]
fn App(window: (), cx: ()) {
    ui! { div { "hello" } }
}
```

- The function must take **0 or 2 parameters**; their *names* become the `window` and
  `cx` bindings inside the generated `render` method, while their *types are ignored*
  (write placeholders, e.g. `()`).
- With 0 parameters, `window`/`cx` are simply unavailable in the body.

## State helpers

### `#[mutex]` and `#[async_mutex]`

Field attributes that rewrite the field's type into a mutex:

```rust
#[derive(Default)]
#[mutex] // for `#[mutex]` working in the struct itself
#[async_mutex] // for `#[async_mutex]` working in the struct itself
struct Model {
    #[mutex]        // -> std::sync::Mutex<std::collections::HashMap<String, i32>>
    cache: std::collections::HashMap<String, i32>,

    #[async_mutex]  // -> tokio::sync::Mutex<Option<String>> (requires `tokio`)
    token: Option<String>,
}
```

They work both as standalone attributes on any struct with named fields and inside
`#[app]` structs, where `#[app]` applies them for you.

### `read_global!`

A shortcut for reading GPUI globals, exported at the crate root:

```rust
// Whole value (closure form):
let counter = xui::read_global!(cx, Counter);

// Project a field / compute something (the global is bound as `value`):
let current = xui::read_global!(cx, Counter; value.value);
```

## Prelude and utilities

`use xui::prelude::*;` brings into scope:

- all procedural macros, including `ui!` (`app`, `render`, `mutex`, `async_mutex`, ...);
- color constants: `BLACK`, `WHITE`, `RED`, `GREEN`, `BLUE`, `YELLOW`;
- sizing helpers: `px`, `rems` (also in `xui::sizes`);
- `gpui::prelude::*` plus `hsla`, `rgba`, `FontWeight` and `Stateful`;
- `gpui` itself, pinned by `xui`.

Anything else is reachable through the re‑exported, version‑pinned GPUI: `xui::gpui`.
Because XUI locks its own GPUI version and re‑exports it, you should **not** add
`gpui` as a direct dependency — always use `xui::gpui` to guarantee a single,
consistent version across your app.

## License

Licensed under either of [MIT](LICENSE-MIT) or [Apache License, Version 2.0](LICENSE-APACHE) at your option.

---

Made with ❤️ for Rust community