pane/lib.rs
1//! # `pane_ui`
2//!
3//! A RON-driven, hot-reloadable UI library for [wgpu](https://github.com/gfx-rs/wgpu).
4//!
5//! Define your entire UI in a `.ron` file. No layout code, no style code, no wiring code.
6//! Edit the file while your app runs and the UI updates instantly — menus, styles, and
7//! shaders alike. Your game loop stays untouched.
8//!
9//! ---
10//!
11//! ## Modes
12//!
13//! Choose the integration that fits your architecture:
14//!
15//! | Mode | Owns window? | GPU required? | Use when |
16//! |------|:---:|:---:|---|
17//! | [`run`] | ✅ | ✅ | Pane IS the app |
18//! | [`overlay`] / [`PaneOverlay`] | ❌ | ✅ | Compositing onto your renderer |
19//! | [`headless`] / [`PaneHeadless`] | ❌ | ❌ | Tests, servers, CI |
20//!
21//! ### Standalone
22//!
23//! Pane owns the window and event loop entirely.
24//!
25//! ```no_run
26//! // Simplest — one line, no callbacks
27//! pane::run("assets/menu.ron");
28//!
29//! // With callback — handle actions and call write/toast each frame
30//! pane::run_with("assets/menu.ron", |ui, action| {
31//! if let pane::PaneAction::Custom(ref id) = action {
32//! if id == "save" { ui.push_toast("Saved!", 2.0, 0.0, -400.0, 300.0, 60.0); }
33//! }
34//! });
35//! ```
36//!
37//! ### Overlay
38//!
39//! Composites onto your existing wgpu renderer. You keep your device, queue, and loop.
40//!
41//! ```ignore
42//! // Startup — pass Some(gilrs) to share your gamepad context, or None to auto-create one
43//! let mut ui = pane::overlay("assets/hud.ron", &device, &queue, format, None);
44//!
45//! // In your event loop
46//! ui.handle_event(&event, window_width, window_height);
47//!
48//! // In your render pass — call after your own draw commands
49//! for action in ui.draw(&mut encoder, &view, pw, ph) {
50//! match action {
51//! pane::PaneAction::Custom(tag) => println!("button: {tag}"),
52//! pane::PaneAction::Slider(id, val) => println!("{id} = {val:.2}"),
53//! pane::PaneAction::Toggle(id, checked) => println!("{id} = {checked}"),
54//! pane::PaneAction::Quit => std::process::exit(0),
55//! _ => {}
56//! }
57//! }
58//! ```
59//!
60//! ### Headless
61//!
62//! No GPU, no window. Useful for testing UI logic in CI or driving a server-side state machine.
63//!
64//! ```no_run
65//! let mut menu = pane::headless("assets/menu.ron");
66//! menu.press("play_button");
67//! let actions = menu.update(1.0 / 60.0);
68//! ```
69//!
70//! ---
71//!
72//! ## Coordinate System
73//!
74//! Pane uses a **centered, resolution-independent** coordinate system:
75//!
76//! - `(0.0, 0.0)` is always the center of the screen
77//! - The screen height is always `1080` units regardless of actual resolution
78//! - Width scales proportionally — widescreen just adds space on the sides
79//! - Positive Y is downward
80//!
81//! A button at `x: -170.0, y: 0.0` is centered vertically on every screen.
82//! You never write resolution-specific layout.
83//!
84//! ---
85//!
86//! ## Widgets
87//!
88//! 15 built-in widget types, all defined in RON:
89//!
90//! `Button` · `Toggle` · `Slider` · `TextBox` · `Dropdown` · `RadioGroup` ·
91//! `ScrollList` · `ScrollPane` · `Bar` · `Popout` · `Label` · `Divider` ·
92//! `Image` · `ProgressBar` · `Tabs`
93//!
94//! Plus `Actor` — a non-interactive animated element that follows the cursor
95//! or moves to programmatic targets.
96//!
97//! ---
98//!
99//! ## Actions
100//!
101//! [`PaneOverlay::draw`] and [`PaneHeadless::update`] return a vector of [`PaneAction`]
102//! each frame. Match on these to drive your application:
103//!
104//! ```no_run
105//! # use pane::PaneAction;
106//! # let actions: Vec<PaneAction> = vec![];
107//! for action in actions {
108//! match action {
109//! PaneAction::Custom(tag) => { /* button pressed */ }
110//! PaneAction::Slider(id, value) => { /* slider moved */ }
111//! PaneAction::Toggle(id, checked) => { /* toggle flipped */ }
112//! PaneAction::Dropdown(id, idx, label) => { /* selection changed */ }
113//! PaneAction::Radio(id, idx, label) => { /* selection changed */ }
114//! PaneAction::TextChanged(id, text) => { /* keystroke */ }
115//! PaneAction::TextSubmitted(id, text) => { /* enter pressed */ }
116//! PaneAction::SwitchRoot(name) => { /* root changed */ }
117//! PaneAction::Quit => { std::process::exit(0); }
118//! }
119//! }
120//! ```
121//!
122//! ---
123//!
124//! ## Runtime API
125//!
126//! Read or write any widget's value from code at any time. Useful for syncing UI
127//! to app state on startup, or driving a progress bar mid-game.
128//!
129//! ```ignore
130//! # use pane::WriteValue;
131//! # let mut ui: pane::PaneOverlay = todo!();
132//! // Read — returns the UiItem definition + current visual state
133//! if let Some((pane::api::UiItem::Slider(s), _)) = ui.read("volume") {
134//! println!("volume = {}", s.value);
135//! }
136//!
137//! // Write — silently sets value without firing PaneActions
138//! ui.write("volume", &WriteValue::Slider(0.8));
139//! ui.write("mute", &WriteValue::Toggle(true));
140//! ui.write("name", &WriteValue::Text("Player 1".into()));
141//! ui.write("quality", &WriteValue::Selected(2));
142//!
143//! // Toast notification (also triggerable directly from RON via on_press: Toast(...))
144//! ui.push_toast("Settings saved", 2.0, 0.0, -400.0, 300.0, 60.0);
145//! ```
146//!
147//! ---
148//!
149//! ## Hot Reload
150//!
151//! Set `hot_reload: true` in your root `.ron` file. While your app runs:
152//!
153//! - Save a menu `.ron` → layout and behaviour update instantly
154//! - Save a style `.ron` → visual design updates instantly
155//! - Save a `.wgsl` shader → shader recompiles and updates instantly
156//!
157//! No restart. No recompile. Just save.
158//!
159//! Requires the `dev` feature for runtime style loading:
160//!
161//! ```toml
162//! pane_ui = { version = "0.1", features = ["dev"] }
163//! ```
164//!
165//! ---
166//!
167//! ## Controller Support
168//!
169//! All widgets support gamepad navigation with no extra configuration:
170//!
171//! - **D-pad / left stick** — moves focus to the nearest widget in that direction
172//! - **South (A / Cross)** — confirms; activates focused widget
173//! - **East (B / Circle)** — cancels; closes popout panels
174//! - **Sliders** — left/right adjusts value continuously
175//! - **Scroll lists & panes** — focus scrolls to keep the active item visible
176//! - **Popouts** — confirm opens, up/down navigates inside, cancel closes
177//!
178//! Pass your existing `gilrs::Gilrs` to share it with your game:
179//!
180//! ```no_run
181//! # let (device, queue, format) = todo!();
182//! # let gilrs: gilrs::Gilrs = todo!();
183//! // Share your game's gilrs context
184//! let ui = pane::overlay("assets/hud.ron", &device, &queue, format, Some(gilrs));
185//!
186//! // Or let pane_ui manage its own
187//! let ui = pane::overlay("assets/hud.ron", &device, &queue, format, None);
188//! ```
189//!
190//! ---
191//!
192//! ## Custom Styles & Shaders
193//!
194//! Drop `.ron` style files in your style directory and `.wgsl` shaders in your shader
195//! directory. Reference them by filename (without extension). Users and modders can
196//! reskin your entire UI without touching your code.
197//!
198//! ```ron
199//! (
200//! shader_dirs: ["shaders"],
201//! style_dirs: ["styles"],
202//! roots: [
203//! (
204//! name: "main_menu",
205//! buttons: [
206//! (
207//! id: "my_btn",
208//! style: "my_custom_style", // loads styles/my_custom_style.ron
209//! on_press: Custom("clicked"),
210//! // ...
211//! ),
212//! ],
213//! ),
214//! ],
215//! )
216//! ```
217//!
218//! 6 built-in styles are always available: `frosted_glass` · `retro` · `plain` ·
219//! `glass_pill` · `emboss` · `sharp_outline`
220//!
221//! ---
222//!
223//! ## Debugging
224//!
225//! Enable debug logging to print every internal message to stdout:
226//!
227//! ```no_run
228//! # let mut ui: pane::PaneOverlay = todo!();
229//! // Overlay or headless
230//! ui.set_debug(true);
231//! ```
232//!
233//! ```text
234//! // Standalone — set the environment variable before running
235//! PANE_DEBUG=1 cargo run
236//! ```
237
238// ── Public modules ────────────────────────────────────────────────────────────
239
240pub mod api;
241pub mod build;
242pub mod draw;
243pub mod input;
244pub mod loader;
245
246// ── Internal modules ──────────────────────────────────────────────────────────
247
248pub(crate) mod anim;
249pub(crate) mod builder;
250pub(crate) mod items;
251pub(crate) mod keyboard;
252pub(crate) mod logic;
253pub(crate) mod order;
254pub(crate) mod query;
255pub(crate) mod shader_reg;
256pub(crate) mod styles;
257pub(crate) mod styles_reg;
258pub(crate) mod textures;
259pub(crate) mod threader;
260pub(crate) mod widgets;
261
262// ── Re-exports ────────────────────────────────────────────────────────────────
263
264/// Re-exported for convenience — pass a `gilrs::Gilrs` to [`overlay`] to share
265/// your existing gamepad context with `pane_ui`.
266pub use gilrs;
267
268pub use api::{
269 PaneAction, PaneHeadless, PaneOverlay, StandaloneHandle, WriteValue, headless, overlay, run,
270 run_with,
271};