OverrideClip
interaction fix (#20064)
# Objective Picking was changed in the UI transform PR to walk up the tree recursively to check if an interaction was on a clipped node. `OverrideClip` only affects a node's local clipping rect, so valid interactions can be ignored if a node has clipped ancestors. ## Solution Add a `Without<OverrideClip>` query filter to the picking systems' `child_of_query`s. ## Testing This modified `button` example can be used to test the change: ```rust //! This example illustrates how to create a button that changes color and text based on its //! interaction state. use bevy::{color::palettes::basic::*, input_focus::InputFocus, prelude::*, 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()) // `InputFocus` must be set for accessibility to recognize the button. .init_resource::<InputFocus>() .add_systems(Startup, setup) .add_systems(Update, button_system) .run(); } const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15); const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25); const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35); fn button_system( mut input_focus: ResMut<InputFocus>, mut interaction_query: Query< ( Entity, &Interaction, &mut BackgroundColor, &mut BorderColor, &mut Button, &Children, ), Changed<Interaction>, >, mut text_query: Query<&mut Text>, ) { for (entity, interaction, mut color, mut border_color, mut button, children) in &mut interaction_query { let mut text = text_query.get_mut(children[0]).unwrap(); match *interaction { Interaction::Pressed => { input_focus.set(entity); **text = "Press".to_string(); *color = PRESSED_BUTTON.into(); *border_color = BorderColor::all(RED.into()); // The accessibility system's only update the button's state when the `Button` component is marked as changed. button.set_changed(); } Interaction::Hovered => { input_focus.set(entity); **text = "Hover".to_string(); *color = HOVERED_BUTTON.into(); *border_color = BorderColor::all(Color::WHITE); button.set_changed(); } Interaction::None => { input_focus.clear(); **text = "Button".to_string(); *color = NORMAL_BUTTON.into(); *border_color = BorderColor::all(Color::BLACK); } } } } fn setup(mut commands: Commands, assets: Res<AssetServer>) { // ui camera commands.spawn(Camera2d); commands.spawn(button(&assets)); } fn button(asset_server: &AssetServer) -> impl Bundle + use<> { ( Node { width: Val::Percent(100.0), height: Val::Percent(100.0), align_items: AlignItems::Center, justify_content: JustifyContent::Center, ..default() }, children![( Node { width: Val::Px(0.), height: Val::Px(0.), overflow: Overflow::clip(), ..default() }, children![( //OverrideClip, Button, Node { position_type: PositionType::Absolute, width: Val::Px(150.0), height: Val::Px(65.0), border: UiRect::all(Val::Px(5.0)), // horizontally center child text justify_content: JustifyContent::Center, // vertically center child text align_items: AlignItems::Center, ..default() }, BorderColor::all(Color::WHITE), BorderRadius::MAX, BackgroundColor(Color::BLACK), children![( Text::new("Button"), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf"), font_size: 33.0, ..default() }, TextColor(Color::srgb(0.9, 0.9, 0.9)), TextShadow::default(), )] )], )], ) } ``` On main the button ignores interactions, with this PR it should respond correctly. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
63dd8b6652
commit
df5dfcd298
@ -1,10 +1,12 @@
|
||||
use crate::{ui_transform::UiGlobalTransform, ComputedNode, ComputedNodeTarget, Node, UiStack};
|
||||
use crate::{
|
||||
ui_transform::UiGlobalTransform, ComputedNode, ComputedNodeTarget, Node, OverrideClip, UiStack,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
change_detection::DetectChangesMut,
|
||||
entity::{ContainsEntity, Entity},
|
||||
hierarchy::ChildOf,
|
||||
prelude::{Component, With},
|
||||
query::QueryData,
|
||||
query::{QueryData, Without},
|
||||
reflect::ReflectComponent,
|
||||
system::{Local, Query, Res},
|
||||
};
|
||||
@ -157,7 +159,7 @@ pub fn ui_focus_system(
|
||||
ui_stack: Res<UiStack>,
|
||||
mut node_query: Query<NodeQuery>,
|
||||
clipping_query: Query<(&ComputedNode, &UiGlobalTransform, &Node)>,
|
||||
child_of_query: Query<&ChildOf>,
|
||||
child_of_query: Query<&ChildOf, Without<OverrideClip>>,
|
||||
) {
|
||||
let primary_window = primary_window.iter().next();
|
||||
|
||||
@ -325,11 +327,12 @@ pub fn ui_focus_system(
|
||||
}
|
||||
|
||||
/// Walk up the tree child-to-parent checking that `point` is not clipped by any ancestor node.
|
||||
/// If `entity` has an [`OverrideClip`] component it ignores any inherited clipping and returns true.
|
||||
pub fn clip_check_recursive(
|
||||
point: Vec2,
|
||||
entity: Entity,
|
||||
clipping_query: &Query<'_, '_, (&ComputedNode, &UiGlobalTransform, &Node)>,
|
||||
child_of_query: &Query<&ChildOf>,
|
||||
child_of_query: &Query<&ChildOf, Without<OverrideClip>>,
|
||||
) -> bool {
|
||||
if let Ok(child_of) = child_of_query.get(entity) {
|
||||
let parent = child_of.0;
|
||||
|
@ -109,7 +109,7 @@ pub fn ui_picking(
|
||||
node_query: Query<NodeQuery>,
|
||||
mut output: EventWriter<PointerHits>,
|
||||
clipping_query: Query<(&ComputedNode, &UiGlobalTransform, &Node)>,
|
||||
child_of_query: Query<&ChildOf>,
|
||||
child_of_query: Query<&ChildOf, Without<OverrideClip>>,
|
||||
) {
|
||||
// For each camera, the pointer and its position
|
||||
let mut pointer_pos_by_camera = HashMap::<Entity, HashMap<PointerId, Vec2>>::default();
|
||||
|
Loading…
Reference in New Issue
Block a user