|
1 | 1 | use crate::{camera_config::UiCameraConfig, CalculatedClip, Node, UiStack};
|
| 2 | +use bevy_derive::{Deref, DerefMut}; |
2 | 3 | use bevy_ecs::{
|
3 | 4 | change_detection::DetectChangesMut,
|
4 | 5 | entity::Entity,
|
@@ -52,6 +53,39 @@ impl Default for Interaction {
|
52 | 53 | }
|
53 | 54 | }
|
54 | 55 |
|
| 56 | +/// 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 |
| 57 | +/// If the mouse is not over the node, the value will go beyond the range of (0., 0.) to (1., 1.) |
| 58 | +/// A None value means that the cursor position is unknown. |
| 59 | +/// |
| 60 | +/// It can be used alongside interaction to get the position of the press. |
| 61 | +#[derive( |
| 62 | + Component, |
| 63 | + Deref, |
| 64 | + DerefMut, |
| 65 | + Copy, |
| 66 | + Clone, |
| 67 | + Default, |
| 68 | + PartialEq, |
| 69 | + Debug, |
| 70 | + Reflect, |
| 71 | + Serialize, |
| 72 | + Deserialize, |
| 73 | +)] |
| 74 | +#[reflect(Component, Serialize, Deserialize, PartialEq)] |
| 75 | +pub struct RelativeCursorPosition { |
| 76 | + /// Cursor position relative to size and position of the Node. |
| 77 | + pub normalized: Option<Vec2>, |
| 78 | +} |
| 79 | + |
| 80 | +impl RelativeCursorPosition { |
| 81 | + /// A helper function to check if the mouse is over the node |
| 82 | + pub fn mouse_over(&self) -> bool { |
| 83 | + self.normalized |
| 84 | + .map(|position| (0.0..1.).contains(&position.x) && (0.0..1.).contains(&position.y)) |
| 85 | + .unwrap_or(false) |
| 86 | + } |
| 87 | +} |
| 88 | + |
55 | 89 | /// Describes whether the node should block interactions with lower nodes
|
56 | 90 | #[derive(Component, Copy, Clone, Eq, PartialEq, Debug, Reflect, Serialize, Deserialize)]
|
57 | 91 | #[reflect(Component, Serialize, Deserialize, PartialEq)]
|
@@ -86,6 +120,7 @@ pub struct NodeQuery {
|
86 | 120 | node: &'static Node,
|
87 | 121 | global_transform: &'static GlobalTransform,
|
88 | 122 | interaction: Option<&'static mut Interaction>,
|
| 123 | + relative_cursor_position: Option<&'static mut RelativeCursorPosition>, |
89 | 124 | focus_policy: Option<&'static FocusPolicy>,
|
90 | 125 | calculated_clip: Option<&'static CalculatedClip>,
|
91 | 126 | computed_visibility: Option<&'static ComputedVisibility>,
|
@@ -175,20 +210,34 @@ pub fn ui_focus_system(
|
175 | 210 | let ui_position = position.truncate();
|
176 | 211 | let extents = node.node.size() / 2.0;
|
177 | 212 | let mut min = ui_position - extents;
|
178 |
| - let mut max = ui_position + extents; |
179 | 213 | if let Some(clip) = node.calculated_clip {
|
180 | 214 | min = Vec2::max(min, clip.clip.min);
|
181 |
| - max = Vec2::min(max, clip.clip.max); |
182 | 215 | }
|
183 |
| - // if the current cursor position is within the bounds of the node, consider it for |
| 216 | + |
| 217 | + // The mouse position relative to the node |
| 218 | + // (0., 0.) is the top-left corner, (1., 1.) is the bottom-right corner |
| 219 | + let relative_cursor_position = cursor_position.map(|cursor_position| { |
| 220 | + Vec2::new( |
| 221 | + (cursor_position.x - min.x) / node.node.size().x, |
| 222 | + (cursor_position.y - min.y) / node.node.size().y, |
| 223 | + ) |
| 224 | + }); |
| 225 | + |
| 226 | + // If the current cursor position is within the bounds of the node, consider it for |
184 | 227 | // clicking
|
185 |
| - let contains_cursor = if let Some(cursor_position) = cursor_position { |
186 |
| - (min.x..max.x).contains(&cursor_position.x) |
187 |
| - && (min.y..max.y).contains(&cursor_position.y) |
188 |
| - } else { |
189 |
| - false |
| 228 | + let relative_cursor_position_component = RelativeCursorPosition { |
| 229 | + normalized: relative_cursor_position, |
190 | 230 | };
|
191 | 231 |
|
| 232 | + let contains_cursor = relative_cursor_position_component.mouse_over(); |
| 233 | + |
| 234 | + // Save the relative cursor position to the correct component |
| 235 | + if let Some(mut node_relative_cursor_position_component) = |
| 236 | + node.relative_cursor_position |
| 237 | + { |
| 238 | + *node_relative_cursor_position_component = relative_cursor_position_component; |
| 239 | + } |
| 240 | + |
192 | 241 | if contains_cursor {
|
193 | 242 | Some(*entity)
|
194 | 243 | } else {
|
|
0 commit comments