use gizmo_math::{Vec2, Vec4};
#[derive(Clone, Copy, Debug, PartialEq, Default)]
#[non_exhaustive]
pub enum Val {
#[default]
Auto,
Px(f32),
Percent(f32),
}
impl Val {
pub const ZERO: Val = Val::Px(0.0);
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UiRect {
pub left: Val,
pub right: Val,
pub top: Val,
pub bottom: Val,
}
impl UiRect {
pub const ZERO: UiRect = UiRect::all(Val::ZERO);
pub const AUTO: UiRect = UiRect::all(Val::Auto);
pub const fn all(value: Val) -> Self {
Self { left: value, right: value, top: value, bottom: value }
}
pub const fn axes(horizontal: Val, vertical: Val) -> Self {
Self { left: horizontal, right: horizontal, top: vertical, bottom: vertical }
}
pub const fn new(left: Val, right: Val, top: Val, bottom: Val) -> Self {
Self { left, right, top, bottom }
}
}
impl Default for UiRect {
fn default() -> Self {
Self::ZERO
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Display {
#[default]
Flex,
Block,
None,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum PositionType {
#[default]
Relative,
Absolute,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum FlexDirection {
#[default]
Row,
Column,
RowReverse,
ColumnReverse,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum FlexWrap {
#[default]
NoWrap,
Wrap,
WrapReverse,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AlignItems {
Start,
End,
FlexStart,
FlexEnd,
Center,
Baseline,
Stretch,
}
pub type AlignSelf = AlignItems;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AlignContent {
Start,
End,
FlexStart,
FlexEnd,
Center,
Stretch,
SpaceBetween,
SpaceEvenly,
SpaceAround,
}
pub type JustifyContent = AlignContent;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Style {
pub display: Display,
pub position_type: PositionType,
pub inset: UiRect,
pub width: Val,
pub height: Val,
pub min_width: Val,
pub min_height: Val,
pub max_width: Val,
pub max_height: Val,
pub aspect_ratio: Option<f32>,
pub margin: UiRect,
pub padding: UiRect,
pub border: UiRect,
pub align_items: Option<AlignItems>,
pub align_self: Option<AlignSelf>,
pub align_content: Option<AlignContent>,
pub justify_content: Option<JustifyContent>,
pub flex_direction: FlexDirection,
pub flex_wrap: FlexWrap,
pub flex_grow: f32,
pub flex_shrink: f32,
pub flex_basis: Val,
pub row_gap: Val,
pub column_gap: Val,
}
impl Style {
pub const DEFAULT: Style = Style {
display: Display::Flex,
position_type: PositionType::Relative,
inset: UiRect::AUTO,
width: Val::Auto,
height: Val::Auto,
min_width: Val::Auto,
min_height: Val::Auto,
max_width: Val::Auto,
max_height: Val::Auto,
aspect_ratio: None,
margin: UiRect::ZERO,
padding: UiRect::ZERO,
border: UiRect::ZERO,
align_items: None,
align_self: None,
align_content: None,
justify_content: None,
flex_direction: FlexDirection::Row,
flex_wrap: FlexWrap::NoWrap,
flex_grow: 0.0,
flex_shrink: 1.0,
flex_basis: Val::Auto,
row_gap: Val::ZERO,
column_gap: Val::ZERO,
};
}
impl Default for Style {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Node {
pub size: Vec2,
pub position: Vec2,
}
impl Default for Node {
fn default() -> Self {
Self {
size: Vec2::ZERO,
position: Vec2::ZERO,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Default)]
#[non_exhaustive]
pub enum Interaction {
#[default]
None,
Hovered,
Pressed,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BackgroundColor(pub Vec4);
impl Default for BackgroundColor {
fn default() -> Self {
Self(Vec4::new(1.0, 1.0, 1.0, 1.0))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UiRoot;
gizmo_core::impl_component!(Style, Node, Interaction, BackgroundColor, UiRoot);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn style_is_send_and_sync_without_unsafe() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Style>();
assert_send_sync::<Val>();
assert_send_sync::<UiRect>();
}
#[test]
fn style_is_copy_plain_old_data() {
fn assert_copy<T: Copy>() {}
assert_copy::<Style>();
let a = Style { width: Val::Px(3.0), ..Default::default() };
let b = a; assert_eq!(a, b);
assert_eq!(a.width, Val::Px(3.0));
}
#[test]
fn defaults_are_css_defaults_not_zero() {
let s = Style::default();
assert_eq!(s, Style::DEFAULT);
assert_eq!(s.display, Display::Flex);
assert_eq!(s.position_type, PositionType::Relative);
assert_eq!(s.flex_direction, FlexDirection::Row);
assert_eq!(s.flex_wrap, FlexWrap::NoWrap);
assert_eq!(s.flex_grow, 0.0);
assert_eq!(s.flex_shrink, 1.0, "CSS default is 1.0; 0.0 would disable shrinking");
assert_eq!(s.flex_basis, Val::Auto);
assert_eq!(s.width, Val::Auto);
assert_eq!(s.aspect_ratio, None);
assert_eq!(s.inset, UiRect::AUTO);
assert_eq!(s.margin, UiRect::ZERO);
assert_eq!(s.padding, UiRect::ZERO);
assert_eq!(s.border, UiRect::ZERO);
assert_eq!(s.row_gap, Val::ZERO);
assert_eq!(s.column_gap, Val::ZERO);
}
#[test]
fn ui_rect_default_is_zero_while_val_default_is_auto() {
assert_eq!(Val::default(), Val::Auto);
assert_eq!(UiRect::default(), UiRect::ZERO);
assert_ne!(UiRect::default(), UiRect::AUTO);
assert_eq!(
UiRect::axes(Val::Px(4.0), Val::Percent(10.0)),
UiRect::new(Val::Px(4.0), Val::Px(4.0), Val::Percent(10.0), Val::Percent(10.0))
);
}
}