Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Relative cursor position #7199

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
71056a3
made resource_scope work for non-send resources
Pietrek14 Sep 27, 2022
d607e6b
Merge branch 'bevyengine:main' into main
Pietrek14 Sep 27, 2022
83f2417
fixed the non-send resource scope tests
Pietrek14 Sep 27, 2022
6da0e41
formatting
Pietrek14 Sep 27, 2022
3500039
simplified panic in a non-send resource scope test
Pietrek14 Sep 28, 2022
d823b1b
Merge branch 'bevyengine:main' into main
Pietrek14 Sep 28, 2022
f68eb33
changed the name of non-send struct used for testing
Pietrek14 Sep 28, 2022
6ed303e
Merge branch 'bevyengine:main' into main
Pietrek14 Oct 6, 2022
23a22ae
Merge branch 'bevyengine:main' into main
Pietrek14 Jan 7, 2023
a27f8b1
Merge branch 'main' of https://github.com/bevyengine/bevy into main
Pietrek14 Jan 14, 2023
8513317
added relative cursor position
Pietrek14 Jan 14, 2023
a81cb5a
added a relative cursor position example
Pietrek14 Jan 14, 2023
0921e75
updated example pages
Pietrek14 Jan 14, 2023
42699d1
comment clarification
Pietrek14 Jan 15, 2023
ae57369
line break after doc comment
Pietrek14 Jan 15, 2023
3ddd0c0
made relative cursor position an option
Pietrek14 Jan 15, 2023
2b64fa6
Merge branch 'relative-cursor-position' of https://github.com/Pietrek…
Pietrek14 Jan 15, 2023
9713cc8
clarification
Pietrek14 Jan 15, 2023
32cde9a
added a helper method to check if mouse is over a node
Pietrek14 Jan 15, 2023
b52f950
doc comment for RelativeCursorPosition::normalized
Pietrek14 Jan 15, 2023
6a00611
comment for example system
Pietrek14 Jan 15, 2023
b5d915e
Merge branch 'bevyengine:main' into relative-cursor-position
Pietrek14 Jan 15, 2023
2462874
changed upper-left to top-left
Pietrek14 Jan 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
48 changes: 42 additions & 6 deletions crates/bevy_ui/src/focus.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
Pietrek14 marked this conversation as resolved.
Show resolved Hide resolved
/// 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);
Pietrek14 marked this conversation as resolved.
Show resolved Hide resolved
Pietrek14 marked this conversation as resolved.
Show resolved Hide resolved
Pietrek14 marked this conversation as resolved.
Show resolved Hide resolved

/// 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)]
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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
};
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 76 additions & 0 deletions examples/ui/relative_cursor_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//! Showcases the `RelativeCursorPosition` component.
Pietrek14 marked this conversation as resolved.
Show resolved Hide resolved

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<AssetServer>) {
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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all node-bundles with some form of interaction have this component as default? I think it would make sense on the ButtonBundle at the very least. @alice-i-cecile, thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that it's necessary. If someone needs this functionality they can always insert the component themselves, but I imagine most people won't use it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, having thought about it a bit more, it only makes sense to add this to "default" bundles when we are building some more functionality on top of this and that is a requirement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, we should only add components to bundles when they have a built-in use.


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(
Pietrek14 marked this conversation as resolved.
Show resolved Hide resolved
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)
};
}