guido 0.12.0

A reactive Rust GUI library using wgpu for Wayland layer shell widgets
Documentation
# App Lifecycle

Guido applications can programmatically quit or restart. `App::run()` returns an `ExitReason` so the caller knows why the loop exited: `Quit`, `Restart`, or `Error(PlatformError)` when the platform layer fails (no Wayland session, a compositor without layer-shell support such as GNOME, a lost connection, or no GPU to draw a surface with) — these conditions are reported instead of panicking.

## Quitting

Call `quit_app()` to request a clean shutdown:

```rust
# extern crate guido;
# fn main() {
use guido::prelude::*;

container()
    .padding([8.0, 16.0])
    .background(Color::rgb(0.3, 0.3, 0.4))
    .when_hovered(|s| s.lighter(0.1))
    .on_click(|| quit_app())
    .child(text("Quit"))
# ;
# }
```

The current `App::run()` loop exits and returns `ExitReason::Quit`.

## Outliving the Last Surface

Closing the last surface also ends the loop with `ExitReason::Quit` — whether the app closed it, the compositor did, or its monitor was unplugged. A resident program that only sometimes has something to show — a polkit agent, a notification daemon, an on-screen display — turns that off with `quit_on_last_surface(false)`:

```rust,no_run
# extern crate guido;
use guido::prelude::*;

fn main() {
    App::new().quit_on_last_surface(false).run(|_app| {
        let asked = create_signal(false);
        let mut dialog: Option<SurfaceHandle> = None;
        create_effect(move || {
            if asked.get() {
                dialog = Some(spawn_surface(SurfaceConfig::new(), || text("Authenticate")));
            } else if let Some(open) = dialog.take() {
                open.close();
            }
        });
        // Something outside the UI — a D-Bus request, say — writes `asked`.
    });
}
```

With it off, the app idles when its last surface closes, exactly as one that has not spawned a surface yet does, and ends through `quit_app()` or a lost connection.

An app holds a GPU device only while it has something to draw. The device is made when the first surface needs one, and let go 30 seconds after the last one closes — soon enough that a resident program between windows costs no GPU memory, late enough that a dialog closed and reopened straight away does not wait for a new one.

## Restarting

Call `restart_app()` to request a restart. The loop exits and returns `ExitReason::Restart`, letting the caller re-create the app:

```rust
# extern crate guido;
# use guido::prelude::*;
# fn main() {
container()
    .on_click(|| restart_app())
    .child(text("Restart"))
# ;
# }
```

### Restart Loop

Use a loop in `main()` to support restart:

```rust,no_run
# extern crate guido;
# fn build_ui() -> Container { container() }
use guido::prelude::*;

fn main() {
    loop {
        let reason = App::new().run(|app| {
            app.add_surface(
                SurfaceConfig::new()
                    .height(32)
                    .anchor(Anchor::TOP | Anchor::LEFT | Anchor::RIGHT)
                    .layer(Layer::Top)
                    .namespace("my-bar")
                    .background_color(Color::rgb(0.1, 0.1, 0.15)),
                || build_ui(),
            );
        });

        match reason {
            ExitReason::Quit => break,
            ExitReason::Restart => continue,
            ExitReason::Error(e) => {
                eprintln!("guido could not run: {e}");
                break;
            }
        }
    }
}
```

This is useful for reloading configuration, switching themes, or resetting application state.

## Calling from Background Tasks

Both `quit_app()` and `restart_app()` are `Send` — they work from any thread, including background services:

```rust,ignore
# // not compiled: the task body is tokio's, and the book's samples are
# // compiled without it.
# extern crate guido;
# use guido::prelude::*;
# fn main() {
create_task(move |ctx| async move {
    loop {
        tokio::select! {
            _ = watch_config_file() => {
                // Config changed — trigger restart
                restart_app();
                break;
            }
            _ = tokio::time::sleep(Duration::from_millis(50)) => {
                if !ctx.is_running() { break; }
            }
        }
    }
});
# ;
# }
```

## API Reference

### ExitReason

```rust,ignore
# // not compiled: a listing of a type the crate owns — a copy of it declared
# // here would compile without checking the original.
pub enum ExitReason {
    /// Normal exit: `quit_app()`, or the last surface closing.
    Quit,
    /// Restart requested. The caller should re-create `App` and run again.
    Restart,
    /// The platform layer failed: no Wayland session, a compositor without
    /// layer shell, a lost connection, or no GPU to draw a surface with.
    Error(PlatformError),
}
```

### Functions

```rust,ignore
# // not compiled: a signature listing — these declarations have no bodies.
/// Request a clean application quit.
/// App::run() will return ExitReason::Quit.
pub fn quit_app();

/// Request a clean application restart.
/// App::run() will return ExitReason::Restart.
/// Call from any thread — uses an atomic + ping to wake the event loop.
pub fn restart_app();
```

### App::run

```rust,ignore
# // not compiled: a signature listing — these declarations have no bodies.
impl App {
    /// Run the application. Returns the reason the loop exited.
    pub fn run(self, setup: impl FnOnce(&mut Self)) -> ExitReason;
}
```