From 71056a3552778cc6fbe87da9579f1959988ccaec Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Tue, 27 Sep 2022 19:12:57 +0200 Subject: [PATCH 01/16] made resource_scope work for non-send resources --- crates/bevy_ecs/src/lib.rs | 31 +++++++++++++++++++++++++++++++ crates/bevy_ecs/src/world/mod.rs | 15 ++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 102df951f89a0..9374cdd935d06 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1263,6 +1263,37 @@ mod tests { assert_eq!(world.resource::().0, 1); } + #[test] + fn non_send_resource_scope() { + let mut world = World::default(); + world.insert_non_send_resource(A(0)); + world.resource_scope(|world: &mut World, mut value: Mut| { + value.0 += 1; + assert!(!world.contains_resource::()); + }); + assert_eq!(world.non_send_resource::().0, 1); + } + + #[test] + #[should_panic] + fn non_send_resource_scope_from_different_thread() { + let mut world = World::default(); + world.insert_non_send_resource(A(0)); + + std::thread::scope(|s| { + s.spawn(|| { + // Accessing the non-send resource on a different thread + // Should result in a panic + world.resource_scope(|world: &mut World, mut value: Mut| { + value.0 += 1; + assert!(!world.contains_resource::()); + }); + }); + }); + + assert_eq!(world.get_non_send_resource::().unwrap().0, 1); + } + #[test] fn insert_overwrite_drop() { let (dropck1, dropped1) = DropCk::new_pair(); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 914046526af12..128c5e8dcf094 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1149,7 +1149,13 @@ impl World { /// }); /// assert_eq!(world.get_resource::().unwrap().0, 2); /// ``` - pub fn resource_scope(&mut self, f: impl FnOnce(&mut World, Mut) -> U) -> U { + pub fn resource_scope< + R: 'static, /* The resource doesn't need to be Send nor Sync. */ + U, + >( + &mut self, + f: impl FnOnce(&mut World, Mut) -> U, + ) -> U { let last_change_tick = self.last_change_tick(); let change_tick = self.change_tick(); @@ -1157,6 +1163,13 @@ impl World { .components .get_resource_id(TypeId::of::()) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); + + // If the resource isn't send and sync, validate that we are on the main thread, so that we can access it. + let component_info = self.components().get_info(component_id).unwrap(); + if !component_info.is_send_and_sync() { + self.validate_non_send_access::(); + } + let (ptr, mut ticks) = { let resource_archetype = self.archetypes.resource_mut(); let unique_components = resource_archetype.unique_components_mut(); From 83f241771cdd671ae6e13fe1f7879b62886918b3 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Tue, 27 Sep 2022 21:31:37 +0200 Subject: [PATCH 02/16] fixed the non-send resource scope tests --- crates/bevy_ecs/src/lib.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index b695ea91049e1..f04b4a5da8aa3 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -69,6 +69,7 @@ mod tests { atomic::{AtomicUsize, Ordering}, Arc, Mutex, }, + marker::PhantomData, }; #[derive(Component, Resource, Debug, PartialEq, Eq, Clone, Copy)] @@ -78,6 +79,9 @@ mod tests { #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct C; + #[derive(Default)] + struct NonSend(usize, PhantomData<*mut ()>); + #[derive(Component, Clone, Debug)] struct DropCk(Arc); impl DropCk { @@ -1265,32 +1269,31 @@ mod tests { #[test] fn non_send_resource_scope() { let mut world = World::default(); - world.insert_non_send_resource(A(0)); - world.resource_scope(|world: &mut World, mut value: Mut| { + world.insert_non_send_resource(NonSend::default()); + world.resource_scope(|world: &mut World, mut value: Mut| { value.0 += 1; - assert!(!world.contains_resource::()); + assert!(!world.contains_resource::()); }); - assert_eq!(world.non_send_resource::().0, 1); + assert_eq!(world.non_send_resource::().0, 1); } #[test] - #[should_panic] + #[should_panic(expected = "attempted to access NonSend resource bevy_ecs::tests::NonSend off of the main thread")] fn non_send_resource_scope_from_different_thread() { let mut world = World::default(); - world.insert_non_send_resource(A(0)); - - std::thread::scope(|s| { - s.spawn(|| { - // Accessing the non-send resource on a different thread - // Should result in a panic - world.resource_scope(|world: &mut World, mut value: Mut| { - value.0 += 1; - assert!(!world.contains_resource::()); - }); + world.insert_non_send_resource(NonSend::default()); + + let thread = std::thread::spawn(move || { + // Accessing the non-send resource on a different thread + // Should result in a panic + world.resource_scope(|_: &mut World, mut value: Mut| { + value.0 += 1; }); }); - assert_eq!(world.get_non_send_resource::().unwrap().0, 1); + if let Err(err) = thread.join() { + panic!("{}", err.downcast::().unwrap()); + } } #[test] From 6da0e41d5eb02e78175f74dcf2287ab355889d2f Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Tue, 27 Sep 2022 22:56:06 +0200 Subject: [PATCH 03/16] formatting --- crates/bevy_ecs/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index f04b4a5da8aa3..5fae64cfcad60 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -65,11 +65,11 @@ mod tests { use bevy_tasks::{ComputeTaskPool, TaskPool}; use std::{ any::TypeId, + marker::PhantomData, sync::{ atomic::{AtomicUsize, Ordering}, Arc, Mutex, }, - marker::PhantomData, }; #[derive(Component, Resource, Debug, PartialEq, Eq, Clone, Copy)] @@ -1278,7 +1278,9 @@ mod tests { } #[test] - #[should_panic(expected = "attempted to access NonSend resource bevy_ecs::tests::NonSend off of the main thread")] + #[should_panic( + expected = "attempted to access NonSend resource bevy_ecs::tests::NonSend off of the main thread" + )] fn non_send_resource_scope_from_different_thread() { let mut world = World::default(); world.insert_non_send_resource(NonSend::default()); From 3500039d02175084bbbb66e597e6d758d80013d6 Mon Sep 17 00:00:00 2001 From: Dawid Piotrowski <41804418+Pietrek14@users.noreply.github.com> Date: Wed, 28 Sep 2022 11:10:24 +0200 Subject: [PATCH 04/16] simplified panic in a non-send resource scope test Co-authored-by: Mike --- crates/bevy_ecs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 5fae64cfcad60..3e9ebde63a961 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1294,7 +1294,7 @@ mod tests { }); if let Err(err) = thread.join() { - panic!("{}", err.downcast::().unwrap()); + std::panic::resume_unwind(err); } } From f68eb33f42c0124010cd49a09986a553b0451b4d Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Wed, 28 Sep 2022 12:16:53 +0200 Subject: [PATCH 05/16] changed the name of non-send struct used for testing --- crates/bevy_ecs/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 3e9ebde63a961..6143036737dbb 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -80,7 +80,7 @@ mod tests { struct C; #[derive(Default)] - struct NonSend(usize, PhantomData<*mut ()>); + struct NonSendA(usize, PhantomData<*mut ()>); #[derive(Component, Clone, Debug)] struct DropCk(Arc); @@ -1269,26 +1269,26 @@ mod tests { #[test] fn non_send_resource_scope() { let mut world = World::default(); - world.insert_non_send_resource(NonSend::default()); - world.resource_scope(|world: &mut World, mut value: Mut| { + world.insert_non_send_resource(NonSendA::default()); + world.resource_scope(|world: &mut World, mut value: Mut| { value.0 += 1; - assert!(!world.contains_resource::()); + assert!(!world.contains_resource::()); }); - assert_eq!(world.non_send_resource::().0, 1); + assert_eq!(world.non_send_resource::().0, 1); } #[test] #[should_panic( - expected = "attempted to access NonSend resource bevy_ecs::tests::NonSend off of the main thread" + expected = "attempted to access NonSend resource bevy_ecs::tests::NonSendA off of the main thread" )] fn non_send_resource_scope_from_different_thread() { let mut world = World::default(); - world.insert_non_send_resource(NonSend::default()); + world.insert_non_send_resource(NonSendA::default()); let thread = std::thread::spawn(move || { // Accessing the non-send resource on a different thread // Should result in a panic - world.resource_scope(|_: &mut World, mut value: Mut| { + world.resource_scope(|_: &mut World, mut value: Mut| { value.0 += 1; }); }); From 8513317753ca3f2efb6426d2ba73fab378d4e983 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Sat, 14 Jan 2023 21:26:21 +0100 Subject: [PATCH 06/16] added relative cursor position --- crates/bevy_ui/src/focus.rs | 48 ++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index fab9a0121c1a7..23725144a60e9 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -1,4 +1,5 @@ use crate::{camera_config::UiCameraConfig, CalculatedClip, Node, UiStack}; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ change_detection::DetectChangesMut, entity::Entity, @@ -52,6 +53,25 @@ impl Default for Interaction { } } +/// A component storing the position of the mouse relative to the node, (0., 0.) being the upper-left corner and (1., 1.) being the bottom-right +/// I can be used alongside interaction to get the position of the press. +/// If the mouse is not over the node, the value will go beyond the range of (0., 0.) to (1., 1.) +#[derive( + Component, + Deref, + DerefMut, + Copy, + Clone, + Default, + PartialEq, + Debug, + Reflect, + Serialize, + Deserialize, +)] +#[reflect(Component, Serialize, Deserialize, PartialEq)] +pub struct RelativeCursorPosition(pub Vec2); + /// Describes whether the node should block interactions with lower nodes #[derive(Component, Copy, Clone, Eq, PartialEq, Debug, Reflect, Serialize, Deserialize)] #[reflect(Component, Serialize, Deserialize, PartialEq)] @@ -86,6 +106,7 @@ pub struct NodeQuery { node: &'static Node, global_transform: &'static GlobalTransform, interaction: Option<&'static mut Interaction>, + relative_cursor_position: Option<&'static mut RelativeCursorPosition>, focus_policy: Option<&'static FocusPolicy>, calculated_clip: Option<&'static CalculatedClip>, computed_visibility: Option<&'static ComputedVisibility>, @@ -175,16 +196,31 @@ pub fn ui_focus_system( let ui_position = position.truncate(); let extents = node.node.size() / 2.0; let mut min = ui_position - extents; - let mut max = ui_position + extents; if let Some(clip) = node.calculated_clip { min = Vec2::max(min, clip.clip.min); - max = Vec2::min(max, clip.clip.max); } - // if the current cursor position is within the bounds of the node, consider it for + + // The mouse position relative to the node + // (0., 0.) is the upper-left corner, (1., 1.) is the bottom-right corner + let relative_cursor_postition = cursor_position.map(|cursor_position| { + Vec2::new( + (cursor_position.x - min.x) / node.node.size().x, + (cursor_position.y - min.y) / node.node.size().y, + ) + }); + + // If the current cursor position is within the bounds of the node, consider it for // clicking - let contains_cursor = if let Some(cursor_position) = cursor_position { - (min.x..max.x).contains(&cursor_position.x) - && (min.y..max.y).contains(&cursor_position.y) + let contains_cursor = if let Some(cursor_position) = relative_cursor_postition { + // Save the relative cursor position to the correct component + if let Some(mut relative_cursor_position_component) = + node.relative_cursor_position + { + relative_cursor_position_component.0 = cursor_position; + } + + (0.0..1.0).contains(&cursor_position.x) + && (0.0..1.0).contains(&cursor_position.y) } else { false }; From a81cb5ac6e1df0c4f742574e44116f37d6c29b07 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Sat, 14 Jan 2023 23:09:39 +0100 Subject: [PATCH 07/16] added a relative cursor position example --- Cargo.toml | 10 ++++ examples/ui/relative_cursor_position.rs | 76 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 examples/ui/relative_cursor_position.rs diff --git a/Cargo.toml b/Cargo.toml index 48b7e55bb5d88..898cd1f2e33ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1455,6 +1455,16 @@ description = "Illustrates how FontAtlases are populated (used to optimize text category = "UI (User Interface)" wasm = true +[[example]] +name = "relative_cursor_position" +path = "examples/ui/relative_cursor_position.rs" + +[package.metadata.example.relative_cursor_position] +name = "Relative Cursor Position" +description = "Showcases the RelativeCursorPosition component" +category = "UI (User Interface)" +wasm = true + [[example]] name = "text" path = "examples/ui/text.rs" diff --git a/examples/ui/relative_cursor_position.rs b/examples/ui/relative_cursor_position.rs new file mode 100644 index 0000000000000..60e0744787adf --- /dev/null +++ b/examples/ui/relative_cursor_position.rs @@ -0,0 +1,76 @@ +//! Showcases the `RelativeCursorPosition` component. + +use bevy::{prelude::*, ui::RelativeCursorPosition, winit::WinitSettings}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + // Only run the app when there is user input. This will significantly reduce CPU/GPU use. + .insert_resource(WinitSettings::desktop_app()) + .add_startup_system(setup) + .add_system(relative_cursor_position_system) + .run(); +} + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn(Camera2dBundle::default()); + + commands + .spawn(NodeBundle { + style: Style { + size: Size::new(Val::Percent(100.0), Val::Percent(100.0)), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + flex_direction: FlexDirection::Column, + ..default() + }, + ..default() + }) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + size: Size::new(Val::Px(250.0), Val::Px(250.0)), + margin: UiRect::new(Val::Px(0.), Val::Px(0.), Val::Px(0.), Val::Px(15.)), + ..default() + }, + background_color: Color::rgb(235., 35., 12.).into(), + ..default() + }) + .insert(RelativeCursorPosition::default()); + + parent.spawn(TextBundle { + text: Text::from_section( + "(0.0, 0.0)", + TextStyle { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: 40.0, + color: Color::rgb(0.9, 0.9, 0.9), + }, + ), + ..default() + }); + }); +} + +fn relative_cursor_position_system( + relative_cursor_position_query: Query<&RelativeCursorPosition>, + mut output_query: Query<&mut Text>, +) { + let relative_cursor_position = relative_cursor_position_query.single(); + + let mut output = output_query.single_mut(); + + output.sections[0].value = format!( + "({:.1}, {:.1})", + relative_cursor_position.x, relative_cursor_position.y + ); + + output.sections[0].style.color = if (0.0..1.).contains(&relative_cursor_position.x) + && (0.0..1.).contains(&relative_cursor_position.y) + { + Color::rgb(0.1, 0.9, 0.1) + } else { + Color::rgb(0.9, 0.1, 0.1) + }; +} From 0921e75804aab856610339eda24381afd4b3ae79 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Sat, 14 Jan 2023 23:18:48 +0100 Subject: [PATCH 08/16] updated example pages --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 7817465d9e8b8..83f6c129044f1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -312,6 +312,7 @@ Example | Description --- | --- [Button](../examples/ui/button.rs) | Illustrates creating and updating a button [Font Atlas Debug](../examples/ui/font_atlas_debug.rs) | Illustrates how FontAtlases are populated (used to optimize text rendering internally) +[Relative Cursor Position](../examples/ui/relative_cursor_position.rs) | Showcases the RelativeCursorPosition component [Text](../examples/ui/text.rs) | Illustrates creating and updating text [Text Debug](../examples/ui/text_debug.rs) | An example for debugging text layout [Transparency UI](../examples/ui/transparency_ui.rs) | Demonstrates transparency for UI From 42699d1d930c3c649816c138b8317e3452e27293 Mon Sep 17 00:00:00 2001 From: Dawid Piotrowski <41804418+Pietrek14@users.noreply.github.com> Date: Sun, 15 Jan 2023 01:27:37 +0100 Subject: [PATCH 09/16] comment clarification Co-authored-by: Alice Cecile --- examples/ui/relative_cursor_position.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ui/relative_cursor_position.rs b/examples/ui/relative_cursor_position.rs index 60e0744787adf..3d9050617a7d8 100644 --- a/examples/ui/relative_cursor_position.rs +++ b/examples/ui/relative_cursor_position.rs @@ -1,4 +1,4 @@ -//! Showcases the `RelativeCursorPosition` component. +//! Showcases the `RelativeCursorPosition` component, used to check the position of the cursor relative to a UI node. use bevy::{prelude::*, ui::RelativeCursorPosition, winit::WinitSettings}; From ae57369c2c4ba4f7d9bdd3684a31479143ca82a2 Mon Sep 17 00:00:00 2001 From: Dawid Piotrowski <41804418+Pietrek14@users.noreply.github.com> Date: Sun, 15 Jan 2023 02:02:19 +0100 Subject: [PATCH 10/16] line break after doc comment Co-authored-by: Alice Cecile --- crates/bevy_ui/src/focus.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 23725144a60e9..0beb747c3f712 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -54,6 +54,7 @@ impl Default for Interaction { } /// A component storing the position of the mouse relative to the node, (0., 0.) being the upper-left corner and (1., 1.) being the bottom-right +/// /// I can be used alongside interaction to get the position of the press. /// If the mouse is not over the node, the value will go beyond the range of (0., 0.) to (1., 1.) #[derive( From 3ddd0c00bc038155daed0a0114afb092dc52fad1 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Sun, 15 Jan 2023 02:03:03 +0100 Subject: [PATCH 11/16] made relative cursor position an option --- crates/bevy_ui/src/focus.rs | 18 +++++++++-------- examples/ui/relative_cursor_position.rs | 26 ++++++++++++++----------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 23725144a60e9..09c764d490536 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -70,7 +70,9 @@ impl Default for Interaction { Deserialize, )] #[reflect(Component, Serialize, Deserialize, PartialEq)] -pub struct RelativeCursorPosition(pub Vec2); +pub struct RelativeCursorPosition { + pub normalized: Option, +} /// Describes whether the node should block interactions with lower nodes #[derive(Component, Copy, Clone, Eq, PartialEq, Debug, Reflect, Serialize, Deserialize)] @@ -209,16 +211,16 @@ pub fn ui_focus_system( ) }); + // Save the relative cursor position to the correct component + if let Some(mut relative_cursor_position_component) = + node.relative_cursor_position + { + relative_cursor_position_component.normalized = relative_cursor_postition; + } + // If the current cursor position is within the bounds of the node, consider it for // clicking let contains_cursor = if let Some(cursor_position) = relative_cursor_postition { - // Save the relative cursor position to the correct component - if let Some(mut relative_cursor_position_component) = - node.relative_cursor_position - { - relative_cursor_position_component.0 = cursor_position; - } - (0.0..1.0).contains(&cursor_position.x) && (0.0..1.0).contains(&cursor_position.y) } else { diff --git a/examples/ui/relative_cursor_position.rs b/examples/ui/relative_cursor_position.rs index 60e0744787adf..cb8475fcc0028 100644 --- a/examples/ui/relative_cursor_position.rs +++ b/examples/ui/relative_cursor_position.rs @@ -61,16 +61,20 @@ fn relative_cursor_position_system( let mut output = output_query.single_mut(); - output.sections[0].value = format!( - "({:.1}, {:.1})", - relative_cursor_position.x, relative_cursor_position.y - ); + output.sections[0].value = if let Some(relative_cursor_position) = relative_cursor_position.normalized { + output.sections[0].style.color = if (0.0..1.).contains(&relative_cursor_position.x) + && (0.0..1.).contains(&relative_cursor_position.y) + { + Color::rgb(0.1, 0.9, 0.1) + } else { + Color::rgb(0.9, 0.1, 0.1) + }; - output.sections[0].style.color = if (0.0..1.).contains(&relative_cursor_position.x) - && (0.0..1.).contains(&relative_cursor_position.y) - { - Color::rgb(0.1, 0.9, 0.1) - } else { - Color::rgb(0.9, 0.1, 0.1) - }; + format!( + "({:.1}, {:.1})", + relative_cursor_position.x, relative_cursor_position.y + ) + } else { + "unknown".to_string() + } } From 9713cc885ce308a0eaa08924b4112e93f5b690bd Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Sun, 15 Jan 2023 02:05:58 +0100 Subject: [PATCH 12/16] clarification --- crates/bevy_ui/src/focus.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index c8b9512ad8173..f39aef7d9e7f6 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -54,9 +54,10 @@ impl Default for Interaction { } /// A component storing the position of the mouse relative to the node, (0., 0.) being the upper-left corner and (1., 1.) being the bottom-right -/// -/// I can be used alongside interaction to get the position of the press. /// If the mouse is not over the node, the value will go beyond the range of (0., 0.) to (1., 1.) +/// A None value means that the cursor position is unknown. +/// +/// It can be used alongside interaction to get the position of the press. #[derive( Component, Deref, From 32cde9a7be24299837b468db6b8c6056d859f7b2 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Sun, 15 Jan 2023 02:15:37 +0100 Subject: [PATCH 13/16] added a helper method to check if mouse is over a node --- crates/bevy_ui/src/focus.rs | 32 +++++++++++++++---------- examples/ui/relative_cursor_position.rs | 29 +++++++++++----------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index f39aef7d9e7f6..7c17e24aaa535 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -76,6 +76,15 @@ pub struct RelativeCursorPosition { pub normalized: Option, } +impl RelativeCursorPosition { + /// A helper function to check if the mouse is over the node + pub fn mouse_over(&self) -> bool { + self.normalized + .map(|position| (0.0..1.).contains(&position.x) && (0.0..1.).contains(&position.y)) + .unwrap_or(false) + } +} + /// Describes whether the node should block interactions with lower nodes #[derive(Component, Copy, Clone, Eq, PartialEq, Debug, Reflect, Serialize, Deserialize)] #[reflect(Component, Serialize, Deserialize, PartialEq)] @@ -206,29 +215,28 @@ pub fn ui_focus_system( // The mouse position relative to the node // (0., 0.) is the upper-left corner, (1., 1.) is the bottom-right corner - let relative_cursor_postition = cursor_position.map(|cursor_position| { + let relative_cursor_position = cursor_position.map(|cursor_position| { Vec2::new( (cursor_position.x - min.x) / node.node.size().x, (cursor_position.y - min.y) / node.node.size().y, ) }); + // If the current cursor position is within the bounds of the node, consider it for + // clicking + let relative_cursor_position_component = RelativeCursorPosition { + normalized: relative_cursor_position, + }; + + let contains_cursor = relative_cursor_position_component.mouse_over(); + // Save the relative cursor position to the correct component - if let Some(mut relative_cursor_position_component) = + if let Some(mut node_relative_cursor_position_component) = node.relative_cursor_position { - relative_cursor_position_component.normalized = relative_cursor_postition; + *node_relative_cursor_position_component = relative_cursor_position_component; } - // If the current cursor position is within the bounds of the node, consider it for - // clicking - let contains_cursor = if let Some(cursor_position) = relative_cursor_postition { - (0.0..1.0).contains(&cursor_position.x) - && (0.0..1.0).contains(&cursor_position.y) - } else { - false - }; - if contains_cursor { Some(*entity) } else { diff --git a/examples/ui/relative_cursor_position.rs b/examples/ui/relative_cursor_position.rs index ec48ca20a8133..82c9a2597bf54 100644 --- a/examples/ui/relative_cursor_position.rs +++ b/examples/ui/relative_cursor_position.rs @@ -61,20 +61,19 @@ fn relative_cursor_position_system( let mut output = output_query.single_mut(); - output.sections[0].value = if let Some(relative_cursor_position) = relative_cursor_position.normalized { - output.sections[0].style.color = if (0.0..1.).contains(&relative_cursor_position.x) - && (0.0..1.).contains(&relative_cursor_position.y) - { - Color::rgb(0.1, 0.9, 0.1) - } else { - Color::rgb(0.9, 0.1, 0.1) - }; + output.sections[0].value = + if let Some(relative_cursor_position) = relative_cursor_position.normalized { + format!( + "({:.1}, {:.1})", + relative_cursor_position.x, relative_cursor_position.y + ) + } else { + "unknown".to_string() + }; - format!( - "({:.1}, {:.1})", - relative_cursor_position.x, relative_cursor_position.y - ) - } else { - "unknown".to_string() - } + output.sections[0].style.color = if relative_cursor_position.mouse_over() { + Color::rgb(0.1, 0.9, 0.1) + } else { + Color::rgb(0.9, 0.1, 0.1) + }; } From b52f95029a05b13e911f83a3b085aa255ec141d8 Mon Sep 17 00:00:00 2001 From: Dawid Piotrowski <41804418+Pietrek14@users.noreply.github.com> Date: Sun, 15 Jan 2023 12:52:30 +0100 Subject: [PATCH 14/16] doc comment for RelativeCursorPosition::normalized Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com> --- crates/bevy_ui/src/focus.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 7c17e24aaa535..25e5140f2a940 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -73,6 +73,7 @@ impl Default for Interaction { )] #[reflect(Component, Serialize, Deserialize, PartialEq)] pub struct RelativeCursorPosition { + /// Cursor position relative to size and position of the Node. pub normalized: Option, } From 6a0061151dcc96127eb096e2e43b17d551f06320 Mon Sep 17 00:00:00 2001 From: Dawid Piotrowski <41804418+Pietrek14@users.noreply.github.com> Date: Sun, 15 Jan 2023 12:53:12 +0100 Subject: [PATCH 15/16] comment for example system Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com> --- examples/ui/relative_cursor_position.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/ui/relative_cursor_position.rs b/examples/ui/relative_cursor_position.rs index 82c9a2597bf54..a02a5b3a2d7d5 100644 --- a/examples/ui/relative_cursor_position.rs +++ b/examples/ui/relative_cursor_position.rs @@ -53,6 +53,7 @@ fn setup(mut commands: Commands, asset_server: Res) { }); } +/// This systems polls the relative cursor position and displays its value in a text component. fn relative_cursor_position_system( relative_cursor_position_query: Query<&RelativeCursorPosition>, mut output_query: Query<&mut Text>, From 246287485308c86accfc6cdab24a1b1f677c66f7 Mon Sep 17 00:00:00 2001 From: Pietrek14 Date: Mon, 16 Jan 2023 13:14:09 +0100 Subject: [PATCH 16/16] changed upper-left to top-left --- crates/bevy_ui/src/focus.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 25e5140f2a940..0c71fe790e770 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -53,7 +53,7 @@ impl Default for Interaction { } } -/// A component storing the position of the mouse relative to the node, (0., 0.) being the upper-left corner and (1., 1.) being the bottom-right +/// A component storing the position of the mouse relative to the node, (0., 0.) being the top-left corner and (1., 1.) being the bottom-right /// If the mouse is not over the node, the value will go beyond the range of (0., 0.) to (1., 1.) /// A None value means that the cursor position is unknown. /// @@ -215,7 +215,7 @@ pub fn ui_focus_system( } // The mouse position relative to the node - // (0., 0.) is the upper-left corner, (1., 1.) is the bottom-right corner + // (0., 0.) is the top-left corner, (1., 1.) is the bottom-right corner let relative_cursor_position = cursor_position.map(|cursor_position| { Vec2::new( (cursor_position.x - min.x) / node.node.size().x,