bevy/crates/bevy_ui/src/widget/viewport.rs
AlephCubed 3aed85a88b
Rename send_event and similar methods to write_event (#20017)
Fixes: #18963
Follows up on: #17977
Adopts: #18966

In 0.16, `EventWriter::send` was renamed to `EventWriter::write`, but
many methods were missed (sorry about that). This completes that
refactor by renaming all `send` methods and internals.

| Old | New |

|-------------------------------------|--------------------------------------|
| `World::send_event` | `World::write_event` |
| `World::send_event_default` | `World::write_event_default` |
| `World::send_event_batch` | `World::write_event_batch` |
| `DeferredWorld::send_event` | `DeferredWorld::write_event` |
| `DeferredWorld::send_event_default` |
`DeferredWorld::write_event_default` |
| `DeferredWorld::send_event_batch` | `DeferredWorld::write_event_batch`
|
| `Commands::send_event` | `Commmands::write_event` |
| `Events::send` | `Events::write` |
| `Events::send_default` | `Events::write_default` |
| `Events::send_batch` | `Events::write_batch` |
| `RemovedComponentEvents::send` | `RemovedComponentEvents::write` |
| `command::send_event` | `commmand::write_event` |
| `SendBatchIds` | `WriteBatchIds` |

---------

Co-authored-by: shwwwa <shwwwa.dev@gmail.com>
2025-07-07 22:05:16 +00:00

179 lines
5.6 KiB
Rust

use bevy_asset::Assets;
use bevy_ecs::{
component::Component,
entity::Entity,
query::{Changed, Or},
reflect::ReflectComponent,
system::{Query, ResMut},
};
#[cfg(feature = "bevy_ui_picking_backend")]
use bevy_ecs::{
event::EventReader,
system::{Commands, Res},
};
use bevy_image::{Image, ToExtents};
#[cfg(feature = "bevy_ui_picking_backend")]
use bevy_math::Rect;
use bevy_math::UVec2;
#[cfg(feature = "bevy_ui_picking_backend")]
use bevy_picking::{
events::PointerState,
hover::HoverMap,
pointer::{Location, PointerId, PointerInput, PointerLocation},
};
#[cfg(feature = "bevy_ui_picking_backend")]
use bevy_platform::collections::HashMap;
use bevy_reflect::Reflect;
use bevy_render::camera::Camera;
#[cfg(feature = "bevy_ui_picking_backend")]
use bevy_render::camera::NormalizedRenderTarget;
#[cfg(feature = "bevy_ui_picking_backend")]
use bevy_transform::components::GlobalTransform;
#[cfg(feature = "bevy_ui_picking_backend")]
use uuid::Uuid;
use crate::{ComputedNode, Node};
/// Component used to render a [`Camera::target`] to a node.
///
/// # See Also
///
/// [`update_viewport_render_target_size`]
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component, Debug)]
#[require(Node)]
#[cfg_attr(
feature = "bevy_ui_picking_backend",
require(PointerId::Custom(Uuid::new_v4()))
)]
pub struct ViewportNode {
/// The entity representing the [`Camera`] associated with this viewport.
///
/// Note that removing the [`ViewportNode`] component will not despawn this entity.
pub camera: Entity,
}
impl ViewportNode {
/// Creates a new [`ViewportNode`] with a given `camera`.
pub fn new(camera: Entity) -> Self {
Self { camera }
}
}
#[cfg(feature = "bevy_ui_picking_backend")]
/// Handles viewport picking logic.
///
/// Viewport entities that are being hovered or dragged will have all pointer inputs sent to them.
pub fn viewport_picking(
mut commands: Commands,
mut viewport_query: Query<(
Entity,
&ViewportNode,
&PointerId,
&mut PointerLocation,
&ComputedNode,
&GlobalTransform,
)>,
camera_query: Query<&Camera>,
hover_map: Res<HoverMap>,
pointer_state: Res<PointerState>,
mut pointer_inputs: EventReader<PointerInput>,
) {
// Handle hovered entities.
let mut viewport_picks: HashMap<Entity, PointerId> = hover_map
.iter()
.flat_map(|(hover_pointer_id, hits)| {
hits.iter()
.filter(|(entity, _)| viewport_query.contains(**entity))
.map(|(entity, _)| (*entity, *hover_pointer_id))
})
.collect();
// Handle dragged entities, which need to be considered for dragging in and out of viewports.
for ((pointer_id, _), pointer_state) in pointer_state.pointer_buttons.iter() {
for &target in pointer_state
.dragging
.keys()
.filter(|&entity| viewport_query.contains(*entity))
{
viewport_picks.insert(target, *pointer_id);
}
}
for (
viewport_entity,
&viewport,
&viewport_pointer_id,
mut viewport_pointer_location,
computed_node,
global_transform,
) in &mut viewport_query
{
let Some(pick_pointer_id) = viewport_picks.get(&viewport_entity) else {
// Lift the viewport pointer if it's not being used.
viewport_pointer_location.location = None;
continue;
};
let Ok(camera) = camera_query.get(viewport.camera) else {
continue;
};
let Some(cam_viewport_size) = camera.logical_viewport_size() else {
continue;
};
// Create a `Rect` in *physical* coordinates centered at the node's GlobalTransform
let node_rect = Rect::from_center_size(
global_transform.translation().truncate(),
computed_node.size(),
);
// Location::position uses *logical* coordinates
let top_left = node_rect.min * computed_node.inverse_scale_factor();
let logical_size = computed_node.size() * computed_node.inverse_scale_factor();
let Some(target) = camera.target.as_image() else {
continue;
};
for input in pointer_inputs
.read()
.filter(|input| &input.pointer_id == pick_pointer_id)
{
let local_position = (input.location.position - top_left) / logical_size;
let position = local_position * cam_viewport_size;
let location = Location {
position,
target: NormalizedRenderTarget::Image(target.clone().into()),
};
viewport_pointer_location.location = Some(location.clone());
commands.write_event(PointerInput {
location,
pointer_id: viewport_pointer_id,
action: input.action,
});
}
}
}
/// Updates the size of the associated render target for viewports when the node size changes.
pub fn update_viewport_render_target_size(
viewport_query: Query<
(&ViewportNode, &ComputedNode),
Or<(Changed<ComputedNode>, Changed<ViewportNode>)>,
>,
camera_query: Query<&Camera>,
mut images: ResMut<Assets<Image>>,
) {
for (viewport, computed_node) in &viewport_query {
let camera = camera_query.get(viewport.camera).unwrap();
let size = computed_node.size();
let Some(image_handle) = camera.target.as_image() else {
continue;
};
let size = size.as_uvec2().max(UVec2::ONE).to_extents();
images.get_mut(image_handle).unwrap().resize(size);
}
}