diff --git a/crates/bevy_hierarchy/src/events.rs b/crates/bevy_hierarchy/src/events.rs index c397488c08..5a667fef77 100644 --- a/crates/bevy_hierarchy/src/events.rs +++ b/crates/bevy_hierarchy/src/events.rs @@ -1,4 +1,5 @@ use bevy_ecs::{event::Event, prelude::Entity}; +#[cfg(feature = "reflect")] use bevy_reflect::Reflect; /// An [`Event`] that is fired whenever there is a change in the world's hierarchy. diff --git a/crates/bevy_input/src/button_input.rs b/crates/bevy_input/src/button_input.rs index d329470d8b..c781fafe49 100644 --- a/crates/bevy_input/src/button_input.rs +++ b/crates/bevy_input/src/button_input.rs @@ -210,7 +210,17 @@ where /// Returns `true` if any item in `inputs` has just been released. pub fn any_just_released(&self, inputs: impl IntoIterator) -> bool { - inputs.into_iter().any(|it| self.just_released(it)) + inputs.into_iter().any(|input| self.just_released(input)) + } + + /// Returns `true` if all items in `inputs` have just been released. + pub fn all_just_released(&self, inputs: impl IntoIterator) -> bool { + inputs.into_iter().all(|input| self.just_released(input)) + } + + /// Returns `true` if all items in `inputs` have been just pressed. + pub fn all_just_pressed(&self, inputs: impl IntoIterator) -> bool { + inputs.into_iter().all(|input| self.just_pressed(input)) } /// Clears the `just_released` state of the `input` and returns `true` if the `input` has just been released. diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index a356e525b4..5493103a61 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -307,7 +307,7 @@ pub enum ButtonSettingsError { }, } -/// Stores a connected gamepad's state and any metadata such as the device name. +/// Stores a connected gamepad's metadata such as the name and its [`GamepadButton`] and [`GamepadAxis`]. /// /// An entity with this component is spawned automatically after [`GamepadConnectionEvent`] /// and updated by [`gamepad_event_processing_system`]. @@ -325,11 +325,11 @@ pub enum ButtonSettingsError { /// for (name, gamepad) in &gamepads { /// println!("{name}"); /// -/// if gamepad.digital.just_pressed(GamepadButton::North) { -/// println!("{name} just pressed North") +/// if gamepad.just_pressed(GamepadButton::North) { +/// println!("{} just pressed North", name) /// } /// -/// if let Some(left_stick_x) = gamepad.analog.get(GamepadAxis::LeftStickX) { +/// if let Some(left_stick_x) = gamepad.get(GamepadAxis::LeftStickX) { /// println!("left stick X: {}", left_stick_x) /// } /// } @@ -340,46 +340,175 @@ pub enum ButtonSettingsError { #[require(GamepadSettings)] pub struct Gamepad { /// The USB vendor ID as assigned by the USB-IF, if available. - pub vendor_id: Option, + pub(crate) vendor_id: Option, /// The USB product ID as assigned by the [vendor], if available. /// /// [vendor]: Self::vendor_id - pub product_id: Option, + pub(crate) product_id: Option, /// [`ButtonInput`] of [`GamepadButton`] representing their digital state - pub digital: ButtonInput, + pub(crate) digital: ButtonInput, /// [`Axis`] of [`GamepadButton`] representing their analog state. - pub analog: Axis, + pub(crate) analog: Axis, } impl Gamepad { + /// Returns the USB vendor ID as assigned by the USB-IF, if available. + pub fn vendor_id(&self) -> Option { + self.vendor_id + } + + /// Returns the USB product ID as assigned by the [vendor], if available. + /// + /// [vendor]: Self::vendor_id + pub fn product_id(&self) -> Option { + self.product_id + } + + /// Returns the analog data of the provided [`GamepadAxis`] or [`GamepadButton`]. + /// + /// This will be clamped between [[`Axis::MIN`],[`Axis::MAX`]]. + pub fn get(&self, input: impl Into) -> Option { + self.analog.get(input.into()) + } + + /// Returns the unclamped analog data of the provided [`GamepadAxis`] or [`GamepadButton`]. + /// + /// This value may be outside the [`Axis::MIN`] and [`Axis::MAX`] range. + pub fn get_unclamped(&self, input: impl Into) -> Option { + self.analog.get_unclamped(input.into()) + } + /// Returns the left stick as a [`Vec2`] pub fn left_stick(&self) -> Vec2 { Vec2 { - x: self.analog.get(GamepadAxis::LeftStickX).unwrap_or(0.0), - y: self.analog.get(GamepadAxis::LeftStickY).unwrap_or(0.0), + x: self.get(GamepadAxis::LeftStickX).unwrap_or(0.0), + y: self.get(GamepadAxis::LeftStickY).unwrap_or(0.0), } } /// Returns the right stick as a [`Vec2`] pub fn right_stick(&self) -> Vec2 { Vec2 { - x: self.analog.get(GamepadAxis::RightStickX).unwrap_or(0.0), - y: self.analog.get(GamepadAxis::RightStickY).unwrap_or(0.0), + x: self.get(GamepadAxis::RightStickX).unwrap_or(0.0), + y: self.get(GamepadAxis::RightStickY).unwrap_or(0.0), } } /// Returns the directional pad as a [`Vec2`] pub fn dpad(&self) -> Vec2 { Vec2 { - x: self.analog.get(GamepadButton::DPadRight).unwrap_or(0.0) - - self.analog.get(GamepadButton::DPadLeft).unwrap_or(0.0), - y: self.analog.get(GamepadButton::DPadUp).unwrap_or(0.0) - - self.analog.get(GamepadButton::DPadDown).unwrap_or(0.0), + x: self.get(GamepadButton::DPadRight).unwrap_or(0.0) + - self.get(GamepadButton::DPadLeft).unwrap_or(0.0), + y: self.get(GamepadButton::DPadUp).unwrap_or(0.0) + - self.get(GamepadButton::DPadDown).unwrap_or(0.0), } } + + /// Returns `true` if the [`GamepadButton`] has been pressed. + pub fn pressed(&self, button_type: GamepadButton) -> bool { + self.digital.pressed(button_type) + } + + /// Returns `true` if any item in the [`GamepadButton`] iterator has been pressed. + pub fn any_pressed(&self, button_inputs: impl IntoIterator) -> bool { + self.digital.any_pressed(button_inputs) + } + + /// Returns `true` if all items in the [`GamepadButton`] iterator have been pressed. + pub fn all_pressed(&self, button_inputs: impl IntoIterator) -> bool { + self.digital.all_pressed(button_inputs) + } + + /// Returns `true` if the [`GamepadButton`] has been pressed during the current frame. + /// + /// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_released`]. + pub fn just_pressed(&self, button_type: GamepadButton) -> bool { + self.digital.just_pressed(button_type) + } + + /// Returns `true` if any item in the [`GamepadButton`] iterator has been pressed during the current frame. + pub fn any_just_pressed(&self, button_inputs: impl IntoIterator) -> bool { + self.digital.any_just_pressed(button_inputs) + } + + /// Returns `true` if all items in the [`GamepadButton`] iterator have been just pressed. + pub fn all_just_pressed(&self, button_inputs: impl IntoIterator) -> bool { + self.digital.all_just_pressed(button_inputs) + } + + /// Returns `true` if the [`GamepadButton`] has been released during the current frame. + /// + /// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_pressed`]. + pub fn just_released(&self, button_type: GamepadButton) -> bool { + self.digital.just_released(button_type) + } + + /// Returns `true` if any item in the [`GamepadButton`] iterator has just been released. + pub fn any_just_released( + &self, + button_inputs: impl IntoIterator, + ) -> bool { + self.digital.any_just_released(button_inputs) + } + + /// Returns `true` if all items in the [`GamepadButton`] iterator have just been released. + pub fn all_just_released( + &self, + button_inputs: impl IntoIterator, + ) -> bool { + self.digital.all_just_released(button_inputs) + } + + /// Returns an iterator over all digital [button]s that are pressed. + /// + /// [button]: GamepadButton + pub fn get_pressed(&self) -> impl Iterator { + self.digital.get_pressed() + } + + /// Returns an iterator over all digital [button]s that were just pressed. + /// + /// [button]: GamepadButton + pub fn get_just_pressed(&self) -> impl Iterator { + self.digital.get_just_pressed() + } + + /// Returns an iterator over all digital [button]s that were just released. + /// + /// [button]: GamepadButton + pub fn get_just_released(&self) -> impl Iterator { + self.digital.get_just_released() + } + + /// Returns an iterator over all analog [axes]. + /// + /// [axes]: GamepadInput + pub fn get_analog_axes(&self) -> impl Iterator { + self.analog.all_axes() + } + + /// [`ButtonInput`] of [`GamepadButton`] representing their digital state + pub fn digital(&self) -> &ButtonInput { + &self.digital + } + + /// Mutable [`ButtonInput`] of [`GamepadButton`] representing their digital state. Useful for mocking inputs. + pub fn digital_mut(&mut self) -> &mut ButtonInput { + &mut self.digital + } + + /// [`Axis`] of [`GamepadButton`] representing their analog state. + pub fn analog(&self) -> &Axis { + &self.analog + } + + /// Mutable [`Axis`] of [`GamepadButton`] representing their analog state. Useful for mocking inputs. + pub fn analog_mut(&mut self) -> &mut Axis { + &mut self.analog + } } impl Default for Gamepad { @@ -1307,7 +1436,7 @@ pub fn gamepad_event_processing_system( }; let Some(filtered_value) = gamepad_settings .get_axis_settings(axis) - .filter(value, gamepad_axis.analog.get(axis)) + .filter(value, gamepad_axis.get(axis)) else { continue; }; @@ -1328,7 +1457,7 @@ pub fn gamepad_event_processing_system( }; let Some(filtered_value) = settings .get_button_axis_settings(button) - .filter(value, gamepad_buttons.analog.get(button)) + .filter(value, gamepad_buttons.get(button)) else { continue; }; @@ -1337,7 +1466,7 @@ pub fn gamepad_event_processing_system( if button_settings.is_released(filtered_value) { // Check if button was previously pressed - if gamepad_buttons.digital.pressed(button) { + if gamepad_buttons.pressed(button) { processed_digital_events.send(GamepadButtonStateChangedEvent::new( gamepad, button, @@ -1349,7 +1478,7 @@ pub fn gamepad_event_processing_system( gamepad_buttons.digital.release(button); } else if button_settings.is_pressed(filtered_value) { // Check if button was previously not pressed - if !gamepad_buttons.digital.pressed(button) { + if !gamepad_buttons.pressed(button) { processed_digital_events.send(GamepadButtonStateChangedEvent::new( gamepad, button, @@ -2403,8 +2532,13 @@ mod tests { assert_eq!(event.button, GamepadButton::DPadDown); assert_eq!(event.state, ButtonState::Pressed); } - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(gamepad.digital.pressed(GamepadButton::DPadDown)); + assert!(ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .pressed(GamepadButton::DPadDown)); ctx.app .world_mut() @@ -2419,8 +2553,13 @@ mod tests { .len(), 0 ); - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(gamepad.digital.pressed(GamepadButton::DPadDown)); + assert!(ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .pressed(GamepadButton::DPadDown)); } #[test] @@ -2439,13 +2578,23 @@ mod tests { ctx.update(); // Check it is flagged for this frame - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(gamepad.digital.just_pressed(GamepadButton::DPadDown)); + assert!(ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .just_pressed(GamepadButton::DPadDown)); ctx.update(); //Check it clears next frame - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(!gamepad.digital.just_pressed(GamepadButton::DPadDown)); + assert!(!ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .just_pressed(GamepadButton::DPadDown)); } #[test] fn gamepad_buttons_released() { @@ -2488,8 +2637,13 @@ mod tests { assert_eq!(event.button, GamepadButton::DPadDown); assert_eq!(event.state, ButtonState::Released); } - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(!gamepad.digital.pressed(GamepadButton::DPadDown)); + assert!(!ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .pressed(GamepadButton::DPadDown)); ctx.app .world_mut() .resource_mut::>() @@ -2528,13 +2682,23 @@ mod tests { ctx.update(); // Check it is flagged for this frame - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(gamepad.digital.just_released(GamepadButton::DPadDown)); + assert!(ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .just_released(GamepadButton::DPadDown)); ctx.update(); - // Check it clears next frame - let gamepad = ctx.app.world_mut().get::(entity).unwrap(); - assert!(!gamepad.digital.just_released(GamepadButton::DPadDown)); + //Check it clears next frame + assert!(!ctx + .app + .world_mut() + .query::<&Gamepad>() + .get(ctx.app.world(), entity) + .unwrap() + .just_released(GamepadButton::DPadDown)); } #[test] diff --git a/crates/bevy_text/src/text.rs b/crates/bevy_text/src/text.rs index 13e464451b..0effb361aa 100644 --- a/crates/bevy_text/src/text.rs +++ b/crates/bevy_text/src/text.rs @@ -289,6 +289,28 @@ pub struct TextFont { } impl TextFont { + /// Returns a new [`TextFont`] with the specified font face handle. + pub fn from_font(font: Handle) -> Self { + Self::default().with_font(font) + } + + /// Returns a new [`TextFont`] with the specified font size. + pub fn from_font_size(font_size: f32) -> Self { + Self::default().with_font_size(font_size) + } + + /// Returns this [`TextFont`] with the specified font face handle. + pub fn with_font(mut self, font: Handle) -> Self { + self.font = font; + self + } + + /// Returns this [`TextFont`] with the specified font size. + pub const fn with_font_size(mut self, font_size: f32) -> Self { + self.font_size = font_size; + self + } + /// Returns this [`TextFont`] with the specified [`FontSmoothing`]. pub const fn with_font_smoothing(mut self, font_smoothing: FontSmoothing) -> Self { self.font_smoothing = font_smoothing; diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index fc19dde8bf..4be209f1ed 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -1,3 +1,5 @@ +//! Box shadows rendering + use core::{hash::Hash, ops::Range}; use crate::{ @@ -133,14 +135,14 @@ impl FromWorld for BoxShadowPipeline { } #[derive(Clone, Copy, Hash, PartialEq, Eq)] -pub struct UiTextureSlicePipelineKey { +pub struct BoxShadowPipelineKey { pub hdr: bool, /// Number of samples, a higher value results in better quality shadows. pub samples: u32, } impl SpecializedRenderPipeline for BoxShadowPipeline { - type Key = UiTextureSlicePipelineKey; + type Key = BoxShadowPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { let vertex_layout = VertexBufferLayout::from_vertex_formats( @@ -333,8 +335,8 @@ pub fn extract_shadows( } pub fn queue_shadows( - extracted_ui_slicers: ResMut, - ui_slicer_pipeline: Res, + extracted_box_shadows: ResMut, + box_shadow_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, mut views: Query<(Entity, &ExtractedView, Option<&UiBoxShadowSamples>)>, @@ -342,7 +344,7 @@ pub fn queue_shadows( draw_functions: Res>, ) { let draw_function = draw_functions.read().id::(); - for (entity, extracted_shadow) in extracted_ui_slicers.box_shadows.iter() { + for (entity, extracted_shadow) in extracted_box_shadows.box_shadows.iter() { let Ok((view_entity, view, shadow_samples)) = views.get_mut(extracted_shadow.camera_entity) else { continue; @@ -354,8 +356,8 @@ pub fn queue_shadows( let pipeline = pipelines.specialize( &pipeline_cache, - &ui_slicer_pipeline, - UiTextureSlicePipelineKey { + &box_shadow_pipeline, + BoxShadowPipelineKey { hdr: view.hdr, samples: shadow_samples.copied().unwrap_or_default().0, }, @@ -384,7 +386,7 @@ pub fn prepare_shadows( mut ui_meta: ResMut, mut extracted_shadows: ResMut, view_uniforms: Res, - texture_slicer_pipeline: Res, + box_shadow_pipeline: Res, mut phases: ResMut>, mut previous_len: Local, ) { @@ -394,8 +396,8 @@ pub fn prepare_shadows( ui_meta.vertices.clear(); ui_meta.indices.clear(); ui_meta.view_bind_group = Some(render_device.create_bind_group( - "ui_texture_slice_view_bind_group", - &texture_slicer_pipeline.view_layout, + "box_shadow_view_bind_group", + &box_shadow_pipeline.view_layout, &BindGroupEntries::single(view_binding), )); diff --git a/examples/input/gamepad_input.rs b/examples/input/gamepad_input.rs index d81e6fd836..4f3dfba4ae 100644 --- a/examples/input/gamepad_input.rs +++ b/examples/input/gamepad_input.rs @@ -11,18 +11,18 @@ fn main() { fn gamepad_system(gamepads: Query<(Entity, &Gamepad)>) { for (entity, gamepad) in &gamepads { - if gamepad.digital.just_pressed(GamepadButton::South) { + if gamepad.just_pressed(GamepadButton::South) { info!("{:?} just pressed South", entity); - } else if gamepad.digital.just_released(GamepadButton::South) { + } else if gamepad.just_released(GamepadButton::South) { info!("{:?} just released South", entity); } - let right_trigger = gamepad.analog.get(GamepadButton::RightTrigger2).unwrap(); + let right_trigger = gamepad.get(GamepadButton::RightTrigger2).unwrap(); if right_trigger.abs() > 0.01 { info!("{:?} RightTrigger2 value is {}", entity, right_trigger); } - let left_stick_x = gamepad.analog.get(GamepadAxis::LeftStickX).unwrap(); + let left_stick_x = gamepad.get(GamepadAxis::LeftStickX).unwrap(); if left_stick_x.abs() > 0.01 { info!("{:?} LeftStickX value is {}", entity, left_stick_x); } diff --git a/examples/input/gamepad_rumble.rs b/examples/input/gamepad_rumble.rs index c3262daf52..cdc5a9c85e 100644 --- a/examples/input/gamepad_rumble.rs +++ b/examples/input/gamepad_rumble.rs @@ -19,7 +19,7 @@ fn gamepad_system( mut rumble_requests: EventWriter, ) { for (entity, gamepad) in &gamepads { - if gamepad.digital.just_pressed(GamepadButton::North) { + if gamepad.just_pressed(GamepadButton::North) { info!( "North face button: strong (low-frequency) with low intensity for rumble for 5 seconds. Press multiple times to increase intensity." ); @@ -30,7 +30,7 @@ fn gamepad_system( }); } - if gamepad.digital.just_pressed(GamepadButton::East) { + if gamepad.just_pressed(GamepadButton::East) { info!("East face button: maximum rumble on both motors for 5 seconds"); rumble_requests.send(GamepadRumbleRequest::Add { gamepad: entity, @@ -39,7 +39,7 @@ fn gamepad_system( }); } - if gamepad.digital.just_pressed(GamepadButton::South) { + if gamepad.just_pressed(GamepadButton::South) { info!("South face button: low-intensity rumble on the weak motor for 0.5 seconds"); rumble_requests.send(GamepadRumbleRequest::Add { gamepad: entity, @@ -48,7 +48,7 @@ fn gamepad_system( }); } - if gamepad.digital.just_pressed(GamepadButton::West) { + if gamepad.just_pressed(GamepadButton::West) { info!("West face button: custom rumble intensity for 5 second"); rumble_requests.send(GamepadRumbleRequest::Add { gamepad: entity, @@ -62,7 +62,7 @@ fn gamepad_system( }); } - if gamepad.digital.just_pressed(GamepadButton::Start) { + if gamepad.just_pressed(GamepadButton::Start) { info!("Start button: Interrupt the current rumble"); rumble_requests.send(GamepadRumbleRequest::Stop { gamepad: entity }); } diff --git a/examples/tools/gamepad_viewer.rs b/examples/tools/gamepad_viewer.rs index 35d4fd8ab3..32549893c6 100644 --- a/examples/tools/gamepad_viewer.rs +++ b/examples/tools/gamepad_viewer.rs @@ -395,10 +395,10 @@ fn update_buttons( ) { for gamepad in &gamepads { for (mut handle, react_to) in query.iter_mut() { - if gamepad.digital.just_pressed(**react_to) { + if gamepad.just_pressed(**react_to) { *handle = materials.active.clone(); } - if gamepad.digital.just_released(**react_to) { + if gamepad.just_released(**react_to) { *handle = materials.normal.clone(); } }