[!NOTE]
This crate is part of mevy (tl;dr: more neat macros) so take a look! 🦆
Setup
Multiple bevy versions are supported and managed by features:
mevy_ui = {version="0.3",features=["0.17"]}
mevy_ui = {version="0.3",features=["0.16"]}
mevy_ui = {version="0.3",features=["0.15"]}
⭐ The Star of the Crate: CSS-like Notation
The macro ui!() has multiple modes, that are invoked by (1.) the type of delimiters and (2.) if a name is provided:
'Slim' Tuple Inline Mode
Short syntax, inspired by TailwindCSS!
c.spawn(ui!(
w:100 h:100 radius:6 bg:#fff border:5#f00
shadow:10%+10%+3px+8px#fa4
)?));
Tuple Inline Mode
ui!((..)) (inner round braces) will return a tuple of mentioned components only. See this example.
c.spawn(ui!((
size: 100px 100px;
border: 5px #ff0000;
box_shadow: 10% 10% 3px 8px #ffaa44;
background: #ffffff;
border_radius: 6px;
neat_outline;
)?));
fn neat_outline() -> Outline {ui!((
outline: 3px 1px #00ff00;
))}
The order of the components are based on 'first mention' and consistent:
let (shadow,node) = ui!((
box_shadow: 10% 10% 3px 8px #ffaa44;
size: 100px 100px;
));
Tuple Function Mode
If a name is defined before the inner braces, a function will be defined returning an impl Bundle of its content:
ui!{neat_box(
size: 100px 100px;
background: #ffffff;
)}
pub fn neat_box() -> impl Bundle {ui!((
size: 100px 100px;
background: #ffffff;
))}
Edit Function Mode
Defining a name and then using curly brackets {} instead, will define a function that mutates existing components:
- The parameters of this function are
&mut .. of the components to be mutated
- Custom fields will be
inserted, an EntityCommands will be then added to the parameters
- Using
_ instead of a calue will keep ste already existing one:
- e.g.
box_shadow: _ _ 10px will only affect the blur radius, since x and y are 'kept' and spread & color not mentioned
ui!{into_red_glow{
border: _ #ffffff;
background: #ff0000;
box_shadow: #ff0000
}}
pub fn into_red_glow(
border_color: &mut BorderColor,
background_color: &mut BackgroundColor,
box_shadow: &mut BoxShadow
){
border_color.0 = Srgba::hex("#ffffff").unwrap().into();
background_color.0 = Srgba::hex("#ff0000").unwrap().into();
box_shadow.color = Srgba::hex("#ff0000").unwrap().into();
}
Neat Notation
Inside the macro there you can write those things:
0px = Val::Px(0.)
0% = Val::Percent(0.)
0vh = Val::Vh(0.)
0vw = Val::Vw(0.)
0vmin = Val::VMin(0.)
0vmax = Val::VMax(0.)
#ff0000 = Color::Srgba(Srgba::hex("#ff0000").unwrap())
- checked by the compiler (and LSP), throwing a custom error if not valid!
red|RED = bevy::color::palettes::css::RED
Custom Fields
Currently, There are 2 ways to add those:
- a
fn that returns something that impl Bundle
- the
new method of a struct that impl Bundle
c.spawn(ui!((
just_glow: #ff0000;
Outline: 5px, 2px, #ff0000;
)));
fn just_glow(color:Color) -> BoxShadow {
BoxShadow {
spread_radius: Val::Px(5.0),
blur_radius: Val::Px(5.0),
color,
..default()
}
}
Edge Selection (CSS-like)
border: 5px; border: 5px 2px; border: 5px 2px 8px; border: 5px 2px 4px 1px
Corner Selection (CSS-like)
border-radius: 5px; border-radius: 5px 0px; border-radius: 5px 2px 8px; border-radius: 5px 2px 4px 1px
Limitations
At the moment, you can only use variables in custom fields. It's planned to work for built-in fields soon™.
Built-In Fields
Here's a list of all available out of the box fields, Val order same as in bevy
- numbers can be written as int or float
- enum variants can also be written in snake case
ui!((
position_type: absolute|relative;
absolute; relative;
hidden;
visible;
inherit;
scale: 1.0 1.2; scale: 1.0; rotation: 1.41; rotation: 45deg;
l|left: 1px;
r|right: 1px;
t|top: 1px;
b|bottom: 1px;
x: 1px; y: 1px; xy: 1px; z|z_index: 10;
zg|z_global: 10;
w|width: 1px;
h|height: 1px;
size: 1px 1px; min_w|min_width: 1px;
min_h|min_height: 1px;
max_w|max_width: 1px;
max_h|max_height: 1px;
aspect_ratio: 1;
m|margin: 1px 1px 1px 1px; mt|margin_top: 1px;
mb|margin_bottom: 1px;
ml|margin_left: 1px;
mr|margin_right: 1px;
mx|margin_x: 1px; my|margin_y: 1px;
p|padding: 1px 1px 1px 1px; pt|padding_top: 1px;
pb|padding_bottom: 1px;
pl|padding_left: 1px;
pr|padding_right: 1px;
px|padding_x: 1px; py|padding_y: 1px;
bg|background: red;
shadow|box_shadow: 1px 1px 1px 1px #ff0000;
border: 1px 1px 1px 1px #ff0000; border_color: #ff0000;
outline: 1px 1px #ff0000;
round|border_radius: 1px 1%;
text|text_size|font_size: 20;
text|justify_text: left|center|right|justified;
text|line_break: word_boundary|any_character|word_or_character|no_wrap;
leading|line_height: 1.2;
color|font_color: #ff0000;
text_shadow: 2 2 #000;
img|image: $an_image; img|image: $(path.to.image);
img|image|image_color: #000;
img|image: flip_x|flip_y;
justify_self: auto|start|end|center|baseline|stretch;
align_self: ^|flex_start|flex_end;
display: flex|grid|block|none;
justify_items: default|start|end|center|baseline|stretch;
align_items: ^|flex_start|flex_end;
justify_content: default|start|end|flex_start|flex_end|center|stretch|space_between|space_evenly|space_around;
align_content: ^;
gap_y|row_gap: 1px;
gap_x|column_gap: 1px;
gap: 1px; overflow: visible|clip|hidden|scroll; overflow: clip clip; overflow_clip_margin: content_box|padding_box|border_box 5;
flex|flex_direction: row|column|row_reverse|column_reverse;
flex_basis: 1px;
flex_grow: 1;
flex_shrink: 1;
flex_wrap: no_wrap|wrap|wrap_reverse;
grid_auto_flow: row|column|row_dense|column_dense;
grid_row: span|start|end 10;
grid_row: start_span|start_end|end_span 8 10;
grid_column: span|start|end 10;
grid_column: start_span|start_end|end_span 8 10;
grid_auto_rows: 1px 3% 10fr min_content; grid_auto_columns: 1px 3% 10fr; grid_template_rows: 10:1px 10:3%; grid_template_columns: 10:1px 10:3%;
interaction; cursor_position; focus|focus_policy: pass
scroll|scroll_position: 1px 1px;
))