Canvas · the wheel scrolls as scroll boxes do; pans hold at the press
A wheel without a modifier now slides the content by its delta, as gpui's scroll boxes do; it moved the other way. The wheel reads the live viewport and skips an end with no delta, and a drag pans from the press by the pointer's whole way with the viewport held at the press, so events that land in one frame no longer overwrite each other. Tests: a plain wheel moves the content up with it; three wheels and an empty end add up, and four moves pan their sum; a small frame fits at a zoom of one.
This commit is contained in:
1 parent
e5a0c19611
commit
50259aff77
4 files changed
+73
-15
No files matched your search
@@ -373,3 +373,4 @@ Per-component lines live in `tasks/`. Tags there:
|
||||
| T31c | 2 | PASS | |
|
||||
| T31d | 1 | FAIL | a focused segment past the strip's fold stayed hidden; the tracker lost focus as Start and Stop swapped; Stop and Skip went untested; percent did not refuse content |
|
||||
| T31d | 2 | PASS | |
|
||||
| T32a | 1 | FAIL | the plain wheel panned against every scroll box; the handlers panned from the render's viewport, so events within a frame overwrote each other; the fit's cap at one went untested |
|
||||
+19
-15
@@ -22,11 +22,11 @@ struct Pan {
|
||||
owner: EntityId,
|
||||
}
|
||||
|
||||
/// The canvas's box and the pointer of a pan under way.
|
||||
/// The canvas's box, and the pointer and viewport at the press of a pan under way.
|
||||
#[derive(Default)]
|
||||
struct Plane {
|
||||
bounds: Bounds<Pixels>,
|
||||
grip: Option<Point<Pixels>>,
|
||||
grip: Option<(Point<Pixels>, Viewport)>,
|
||||
}
|
||||
|
||||
/// The view pixels of a canvas frame.
|
||||
@@ -400,6 +400,7 @@ impl RenderOnce for InfiniteCanvas {
|
||||
let owner = plane.entity_id();
|
||||
let (wheel, pan, drop) = (set.clone(), set, plane.clone());
|
||||
let (scrolled, dragged, pressed) = (plane.clone(), plane.clone(), plane);
|
||||
let (live, held) = (local.clone(), local.clone());
|
||||
div()
|
||||
.id(id)
|
||||
.relative()
|
||||
@@ -416,34 +417,37 @@ impl RenderOnce for InfiniteCanvas {
|
||||
(lines.x * line, lines.y * line)
|
||||
}
|
||||
};
|
||||
if delta == (0.0, 0.0) {
|
||||
return;
|
||||
}
|
||||
let pointer = event.position - bounds.origin;
|
||||
let about = (f32::from(pointer.x), f32::from(pointer.y));
|
||||
let now = view;
|
||||
let now = live.read(cx).value;
|
||||
let next = match event.modifiers.platform || event.modifiers.control {
|
||||
true => now.zoomed((-delta.1 / 240.0).exp(), about),
|
||||
false => now.panned((-delta.0, -delta.1)),
|
||||
false => now.panned(delta),
|
||||
};
|
||||
cx.stop_propagation();
|
||||
wheel(next, window, cx);
|
||||
})
|
||||
.on_mouse_down(gpui::MouseButton::Left, move |event, _, cx| {
|
||||
pressed.update(cx, |plane, _| plane.grip = Some(event.position))
|
||||
let now = held.read(cx).value;
|
||||
pressed.update(cx, |plane, _| plane.grip = Some((event.position, now)))
|
||||
})
|
||||
.on_drag(Pan { owner }, |_, _, _, cx| cx.new(|_| EmptyView))
|
||||
.on_drag_move::<Pan>(move |event, window, cx| {
|
||||
if event.drag(cx).owner != owner {
|
||||
return;
|
||||
}
|
||||
let pointer = event.event.position;
|
||||
let last = dragged.update(cx, |plane, _| plane.grip.replace(pointer));
|
||||
if let Some(last) = last {
|
||||
let delta = pointer - last;
|
||||
pan(
|
||||
view.panned((f32::from(delta.x), f32::from(delta.y))),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
let Some((press, start)) = dragged.read(cx).grip else {
|
||||
return;
|
||||
};
|
||||
let way = event.event.position - press;
|
||||
pan(
|
||||
start.panned((f32::from(way.x), f32::from(way.y))),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.on_drop(move |_: &Pan, _, cx| drop.update(cx, |plane, _| plane.grip = None))
|
||||
.child(painted)
|
||||
|
||||
@@ -155,3 +155,53 @@ fn a_press_on_the_map_centers_the_view_there(cx: &mut TestAppContext) {
|
||||
"the view centers on the point pressed: {middle:?}"
|
||||
);
|
||||
}
|
||||
|
||||
fn wheel(at: (f32, f32), dy: f32, modifiers: Modifiers) -> ScrollWheelEvent {
|
||||
ScrollWheelEvent {
|
||||
position: point(px(at.0), px(at.1)),
|
||||
delta: ScrollDelta::Pixels(point(px(0.0), px(dy))),
|
||||
modifiers,
|
||||
touch_phase: TouchPhase::Moved,
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_plain_wheel_moves_the_content_as_a_scroll_box_does(cx: &mut TestAppContext) {
|
||||
let (host, cx) = stage(plane, cx);
|
||||
cx.simulate_event(wheel((100.0, 50.0), -50.0, Modifiers::none()));
|
||||
settle(cx);
|
||||
let view = *heard(&host, cx).last().expect("a scroll");
|
||||
assert_eq!(
|
||||
view.to_view((0.0, 0.0)),
|
||||
(0.0, -50.0),
|
||||
"the content went up with the wheel"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn wheels_and_moves_add_up(cx: &mut TestAppContext) {
|
||||
let (host, cx) = stage(plane, cx);
|
||||
for dy in [-10.0, -10.0, -10.0, 0.0] {
|
||||
cx.simulate_event(wheel((100.0, 50.0), dy, Modifiers::none()));
|
||||
}
|
||||
settle(cx);
|
||||
let view = *heard(&host, cx).last().expect("a scroll");
|
||||
assert_eq!(
|
||||
view.y, 30.0,
|
||||
"three wheels add up, and an end that moves nothing leaves it"
|
||||
);
|
||||
let at = |x: f32, y: f32| point(px(x), px(y));
|
||||
cx.simulate_mouse_move(at(200.0, 150.0), None, Modifiers::none());
|
||||
cx.simulate_mouse_down(at(200.0, 150.0), MouseButton::Left, Modifiers::none());
|
||||
for x in [190.0, 180.0, 170.0, 160.0] {
|
||||
cx.simulate_mouse_move(at(x, 150.0), Some(MouseButton::Left), Modifiers::none());
|
||||
}
|
||||
cx.simulate_mouse_up(at(160.0, 150.0), MouseButton::Left, Modifiers::none());
|
||||
settle(cx);
|
||||
let view = *heard(&host, cx).last().expect("a pan");
|
||||
assert_eq!(
|
||||
(view.x, view.y),
|
||||
(40.0, 30.0),
|
||||
"the drag pans from the press by the pointer's whole way"
|
||||
);
|
||||
}
|
||||
@@ -159,6 +159,9 @@ mod tests {
|
||||
let view = Viewport::fitting(Frame::new(0.0, 0.0, 800.0, 400.0), (440.0, 440.0), 20.0);
|
||||
assert_eq!(view.zoom, 0.5);
|
||||
assert_eq!(view.to_view((400.0, 200.0)), (220.0, 220.0));
|
||||
let small = Viewport::fitting(Frame::new(0.0, 0.0, 100.0, 50.0), (440.0, 440.0), 20.0);
|
||||
assert_eq!(small.zoom, 1.0, "no nearer than a zoom of one");
|
||||
assert_eq!(small.to_view((50.0, 25.0)), (220.0, 220.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user