The design is inspired from Flutter. This is the most ergonomic ECS Data Driven UI Framework.
| bevy | wings |
|---|---|
| 0.11 | 0.1 |
ScaffoldContainerSizedBoxAlignCenterFlatButtonColumnRowConstrainedWidthConstrainedHeightHorizontalDividerVerticalDividerVisibleLayoutVisibilityPaddingParagraphGestureDetectorA widget that detects gestures.
ColorQuery(background_color)SizeQuery(width&height)VisibleQueryLayoutVisibilityQueryTextQuery(text&font_size&color)
OnTap|on_tap! { || {} }Fires when a tap with a pointer button has occurred.OnTapDown|on_tap_down! { || {} }Fires when a pointer that might cause a tap with a button has contacted the screen at a particular location.OnTapUp|on_tap_up! { || {} }Fires when a pointer that will trigger a tap with a button has stopped contacting the screen at a particular location.OnMove|on_move! { || {} }Fires when a pointer is moving over the widget.OnContact|on_contact! { || {} }Fires when a pointer crosses into the bounds of the target entity.OnLeave|on_leave! { || {} }Fires when a pointer crosses out of the bounds of the target entity.
str!["Play"]is equivalent to"Play".to_string()val![100. px]is equivalent toVal::Px(100.)val![100. %]is equivalent toVal::Percent(100.)color![TEAL]is equivalent toSome(Color::TEAL)color![^TEAL]is equivalent toColor::TEALcolor![Color::default()]is equivalent toSome(Color::default())color![^Color::default()]is equivalent toColor::default()color![r: 0.75, g: 0.5., b: 0.25]is equivalent toSome(Color::rgb(0.75, 0.5, 0.25))color![^r: 0.75, g: 0.5, b: 0.25]is equivalent toColor::rgb(0.75, 0.5, 0.25)color![r: 0.75, g: 0.5, b: 0.25, a: 1.]is equivalent toSome(Color::rgba(0.75, 0.5, 0.25, 1.))color![^r: 0.75, g: 0.5, b: 0.25, a: 1.]is equivalent toColor::rgba(0.75, 0.5, 0.25, 1.)color![r8: 117, g8: 63, b8: 223]is equivalent toSome(Color::rgb_u8(117, 63, 223))color![^r8: 117, g8: 63, b8: 223]is equivalent toColor::rgb_u8(117, 63, 223)color![r8: 117, g8: 63, b8: 223, a8: 255]is equivalent toSome(Color::rgba_u8(117, 63, 223, 255))color![^r8: 117, g8: 63, b8: 223, a8: 255]is equivalent toColor::rgba_u8(117, 63, 223, 255)color!["#00FF00FF"]is equivalent toSome(Color::hex("#00FF00FF").unwrap_or(Color::NONE))color![^"#00FF00FF"]is equivalent toColor::hex("#00FF00FF").unwrap_or(Color::NONE)color![hex my_string]is equivalent toSome(Color::hex(my_string).unwrap_or(Color::NONE))color![^hex my_string]is equivalent toColor::hex(my_string).unwrap_or(Color::NONE)edge_insets_only![left: 10., right: 15., top: 20., bottom: 25.]is equivalent toEdgeInsets::from_ltrb(10., 15., 20., 25.)You can keep only what side(s) you need, everything else will be set to 0.edge_insets_symmetric![vertical: 10.]is equivalent toEdgeInsets::symmetric_vertical(10.)edge_insets_symmetric![horizontal: 10.]is equivalent toEdgeInsets::symmetric_horizontal(10.)edge_insets_symmetric![vertical: 10., horizontal: 15.]is equivalent toEdgeInsets::symmetric_vh(10., 15.)edge_insets_symmetric![horizontal: 15., vertical: 10.]is equivalent toEdgeInsets::symmetric_vh(10., 15.)
- Currently, you cannot create your own widgets. Consider adding
compose_widget!proc macro. - Expressions such as
for/while/loop/if-else/matchinsidewidget_tree!are missing. - Missing a lot of useful widgets such as
ProgressIndicator,CheckBox,RadioButton,ToggleButton,Dropdown,TextEdit,SelectableText,ScrollArea,ListView,AppBar,Icon,Stack,Grid,Wrap,Drawer,ColorPicker,FilePickerand so on.. - Animation capability is still in design phase.
Alignmentmust be fixed before. Alignmentvalues have issues. Consider adding a system to process them.- All 4 borders inside
BoxDecorationmust have the same color in order to render the border.
widget_tree!(
Scaffold {
child: Container {
color: color![BLUE]
width: val![500. px]
height: val![500. px]
child: Padding {
padding: EdgeInsets::all(val![80. px])
child: Container {
color: color![YELLOW]
width: val![100. %]
height: val![100. %]
}
}
}
}
);widget_tree!(
Scaffold {
child: Center {
child: Column {
main_axis_size: MainAxisSize::Max
main_axis_alignment: MainAxisAlignment::End
cross_axis_alignment: CrossAxisAlignment::End
children: [
Container {
color: color![RED]
width: val![300. px]
}
Container {
color: color![GREEN]
width: val![480. px]
}
Container {
color: color![BLUE]
width: val![200. px]
}
]
}
}
}
);widget_tree!(
Scaffold {
child: Center {
child: Row {
main_axis_size: MainAxisSize::Max
main_axis_alignment: MainAxisAlignment::End
cross_axis_alignment: CrossAxisAlignment::End
children: [
Container {
color: color![RED]
height: val![300. px]
}
Container {
color: color![GREEN]
height: val![480. px]
}
Container {
color: color![BLUE]
height: val![200. px]
}
]
}
}
}
);use bevy::prelude::*;
use wings::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(WingsPlugin)
.add_systems(Startup, startup)
.add_systems(Update, change_color)
.run();
}
#[derive(Component, Debug)]
pub struct ColorTag;
fn startup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
widget_tree! {
Scaffold {
child: Container {
decoration: Some(BoxDecoration {
color: Color::BLACK,
border: Border::all(BorderSide::from_width_color(val![15. px], Color::SILVER)),
..default()
})
width: val![500. px]
height: val![500. px]
margin: edge_insets_only! {
left: val![100. px],
top: val![50. px],
}
child: Center {
child: Column {
children: [
Container { color: color![RED] }
SizedBox { height: val![10. px] }
Visible {
tags: [Collapsible]
child: Column {
children: [
Container {
tags: [ColorTag]
color: color![ORANGE]
}
SizedBox { height: val![10. px] }
]
}
}
Container { color: color![DARK_GREEN] }
]
}
}
}
}
}
}
fn change_color(
keyboard_input: Res<Input<KeyCode>>,
mut color_query: ColorQuery<ColorTag>,
mut visibility_query: VisibleQuery<Collapsible>,
) {
if keyboard_input.just_pressed(KeyCode::C) {
color_query.set_random_color();
}
if keyboard_input.just_pressed(KeyCode::V) {
visibility_query.set_visible(|v| !v);
}
}Every time when you click on the first Container, all Containers in the Widget Tree will change their color.
Mouse enter will trigger on_contact and mouse exit will trigger on_leave.
widget_tree! {
Center {
child: GestureDetector {
on_tap: on_tap! {
|mut query: ColorQuery<Container>| {
query.set_random_color();
}
}
on_contact: on_contact! { || println!("Contact") }
on_leave: on_leave! { || println!("Leave") }
child: Container {
width: val![500. px]
child: Align {
alignment: Alignment::CENTER_RIGHT
child: Container {}
}
}
}
}
}// This is bad use
fn apply_theme_bad(mut query: Query<(&mut BackgroundColor, With<ContainerWidget>)>) {
for (mut bg_color, _) in query.iter_mut() {
bg_color.0 = get_random_color();
}
}
// You can do this if you want more control over entities, but still not recommended
fn apply_theme_also_bad(mut query: ColorQuery<ContainerWidget>) {
query.get_mut().for_each_mut(|(_, mut c_color, _)| {
c_color.0 = get_random_color();
});
}
// This is the most ergonomic way
fn apply_theme_good(mut query: ColorQuery<ContainerWidget>) {
query.set_random_color();
}


