1use std::{
4 collections::{HashMap, HashSet},
5 fmt, mem,
6};
7
8use zng_app::{
9 APP, AppExtension,
10 event::{event, event_args},
11 update::{EventUpdate, UPDATES},
12 view_process::{
13 VIEW_PROCESS_INITED_EVENT,
14 raw_device_events::InputDeviceId,
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::{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, InputDeviceId), DipPoint>,
51 mouse_down: HashSet<(WindowId, InputDeviceId, MouseButton)>,
52 touch_down: HashSet<(WindowId, InputDeviceId, 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 && c.target.window_id() == args.window_id
60 && let Ok(info) = WINDOWS.widget_tree(args.window_id)
61 {
62 self.continue_capture(&info);
63 }
64 } else if let Some(args) = RAW_MOUSE_MOVED_EVENT.on(update) {
66 self.mouse_position.insert((args.window_id, args.device_id), args.position);
67 } else if let Some(args) = RAW_MOUSE_INPUT_EVENT.on(update) {
68 match args.state {
69 ButtonState::Pressed => {
70 if self.mouse_down.insert((args.window_id, args.device_id, args.button))
71 && self.mouse_down.len() == 1
72 && self.touch_down.is_empty()
73 {
74 self.on_first_down(
75 args.window_id,
76 self.mouse_position
77 .get(&(args.window_id, args.device_id))
78 .copied()
79 .unwrap_or_default(),
80 );
81 }
82 }
83 ButtonState::Released => {
84 if self.mouse_down.remove(&(args.window_id, args.device_id, args.button))
85 && self.mouse_down.is_empty()
86 && self.touch_down.is_empty()
87 {
88 self.on_last_up();
89 }
90 }
91 }
92 } else if let Some(args) = RAW_TOUCH_EVENT.on(update) {
93 for touch in &args.touches {
94 match touch.phase {
95 TouchPhase::Start => {
96 if self.touch_down.insert((args.window_id, args.device_id, touch.touch))
97 && self.touch_down.len() == 1
98 && self.mouse_down.is_empty()
99 {
100 self.on_first_down(args.window_id, touch.position);
101 }
102 }
103 TouchPhase::End | TouchPhase::Cancel => {
104 if self.touch_down.remove(&(args.window_id, args.device_id, touch.touch))
105 && self.touch_down.is_empty()
106 && self.mouse_down.is_empty()
107 {
108 self.on_last_up();
109 }
110 }
111 TouchPhase::Move => {}
112 }
113 }
114 } else if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
115 if let Some(c) = &self.capture
116 && c.target.window_id() == args.window_id
117 {
118 self.continue_capture(&args.tree);
119 }
120 } else if let Some(args) = RAW_WINDOW_CLOSE_EVENT.on(update) {
121 self.remove_window(args.window_id);
122 } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
123 let actual_prev = args.prev_focus.map(|id| WINDOWS.nest_parent(id).map(|(p, _)| p).unwrap_or(id));
124 let actual_new = args.new_focus.map(|id| WINDOWS.nest_parent(id).map(|(p, _)| p).unwrap_or(id));
125
126 if actual_prev == actual_new {
127 return;
129 }
130
131 if let Some(w) = actual_prev {
132 self.remove_window(w);
133 }
134 } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
135 && args.is_respawn
136 && (!self.mouse_down.is_empty() || !self.touch_down.is_empty())
137 {
138 self.mouse_down.clear();
139 self.touch_down.clear();
140 self.on_last_up();
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 && let Some(w) = info.get(t.widget_id)
199 && let Some(t) = w.nested_window_tree()
200 {
201 info = t;
202 point = w
203 .inner_transform()
204 .inverse()
205 .and_then(|t| t.transform_point(point))
206 .unwrap_or(point);
207 }
208
209 if let Some((widget_id, mode)) = cap.capture_request.take()
210 && let Some(w_info) = info.get(widget_id)
211 && w_info.hit_test(point).contains(widget_id)
212 {
213 self.set_capture(&mut cap, w_info.interaction_path(), mode);
215 return;
216 }
217
218 self.set_capture(&mut cap, info.root().interaction_path(), CaptureMode::Window);
220 }
221 }
222
223 fn on_last_up(&mut self) {
224 let mut cap = POINTER_CAPTURE_SV.write();
225 cap.release_requested = false;
226 cap.capture_request = None;
227 self.unset_capture(&mut cap);
228 }
229
230 fn continue_capture(&mut self, info: &WidgetInfoTree) {
231 let current = self.capture.as_ref().unwrap();
232
233 if let Some(widget) = info.get(current.target.widget_id()) {
234 if let Some(new_path) = widget.new_interaction_path(&InteractionPath::from_enabled(current.target.clone())) {
235 let mode = current.mode;
237 self.set_capture(&mut POINTER_CAPTURE_SV.write(), new_path, mode);
238 }
239 } else {
240 self.set_capture(&mut POINTER_CAPTURE_SV.write(), info.root().interaction_path(), CaptureMode::Window);
242 }
243 }
244
245 fn set_capture(&mut self, cap: &mut PointerCaptureService, target: InteractionPath, mode: CaptureMode) {
246 let new = target.enabled().map(|target| CaptureInfo { target, mode });
247 if new.is_none() {
248 self.unset_capture(cap);
249 return;
250 }
251 if new != self.capture {
252 let prev = self.capture.take();
253 self.capture.clone_from(&new);
254 cap.capture_value.clone_from(&new);
255 cap.capture.set(new.clone());
256 POINTER_CAPTURE_EVENT.notify(PointerCaptureArgs::now(prev, new));
257 }
258 }
259
260 fn unset_capture(&mut self, cap: &mut PointerCaptureService) {
261 if self.capture.is_some() {
262 let prev = self.capture.take();
263 cap.capture_value = None;
264 cap.capture.set(None);
265 POINTER_CAPTURE_EVENT.notify(PointerCaptureArgs::now(prev, None));
266 }
267 }
268}
269
270#[expect(non_camel_case_types)]
286pub struct POINTER_CAPTURE;
287impl POINTER_CAPTURE {
288 pub fn current_capture(&self) -> Var<Option<CaptureInfo>> {
290 POINTER_CAPTURE_SV.read().capture.read_only()
291 }
292
293 pub fn capture_widget(&self, widget_id: WidgetId) {
297 let mut m = POINTER_CAPTURE_SV.write();
298 m.capture_request = Some((widget_id, CaptureMode::Widget));
299 UPDATES.update(None);
300 }
301
302 pub fn capture_subtree(&self, widget_id: WidgetId) {
309 let mut m = POINTER_CAPTURE_SV.write();
310 m.capture_request = Some((widget_id, CaptureMode::Subtree));
311 UPDATES.update(None);
312 }
313
314 pub fn release_capture(&self) {
319 let mut m = POINTER_CAPTURE_SV.write();
320 m.release_requested = true;
321 UPDATES.update(None);
322 }
323
324 pub(crate) fn current_capture_value(&self) -> Option<CaptureInfo> {
326 POINTER_CAPTURE_SV.read().capture_value.clone()
327 }
328}
329
330#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
332pub enum CaptureMode {
333 Window,
337 Subtree,
340
341 Widget,
343}
344impl fmt::Debug for CaptureMode {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 if f.alternate() {
347 write!(f, "CaptureMode::")?;
348 }
349 match self {
350 CaptureMode::Window => write!(f, "Window"),
351 CaptureMode::Subtree => write!(f, "Subtree"),
352 CaptureMode::Widget => write!(f, "Widget"),
353 }
354 }
355}
356impl Default for CaptureMode {
357 fn default() -> Self {
359 CaptureMode::Window
360 }
361}
362impl_from_and_into_var! {
363 fn from(widget: bool) -> CaptureMode {
365 if widget { CaptureMode::Widget } else { CaptureMode::Window }
366 }
367}
368
369#[derive(Debug, Clone, PartialEq, Eq)]
371pub struct CaptureInfo {
372 pub target: WidgetPath,
378 pub mode: CaptureMode,
380}
381impl CaptureInfo {
382 pub fn allows(&self) -> bool {
395 match self.mode {
396 CaptureMode::Window => self.target.window_id() == WINDOW.id(),
397 CaptureMode::Widget => self.target.widget_id() == WIDGET.id(),
398 CaptureMode::Subtree => {
399 let tree = WINDOW.info();
400 if let Some(wgt) = tree.get(WIDGET.id()) {
401 for wgt in wgt.self_and_ancestors() {
402 if wgt.id() == self.target.widget_id() {
403 return true;
404 }
405 }
406 }
407 false
408 }
409 }
410 }
411}
412
413app_local! {
414 static POINTER_CAPTURE_SV: PointerCaptureService = {
415 APP.extensions().require::<PointerCaptureManager>();
416 PointerCaptureService {
417 capture_value: None,
418 capture: var(None),
419 capture_request: None,
420 release_requested: false,
421 }
422 };
423}
424
425struct PointerCaptureService {
426 capture_value: Option<CaptureInfo>,
427 capture: Var<Option<CaptureInfo>>,
428 capture_request: Option<(WidgetId, CaptureMode)>,
429 release_requested: bool,
430}
431
432event! {
433 pub static POINTER_CAPTURE_EVENT: PointerCaptureArgs;
435}
436
437event_args! {
438 pub struct PointerCaptureArgs {
440 pub prev_capture: Option<CaptureInfo>,
442 pub new_capture: Option<CaptureInfo>,
444
445 ..
446
447 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
452 if let Some(p) = &self.prev_capture {
453 list.insert_wgt(&p.target);
454 }
455 if let Some(p) = &self.new_capture {
456 list.insert_wgt(&p.target);
457 }
458 }
459 }
460}
461
462impl PointerCaptureArgs {
463 pub fn is_widget_move(&self) -> bool {
465 match (&self.prev_capture, &self.new_capture) {
466 (Some(prev), Some(new)) => prev.target.widget_id() == new.target.widget_id() && prev.target != new.target,
467 _ => false,
468 }
469 }
470
471 pub fn is_mode_change(&self) -> bool {
473 match (&self.prev_capture, &self.new_capture) {
474 (Some(prev), Some(new)) => prev.target.widget_id() == new.target.widget_id() && prev.mode != new.mode,
475 _ => false,
476 }
477 }
478
479 pub fn is_lost(&self, widget_id: WidgetId) -> bool {
481 match (&self.prev_capture, &self.new_capture) {
482 (None, _) => false,
483 (Some(p), None) => p.target.widget_id() == widget_id,
484 (Some(prev), Some(new)) => prev.target.widget_id() == widget_id && new.target.widget_id() != widget_id,
485 }
486 }
487
488 pub fn is_got(&self, widget_id: WidgetId) -> bool {
490 match (&self.prev_capture, &self.new_capture) {
491 (_, None) => false,
492 (None, Some(p)) => p.target.widget_id() == widget_id,
493 (Some(prev), Some(new)) => prev.target.widget_id() != widget_id && new.target.widget_id() == widget_id,
494 }
495 }
496}