From 4a5a6b426ae8a92025775c762107a39f81cea475 Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Tue, 11 Jun 2024 08:07:34 +0200 Subject: [PATCH 1/7] Started porting components from Bevypunk --- crates/bevy_lunex/src/lib.rs | 16 ++++- crates/bevy_lunex/src/logic/core.rs | 76 +++++++++++++++++++++ crates/bevy_lunex/src/logic/mod.rs | 21 ++++++ crates/bevy_lunex/src/structs.rs | 9 +-- crates/bevy_lunex/src/systems.rs | 8 +-- docs/src/advanced/abstraction/components.md | 4 +- 6 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 crates/bevy_lunex/src/logic/core.rs create mode 100644 crates/bevy_lunex/src/logic/mod.rs diff --git a/crates/bevy_lunex/src/lib.rs b/crates/bevy_lunex/src/lib.rs index f8503f8..8ba58e5 100644 --- a/crates/bevy_lunex/src/lib.rs +++ b/crates/bevy_lunex/src/lib.rs @@ -1,6 +1,15 @@ #![doc = include_str!("../README.md")] -use bevy::prelude::*; +// #==============================# +// #=== IMPORTS FOR THIS CRATE ===# + +pub (crate) use std::{borrow::Borrow, marker::PhantomData}; +pub (crate) use bevy::prelude::*; +pub (crate) use colored::Colorize; +pub (crate) use lunex_engine::prelude::*; + +#[cfg(feature = "picking")] +pub (crate) use bevy_mod_picking::prelude::*; // #======================# @@ -30,7 +39,10 @@ pub use events::*; pub mod interaction; pub use interaction::*; -pub mod macros; +pub mod logic; +pub use logic::*; + +//pub mod macros; #[cfg(feature = "picking")] pub mod picking; diff --git a/crates/bevy_lunex/src/logic/core.rs b/crates/bevy_lunex/src/logic/core.rs new file mode 100644 index 0000000..df1b40e --- /dev/null +++ b/crates/bevy_lunex/src/logic/core.rs @@ -0,0 +1,76 @@ +use crate::*; + + +// #==============# +// #=== EVENTS ===# + +/// This is an event you can listen to which broadcasts the entity the pointer clicked on. +/// [`UiClickEmitter`] is the component creating these events. +#[derive(Event, Debug, Clone, PartialEq, Eq)] +pub struct UiClickEvent { + /// The targetted entity that was clicked on + pub target: Entity, +} + +/// This is an event you can listen to which broadcasts the entity that changed its value. +/// This event is for example created when you change values in text-input field, spinbox, +/// radio button, etc. +#[derive(Event, Debug, Clone, PartialEq, Eq)] +pub struct UiChangeEvent { + /// The targetted entity that changed its value + pub target: Entity, + /// The new value + pub value: String, +} + + +// #=================# +// #=== LISTENERS ===# + +/// When clicked on this entity, it will create [`UiClickEvent`] event for the specified entity. +/// This component is commonly used in abstraction, where you want to listen to pointer events +/// from another entity that is not the parent and send that data over. +#[derive(Component, Debug, Clone, PartialEq, Eq)] +pub struct UiClickEmitter { + pub trigger: Option, +} +impl UiClickEmitter { + /// The entity will create the event for itself and not other entities. + pub const SELF: UiClickEmitter = UiClickEmitter { trigger: None }; + /// Specify the entity you want to create events for. + pub fn new(entity: Entity) -> Self { + UiClickEmitter { + trigger: Some(entity) + } + } +} + +/// System that triggers when a pointer clicks a node and emmits an event +fn ui_click_listener_system(mut events: EventReader>, mut write: EventWriter, query: Query<(&UiClickEmitter, Entity)>) { + for event in events.read() { + if let Ok((emitter, entity)) = query.get(event.target) { + write.send(UiClickEvent { + target: if let Some(e) = emitter.trigger { e } else { entity }, + }); + } + } +} + + +// #====================# +// #=== HOVER PLUGIN ===# + +/// Plugin adding all our logic +pub struct CorePlugin; +impl Plugin for CorePlugin { + fn build(&self, app: &mut App) { + app + // Add our events + + .add_event::() + .add_systems(Update, ui_click_listener_system.run_if(on_event::>())) + + .add_event::() + ; + } +} \ No newline at end of file diff --git a/crates/bevy_lunex/src/logic/mod.rs b/crates/bevy_lunex/src/logic/mod.rs new file mode 100644 index 0000000..8bbbbeb --- /dev/null +++ b/crates/bevy_lunex/src/logic/mod.rs @@ -0,0 +1,21 @@ +pub mod core; +pub use core::*; + +//pub mod hover; +//pub use hover::*; + + +// #====================# +// #=== LOGIC PLUGIN ===# + +use bevy::prelude::*; + +/// Plugin adding all our route logic +pub struct LogicPlugin; +impl Plugin for LogicPlugin { + fn build(&self, app: &mut App) { + app + .add_plugins(CorePlugin); + //.add_plugins(HoverPlugin); + } +} \ No newline at end of file diff --git a/crates/bevy_lunex/src/structs.rs b/crates/bevy_lunex/src/structs.rs index 9889e6e..6ffb9c1 100644 --- a/crates/bevy_lunex/src/structs.rs +++ b/crates/bevy_lunex/src/structs.rs @@ -1,10 +1,5 @@ -use std::{borrow::Borrow, marker::PhantomData}; - -use bevy::{prelude::*, render::primitives::Aabb, sprite::Anchor, text::{Text2dBounds, TextLayoutInfo}}; -use lunex_engine::prelude::*; -#[cfg(feature = "picking")] -use bevy_mod_picking::prelude::*; - +use crate::*; +use bevy::{render::primitives::Aabb, sprite::Anchor, text::{Text2dBounds, TextLayoutInfo}}; // #==================# diff --git a/crates/bevy_lunex/src/systems.rs b/crates/bevy_lunex/src/systems.rs index 7e3d317..404f29c 100644 --- a/crates/bevy_lunex/src/systems.rs +++ b/crates/bevy_lunex/src/systems.rs @@ -1,11 +1,7 @@ -use std::marker::PhantomData; -use bevy::{math::Vec3A, prelude::*, render::primitives::Aabb, text::TextLayoutInfo, window::PrimaryWindow}; -#[cfg(feature = "debug")] -use colored::Colorize; +use crate::*; +use bevy::{math::Vec3A, render::primitives::Aabb, text::TextLayoutInfo, window::PrimaryWindow}; use lunex_engine::*; -use crate::{Dimension, Element, MovableByCamera, UiContent, UiDepthBias, UiLink}; - // #===================# // #=== CORE SYSTEM ===# diff --git a/docs/src/advanced/abstraction/components.md b/docs/src/advanced/abstraction/components.md index ce403ba..7a34679 100644 --- a/docs/src/advanced/abstraction/components.md +++ b/docs/src/advanced/abstraction/components.md @@ -39,14 +39,14 @@ fn build_route(mut commands: Commands, assets: Res, query: Query::from(UiTree::new("MyRoute")), + UiTreeBundle::::from(UiTree::new("CustomButton")), // Now spawn the UI as children )).with_children(|ui| { // Spawn some UI nodes ui.spawn(( // Link this widget // Note that CustomButtonUi is used here instead of MainUi - UiLink::::path("Control/Image"), + UiLink::::path("Image"), // Add layout UiLayout::window_full().pack(), From fe7ada032e8f5f0d83a309d96c59c7d658290efe Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Tue, 11 Jun 2024 08:14:35 +0200 Subject: [PATCH 2/7] small fix --- crates/bevy_lunex/src/lib.rs | 4 +++- crates/bevy_lunex/src/structs.rs | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/bevy_lunex/src/lib.rs b/crates/bevy_lunex/src/lib.rs index 8ba58e5..d8e2739 100644 --- a/crates/bevy_lunex/src/lib.rs +++ b/crates/bevy_lunex/src/lib.rs @@ -5,9 +5,11 @@ pub (crate) use std::{borrow::Borrow, marker::PhantomData}; pub (crate) use bevy::prelude::*; -pub (crate) use colored::Colorize; pub (crate) use lunex_engine::prelude::*; +#[cfg(feature = "debug")] +pub (crate) use colored::Colorize; + #[cfg(feature = "picking")] pub (crate) use bevy_mod_picking::prelude::*; diff --git a/crates/bevy_lunex/src/structs.rs b/crates/bevy_lunex/src/structs.rs index 6ffb9c1..3c0d4b2 100644 --- a/crates/bevy_lunex/src/structs.rs +++ b/crates/bevy_lunex/src/structs.rs @@ -80,8 +80,9 @@ impl Dimension { } -/// This struct holds depth bias that will be relatively added to [`Transform`] `Z` after layout calculation. +/// This struct holds depth bias that will be relatively added to `depth` in the layout calculation. /// Nodes will higher depth bias will be placed on top nodes with lower depth bias. +/// It is recursive. #[derive(Component, Debug, Default, Clone, Copy, PartialEq)] pub struct UiDepthBias (pub f32); From 394946e1f5bddfc8a15fb3ce092487c4b242aa03 Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Tue, 11 Jun 2024 10:05:46 +0200 Subject: [PATCH 3/7] Implemented Layout state generic --- crates/bevy_lunex/src/events/mod.rs | 4 +- crates/bevy_lunex/src/structs.rs | 175 ++++++++++++++++++++--- crates/bevy_lunex/src/systems.rs | 10 +- crates/lunex_engine/src/core/compute.rs | 10 +- crates/lunex_engine/src/core/structs.rs | 4 +- crates/lunex_engine/src/layout/layout.rs | 72 +++++----- crates/lunex_engine/src/layout/mod.rs | 2 +- examples/minimal/src/main.rs | 4 +- 8 files changed, 211 insertions(+), 70 deletions(-) diff --git a/crates/bevy_lunex/src/events/mod.rs b/crates/bevy_lunex/src/events/mod.rs index f1407b9..43ed0bd 100644 --- a/crates/bevy_lunex/src/events/mod.rs +++ b/crates/bevy_lunex/src/events/mod.rs @@ -1,9 +1,9 @@ use bevy::prelude::*; #[cfg(feature = "debug")] use colored::Colorize; -use lunex_engine::UiLayout; +//use lunex_engine::Layout; -use crate::Cursor2d; +use crate::{Cursor2d, UiLayout}; // #==============# diff --git a/crates/bevy_lunex/src/structs.rs b/crates/bevy_lunex/src/structs.rs index 3c0d4b2..3c12a85 100644 --- a/crates/bevy_lunex/src/structs.rs +++ b/crates/bevy_lunex/src/structs.rs @@ -2,8 +2,19 @@ use crate::*; use bevy::{render::primitives::Aabb, sprite::Anchor, text::{Text2dBounds, TextLayoutInfo}}; -// #==================# -// #=== COMPONENTS ===# +// #========================# +// #=== STATE COMPONENTS ===# + + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Base; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Hover; + + +// #=========================# +// #=== MARKER COMPONENTS ===# /// This struct marks [`UiTree`] entity to receive piped [`Camera`] size and position to its [`Dimension`] and [`Transform`] component. #[derive(Component, Debug, Default, Clone, Copy, PartialEq)] @@ -16,6 +27,23 @@ pub struct MovableByCamera; pub struct Element; +// #======================# +// #=== STD COMPONENTS ===# + +/// This struct holds rectangular data. If the component covers some kind of 2D area, it should be stored in this component. +/// Lunex uses this component to mirror node size in & out from parent [`UiTree`]. +#[derive(Component, Debug, Default, Clone, Copy, PartialEq)] +pub struct Dimension { + pub size: Vec2, +} +impl Dimension { + pub fn new(size: impl Into) -> Self { + Dimension { + size: size.into() + } + } +} + /// # WIP - used for Div layout #[derive(Component, Debug, Default, Clone, Copy, PartialEq)] pub struct UiContent { @@ -28,6 +56,133 @@ impl UiContent { } +// #=======================# +// #=== MAIN COMPONENTS ===# + +#[derive(Component, Debug, Copy, Clone, PartialEq)] +pub struct UiLayout { + pub layout: Layout, + state: PhantomData, +} +impl UiLayout { + /// **Boundary** - Declarative layout type that is defined by its top-left corner and bottom-right corner. + /// Nodes with this layout are not included in the ui flow. + /// ## 🛠️ Example + /// ``` + /// # use lunex_engine::{UiLayout, Rl}; + /// let layout: UiLayout = UiLayout::boundary().pos1(Rl(20.0)).pos2(Rl(80.0)).pack(); + /// ``` + pub fn boundary() -> ui::Boundary { + ui::Boundary::new() + } + /// **Window** - Declarative layout type that is defined by its size and position. + /// Nodes with this layout are not included in the ui flow. + /// ## 🛠️ Example + /// ``` + /// # use lunex_engine::{UiLayout, Ab, Rl}; + /// let layout: UiLayout = UiLayout::window().pos(Ab(100.0)).size(Rl(50.0)).pack(); + /// ``` + pub fn window() -> ui::Window { + ui::Window::new() + } + /// **Window** (full) - Declarative layout type that is defined by its size and position. + /// Nodes with this layout are not included in the ui flow. + /// ## 🛠️ Example + /// ``` + /// # use lunex_engine::{UiLayout, Rl}; + /// let layout: UiLayout = UiLayout::window().size(Rl(100.0)).pack(); // Same as UiLayout::window_full() + /// ``` + pub fn window_full() -> ui::Window { + ui::Window::full() + } + /// **Solid** - Declarative layout type that is defined by its width and height ratio. + /// Scales in a way to fit itself inside parent container. It never deforms. + /// Nodes with this layout are not included in the ui flow. + /// ## 🛠️ Example + /// ``` + /// # use lunex_engine::UiLayout; + /// let layout: UiLayout = UiLayout::solid().size((4.0, 3.0)).align_x(-0.8).pack(); + /// ``` + pub fn solid() -> ui::Solid { + ui::Solid::new() + } + /// **Div** - Parametric layout type that is defined by margin, border and padding. Its location and size + /// is based on the surrounding nodes, like HTML. It is also the only node layout that uses the [`Sp`] unit. + /// You can use this unit for alignment and justification. + /// ## 🛠️ Example + /// ``` + /// # use lunex_engine::{UiLayout, Sp}; + /// let layout: UiLayout = UiLayout::new().pad_x(2.0).margin_y(Sp(1.0)).br().pack(); + /// ``` + pub fn div() -> ui::Div { + ui::Div::new() + } +} +impl UiLayout { + /// Creates struct from layout + pub fn from(layout: impl Into) -> UiLayout { + UiLayout { + layout: layout.into(), + state: PhantomData, + } + } +} +impl Default for UiLayout { + fn default() -> Self { + UiLayout { + layout: Layout::default(), + state: PhantomData, + } + } +} + +pub trait PackageLayout { + fn pack(self) -> UiLayout; +} + +// Implement packaging +impl Into> for ui::Boundary { + fn into(self) -> UiLayout { + self.pack::() + } +} +impl PackageLayout for ui::Boundary { + fn pack(self) -> UiLayout { + UiLayout::::from(self) + } +} +impl Into> for ui::Window { + fn into(self) -> UiLayout { + self.pack::() + } +} +impl PackageLayout for ui::Window { + fn pack(self) -> UiLayout { + UiLayout::::from(self) + } +} +impl Into> for ui::Solid { + fn into(self) -> UiLayout { + self.pack::() + } +} +impl PackageLayout for ui::Solid { + fn pack(self) -> UiLayout { + UiLayout::::from(self) + } +} +impl Into> for ui::Div { + fn into(self) -> UiLayout { + self.pack::() + } +} +impl PackageLayout for ui::Div { + fn pack(self) -> UiLayout { + UiLayout::::from(self) + } +} + + /// This struct is a string reference to a specific node in a parent [`UiTree`]. /// Lunex uses this component to locate what data this entity should be working with. #[derive(Component, Debug, Clone, PartialEq)] @@ -65,27 +220,13 @@ impl Default for UiLink { } -/// This struct holds rectangular data. If the component covers some kind of 2D area, it should be stored in this component. -/// Lunex uses this component to mirror node size in & out from parent [`UiTree`]. -#[derive(Component, Debug, Default, Clone, Copy, PartialEq)] -pub struct Dimension { - pub size: Vec2, -} -impl Dimension { - pub fn new(size: impl Into) -> Self { - Dimension { - size: size.into() - } - } -} - - /// This struct holds depth bias that will be relatively added to `depth` in the layout calculation. /// Nodes will higher depth bias will be placed on top nodes with lower depth bias. /// It is recursive. #[derive(Component, Debug, Default, Clone, Copy, PartialEq)] pub struct UiDepthBias (pub f32); + // #====================# // #=== MAIN BUNDLES ===# diff --git a/crates/bevy_lunex/src/systems.rs b/crates/bevy_lunex/src/systems.rs index 404f29c..7c45d61 100644 --- a/crates/bevy_lunex/src/systems.rs +++ b/crates/bevy_lunex/src/systems.rs @@ -44,7 +44,7 @@ pub fn debug_draw_gizmo( let mut color = Color::LIME_GREEN; - if let UiLayout::Solid(_) = container.layout { color = Color::YELLOW } + if let Layout::Solid(_) = container.layout { color = Color::YELLOW } let mut pos = container.rectangle.pos.invert_y() + transform.translation(); pos.x += container.rectangle.size.x / 2.0; @@ -175,7 +175,7 @@ pub fn send_layout_to_node( if let Some(container) = node.obtain_data_mut() { #[cfg(feature = "debug")] info!("{} {} - Received Layout data", "->".blue(), link.path.yellow().bold()); - container.layout = *layout; + container.layout = layout.layout; } } } @@ -393,9 +393,9 @@ pub fn element_text_size_to_layout( for (mut layout, text_info) in &mut query { #[cfg(feature = "debug")] info!("{} {} - Converted text size into Layout", "--".yellow(), "ELEMENT".red()); - match layout.as_mut() { - UiLayout::Window(window) => {window.size = Rh(text_info.logical_size).into()}, - UiLayout::Solid(solid) => {solid.size = Ab(text_info.logical_size).into()}, + match &mut layout.layout { + Layout::Window(window) => {window.size = Rh(text_info.logical_size).into()}, + Layout::Solid(solid) => {solid.size = Ab(text_info.logical_size).into()}, _ => {}, } } diff --git a/crates/lunex_engine/src/core/compute.rs b/crates/lunex_engine/src/core/compute.rs index c25c41b..ac21bdb 100644 --- a/crates/lunex_engine/src/core/compute.rs +++ b/crates/lunex_engine/src/core/compute.rs @@ -6,7 +6,7 @@ use crate::NodeTopDataTrait; use crate::UiNode; use crate::UiTree; use crate::Rectangle3D; -use crate::UiLayout; +use crate::Layout; /// Trait with [`UiTree`] layout computation methods. pub trait UiNodeTreeComputeTrait { @@ -56,18 +56,18 @@ impl UiNodeComputeTrait for UiNode { // Compute node layout match &node_data.layout { - UiLayout::Div(_) => { + Layout::Div(_) => { is_parametric = true; }, - UiLayout::Boundary(l) => { + Layout::Boundary(l) => { node_data.rectangle = l.compute(parent.into(), absolute_scale, viewport_size, font_size).into(); skip = false; }, - UiLayout::Window(l) => { + Layout::Window(l) => { node_data.rectangle = l.compute(parent.into(), absolute_scale, viewport_size, font_size).into(); skip = false; }, - UiLayout::Solid(l) => { + Layout::Solid(l) => { node_data.rectangle = l.compute(parent.into(), absolute_scale, viewport_size, font_size).into(); skip = false; }, diff --git a/crates/lunex_engine/src/core/structs.rs b/crates/lunex_engine/src/core/structs.rs index 0c27a0d..8e7c64e 100644 --- a/crates/lunex_engine/src/core/structs.rs +++ b/crates/lunex_engine/src/core/structs.rs @@ -5,7 +5,7 @@ use bevy::ecs::component::Component; use colored::Colorize; use crate::nodes::prelude::*; -use crate::layout::UiLayout; +use crate::layout::Layout; // #==================# @@ -197,7 +197,7 @@ pub struct NodeData { /// Calculated rectangle from layout. pub rectangle: Rectangle3D, /// Layout of this node. - pub layout: UiLayout, + pub layout: Layout, /// Layout of subnodes and how to stack them. pub stack: UiStack, /// Optional font size to overwrite the inherited master font size. diff --git a/crates/lunex_engine/src/layout/layout.rs b/crates/lunex_engine/src/layout/layout.rs index 6d4162f..ce57b0e 100644 --- a/crates/lunex_engine/src/layout/layout.rs +++ b/crates/lunex_engine/src/layout/layout.rs @@ -19,13 +19,13 @@ use crate::{NiceDisplay, Rectangle2D, UiValue, UiValueEvaluate, Ab, Rl}; /// The expected range is `-1.0` to `1.0`, but you can extrapolate. #[cfg_attr(feature = "bevy", derive(Component))] #[derive(Debug, Clone, Copy, PartialEq)] -pub enum UiLayout { +pub enum Layout { Boundary(Boundary), Window(Window), Solid(Solid), Div(Div), } -impl UiLayout { +impl Layout { /// **Boundary** - Declarative layout type that is defined by its top-left corner and bottom-right corner. /// Nodes with this layout are not included in the ui flow. @@ -87,72 +87,72 @@ impl UiLayout { /// Unwrap the type, panic if not Boundary variant pub fn expect_boundary(&self) -> &Boundary { match self { - UiLayout::Boundary(b) => b, + Layout::Boundary(b) => b, _ => panic!("A different layout type than expected! Got {}, expected Boundary", self.to_nicestr()) } } /// Unwrap the type, panic if not Boundary variant pub fn expect_boundary_mut(&mut self) -> &mut Boundary { match self { - UiLayout::Boundary(b) => b, + Layout::Boundary(b) => b, _ => panic!("A different layout type than expected! Got {}, expected Boundary", self.to_nicestr()) } } /// Unwrap the type, panic if not Window variant pub fn expect_window(&self) -> &Window { match self { - UiLayout::Window(w) => w, + Layout::Window(w) => w, _ => panic!("A different layout type than expected! Got {}, expected Window", self.to_nicestr()) } } /// Unwrap the type, panic if not Window variant pub fn expect_window_mut(&mut self) -> &mut Window { match self { - UiLayout::Window(w) => w, + Layout::Window(w) => w, _ => panic!("A different layout type than expected! Got {}, expected Window", self.to_nicestr()) } } /// Unwrap the type, panic if not Solid variant pub fn expect_solid(&self) -> &Solid { match self { - UiLayout::Solid(s) => s, + Layout::Solid(s) => s, _ => panic!("A different layout type than expected! Got {}, expected Solid", self.to_nicestr()) } } /// Unwrap the type, panic if not Solid variant pub fn expect_solid_mut(&mut self) -> &mut Solid { match self { - UiLayout::Solid(s) => s, + Layout::Solid(s) => s, _ => panic!("A different layout type than expected! Got {}, expected Solid", self.to_nicestr()) } } /// Unwrap the type, panic if not Div variant pub fn expect_div(&self) -> &Div { match self { - UiLayout::Div(d) => d, + Layout::Div(d) => d, _ => panic!("A different layout type than expected! Got {}, expected Div", self.to_nicestr()) } } /// Unwrap the type, panic if not Div variant pub fn expect_div_mut(&mut self) -> &mut Div { match self { - UiLayout::Div(d) => d, + Layout::Div(d) => d, _ => panic!("A different layout type than expected! Got {}, expected Div", self.to_nicestr()) } } } -impl Default for UiLayout { +impl Default for Layout { fn default() -> Self { Window::new().size(Rl(100.0)).into() } } -impl NiceDisplay for UiLayout { +impl NiceDisplay for Layout { fn to_nicestr(&self) -> String { match self { - UiLayout::Boundary(layout) => format!("{} {}", "Boundary".bold().bright_cyan(), layout.to_nicestr()), - UiLayout::Solid(layout) => format!("{} {}", "Solid".bold().bright_cyan(), layout.to_nicestr()), - UiLayout::Window(layout) => format!("{} {}", "Window".bold().bright_cyan(), layout.to_nicestr()), - UiLayout::Div(layout) => format!("{} {}", "Div".bold().bright_cyan(), layout.to_nicestr()), + Layout::Boundary(layout) => format!("{} {}", "Boundary".bold().bright_cyan(), layout.to_nicestr()), + Layout::Solid(layout) => format!("{} {}", "Solid".bold().bright_cyan(), layout.to_nicestr()), + Layout::Window(layout) => format!("{} {}", "Window".bold().bright_cyan(), layout.to_nicestr()), + Layout::Div(layout) => format!("{} {}", "Div".bold().bright_cyan(), layout.to_nicestr()), } } } @@ -408,14 +408,14 @@ impl Boundary { size: pos2 - pos1, } } - /// Packs the struct into UiLayout. - pub fn pack(self) -> UiLayout { + /// Packs the struct into Layout. + pub fn package(self) -> Layout { self.into() } } -impl Into for Boundary { - fn into(self) -> UiLayout { - UiLayout::Boundary(self) +impl Into for Boundary { + fn into(self) -> Layout { + Layout::Boundary(self) } } impl NiceDisplay for Boundary { @@ -532,14 +532,14 @@ impl Window { size, } } - /// Packs the struct into UiLayout. - pub fn pack(self) -> UiLayout { + /// Packs the struct into Layout. + pub fn package(self) -> Layout { self.into() } } -impl Into for Window { - fn into(self) -> UiLayout { - UiLayout::Window(self) +impl Into for Window { + fn into(self) -> Layout { + Layout::Window(self) } } impl NiceDisplay for Window { @@ -660,14 +660,14 @@ impl Solid { size: (computed_width, computed_height).into(), } } - /// Packs the struct into UiLayout. - pub fn pack(self) -> UiLayout { + /// Packs the struct into Layout. + pub fn package(self) -> Layout { self.into() } } -impl Into for Solid { - fn into(self) -> UiLayout { - UiLayout::Solid(self) +impl Into for Solid { + fn into(self) -> Layout { + Layout::Solid(self) } } impl NiceDisplay for Solid { @@ -1054,14 +1054,14 @@ impl Div { self.margin.set_w(margin); } - /// Packs the struct into UiLayout - pub fn pack(self) -> UiLayout { + /// Packs the struct into Layout + pub fn package(self) -> Layout { self.into() } } -impl Into for Div { - fn into(self) -> UiLayout { - UiLayout::Div(self) +impl Into for Div { + fn into(self) -> Layout { + Layout::Div(self) } } impl NiceDisplay for Div { diff --git a/crates/lunex_engine/src/layout/mod.rs b/crates/lunex_engine/src/layout/mod.rs index 91ef25d..63933ce 100644 --- a/crates/lunex_engine/src/layout/mod.rs +++ b/crates/lunex_engine/src/layout/mod.rs @@ -8,7 +8,7 @@ pub use stack::*; // #=== PRELUDE EXPORT ===# pub mod prelude { - pub use super::UiLayout; + pub use super::Layout; pub use super::{Align, Scaling, Sizing}; pub use super::UiStack; diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 84f3028..0ed48b4 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -35,12 +35,12 @@ fn setup(mut cmd: Commands, mut _mat: ResMut>, assets: ui.spawn(( UiLink::::path("Root"), - UiLayout::boundary().pos1(Ab(20.0)).pos2(Rl(100.0) - Ab(20.0)).pack(), + UiLayout::boundary().pos1(Ab(20.0)).pos2(Rl(100.0) - Ab(20.0)).pack::(), )); ui.spawn(( UiLink::::path("Root/Rectangle"), - UiLayout::solid().size((Ab(1920.0), Ab(1080.0))).pack(), + UiLayout::solid().size((Ab(1920.0), Ab(1080.0))).pack::(), UiImage2dBundle::from(assets.load("background.png")), )); From 6e7976239be780bf3058b449a62e58a0cc955bb8 Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Tue, 11 Jun 2024 10:07:02 +0200 Subject: [PATCH 4/7] comment out the disambiguities --- crates/bevy_lunex/src/lib.rs | 4 ++-- crates/bevy_lunex/src/structs.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_lunex/src/lib.rs b/crates/bevy_lunex/src/lib.rs index d8e2739..697a031 100644 --- a/crates/bevy_lunex/src/lib.rs +++ b/crates/bevy_lunex/src/lib.rs @@ -41,8 +41,8 @@ pub use events::*; pub mod interaction; pub use interaction::*; -pub mod logic; -pub use logic::*; +//pub mod logic; +//pub use logic::*; //pub mod macros; diff --git a/crates/bevy_lunex/src/structs.rs b/crates/bevy_lunex/src/structs.rs index 3c12a85..2ad62d9 100644 --- a/crates/bevy_lunex/src/structs.rs +++ b/crates/bevy_lunex/src/structs.rs @@ -9,8 +9,8 @@ use bevy::{render::primitives::Aabb, sprite::Anchor, text::{Text2dBounds, TextLa #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Base; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Hover; +//#[derive(Debug, Copy, Clone, PartialEq, Eq)] +//pub struct Hover; // #=========================# From ed623e468a2faf00e168274a68a41df06752bdc6 Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Tue, 11 Jun 2024 10:15:49 +0200 Subject: [PATCH 5/7] Documentation changes --- docs/src/advanced/abstraction/components.md | 4 ++-- docs/src/advanced/abstraction/routes.md | 2 +- docs/src/advanced/layouts.md | 6 +++--- docs/src/advanced/text.md | 2 +- docs/src/overview.md | 2 +- docs/src/quick_start.md | 6 ++++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/src/advanced/abstraction/components.md b/docs/src/advanced/abstraction/components.md index 7a34679..547a5fd 100644 --- a/docs/src/advanced/abstraction/components.md +++ b/docs/src/advanced/abstraction/components.md @@ -49,7 +49,7 @@ fn build_route(mut commands: Commands, assets: Res, query: Query::path("Image"), // Add layout - UiLayout::window_full().pack(), + UiLayout::window_full().pack::(), // Give it a background image UiImage2dBundle { @@ -95,7 +95,7 @@ To spawn the component, you have to spawn it as UI node of another UI system, ei // Spawning the component ui.spawn(( UiLink::::path("Button"), - UiLayout::window().size(Rl(50.0)).pack(), + UiLayout::window().size(Rl(50.0)).pack::(), CustomButton { text: "PRESS ME!".to_string(), diff --git a/docs/src/advanced/abstraction/routes.md b/docs/src/advanced/abstraction/routes.md index 24b140a..5bd02e9 100644 --- a/docs/src/advanced/abstraction/routes.md +++ b/docs/src/advanced/abstraction/routes.md @@ -30,7 +30,7 @@ fn build_route(mut commands: Commands, assets: Res, query: Query::path("Background"), - UiLayout::solid().size((1920.0, 1080.0)).scaling(Scaling::Fill).pack(), + UiLayout::solid().size((1920.0, 1080.0)).scaling(Scaling::Fill).pack::(), UiImage2dBundle::from(assets.load("images/background.png")), )); }); diff --git a/docs/src/advanced/layouts.md b/docs/src/advanced/layouts.md index 6c6321c..facb71d 100644 --- a/docs/src/advanced/layouts.md +++ b/docs/src/advanced/layouts.md @@ -13,7 +13,7 @@ This will make a node start at `20%` and end at `80%` on both axis from the pare UiLayout::boundary() .pos1(Rl(20.0)) .pos2(Rl(80.0)) - .pack() + .pack::() ``` ### Window @@ -29,7 +29,7 @@ UiLayout::window() .pos(Rl((53.0, 15.0))) .anchor(Anchor::Center) .size(Rl((60.0, 65.0))) - .pack() + .pack::() ``` ### Solid @@ -47,7 +47,7 @@ Then we can align it horizontally. UiLayout::solid() .size((881.0, 1600.0)) .align_x(-0.74) - .pack(), + .pack::(), ``` ### Div diff --git a/docs/src/advanced/text.md b/docs/src/advanced/text.md index b9a5d33..d90323f 100644 --- a/docs/src/advanced/text.md +++ b/docs/src/advanced/text.md @@ -14,7 +14,7 @@ UiLink::::path("Text"), // Here we can define where we want to position our text within the parent node, // don't worry about size, that is picked up and overwritten automaticaly by Lunex to match text size. -UiLayout::window().pos(Rl((5., 50.))).anchor(Anchor::CenterLeft).pack(), +UiLayout::window().pos(Rl((5., 50.))).anchor(Anchor::CenterLeft).pack::(), // Add text UiText2dBundle { diff --git a/docs/src/overview.md b/docs/src/overview.md index 1560c96..b9db1d1 100644 --- a/docs/src/overview.md +++ b/docs/src/overview.md @@ -30,7 +30,7 @@ commands.spawn(( UiLink::::path("Menu/Button"), // Here you can define the layout using the provided units - UiLayout::window().pos(Rl((50.0, 50.0))).size((Rh(45.0), Rl(60.0))).pack(), + UiLayout::window().pos(Rl((50.0, 50.0))).size((Rh(45.0), Rl(60.0))).pack::(), // #=== CUSTOMIZATION ===# diff --git a/docs/src/quick_start.md b/docs/src/quick_start.md index eee31f4..a8acefc 100644 --- a/docs/src/quick_start.md +++ b/docs/src/quick_start.md @@ -80,6 +80,8 @@ Now, any entity with `UiLayout` + `UiLink` spawned as a child of the `UiTree` wi You can add a `UiImage2dBundle` to the entity to add images to your widgets. Or you can add another `UiTree` as a child, which will use the computed size output in `Dimension` component instead of a `Camera` piping the size to it. +The generic at `pack::()` represents state. Now leave it at `Base`, but when you later want to add hover animation use `Hover` instead. + ```rust ui.spawn(( @@ -87,7 +89,7 @@ ui.spawn(( UiLink::::path("Root"), // Specify UI layout - UiLayout::window_full().pos(Ab(20.0)).size(Rl(100.0) - Ab(40.0)).pack(), + UiLayout::window_full().pos(Ab(20.0)).size(Rl(100.0) - Ab(40.0)).pack::(), )); ui.spawn(( @@ -96,7 +98,7 @@ ui.spawn(( UiLink::::path("Root/Rectangle"), // Specify UI layout - UiLayout::solid().size(Ab((1920.0, 1080.0))).pack(), + UiLayout::solid().size(Ab((1920.0, 1080.0))).pack::(), // Add image to the entity UiImage2dBundle::from(assets.load("background.png")), From 01b2e0a6dcfb5637ab6c37466c57911b5ad6308c Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Wed, 12 Jun 2024 17:13:22 +0200 Subject: [PATCH 6/7] wasm fix --- Cargo.toml | 2 +- crates/bevy_lunex/src/interaction/cursor2d.rs | 4 +- crates/bevy_lunex/src/lib.rs | 10 +- crates/bevy_lunex/src/logic/actions.rs | 195 ++++++++++++++++++ crates/bevy_lunex/src/logic/mod.rs | 3 + 5 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 crates/bevy_lunex/src/logic/actions.rs diff --git a/Cargo.toml b/Cargo.toml index f06dea7..186d1b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,4 +44,4 @@ "bevy_gizmos", ] } - bevy_mod_picking = { version = "0.18.2", default_features = false, features = [] } \ No newline at end of file + bevy_mod_picking = { version = "0.19.0", default_features = false, features = [] } \ No newline at end of file diff --git a/crates/bevy_lunex/src/interaction/cursor2d.rs b/crates/bevy_lunex/src/interaction/cursor2d.rs index 650c561..38d9508 100644 --- a/crates/bevy_lunex/src/interaction/cursor2d.rs +++ b/crates/bevy_lunex/src/interaction/cursor2d.rs @@ -35,7 +35,7 @@ impl Cursor2d { hidden: false, } } - /// A toggle if this cursor should native + /// A toggle if this cursor should be native pub fn native_cursor(mut self, enable: bool) -> Self { self.native_cursor = enable; self @@ -59,7 +59,7 @@ pub fn cursor_update( mut windows: Query<&mut Window, With>, mut for (cursor, mut transform, mut visibility) in &mut query { window.cursor.visible = if cursor.native_cursor { !cursor.hidden } else { false }; - window.cursor.icon = cursor.cursor_request; + if window.cursor.visible { window.cursor.icon = cursor.cursor_request; } match window.cursor_position() { Some(position) => { diff --git a/crates/bevy_lunex/src/lib.rs b/crates/bevy_lunex/src/lib.rs index 697a031..21efd88 100644 --- a/crates/bevy_lunex/src/lib.rs +++ b/crates/bevy_lunex/src/lib.rs @@ -35,14 +35,14 @@ impl Plugin for UiGeneralPlugin { // #======================# // #=== PRELUDE EXPORT ===# -pub mod events; -pub use events::*; +//pub mod events; +//pub use events::*; pub mod interaction; pub use interaction::*; -//pub mod logic; -//pub use logic::*; +pub mod logic; +pub use logic::*; //pub mod macros; @@ -61,7 +61,7 @@ pub use systems::*; pub mod prelude { pub use super::Cursor2d; - pub use super::events::{SetColor, SetUiLayout}; + pub use super::{SetColor, SetUiLayout}; // BEVY-LUNEX SPECIFIC pub use super::UiGeneralPlugin; diff --git a/crates/bevy_lunex/src/logic/actions.rs b/crates/bevy_lunex/src/logic/actions.rs new file mode 100644 index 0000000..baa755c --- /dev/null +++ b/crates/bevy_lunex/src/logic/actions.rs @@ -0,0 +1,195 @@ +use bevy::window::{EnabledButtons, PresentMode, PrimaryWindow, WindowMode}; + +use crate::*; + + +// #=====================# +// #=== ACTION EVENTS ===# +// These events behave like [`AppExit`] event. +// When you call them something will happen. + +// #=== WINDOW ===# + +/// This event will change the presentation mode (VSYNC) +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowPresentModeAction (pub PresentMode); + +/// This event will change the window mode (FULLSCREEN) +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowModeAction (pub WindowMode); + +/// This event will change the window position +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowPositionAction (pub WindowPosition); + +/// This event will change the window title +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowTitleAction (pub String); + +/// This event will change the window size +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowResolutionAction (pub Vec2); + +/// This event will change the window resize contstrains +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowResizeConstrainsAction (pub WindowResizeConstraints); + +/// This event will change if window is resizable +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowResizableAction (pub bool); + +/// This event will change the enabled buttons for the window +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowEnabledButtonsAction (pub EnabledButtons); + +/// This event will change if window decorations are available +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowDecorationsAction (pub bool); + +/// This event will focus OS on the window +#[derive(Event, Debug, Clone, PartialEq)] +pub struct SetWindowFocusAction (pub bool); + + + + + + + +fn set_window_present_mode_action(mut events: EventReader, mut query: Query<&mut Window, With>) { + for event in events.read() { + if let Ok(window) = &mut query.get_single_mut() { + #[cfg(feature = "debug")] + info!("{} - Changed present mode to: {:?}", "ACTION".red().bold(), event.0); + window.present_mode = event.0; + } + } +} + +fn set_window_mode_action(mut events: EventReader, mut query: Query<&mut Window, With>) { + for event in events.read() { + if let Ok(window) = &mut query.get_single_mut() { + #[cfg(feature = "debug")] + info!("{} - Changed window mode to: {:?}", "ACTION".red().bold(), event.0); + window.mode = event.0; + } + } +} + +fn set_window_position_action(mut events: EventReader, mut query: Query<&mut Window, With>) { + for event in events.read() { + if let Ok(window) = &mut query.get_single_mut() { + #[cfg(feature = "debug")] + info!("{} - Changed window position to: {:?}", "ACTION".red().bold(), event.0); + window.position = event.0; + } + } +} + +fn set_window_title_action(mut events: EventReader, mut query: Query<&mut Window, With>) { + for event in events.read() { + if let Ok(window) = &mut query.get_single_mut() { + #[cfg(feature = "debug")] + info!("{} - Changed window title to: {:?}", "ACTION".red().bold(), event.0); + window.title = event.0.clone(); + } + } +} + +fn set_window_resolution_action(mut events: EventReader, mut query: Query<&mut Window, With>) { + for event in events.read() { + if let Ok(window) = &mut query.get_single_mut() { + #[cfg(feature = "debug")] + info!("{} - Changed window resolution to: {:?}", "ACTION".red().bold(), event.0); + window.resolution.set(event.0.x, event.0.y); + } + } +} + +fn set_window_resize_constrains_action(mut events: EventReader, mut query: Query<&mut Window, With>) { + for event in events.read() { + if let Ok(window) = &mut query.get_single_mut() { + #[cfg(feature = "debug")] + info!("{} - Changed window position to: {:?}", "ACTION".red().bold(), event.0); + window.resize_constraints = event.0; + } + } +} + + + + + + + + + + +/// This event will override layout of targetted entity +#[derive(Event, PartialEq, Clone, Copy)] +pub struct HideCursor2d (pub bool); +fn apply_event_hide_cursor_2d(mut events: EventReader, mut query: Query<&mut Cursor2d>) { + for event in events.read() { + for mut cursor in &mut query { + #[cfg(feature = "debug")] + info!("{} - Set cursor to hidden: {}", "EVENT".purple().bold(), event.0); + cursor.hidden = event.0; + } + } +} + +/// This event will override layout of targetted entity +#[derive(Event, PartialEq, Clone, Copy)] +pub struct SetUiLayout { + pub target: Entity, + pub layout: UiLayout, +} +fn apply_event_set_ui_layout(mut events: EventReader, mut query: Query<&mut UiLayout>) { + for event in events.read() { + if let Ok(mut layout) = query.get_mut(event.target) { + if layout.clone() != event.layout{ + *layout = event.layout; + } + } + } +} + +/// This event will override sprite/text color of targetted entity +#[derive(Event, PartialEq, Clone, Copy)] +pub struct SetColor { + pub target: Entity, + pub color: Color, +} +fn apply_event_set_color(mut events: EventReader, mut query: Query<(Option<&mut Sprite>, Option<&mut Text>)>) { + for event in events.read() { + if let Ok((sprite_option, text_option)) = query.get_mut(event.target) { + if let Some(mut sprite) = sprite_option { + sprite.color = event.color; + } + if let Some(mut text) = text_option { + for section in &mut text.sections { + section.style.color = event.color; + } + } + } + } +} + + +// #==============# +// #=== PLUGIN ===# + +pub struct UiEventPlugin; +impl Plugin for UiEventPlugin { + fn build(&self, app: &mut App) { + app + .add_event::() + .add_systems(Update, apply_event_hide_cursor_2d.run_if(on_event::())) + + .add_event::() + .add_systems(Update, apply_event_set_ui_layout.run_if(on_event::())) + + .add_event::() + .add_systems(Update, apply_event_set_color.run_if(on_event::())); + } +} \ No newline at end of file diff --git a/crates/bevy_lunex/src/logic/mod.rs b/crates/bevy_lunex/src/logic/mod.rs index 8bbbbeb..11b83fe 100644 --- a/crates/bevy_lunex/src/logic/mod.rs +++ b/crates/bevy_lunex/src/logic/mod.rs @@ -1,3 +1,6 @@ +pub mod actions; +pub use actions::*; + pub mod core; pub use core::*; From 9ec4a740ed51be0a9da641a1df3595695acf3a79 Mon Sep 17 00:00:00 2001 From: IDEDARY Date: Wed, 12 Jun 2024 17:16:07 +0200 Subject: [PATCH 7/7] added systems for actions --- crates/bevy_lunex/src/logic/actions.rs | 30 +++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/bevy_lunex/src/logic/actions.rs b/crates/bevy_lunex/src/logic/actions.rs index baa755c..0718cc7 100644 --- a/crates/bevy_lunex/src/logic/actions.rs +++ b/crates/bevy_lunex/src/logic/actions.rs @@ -110,7 +110,7 @@ fn set_window_resize_constrains_action(mut events: EventReader() + .add_systems(Update, set_window_present_mode_action.run_if(on_event::())) + + .add_event::() + .add_systems(Update, set_window_mode_action.run_if(on_event::())) + + .add_event::() + .add_systems(Update, set_window_position_action.run_if(on_event::())) + + .add_event::() + .add_systems(Update, set_window_title_action.run_if(on_event::())) + + .add_event::() + .add_systems(Update, set_window_resolution_action.run_if(on_event::())) + + .add_event::() + .add_systems(Update, set_window_resize_constrains_action.run_if(on_event::())) + + + + + + + + + + .add_event::() .add_systems(Update, apply_event_hide_cursor_2d.run_if(on_event::()))