5 releases
Uses new Rust 2024
| new 0.1.0-alpha.5 | Apr 15, 2026 |
|---|---|
| 0.1.0-alpha.4 | Apr 10, 2026 |
| 0.1.0-alpha.3 | Apr 7, 2026 |
| 0.1.0-alpha.2 | Apr 1, 2026 |
| 0.1.0-alpha.1 | Mar 31, 2026 |
#127 in Graphics APIs
3MB
61K
SLoC
๐งฉ RFGUI
Demo
(Web version is a little glitchy, use desktop for best experience)RFGUI is a ๐ฆ Rust-based retained-mode GUI framework built on top of a ๐ง frame graphโdriven rendering architecture.
It is designed for developers who want ๐ explicit control over rendering passes, predictable performance, and a ๐ modern retained UI model, rather than an immediate-mode GUI.
RFGUI treats UI rendering as a ๐ directed acyclic graph (DAG) of render passes, similar to frame graph systems used in modern game engines.
Each UI component contributes render passes and resources, which are composed and scheduled automatically.
โจ Key Characteristics
- ๐งฑ Retained-mode GUI โ UI state is preserved and updated declaratively, instead of redrawn every frame
- ๐ง Frame Graph architecture โ rendering is expressed as connected render passes with explicit resource dependencies
- ๐งฎ Deterministic rendering order โ pass execution is derived from graph topology, not ad-hoc draw calls
- ๐ Explicit resource management โ textures, buffers, and render targets are modeled as graph resources
- ๐ Designed for modern GPU APIs โ suitable for rendering backends
RFGUI is not an immediate-mode GUI like egui or imgui.
It is closer in spirit to ๐ retained UI frameworks combined with ๐ฎ engine-style render pipelines.
๐ This project is currently under active development and focuses on core architecture, correctness, and composability before higher-level widgets.
Features
wgpurendering pipeline- RSX-style UI declaration via
rust-gui-rsx #[component]for reusable UI composition- Custom host-element extension by composing from
Element - Typed style/layout model (
Length,Border,BorderRadius,ColorLike) - Frame Graph abstraction for pass/resource orchestration
- Built-in interaction primitives: hover, scroll, bubbling events, transitions
Quick Start
Requirements
- Rust (stable recommended)
- A graphics-capable environment (macOS / Linux / Windows)
Build
cargo build
Run demo
cargo run -r -p examples --bin 01_window
Project Layout
.
โโโ src/
โ โโโ style/ # typed style model + parsing/computation
โ โโโ ui/ # RSX tree, events, runtime, host elements
โ โโโ view/ # viewport, render passes, frame graph
โ โโโ transition/ # animation/transition system
โ โโโ shader/ # WGSL shaders
โโโ rsx-macro/ # proc-macro crate for RSX
โโโ examples/ # runnable demos
Style Model
- Parsed Style: typed external style input (
PropertyId+ typed values) - ComputedStyle: structured engine-level style (no string parsing)
- LayoutState: solver output only (position/size/baseline, etc.)
Frame Graph
src/view/frame_graph/ manages render-stage dependencies and resources.
Key files:
frame_graph.rs(core graph/execution)builder.rs(graph construction)render_node.rs(node contracts)texture_resource.rs,buffer_resource.rs(resources)slot.rs(node input/output slots)
Custom Components
1) #[component] reusable composition
use rfgui::ui::{component, rsx, RsxNode};
use rfgui::view::Element;
use rfgui::{Layout, Length};
#[component]
fn Card() -> RsxNode {
rsx! {
<Element style={{
width: Length::px(180.0),
height: Length::px(100.0),
display: Layout::flow().column(),
}}>
Hello Component
</Element>
}
}
2) Hand-written typed create_tag_element(...)
use rfgui::view::{Element, ElementPropSchema};
use rfgui::ui::{create_tag_element, RsxChildrenPolicy, RsxComponent, RsxNode, props};
use rfgui::{Border, BorderRadius, Color, Length, Padding, Style};
pub struct Card;
#[props]
pub struct CardProps {
pub style: Option<Style>,
}
impl RsxComponent<CardProps> for Card {
fn render(props: CardProps, children: Vec<RsxNode>) -> RsxNode {
let mut style = props.style.unwrap_or_else(Style::new);
style = style
.with_padding(Padding::uniform(Length::px(12.0)))
.with_border(Border::uniform(Length::px(2.0), &Color::hex("#1f2937")))
.with_border_radius(BorderRadius::uniform(Length::px(10.0)));
create_tag_element::<Element, _, _>(
ElementPropSchema {
anchor: None,
style: Some(style),
on_mouse_down: None,
on_mouse_up: None,
on_mouse_move: None,
on_mouse_enter: None,
on_mouse_leave: None,
on_click: None,
on_key_down: None,
on_key_up: None,
on_focus: None,
on_blur: None,
},
children,
)
}
}
impl RsxChildrenPolicy for Card {
const ACCEPTS_CHILDREN: bool = true;
}
Key Semantics
RSX currently supports two kinds of key:
- local key: only affects sibling identity within the same parent
- global key: must be globally unique within the same build pass and can preserve component state when moving across parents
use rfgui::ui::{GlobalKey, rsx};
use rfgui::view::Element;
let tree = rsx! {
<Element style={{}}>
<Element key="item-1" style={{}} />
<Element key={GlobalKey::from("dialog-root")} style={{}} />
</Element>
};
Notes:
- String and numeric
keyvalues are treated as local keys, for examplekey="item-1". GlobalKeymust be written as a Rust expression, so usekey={GlobalKey::from("dialog-root")}.- Reusing the same
GlobalKeyin a single build pass is an error. - Reconciliation identity is based on
type + key;<Button key={...} />and<Element key={...} />are not treated as the same node.
Development
cargo fmt
cargo clippy --all-targets --all-features
cargo test
Dependencies
~37โ58MB
~1M SLoC