// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 use super::{ DropEvent, Item, ItemConsts, ItemRc, MouseCursor, PointerEventButton, RenderingResult, }; use crate::input::{ FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, MouseEvent, }; use crate::item_rendering::{CachedRenderingData, ItemRenderer}; use crate::layout::{LayoutInfo, Orientation}; use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize}; #[cfg(feature = "rtti")] use crate::rtti::*; use crate::window::WindowAdapter; use crate::{Callback, Property, SharedString}; use alloc::rc::Rc; use const_field_offset::FieldOffsets; use core::cell::Cell; use core::pin::Pin; use i_slint_core_macros::*; pub type DropEventArg = (DropEvent,); #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `DragArea` element pub struct DragArea { pub enabled: Property, pub mime_type: Property, pub data: Property, pressed: Cell, pressed_position: Cell, pub cached_rendering_data: CachedRenderingData, } impl Item for DragArea { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn layout_info( self: Pin<&Self>, _: Orientation, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, event: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { if !self.enabled() { self.cancel(); return InputEventFilterResult::ForwardAndIgnore; } match event { MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => { self.pressed_position.set(*position); self.pressed.set(true); InputEventFilterResult::ForwardAndInterceptGrab } MouseEvent::Exit => { self.cancel(); InputEventFilterResult::ForwardAndIgnore } MouseEvent::Released { button: PointerEventButton::Left, .. } => { self.pressed.set(false); InputEventFilterResult::ForwardAndIgnore } MouseEvent::Moved { position } => { if !self.pressed.get() { InputEventFilterResult::ForwardEvent } else { let pressed_pos = self.pressed_position.get(); let dx = (position.x - pressed_pos.x).abs(); let dy = (position.y - pressed_pos.y).abs(); let threshold = super::flickable::DISTANCE_THRESHOLD.get(); if dy > threshold || dx > threshold { InputEventFilterResult::Intercept } else { InputEventFilterResult::ForwardAndInterceptGrab } } } MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore, // Not the left button MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => { InputEventFilterResult::ForwardAndIgnore } MouseEvent::DragMove(..) | MouseEvent::Drop(..) => { InputEventFilterResult::ForwardAndIgnore } } } fn input_event( self: Pin<&Self>, event: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { match event { MouseEvent::Pressed { .. } => InputEventResult::EventAccepted, MouseEvent::Exit => { self.cancel(); InputEventResult::EventIgnored } MouseEvent::Released { .. } => { self.cancel(); InputEventResult::EventIgnored } MouseEvent::Moved { position } => { if !self.pressed.get() || !self.enabled() { return InputEventResult::EventIgnored; } let pressed_pos = self.pressed_position.get(); let dx = (position.x - pressed_pos.x).abs(); let dy = (position.y - pressed_pos.y).abs(); let threshold = super::flickable::DISTANCE_THRESHOLD.get(); let start_drag = dx > threshold || dy > threshold; if start_drag { self.pressed.set(false); InputEventResult::StartDrag } else { InputEventResult::EventAccepted } } MouseEvent::Wheel { .. } => InputEventResult::EventIgnored, MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored, } } fn capture_key_event( self: Pin<&Self>, _: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, _: &mut &mut dyn ItemRenderer, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, mut geometry: LogicalRect, ) -> LogicalRect { geometry.size = LogicalSize::zero(); geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for DragArea { const cached_rendering_data_offset: const_field_offset::FieldOffset< DragArea, CachedRenderingData, > = DragArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); } impl DragArea { fn cancel(self: Pin<&Self>) { self.pressed.set(false) } } #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] /// The implementation of the `DropArea` element pub struct DropArea { pub enabled: Property, pub contains_drag: Property, pub can_drop: Callback, pub dropped: Callback, pub cached_rendering_data: CachedRenderingData, } impl Item for DropArea { fn init(self: Pin<&Self>, _self_rc: &ItemRc) {} fn layout_info( self: Pin<&Self>, _: Orientation, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { LayoutInfo { stretch: 1., ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardEvent } fn input_event( self: Pin<&Self>, event: &MouseEvent, window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventResult { if !self.enabled() { return InputEventResult::EventIgnored; } match event { MouseEvent::DragMove(event) => { let r = Self::FIELD_OFFSETS.can_drop.apply_pin(self).call(&(event.clone(),)); if r { self.contains_drag.set(true); if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) { window_adapter.set_mouse_cursor(MouseCursor::Copy); } InputEventResult::EventAccepted } else { self.contains_drag.set(false); InputEventResult::EventIgnored } } MouseEvent::Drop(event) => { self.contains_drag.set(false); Self::FIELD_OFFSETS.dropped.apply_pin(self).call(&(event.clone(),)); InputEventResult::EventAccepted } MouseEvent::Exit => { self.contains_drag.set(false); InputEventResult::EventIgnored } _ => InputEventResult::EventIgnored, } } fn capture_key_event( self: Pin<&Self>, _: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn render( self: Pin<&Self>, _: &mut &mut dyn ItemRenderer, _self_rc: &ItemRc, _size: LogicalSize, ) -> RenderingResult { RenderingResult::ContinueRenderingChildren } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, mut geometry: LogicalRect, ) -> LogicalRect { geometry.size = LogicalSize::zero(); geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for DropArea { const cached_rendering_data_offset: const_field_offset::FieldOffset< DropArea, CachedRenderingData, > = DropArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); }