The design is inspired from Flutter. This is the most ergonomic ECS Data Driven UI Framework.
bevy | wings |
---|---|
0.11 | 0.1 |
Scaffold
Container
SizedBox
Align
Center
FlatButton
Column
Row
ConstrainedWidth
ConstrainedHeight
HorizontalDivider
VerticalDivider
Visible
LayoutVisibility
Padding
Paragraph
GestureDetector
A widget that detects gestures.
ColorQuery
(background_color
)SizeQuery
(width
&height
)VisibleQuery
LayoutVisibilityQuery
TextQuery
(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::TEAL
color![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
/match
insidewidget_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
,FilePicker
and so on.. - Animation capability is still in design phase.
Alignment
must be fixed before. Alignment
values have issues. Consider adding a system to process them.- All 4 borders inside
BoxDecoration
must 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();
}