diff --git a/Cargo.toml b/Cargo.toml index 9204a8053d4bcb606ba0382e8311aa5bbdd2a679..02ee2caf477c6d7415862b45acaf81e0a63bf22f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ resources = "1.1" [dev-dependencies] fastrand = "1.8" +bevy-inspector-egui = "0.14" [[example]] name = "tabs" diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs index 3072de064177cf0e072df87b7adc5658d4a69bc5..0e50bea1313e7655e2bd1742f11e9716f2d02130 100644 --- a/examples/todo/todo.rs +++ b/examples/todo/todo.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use bevy_inspector_egui::WorldInspectorPlugin; use kayak_ui::prelude::{widgets::*, *}; mod input; @@ -105,6 +106,7 @@ fn main() { App::new() .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) + .add_plugin(WorldInspectorPlugin::new()) .add_plugin(KayakContextPlugin) .add_plugin(KayakWidgets) .insert_non_send_resource(TodoList::new()) diff --git a/kayak_font/src/layout/glyph.rs b/kayak_font/src/layout/glyph.rs index cfd33e3b8350e560c87784bb42f5831eb0239781..a7d7f8429634d18e409ad23cbfa7cdb29f96151f 100644 --- a/kayak_font/src/layout/glyph.rs +++ b/kayak_font/src/layout/glyph.rs @@ -1,5 +1,7 @@ +use bevy::reflect::{FromReflect, Reflect}; + /// Layout information for a renderable glyph. -#[derive(Default, Debug, Clone, Copy, PartialEq)] +#[derive(Default, Reflect, FromReflect, Debug, Clone, Copy, PartialEq)] pub struct GlyphRect { pub position: (f32, f32), pub size: (f32, f32), diff --git a/kayak_font/src/layout/grapheme.rs b/kayak_font/src/layout/grapheme.rs index 23cf11850eb7f027f9965489989a2e3697bb527b..df0ce15308f14e0dc548cdb1558b17415aa106cc 100644 --- a/kayak_font/src/layout/grapheme.rs +++ b/kayak_font/src/layout/grapheme.rs @@ -1,9 +1,11 @@ use std::cmp::Ordering; +use bevy::reflect::{FromReflect, Reflect}; + /// A representation of a grapheme cluster, as defined by [Unicode UAX #29]. /// /// [Unicode UAX #29]: https://unicode.org/reports/tr29/ -#[derive(Default, Debug, Copy, Clone, PartialEq)] +#[derive(Default, Debug, Reflect, FromReflect, Copy, Clone, PartialEq)] pub struct Grapheme { /// The index of the starting char within this grapheme, relative to the entire text content. pub char_index: usize, diff --git a/kayak_font/src/layout/line.rs b/kayak_font/src/layout/line.rs index bd6a688970c517c33916373dcd904e427aca3a3c..41e07030599b773aa1bb757189c6f3bbf2482f72 100644 --- a/kayak_font/src/layout/line.rs +++ b/kayak_font/src/layout/line.rs @@ -1,10 +1,12 @@ +use bevy::reflect::{FromReflect, Reflect}; + use crate::layout::grapheme::Grapheme; use std::cmp::Ordering; use std::ops::Index; use std::slice::SliceIndex; /// Contains details for a calculated line of text. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Reflect, FromReflect, Debug, PartialEq)] pub struct Line { grapheme_index: usize, graphemes: Vec<Grapheme>, diff --git a/kayak_font/src/layout/text.rs b/kayak_font/src/layout/text.rs index fd646a3a1ce8fde5282eecd50deb0b6e2c70aaf5..8d5a59031799cf9979a846bcf526b518e2dd116d 100644 --- a/kayak_font/src/layout/text.rs +++ b/kayak_font/src/layout/text.rs @@ -1,8 +1,10 @@ +use bevy::reflect::{FromReflect, Reflect}; + use crate::{GlyphRect, Line, RowCol}; use std::cmp::Ordering; /// The text alignment. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Reflect, FromReflect, Debug, PartialEq, Eq)] pub enum Alignment { Start, Middle, @@ -10,7 +12,7 @@ pub enum Alignment { } /// Properties to control text layout. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Reflect, FromReflect, Debug, PartialEq)] pub struct TextProperties { /// The font size (in pixels). pub font_size: f32, @@ -39,7 +41,7 @@ impl Default for TextProperties { /// Calculated text layout. /// /// This can be retrieved using [`measure`](crate::KayakFont::measure). -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Reflect, FromReflect, Debug, Default, PartialEq)] pub struct TextLayout { glyphs: Vec<GlyphRect>, lines: Vec<Line>, diff --git a/src/children.rs b/src/children.rs index c2e2de6915d696c5fcc711d0fe562bcc4fe90ff1..d5dd84503177b45045a2eab4141f3a26cd90078d 100644 --- a/src/children.rs +++ b/src/children.rs @@ -3,7 +3,8 @@ use bevy::prelude::*; use crate::prelude::KayakWidgetContext; /// Defers widgets being added to the widget tree. -#[derive(Component, Debug, Default, Clone, PartialEq, Eq)] +#[derive(Component, Reflect, Debug, Default, Clone, PartialEq, Eq)] +#[reflect(Component)] pub struct KChildren { inner: Vec<Entity>, } diff --git a/src/context.rs b/src/context.rs index a8389ab1dc1a75dce070d1f43328bbe51b996bc3..4cac17487e7d4c9e2b924d9c588b43f0d2909f98 100644 --- a/src/context.rs +++ b/src/context.rs @@ -12,6 +12,7 @@ use crate::{ children::KChildren, clone_component::{clone_state, clone_system, EntityCloneSystems, PreviousWidget}, context_entities::ContextEntities, + cursor::PointerEvents, event_dispatcher::EventDispatcher, focus_tree::FocusTree, input::query_world, @@ -20,14 +21,18 @@ use crate::{ node::{DirtyNode, WrappedIndex}, prelude::KayakWidgetContext, render_primitive::RenderPrimitive, - styles::KStyle, + styles::{ + Corner, Edge, KCursorIcon, KPositionType, KStyle, LayoutType, RenderCommand, StyleProp, + Units, + }, tree::{Change, Tree}, widget_state::WidgetState, Focusable, KayakUIPlugin, WindowSize, }; /// A tag component representing when a widget has been mounted(added to the tree). -#[derive(Component)] +#[derive(Component, Reflect, Default)] +#[reflect(Component)] pub struct Mounted; const UPDATE_DEPTH: u32 = 0; @@ -976,6 +981,25 @@ impl Plugin for KayakContextPlugin { .add_system_to_stage(CoreStage::PostUpdate, update_widgets_sys.at_start()) .add_system_to_stage(CoreStage::PostUpdate, calculate_ui.at_end()) .add_system(crate::window_size::update_window_size); + + // Register reflection types. + // A bit annoying.. + app.register_type::<KStyle>() + .register_type::<KChildren>() + .register_type::<WidgetName>() + .register_type::<StyleProp<Color>>() + .register_type::<StyleProp<Corner<f32>>>() + .register_type::<StyleProp<Edge<f32>>>() + .register_type::<StyleProp<Units>>() + .register_type::<StyleProp<KCursorIcon>>() + .register_type::<StyleProp<String>>() + .register_type::<StyleProp<f32>>() + .register_type::<StyleProp<LayoutType>>() + .register_type::<StyleProp<Edge<Units>>>() + .register_type::<StyleProp<PointerEvents>>() + .register_type::<StyleProp<KPositionType>>() + .register_type::<StyleProp<RenderCommand>>() + .register_type::<StyleProp<i32>>(); } } @@ -1040,9 +1064,17 @@ fn calculate_ui(world: &mut World) { /// A simple component that stores the type name of a widget /// This is used by Kayak in order to find out which systems to run. -#[derive(Component, Debug, Clone, PartialEq, Eq)] +#[derive(Component, Reflect, Debug, Clone, PartialEq, Eq)] +#[reflect(Component)] pub struct WidgetName(pub String); +impl Default for WidgetName { + fn default() -> Self { + log::warn!("You did not specify a widget name for a widget!"); + Self("NO_NAME".to_string()) + } +} + impl From<String> for WidgetName { fn from(value: String) -> Self { WidgetName(value) diff --git a/src/cursor.rs b/src/cursor.rs index 2b4539050e4b86d5e3c0aef5be631a9656073f4f..b3df333cc497d41e653d6f4e4973ea23acf92010 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,47 +1,49 @@ -/// Controls how the cursor interacts on a given node -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PointerEvents { - /// Allow all pointer events on this node and its children - All, - /// Allow pointer events on this node but not on its children - SelfOnly, - /// Allow pointer events on this node's children but not on itself - ChildrenOnly, - /// Disallow all pointer events on this node and its children - None, -} - -impl Default for PointerEvents { - fn default() -> Self { - Self::All - } -} - -#[derive(Default, Debug, Copy, Clone, PartialEq)] -pub struct CursorEvent { - pub pressed: bool, - pub just_pressed: bool, - pub just_released: bool, - pub position: (f32, f32), -} - -/// An event created on scroll -#[derive(Default, Debug, Copy, Clone, PartialEq)] -pub struct ScrollEvent { - /// The amount scrolled - pub delta: ScrollUnit, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum ScrollUnit { - /// A scroll unit that goes by a "line of text" - Line { x: f32, y: f32 }, - /// A scroll unit that goes by individual pixels - Pixel { x: f32, y: f32 }, -} - -impl Default for ScrollUnit { - fn default() -> Self { - ScrollUnit::Pixel { x: 0.0, y: 0.0 } - } -} +use bevy::reflect::{FromReflect, Reflect}; + +/// Controls how the cursor interacts on a given node +#[derive(Debug, Reflect, FromReflect, Copy, Clone, PartialEq, Eq)] +pub enum PointerEvents { + /// Allow all pointer events on this node and its children + All, + /// Allow pointer events on this node but not on its children + SelfOnly, + /// Allow pointer events on this node's children but not on itself + ChildrenOnly, + /// Disallow all pointer events on this node and its children + None, +} + +impl Default for PointerEvents { + fn default() -> Self { + Self::All + } +} + +#[derive(Default, Debug, Copy, Clone, PartialEq)] +pub struct CursorEvent { + pub pressed: bool, + pub just_pressed: bool, + pub just_released: bool, + pub position: (f32, f32), +} + +/// An event created on scroll +#[derive(Default, Debug, Copy, Clone, PartialEq)] +pub struct ScrollEvent { + /// The amount scrolled + pub delta: ScrollUnit, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ScrollUnit { + /// A scroll unit that goes by a "line of text" + Line { x: f32, y: f32 }, + /// A scroll unit that goes by individual pixels + Pixel { x: f32, y: f32 }, +} + +impl Default for ScrollUnit { + fn default() -> Self { + ScrollUnit::Pixel { x: 0.0, y: 0.0 } + } +} diff --git a/src/focus_tree.rs b/src/focus_tree.rs index 038e8c66a4b1cdfdf7140167ae8295cf93f496ec..b3dfbc066679cd5026d51183506b7e6cfc25dfc1 100644 --- a/src/focus_tree.rs +++ b/src/focus_tree.rs @@ -1,8 +1,12 @@ -use bevy::{prelude::Component, utils::HashMap}; +use bevy::{ + prelude::{Component, Reflect, ReflectComponent}, + utils::HashMap, +}; use crate::{node::WrappedIndex, prelude::Tree}; -#[derive(Component, Default, Clone, Copy)] +#[derive(Component, Reflect, Default, Clone, Copy)] +#[reflect(Component)] pub struct Focusable; #[derive(Debug, Default, PartialEq, Eq)] diff --git a/src/layout.rs b/src/layout.rs index 8f49ce57135f0da52018c11c4277e02bf73e2650..3cffb5bb5922cae2a0067882d2bfa9ff034bff81 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,13 +1,16 @@ use std::collections::hash_map::Iter; use std::collections::HashMap; -use bevy::prelude::{Entity, Query}; +use bevy::{ + prelude::{Entity, Query}, + reflect::{FromReflect, Reflect}, +}; use morphorm::Cache; pub use morphorm::GeometryChanged; use crate::node::WrappedIndex; -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Debug, Reflect, FromReflect, Default, Clone, Copy, PartialEq)] pub struct Rect { pub posx: f32, pub posy: f32, diff --git a/src/node.rs b/src/node.rs index d10ba9eb9c668b656dadc2e2847fb1b925b984f1..9c655a2613d988c6712df13a0f1fa37c8ebc1f8f 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,4 +1,7 @@ -use bevy::prelude::{Component, Entity, Query}; +use bevy::{ + prelude::{Component, Entity, Query, Reflect, ReflectComponent}, + reflect::FromReflect, +}; use crate::{ render_primitive::RenderPrimitive, @@ -9,7 +12,8 @@ use crate::{ pub struct DirtyNode; /// A widget node used for building the layout tree -#[derive(Debug, Clone, PartialEq, Component)] +#[derive(Debug, Reflect, Clone, PartialEq, Component)] +#[reflect(Component)] pub struct Node { /// The list of children directly under this node pub children: Vec<WrappedIndex>, @@ -107,7 +111,7 @@ impl NodeBuilder { } } -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Reflect, FromReflect, Clone, Copy, Hash, PartialEq, Eq)] pub struct WrappedIndex(pub Entity); impl<'a> morphorm::Node<'a> for WrappedIndex { @@ -117,7 +121,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.layout_type { StyleProp::Default => Some(morphorm::LayoutType::default()), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::LayoutType::default()), }; } @@ -128,7 +132,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.position_type { StyleProp::Default => Some(morphorm::PositionType::default()), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::PositionType::default()), }; } @@ -139,7 +143,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.width { StyleProp::Default => Some(morphorm::Units::Stretch(1.0)), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Stretch(1.0)), }; } @@ -150,7 +154,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.height { StyleProp::Default => Some(morphorm::Units::Stretch(1.0)), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Stretch(1.0)), }; } @@ -161,7 +165,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.min_width { StyleProp::Default => Some(morphorm::Units::Pixels(0.0)), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -172,7 +176,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.min_height { StyleProp::Default => Some(morphorm::Units::Pixels(0.0)), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -183,7 +187,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.max_width { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -194,7 +198,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.max_height { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -206,10 +210,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.left { StyleProp::Default => match node.resolved_styles.offset { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.left), + StyleProp::Value(prop) => Some(prop.left.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -221,10 +225,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.right { StyleProp::Default => match node.resolved_styles.offset { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.right), + StyleProp::Value(prop) => Some(prop.right.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -236,10 +240,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.top { StyleProp::Default => match node.resolved_styles.offset { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.top), + StyleProp::Value(prop) => Some(prop.top.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -251,10 +255,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.bottom { StyleProp::Default => match node.resolved_styles.offset { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.bottom), + StyleProp::Value(prop) => Some(prop.bottom.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -298,10 +302,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.padding_left { StyleProp::Default => match node.resolved_styles.padding { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.left), + StyleProp::Value(prop) => Some(prop.left.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -313,10 +317,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.padding_right { StyleProp::Default => match node.resolved_styles.padding { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.right), + StyleProp::Value(prop) => Some(prop.right.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -328,10 +332,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.padding_top { StyleProp::Default => match node.resolved_styles.padding { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.top), + StyleProp::Value(prop) => Some(prop.top.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -343,10 +347,10 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { return match node.resolved_styles.padding_bottom { StyleProp::Default => match node.resolved_styles.padding { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop.bottom), + StyleProp::Value(prop) => Some(prop.bottom.into()), _ => Some(morphorm::Units::Auto), }, - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -357,7 +361,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.row_between { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } @@ -368,7 +372,7 @@ impl<'a> morphorm::Node<'a> for WrappedIndex { if let Ok(node) = store.get(self.0) { return match node.resolved_styles.col_between { StyleProp::Default => Some(morphorm::Units::Auto), - StyleProp::Value(prop) => Some(prop), + StyleProp::Value(prop) => Some(prop.into()), _ => Some(morphorm::Units::Auto), }; } diff --git a/src/render_primitive.rs b/src/render_primitive.rs index 3a3d95caa8abaaaf5669ddc4233137b2e7ad4df4..9c3fa2d6820b8bc656199be97f0ffffe9d9e30aa 100644 --- a/src/render_primitive.rs +++ b/src/render_primitive.rs @@ -2,10 +2,13 @@ use crate::{ layout::Rect, styles::{Corner, Edge, KStyle, RenderCommand}, }; -use bevy::prelude::{Color, Handle, Image, Vec2}; +use bevy::{ + prelude::{Color, Handle, Image, Vec2}, + reflect::Reflect, +}; use kayak_font::{TextLayout, TextProperties}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Reflect, Clone, PartialEq)] pub enum RenderPrimitive { Empty, Clip { diff --git a/src/styles/corner.rs b/src/styles/corner.rs index dfbfcdbbfeb720ca06d25819392e2e33fa379f54..fa02cdd1f73f6542bce7e84fced2185c68ab332a 100644 --- a/src/styles/corner.rs +++ b/src/styles/corner.rs @@ -1,234 +1,236 @@ -use std::ops::{Mul, MulAssign}; - -/// A struct for defining properties related to the corners of widgets -/// -/// This is useful for things like border radii, etc. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] -pub struct Corner<T> -where - T: Copy + Default + PartialEq, -{ - /// The value of the top-left corner - pub top_left: T, - /// The value of the top-right corner - pub top_right: T, - /// The value of the bottom-left corner - pub bottom_left: T, - /// The value of the bottom-right corner - pub bottom_right: T, -} - -impl<T> Corner<T> -where - T: Copy + Default + PartialEq, -{ - /// Creates a new `Corner` with values individually specified for each corner - /// - /// # Arguments - /// - /// * `top_left`: The top-left corner value - /// * `top_right`: The top_-right corner value - /// * `bottom_left`: The bottom_-left corner value - /// * `bottom_right`: The bottom_-right corner value - /// - pub fn new(top_left: T, top_right: T, bottom_left: T, bottom_right: T) -> Self { - Self { - top_left, - top_right, - bottom_left, - bottom_right, - } - } - - /// Creates a new `Corner` with matching top corners and matching bottom corners - /// - /// # Arguments - /// - /// * `top`: The value of the top corners - /// * `bottom`: The value of the bottom corners - /// - /// ``` - /// # use kayak_core::styles::Corner; - /// // Creates a `Corner` with only the top corners rounded - /// let corner_radius = Corner::vertical(10.0, 0.0); - /// - /// // Creates a `Corner` with only the bottom corners rounded - /// let corner_radius = Corner::vertical(0.0, 10.0); - /// ``` - pub fn vertical(top: T, bottom: T) -> Self { - Self { - top_left: top, - top_right: top, - bottom_left: bottom, - bottom_right: bottom, - } - } - - /// Creates a new `Corner` with matching left corners and matching right corners - /// - /// # Arguments - /// - /// * `left`: The value of the left corners - /// * `right`: The value of the right corners - /// - /// ``` - /// # use kayak_core::styles::Corner; - /// // Creates a `Corner` with only the left corners rounded - /// let corner_radius = Corner::horizontal(10.0, 0.0); - /// - /// // Creates a `Corner` with only the right corners rounded - /// let corner_radius = Corner::horizontal(0.0, 10.0); - /// ``` - pub fn horizontal(left: T, right: T) -> Self { - Self { - top_left: left, - top_right: right, - bottom_left: left, - bottom_right: right, - } - } - - /// Creates a new `Corner` with all corners having the same value - /// - /// # Arguments - /// - /// * `value`: The value of all corners - /// - pub fn all(value: T) -> Self { - Self { - top_left: value, - top_right: value, - bottom_left: value, - bottom_right: value, - } - } - - /// Converts this `Corner` into a tuple matching `(Top Left, Top Right, Bottom Left, Bottom Right)` - pub fn into_tuple(self) -> (T, T, T, T) { - ( - self.top_left, - self.top_right, - self.bottom_left, - self.bottom_right, - ) - } -} - -impl<T> From<Corner<T>> for (T, T, T, T) -where - T: Copy + Default + PartialEq, -{ - /// Creates a tuple matching the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)` - fn from(edge: Corner<T>) -> Self { - edge.into_tuple() - } -} - -impl<T> From<T> for Corner<T> -where - T: Copy + Default + PartialEq, -{ - fn from(value: T) -> Self { - Corner::all(value) - } -} - -impl<T> From<(T, T, T, T)> for Corner<T> -where - T: Copy + Default + PartialEq, -{ - /// Converts the tuple according to the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)` - fn from(value: (T, T, T, T)) -> Self { - Corner::new(value.0, value.1, value.2, value.3) - } -} - -impl<T> Mul<T> for Corner<T> -where - T: Copy + Default + PartialEq + Mul<Output = T>, -{ - type Output = Self; - - fn mul(self, rhs: T) -> Self::Output { - Self { - top_left: self.top_left * rhs, - top_right: self.top_right * rhs, - bottom_left: self.bottom_left * rhs, - bottom_right: self.bottom_right * rhs, - } - } -} - -impl<T> Mul<Corner<T>> for Corner<T> -where - T: Copy + Default + PartialEq + Mul<Output = T>, -{ - type Output = Self; - - fn mul(self, rhs: Corner<T>) -> Self::Output { - Self { - top_left: rhs.top_left * self.top_left, - top_right: rhs.top_right * self.top_right, - bottom_left: rhs.bottom_left * self.bottom_left, - bottom_right: rhs.bottom_right * self.bottom_right, - } - } -} - -impl<T> MulAssign<T> for Corner<T> -where - T: Copy + Default + PartialEq + MulAssign, -{ - fn mul_assign(&mut self, rhs: T) { - self.top_left *= rhs; - self.top_right *= rhs; - self.bottom_left *= rhs; - self.bottom_right *= rhs; - } -} - -impl<T> MulAssign<Corner<T>> for Corner<T> -where - T: Copy + Default + PartialEq + MulAssign, -{ - fn mul_assign(&mut self, rhs: Corner<T>) { - self.top_left *= rhs.top_left; - self.top_right *= rhs.top_right; - self.bottom_left *= rhs.bottom_left; - self.bottom_right *= rhs.bottom_right; - } -} - -#[cfg(test)] -mod tests { - use super::Corner; - - #[test] - fn tuples_should_convert_to_corner() { - let expected = (1.0, 2.0, 3.0, 4.0); - let corner: Corner<f32> = expected.into(); - assert_eq!(expected, corner.into_tuple()); - - let expected = (1.0, 1.0, 1.0, 1.0); - let corner: Corner<f32> = (expected.0).into(); - assert_eq!(expected, corner.into_tuple()); - - let expected = (1.0, 1.0, 1.0, 1.0); - let corner: Corner<f32> = expected.0.into(); - assert_eq!(expected, corner.into_tuple()); - } - - #[test] - fn multiplication_should_work_on_corners() { - let expected = (10.0, 20.0, 30.0, 40.0); - let mut corner = Corner::new(1.0, 2.0, 3.0, 4.0); - - // Basic multiplication - let multiplied = corner * 10.0; - assert_eq!(expected, multiplied.into_tuple()); - - // Multiply and assign - corner *= 10.0; - assert_eq!(expected, corner.into_tuple()); - } -} +use std::ops::{Mul, MulAssign}; + +use bevy::reflect::{FromReflect, Reflect}; + +/// A struct for defining properties related to the corners of widgets +/// +/// This is useful for things like border radii, etc. +#[derive(Debug, Default, Reflect, FromReflect, Copy, Clone, PartialEq, Eq)] +pub struct Corner<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + /// The value of the top-left corner + pub top_left: T, + /// The value of the top-right corner + pub top_right: T, + /// The value of the bottom-left corner + pub bottom_left: T, + /// The value of the bottom-right corner + pub bottom_right: T, +} + +impl<T> Corner<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + /// Creates a new `Corner` with values individually specified for each corner + /// + /// # Arguments + /// + /// * `top_left`: The top-left corner value + /// * `top_right`: The top_-right corner value + /// * `bottom_left`: The bottom_-left corner value + /// * `bottom_right`: The bottom_-right corner value + /// + pub fn new(top_left: T, top_right: T, bottom_left: T, bottom_right: T) -> Self { + Self { + top_left, + top_right, + bottom_left, + bottom_right, + } + } + + /// Creates a new `Corner` with matching top corners and matching bottom corners + /// + /// # Arguments + /// + /// * `top`: The value of the top corners + /// * `bottom`: The value of the bottom corners + /// + /// ``` + /// # use kayak_core::styles::Corner; + /// // Creates a `Corner` with only the top corners rounded + /// let corner_radius = Corner::vertical(10.0, 0.0); + /// + /// // Creates a `Corner` with only the bottom corners rounded + /// let corner_radius = Corner::vertical(0.0, 10.0); + /// ``` + pub fn vertical(top: T, bottom: T) -> Self { + Self { + top_left: top, + top_right: top, + bottom_left: bottom, + bottom_right: bottom, + } + } + + /// Creates a new `Corner` with matching left corners and matching right corners + /// + /// # Arguments + /// + /// * `left`: The value of the left corners + /// * `right`: The value of the right corners + /// + /// ``` + /// # use kayak_core::styles::Corner; + /// // Creates a `Corner` with only the left corners rounded + /// let corner_radius = Corner::horizontal(10.0, 0.0); + /// + /// // Creates a `Corner` with only the right corners rounded + /// let corner_radius = Corner::horizontal(0.0, 10.0); + /// ``` + pub fn horizontal(left: T, right: T) -> Self { + Self { + top_left: left, + top_right: right, + bottom_left: left, + bottom_right: right, + } + } + + /// Creates a new `Corner` with all corners having the same value + /// + /// # Arguments + /// + /// * `value`: The value of all corners + /// + pub fn all(value: T) -> Self { + Self { + top_left: value, + top_right: value, + bottom_left: value, + bottom_right: value, + } + } + + /// Converts this `Corner` into a tuple matching `(Top Left, Top Right, Bottom Left, Bottom Right)` + pub fn into_tuple(self) -> (T, T, T, T) { + ( + self.top_left, + self.top_right, + self.bottom_left, + self.bottom_right, + ) + } +} + +impl<T> From<Corner<T>> for (T, T, T, T) +where + T: Copy + Default + PartialEq + Reflect, +{ + /// Creates a tuple matching the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)` + fn from(edge: Corner<T>) -> Self { + edge.into_tuple() + } +} + +impl<T> From<T> for Corner<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + fn from(value: T) -> Self { + Corner::all(value) + } +} + +impl<T> From<(T, T, T, T)> for Corner<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + /// Converts the tuple according to the pattern: `(Top Left, Top Right, Bottom Left, Bottom Right)` + fn from(value: (T, T, T, T)) -> Self { + Corner::new(value.0, value.1, value.2, value.3) + } +} + +impl<T> Mul<T> for Corner<T> +where + T: Copy + Default + PartialEq + Mul<Output = T> + Reflect, +{ + type Output = Self; + + fn mul(self, rhs: T) -> Self::Output { + Self { + top_left: self.top_left * rhs, + top_right: self.top_right * rhs, + bottom_left: self.bottom_left * rhs, + bottom_right: self.bottom_right * rhs, + } + } +} + +impl<T> Mul<Corner<T>> for Corner<T> +where + T: Copy + Default + PartialEq + Mul<Output = T> + Reflect, +{ + type Output = Self; + + fn mul(self, rhs: Corner<T>) -> Self::Output { + Self { + top_left: rhs.top_left * self.top_left, + top_right: rhs.top_right * self.top_right, + bottom_left: rhs.bottom_left * self.bottom_left, + bottom_right: rhs.bottom_right * self.bottom_right, + } + } +} + +impl<T> MulAssign<T> for Corner<T> +where + T: Copy + Default + PartialEq + MulAssign + Reflect, +{ + fn mul_assign(&mut self, rhs: T) { + self.top_left *= rhs; + self.top_right *= rhs; + self.bottom_left *= rhs; + self.bottom_right *= rhs; + } +} + +impl<T> MulAssign<Corner<T>> for Corner<T> +where + T: Copy + Default + PartialEq + MulAssign + Reflect, +{ + fn mul_assign(&mut self, rhs: Corner<T>) { + self.top_left *= rhs.top_left; + self.top_right *= rhs.top_right; + self.bottom_left *= rhs.bottom_left; + self.bottom_right *= rhs.bottom_right; + } +} + +#[cfg(test)] +mod tests { + use super::Corner; + + #[test] + fn tuples_should_convert_to_corner() { + let expected = (1.0, 2.0, 3.0, 4.0); + let corner: Corner<f32> = expected.into(); + assert_eq!(expected, corner.into_tuple()); + + let expected = (1.0, 1.0, 1.0, 1.0); + let corner: Corner<f32> = (expected.0).into(); + assert_eq!(expected, corner.into_tuple()); + + let expected = (1.0, 1.0, 1.0, 1.0); + let corner: Corner<f32> = expected.0.into(); + assert_eq!(expected, corner.into_tuple()); + } + + #[test] + fn multiplication_should_work_on_corners() { + let expected = (10.0, 20.0, 30.0, 40.0); + let mut corner = Corner::new(1.0, 2.0, 3.0, 4.0); + + // Basic multiplication + let multiplied = corner * 10.0; + assert_eq!(expected, multiplied.into_tuple()); + + // Multiply and assign + corner *= 10.0; + assert_eq!(expected, corner.into_tuple()); + } +} diff --git a/src/styles/edge.rs b/src/styles/edge.rs index dabef3afc026612cdf66b4c9806a7f75183485b2..b11e1bc2de636b494bfbdf7e04807179ecdf784e 100644 --- a/src/styles/edge.rs +++ b/src/styles/edge.rs @@ -1,208 +1,210 @@ -use std::ops::{Mul, MulAssign}; - -/// A struct for defining properties related to the edges of widgets -/// -/// This is useful for things like borders, padding, etc. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] -pub struct Edge<T> -where - T: Copy + Default + PartialEq, -{ - /// The value of the top edge - pub top: T, - /// The value of the right edge - pub right: T, - /// The value of the bottom edge - pub bottom: T, - /// The value of the left edge - pub left: T, -} - -impl<T> Edge<T> -where - T: Copy + Default + PartialEq, -{ - /// Creates a new `Edge` with values individually specified for each edge - /// - /// # Arguments - /// - /// * `top`: The top edge value - /// * `right`: The right edge value - /// * `bottom`: The bottom edge value - /// * `left`: The left edge value - /// - pub fn new(top: T, right: T, bottom: T, left: T) -> Self { - Self { - top, - right, - bottom, - left, - } - } - - /// Creates a new `Edge` with matching vertical edges and matching horizontal edges - /// - /// # Arguments - /// - /// * `vertical`: The value of the vertical edges - /// * `horizontal`: The value of the horizontal edges - /// - pub fn axis(vertical: T, horizontal: T) -> Self { - Self { - top: vertical, - right: horizontal, - bottom: vertical, - left: horizontal, - } - } - - /// Creates a new `Edge` with all edges having the same value - /// - /// # Arguments - /// - /// * `value`: The value of all edges - /// - pub fn all(value: T) -> Self { - Self { - top: value, - right: value, - bottom: value, - left: value, - } - } - - /// Converts this `Edge` into a tuple matching `(Top, Right, Bottom, Left)` - pub fn into_tuple(self) -> (T, T, T, T) { - (self.top, self.right, self.bottom, self.left) - } -} - -impl<T> From<Edge<T>> for (T, T, T, T) -where - T: Copy + Default + PartialEq, -{ - fn from(edge: Edge<T>) -> Self { - edge.into_tuple() - } -} - -impl<T> From<T> for Edge<T> -where - T: Copy + Default + PartialEq, -{ - fn from(value: T) -> Self { - Edge::all(value) - } -} - -impl<T> From<(T, T)> for Edge<T> -where - T: Copy + Default + PartialEq, -{ - fn from(value: (T, T)) -> Self { - Edge::axis(value.0, value.1) - } -} - -impl<T> From<(T, T, T, T)> for Edge<T> -where - T: Copy + Default + PartialEq, -{ - fn from(value: (T, T, T, T)) -> Self { - Edge::new(value.0, value.1, value.2, value.3) - } -} - -impl<T> Mul<T> for Edge<T> -where - T: Copy + Default + PartialEq + Mul<Output = T>, -{ - type Output = Self; - - fn mul(self, rhs: T) -> Self::Output { - Self { - top: self.top * rhs, - right: self.right * rhs, - bottom: self.bottom * rhs, - left: self.left * rhs, - } - } -} - -impl<T> Mul<Edge<T>> for Edge<T> -where - T: Copy + Default + PartialEq + Mul<Output = T>, -{ - type Output = Self; - - fn mul(self, rhs: Edge<T>) -> Self::Output { - Self { - top: rhs.top * self.top, - right: rhs.right * self.right, - bottom: rhs.bottom * self.bottom, - left: rhs.left * self.left, - } - } -} - -impl<T> MulAssign<T> for Edge<T> -where - T: Copy + Default + PartialEq + MulAssign, -{ - fn mul_assign(&mut self, rhs: T) { - self.top *= rhs; - self.right *= rhs; - self.bottom *= rhs; - self.left *= rhs; - } -} - -impl<T> MulAssign<Edge<T>> for Edge<T> -where - T: Copy + Default + PartialEq + MulAssign, -{ - fn mul_assign(&mut self, rhs: Edge<T>) { - self.top *= rhs.top; - self.right *= rhs.right; - self.bottom *= rhs.bottom; - self.left *= rhs.left; - } -} - -#[cfg(test)] -mod tests { - use super::Edge; - - #[test] - fn tuples_should_convert_to_edge() { - let expected = (1.0, 2.0, 3.0, 4.0); - let edge: Edge<f32> = expected.into(); - assert_eq!(expected, edge.into_tuple()); - - let expected = (1.0, 2.0, 1.0, 2.0); - let edge: Edge<f32> = (expected.0, expected.1).into(); - assert_eq!(expected, edge.into_tuple()); - - let expected = (1.0, 1.0, 1.0, 1.0); - let edge: Edge<f32> = (expected.0).into(); - assert_eq!(expected, edge.into_tuple()); - - let expected = (1.0, 1.0, 1.0, 1.0); - let edge: Edge<f32> = expected.0.into(); - assert_eq!(expected, edge.into_tuple()); - } - - #[test] - fn multiplication_should_work_on_edges() { - let expected = (10.0, 20.0, 30.0, 40.0); - let mut corner = Edge::new(1.0, 2.0, 3.0, 4.0); - - // Basic multiplication - let multiplied = corner * 10.0; - assert_eq!(expected, multiplied.into_tuple()); - - // Multiply and assign - corner *= 10.0; - assert_eq!(expected, corner.into_tuple()); - } -} +use std::ops::{Mul, MulAssign}; + +use bevy::reflect::{FromReflect, Reflect}; + +/// A struct for defining properties related to the edges of widgets +/// +/// This is useful for things like borders, padding, etc. +#[derive(Debug, Default, Reflect, FromReflect, Copy, Clone, PartialEq, Eq)] +pub struct Edge<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + /// The value of the top edge + pub top: T, + /// The value of the right edge + pub right: T, + /// The value of the bottom edge + pub bottom: T, + /// The value of the left edge + pub left: T, +} + +impl<T> Edge<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + /// Creates a new `Edge` with values individually specified for each edge + /// + /// # Arguments + /// + /// * `top`: The top edge value + /// * `right`: The right edge value + /// * `bottom`: The bottom edge value + /// * `left`: The left edge value + /// + pub fn new(top: T, right: T, bottom: T, left: T) -> Self { + Self { + top, + right, + bottom, + left, + } + } + + /// Creates a new `Edge` with matching vertical edges and matching horizontal edges + /// + /// # Arguments + /// + /// * `vertical`: The value of the vertical edges + /// * `horizontal`: The value of the horizontal edges + /// + pub fn axis(vertical: T, horizontal: T) -> Self { + Self { + top: vertical, + right: horizontal, + bottom: vertical, + left: horizontal, + } + } + + /// Creates a new `Edge` with all edges having the same value + /// + /// # Arguments + /// + /// * `value`: The value of all edges + /// + pub fn all(value: T) -> Self { + Self { + top: value, + right: value, + bottom: value, + left: value, + } + } + + /// Converts this `Edge` into a tuple matching `(Top, Right, Bottom, Left)` + pub fn into_tuple(self) -> (T, T, T, T) { + (self.top, self.right, self.bottom, self.left) + } +} + +impl<T> From<Edge<T>> for (T, T, T, T) +where + T: Copy + Default + PartialEq + Reflect, +{ + fn from(edge: Edge<T>) -> Self { + edge.into_tuple() + } +} + +impl<T> From<T> for Edge<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + fn from(value: T) -> Self { + Edge::all(value) + } +} + +impl<T> From<(T, T)> for Edge<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + fn from(value: (T, T)) -> Self { + Edge::axis(value.0, value.1) + } +} + +impl<T> From<(T, T, T, T)> for Edge<T> +where + T: Copy + Default + PartialEq + Reflect, +{ + fn from(value: (T, T, T, T)) -> Self { + Edge::new(value.0, value.1, value.2, value.3) + } +} + +impl<T> Mul<T> for Edge<T> +where + T: Copy + Default + PartialEq + Mul<Output = T> + Reflect, +{ + type Output = Self; + + fn mul(self, rhs: T) -> Self::Output { + Self { + top: self.top * rhs, + right: self.right * rhs, + bottom: self.bottom * rhs, + left: self.left * rhs, + } + } +} + +impl<T> Mul<Edge<T>> for Edge<T> +where + T: Copy + Default + PartialEq + Mul<Output = T> + Reflect, +{ + type Output = Self; + + fn mul(self, rhs: Edge<T>) -> Self::Output { + Self { + top: rhs.top * self.top, + right: rhs.right * self.right, + bottom: rhs.bottom * self.bottom, + left: rhs.left * self.left, + } + } +} + +impl<T> MulAssign<T> for Edge<T> +where + T: Copy + Default + PartialEq + MulAssign + Reflect, +{ + fn mul_assign(&mut self, rhs: T) { + self.top *= rhs; + self.right *= rhs; + self.bottom *= rhs; + self.left *= rhs; + } +} + +impl<T> MulAssign<Edge<T>> for Edge<T> +where + T: Copy + Default + PartialEq + MulAssign + Reflect, +{ + fn mul_assign(&mut self, rhs: Edge<T>) { + self.top *= rhs.top; + self.right *= rhs.right; + self.bottom *= rhs.bottom; + self.left *= rhs.left; + } +} + +#[cfg(test)] +mod tests { + use super::Edge; + + #[test] + fn tuples_should_convert_to_edge() { + let expected = (1.0, 2.0, 3.0, 4.0); + let edge: Edge<f32> = expected.into(); + assert_eq!(expected, edge.into_tuple()); + + let expected = (1.0, 2.0, 1.0, 2.0); + let edge: Edge<f32> = (expected.0, expected.1).into(); + assert_eq!(expected, edge.into_tuple()); + + let expected = (1.0, 1.0, 1.0, 1.0); + let edge: Edge<f32> = (expected.0).into(); + assert_eq!(expected, edge.into_tuple()); + + let expected = (1.0, 1.0, 1.0, 1.0); + let edge: Edge<f32> = expected.0.into(); + assert_eq!(expected, edge.into_tuple()); + } + + #[test] + fn multiplication_should_work_on_edges() { + let expected = (10.0, 20.0, 30.0, 40.0); + let mut corner = Edge::new(1.0, 2.0, 3.0, 4.0); + + // Basic multiplication + let multiplied = corner * 10.0; + assert_eq!(expected, multiplied.into_tuple()); + + // Multiply and assign + corner *= 10.0; + assert_eq!(expected, corner.into_tuple()); + } +} diff --git a/src/styles/mod.rs b/src/styles/mod.rs index c6c91acb511bb0f6524d7a5804dfec3c386c3901..2ffda25864e0c179e0e809fc2f4d3d60982e277c 100644 --- a/src/styles/mod.rs +++ b/src/styles/mod.rs @@ -3,9 +3,11 @@ mod edge; mod options_ref; mod render_command; mod style; +mod units; pub use corner::Corner; pub use edge::Edge; pub use options_ref::AsRefOption; pub use render_command::RenderCommand; pub use style::*; +pub use units::*; diff --git a/src/styles/render_command.rs b/src/styles/render_command.rs index eafb32c58690efa6c2da550465f31c745f29d591..d5a2630214bf9738b15858a23017f631f7c9110c 100644 --- a/src/styles/render_command.rs +++ b/src/styles/render_command.rs @@ -1,36 +1,39 @@ -use bevy::prelude::{Handle, Image, Vec2}; -use kayak_font::Alignment; - -use super::Edge; - -#[derive(Debug, Clone, PartialEq)] -pub enum RenderCommand { - Empty, - /// Represents a node that has no renderable object but contributes to the layout. - Layout, - Clip, - Quad, - Text { - content: String, - alignment: Alignment, - word_wrap: bool, - }, - Image { - handle: Handle<Image>, - }, - TextureAtlas { - position: Vec2, - size: Vec2, - handle: Handle<Image>, - }, - NinePatch { - border: Edge<f32>, - handle: Handle<Image>, - }, -} - -impl Default for RenderCommand { - fn default() -> Self { - Self::Empty - } -} +use bevy::{ + prelude::{Handle, Image, Vec2}, + reflect::{FromReflect, Reflect}, +}; +use kayak_font::Alignment; + +use super::Edge; + +#[derive(Debug, Reflect, FromReflect, Clone, PartialEq)] +pub enum RenderCommand { + Empty, + /// Represents a node that has no renderable object but contributes to the layout. + Layout, + Clip, + Quad, + Text { + content: String, + alignment: Alignment, + word_wrap: bool, + }, + Image { + handle: Handle<Image>, + }, + TextureAtlas { + position: Vec2, + size: Vec2, + handle: Handle<Image>, + }, + NinePatch { + border: Edge<f32>, + handle: Handle<Image>, + }, +} + +impl Default for RenderCommand { + fn default() -> Self { + Self::Empty + } +} diff --git a/src/styles/style.rs b/src/styles/style.rs index 4439a2fd49a286e919c6a078d124e6365a4f1efe..70b7f8f17344d4dd1e606ca5530f1cc5c4095bbe 100644 --- a/src/styles/style.rs +++ b/src/styles/style.rs @@ -2,10 +2,13 @@ use std::ops::Add; +pub use super::units::{KPositionType, LayoutType, Units}; use bevy::prelude::Color; use bevy::prelude::Component; +use bevy::prelude::ReflectComponent; +use bevy::reflect::FromReflect; +use bevy::reflect::Reflect; use bevy::window::CursorIcon; -pub use morphorm::{LayoutType, PositionType as KPositionType, Units}; use crate::cursor::PointerEvents; @@ -15,8 +18,14 @@ pub use super::Edge; use super::RenderCommand; /// Just a wrapper around bevy's CursorIcon so we can define a default. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct KCursorIcon(pub CursorIcon); +#[derive(Debug, Reflect, Clone, PartialEq, Eq)] +pub struct KCursorIcon(#[reflect(ignore)] pub CursorIcon); + +impl FromReflect for KCursorIcon { + fn from_reflect(_reflect: &dyn Reflect) -> Option<Self> { + None + } +} impl Default for KCursorIcon { fn default() -> Self { @@ -27,8 +36,8 @@ impl Default for KCursorIcon { /// The base container of all style properties /// /// The default value for this enum is [`StyleProp::Unset`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum StyleProp<T: Default + Clone> { +#[derive(Debug, Reflect, FromReflect, Clone, PartialEq, Eq)] +pub enum StyleProp<T: Default + Clone + Reflect + FromReflect> { /// This prop is unset, meaning its actual value is not determined until style resolution, /// wherein it will be set to the property's default value. /// @@ -46,7 +55,7 @@ pub enum StyleProp<T: Default + Clone> { impl<T> Default for StyleProp<T> where - T: Default + Clone, + T: Default + Clone + Reflect + FromReflect, { fn default() -> Self { Self::Unset @@ -55,7 +64,7 @@ where impl<T> StyleProp<T> where - T: Default + Clone, + T: Default + Clone + Reflect + FromReflect, { /// Resolves this style property into a concrete value. /// @@ -122,7 +131,7 @@ where } } -impl<T: Default + Clone> From<T> for StyleProp<T> { +impl<T: Default + Clone + Reflect + FromReflect> From<T> for StyleProp<T> { fn from(value: T) -> Self { StyleProp::Value(value) } @@ -239,7 +248,8 @@ define_styles! { /// // Applied second (sets any remaining `StyleProp::Unset` fields) /// .with_style(&style_b); /// ``` - #[derive(Component, Debug, Default, Clone, PartialEq)] + #[derive(Component, Reflect, FromReflect, Debug, Default, Clone, PartialEq)] + #[reflect(Component)] pub struct KStyle { /// The background color of this widget /// @@ -275,6 +285,7 @@ define_styles! { /// The spacing between child widgets along the horizontal axis pub col_between: StyleProp<Units>, /// The cursor icon to display when hovering this widget + #[reflect(ignore)] pub cursor: StyleProp<KCursorIcon>, /// The font name for this widget /// diff --git a/src/styles/units.rs b/src/styles/units.rs new file mode 100644 index 0000000000000000000000000000000000000000..01c563153c3db11b0b397b677c2a89f2ada5b967 --- /dev/null +++ b/src/styles/units.rs @@ -0,0 +1,126 @@ +use bevy::reflect::{FromReflect, Reflect}; + +/// The layout type determines how nodes will be positioned when directed by the parent +#[derive(Debug, FromReflect, Reflect, Clone, Copy, PartialEq)] +pub enum LayoutType { + /// Stack child elements horizontally + Row, + /// Stack child elements vertically + Column, + /// Position child elements into specified rows and columns + Grid, +} + +impl Default for LayoutType { + fn default() -> Self { + LayoutType::Column + } +} + +impl Into<morphorm::LayoutType> for LayoutType { + fn into(self) -> morphorm::LayoutType { + match self { + LayoutType::Column => morphorm::LayoutType::Column, + LayoutType::Row => morphorm::LayoutType::Row, + LayoutType::Grid => morphorm::LayoutType::Grid, + } + } +} + +/// The position type determines whether a node will be positioned in-line with its siblings or seperate +#[derive(Debug, Reflect, FromReflect, Clone, Copy, PartialEq)] +pub enum KPositionType { + /// Node is positioned relative to parent but ignores its siblings + SelfDirected, + /// Node is positioned relative to parent and in-line with siblings + ParentDirected, +} + +impl Default for KPositionType { + fn default() -> Self { + KPositionType::ParentDirected + } +} + +impl Into<morphorm::PositionType> for KPositionType { + fn into(self) -> morphorm::PositionType { + match self { + Self::ParentDirected => morphorm::PositionType::ParentDirected, + Self::SelfDirected => morphorm::PositionType::SelfDirected, + } + } +} + +/// Units which describe spacing and size +#[derive(Debug, FromReflect, Reflect, Clone, Copy, PartialEq)] +pub enum Units { + /// A number of pixels + Pixels(f32), + /// A percentage of the parent dimension + Percentage(f32), + /// A factor of the remaining free space + Stretch(f32), + /// Automatically determine the value + Auto, +} + +impl Default for Units { + fn default() -> Self { + Units::Auto + } +} + +impl Into<morphorm::Units> for Units { + fn into(self) -> morphorm::Units { + match self { + Self::Pixels(value) => morphorm::Units::Pixels(value), + Self::Percentage(value) => morphorm::Units::Percentage(value), + Self::Stretch(value) => morphorm::Units::Stretch(value), + Self::Auto => morphorm::Units::Auto, + } + } +} + +impl Units { + /// Converts the units to an f32 value + pub fn value_or(&self, parent_value: f32, auto: f32) -> f32 { + match self { + &Units::Pixels(pixels) => pixels, + &Units::Percentage(percentage) => (percentage / 100.0) * parent_value, + &Units::Stretch(_) => auto, + &Units::Auto => auto, + } + } + + /// Returns true if the value is in pixels + pub fn is_pixels(&self) -> bool { + match self { + Units::Pixels(_) => true, + _ => false, + } + } + + /// Returns true if the value is a percentage + pub fn is_percentage(&self) -> bool { + match self { + Units::Percentage(_) => true, + _ => false, + } + } + + /// Returns true if the value is a stretch factor + pub fn is_stretch(&self) -> bool { + match self { + Units::Stretch(_) => true, + _ => false, + } + } + + /// Returns true if the value is auto + pub fn is_auto(&self) -> bool { + match self { + Units::Auto => true, + _ => false, + } + } +} diff --git a/src/widgets/app.rs b/src/widgets/app.rs index 9d371177800e0a1acfc94a5745c6f84952d1026a..c234a92142ae98331e46de8ec5eb3b67e720b352 100644 --- a/src/widgets/app.rs +++ b/src/widgets/app.rs @@ -1,12 +1,11 @@ use bevy::prelude::*; use kayak_ui_macros::rsx; -use morphorm::Units; use crate::{ children::KChildren, context::WidgetName, prelude::KayakWidgetContext, - styles::{KStyle, RenderCommand, StyleProp}, + styles::{KStyle, RenderCommand, StyleProp, Units}, widget::{EmptyState, Widget, WidgetParam}, CameraUIKayak, };