1use std::{
4 collections::{HashMap, HashSet},
5 fmt, mem,
6};
7
8use zng_app::{
9 AppExtension,
10 event::{event, event_args},
11 update::{EventUpdate, UPDATES},
12 view_process::{
13 VIEW_PROCESS_INITED_EVENT,
14 raw_device_events::DeviceId,
15 raw_events::{
16 RAW_FRAME_RENDERED_EVENT, RAW_MOUSE_INPUT_EVENT, RAW_MOUSE_MOVED_EVENT, RAW_TOUCH_EVENT, RAW_WINDOW_CLOSE_EVENT,
17 RAW_WINDOW_FOCUS_EVENT,
18 },
19 },
20 widget::{
21 WIDGET, WidgetId,
22 info::{InteractionPath, WIDGET_INFO_CHANGED_EVENT, WidgetInfoTree, WidgetPath},
23 },
24 window::{WINDOW, WindowId},
25};
26use zng_app_context::app_local;
27use zng_ext_window::{NestedWindowWidgetInfoExt, WINDOWS};
28use zng_layout::unit::{DipPoint, DipToPx};
29use zng_var::{ArcVar, ReadOnlyArcVar, Var, impl_from_and_into_var, var};
30use zng_view_api::{
31 mouse::{ButtonState, MouseButton},
32 touch::{TouchId, TouchPhase},
33};
34
35#[derive(Default)]
49pub struct PointerCaptureManager {
50 mouse_position: HashMap<(WindowId, DeviceId), DipPoint>,
51 mouse_down: HashSet<(WindowId, DeviceId, MouseButton)>,
52 touch_down: HashSet<(WindowId, DeviceId, TouchId)>,
53 capture: Option<CaptureInfo>,
54}
55impl AppExtension for PointerCaptureManager {
56 fn event(&mut self, update: &mut EventUpdate) {
57 if let Some(args) = RAW_FRAME_RENDERED_EVENT.on(update) {
58 if let Some(c) = &self.capture {
59 if c.target.window_id() == args.window_id {
60 if let Ok(info) = WINDOWS.widget_tree(args.window_id) {
61 self.continue_capture(&info);
62 }
63 }
65 }
66 } else if let Some(args) = RAW_MOUSE_MOVED_EVENT.on(update) {
67 self.mouse_position.insert((args.window_id, args.device_id), args.position);
68 } else if let Some(args) = RAW_MOUSE_INPUT_EVENT.on(update) {
69 match args.state {
70 ButtonState::Pressed => {
71 if self.mouse_down.insert((args.window_id, args.device_id, args.button))
72 && self.mouse_down.len() == 1
73 && self.touch_down.is_empty()
74 {
75 self.on_first_down(
76 args.window_id,
77 self.mouse_position
78 .get(&(args.window_id, args.device_id))
79 .copied()
80 .unwrap_or_default(),
81 );
82 }
83 }
84 ButtonState::Released => {
85 if self.mouse_down.remove(&(args.window_id, args.device_id, args.button))
86 && self.mouse_down.is_empty()
87 && self.touch_down.is_empty()
88 {
89 self.on_last_up();
90 }
91 }
92 }
93 } else if let Some(args) = RAW_TOUCH_EVENT.on(update) {
94 for touch in &args.touches {
95 match touch.phase {
96 TouchPhase::Start => {
97 if self.touch_down.insert((args.window_id, args.device_id, touch.touch))
98 && self.touch_down.len() == 1
99 && self.mouse_down.is_empty()
100 {
101 self.on_first_down(args.window_id, touch.position);
102 }
103 }
104 TouchPhase::End | TouchPhase::Cancel => {
105 if self.touch_down.remove(&(args.window_id, args.device_id, touch.touch))
106 && self.touch_down.is_empty()
107 && self.mouse_down.is_empty()
108 {
109 self.on_last_up();
110 }
111 }
112 TouchPhase::Move => {}
113 }
114 }
115 } else if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
116 if let Some(c) = &self.capture {
117 if c.target.window_id() == args.window_id {
118 self.continue_capture(&args.tree);
119 }
120 }
121 } else if let Some(args) = RAW_WINDOW_CLOSE_EVENT.on(update) {
122 self.remove_window(args.window_id);
123 } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
124 let actual_prev = args.prev_focus.map(|id| WINDOWS.nest_parent(id).map(|(p, _)| p).unwrap_or(id));
125 let actual_new = args.new_focus.map(|id| WINDOWS.nest_parent(id).map(|(p, _)| p).unwrap_or(id));
126
127 if actual_prev == actual_new {
128 return;
130 }
131
132 if let Some(w) = actual_prev {
133 self.remove_window(w);
134 }
135 } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update) {
136 if args.is_respawn && (!self.mouse_down.is_empty() || !self.touch_down.is_empty()) {
137 self.mouse_down.clear();
138 self.touch_down.clear();
139 self.on_last_up();
140 }
141 }
142 }
143
144 fn update(&mut self) {
145 if let Some(current) = &self.capture {
146 let mut cap = POINTER_CAPTURE_SV.write();
147 if let Some((widget_id, mode)) = cap.capture_request.take() {
148 let is_win_focused = match WINDOWS.is_focused(current.target.window_id()) {
149 Ok(mut f) => {
150 if !f {
151 if let Some(parent) = WINDOWS.nest_parent(current.target.window_id()).map(|(p, _)| p) {
153 f = WINDOWS.is_focused(parent) == Ok(true);
154 }
155 }
156 f
157 }
158 Err(_) => false,
159 };
160 if is_win_focused {
161 if let Some(widget) = WINDOWS.widget_tree(current.target.window_id()).unwrap().get(widget_id) {
163 self.set_capture(&mut cap, widget.interaction_path(), mode);
165 }
166 }
167 } else if mem::take(&mut cap.release_requested) && current.mode != CaptureMode::Window {
168 let target = current.target.root_path();
170 self.set_capture(&mut cap, InteractionPath::from_enabled(target.into_owned()), CaptureMode::Window);
171 }
172 }
173 }
174}
175impl PointerCaptureManager {
176 fn remove_window(&mut self, window_id: WindowId) {
177 self.mouse_position.retain(|(w, _), _| *w != window_id);
178
179 if !self.mouse_down.is_empty() || !self.touch_down.is_empty() {
180 self.mouse_down.retain(|(w, _, _)| *w != window_id);
181 self.touch_down.retain(|(w, _, _)| *w != window_id);
182
183 if self.mouse_down.is_empty() && self.touch_down.is_empty() {
184 self.on_last_up();
185 }
186 }
187 }
188
189 fn on_first_down(&mut self, window_id: WindowId, point: DipPoint) {
190 if let Ok(mut info) = WINDOWS.widget_tree(window_id) {
191 let mut cap = POINTER_CAPTURE_SV.write();
192 cap.release_requested = false;
193
194 let mut point = point.to_px(info.scale_factor());
195
196 if let Some(t) = info.root().hit_test(point).target() {
198 if let Some(w) = info.get(t.widget_id) {
199 if let Some(t) = w.nested_window_tree() {
200 info = t;
201 point = w
202 .inner_transform()
203 .inverse()
204 .and_then(|t| t.transform_point(point))
205 .unwrap_or(point);
206 }
207 }
208 }
209
210 if let Some((widget_id, mode)) = cap.capture_request.take() {
211 if let Some(w_info) = info.get(widget_id) {
212 if w_info.hit_test(point).contains(widget_id) {
213 self.set_capture(&mut cap, w_info.interaction_path(), mode);
215 return;
216 }
217 }
218 }
219
220 self.set_capture(&mut cap, info.root().interaction_path(), CaptureMode::Window);
222 }
223 }
224
225 fn on_last_up(&mut self) {
226 let mut cap = POINTER_CAPTURE_SV.write();
227 cap.release_requested = false;
228 cap.capture_request = None;
229 self.unset_capture(&mut cap);
230 }
231
232 fn continue_capture(&mut self, info: &WidgetInfoTree) {
233 let current = self.capture.as_ref().unwrap();
234
235 if let Some(widget) = info.get(current.target.widget_id()) {
236 if let Some(new_path) = widget.new_interaction_path(&InteractionPath::from_enabled(current.target.clone())) {
237 let mode = current.mode;
239 self.set_capture(&mut POINTER_CAPTURE_SV.write(), new_path, mode);
240 }
241 } else {
242 self.set_capture(&mut POINTER_CAPTURE_SV.write(), info.root().interaction_path(), CaptureMode::Window);
244 }
245 }
246
247 fn set_capture(&mut self, cap: &mut PointerCaptureService, target: InteractionPath, mode: CaptureMode) {
248 let new = target.enabled().map(|target| CaptureInfo { target, mode });
249 if new.is_none() {
250 self.unset_capture(cap);
251 return;
252 }
253 if new != self.capture {
254 let prev = self.capture.take();
255 self.capture.clone_from(&new);
256 cap.capture_value.clone_from(&new);
257 cap.capture.set(new.clone());
258 POINTER_CAPTURE_EVENT.notify(PointerCaptureArgs::now(prev, new));
259 }
260 }
261
262 fn unset_capture(&mut self, cap: &mut PointerCaptureService) {
263 if self.capture.is_some() {
264 let prev = self.capture.take();
265 cap.capture_value = None;
266 cap.capture.set(None);
267 POINTER_CAPTURE_EVENT.notify(PointerCaptureArgs::now(prev, None));
268 }
269 }
270}
271
272#[expect(non_camel_case_types)]
288pub struct POINTER_CAPTURE;
289impl POINTER_CAPTURE {
290 pub fn current_capture(&self) -> ReadOnlyArcVar<Option<CaptureInfo>> {
292 POINTER_CAPTURE_SV.read().capture.read_only()
293 }
294
295 pub fn capture_widget(&self, widget_id: WidgetId) {
299 let mut m = POINTER_CAPTURE_SV.write();
300 m.capture_request = Some((widget_id, CaptureMode::Widget));
301 UPDATES.update(None);
302 }
303
304 pub fn capture_subtree(&self, widget_id: WidgetId) {
311 let mut m = POINTER_CAPTURE_SV.write();
312 m.capture_request = Some((widget_id, CaptureMode::Subtree));
313 UPDATES.update(None);
314 }
315
316 pub fn release_capture(&self) {
321 let mut m = POINTER_CAPTURE_SV.write();
322 m.release_requested = true;
323 UPDATES.update(None);
324 }
325
326 pub(crate) fn current_capture_value(&self) -> Option<CaptureInfo> {
328 POINTER_CAPTURE_SV.read().capture_value.clone()
329 }
330}
331
332#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
334pub enum CaptureMode {
335 Window,
339 Subtree,
342
343 Widget,
345}
346impl fmt::Debug for CaptureMode {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 if f.alternate() {
349 write!(f, "CaptureMode::")?;
350 }
351 match self {
352 CaptureMode::Window => write!(f, "Window"),
353 CaptureMode::Subtree => write!(f, "Subtree"),
354 CaptureMode::Widget => write!(f, "Widget"),
355 }
356 }
357}
358impl Default for CaptureMode {
359 fn default() -> Self {
361 CaptureMode::Window
362 }
363}
364impl_from_and_into_var! {
365 fn from(widget: bool) -> CaptureMode {
367 if widget {
368 CaptureMode::Widget
369 } else {
370 CaptureMode::Window
371 }
372 }
373}
374
375#[derive(Debug, Clone, PartialEq, Eq)]
377pub struct CaptureInfo {
378 pub target: WidgetPath,
384 pub mode: CaptureMode,
386}
387impl CaptureInfo {
388 pub fn allows(&self) -> bool {
401 match self.mode {
402 CaptureMode::Window => self.target.window_id() == WINDOW.id(),
403 CaptureMode::Widget => self.target.widget_id() == WIDGET.id(),
404 CaptureMode::Subtree => {
405 let tree = WINDOW.info();
406 if let Some(wgt) = tree.get(WIDGET.id()) {
407 for wgt in wgt.self_and_ancestors() {
408 if wgt.id() == self.target.widget_id() {
409 return true;
410 }
411 }
412 }
413 false
414 }
415 }
416 }
417}
418
419app_local! {
420 static POINTER_CAPTURE_SV: PointerCaptureService = PointerCaptureService {
421 capture_value: None,
422 capture: var(None),
423 capture_request: None,
424 release_requested: false,
425 };
426}
427
428struct PointerCaptureService {
429 capture_value: Option<CaptureInfo>,
430 capture: ArcVar<Option<CaptureInfo>>,
431 capture_request: Option<(WidgetId, CaptureMode)>,
432 release_requested: bool,
433}
434
435event! {
436 pub static POINTER_CAPTURE_EVENT: PointerCaptureArgs;
438}
439
440event_args! {
441 pub struct PointerCaptureArgs {
443 pub prev_capture: Option<CaptureInfo>,
445 pub new_capture: Option<CaptureInfo>,
447
448 ..
449
450 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
455 if let Some(p) = &self.prev_capture {
456 list.insert_wgt(&p.target);
457 }
458 if let Some(p) = &self.new_capture {
459 list.insert_wgt(&p.target);
460 }
461 }
462 }
463}
464
465impl PointerCaptureArgs {
466 pub fn is_widget_move(&self) -> bool {
468 match (&self.prev_capture, &self.new_capture) {
469 (Some(prev), Some(new)) => prev.target.widget_id() == new.target.widget_id() && prev.target != new.target,
470 _ => false,
471 }
472 }
473
474 pub fn is_mode_change(&self) -> bool {
476 match (&self.prev_capture, &self.new_capture) {
477 (Some(prev), Some(new)) => prev.target.widget_id() == new.target.widget_id() && prev.mode != new.mode,
478 _ => false,
479 }
480 }
481
482 pub fn is_lost(&self, widget_id: WidgetId) -> bool {
484 match (&self.prev_capture, &self.new_capture) {
485 (None, _) => false,
486 (Some(p), None) => p.target.widget_id() == widget_id,
487 (Some(prev), Some(new)) => prev.target.widget_id() == widget_id && new.target.widget_id() != widget_id,
488 }
489 }
490
491 pub fn is_got(&self, widget_id: WidgetId) -> bool {
493 match (&self.prev_capture, &self.new_capture) {
494 (_, None) => false,
495 (None, Some(p)) => p.target.widget_id() == widget_id,
496 (Some(prev), Some(new)) => prev.target.widget_id() != widget_id && new.target.widget_id() == widget_id,
497 }
498 }
499}