Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/compiler/builtins.slint
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export component Flickable inherits Empty {
in property <length> viewport-width;
in-out property <length> viewport-x;
in-out property <length> viewport-y;
in property <bool> smooth-scroll: true;
in property <bool> interactive: true;
callback flicked();
//-default_size_binding:expands_to_parent_geometry
Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/widgets/cosmic/scrollview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <bool> smooth-scroll: true;
in property <ScrollBarPolicy> vertical-scrollbar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-scrollbar-policy <=> horizontal-bar.policy;
in property <bool> mouse-drag-pan-enabled <=> flickable.interactive;
Expand All @@ -108,6 +109,7 @@ export component ScrollView {

flickable := Flickable {
interactive: false;
smooth-scroll: root.smooth-scroll;
viewport-y <=> vertical-bar.value;
viewport-x <=> horizontal-bar.value;
width: 100%;
Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/widgets/cupertino/scrollview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <bool> smooth-scroll: true;
in property <ScrollBarPolicy> vertical-scrollbar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-scrollbar-policy <=> horizontal-bar.policy;
in property <bool> mouse-drag-pan-enabled <=> flickable.interactive;
Expand All @@ -118,6 +119,7 @@ export component ScrollView {
x: 2px;
y: 2px;
interactive: false;
smooth-scroll: root.smooth-scroll;
viewport-y <=> vertical-bar.value;
viewport-x <=> horizontal-bar.value;
width: parent.width - 4px;
Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/widgets/fluent/scrollview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <bool> smooth-scroll: true;
in property <ScrollBarPolicy> vertical-scrollbar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-scrollbar-policy <=> horizontal-bar.policy;
in property <bool> mouse-drag-pan-enabled <=> flickable.interactive;
Expand All @@ -163,6 +164,7 @@ export component ScrollView {
interactive: false;
viewport-y <=> vertical-bar.value;
viewport-x <=> horizontal-bar.value;
smooth-scroll: root.smooth-scroll;
width: parent.width;
height: parent.height;

Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/widgets/material/scrollview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <bool> smooth-scroll: true;
in property <ScrollBarPolicy> vertical-scrollbar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-scrollbar-policy <=> horizontal-bar.policy;
in property <bool> mouse-drag-pan-enabled <=> flickable.interactive;
Expand All @@ -120,6 +121,7 @@ export component ScrollView {
y: 0;
viewport-y <=> vertical-bar.value;
viewport-x <=> horizontal-bar.value;
smooth-scroll: root.smooth-scroll;
width: parent.width - vertical-bar.width - 4px;
height: parent.height - horizontal-bar.height - 4px;

Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/widgets/qt/internal-scrollview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export component InternalScrollView {
out property <length> visible-height <=> fli.height;
in-out property <bool> has-focus <=> native.has-focus;
in property <bool> enabled <=> native.enabled;
in property <bool> smooth-scroll: true;
in property <ScrollBarPolicy> vertical-scrollbar-policy <=> native.vertical-scrollbar-policy;
in property <ScrollBarPolicy> horizontal-scrollbar-policy <=> native.horizontal-scrollbar-policy;
in property <bool> mouse-drag-pan-enabled <=> fli.interactive;
Expand Down Expand Up @@ -49,6 +50,7 @@ export component InternalScrollView {

@children
interactive: false;
smooth-scroll: root.smooth-scroll;
viewport-y <=> native.vertical-value;
viewport-x <=> native.horizontal-value;
}
Expand Down
2 changes: 2 additions & 0 deletions internal/compiler/widgets/qt/scrollview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export component ScrollView {
in-out property <length> viewport-height <=> internal.viewport-height;
in-out property <length> viewport-x <=> internal.viewport-x;
in-out property <length> viewport-y <=> internal.viewport-y;
in property <bool> smooth-scroll: true;
in property <ScrollBarPolicy> vertical-scrollbar-policy <=> internal.vertical-scrollbar-policy;
in property <ScrollBarPolicy> horizontal-scrollbar-policy <=> internal.horizontal-scrollbar-policy;
in property <bool> mouse-drag-pan-enabled <=> internal.mouse-drag-pan-enabled;
Expand All @@ -26,6 +27,7 @@ export component ScrollView {
preferred-width: 100%;

internal := InternalScrollView {
smooth-scroll: root.smooth-scroll;
@children
}
}
87 changes: 64 additions & 23 deletions internal/core/items/flickable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::input::{
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent, MouseEvent,
};
use crate::item_rendering::CachedRenderingData;
use crate::items::PropertyAnimation;
use crate::items::{AnimationDirection, PropertyAnimation};
use crate::layout::{LayoutInfo, Orientation};
use crate::lengths::{
LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
Expand Down Expand Up @@ -48,6 +48,7 @@ pub struct Flickable {
pub viewport_height: Property<LogicalLength>,

pub interactive: Property<bool>,
pub smooth_scroll: Property<bool>,

pub flicked: Callback<VoidArg>,

Expand Down Expand Up @@ -242,6 +243,15 @@ pub(super) const DURATION_THRESHOLD: Duration = Duration::from_millis(500);
/// The delay to which press are forwarded to the inner item
pub(super) const FORWARD_DELAY: Duration = Duration::from_millis(100);

const SMOOTH_SCROLL_DURATION: i32 = 250;
const SMOOTH_SCROLL_ANIM: PropertyAnimation = PropertyAnimation {
duration: SMOOTH_SCROLL_DURATION,
easing: EasingCurve::CubicBezier([0.0, 0.0, 0.58, 1.0]),
delay: 0,
iteration_count: 1.,
direction: AnimationDirection::Normal,
};

#[derive(Default, Debug)]
struct FlickableDataInner {
/// The position in which the press was made
Expand All @@ -250,6 +260,8 @@ struct FlickableDataInner {
pressed_viewport_pos: LogicalPoint,
/// Set to true if the flickable is flicking and capturing all mouse event, not forwarding back to the children
capture_events: bool,
smooth_scroll_time: Option<Instant>,
smooth_scroll_target: LogicalPoint,
}

#[derive(Default, Debug)]
Expand Down Expand Up @@ -395,7 +407,7 @@ impl FlickableData {
}
}
MouseEvent::Wheel { delta_x, delta_y, .. } => {
let delta = if window_adapter.window().0.modifiers.get().shift()
let mut delta = if window_adapter.window().0.modifiers.get().shift()
&& !cfg!(target_os = "macos")
{
// Shift invert coordinate for the purpose of scrolling. But not on macOs because there the OS already take care of the change
Expand All @@ -413,18 +425,50 @@ impl FlickableData {
return InputEventResult::EventIgnored;
}

let old_pos = LogicalPoint::from_lengths(
(Flickable::FIELD_OFFSETS.viewport_x).apply_pin(flick).get(),
(Flickable::FIELD_OFFSETS.viewport_y).apply_pin(flick).get(),
);
let new_pos = ensure_in_bound(flick, old_pos + delta, flick_rc);

let viewport_x = (Flickable::FIELD_OFFSETS.viewport_x).apply_pin(flick);
let viewport_y = (Flickable::FIELD_OFFSETS.viewport_y).apply_pin(flick);
let old_pos = (viewport_x.get(), viewport_y.get());
viewport_x.set(new_pos.x_length());
viewport_y.set(new_pos.y_length());
if old_pos.0 != new_pos.x_length() || old_pos.1 != new_pos.y_length() {

let old_pos = LogicalPoint::from_lengths(viewport_x.get(), viewport_y.get());

let smooth_scroll = (Flickable::FIELD_OFFSETS.smooth_scroll).apply_pin(flick).get();

if smooth_scroll {
// Accumulate scroll delta
if let Some(smooth_scroll_time) = inner.smooth_scroll_time.take() {
let millis = (crate::animations::current_tick() - smooth_scroll_time)
.as_millis() as i32;

if millis < SMOOTH_SCROLL_DURATION {
let remaining_delta = inner.smooth_scroll_target - old_pos;

// Only if is in the same direction.
// `Default` is because in embedded `dot` returns `i32`
// but in any other platform it returns `f32`
if delta.dot(remaining_delta) > Default::default() {
delta += remaining_delta;
}
}
}
}

let new_pos = ensure_in_bound(flick, old_pos + delta, flick_rc);

if smooth_scroll {
inner.smooth_scroll_target = new_pos;
inner.smooth_scroll_time = Some(Instant::now());

// Discard previous animation
viewport_x.set_binding(|| euclid::Length::default());
viewport_y.set_binding(|| euclid::Length::default());

viewport_x.set_animated_value(new_pos.x_length(), SMOOTH_SCROLL_ANIM);
viewport_y.set_animated_value(new_pos.y_length(), SMOOTH_SCROLL_ANIM);
} else {
viewport_x.set(new_pos.x_length());
viewport_y.set(new_pos.y_length());
}

if old_pos != new_pos {
(Flickable::FIELD_OFFSETS.flicked).apply_pin(flick).call(&());
}
InputEventResult::EventAccepted
Expand All @@ -449,24 +493,21 @@ impl FlickableData {
{
let speed = dist / (millis as f32);

let duration = 250;
let final_pos = ensure_in_bound(
flick,
(inner.pressed_viewport_pos.cast() + dist + speed * (duration as f32)).cast(),
(inner.pressed_viewport_pos.cast()
+ dist
+ speed * (SMOOTH_SCROLL_DURATION as f32))
.cast(),
flick_rc,
);
let anim = PropertyAnimation {
duration,
easing: EasingCurve::CubicBezier([0.0, 0.0, 0.58, 1.0]),
..PropertyAnimation::default()
};

let viewport_x = (Flickable::FIELD_OFFSETS.viewport_x).apply_pin(flick);
let viewport_y = (Flickable::FIELD_OFFSETS.viewport_y).apply_pin(flick);
let old_pos = (viewport_x.get(), viewport_y.get());
viewport_x.set_animated_value(final_pos.x_length(), anim.clone());
viewport_y.set_animated_value(final_pos.y_length(), anim);
if old_pos.0 != final_pos.x_length() || old_pos.1 != final_pos.y_length() {
let old_pos = LogicalPoint::from_lengths(viewport_x.get(), viewport_y.get());
viewport_x.set_animated_value(final_pos.x_length(), SMOOTH_SCROLL_ANIM);
viewport_y.set_animated_value(final_pos.y_length(), SMOOTH_SCROLL_ANIM);
if old_pos != final_pos {
(Flickable::FIELD_OFFSETS.flicked).apply_pin(flick).call(&());
}
}
Expand Down
3 changes: 2 additions & 1 deletion internal/core/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,8 @@ impl<C: RepeatedItemTree + 'static> Repeater<C> {
viewport_height.set(inner.cached_item_height * row_count as Coord);
viewport_width.set(vp_width);
let new_viewport_y = -inner.anchor_y + new_offset_y;
viewport_y.set(new_viewport_y);
// XXX: This `set` prevents smooth scroll animated value
// viewport_y.set(new_viewport_y);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why this is a problem, but maybe there are better options:

Either:

  • try to avoid calling it if nothing changed (which should be most of the time as this is only useful if the height changed or something, so we could try to heck if it is not different from the current one before setting)
  • Instead of passing viewport_* properties, pass a Pin<&Flickable> to that function, and add a function in Flickable to scroll to that position. (Although thinking about this, it might not want to be what you want if scrolling.

inner.previous_viewport_y = new_viewport_y;
break;
}
Expand Down
1 change: 1 addition & 0 deletions tests/cases/elements/flickable.slint
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ TestCase := Window {
height: parent.height - 20phx;
viewport_width: 2100phx;
viewport_height: 2100phx;
smooth-scroll: false;

flicked => {
root.flicked += viewport_x/1phx * 100000 + viewport_y/1phx;
Expand Down
4 changes: 3 additions & 1 deletion tests/cases/elements/flickable2.slint
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ TestCase := Window {
}
}

Flickable { for i in 5: Rectangle {} }
Flickable {
for i in 5: Rectangle {}
}

property<bool> all_ok: r1.ok && r2.ok && r3.ok && r4.ok;
property<bool> test: all_ok;
Expand Down
2 changes: 2 additions & 0 deletions tests/cases/elements/flickable3.slint
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ TestCase := Window {
x: 0phx;
width: 250phx;
viewport-height: 800phx;
smooth-scroll: false;

t1 := TouchArea {
height: 50phx;
Expand All @@ -27,6 +28,7 @@ TestCase := Window {
x: 250phx;
width: 250phx;
viewport-width: 800phx;
smooth-scroll: false;
y: 0;
height: 300phx;

Expand Down
3 changes: 3 additions & 0 deletions tests/cases/elements/flickable_in_flickable.slint
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ TestCase := Window {
height: parent.height - 20px;
viewport_width: width;
viewport_height: 980px;
smooth-scroll: false;

inner := Flickable {
viewport_width: 1500px;
smooth-scroll: false;

Rectangle {
background: @radial-gradient(circle, yellow, blue, red, green);
}
Expand Down
1 change: 1 addition & 0 deletions tests/cases/elements/flickable_stay_in_bounds.slint
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export component TestCase inherits Window {
height: 300phx;
viewport-height: 800phx;
viewport-width: 400phx;
smooth-scroll: false;
}

in-out property fli_width <=> fli.width;
Expand Down
2 changes: 2 additions & 0 deletions tests/cases/elements/listview-millions.slint
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export component TestCase inherits Window {
in-out property <length> viewport-y <=> lv.viewport-y;

lv := ListView {
smooth-scroll: false;

for _[num] in 2130000000: Rectangle {
height: 20px;
border-width: 1px;
Expand Down
2 changes: 2 additions & 0 deletions tests/cases/elements/listview.slint
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ TestCase := Window {


listview := ListView {
smooth-scroll: false;

for data in [
{ text: "Blue", color: #0000ff, bg: #eeeeee},
{ text: "Red", color: #ff0000, bg: #eeeeee},
Expand Down
Loading