From 5113ada81da4983e9de2496ac291f36d7f9710a2 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 27 Oct 2022 17:42:05 +0200 Subject: [PATCH] Flickable: do not capture the event when moving the mouse bellow threshold Because an inner item (eg, another Flickable) way still want to intercept the delayed event --- internal/core/items/flickable.rs | 26 ++-- .../elements/flickable_in_flickable.slint | 130 ++++++++++++++++++ 2 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 tests/cases/elements/flickable_in_flickable.slint diff --git a/internal/core/items/flickable.rs b/internal/core/items/flickable.rs index 757eaa8eb5d..aaffbb28624 100644 --- a/internal/core/items/flickable.rs +++ b/internal/core/items/flickable.rs @@ -289,18 +289,25 @@ impl FlickableData { } MouseEvent::Moved { position } => { if inner.pressed_time.is_some() { - inner.capture_events = true; let new_pos = ensure_in_bound( flick, inner.pressed_viewport_pos + (position - inner.pressed_pos), ); - (Flickable::FIELD_OFFSETS.viewport + Empty::FIELD_OFFSETS.x) - .apply_pin(flick) - .set(new_pos.x_length()); - (Flickable::FIELD_OFFSETS.viewport + Empty::FIELD_OFFSETS.y) - .apply_pin(flick) - .set(new_pos.y_length()); - InputEventResult::GrabMouse + let x = (Flickable::FIELD_OFFSETS.viewport + Empty::FIELD_OFFSETS.x) + .apply_pin(flick); + let y = (Flickable::FIELD_OFFSETS.viewport + Empty::FIELD_OFFSETS.y) + .apply_pin(flick); + if inner.capture_events + || (new_pos - LogicalPoint::from_lengths(x.get(), y.get())).square_length() + >= (DISTANCE_THRESHOLD.get() * DISTANCE_THRESHOLD.get()) as _ + { + x.set(new_pos.x_length()); + y.set(new_pos.y_length()); + inner.capture_events = true; + InputEventResult::GrabMouse + } else { + InputEventResult::EventIgnored + } } else { inner.capture_events = false; InputEventResult::EventIgnored @@ -335,7 +342,8 @@ impl FlickableData { let dist = (pos - inner.pressed_pos).cast::(); let millis = (crate::animations::current_tick() - pressed_time).as_millis(); - if dist.square_length() > (DISTANCE_THRESHOLD.get() * DISTANCE_THRESHOLD.get()) as f32 + if inner.capture_events + && dist.square_length() > (DISTANCE_THRESHOLD.get() * DISTANCE_THRESHOLD.get()) as _ && millis > 1 { let speed = dist / (millis as f32); diff --git a/tests/cases/elements/flickable_in_flickable.slint b/tests/cases/elements/flickable_in_flickable.slint new file mode 100644 index 00000000000..c34f38041dc --- /dev/null +++ b/tests/cases/elements/flickable_in_flickable.slint @@ -0,0 +1,130 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +TestCase := Window { + width: 500px; + height: 500px; + no-frame: false; + + outer := Flickable { + x: 10px; + y: 10px; + width: parent.width - 20px; + height: parent.height - 20px; + viewport_width: width; + viewport_height: 980px; + + inner := Flickable { + viewport_width: 1500px; + Rectangle { + background: @radial-gradient(circle, yellow, blue, red, green); + } + } + } + + property outer_y: - outer.viewport-y; + property inner_x: - inner.viewport-x; + + property test: outer.viewport-x == 0 && inner.viewport-y == 0; + +} + +/* + +```rust +use slint::{WindowEvent, PointerEventButton, LogicalPosition}; +let instance = TestCase::new(); + +// First, try to scroll up +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 200.0) }); +slint_testing::mock_elapsed_time(16); +instance.window().dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(150.0, 200.0), button: PointerEventButton::Left }); +slint_testing::mock_elapsed_time(16); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 205.0) }); +slint_testing::mock_elapsed_time(16); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 290.0) }); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 300.0) }); +// shouldn't move because we are already up +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 0.); +eprintln!("Meh"); + +// now, scroll down +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 202.0) }); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 0.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 200.0) }); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 0.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 198.0) }); +// (still not above threshold : 200 - 198 < threshold) +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 0.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 170.0) }); +//instance.run(); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 30.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 150.0) }); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 50.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(100.0, 150.0) }); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 50.); +slint_testing::mock_elapsed_time(100); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(100.0, -2000.0) }); +assert_eq!(instance.get_inner_x(), 0.); +// all the way down +assert_eq!(instance.get_outer_y(), 500.); +slint_testing::mock_elapsed_time(100); +instance.window().dispatch_event(WindowEvent::PointerReleased { position: LogicalPosition::new(250.0, -2000.0), button: PointerEventButton::Left }); +slint_testing::mock_elapsed_time(1000); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 500.); + +// Now, try to go to the right and the bottom at the same time: we should go to the right because that's the only option +instance.window().dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(100.0, 100.0), button: PointerEventButton::Left }); +slint_testing::mock_elapsed_time(16); +assert_eq!(instance.get_inner_x(), 0.); +assert_eq!(instance.get_outer_y(), 500.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(90.0, 90.0) }); +slint_testing::mock_elapsed_time(120); // we need to wait enough because of the delay in the outer one +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(90.0, 90.0) }); +eprintln!("YPO {} {}", instance.get_inner_x(), instance.get_outer_y()); +assert_eq!(instance.get_inner_x(), 10.); +assert_eq!(instance.get_outer_y(), 500.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(20.0, 90.0) }); +slint_testing::mock_elapsed_time(16); +assert_eq!(instance.get_inner_x(), 80.); +assert_eq!(instance.get_outer_y(), 500.); + +// Scolling up still work as the outer flickable now intersepcts it, and then the inner one lost +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(10.0, 190.0) }); +slint_testing::mock_elapsed_time(16); +assert_eq!(instance.get_inner_x(), 80.); +assert_eq!(instance.get_outer_y(), 410.); +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(150.0, 200.0) }); +slint_testing::mock_elapsed_time(16); +assert_eq!(instance.get_inner_x(), 80.); +assert_eq!(instance.get_outer_y(), 400.); +slint_testing::mock_elapsed_time(1600); +instance.window().dispatch_event(WindowEvent::PointerReleased { position: LogicalPosition::new(150.0, 200.0), button: PointerEventButton::Left }); +slint_testing::mock_elapsed_time(16000); // finish the animation + + +// scroll to the left +let old_outer_y = instance.get_outer_y(); +instance.window().dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(100.0, 100.0), button: PointerEventButton::Left }); +slint_testing::mock_elapsed_time(160); +assert_eq!(instance.get_inner_x(), 80.); +assert_eq!(instance.get_outer_y(), old_outer_y); +// 103 is bellow the threshold, 120 is not +instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(120.0, 103.0) }); +slint_testing::mock_elapsed_time(16); +assert_eq!(instance.get_inner_x(), 60.); +assert_eq!(instance.get_outer_y(), old_outer_y); + + + +``` + +*/