Allow user to choose default ui camera (#11436)

# Objective

- Resolves #11377 

## Solution

- Add marker component `IsDefaultUiCamera` that will be choosen first as
the default camera.

If you want the IsDefaultUiCamera default camera to be in another
window, thats now possible.
- `IsDefaultUiCamera` is expected to be within a single Camera, if that
assertion fails, one PrimaryWindow Camera will be choosen.

---

## Changelog

### Added
- Added `IsDefaultUiCamera` marker component.

---------

Co-authored-by: Mateusz Wachowiak <mateusz_wachowiak@outlook.com>
This commit is contained in:
pablo-lua 2024-01-27 14:27:24 -03:00 committed by GitHub
parent 3851679173
commit 1e241fb6b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -9,7 +9,7 @@ use bevy_render::{
texture::Image,
};
use bevy_transform::prelude::GlobalTransform;
use bevy_utils::smallvec::SmallVec;
use bevy_utils::{smallvec::SmallVec, warn_once};
use bevy_window::{PrimaryWindow, WindowRef};
use std::num::{NonZeroI16, NonZeroU16};
use thiserror::Error;
@ -1844,22 +1844,69 @@ impl TargetCamera {
}
}
#[derive(Component)]
/// Marker used to identify default cameras, they will have priority over the [`PrimaryWindow`] camera.
///
/// This is useful if the [`PrimaryWindow`] has two cameras, one of them used
/// just for debug purposes and the user wants a way to choose the default [`Camera`]
/// without having to add a [`TargetCamera`] to the root node.
///
/// Another use is when the user wants the Ui to be in another window by default,
/// all that is needed is to place this component on the camera
///
/// ```
/// # use bevy_ui::prelude::*;
/// # use bevy_ecs::prelude::Commands;
/// # use bevy_render::camera::{Camera, RenderTarget};
/// # use bevy_core_pipeline::prelude::Camera2dBundle;
/// # use bevy_window::{Window, WindowRef};
///
/// fn spawn_camera(mut commands: Commands) {
/// let another_window = commands.spawn(Window {
/// title: String::from("Another window"),
/// ..Default::default()
/// }).id();
/// commands.spawn((
/// Camera2dBundle {
/// camera: Camera {
/// target: RenderTarget::Window(WindowRef::Entity(another_window)),
/// ..Default::default()
/// },
/// ..Default::default()
/// },
/// // We add the Marker here so all Ui will spawn in
/// // another window if no TargetCamera is specified
/// IsDefaultUiCamera
/// ));
/// }
/// ```
pub struct IsDefaultUiCamera;
#[derive(SystemParam)]
pub struct DefaultUiCamera<'w, 's> {
cameras: Query<'w, 's, (Entity, &'static Camera)>,
default_cameras: Query<'w, 's, Entity, (With<Camera>, With<IsDefaultUiCamera>)>,
primary_window: Query<'w, 's, Entity, With<PrimaryWindow>>,
}
impl<'w, 's> DefaultUiCamera<'w, 's> {
pub fn get(&self) -> Option<Entity> {
self.cameras
.iter()
.filter(|(_, c)| match c.target {
RenderTarget::Window(WindowRef::Primary) => true,
RenderTarget::Window(WindowRef::Entity(w)) => self.primary_window.get(w).is_ok(),
_ => false,
})
.max_by_key(|(e, c)| (c.order, *e))
.map(|(e, _)| e)
self.default_cameras.get_single().ok().or_else(|| {
// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.
if !self.default_cameras.is_empty() {
warn_once!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed.");
}
self.cameras
.iter()
.filter(|(_, c)| match c.target {
RenderTarget::Window(WindowRef::Primary) => true,
RenderTarget::Window(WindowRef::Entity(w)) => {
self.primary_window.get(w).is_ok()
}
_ => false,
})
.max_by_key(|(e, c)| (c.order, *e))
.map(|(e, _)| e)
})
}
}