A XAML / WPF-style UI framework for the Bevy game engine. Declare the interface in markup — inline through the xaml! macro or in separate .xaml files — style it with resources and triggers, and bind it to Rust state. The control set, the parser and the binding model follow WPF, so the markup you already know carries over.
Add the plugin, spawn a camera, spawn some markup. The xaml! macro parses at compile time, so a typo in the markup is a build error, not a surprise at runtime.
Rust
use bevy::prelude::*;
use bevy_pf::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PfUiPlugin)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands.spawn_xaml(xaml!(
r#"<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Margin="24" Spacing="8">
<TextBlock Text="Hello from XAML" FontSize="28" FontWeight="Bold"/>
<Button x:Name="Go" Content="Click me" Width="140"/>
</StackPanel>"#
));
}
Three ways to author
Inline, from a file, or at runtime
The first two are checked when you build. The third is for markup you do not have until the program is running — a document the user opened, or one you generated.
Rust
// 1. Inline, for a snippet.
commands.spawn_xaml(xaml!(r#"<Button Content="Go"/>"#));
// 2. A separate .xaml file, validated at COMPILE time. No string literal.
commands.spawn_xaml(include_xaml!("ui/main.xaml"));
// 3. At runtime, from a String (a loaded file, a generated document).
let scene = XamlScene::parse(std::fs::read_to_string("ui/main.xaml")?)?;
What it looks like
Rendered by Bevy, from the markup beside it
This is the snippets_gallery example running natively. Each card renders the exact string printed next to it — the demo and the code are the same markup, so neither can drift from the other.
Control recipes
Every control, and the markup that makes it
15 recipes, taken verbatim from the framework’s own snippets_gallery example — so this is the exact markup that example compiles and renders, not a paraphrase of it.
Button
Command= names a closure registered on the view-model; the count flows back through a normal binding.
<Expander Header="Details" IsExpanded="True" Width="230">
<StackPanel Spacing="4">
<TextBlock Text="Anything can live in here."/>
<Button Content="With a tooltip" Width="140"
ToolTip="Hovered long enough!"/>
</StackPanel>
</Expander>
ScrollBar
Proportional thumb (ViewportSize), draggable, line buttons repeat while held; Value binds two-way — drag it and watch the readout.
The repository carries twenty-odd examples — a control gallery, theming, data binding, navigation, an RPG HUD and a playable Breakout. Clone it and pick one.
Shell
git clone https://github.com/edgarhsanchez/bevy_pf
cd bevy_pf
cargo run --example snippets_gallery