mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-18 06:17:59 +00:00

Some checks are pending
autofix.ci / format_fix (push) Waiting to run
autofix.ci / lint_typecheck (push) Waiting to run
CI / tree-sitter (push) Blocked by required conditions
CI / updater_test (0.3.0) (push) Blocked by required conditions
CI / fmt_test (push) Blocked by required conditions
CI / files-changed (push) Waiting to run
CI / build_and_test (--exclude bevy-example, ubuntu-22.04, 1.85) (push) Blocked by required conditions
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, --exclude bevy-example, windows-2022, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (push) Blocked by required conditions
CI / node_test (macos-14) (push) Blocked by required conditions
CI / node_test (ubuntu-22.04) (push) Blocked by required conditions
CI / node_test (windows-2022) (push) Blocked by required conditions
CI / python_test (macos-14) (push) Blocked by required conditions
CI / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (macos-13) (push) Blocked by required conditions
CI / cpp_test_driver (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.85) (push) Blocked by required conditions
CI / cpp_cmake (ubuntu-22.04, stable) (push) Blocked by required conditions
CI / cpp_cmake (windows-2022, nightly) (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / esp-idf-quick (push) Blocked by required conditions
CI / android (push) Blocked by required conditions
CI / miri (push) Blocked by required conditions
CI / test-figma-inspector (push) Blocked by required conditions
CI / wasm_demo (push) Blocked by required conditions
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Blocked by required conditions
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Blocked by required conditions
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Blocked by required conditions
CI / mcu-embassy (push) Blocked by required conditions
CI / ffi_32bit_build (push) Blocked by required conditions
CI / docs (push) Blocked by required conditions
- When checking the threshold, make sure that the change in the interrested dirrection is much more significant that the delta in the orthogonal direction (hence the `/ 2`) - Accept all mouse move event as we otherwise don't get them further if the event was delayed by another SwipeGestureHandler or flickable
684 lines
24 KiB
Rust
684 lines
24 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
use super::{
|
|
EventResult, FocusReasonArg, Item, ItemConsts, ItemRc, ItemRendererRef, KeyEventArg,
|
|
MouseCursor, PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind,
|
|
PointerScrollEvent, PointerScrollEventArg, RenderingResult, VoidArg,
|
|
};
|
|
use crate::api::LogicalPosition;
|
|
use crate::input::{
|
|
FocusEvent, FocusEventResult, FocusReason, InputEventFilterResult, InputEventResult, KeyEvent,
|
|
KeyEventResult, KeyEventType, MouseEvent,
|
|
};
|
|
use crate::item_rendering::CachedRenderingData;
|
|
use crate::layout::{LayoutInfo, Orientation};
|
|
use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PointLengths};
|
|
#[cfg(feature = "rtti")]
|
|
use crate::rtti::*;
|
|
use crate::window::{WindowAdapter, WindowInner};
|
|
use crate::{Callback, Coord, Property};
|
|
use alloc::rc::Rc;
|
|
use const_field_offset::FieldOffsets;
|
|
use core::cell::Cell;
|
|
use core::pin::Pin;
|
|
use i_slint_core_macros::*;
|
|
|
|
/// The implementation of the `TouchArea` element
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets, SlintElement, Default)]
|
|
#[pin]
|
|
pub struct TouchArea {
|
|
pub enabled: Property<bool>,
|
|
/// FIXME: We should annotate this as an "output" property.
|
|
pub pressed: Property<bool>,
|
|
pub has_hover: Property<bool>,
|
|
/// FIXME: there should be just one property for the point instead of two.
|
|
/// Could even be merged with pressed in a `Property<Option<Point>>` (of course, in the
|
|
/// implementation item only, for the compiler it would stay separate properties)
|
|
pub pressed_x: Property<LogicalLength>,
|
|
pub pressed_y: Property<LogicalLength>,
|
|
/// FIXME: should maybe be as parameter to the mouse event instead. Or at least just one property
|
|
pub mouse_x: Property<LogicalLength>,
|
|
pub mouse_y: Property<LogicalLength>,
|
|
pub mouse_cursor: Property<MouseCursor>,
|
|
pub clicked: Callback<VoidArg>,
|
|
pub double_clicked: Callback<VoidArg>,
|
|
pub moved: Callback<VoidArg>,
|
|
pub pointer_event: Callback<PointerEventArg>,
|
|
pub scroll_event: Callback<PointerScrollEventArg, EventResult>,
|
|
/// FIXME: remove this
|
|
pub cached_rendering_data: CachedRenderingData,
|
|
/// true when we are currently grabbing the mouse
|
|
grabbed: Cell<bool>,
|
|
}
|
|
|
|
impl Item for TouchArea {
|
|
fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
|
|
|
|
fn layout_info(
|
|
self: Pin<&Self>,
|
|
_orientation: Orientation,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> LayoutInfo {
|
|
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
|
|
}
|
|
|
|
fn input_event_filter_before_children(
|
|
self: Pin<&Self>,
|
|
event: &MouseEvent,
|
|
window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> InputEventFilterResult {
|
|
if !self.enabled() {
|
|
self.has_hover.set(false);
|
|
if self.grabbed.replace(false) {
|
|
self.pressed.set(false);
|
|
Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
|
|
button: PointerEventButton::Other,
|
|
kind: PointerEventKind::Cancel,
|
|
modifiers: window_adapter.window().0.modifiers.get().into(),
|
|
},));
|
|
}
|
|
return InputEventFilterResult::ForwardAndIgnore;
|
|
}
|
|
if matches!(event, MouseEvent::DragMove(..) | MouseEvent::Drop(..)) {
|
|
// Someone else has the grab, don't handle hover
|
|
return InputEventFilterResult::ForwardAndIgnore;
|
|
}
|
|
if let Some(pos) = event.position() {
|
|
Self::FIELD_OFFSETS.mouse_x.apply_pin(self).set(pos.x_length());
|
|
Self::FIELD_OFFSETS.mouse_y.apply_pin(self).set(pos.y_length());
|
|
}
|
|
let hovering = !matches!(event, MouseEvent::Exit);
|
|
Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(hovering);
|
|
if hovering {
|
|
if let Some(x) = window_adapter.internal(crate::InternalToken) {
|
|
x.set_mouse_cursor(self.mouse_cursor());
|
|
}
|
|
}
|
|
InputEventFilterResult::ForwardAndInterceptGrab
|
|
}
|
|
|
|
fn input_event(
|
|
self: Pin<&Self>,
|
|
event: &MouseEvent,
|
|
window_adapter: &Rc<dyn WindowAdapter>,
|
|
self_rc: &ItemRc,
|
|
) -> InputEventResult {
|
|
if matches!(event, MouseEvent::Exit) {
|
|
Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
|
|
if let Some(x) = window_adapter.internal(crate::InternalToken) {
|
|
x.set_mouse_cursor(MouseCursor::Default);
|
|
}
|
|
}
|
|
if !self.enabled() {
|
|
return InputEventResult::EventIgnored;
|
|
}
|
|
|
|
match event {
|
|
MouseEvent::Pressed { position, button, .. } => {
|
|
self.grabbed.set(true);
|
|
if *button == PointerEventButton::Left {
|
|
Self::FIELD_OFFSETS.pressed_x.apply_pin(self).set(position.x_length());
|
|
Self::FIELD_OFFSETS.pressed_y.apply_pin(self).set(position.y_length());
|
|
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(true);
|
|
}
|
|
Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
|
|
button: *button,
|
|
kind: PointerEventKind::Down,
|
|
modifiers: window_adapter.window().0.modifiers.get().into(),
|
|
},));
|
|
|
|
InputEventResult::GrabMouse
|
|
}
|
|
MouseEvent::Exit => {
|
|
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
|
|
if self.grabbed.replace(false) {
|
|
Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
|
|
button: PointerEventButton::Other,
|
|
kind: PointerEventKind::Cancel,
|
|
modifiers: window_adapter.window().0.modifiers.get().into(),
|
|
},));
|
|
}
|
|
|
|
InputEventResult::EventAccepted
|
|
}
|
|
|
|
MouseEvent::Released { button, position, click_count } => {
|
|
let geometry = self_rc.geometry();
|
|
if *button == PointerEventButton::Left
|
|
&& LogicalRect::new(LogicalPoint::default(), geometry.size).contains(*position)
|
|
&& self.pressed()
|
|
{
|
|
Self::FIELD_OFFSETS.clicked.apply_pin(self).call(&());
|
|
if (click_count % 2) == 1 {
|
|
Self::FIELD_OFFSETS.double_clicked.apply_pin(self).call(&())
|
|
}
|
|
}
|
|
|
|
self.grabbed.set(false);
|
|
if *button == PointerEventButton::Left {
|
|
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
|
|
}
|
|
Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
|
|
button: *button,
|
|
kind: PointerEventKind::Up,
|
|
modifiers: window_adapter.window().0.modifiers.get().into(),
|
|
},));
|
|
|
|
InputEventResult::EventAccepted
|
|
}
|
|
MouseEvent::Moved { .. } => {
|
|
Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
|
|
button: PointerEventButton::Other,
|
|
kind: PointerEventKind::Move,
|
|
modifiers: window_adapter.window().0.modifiers.get().into(),
|
|
},));
|
|
if self.grabbed.get() {
|
|
Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
|
|
InputEventResult::GrabMouse
|
|
} else {
|
|
InputEventResult::EventAccepted
|
|
}
|
|
}
|
|
MouseEvent::Wheel { delta_x, delta_y, .. } => {
|
|
let modifiers = window_adapter.window().0.modifiers.get().into();
|
|
let r =
|
|
Self::FIELD_OFFSETS.scroll_event.apply_pin(self).call(&(PointerScrollEvent {
|
|
delta_x: *delta_x,
|
|
delta_y: *delta_y,
|
|
modifiers,
|
|
},));
|
|
if self.grabbed.get() {
|
|
InputEventResult::GrabMouse
|
|
} else {
|
|
match r {
|
|
EventResult::Reject => {
|
|
// We are ignoring the event, so we will be removed from the item_stack,
|
|
// therefore we must remove the has_hover flag as there might be a scroll under us.
|
|
// It will be put back later.
|
|
Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
|
|
InputEventResult::EventIgnored
|
|
}
|
|
EventResult::Accept => InputEventResult::EventAccepted,
|
|
}
|
|
}
|
|
}
|
|
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
|
}
|
|
}
|
|
|
|
fn capture_key_event(
|
|
self: Pin<&Self>,
|
|
_: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
KeyEventResult::EventIgnored
|
|
}
|
|
|
|
fn key_event(
|
|
self: Pin<&Self>,
|
|
_: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
KeyEventResult::EventIgnored
|
|
}
|
|
|
|
fn focus_event(
|
|
self: Pin<&Self>,
|
|
_: &FocusEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> FocusEventResult {
|
|
FocusEventResult::FocusIgnored
|
|
}
|
|
|
|
fn render(
|
|
self: Pin<&Self>,
|
|
_backend: &mut ItemRendererRef,
|
|
_self_rc: &ItemRc,
|
|
_size: LogicalSize,
|
|
) -> RenderingResult {
|
|
RenderingResult::ContinueRenderingChildren
|
|
}
|
|
|
|
fn bounding_rect(
|
|
self: core::pin::Pin<&Self>,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_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 TouchArea {
|
|
const cached_rendering_data_offset: const_field_offset::FieldOffset<
|
|
TouchArea,
|
|
CachedRenderingData,
|
|
> = TouchArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
|
}
|
|
|
|
/// A runtime item that exposes key
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets, Default, SlintElement)]
|
|
#[pin]
|
|
pub struct FocusScope {
|
|
pub enabled: Property<bool>,
|
|
pub has_focus: Property<bool>,
|
|
pub focus_on_click: Property<bool>,
|
|
pub focus_on_tab_navigation: Property<bool>,
|
|
pub key_pressed: Callback<KeyEventArg, EventResult>,
|
|
pub key_released: Callback<KeyEventArg, EventResult>,
|
|
pub capture_key_pressed: Callback<KeyEventArg, EventResult>,
|
|
pub capture_key_released: Callback<KeyEventArg, EventResult>,
|
|
pub focus_changed_event: Callback<FocusReasonArg>,
|
|
pub focus_gained: Callback<FocusReasonArg>,
|
|
pub focus_lost: Callback<FocusReasonArg>,
|
|
/// FIXME: remove this
|
|
pub cached_rendering_data: CachedRenderingData,
|
|
}
|
|
|
|
impl Item for FocusScope {
|
|
fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
|
|
|
|
fn layout_info(
|
|
self: Pin<&Self>,
|
|
_orientation: Orientation,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> LayoutInfo {
|
|
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
|
|
}
|
|
|
|
fn input_event_filter_before_children(
|
|
self: Pin<&Self>,
|
|
_: &MouseEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> InputEventFilterResult {
|
|
InputEventFilterResult::ForwardEvent
|
|
}
|
|
|
|
fn input_event(
|
|
self: Pin<&Self>,
|
|
event: &MouseEvent,
|
|
window_adapter: &Rc<dyn WindowAdapter>,
|
|
self_rc: &ItemRc,
|
|
) -> InputEventResult {
|
|
if self.enabled()
|
|
&& self.focus_on_click()
|
|
&& matches!(event, MouseEvent::Pressed { .. })
|
|
&& !self.has_focus()
|
|
{
|
|
WindowInner::from_pub(window_adapter.window()).set_focus_item(
|
|
self_rc,
|
|
true,
|
|
FocusReason::PointerClick,
|
|
);
|
|
InputEventResult::EventAccepted
|
|
} else {
|
|
InputEventResult::EventIgnored
|
|
}
|
|
}
|
|
|
|
fn capture_key_event(
|
|
self: Pin<&Self>,
|
|
event: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
let r = match event.event_type {
|
|
KeyEventType::KeyPressed => {
|
|
Self::FIELD_OFFSETS.capture_key_pressed.apply_pin(self).call(&(event.clone(),))
|
|
}
|
|
KeyEventType::KeyReleased => {
|
|
Self::FIELD_OFFSETS.capture_key_released.apply_pin(self).call(&(event.clone(),))
|
|
}
|
|
KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
|
|
EventResult::Reject
|
|
}
|
|
};
|
|
match r {
|
|
EventResult::Accept => KeyEventResult::EventAccepted,
|
|
EventResult::Reject => KeyEventResult::EventIgnored,
|
|
}
|
|
}
|
|
|
|
fn key_event(
|
|
self: Pin<&Self>,
|
|
event: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
let r = match event.event_type {
|
|
KeyEventType::KeyPressed => {
|
|
Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),))
|
|
}
|
|
KeyEventType::KeyReleased => {
|
|
Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),))
|
|
}
|
|
KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
|
|
EventResult::Reject
|
|
}
|
|
};
|
|
match r {
|
|
EventResult::Accept => KeyEventResult::EventAccepted,
|
|
EventResult::Reject => KeyEventResult::EventIgnored,
|
|
}
|
|
}
|
|
|
|
fn focus_event(
|
|
self: Pin<&Self>,
|
|
event: &FocusEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> FocusEventResult {
|
|
if !self.enabled() {
|
|
return FocusEventResult::FocusIgnored;
|
|
}
|
|
|
|
match event {
|
|
FocusEvent::FocusIn(reason) => {
|
|
match reason {
|
|
FocusReason::TabNavigation if !self.focus_on_tab_navigation() => {
|
|
return FocusEventResult::FocusIgnored
|
|
}
|
|
FocusReason::PointerClick if !self.focus_on_click() => {
|
|
return FocusEventResult::FocusIgnored
|
|
}
|
|
_ => (),
|
|
};
|
|
|
|
self.has_focus.set(true);
|
|
Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&((*reason,)));
|
|
Self::FIELD_OFFSETS.focus_gained.apply_pin(self).call(&((*reason,)));
|
|
}
|
|
FocusEvent::FocusOut(reason) => {
|
|
self.has_focus.set(false);
|
|
Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&((*reason,)));
|
|
Self::FIELD_OFFSETS.focus_lost.apply_pin(self).call(&((*reason,)));
|
|
}
|
|
}
|
|
FocusEventResult::FocusAccepted
|
|
}
|
|
|
|
fn render(
|
|
self: Pin<&Self>,
|
|
_backend: &mut ItemRendererRef,
|
|
_self_rc: &ItemRc,
|
|
_size: LogicalSize,
|
|
) -> RenderingResult {
|
|
RenderingResult::ContinueRenderingChildren
|
|
}
|
|
|
|
fn bounding_rect(
|
|
self: core::pin::Pin<&Self>,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_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 FocusScope {
|
|
const cached_rendering_data_offset: const_field_offset::FieldOffset<
|
|
FocusScope,
|
|
CachedRenderingData,
|
|
> = FocusScope::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets, Default, SlintElement)]
|
|
#[pin]
|
|
pub struct SwipeGestureHandler {
|
|
pub enabled: Property<bool>,
|
|
pub handle_swipe_left: Property<bool>,
|
|
pub handle_swipe_right: Property<bool>,
|
|
pub handle_swipe_up: Property<bool>,
|
|
pub handle_swipe_down: Property<bool>,
|
|
|
|
pub moved: Callback<VoidArg>,
|
|
pub swiped: Callback<VoidArg>,
|
|
pub cancelled: Callback<VoidArg>,
|
|
|
|
pub pressed_position: Property<LogicalPosition>,
|
|
pub current_position: Property<LogicalPosition>,
|
|
pub swiping: Property<bool>,
|
|
|
|
// true when the cursor is pressed down and we haven't cancelled yet for another reason
|
|
pressed: Cell<bool>,
|
|
// capture_events: Cell<bool>,
|
|
/// FIXME: remove this
|
|
pub cached_rendering_data: CachedRenderingData,
|
|
}
|
|
|
|
impl Item for SwipeGestureHandler {
|
|
fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
|
|
|
|
fn layout_info(
|
|
self: Pin<&Self>,
|
|
_orientation: Orientation,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> LayoutInfo {
|
|
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
|
|
}
|
|
|
|
fn input_event_filter_before_children(
|
|
self: Pin<&Self>,
|
|
event: &MouseEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> InputEventFilterResult {
|
|
if !self.enabled() {
|
|
if self.pressed.get() {
|
|
self.cancel_impl();
|
|
}
|
|
return InputEventFilterResult::ForwardAndIgnore;
|
|
}
|
|
|
|
match event {
|
|
MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
|
|
Self::FIELD_OFFSETS
|
|
.pressed_position
|
|
.apply_pin(self)
|
|
.set(crate::lengths::logical_position_to_api(*position));
|
|
self.pressed.set(true);
|
|
InputEventFilterResult::DelayForwarding(
|
|
super::flickable::FORWARD_DELAY.as_millis() as _
|
|
)
|
|
}
|
|
MouseEvent::Exit => {
|
|
self.cancel_impl();
|
|
InputEventFilterResult::ForwardAndIgnore
|
|
}
|
|
MouseEvent::Released { button: PointerEventButton::Left, .. } => {
|
|
if self.swiping() {
|
|
InputEventFilterResult::Intercept
|
|
} else {
|
|
self.pressed.set(false);
|
|
InputEventFilterResult::ForwardEvent
|
|
}
|
|
}
|
|
MouseEvent::Moved { position } => {
|
|
if self.swiping() {
|
|
InputEventFilterResult::Intercept
|
|
} else if !self.pressed.get() {
|
|
InputEventFilterResult::ForwardEvent
|
|
} else if self.is_over_threshold(position) {
|
|
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<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> InputEventResult {
|
|
match event {
|
|
MouseEvent::Pressed { .. } => InputEventResult::GrabMouse,
|
|
MouseEvent::Exit => {
|
|
self.cancel_impl();
|
|
InputEventResult::EventIgnored
|
|
}
|
|
MouseEvent::Released { position, .. } => {
|
|
if !self.pressed.get() && !self.swiping() {
|
|
return InputEventResult::EventIgnored;
|
|
}
|
|
self.current_position.set(crate::lengths::logical_position_to_api(*position));
|
|
self.pressed.set(false);
|
|
if self.swiping() {
|
|
Self::FIELD_OFFSETS.swiping.apply_pin(self).set(false);
|
|
Self::FIELD_OFFSETS.swiped.apply_pin(self).call(&());
|
|
InputEventResult::EventAccepted
|
|
} else {
|
|
InputEventResult::EventIgnored
|
|
}
|
|
}
|
|
MouseEvent::Moved { position } => {
|
|
if !self.pressed.get() {
|
|
return InputEventResult::EventAccepted;
|
|
}
|
|
self.current_position.set(crate::lengths::logical_position_to_api(*position));
|
|
let mut swiping = self.swiping();
|
|
if !swiping && self.is_over_threshold(position) {
|
|
Self::FIELD_OFFSETS.swiping.apply_pin(self).set(true);
|
|
swiping = true;
|
|
}
|
|
Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
|
|
if swiping {
|
|
InputEventResult::GrabMouse
|
|
} else {
|
|
InputEventResult::EventAccepted
|
|
}
|
|
}
|
|
MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
|
|
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
|
}
|
|
}
|
|
|
|
fn capture_key_event(
|
|
self: Pin<&Self>,
|
|
_: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
KeyEventResult::EventIgnored
|
|
}
|
|
|
|
fn key_event(
|
|
self: Pin<&Self>,
|
|
_event: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
KeyEventResult::EventIgnored
|
|
}
|
|
|
|
fn focus_event(
|
|
self: Pin<&Self>,
|
|
_: &FocusEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> FocusEventResult {
|
|
FocusEventResult::FocusIgnored
|
|
}
|
|
|
|
fn render(
|
|
self: Pin<&Self>,
|
|
_backend: &mut ItemRendererRef,
|
|
_self_rc: &ItemRc,
|
|
_size: LogicalSize,
|
|
) -> RenderingResult {
|
|
RenderingResult::ContinueRenderingChildren
|
|
}
|
|
|
|
fn bounding_rect(
|
|
self: core::pin::Pin<&Self>,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_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 SwipeGestureHandler {
|
|
const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
|
|
Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
|
}
|
|
|
|
impl SwipeGestureHandler {
|
|
pub fn cancel(self: Pin<&Self>, _: &Rc<dyn WindowAdapter>, _: &ItemRc) {
|
|
self.cancel_impl();
|
|
}
|
|
|
|
fn cancel_impl(self: Pin<&Self>) {
|
|
if !self.pressed.replace(false) {
|
|
debug_assert!(!self.swiping());
|
|
return;
|
|
}
|
|
if self.swiping() {
|
|
Self::FIELD_OFFSETS.swiping.apply_pin(self).set(false);
|
|
Self::FIELD_OFFSETS.cancelled.apply_pin(self).call(&());
|
|
}
|
|
}
|
|
|
|
fn is_over_threshold(self: Pin<&Self>, position: &LogicalPoint) -> bool {
|
|
let pressed_pos = self.pressed_position();
|
|
let dx = position.x - pressed_pos.x as Coord;
|
|
let dy = position.y - pressed_pos.y as Coord;
|
|
let threshold = super::flickable::DISTANCE_THRESHOLD.get();
|
|
(self.handle_swipe_down() && dy > threshold && dy > dx.abs() / 2 as Coord)
|
|
|| (self.handle_swipe_up() && dy < -threshold && dy < -dx.abs() / 2 as Coord)
|
|
|| (self.handle_swipe_left() && dx < -threshold && dx < -dy.abs() / 2 as Coord)
|
|
|| (self.handle_swipe_right() && dx > threshold && dx > dy.abs() / 2 as Coord)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "ffi")]
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn slint_swipegesturehandler_cancel(
|
|
s: Pin<&SwipeGestureHandler>,
|
|
window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
|
|
self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
|
|
self_index: u32,
|
|
) {
|
|
let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
|
|
let self_rc = ItemRc::new(self_component.clone(), self_index);
|
|
s.cancel(window_adapter, &self_rc);
|
|
}
|