Skip to content

Commit

Permalink
Flickable: do not capture the event when moving the mouse bellow thre…
Browse files Browse the repository at this point in the history
…shold

Because an inner item (eg, another Flickable) way still want
to intercept the delayed event
  • Loading branch information
ogoffart committed Oct 27, 2022
1 parent 65ac9df commit 5113ada
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 9 deletions.
26 changes: 17 additions & 9 deletions internal/core/items/flickable.rs
Expand Up @@ -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
Expand Down Expand Up @@ -335,7 +342,8 @@ impl FlickableData {
let dist = (pos - inner.pressed_pos).cast::<f32>();

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);
Expand Down
130 changes: 130 additions & 0 deletions tests/cases/elements/flickable_in_flickable.slint
@@ -0,0 +1,130 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// 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<length> outer_y: - outer.viewport-y;
property<length> inner_x: - inner.viewport-x;

property <bool> 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);
```
*/

0 comments on commit 5113ada

Please sign in to comment.