-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Closed
Labels
A-UIGraphical user interfaces, styles, layouts, and widgetsGraphical user interfaces, styles, layouts, and widgetsC-FeatureA new feature, making something new possibleA new feature, making something new possible
Description
What problem does this solve or what need does it fill?
Interaction, used for handling mouse interactions from buttons, only has three states: Clicked, Hovered and None.
This causes sustained clicks to repeatedly fire the button pressed, in a non-intuitive and typically incorrect way.
This is problematic when
What solution would you like?
Migrate to a per-entity events (#1626, #2116) model of handling UI inputs. As part of that, expose whether a click event is new.
What alternative(s) have you considered?
Add an internal timer or state to the Button manually?
Additional context
The example below demonstrates the problem, as the purple / not-purple state flickers rapidly if you click repeatedly.
use bevy::prelude::*;
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.init_resource::<WhiteMaterial>()
.init_resource::<PurpleMaterial>()
.add_startup_system(spawn_camera.system())
.add_startup_system(spawn_buttons.system())
.add_system(purplify_on_click.system())
.add_system(enforce_purple.system())
.run()
}
// These resources store persistent handles to our white and purple materials
// See the chapter on Assets for more details on how this works
struct WhiteMaterial(Handle<ColorMaterial>);
struct PurpleMaterial(Handle<ColorMaterial>);
impl FromWorld for WhiteMaterial {
fn from_world(world: &mut World) -> Self {
let mut materials = world.get_resource_mut::<Assets<ColorMaterial>>().unwrap();
let handle = materials.add(Color::WHITE.into());
WhiteMaterial(handle)
}
}
impl FromWorld for PurpleMaterial {
fn from_world(world: &mut World) -> Self {
let mut materials = world.get_resource_mut::<Assets<ColorMaterial>>().unwrap();
let handle = materials.add(Color::PURPLE.into());
PurpleMaterial(handle)
}
}
fn spawn_camera(mut commands: Commands) {
commands.spawn_bundle(UiCameraBundle::default());
}
fn spawn_buttons(mut commands: Commands) {
let button_transforms = vec![
Transform::from_xyz(-300.0, 0.0, 0.0),
Transform::from_xyz(0.0, 0.0, 0.0),
Transform::from_xyz(300.0, 0.0, 0.0),
];
commands.spawn_batch(button_transforms.into_iter().map(|transform| ButtonBundle {
// Each button has a unique transform, based in via .map
transform,
style: Style {
// Set button size
size: Size::new(Val::Px(150.0), Val::Px(150.0)),
// Center button
margin: Rect::all(Val::Auto),
..Default::default()
},
..Default::default()
}));
}
/// Simple marker component to dictate whether our button should be purple or not
struct Purple;
fn purplify_on_click(
query: Query<(Entity, &Interaction, Option<&Purple>)>,
mut commands: Commands,
) {
for (entity, interaction, maybe_purple) in query.iter() {
if *interaction == Interaction::Clicked {
// Adds or removes the Purple marker component when the entity is clicked
match maybe_purple {
// Adding components requires a concrete value for the new component
None => commands.entity(entity).insert(Purple),
// But removing them just requires the type
Some(_) => commands.entity(entity).remove::<Purple>(),
};
}
}
}
// This example is contrived for demonstration purposes:
// it would be much more efficient to simply set the material directly
// rather than using a marker component + system
fn enforce_purple(
mut query: Query<(&mut Handle<ColorMaterial>, Option<&Purple>)>,
white_material: Res<WhiteMaterial>,
purple_material: Res<PurpleMaterial>,
) {
for (mut material, maybe_purple) in query.iter_mut() {
*material = match maybe_purple {
None => white_material.0.clone(),
Some(_) => purple_material.0.clone(),
}
}
}
Metadata
Metadata
Assignees
Labels
A-UIGraphical user interfaces, styles, layouts, and widgetsGraphical user interfaces, styles, layouts, and widgetsC-FeatureA new feature, making something new possibleA new feature, making something new possible