bevy_trackball/controller/
viewport.rs1use bevy::{
2 camera::RenderTarget,
3 input::{mouse::MouseWheel, touch::TouchPhase},
4 prelude::*,
5 window::{CursorOptions, PrimaryWindow, WindowRef},
6};
7
8use super::{TrackballCamera, TrackballController};
9
10#[derive(Resource, Clone, Debug, PartialEq, Eq, Default)]
15pub struct TrackballViewport {
16 entity: Option<Entity>,
17 stolen: usize,
18}
19
20impl TrackballViewport {
21 #[allow(clippy::needless_pass_by_value, clippy::missing_const_for_fn)]
28 #[must_use]
29 pub fn stolen(viewport: Res<Self>) -> bool {
30 viewport.stolen != 0
31 }
32 #[must_use]
38 pub const fn was_stolen(&self) -> bool {
39 self.entity.is_none()
40 }
41 #[allow(clippy::needless_pass_by_value)]
80 pub const fn set_stolen(&mut self, stolen: Option<usize>) {
81 if let Some(frames) = stolen {
82 self.entity = None;
83 self.stolen = frames;
84 } else if self.stolen != 0 {
85 self.stolen -= 1;
86 }
87 }
88 #[allow(clippy::too_many_arguments)]
89 #[allow(clippy::type_complexity)]
90 pub(super) fn select<'a>(
91 viewport: &mut ResMut<Self>,
92 key_input: &Res<ButtonInput<KeyCode>>,
93 mouse_input: &Res<ButtonInput<MouseButton>>,
94 touch_events: &mut MessageReader<TouchInput>,
95 wheel_events: &MessageReader<MouseWheel>,
96 primary_windows: &'a mut Query<
97 (Entity, &mut Window, &mut CursorOptions),
98 With<PrimaryWindow>,
99 >,
100 secondary_windows: &'a mut Query<(&mut Window, &mut CursorOptions), Without<PrimaryWindow>>,
101 cameras: &'a mut Query<(
102 Entity,
103 &Camera,
104 &RenderTarget,
105 &TrackballCamera,
106 &mut TrackballController,
107 )>,
108 ) -> Option<(
109 bool,
110 Entity,
111 Mut<'a, Window>,
112 Mut<'a, CursorOptions>,
113 Entity,
114 &'a Camera,
115 &'a TrackballCamera,
116 Mut<'a, TrackballController>,
117 )> {
118 let touch = touch_events
119 .read()
120 .filter_map(|touch| (touch.phase == TouchPhase::Started).then_some(touch.position))
121 .last();
122 let input = !wheel_events.is_empty()
123 || key_input.get_just_pressed().len() != 0
124 || mouse_input.get_just_pressed().len() != 0;
125 let mut new_viewport = viewport.clone();
126 let mut max_order = 0;
127 for (group, camera, target, _trackball, _controller) in cameras.iter() {
128 let RenderTarget::Window(window_ref) = target else {
129 continue;
130 };
131 let window = match window_ref {
132 WindowRef::Primary => primary_windows
133 .single()
134 .ok()
135 .map(|(_id, window, _cursor_options)| window),
136 WindowRef::Entity(id) => secondary_windows
137 .get(*id)
138 .ok()
139 .map(|(window, _cursor_options)| window),
140 };
141 let Some(window) = window else {
142 continue;
143 };
144 let Some(pos) = touch
145 .filter(|_pos| window.focused)
146 .or_else(|| window.cursor_position().filter(|_pos| input))
147 else {
148 continue;
149 };
150 let Some(Rect { min, max }) = camera.logical_viewport_rect() else {
151 continue;
152 };
153 let contained = (min.x..max.x).contains(&pos.x) && (min.y..max.y).contains(&pos.y);
154 if contained && camera.order >= max_order {
155 new_viewport.entity = Some(group);
156 max_order = camera.order;
157 }
158 }
159 let is_changed = viewport.entity != new_viewport.entity;
160 if is_changed {
161 viewport.entity = new_viewport.entity;
162 }
163 let camera = viewport
164 .entity
165 .and_then(|entity| cameras.get_mut(entity).ok());
166 let Some((group, camera, target, trackball, controller)) = camera else {
167 viewport.entity = None;
168 return None;
169 };
170 let RenderTarget::Window(window_ref) = target else {
171 return None;
172 };
173 let (window_id, window, cursor_options) = match window_ref {
174 WindowRef::Primary => primary_windows.single_mut().ok(),
175 WindowRef::Entity(id) => secondary_windows
176 .get_mut(*id)
177 .ok()
178 .map(|(window, cursor_options)| (*id, window, cursor_options)),
179 }?;
180 Some((
181 is_changed,
182 window_id,
183 window,
184 cursor_options,
185 group,
186 camera,
187 trackball,
188 controller,
189 ))
190 }
191}