use std::time::Duration;
use freya::prelude::*;
use freya_testing::prelude::*;
#[test]
fn scroll_view_auto_hide_scrollbars() {
for auto_hide_scrollbars in [true, false] {
let mut test = launch_test(move || {
ScrollView::new()
.auto_hide_scrollbars(auto_hide_scrollbars)
.scrollbar(move |context: ScrollBarContext| {
assert_eq!(context.auto_hide_scrollbars, auto_hide_scrollbars);
ScrollBar::new(context).into()
})
.child(rect().width(Size::px(1000.)).height(Size::px(1000.)))
});
test.sync_and_update();
let scrollbars = test.find_many(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.relative_layer == 999.into())
.map(|_| node)
});
assert_eq!(scrollbars.len(), 2);
for scrollbar in &scrollbars {
assert!(scrollbar.layout().area.width() > 0.);
assert!(scrollbar.layout().area.height() > 0.);
}
test.poll(Duration::from_millis(20), Duration::from_millis(1000));
for scrollbar in &scrollbars {
assert_eq!(scrollbar.layout().area.width() > 0., !auto_hide_scrollbars);
assert_eq!(scrollbar.layout().area.height() > 0., !auto_hide_scrollbars);
assert_eq!(scrollbar.children().is_empty(), auto_hide_scrollbars);
}
}
}
#[test]
fn scroll_view_mixed_auto_hide_scrollbars() {
fn app() -> impl IntoElement {
rect()
.child(
ScrollView::new().height(Size::percent(50.)).child(
rect().spacing(6.).children(
(0..30).map(|_| rect().width(Size::fill()).height(Size::px(80.))),
),
),
)
.child(
ScrollView::new()
.auto_hide_scrollbars(false)
.height(Size::percent(50.))
.child(rect().horizontal().spacing(6.).children(
(0..30).map(|_| rect().width(Size::px(80.)).height(Size::fill())),
)),
)
}
let mut test = launch_test(app);
test.sync_and_update();
let scrollbars = test.find_many(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.relative_layer == 999.into())
.map(|_| node)
});
assert_eq!(scrollbars.len(), 2);
test.move_cursor((495., 20.));
test.sync_and_update();
test.poll(Duration::from_millis(20), Duration::from_secs(2));
for scrollbar in &scrollbars {
assert!(scrollbar.layout().area.width() > 0.);
assert!(!scrollbar.children().is_empty());
}
test.move_cursor((100., 100.));
test.sync_and_update();
test.poll(Duration::from_millis(20), Duration::from_secs(2));
let visible_scrollbars = scrollbars
.iter()
.filter(|scrollbar| scrollbar.layout().area.width() > 0.)
.collect::<Vec<_>>();
for scrollbar in &scrollbars {
if scrollbar.layout().area.width() == 0. {
assert!(scrollbar.children().is_empty());
}
}
assert_eq!(visible_scrollbars.len(), 1);
assert!(visible_scrollbars[0].layout().area.width() > 20.);
assert_eq!(visible_scrollbars[0].layout().area.height(), 20.);
}
#[test]
pub fn scroll_view_wheel() {
fn scroll_view_wheel_app() -> impl IntoElement {
ScrollView::new()
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
}
let mut test = launch_test(scroll_view_wheel_app);
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let content = scrollview.children()[0].children()[0].children();
assert!(content[0].is_visible()); assert!(content[1].is_visible()); assert!(content[2].is_visible()); assert!(!content[3].is_visible());
test.scroll((5., 5.), (0., -300.));
assert!(!content[0].is_visible()); assert!(content[1].is_visible()); assert!(content[2].is_visible()); assert!(content[3].is_visible()); }
#[test]
pub fn scroll_view_smooth_scrolling() {
fn scroll_view_smooth_scrolling_app() -> impl IntoElement {
ScrollView::new()
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
}
let mut test = launch_test(scroll_view_smooth_scrolling_app);
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let content = scrollview.children()[0].children()[0].children();
test.send_event(PlatformEvent::Wheel {
name: WheelEventName::Wheel,
scroll: (0., -300.).into(),
cursor: (5., 5.).into(),
source: WheelSource::Line,
phase: TouchPhase::Moved,
});
test.sync_and_update();
assert!(content[0].is_visible());
assert!(!content[3].is_visible());
test.poll(Duration::from_millis(16), Duration::from_secs(1));
assert!(!content[0].is_visible());
assert!(content[3].is_visible());
}
#[test]
pub fn scroll_view_hover_updates_on_scroll() {
fn scroll_view_hover_app() -> impl IntoElement {
let mut hovered = use_state(|| None::<usize>);
rect()
.child(
label()
.height(Size::px(100.))
.text(format!("hovered {:?}", hovered())),
)
.child(
ScrollView::new()
.height(Size::px(400.))
.children((0..4).map(|i| {
rect()
.key(i)
.height(Size::px(200.))
.width(Size::px(200.))
.on_pointer_enter(move |_| hovered.set(Some(i)))
})),
)
}
let mut test = launch_test(scroll_view_hover_app);
let hovered_label = |test: &TestingRunner| {
test.find(|_, element| Label::try_downcast(element).map(|label| label.text.to_string()))
.unwrap()
};
assert_eq!(hovered_label(&test), "hovered None");
test.move_cursor((100., 350.));
test.sync_and_update();
assert_eq!(hovered_label(&test), "hovered Some(1)");
test.scroll((100., 350.), (0., -300.));
assert_eq!(hovered_label(&test), "hovered Some(2)");
}
#[test]
pub fn scroll_view_scrollbar() {
fn scroll_view_scrollbar_app() -> impl IntoElement {
ScrollView::new()
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
}
let mut test = launch_test(scroll_view_scrollbar_app);
test.animation_clock().disable();
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let content = scrollview.children()[0].children()[0].children();
assert!(content[0].is_visible()); assert!(content[1].is_visible()); assert!(content[2].is_visible()); assert!(!content[3].is_visible());
test.move_cursor((495., 20.));
test.sync_and_update();
test.press_cursor((495., 20.));
test.sync_and_update();
test.move_cursor((495., 320.));
test.sync_and_update();
test.release_cursor((495., 320.));
test.sync_and_update();
assert!(!content[0].is_visible()); assert!(content[1].is_visible()); assert!(content[2].is_visible()); assert!(content[3].is_visible());
for _ in 0..5 {
test.press_key(Key::Named(NamedKey::ArrowUp));
test.poll_n(Duration::from_millis(1), 5);
}
assert!(content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(!content[3].is_visible());
test.press_key(Key::Named(NamedKey::End));
test.poll_n(Duration::from_millis(1), 5);
assert!(!content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(content[3].is_visible());
}
#[test]
pub fn scroll_view_drag_scrolling() {
fn scroll_view_drag_scrolling_app() -> impl IntoElement {
ScrollView::new()
.drag_scrolling(true)
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
}
let mut test = launch_test(scroll_view_drag_scrolling_app);
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let content = scrollview.children()[0].children()[0].children();
assert!(content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(!content[3].is_visible());
test.press_touch((100., 400.));
test.move_touch((100., 100.));
test.release_touch((100., 100.));
assert!(!content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(content[3].is_visible());
}
#[test]
pub fn scroll_view_drag_scrolling_release_stops() {
fn scroll_view_drag_release_app() -> impl IntoElement {
ScrollView::new()
.drag_scrolling(true)
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
}
let mut test = launch_test(scroll_view_drag_release_app);
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let content = scrollview.children()[0].children()[0].children();
test.press_touch((100., 300.));
test.move_touch((100., 200.));
test.release_touch((100., 200.));
assert!(content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(!content[3].is_visible());
test.move_touch((100., 50.));
assert!(content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(!content[3].is_visible());
}
#[test]
pub fn scroll_view_drag_scrolling_horizontal() {
fn scroll_view_drag_horizontal_app() -> impl IntoElement {
ScrollView::new()
.drag_scrolling(true)
.direction(Direction::Horizontal)
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
.child(rect().height(Size::px(200.)).width(Size::px(200.)))
}
let mut test = launch_test(scroll_view_drag_horizontal_app);
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let content = scrollview.children()[0].children()[0].children();
assert!(content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(!content[3].is_visible());
test.press_touch((400., 100.));
test.move_touch((100., 100.));
test.release_touch((100., 100.));
assert!(!content[0].is_visible());
assert!(content[1].is_visible());
assert!(content[2].is_visible());
assert!(content[3].is_visible());
}
#[test]
pub fn scroll_view_scrollbar_smaller_than_thumb() {
fn small_scroll_view_app() -> impl IntoElement {
rect()
.height(Size::px(48.))
.width(Size::px(200.))
.child(ScrollView::new().children((0..30).map(|i| {
label()
.key(i)
.height(Size::px(20.))
.text(format!("Row {i}"))
})))
}
let mut test = launch_test(small_scroll_view_app);
test.sync_and_update();
test.move_cursor((100., 24.));
test.sync_and_update();
test.scroll((100., 24.), (0., -20.));
test.sync_and_update();
let scrollview = test
.find(|node, element| {
Rect::try_downcast(element)
.filter(|rect| rect.accessibility.builder.role() == AccessibilityRole::ScrollView)
.map(move |_| node)
})
.unwrap();
let scrolled_before = scrollview.children()[0].children()[0].children()[0]
.layout()
.area
.min_y();
test.move_cursor((192., 5.));
test.sync_and_update();
test.press_cursor((192., 5.));
test.sync_and_update();
test.move_cursor((192., 30.));
test.sync_and_update();
test.release_cursor((192., 30.));
test.sync_and_update();
let scrolled_after = scrollview.children()[0].children()[0].children()[0]
.layout()
.area
.min_y();
assert_eq!(scrolled_before, scrolled_after);
}