Skip to main content

yog_ui/
lib.rs

1//! yog-ui — retained-mode UI framework for Yog mods.
2//!
3//! Flexbox-inspired layout engine + GPU rendering via [`yog-gfx`].
4//! Use for custom inventories, guide books, tooltips, HUD overlays.
5//!
6//! # Quick start
7//! ```ignore
8//! use yog_ui::{UiRoot, widget, Align, FlexDir, Units};
9//!
10//! let ui = UiRoot::new("mymod:main_menu")
11//!     .style(|s| s.bg(0x88332211).padding(8.0, 8.0, 8.0, 8.0))
12//!     .child(
13//!         widget::panel(FlexDir::Column).gap(4.0)
14//!             .child(widget::label("Hello, World!").color(0xFF_DDAA00))
15//!             .child(widget::button("Click me").on_click("mymod:btn_click"))
16//!     );
17//! ```
18
19pub mod layout;
20mod render;
21pub mod widget;
22
23pub use layout::{Align, FlexDir, LayoutNode, Rect, Size};
24pub use widget::Widget;
25
26use yog_gfx::GfxContext;
27
28/// Top-level UI tree.  Build it, call [`layout`], then [`render`] each frame.
29pub struct UiRoot {
30    pub id: String,
31    pub root: Widget,
32    pub(crate) layout_root: LayoutNode,
33    pub needs_layout: bool,
34}
35
36impl UiRoot {
37    pub fn new(id: impl Into<String>, root: Widget) -> Self {
38        Self { id: id.into(), root, layout_root: LayoutNode::default(), needs_layout: true }
39    }
40
41    /// Recalculate layout. Call after changing the tree or on window resize.
42    pub fn layout(&mut self, screen_w: f32, screen_h: f32) {
43        self.layout_root = layout::compute(&self.root, screen_w, screen_h);
44        self.needs_layout = false;
45    }
46
47    /// Render the UI tree via `yog-gfx` draw2d.
48    /// Must be called from `on_hud_render`.
49    pub fn render(&self, ctx: &GfxContext) {
50        let d2d = ctx.draw2d();
51        render::render_node(&d2d, &self.root, &self.layout_root);
52    }
53
54    /// Find the deepest clickable widget at `(mx, my)` in screen coordinates.
55    pub fn hit_test(&self, mx: f32, my: f32) -> Option<&LayoutNode> {
56        layout::hit_test(&self.layout_root, mx, my)
57    }
58}