1use bevy_core_pipeline::core_2d::*;
6use bevy_ecs::{
7 prelude::*,
8 query::{QueryData, QueryItem},
9 system::{StaticSystemParam, SystemParam, SystemParamItem, lifetimeless::Read},
10};
11use bevy_math::prelude::*;
12use bevy_render::prelude::*;
13use bevy_transform::prelude::*;
14
15pub trait UiRoot: Component {
25 type Param: SystemParam;
27 type Item: QueryData;
30
31 fn compute_root_transform(
34 &mut self,
35 param: &mut SystemParamItem<Self::Param>,
36 item: QueryItem<Self::Item>,
37 ) -> (Transform, Vec2);
38}
39
40#[derive(Component, Copy, Clone, Default)]
43pub struct UiUnrounded;
44
45#[derive(Component, Copy, Clone, Default)]
46pub(crate) struct UiRootTrns {
47 pub transform: Transform,
48 pub size: Vec2,
49}
50
51pub(crate) fn compute_root_transform<T: UiRoot>(
52 mut param: StaticSystemParam<T::Param>,
53 mut query: Query<(&mut T, &mut UiRootTrns, T::Item)>,
54) {
55 for (mut root, mut output, item) in &mut query {
56 let (transform, size) = root.compute_root_transform(&mut param, item);
57
58 output.bypass_change_detection().transform = transform;
59 output.map_unchanged(|trns| &mut trns.size).set_if_neq(size);
60 }
61}
62
63#[derive(Component, Copy, Clone)]
66#[require(Camera2d)]
67pub struct Camera2dRoot {
68 pub scale: f32,
71 pub offset: f32,
73}
74
75impl Default for Camera2dRoot {
76 #[inline]
77 fn default() -> Self {
78 Self {
79 scale: 1.,
80 offset: -100.,
81 }
82 }
83}
84
85impl UiRoot for Camera2dRoot {
86 type Param = ();
87 type Item = (Read<Camera>, Read<OrthographicProjection>, Has<UiUnrounded>);
88
89 #[inline]
90 fn compute_root_transform(
91 &mut self,
92 _: &mut SystemParamItem<Self::Param>,
93 (camera, projection, is_unrounded): QueryItem<Self::Item>,
94 ) -> (Transform, Vec2) {
95 let area = projection.area;
96 let size = (camera.physical_viewport_size().unwrap_or_default().as_vec2() / self.scale)
97 .map(|value| if !is_unrounded { value.round() } else { value });
98
99 (
100 Transform {
101 translation: area.min.extend(self.offset),
104 rotation: Quat::IDENTITY,
105 scale: (area.size() / size).extend(1.),
108 },
109 size,
110 )
111 }
112}