Architecture

Architecture documentation


Overview

saddle-pane is a debug/tweaking panel library for Bevy built entirely on Bevy's native UI system. It provides two APIs: a recommended Derive API and a lower-level Builder API.

Core Data Flow

Resource (game state)
    ↕ bidirectional sync (binding.rs)
PaneStore (central value store)
    ↕ sync systems (sync.rs)
Control Components (per-row ECS entities)
    ↕ interaction/display systems (controls/*.rs)
Bevy UI Nodes (visual elements)
Resource (game state)
    ↕ bidirectional sync (binding.rs)
PaneStore (central value store)
    ↕ sync systems (sync.rs)
Control Components (per-row ECS entities)
    ↕ interaction/display systems (controls/*.rs)
Bevy UI Nodes (visual elements)

Key Components

PanePlugin (plugin.rs)

Registers all core systems in three ordered system sets:

  1. PaneSystems::Interaction — widget events → control components (PostUpdate)
  2. PaneSystems::Sync — control components → PaneStore + events (PostUpdate)
  3. PaneSystems::Display — control components → UI updates (PostUpdate)

PaneStore (store.rs)

Central value store keyed by (pane_title, field_label). Stores PaneValue enum variants (Float, Bool, String, Color, Int, Custom). Tracks initial values for reset and a dirty set for external updates.

Derive Binding (binding.rs)

The PaneDerive trait (generated by the proc macro) provides typed read_field()/write_field() methods. RegisterPaneExt::register_pane::<T>() spawns the UI and adds bidirectional sync systems with feedback-loop prevention via DeriveSyncFlag.

Control Registry (registry.rs)

Plugin controls register via PaneControlRegistry. Each plugin provides:

  • id — control type identifier (e.g., "interval", "vector2")
  • spawn fn — builds UI hierarchy
  • build fn — registers systems
  • default_value fn — initial value factory

Built-in Controls (controls/)

ControlFileTrigger
Sliderslider.rs#[pane(slider)] or builder .slider()
Toggletoggle.rsbool field or .toggle()
Numbernumber.rsnumeric field or .number()
Texttext.rsString field or .text()
Selectselect.rs#[pane(select)] or .select()
Colorcolor.rs + color_picker.rs#[pane(color)] or .color()
Monitormonitor.rs#[pane(monitor)]
Buttonbutton.rs.button()
Separatorseparator.rs.separator()

Theme System (theme.rs)

PaneTheme resource with dark/light presets. PaneThemeOverride component for per-pane overrides. CSS variables defined in style/tokens.css.

Crate Structure

saddle-pane/              Main library (PanePlugin, derive, builder, controls)
├── crates/derive/        Proc macro (#[derive(Pane)])
├── crates/interval/      Range slider plugin
├── crates/vector2/       2D joystick plugin
├── crates/button-grid/   Button grid plugin
├── crates/radio-grid/    Radio/checkbox grid plugin
├── crates/bezier/        Bezier curve editor plugin
└── crates/file/          File browser plugin
saddle-pane/              Main library (PanePlugin, derive, builder, controls)
├── crates/derive/        Proc macro (#[derive(Pane)])
├── crates/interval/      Range slider plugin
├── crates/vector2/       2D joystick plugin
├── crates/button-grid/   Button grid plugin
├── crates/radio-grid/    Radio/checkbox grid plugin
├── crates/bezier/        Bezier curve editor plugin
└── crates/file/          File browser plugin

Styling

All styling is CSS-based via bevy_flair. Stylesheets are embedded at compile time using embedded_asset!. Design tokens in style/tokens.css define colors, spacing, and typography.