1use super::{
5 EventResult, FocusReasonArg, Item, ItemConsts, ItemRc, ItemRendererRef, KeyEventArg,
6 MouseCursor, PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind,
7 PointerScrollEvent, PointerScrollEventArg, RenderingResult, VoidArg,
8};
9use crate::api::LogicalPosition;
10use crate::input::{
11 FocusEvent, FocusEventResult, FocusReason, InputEventFilterResult, InputEventResult, KeyEvent,
12 KeyEventResult, KeyEventType, MouseEvent,
13};
14use crate::item_rendering::CachedRenderingData;
15use crate::layout::{LayoutInfo, Orientation};
16use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PointLengths};
17#[cfg(feature = "rtti")]
18use crate::rtti::*;
19use crate::window::{WindowAdapter, WindowInner};
20use crate::{Callback, Coord, Property};
21use alloc::rc::Rc;
22use const_field_offset::FieldOffsets;
23use core::cell::Cell;
24use core::pin::Pin;
25use i_slint_core_macros::*;
26
27#[repr(C)]
29#[derive(FieldOffsets, SlintElement, Default)]
30#[pin]
31pub struct TouchArea {
32 pub enabled: Property<bool>,
33 pub pressed: Property<bool>,
35 pub has_hover: Property<bool>,
36 pub pressed_x: Property<LogicalLength>,
40 pub pressed_y: Property<LogicalLength>,
41 pub mouse_x: Property<LogicalLength>,
43 pub mouse_y: Property<LogicalLength>,
44 pub mouse_cursor: Property<MouseCursor>,
45 pub clicked: Callback<VoidArg>,
46 pub double_clicked: Callback<VoidArg>,
47 pub moved: Callback<VoidArg>,
48 pub pointer_event: Callback<PointerEventArg>,
49 pub scroll_event: Callback<PointerScrollEventArg, EventResult>,
50 pub cached_rendering_data: CachedRenderingData,
52 grabbed: Cell<bool>,
54}
55
56impl Item for TouchArea {
57 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
58
59 fn layout_info(
60 self: Pin<&Self>,
61 _orientation: Orientation,
62 _window_adapter: &Rc<dyn WindowAdapter>,
63 _self_rc: &ItemRc,
64 ) -> LayoutInfo {
65 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
66 }
67
68 fn input_event_filter_before_children(
69 self: Pin<&Self>,
70 event: MouseEvent,
71 window_adapter: &Rc<dyn WindowAdapter>,
72 _self_rc: &ItemRc,
73 ) -> InputEventFilterResult {
74 if !self.enabled() {
75 self.has_hover.set(false);
76 if self.grabbed.replace(false) {
77 self.pressed.set(false);
78 Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
79 button: PointerEventButton::Other,
80 kind: PointerEventKind::Cancel,
81 modifiers: window_adapter.window().0.modifiers.get().into(),
82 },));
83 }
84 return InputEventFilterResult::ForwardAndIgnore;
85 }
86 if let Some(pos) = event.position() {
87 Self::FIELD_OFFSETS.mouse_x.apply_pin(self).set(pos.x_length());
88 Self::FIELD_OFFSETS.mouse_y.apply_pin(self).set(pos.y_length());
89 }
90 let hovering = !matches!(event, MouseEvent::Exit);
91 Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(hovering);
92 if hovering {
93 if let Some(x) = window_adapter.internal(crate::InternalToken) {
94 x.set_mouse_cursor(self.mouse_cursor());
95 }
96 }
97 InputEventFilterResult::ForwardAndInterceptGrab
98 }
99
100 fn input_event(
101 self: Pin<&Self>,
102 event: MouseEvent,
103 window_adapter: &Rc<dyn WindowAdapter>,
104 self_rc: &ItemRc,
105 ) -> InputEventResult {
106 if matches!(event, MouseEvent::Exit) {
107 Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
108 if let Some(x) = window_adapter.internal(crate::InternalToken) {
109 x.set_mouse_cursor(MouseCursor::Default);
110 }
111 }
112 if !self.enabled() {
113 return InputEventResult::EventIgnored;
114 }
115
116 match event {
117 MouseEvent::Pressed { position, button, .. } => {
118 self.grabbed.set(true);
119 if button == PointerEventButton::Left {
120 Self::FIELD_OFFSETS.pressed_x.apply_pin(self).set(position.x_length());
121 Self::FIELD_OFFSETS.pressed_y.apply_pin(self).set(position.y_length());
122 Self::FIELD_OFFSETS.pressed.apply_pin(self).set(true);
123 }
124 Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
125 button,
126 kind: PointerEventKind::Down,
127 modifiers: window_adapter.window().0.modifiers.get().into(),
128 },));
129
130 InputEventResult::GrabMouse
131 }
132 MouseEvent::Exit => {
133 Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
134 if self.grabbed.replace(false) {
135 Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
136 button: PointerEventButton::Other,
137 kind: PointerEventKind::Cancel,
138 modifiers: window_adapter.window().0.modifiers.get().into(),
139 },));
140 }
141
142 InputEventResult::EventAccepted
143 }
144
145 MouseEvent::Released { button, position, click_count } => {
146 let geometry = self_rc.geometry();
147 if button == PointerEventButton::Left
148 && LogicalRect::new(LogicalPoint::default(), geometry.size).contains(position)
149 && self.pressed()
150 {
151 Self::FIELD_OFFSETS.clicked.apply_pin(self).call(&());
152 if (click_count % 2) == 1 {
153 Self::FIELD_OFFSETS.double_clicked.apply_pin(self).call(&())
154 }
155 }
156
157 self.grabbed.set(false);
158 if button == PointerEventButton::Left {
159 Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
160 }
161 Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
162 button,
163 kind: PointerEventKind::Up,
164 modifiers: window_adapter.window().0.modifiers.get().into(),
165 },));
166
167 InputEventResult::EventAccepted
168 }
169 MouseEvent::Moved { .. } => {
170 Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
171 button: PointerEventButton::Other,
172 kind: PointerEventKind::Move,
173 modifiers: window_adapter.window().0.modifiers.get().into(),
174 },));
175 if self.grabbed.get() {
176 Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
177 InputEventResult::GrabMouse
178 } else {
179 InputEventResult::EventAccepted
180 }
181 }
182 MouseEvent::Wheel { delta_x, delta_y, .. } => {
183 let modifiers = window_adapter.window().0.modifiers.get().into();
184 let r = Self::FIELD_OFFSETS
185 .scroll_event
186 .apply_pin(self)
187 .call(&(PointerScrollEvent { delta_x, delta_y, modifiers },));
188 if self.grabbed.get() {
189 InputEventResult::GrabMouse
190 } else {
191 match r {
192 EventResult::Reject => {
193 Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
197 InputEventResult::EventIgnored
198 }
199 EventResult::Accept => InputEventResult::EventAccepted,
200 }
201 }
202 }
203 }
204 }
205
206 fn key_event(
207 self: Pin<&Self>,
208 _: &KeyEvent,
209 _window_adapter: &Rc<dyn WindowAdapter>,
210 _self_rc: &ItemRc,
211 ) -> KeyEventResult {
212 KeyEventResult::EventIgnored
213 }
214
215 fn focus_event(
216 self: Pin<&Self>,
217 _: &FocusEvent,
218 _window_adapter: &Rc<dyn WindowAdapter>,
219 _self_rc: &ItemRc,
220 ) -> FocusEventResult {
221 FocusEventResult::FocusIgnored
222 }
223
224 fn render(
225 self: Pin<&Self>,
226 _backend: &mut ItemRendererRef,
227 _self_rc: &ItemRc,
228 _size: LogicalSize,
229 ) -> RenderingResult {
230 RenderingResult::ContinueRenderingChildren
231 }
232
233 fn bounding_rect(
234 self: core::pin::Pin<&Self>,
235 _window_adapter: &Rc<dyn WindowAdapter>,
236 _self_rc: &ItemRc,
237 mut geometry: LogicalRect,
238 ) -> LogicalRect {
239 geometry.size = LogicalSize::zero();
240 geometry
241 }
242
243 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
244 false
245 }
246}
247
248impl ItemConsts for TouchArea {
249 const cached_rendering_data_offset: const_field_offset::FieldOffset<
250 TouchArea,
251 CachedRenderingData,
252 > = TouchArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
253}
254
255#[repr(C)]
257#[derive(FieldOffsets, Default, SlintElement)]
258#[pin]
259pub struct FocusScope {
260 pub enabled: Property<bool>,
261 pub has_focus: Property<bool>,
262 pub key_pressed: Callback<KeyEventArg, EventResult>,
263 pub key_released: Callback<KeyEventArg, EventResult>,
264 pub focus_changed_event: Callback<FocusReasonArg>,
265 pub focus_gained: Callback<FocusReasonArg>,
266 pub focus_lost: Callback<FocusReasonArg>,
267 pub cached_rendering_data: CachedRenderingData,
269}
270
271impl Item for FocusScope {
272 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
273
274 fn layout_info(
275 self: Pin<&Self>,
276 _orientation: Orientation,
277 _window_adapter: &Rc<dyn WindowAdapter>,
278 _self_rc: &ItemRc,
279 ) -> LayoutInfo {
280 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
281 }
282
283 fn input_event_filter_before_children(
284 self: Pin<&Self>,
285 _: MouseEvent,
286 _window_adapter: &Rc<dyn WindowAdapter>,
287 _self_rc: &ItemRc,
288 ) -> InputEventFilterResult {
289 InputEventFilterResult::ForwardEvent
290 }
291
292 fn input_event(
293 self: Pin<&Self>,
294 event: MouseEvent,
295 window_adapter: &Rc<dyn WindowAdapter>,
296 self_rc: &ItemRc,
297 ) -> InputEventResult {
298 if self.enabled() && matches!(event, MouseEvent::Pressed { .. }) && !self.has_focus() {
299 WindowInner::from_pub(window_adapter.window()).set_focus_item(
300 self_rc,
301 true,
302 FocusReason::PointerClick,
303 );
304 InputEventResult::EventAccepted
305 } else {
306 InputEventResult::EventIgnored
307 }
308 }
309
310 fn key_event(
311 self: Pin<&Self>,
312 event: &KeyEvent,
313 _window_adapter: &Rc<dyn WindowAdapter>,
314 _self_rc: &ItemRc,
315 ) -> KeyEventResult {
316 let r = match event.event_type {
317 KeyEventType::KeyPressed => {
318 Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),))
319 }
320 KeyEventType::KeyReleased => {
321 Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),))
322 }
323 KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
324 EventResult::Reject
325 }
326 };
327 match r {
328 EventResult::Accept => KeyEventResult::EventAccepted,
329 EventResult::Reject => KeyEventResult::EventIgnored,
330 }
331 }
332
333 fn focus_event(
334 self: Pin<&Self>,
335 event: &FocusEvent,
336 _window_adapter: &Rc<dyn WindowAdapter>,
337 _self_rc: &ItemRc,
338 ) -> FocusEventResult {
339 if !self.enabled() {
340 return FocusEventResult::FocusIgnored;
341 }
342
343 match event {
344 FocusEvent::FocusIn(reason) => {
345 self.has_focus.set(true);
346 Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&((*reason,)));
347 Self::FIELD_OFFSETS.focus_gained.apply_pin(self).call(&((*reason,)));
348 }
349 FocusEvent::FocusOut(reason) => {
350 self.has_focus.set(false);
351 Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&((*reason,)));
352 Self::FIELD_OFFSETS.focus_lost.apply_pin(self).call(&((*reason,)));
353 }
354 }
355 FocusEventResult::FocusAccepted
356 }
357
358 fn render(
359 self: Pin<&Self>,
360 _backend: &mut ItemRendererRef,
361 _self_rc: &ItemRc,
362 _size: LogicalSize,
363 ) -> RenderingResult {
364 RenderingResult::ContinueRenderingChildren
365 }
366
367 fn bounding_rect(
368 self: core::pin::Pin<&Self>,
369 _window_adapter: &Rc<dyn WindowAdapter>,
370 _self_rc: &ItemRc,
371 mut geometry: LogicalRect,
372 ) -> LogicalRect {
373 geometry.size = LogicalSize::zero();
374 geometry
375 }
376
377 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
378 false
379 }
380}
381
382impl ItemConsts for FocusScope {
383 const cached_rendering_data_offset: const_field_offset::FieldOffset<
384 FocusScope,
385 CachedRenderingData,
386 > = FocusScope::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
387}
388
389#[repr(C)]
390#[derive(FieldOffsets, Default, SlintElement)]
391#[pin]
392pub struct SwipeGestureHandler {
393 pub enabled: Property<bool>,
394 pub handle_swipe_left: Property<bool>,
395 pub handle_swipe_right: Property<bool>,
396 pub handle_swipe_up: Property<bool>,
397 pub handle_swipe_down: Property<bool>,
398
399 pub moved: Callback<VoidArg>,
400 pub swiped: Callback<VoidArg>,
401 pub cancelled: Callback<VoidArg>,
402
403 pub pressed_position: Property<LogicalPosition>,
404 pub current_position: Property<LogicalPosition>,
405 pub swiping: Property<bool>,
406
407 pressed: Cell<bool>,
409 pub cached_rendering_data: CachedRenderingData,
412}
413
414impl Item for SwipeGestureHandler {
415 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
416
417 fn layout_info(
418 self: Pin<&Self>,
419 _orientation: Orientation,
420 _window_adapter: &Rc<dyn WindowAdapter>,
421 _self_rc: &ItemRc,
422 ) -> LayoutInfo {
423 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
424 }
425
426 fn input_event_filter_before_children(
427 self: Pin<&Self>,
428 event: MouseEvent,
429 _window_adapter: &Rc<dyn WindowAdapter>,
430 _self_rc: &ItemRc,
431 ) -> InputEventFilterResult {
432 if !self.enabled() {
433 if self.pressed.get() {
434 self.cancel_impl();
435 }
436 return InputEventFilterResult::ForwardAndIgnore;
437 }
438
439 match event {
440 MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
441 Self::FIELD_OFFSETS
442 .pressed_position
443 .apply_pin(self)
444 .set(crate::lengths::logical_position_to_api(position));
445 self.pressed.set(true);
446 InputEventFilterResult::DelayForwarding(
447 super::flickable::FORWARD_DELAY.as_millis() as _
448 )
449 }
450 MouseEvent::Exit => {
451 self.cancel_impl();
452 InputEventFilterResult::ForwardAndIgnore
453 }
454 MouseEvent::Released { button: PointerEventButton::Left, .. } => {
455 if self.swiping() {
456 InputEventFilterResult::Intercept
457 } else {
458 self.pressed.set(false);
459 InputEventFilterResult::ForwardEvent
460 }
461 }
462 MouseEvent::Moved { position } => {
463 if self.swiping() {
464 InputEventFilterResult::Intercept
465 } else if !self.pressed.get() {
466 InputEventFilterResult::ForwardEvent
467 } else {
468 let pressed_pos = self.pressed_position();
469 let dx = position.x - pressed_pos.x as Coord;
470 let dy = position.y - pressed_pos.y as Coord;
471 let threshold = super::flickable::DISTANCE_THRESHOLD.get();
472 if (self.handle_swipe_down() && dy > threshold)
473 || (self.handle_swipe_up() && dy < -threshold)
474 || (self.handle_swipe_left() && dx < -threshold)
475 || (self.handle_swipe_right() && dx > threshold)
476 {
477 InputEventFilterResult::Intercept
478 } else {
479 InputEventFilterResult::ForwardAndInterceptGrab
480 }
481 }
482 }
483 MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore,
484 MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
486 InputEventFilterResult::ForwardAndIgnore
487 }
488 }
489 }
490
491 fn input_event(
492 self: Pin<&Self>,
493 event: MouseEvent,
494 _window_adapter: &Rc<dyn WindowAdapter>,
495 _self_rc: &ItemRc,
496 ) -> InputEventResult {
497 match event {
498 MouseEvent::Pressed { .. } => InputEventResult::GrabMouse,
499 MouseEvent::Exit => {
500 self.cancel_impl();
501 InputEventResult::EventIgnored
502 }
503 MouseEvent::Released { position, .. } => {
504 if !self.pressed.get() && !self.swiping() {
505 return InputEventResult::EventIgnored;
506 }
507 self.current_position.set(crate::lengths::logical_position_to_api(position));
508 self.pressed.set(false);
509 if self.swiping() {
510 Self::FIELD_OFFSETS.swiping.apply_pin(self).set(false);
511 Self::FIELD_OFFSETS.swiped.apply_pin(self).call(&());
512 InputEventResult::EventAccepted
513 } else {
514 InputEventResult::EventIgnored
515 }
516 }
517 MouseEvent::Moved { position } => {
518 if !self.pressed.get() {
519 return InputEventResult::EventIgnored;
520 }
521 self.current_position.set(crate::lengths::logical_position_to_api(position));
522 if !self.swiping() {
523 let pressed_pos = self.pressed_position();
524 let dx = position.x - pressed_pos.x as Coord;
525 let dy = position.y - pressed_pos.y as Coord;
526 let threshold = super::flickable::DISTANCE_THRESHOLD.get();
527 let start_swipe = (self.handle_swipe_down() && dy > threshold)
528 || (self.handle_swipe_up() && dy < -threshold)
529 || (self.handle_swipe_left() && dx < -threshold)
530 || (self.handle_swipe_right() && dx > threshold);
531
532 if start_swipe {
533 Self::FIELD_OFFSETS.swiping.apply_pin(self).set(true);
534 }
535 }
536 Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
537 InputEventResult::GrabMouse
538 }
539 MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
540 }
541 }
542
543 fn key_event(
544 self: Pin<&Self>,
545 _event: &KeyEvent,
546 _window_adapter: &Rc<dyn WindowAdapter>,
547 _self_rc: &ItemRc,
548 ) -> KeyEventResult {
549 KeyEventResult::EventIgnored
550 }
551
552 fn focus_event(
553 self: Pin<&Self>,
554 _: &FocusEvent,
555 _window_adapter: &Rc<dyn WindowAdapter>,
556 _self_rc: &ItemRc,
557 ) -> FocusEventResult {
558 FocusEventResult::FocusIgnored
559 }
560
561 fn render(
562 self: Pin<&Self>,
563 _backend: &mut ItemRendererRef,
564 _self_rc: &ItemRc,
565 _size: LogicalSize,
566 ) -> RenderingResult {
567 RenderingResult::ContinueRenderingChildren
568 }
569
570 fn bounding_rect(
571 self: core::pin::Pin<&Self>,
572 _window_adapter: &Rc<dyn WindowAdapter>,
573 _self_rc: &ItemRc,
574 mut geometry: LogicalRect,
575 ) -> LogicalRect {
576 geometry.size = LogicalSize::zero();
577 geometry
578 }
579
580 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
581 false
582 }
583}
584
585impl ItemConsts for SwipeGestureHandler {
586 const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
587 Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
588}
589
590impl SwipeGestureHandler {
591 pub fn cancel(self: Pin<&Self>, _: &Rc<dyn WindowAdapter>, _: &ItemRc) {
592 self.cancel_impl();
593 }
594
595 fn cancel_impl(self: Pin<&Self>) {
596 if !self.pressed.replace(false) {
597 debug_assert!(!self.swiping());
598 return;
599 }
600 if self.swiping() {
601 Self::FIELD_OFFSETS.swiping.apply_pin(self).set(false);
602 Self::FIELD_OFFSETS.cancelled.apply_pin(self).call(&());
603 }
604 }
605}
606
607#[cfg(feature = "ffi")]
608#[unsafe(no_mangle)]
609pub unsafe extern "C" fn slint_swipegesturehandler_cancel(
610 s: Pin<&SwipeGestureHandler>,
611 window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
612 self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
613 self_index: u32,
614) {
615 let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
616 let self_rc = ItemRc::new(self_component.clone(), self_index);
617 s.cancel(window_adapter, &self_rc);
618}