Every widget here — menus, tabs, tree, data grid, the animated swatch — is XAML instantiated into Bevy ECS entities. The clock, progress bar, and swatch are driven by plain Bevy systems locating elements by x:Uid, AutomationId, and x:Name.
How it's built
Everything in this demo comes from examples/components_showcase.rs — these are its actual pieces, in build order.
1. Spawn a XAML scene with a bound view-model
The xaml! macro validates markup at compile time; spawn_xaml_bound attaches a reflected view-model as the DataContext.
let vm = Bindable::new(Vm {
files: vec![FileRow { name: "main.rs".into(), kind: "Rust".into(), size: 1240 }, /* ... */],
});
commands.spawn_xaml_bound(xaml!(r##"<Window ...>
<DockPanel>
<Menu DockPanel.Dock="Top"> ... </Menu>
<TabControl Margin="8"> ... </TabControl>
</DockPanel>
</Window>"##), vm);2. Declare controls in plain WPF markup
The Lists + Data tab: ItemsSource binding, a TreeView, and a DataGrid with typed columns.
<ListBox ItemsSource="{Binding players}"/>
<TreeView>
<TreeViewItem Header="workspace" IsExpanded="True">
<TreeViewItem Header="crates" IsExpanded="True"> ... </TreeViewItem>
</TreeViewItem>
</TreeView>
<DataGrid ItemsSource="{Binding files}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding name}" Width="2*"/>
<DataGridTextColumn Header="Bytes" Binding="{Binding size}" Width="*"/>
</DataGrid.Columns>
</DataGrid>3. Drive the UI from any Bevy system
The ticking clock, progress bar, and color-cycling swatch are ordinary systems locating elements by x:Uid, AutomationId, and x:Name.
fn live_updates(time: Res<Time>, ui: PfQuery, mut texts: Query<&mut Text>,
mut bars: Query<&mut PfProgress>, mut commands: Commands) {
if let Some(clock) = ui.by_uid("clock.uid")
&& let Some(t) = ui.first_text_in(clock)
&& let Ok(mut text) = texts.get_mut(t) {
text.0 = format!("clock: {:.1}s", time.elapsed_secs());
}
if let Some(bar) = ui.by_automation_id("LiveProgress")
&& let Ok(mut p) = bars.get_mut(bar) {
p.value = (time.elapsed_secs() * 10.0) % 100.0; // fill resizes itself
}
}4. Toolkit controls (the Toolkit tab)
Ecosystem controls every WPF toolkit ships, as first-class elements.
<ToggleSwitch Content="Wi-Fi" IsOn="True"/>
<NumericUpDown Value="5" Minimum="0" Maximum="10"/>
<RatingBar Value="3"/>
<Badge Badge="12"><Button Content="Inbox"/></Badge>
<Chip><TextBlock Text="bevy"/></Chip>
<Card><StackPanel> ... </StackPanel></Card>
<BusyIndicator IsBusy="True" BusyContent="Loading..."> ... </BusyIndicator>
<RangeSlider Minimum="0" Maximum="100" LowerValue="25" UpperValue="70"/>
<TextBox Watermark="Search..."/>
<TimePicker SelectedTime="09:30"/>
<ColorPicker SelectedColor="#3366CC"/>
<AutoSuggestBox Suggestions="Amsterdam,Athens,Berlin,Bern"/>
<PackIcon Kind="Home" Width="20" Height="20"/> <!-- 40+ vector icons -->
<NavigationView>
<NavigationViewItem Content="Dashboard" Icon="Home" Tag="nav-dashboard"/>
</NavigationView>
<!-- Animation tab: storyboards, visual states, behaviors -->
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard><Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1.2">
<DoubleAnimation.EasingFunction><CubicEase EasingMode="EaseOut"/></DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard></BeginStoryboard>
</EventTrigger>
<VisualState x:Name="MouseOver">
<Storyboard><ColorAnimation Storyboard.TargetName="chrome"
Storyboard.TargetProperty="Background" To="#FF5B8DEF" Duration="0:0:0.15"/></Storyboard>
</VisualState>
<Interaction.Triggers>
<EventTrigger EventName="Click">
<InvokeCommandAction Command="{Binding save}"/>
<ControlStoryboardAction Storyboard="{StaticResource Nudge}"/>
</EventTrigger>
</Interaction.Triggers>
// and from any system:
bevy_pf::toast::show_with(world, "Saved!", Severity::Success, 4.0);5. Wire a menu item with an observer
The View > Vsync toggle: find by name once, observe clicks, flip real window state.
commands.entity(toggle).observe(
|_click: On<Pointer<Click>>, mut vsync: ResMut<Vsync>| {
vsync.on = !vsync.on;
vsync.dirty = true; // apply_vsync flips Window.present_mode
},
);