use gpui::{
Axis, ScrollDelta, ScrollHandle, ScrollWheelEvent, TestAppContext, VisualTestContext, div,
point, prelude::*, px, size,
};
use ui::scroll::{self, ClaimState};
const WIDTH: f32 = 400.0;
const HEIGHT: f32 = 300.0;
const INNER_BOX: f32 = 100.0;
const INNER_CONTENT: f32 = 400.0;
const SPACER: f32 = 600.0;
const NOTCH: f32 = -40.0;
struct Nested {
outer: ScrollHandle,
inner: ScrollHandle,
claims: bool,
claim: ClaimState,
}
impl gpui::Render for Nested {
fn render(&mut self, _: &mut gpui::Window, _: &mut gpui::Context<Self>) -> impl IntoElement {
let inner = div()
.id("inner")
.w_full()
.h(px(INNER_BOX))
.overflow_y_scroll()
.track_scroll(&self.inner)
.child(div().w_full().h(px(INNER_CONTENT)));
let inner = if self.claims {
scroll::claim_wheel(inner, &self.inner, Axis::Vertical, &self.claim)
} else {
inner
};
div()
.id("outer")
.size_full()
.overflow_y_scroll()
.track_scroll(&self.outer)
.child(inner)
.child(div().w_full().h(px(SPACER)))
}
}
fn open(claims: bool, cx: &mut TestAppContext) -> (gpui::Entity<Nested>, VisualTestContext) {
cx.update(|cx| theme::Theme::install(theme::Appearance::Dark, cx));
let window = cx.add_window(|_, _| Nested {
outer: ScrollHandle::new(),
inner: ScrollHandle::new(),
claims,
claim: ClaimState::new(),
});
let view = window.root(cx).unwrap();
let visual = VisualTestContext::from_window(window.into(), cx);
visual.simulate_resize(size(px(WIDTH), px(HEIGHT)));
visual.run_until_parked();
(view, visual)
}
fn wheel_over_inner(cx: &mut VisualTestContext) {
cx.simulate_event(ScrollWheelEvent {
position: point(px(WIDTH / 2.0), px(INNER_BOX / 2.0)),
delta: ScrollDelta::Pixels(point(px(0.0), px(NOTCH))),
modifiers: Default::default(),
touch_phase: Default::default(),
});
cx.run_until_parked();
}
fn travelled(view: &gpui::Entity<Nested>, cx: &mut VisualTestContext) -> (f32, f32) {
cx.update(|_, cx| {
let view = view.read(cx);
(
f32::from(view.outer.offset().y).abs(),
f32::from(view.inner.offset().y).abs(),
)
})
}
#[gpui::test]
fn a_wheel_over_a_nested_pane_moves_only_that_pane(cx: &mut TestAppContext) {
let (view, mut cx) = open(true, cx);
wheel_over_inner(&mut cx);
let (outer, inner) = travelled(&view, &mut cx);
assert!(inner > 0.0, "the pane under the pointer takes the wheel");
assert_eq!(outer, 0.0, "and the page behind it stays where it was");
}
#[gpui::test]
fn a_nested_pane_at_its_end_passes_the_wheel_on(cx: &mut TestAppContext) {
let (view, mut cx) = open(true, cx);
for _ in 0..(((INNER_CONTENT - INNER_BOX) / -NOTCH) as usize + 1) {
wheel_over_inner(&mut cx);
}
let (outer, inner) = travelled(&view, &mut cx);
assert_eq!(
inner,
INNER_CONTENT - INNER_BOX,
"the inner pane is at its end"
);
assert_eq!(outer, 0.0, "and got there without dragging the page along");
wheel_over_inner(&mut cx);
let (outer, _) = travelled(&view, &mut cx);
assert!(
outer > 0.0,
"with nowhere left to go it hands the wheel to the page, rather than \
stranding it and making the pointer move"
);
}
#[gpui::test]
fn without_the_claim_both_panes_move_at_once(cx: &mut TestAppContext) {
let (view, mut cx) = open(false, cx);
wheel_over_inner(&mut cx);
let (outer, inner) = travelled(&view, &mut cx);
assert!(inner > 0.0 && outer > 0.0, "which is the reported bug");
}