diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index 41cd0c2ca5b46..2b2b64f29c19c 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -21,6 +21,17 @@ pub enum MouseButton { Other(u8), } +impl Into for MouseButton { + fn into(self) -> u16 { + match self { + MouseButton::Left => 0, + MouseButton::Right => 1, + MouseButton::Middle => 2, + MouseButton::Other(i) => u16::from(i), + } + } +} + /// A mouse motion event #[derive(Debug, Clone)] pub struct MouseMotion { diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 9006ef20d5c9b..c1cd1739826c6 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -9,11 +9,44 @@ use bevy_window::CursorMoved; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum Interaction { - Clicked, + Clicked(MouseFlags), Hovered, None, } +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct MouseFlags(u16); + +impl MouseFlags { + pub fn check(self, button: MouseButton) -> bool { + let i: u16 = button.into(); + ((self.0 << i) >> 15) > 0 + } + + pub fn build(button: &MouseButton) -> Self { + button.clone().into() + } + + pub fn remove(&mut self, button: &MouseButton) { + let i: u16 = button.clone().into(); + let mask = !((u16::MAX << 15) >> i); + self.0 &= mask; + } + + pub fn add(&mut self, button: &MouseButton) { + let i: u16 = button.clone().into(); + let mask = (u16::MAX << 15) >> i; + self.0 |= mask; + } +} + +impl From for MouseFlags { + fn from(button: MouseButton) -> Self { + let i: u16 = button.into(); + MouseFlags((u16::MAX << 15) >> i) + } +} + impl Default for Interaction { fn default() -> Self { Interaction::None @@ -55,17 +88,21 @@ pub fn ui_focus_system( state.cursor_position = cursor_moved.position; } - if mouse_button_input.just_released(MouseButton::Left) { - for (_entity, _node, _transform, interaction, _focus_policy) in &mut node_query.iter() { - if let Some(mut interaction) = interaction { - if *interaction == Interaction::Clicked { - *interaction = Interaction::None; + for (_entity, _node, _transform, interaction, _focus_policy) in &mut node_query.iter() { + if let Some(mut interaction) = interaction { + for released in mouse_button_input.get_just_released() { + if let Interaction::Clicked(mut flags) = *interaction { + flags.remove(released); + *interaction = Interaction::Clicked(flags); } } + + if let Interaction::Clicked(MouseFlags(0)) = *interaction { + *interaction = Interaction::None; + } } } - let mouse_clicked = mouse_button_input.just_pressed(MouseButton::Left); let mut hovered_entity = None; { @@ -97,13 +134,29 @@ pub fn ui_focus_system( moused_over_z_sorted_nodes.sort_by_key(|(_, _, _, z)| -*z); for (entity, focus_policy, interaction, _) in moused_over_z_sorted_nodes { if let Some(mut interaction) = interaction { - if mouse_clicked { - // only consider nodes with ClickState "clickable" - if *interaction != Interaction::Clicked { - *interaction = Interaction::Clicked; - } - } else if *interaction == Interaction::None { + // Build bitflags out of hashset + let new_flags = mouse_button_input.get_just_pressed().fold( + MouseFlags(0), + |mut flags, button| { + flags.add(button); + flags + }, + ); + + let old_flags = + mouse_button_input + .get_pressed() + .fold(MouseFlags(0), |mut flags, button| { + flags.add(button); + flags + }); + + if old_flags.0 == 0 + // nothing is clicked + { *interaction = Interaction::Hovered; + } else if new_flags.0 != 0 { + *interaction = Interaction::Clicked(old_flags) } } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 56ccaeb31a772..4d61f44e4a431 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -20,7 +20,7 @@ pub mod prelude { entity::*, node::*, widget::{Button, Text}, - Anchors, Interaction, Margins, + Anchors, Interaction, Margins, MouseFlags, }; } diff --git a/examples/ui/button.rs b/examples/ui/button.rs index 6e371de7804bc..ac70f3a998a71 100644 --- a/examples/ui/button.rs +++ b/examples/ui/button.rs @@ -40,8 +40,18 @@ fn button_system( for (_button, interaction, mut material, children) in &mut interaction_query.iter() { let mut text = text_query.get_mut::(children[0]).unwrap(); match *interaction { - Interaction::Clicked => { - text.value = "Press".to_string(); + Interaction::Clicked(i) => { + let mut s = String::from("Pressing button(s)"); + if i.check(MouseButton::Left) { + s.push_str(" Left"); + } + if i.check(MouseButton::Right) { + s.push_str(" Right"); + } + if i.check(MouseButton::Middle) { + s.push_str(" Middle"); + } + text.value = s; *material = button_materials.pressed; } Interaction::Hovered => { @@ -66,7 +76,7 @@ fn setup( .spawn(UiCameraComponents::default()) .spawn(ButtonComponents { style: Style { - size: Size::new(Val::Px(150.0), Val::Px(65.0)), + size: Size::new(Val::Px(550.0), Val::Px(65.0)), // center button margin: Rect::all(Val::Auto), // horizontally center child text