From fe1adb6cf60a9bad8566560e1205a1eccd1af3c5 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 18 Jul 2020 14:08:46 -0700 Subject: [PATCH] ui: focus/click/hover system. initial buttons --- Cargo.toml | 4 + crates/bevy_ecs/hecs/src/query.rs | 81 ++++++----- crates/bevy_ecs/src/lib.rs | 2 +- .../src/schedule/parallel_executor.rs | 2 + crates/bevy_ecs/src/schedule/schedule.rs | 2 + crates/bevy_ecs/src/system/into_system.rs | 16 +++ crates/bevy_render/src/color.rs | 2 +- crates/bevy_ui/Cargo.toml | 2 + crates/bevy_ui/src/entity.rs | 66 ++++++++- crates/bevy_ui/src/focus.rs | 133 ++++++++++++++++++ crates/bevy_ui/src/lib.rs | 15 +- .../src/{ui_update_system.rs => update.rs} | 0 crates/bevy_ui/src/widget/button.rs | 1 + crates/bevy_ui/src/widget/mod.rs | 2 + crates/bevy_winit/src/lib.rs | 6 +- examples/ui/button.rs | 65 +++++++++ examples/ui/ui.rs | 22 +-- examples/ui/ui_bench.rs | 2 +- 18 files changed, 363 insertions(+), 60 deletions(-) create mode 100644 crates/bevy_ui/src/focus.rs rename crates/bevy_ui/src/{ui_update_system.rs => update.rs} (100%) create mode 100644 crates/bevy_ui/src/widget/button.rs create mode 100644 examples/ui/button.rs diff --git a/Cargo.toml b/Cargo.toml index 8274c7ccc5..2f8ea26f61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -176,6 +176,10 @@ path = "examples/shader/shader_custom_material.rs" name = "shader_defs" path = "examples/shader/shader_defs.rs" +[[example]] +name = "button" +path = "examples/ui/button.rs" + [[example]] name = "text" path = "examples/ui/text.rs" diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 878b1d9634..d02401f75e 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -14,7 +14,11 @@ // modified by Bevy contributors -use core::{marker::PhantomData, ptr::NonNull, ops::{Deref, DerefMut}}; +use core::{ + marker::PhantomData, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; use crate::{archetype::Archetype, Component, Entity}; @@ -615,7 +619,7 @@ macro_rules! tuple_impl { let ($($name,)*) = self; ($($name.next(),)*) } - + unsafe fn should_skip(&self) -> bool { #[allow(non_snake_case)] let ($($name,)*) = self; @@ -702,58 +706,65 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { } #[allow(missing_docs)] -pub struct Changed(PhantomData<(Q, fn(T))>); +pub struct Changed<'a, T> { + value: &'a T, +} -impl Query for Changed { - type Fetch = FetchChanged; +impl<'a, T: Component> Deref for Changed<'a, T> { + type Target = T; + fn deref(&self) -> &T { + self.value + } +} + +impl<'a, T: Component> Query for Changed<'a, T> { + type Fetch = FetchChanged; } #[doc(hidden)] -pub struct FetchChanged(F, PhantomData, NonNull); +pub struct FetchChanged(NonNull, NonNull); -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchChanged { - type Item = F::Item; +impl<'a, T: Component> Fetch<'a> for FetchChanged { + type Item = Changed<'a, T>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { - F::access(archetype) + Some(Access::Read) } else { None } } fn borrow(archetype: &Archetype) { - F::borrow(archetype) + archetype.borrow::(); } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - if !archetype.has::() { - return None; - } - Some(Self( - F::get(archetype, offset)?, - PhantomData, - NonNull::new_unchecked(archetype.get_modified::()?.as_ptr().add(offset)), - )) + let components = NonNull::new_unchecked(archetype.get::()?.as_ptr().add(offset)); + let modified = NonNull::new_unchecked(archetype.get_modified::()?.as_ptr().add(offset)); + Some(Self(components, modified)) } fn release(archetype: &Archetype) { - F::release(archetype) + archetype.release::(); } unsafe fn should_skip(&self) -> bool { // skip if the current item wasn't changed - !*self.2.as_ref() || self.0.should_skip() + !*self.1.as_ref() } - unsafe fn next(&mut self) -> F::Item { - self.2 = NonNull::new_unchecked(self.2.as_ptr().add(1)); - self.0.next() + #[inline] + unsafe fn next(&mut self) -> Self::Item { + self.1 = NonNull::new_unchecked(self.1.as_ptr().add(1)); + let value = self.0.as_ptr(); + self.0 = NonNull::new_unchecked(value.add(1)); + Changed { value: &*value } } } #[cfg(test)] mod tests { - use crate::{Entity, World, Mut, Changed}; - use std::{vec::Vec, vec}; + use crate::{Changed, Entity, Mut, World}; + use std::{vec, vec::Vec}; use super::*; @@ -784,8 +795,9 @@ mod tests { fn get_changed_a(world: &World) -> Vec { world - .query::>() + .query::<(Changed, Entity)>() .iter() + .map(|(_a, e)| e) .collect::>() }; @@ -823,14 +835,15 @@ mod tests { world.clear_trackers(); assert!(world - .query::>() + .query::<(Changed, Entity)>() .iter() + .map(|(_a, e)| e) .collect::>() .is_empty()); } #[test] - fn nested_changed_query() { + fn multiple_changed_query() { let mut world = World::default(); world.spawn((A(0), B(0))); let e2 = world.spawn((A(0), B(0))); @@ -845,16 +858,10 @@ mod tests { } let a_b_changed = world - .query::>>() + .query::<(Changed, Changed, Entity)>() .iter() + .map(|(_a, _b, e)| e) .collect::>(); assert_eq!(a_b_changed, vec![e2]); - - let a_b_changed_tuple = world - .query::<(Changed, Changed)>() - .iter() - .map(|(e, _b)| e) - .collect::>(); - assert_eq!(a_b_changed_tuple, vec![e2]); } -} \ No newline at end of file +} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index ae6f4fa188..19f000385b 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -16,6 +16,6 @@ pub mod prelude { Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System, }, world::WorldBuilderSource, - Bundle, Component, Entity, Ref, RefMut, With, Without, World, + Bundle, Changed, Component, Entity, Ref, RefMut, With, Without, World, }; } diff --git a/crates/bevy_ecs/src/schedule/parallel_executor.rs b/crates/bevy_ecs/src/schedule/parallel_executor.rs index 08a89d5f7d..e46618dc58 100644 --- a/crates/bevy_ecs/src/schedule/parallel_executor.rs +++ b/crates/bevy_ecs/src/schedule/parallel_executor.rs @@ -52,6 +52,8 @@ impl ParallelExecutor { executor_stage.run(world, resources, stage_systems); } } + + world.clear_trackers(); } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index a805078755..22e01b602b 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -131,6 +131,8 @@ impl Schedule { } } } + + world.clear_trackers(); } // TODO: move this code to ParallelExecutor diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 5ea56b4cde..e9952084a3 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -150,6 +150,8 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { self.world.query::() } + /// Gets a reference to the entity's component of the given type. This will fail if the entity does not have + /// the given component type or if the given component type does not match this query. pub fn get(&self, entity: Entity) -> Result, QueryComponentError> { if let Some(location) = self.world.get_entity_location(entity) { if self @@ -170,6 +172,8 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } } + /// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have + /// the given component type or if the given component type does not match this query. pub fn get_mut( &self, entity: Entity, @@ -192,6 +196,18 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { )) } } + + /// Sets the entity's component to the given value. This will fail if the entity does not already have + /// the given component type or if the given component type does not match this query. + pub fn set( + &self, + entity: Entity, + component: T, + ) -> Result<(), QueryComponentError> { + let mut current = self.get_mut::(entity)?; + *current = component; + Ok(()) + } } struct QuerySystemState { diff --git a/crates/bevy_render/src/color.rs b/crates/bevy_render/src/color.rs index ff826d3c26..75dbcd388c 100644 --- a/crates/bevy_render/src/color.rs +++ b/crates/bevy_render/src/color.rs @@ -23,7 +23,7 @@ unsafe impl Byteable for Color {} impl Color { pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0); - pub const BLACK: Color = Color::rgb(0.0, 1.0, 0.0); + pub const BLACK: Color = Color::rgb(0.0, 0.0, 0.0); pub const RED: Color = Color::rgb(1.0, 0.0, 0.0); pub const GREEN: Color = Color::rgb(0.0, 1.0, 0.0); pub const BLUE: Color = Color::rgb(0.0, 0.0, 1.0); diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index fbefea7e2f..39f419a25d 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -7,9 +7,11 @@ edition = "2018" [dependencies] bevy_app = { path = "../bevy_app" } bevy_asset = { path = "../bevy_asset" } +bevy_core = { path = "../bevy_core" } bevy_type_registry = { path = "../bevy_type_registry" } bevy_derive = { path = "../bevy_derive" } bevy_ecs = { path = "../bevy_ecs" } +bevy_input = { path = "../bevy_input" } bevy_sprite = { path = "../bevy_sprite" } bevy_text = { path = "../bevy_text" } bevy_transform = { path = "../bevy_transform" } diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index f617fbf32c..50bd8ede11 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -1,5 +1,9 @@ use super::Node; -use crate::{render::UI_PIPELINE_HANDLE, widget::Label}; +use crate::{ + render::UI_PIPELINE_HANDLE, + widget::{Button, Label}, + Click, Hover, FocusPolicy, +}; use bevy_asset::Handle; use bevy_ecs::Bundle; use bevy_render::{ @@ -11,7 +15,7 @@ use bevy_sprite::{ColorMaterial, QUAD_HANDLE}; use bevy_transform::prelude::{Rotation, Scale, Transform, Translation}; #[derive(Bundle)] -pub struct UiComponents { +pub struct NodeComponents { pub node: Node, pub mesh: Handle, // TODO: maybe abstract this out pub material: Handle, @@ -23,9 +27,9 @@ pub struct UiComponents { pub scale: Scale, } -impl Default for UiComponents { +impl Default for NodeComponents { fn default() -> Self { - UiComponents { + NodeComponents { mesh: QUAD_HANDLE, render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized( UI_PIPELINE_HANDLE, @@ -83,3 +87,57 @@ impl Default for LabelComponents { } } } + +#[derive(Bundle)] +pub struct ButtonComponents { + pub node: Node, + pub button: Button, + pub click: Click, + pub hover: Hover, + pub focus_policy: FocusPolicy, + pub mesh: Handle, // TODO: maybe abstract this out + pub material: Handle, + pub draw: Draw, + pub render_pipelines: RenderPipelines, + pub transform: Transform, + pub translation: Translation, + pub rotation: Rotation, + pub scale: Scale, +} + +impl Default for ButtonComponents { + fn default() -> Self { + ButtonComponents { + button: Button, + click: Click::default(), + hover: Hover::default(), + focus_policy: FocusPolicy::default(), + mesh: QUAD_HANDLE, + render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized( + UI_PIPELINE_HANDLE, + PipelineSpecialization { + dynamic_bindings: vec![ + // Transform + DynamicBinding { + bind_group: 1, + binding: 0, + }, + // Node_size + DynamicBinding { + bind_group: 1, + binding: 1, + }, + ], + ..Default::default() + }, + )]), + node: Default::default(), + material: Default::default(), + draw: Default::default(), + transform: Default::default(), + translation: Default::default(), + rotation: Default::default(), + scale: Default::default(), + } + } +} diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs new file mode 100644 index 0000000000..dedc9badba --- /dev/null +++ b/crates/bevy_ui/src/focus.rs @@ -0,0 +1,133 @@ +use crate::Node; +use bevy_app::{EventReader, Events}; +use bevy_core::FloatOrd; +use bevy_ecs::prelude::*; +use bevy_input::{mouse::MouseButton, Input}; +use bevy_math::Vec2; +use bevy_transform::components::Transform; +use bevy_window::{CursorMoved, Windows}; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Click { + Released, + Pressed, +} + +impl Default for Click { + fn default() -> Self { + Click::Released + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Hover { + Hovered, + NotHovered, +} + +impl Default for Hover { + fn default() -> Self { + Hover::NotHovered + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum FocusPolicy { + Block, + Pass, +} + +impl Default for FocusPolicy { + fn default() -> Self { + FocusPolicy::Block + } +} + +#[derive(Default)] +pub struct State { + cursor_moved_event_reader: EventReader, + cursor_position: Vec2, +} + +pub fn ui_focus_system( + mut state: Local, + windows: Res, + mouse_button_input: Res>, + cursor_moved_events: Res>, + mut node_query: Query<( + &Node, + &Transform, + Option<&mut Click>, + Option<&mut Hover>, + Option<&FocusPolicy>, + )>, +) { + if let Some(cursor_moved) = state.cursor_moved_event_reader.latest(&cursor_moved_events) { + state.cursor_position = cursor_moved.position; + } + + if mouse_button_input.just_released(MouseButton::Left) { + for (_node, _transform, click, _hover, _focus_policy) in &mut node_query.iter() { + if let Some(mut click) = click { + if *click == Click::Pressed { + *click = Click::Released; + } + } + } + } + + let mouse_clicked = mouse_button_input.just_pressed(MouseButton::Left); + let window = windows.get_primary().unwrap(); + + let mut query_iter = node_query.iter(); + let mut moused_over_z_sorted_nodes = query_iter + .iter() + .filter_map(|(node, transform, click, hover, focus_policy)| { + let position = transform.value.w_axis(); + // TODO: ui transform is currently in world space, so we need to move it to ui space. we should make these transforms ui space + let ui_position = position.truncate().truncate() + + Vec2::new(window.width as f32 / 2.0, window.height as f32 / 2.0); + let extents = node.size / 2.0; + let min = ui_position - extents; + let max = ui_position + extents; + // if the current cursor position is within the bounds of the node, consider it for clicking + if (min.x()..max.x()).contains(&state.cursor_position.x()) + && (min.y()..max.y()).contains(&state.cursor_position.y()) + { + Some((focus_policy, click, hover, FloatOrd(position.z()))) + } else { + if let Some(mut hover) = hover { + if *hover == Hover::Hovered { + *hover = Hover::NotHovered; + } + } + None + } + }) + .collect::>(); + + // TODO: sort by negative when we move back to a right handed coordinate system + moused_over_z_sorted_nodes.sort_by_key(|(_, _, _, z)| *z); + for (focus_policy, click, hover, _) in moused_over_z_sorted_nodes { + if mouse_clicked { + // only consider nodes with ClickState "clickable" + if let Some(mut click) = click { + if *click == Click::Released { + *click = Click::Pressed; + } + } + } + // only consider nodes with Hover "hoverable" + if let Some(mut hover) = hover { + if *hover == Hover::NotHovered { + *hover = Hover::Hovered; + } + } + match focus_policy.cloned().unwrap_or(FocusPolicy::Block) { + FocusPolicy::Block => { + break; + } + FocusPolicy::Pass => { /* allow the next node to be hovered/clicked */ } + } + } +} diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 3ef1b0649a..7587012b42 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -1,24 +1,30 @@ mod anchors; +mod focus; pub mod entity; mod margins; mod node; mod render; -mod ui_update_system; +pub mod update; pub mod widget; pub use anchors::*; +pub use focus::*; pub use margins::*; pub use node::*; pub use render::*; -pub use ui_update_system::*; pub mod prelude { - pub use crate::{entity::*, widget::Label, Anchors, Margins, Node}; + pub use crate::{ + entity::*, + widget::{Button, Label}, + Anchors, Click, Hover, Margins, Node, + }; } use bevy_app::prelude::*; use bevy_ecs::IntoQuerySystem; use bevy_render::render_graph::RenderGraph; +use update::ui_update_system; use widget::Label; #[derive(Default)] @@ -26,7 +32,8 @@ pub struct UiPlugin; impl AppPlugin for UiPlugin { fn build(&self, app: &mut AppBuilder) { - app.add_system_to_stage(stage::POST_UPDATE, ui_update_system.system()) + app.add_system_to_stage(stage::PRE_UPDATE, ui_focus_system.system()) + .add_system_to_stage(stage::POST_UPDATE, ui_update_system.system()) .add_system_to_stage(stage::POST_UPDATE, Label::label_system.system()) .add_system_to_stage(bevy_render::stage::DRAW, Label::draw_label_system.system()); diff --git a/crates/bevy_ui/src/ui_update_system.rs b/crates/bevy_ui/src/update.rs similarity index 100% rename from crates/bevy_ui/src/ui_update_system.rs rename to crates/bevy_ui/src/update.rs diff --git a/crates/bevy_ui/src/widget/button.rs b/crates/bevy_ui/src/widget/button.rs new file mode 100644 index 0000000000..04b8456545 --- /dev/null +++ b/crates/bevy_ui/src/widget/button.rs @@ -0,0 +1 @@ +pub struct Button; \ No newline at end of file diff --git a/crates/bevy_ui/src/widget/mod.rs b/crates/bevy_ui/src/widget/mod.rs index ea3183f508..48f9c41028 100644 --- a/crates/bevy_ui/src/widget/mod.rs +++ b/crates/bevy_ui/src/widget/mod.rs @@ -1,3 +1,5 @@ +mod button; mod label; +pub use button::*; pub use label::*; diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 18042efdb5..0ecfb8a2bb 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -107,9 +107,13 @@ pub fn winit_runner(mut app: App) { app.resources.get_mut::>().unwrap(); let winit_windows = app.resources.get_mut::().unwrap(); let window_id = winit_windows.get_window_id(winit_window_id).unwrap(); + let window = winit_windows.get_window(window_id).unwrap(); + let inner_size = window.inner_size(); + // move origin to bottom left + let y_position = inner_size.height as f32 - position.y as f32; cursor_moved_events.send(CursorMoved { id: window_id, - position: Vec2::new(position.x as f32, position.y as f32), + position: Vec2::new(position.x as f32, y_position as f32), }); } WindowEvent::MouseInput { state, button, .. } => { diff --git a/examples/ui/button.rs b/examples/ui/button.rs new file mode 100644 index 0000000000..b165159522 --- /dev/null +++ b/examples/ui/button.rs @@ -0,0 +1,65 @@ +use bevy::prelude::*; + +fn main() { + App::build() + .add_default_plugins() + .add_startup_system(setup.system()) + .add_system(button_system.system()) + .run(); +} + +fn button_system( + mut click_query: Query<(&Button, Changed)>, + mut hover_query: Query<(&Button, Changed)>, +) { + for (_button, click) in &mut click_query.iter() { + match *click { + Click::Pressed => { + println!("pressed"); + } + Click::Released => { + println!("released"); + } + } + } + + for (_button, hover) in &mut hover_query.iter() { + match *hover { + Hover::Hovered => { + println!("hovered"); + } + Hover::NotHovered => { + println!("unhovered"); + } + } + } +} + +fn setup( + mut commands: Commands, + asset_server: Res, + mut materials: ResMut>, +) { + commands + // ui camera + .spawn(OrthographicCameraComponents::default()) + .spawn(ButtonComponents { + node: Node::new(Anchors::BOTTOM_LEFT, Margins::new(10.0, 160.0, 10.0, 80.0)), + material: materials.add(Color::rgb(0.2, 0.8, 0.2).into()), + ..Default::default() + }) + .with_children(|parent| { + parent.spawn(LabelComponents { + node: Node::new(Anchors::CENTER, Margins::new(52.0, 10.0, 20.0, 20.0)), + label: Label { + text: "Button".to_string(), + font: asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap(), + style: TextStyle { + font_size: 40.0, + color: Color::rgb(0.1, 0.1, 0.1), + }, + }, + ..Default::default() + }); + }); +} diff --git a/examples/ui/ui.rs b/examples/ui/ui.rs index d3cf648a6d..151491b748 100644 --- a/examples/ui/ui.rs +++ b/examples/ui/ui.rs @@ -25,7 +25,7 @@ fn setup( // ui camera .spawn(OrthographicCameraComponents::default()) // root node - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::new(Anchors::FULL, Margins::default()), material: materials.add(Color::NONE.into()), ..Default::default() @@ -33,7 +33,7 @@ fn setup( .with_children(|parent| { parent // left vertical fill - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::new(Anchors::LEFT_FULL, Margins::new(10.0, 200.0, 10.0, 10.0)), material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()), ..Default::default() @@ -53,13 +53,13 @@ fn setup( }); }) // right vertical fill - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::new(Anchors::RIGHT_FULL, Margins::new(10.0, 100.0, 100.0, 100.0)), material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()), ..Default::default() }) // render order test: reddest in the back, whitest in the front - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::positioned( Vec2::new(75.0, 60.0), Anchors::CENTER, @@ -68,7 +68,7 @@ fn setup( material: materials.add(Color::rgb(1.0, 0.0, 0.0).into()), ..Default::default() }) - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::positioned( Vec2::new(50.0, 35.0), Anchors::CENTER, @@ -77,7 +77,7 @@ fn setup( material: materials.add(Color::rgb(1.0, 0.3, 0.3).into()), ..Default::default() }) - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::positioned( Vec2::new(100.0, 85.0), Anchors::CENTER, @@ -86,7 +86,7 @@ fn setup( material: materials.add(Color::rgb(1.0, 0.5, 0.5).into()), ..Default::default() }) - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::positioned( Vec2::new(150.0, 135.0), Anchors::CENTER, @@ -96,7 +96,7 @@ fn setup( ..Default::default() }) // parenting - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::positioned( Vec2::new(210.0, 0.0), Anchors::BOTTOM_LEFT, @@ -106,14 +106,14 @@ fn setup( ..Default::default() }) .with_children(|parent| { - parent.spawn(UiComponents { + parent.spawn(NodeComponents { node: Node::new(Anchors::FULL, Margins::new(20.0, 20.0, 20.0, 20.0)), material: materials.add(Color::rgb(0.6, 0.6, 1.0).into()), ..Default::default() }); }) // alpha test - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::positioned( Vec2::new(200.0, 185.0), Anchors::CENTER, @@ -123,7 +123,7 @@ fn setup( ..Default::default() }) // texture - .spawn(UiComponents { + .spawn(NodeComponents { node: Node::new( Anchors::CENTER_TOP, Margins::new(-250.0, 250.0, 510.0 * aspect, 10.0), diff --git a/examples/ui/ui_bench.rs b/examples/ui/ui_bench.rs index ba41597a10..560c94dde1 100644 --- a/examples/ui/ui_bench.rs +++ b/examples/ui/ui_bench.rs @@ -29,7 +29,7 @@ fn setup(mut commands: Commands, mut materials: ResMut>) { for i in 0..count { // 2d camera let cur = Vec2::new(1.0, 1.0) + prev; - commands.spawn(UiComponents { + commands.spawn(NodeComponents { node: Node { position: Vec2::new(75.0, 75.0) + cur, anchors: Anchors::new(0.5, 0.5, 0.5, 0.5),