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

UI- Interaction Released state for nodes #8157

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
90 changes: 54 additions & 36 deletions crates/bevy_ui/src/focus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub enum Interaction {
Hovered,
/// Nothing has happened
None,
/// The node has just been released
Released,
}

impl Interaction {
Expand Down Expand Up @@ -151,15 +153,6 @@ pub fn ui_focus_system(

let mouse_released =
mouse_button_input.just_released(MouseButton::Left) || touches_input.any_just_released();
if mouse_released {
for node in node_query.iter_mut() {
if let Some(mut interaction) = node.interaction {
if *interaction == Interaction::Clicked {
*interaction = Interaction::None;
}
}
}
}

let mouse_clicked =
mouse_button_input.just_pressed(MouseButton::Left) || touches_input.any_just_pressed();
Expand All @@ -171,10 +164,10 @@ pub fn ui_focus_system(
.iter()
.filter(|(_, camera_ui)| !is_ui_disabled(*camera_ui))
.filter_map(|(camera, _)| {
if let Some(NormalizedRenderTarget::Window(window_ref)) =
if let Some(NormalizedRenderTarget::Window(window_id)) =
camera.target.normalize(primary_window)
{
Some(window_ref)
Some(window_id)
} else {
None
}
Expand All @@ -188,6 +181,20 @@ pub fn ui_focus_system(
})
})
.or_else(|| touches_input.first_pressed_position());
if mouse_released {
for node in node_query.iter_mut() {
let contains_cursor = get_mouse_relative_to_node(&node, cursor_position).mouse_over();
if let Some(mut interaction) = node.interaction {
if *interaction == Interaction::Clicked {
*interaction = if contains_cursor {
Interaction::Released
} else {
Interaction::None
};
}
}
}
}

// prepare an iterator that contains all the nodes that have the cursor in their rect,
// from the top node to the bottom one. this will also reset the interaction to `None`
Expand All @@ -211,29 +218,8 @@ pub fn ui_focus_system(
return None;
}
}

let position = node.global_transform.translation();
let ui_position = position.truncate();
let extents = node.node.size() / 2.0;
let mut min = ui_position - extents;
if let Some(clip) = node.calculated_clip {
min = Vec2::max(min, clip.clip.min);
}

// The mouse position relative to the node
// (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,
(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 relative_cursor_position_component =
get_mouse_relative_to_node(&node, cursor_position);

let contains_cursor = relative_cursor_position_component.mouse_over();

Expand All @@ -248,7 +234,10 @@ pub fn ui_focus_system(
Some(*entity)
} else {
if let Some(mut interaction) = node.interaction {
if *interaction == Interaction::Hovered || (cursor_position.is_none()) {
if *interaction == Interaction::Hovered
|| *interaction == Interaction::Released
|| (cursor_position.is_none())
{
interaction.set_if_neq(Interaction::None);
}
}
Expand Down Expand Up @@ -289,7 +278,7 @@ pub fn ui_focus_system(
}
}
// reset `Interaction` for the remaining lower nodes to `None`. those are the nodes that remain in
// `moused_over_nodes` after the previous loop is exited.
// `hovered_nodes` after the previous loop is exited.
let mut iter = node_query.iter_many_mut(hovered_nodes);
while let Some(node) = iter.fetch_next() {
if let Some(mut interaction) = node.interaction {
Expand All @@ -300,3 +289,32 @@ pub fn ui_focus_system(
}
}
}

/// helper functions for calculating cursor relative position to node
fn get_mouse_relative_to_node(
node: &NodeQueryItem,
cursor_position: Option<Vec2>,
) -> RelativeCursorPosition {
let position = node.global_transform.translation();
let ui_position = position.truncate();
let extents = node.node.size() / 2.0;
let mut min = ui_position - extents;
if let Some(clip) = node.calculated_clip {
min = Vec2::max(min, clip.clip.min);
}

// The mouse position relative to the node
// (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,
(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
RelativeCursorPosition {
normalized: relative_cursor_position,
}
}
2 changes: 1 addition & 1 deletion examples/ecs/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ fn menu(
) {
for (interaction, mut color) in &mut interaction_query {
match *interaction {
Interaction::Clicked => {
Interaction::Clicked | Interaction::Released => {
*color = PRESSED_BUTTON.into();
next_state.set(AppState::InGame);
}
Expand Down
8 changes: 5 additions & 3 deletions examples/games/game_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,12 @@ mod menu {
) {
for (interaction, mut color, selected) in &mut interaction_query {
*color = match (*interaction, selected) {
(Interaction::Clicked, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON.into(),
(Interaction::Clicked, _)
| (Interaction::Released, _)
| (Interaction::None, Some(_)) => PRESSED_BUTTON.into(),
(Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON.into(),
(Interaction::Hovered, None) => HOVERED_BUTTON.into(),
(Interaction::None, None) => NORMAL_BUTTON.into(),
(_, _) => NORMAL_BUTTON.into(),
}
}
}
Expand Down Expand Up @@ -796,7 +798,7 @@ mod menu {
mut game_state: ResMut<NextState<GameState>>,
) {
for (interaction, menu_button_action) in &interaction_query {
if *interaction == Interaction::Clicked {
if *interaction == Interaction::Released {
match menu_button_action {
MenuButtonAction::Quit => app_exit_events.send(AppExit),
MenuButtonAction::Play => {
Expand Down
2 changes: 1 addition & 1 deletion examples/mobile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ fn button_handler(
) {
for (interaction, mut color) in &mut interaction_query {
match *interaction {
Interaction::Clicked => {
Interaction::Clicked | Interaction::Released => {
*color = Color::BLUE.into();
}
Interaction::Hovered => {
Expand Down
4 changes: 4 additions & 0 deletions examples/ui/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ fn button_system(
for (interaction, mut color, children) in &mut interaction_query {
let mut text = text_query.get_mut(children[0]).unwrap();
match *interaction {
Interaction::Released => {
text.sections[0].value = "Released".to_string();
*color = PRESSED_BUTTON.into();
}
Interaction::Clicked => {
text.sections[0].value = "Press".to_string();
*color = PRESSED_BUTTON.into();
Expand Down