Skip to main content

Crate pane

Crate pane 

Source
Expand description

§pane_ui

A RON-driven, hot-reloadable UI library for wgpu.

Define your entire UI in a .ron file. No layout code, no style code, no wiring code. Edit the file while your app runs and the UI updates instantly — menus, styles, and shaders alike. Your game loop stays untouched.


§Modes

Choose the integration that fits your architecture:

ModeOwns window?GPU required?Use when
runPane IS the app
overlay / PaneOverlayCompositing onto your renderer
headless / PaneHeadlessTests, servers, CI

§Standalone

Pane owns the window and event loop entirely.

// Simplest — one line, no callbacks
pane::run("assets/menu.ron");

// With callback — handle actions and call write/toast each frame
pane::run_with("assets/menu.ron", |ui, action| {
    if let pane::PaneAction::Custom(ref id) = action {
        if id == "save" { ui.push_toast("Saved!", 2.0, 0.0, -400.0, 300.0, 60.0); }
    }
});

§Overlay

Composites onto your existing wgpu renderer. You keep your device, queue, and loop.

// Startup — pass Some(gilrs) to share your gamepad context, or None to auto-create one
let mut ui = pane::overlay("assets/hud.ron", &device, &queue, format, None);

// In your event loop
ui.handle_event(&event, window_width, window_height);

// In your render pass — call after your own draw commands
for action in ui.draw(&mut encoder, &view, pw, ph) {
    match action {
        pane::PaneAction::Custom(tag)         => println!("button: {tag}"),
        pane::PaneAction::Slider(id, val)     => println!("{id} = {val:.2}"),
        pane::PaneAction::Toggle(id, checked) => println!("{id} = {checked}"),
        pane::PaneAction::Quit                => std::process::exit(0),
        _                                     => {}
    }
}

§Headless

No GPU, no window. Useful for testing UI logic in CI or driving a server-side state machine.

let mut menu = pane::headless("assets/menu.ron");
menu.press("play_button");
let actions = menu.update(1.0 / 60.0);

§Coordinate System

Pane uses a centered, resolution-independent coordinate system:

  • (0.0, 0.0) is always the center of the screen
  • The screen height is always 1080 units regardless of actual resolution
  • Width scales proportionally — widescreen just adds space on the sides
  • Positive Y is downward

A button at x: -170.0, y: 0.0 is centered vertically on every screen. You never write resolution-specific layout.


§Widgets

15 built-in widget types, all defined in RON:

Button · Toggle · Slider · TextBox · Dropdown · RadioGroup · ScrollList · ScrollPane · Bar · Popout · Label · Divider · Image · ProgressBar · Tabs

Plus Actor — a non-interactive animated element that follows the cursor or moves to programmatic targets.


§Actions

PaneOverlay::draw and PaneHeadless::update return a vector of PaneAction each frame. Match on these to drive your application:

for action in actions {
    match action {
        PaneAction::Custom(tag)              => { /* button pressed */ }
        PaneAction::Slider(id, value)        => { /* slider moved */ }
        PaneAction::Toggle(id, checked)      => { /* toggle flipped */ }
        PaneAction::Dropdown(id, idx, label) => { /* selection changed */ }
        PaneAction::Radio(id, idx, label)    => { /* selection changed */ }
        PaneAction::TextChanged(id, text)    => { /* keystroke */ }
        PaneAction::TextSubmitted(id, text)  => { /* enter pressed */ }
        PaneAction::SwitchRoot(name)         => { /* root changed */ }
        PaneAction::Quit                     => { std::process::exit(0); }
    }
}

§Runtime API

Read or write any widget’s value from code at any time. Useful for syncing UI to app state on startup, or driving a progress bar mid-game.

// Read — returns the UiItem definition + current visual state
if let Some((pane::api::UiItem::Slider(s), _)) = ui.read("volume") {
    println!("volume = {}", s.value);
}

// Write — silently sets value without firing PaneActions
ui.write("volume",  &WriteValue::Slider(0.8));
ui.write("mute",    &WriteValue::Toggle(true));
ui.write("name",    &WriteValue::Text("Player 1".into()));
ui.write("quality", &WriteValue::Selected(2));

// Toast notification (also triggerable directly from RON via on_press: Toast(...))
ui.push_toast("Settings saved", 2.0, 0.0, -400.0, 300.0, 60.0);

§Hot Reload

Set hot_reload: true in your root .ron file. While your app runs:

  • Save a menu .ron → layout and behaviour update instantly
  • Save a style .ron → visual design updates instantly
  • Save a .wgsl shader → shader recompiles and updates instantly

No restart. No recompile. Just save.

Requires the dev feature for runtime style loading:

pane_ui = { version = "0.1", features = ["dev"] }

§Controller Support

All widgets support gamepad navigation with no extra configuration:

  • D-pad / left stick — moves focus to the nearest widget in that direction
  • South (A / Cross) — confirms; activates focused widget
  • East (B / Circle) — cancels; closes popout panels
  • Sliders — left/right adjusts value continuously
  • Scroll lists & panes — focus scrolls to keep the active item visible
  • Popouts — confirm opens, up/down navigates inside, cancel closes

Pass your existing gilrs::Gilrs to share it with your game:

// Share your game's gilrs context
let ui = pane::overlay("assets/hud.ron", &device, &queue, format, Some(gilrs));

// Or let pane_ui manage its own
let ui = pane::overlay("assets/hud.ron", &device, &queue, format, None);

§Custom Styles & Shaders

Drop .ron style files in your style directory and .wgsl shaders in your shader directory. Reference them by filename (without extension). Users and modders can reskin your entire UI without touching your code.

(
    shader_dirs: ["shaders"],
    style_dirs:  ["styles"],
    roots: [
        (
            name: "main_menu",
            buttons: [
                (
                    id: "my_btn",
                    style: "my_custom_style",  // loads styles/my_custom_style.ron
                    on_press: Custom("clicked"),
                    // ...
                ),
            ],
        ),
    ],
)

6 built-in styles are always available: frosted_glass · retro · plain · glass_pill · emboss · sharp_outline


§Debugging

Enable debug logging to print every internal message to stdout:

// Overlay or headless
ui.set_debug(true);
// Standalone — set the environment variable before running
PANE_DEBUG=1 cargo run

Re-exports§

pub use api::PaneAction;
pub use api::PaneHeadless;
pub use api::PaneOverlay;
pub use api::StandaloneHandle;
pub use api::WriteValue;
pub use api::headless;
pub use api::overlay;
pub use api::run;
pub use api::run_with;
pub use gilrs;

Modules§

api
Pane
build
Code-side widget construction.
draw
input
loader