1use crate::{AppWindowId, PointerId, RenderTargetId, ViewportFit, ViewportMapping};
2
3use super::{Modifiers, MouseButton, MouseButtons, PointerCancelReason, PointerType};
4use crate::geometry::{Point, Rect};
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct ViewportInputGeometry {
8 pub content_rect_px: Rect,
10 pub draw_rect_px: Rect,
12 pub target_px_size: (u32, u32),
14 pub fit: ViewportFit,
15 pub pixels_per_point: f32,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq)]
20pub struct ViewportInputEvent {
21 pub window: AppWindowId,
22 pub target: RenderTargetId,
23 pub pointer_id: PointerId,
24 pub pointer_type: PointerType,
25 pub geometry: ViewportInputGeometry,
26 pub cursor_px: Point,
28 pub uv: (f32, f32),
29 pub target_px: (u32, u32),
30 pub kind: ViewportInputKind,
31}
32
33impl ViewportInputEvent {
34 pub fn target_px_per_screen_px(&self) -> Option<f32> {
43 let (tw, th) = self.geometry.target_px_size;
44 let tw = tw.max(1) as f32;
45 let th = th.max(1) as f32;
46
47 let rect = self.geometry.draw_rect_px;
48 let dw = rect.size.width.0.max(0.0);
49 let dh = rect.size.height.0.max(0.0);
50 if dw <= 0.0 || dh <= 0.0 || !dw.is_finite() || !dh.is_finite() {
51 return None;
52 }
53
54 let sx = tw / dw;
55 let sy = th / dh;
56 let s = sx.min(sy);
57 (s.is_finite() && s > 0.0).then_some(s)
58 }
59
60 pub fn cursor_target_px_f32(&self) -> Option<(f32, f32)> {
71 let (tw, th) = self.geometry.target_px_size;
72 let tw = tw.max(1) as f32;
73 let th = th.max(1) as f32;
74
75 let rect = self.geometry.draw_rect_px;
76 let dw = rect.size.width.0.max(0.0);
77 let dh = rect.size.height.0.max(0.0);
78 if dw <= 0.0 || dh <= 0.0 || !dw.is_finite() || !dh.is_finite() {
79 return None;
80 }
81
82 let uv_x = (self.cursor_px.x.0 - rect.origin.x.0) / dw;
83 let uv_y = (self.cursor_px.y.0 - rect.origin.y.0) / dh;
84 Some((uv_x * tw, uv_y * th))
85 }
86
87 pub fn cursor_target_px_f32_clamped(&self) -> (f32, f32) {
90 let (tw, th) = self.geometry.target_px_size;
91 let tw = tw.max(1) as f32;
92 let th = th.max(1) as f32;
93
94 let Some((x, y)) = self.cursor_target_px_f32() else {
95 return (self.target_px.0 as f32, self.target_px.1 as f32);
96 };
97 (x.clamp(0.0, tw), y.clamp(0.0, th))
98 }
99
100 #[allow(clippy::too_many_arguments)]
101 pub fn from_mapping_window_point(
102 window: AppWindowId,
103 target: RenderTargetId,
104 mapping: &ViewportMapping,
105 pixels_per_point: f32,
106 pointer_id: PointerId,
107 pointer_type: PointerType,
108 position: Point,
109 kind: ViewportInputKind,
110 ) -> Option<Self> {
111 let mapped = mapping.map();
112 let uv = mapping.window_point_to_uv(position)?;
113 let target_px = mapping.window_point_to_target_px(position)?;
114 Some(Self {
115 window,
116 target,
117 pointer_id,
118 pointer_type,
119 geometry: ViewportInputGeometry {
120 content_rect_px: mapping.content_rect,
121 draw_rect_px: mapped.draw_rect,
122 target_px_size: mapping.target_px_size,
123 fit: mapping.fit,
124 pixels_per_point,
125 },
126 cursor_px: position,
127 uv,
128 target_px,
129 kind,
130 })
131 }
132
133 #[allow(clippy::too_many_arguments)]
134 pub fn from_mapping_window_point_clamped(
135 window: AppWindowId,
136 target: RenderTargetId,
137 mapping: &ViewportMapping,
138 pixels_per_point: f32,
139 pointer_id: PointerId,
140 pointer_type: PointerType,
141 position: Point,
142 kind: ViewportInputKind,
143 ) -> Self {
144 let mapped = mapping.map();
145 let uv = mapping.window_point_to_uv_clamped(position);
146 let target_px = mapping.window_point_to_target_px_clamped(position);
147 Self {
148 window,
149 target,
150 pointer_id,
151 pointer_type,
152 geometry: ViewportInputGeometry {
153 content_rect_px: mapping.content_rect,
154 draw_rect_px: mapped.draw_rect,
155 target_px_size: mapping.target_px_size,
156 fit: mapping.fit,
157 pixels_per_point,
158 },
159 cursor_px: position,
160 uv,
161 target_px,
162 kind,
163 }
164 }
165
166 #[allow(clippy::too_many_arguments)]
167 pub fn from_mapping_window_point_maybe_clamped(
168 window: AppWindowId,
169 target: RenderTargetId,
170 mapping: &ViewportMapping,
171 pixels_per_point: f32,
172 pointer_id: PointerId,
173 pointer_type: PointerType,
174 position: Point,
175 kind: ViewportInputKind,
176 clamped: bool,
177 ) -> Option<Self> {
178 if clamped {
179 Some(Self::from_mapping_window_point_clamped(
180 window,
181 target,
182 mapping,
183 pixels_per_point,
184 pointer_id,
185 pointer_type,
186 position,
187 kind,
188 ))
189 } else {
190 Self::from_mapping_window_point(
191 window,
192 target,
193 mapping,
194 pixels_per_point,
195 pointer_id,
196 pointer_type,
197 position,
198 kind,
199 )
200 }
201 }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq)]
205pub enum ViewportInputKind {
206 PointerMove {
207 buttons: MouseButtons,
208 modifiers: Modifiers,
209 },
210 PointerDown {
211 button: MouseButton,
212 modifiers: Modifiers,
213 click_count: u8,
215 },
216 PointerUp {
217 button: MouseButton,
218 modifiers: Modifiers,
219 is_click: bool,
223 click_count: u8,
225 },
226 PointerCancel {
227 buttons: MouseButtons,
228 modifiers: Modifiers,
229 reason: PointerCancelReason,
230 },
231 Wheel {
232 delta: Point,
233 modifiers: Modifiers,
234 },
235}