add layout crate with resize handle
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
Axis, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _,
|
||||
Pixels, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px,
|
||||
};
|
||||
use gpui_native_layout::resize_handle::ResizeHandle;
|
||||
use gpui_native_theme::Theme;
|
||||
|
||||
type DeltaSink = Rc<RefCell<Vec<Pixels>>>;
|
||||
type ResetSink = Rc<RefCell<usize>>;
|
||||
|
||||
struct Harness {
|
||||
deltas: Rc<RefCell<Vec<Pixels>>>,
|
||||
resets: Rc<RefCell<usize>>,
|
||||
axis: Axis,
|
||||
disabled: bool,
|
||||
}
|
||||
|
||||
impl Render for Harness {
|
||||
fn render(
|
||||
&mut self,
|
||||
_window: &mut Window,
|
||||
_cx: &mut gpui::Context<Self>,
|
||||
) -> impl gpui::IntoElement {
|
||||
let deltas = self.deltas.clone();
|
||||
let resets = self.resets.clone();
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.child(div().w(px(120.)).h_full())
|
||||
.child(
|
||||
ResizeHandle::new("test-handle")
|
||||
.axis(self.axis)
|
||||
.disabled(self.disabled)
|
||||
.on_resize(move |delta, _, _| deltas.borrow_mut().push(delta))
|
||||
.on_reset(move |_, _| *resets.borrow_mut() += 1),
|
||||
)
|
||||
.child(div().flex_1().h_full())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_window(
|
||||
axis: Axis,
|
||||
disabled: bool,
|
||||
) -> (TestAppContext, DeltaSink, ResetSink, VisualTestContext) {
|
||||
let mut cx = TestAppContext::single();
|
||||
cx.update(Theme::init);
|
||||
let deltas = Rc::new(RefCell::new(Vec::new()));
|
||||
let resets = Rc::new(RefCell::new(0usize));
|
||||
let deltas_for_window = deltas.clone();
|
||||
let resets_for_window = resets.clone();
|
||||
let window = cx.add_window(move |_, _| Harness {
|
||||
deltas: deltas_for_window,
|
||||
resets: resets_for_window,
|
||||
axis,
|
||||
disabled,
|
||||
});
|
||||
let mut visual = VisualTestContext::from_window(window.into(), &cx);
|
||||
visual.update(|window, cx| {
|
||||
window.draw(cx).clear(cx);
|
||||
});
|
||||
(cx, deltas, resets, visual)
|
||||
}
|
||||
|
||||
fn handle_center(visual: &mut VisualTestContext) -> gpui::Point<gpui::Pixels> {
|
||||
let bounds = visual
|
||||
.debug_bounds("resize-handle")
|
||||
.expect("resize handle missing");
|
||||
point(bounds.center().x, bounds.center().y)
|
||||
}
|
||||
|
||||
fn click(visual: &mut VisualTestContext, position: gpui::Point<gpui::Pixels>) {
|
||||
visual.simulate_mouse_down(position, MouseButton::Left, Modifiers::default());
|
||||
visual.simulate_mouse_up(position, MouseButton::Left, Modifiers::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dragging_reports_signed_deltas() {
|
||||
let (_cx, deltas, _resets, mut visual) = handle_window(Axis::Horizontal, false);
|
||||
let start = handle_center(&mut visual);
|
||||
|
||||
visual.simulate_mouse_down(start, MouseButton::Left, Modifiers::default());
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(start.x + px(30.), start.y),
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(start.x + px(10.), start.y),
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
visual.simulate_mouse_up(start, MouseButton::Left, Modifiers::default());
|
||||
|
||||
assert_eq!(*deltas.borrow(), vec![px(30.), px(-20.)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical_seams_report_y_deltas() {
|
||||
let (_cx, deltas, _resets, mut visual) = handle_window(Axis::Vertical, false);
|
||||
let start = handle_center(&mut visual);
|
||||
|
||||
visual.simulate_mouse_down(start, MouseButton::Left, Modifiers::default());
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(start.x, start.y + px(40.)),
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
visual.simulate_mouse_up(start, MouseButton::Left, Modifiers::default());
|
||||
|
||||
assert_eq!(*deltas.borrow(), vec![px(40.)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moves_without_a_drag_report_nothing() {
|
||||
let (_cx, deltas, _resets, mut visual) = handle_window(Axis::Horizontal, false);
|
||||
let start = handle_center(&mut visual);
|
||||
|
||||
// Hovering and moving across the seam without pressing does nothing.
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(start.x + px(30.), start.y),
|
||||
pressed_button: None,
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
// A press-and-move that started elsewhere must not reach the handle either.
|
||||
visual.simulate_mouse_down(
|
||||
point(px(20.), px(20.)),
|
||||
MouseButton::Left,
|
||||
Modifiers::default(),
|
||||
);
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(px(50.), px(20.)),
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
|
||||
assert!(deltas.borrow().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrow_keys_move_the_seam_when_focused() {
|
||||
let (_cx, deltas, _resets, mut visual) = handle_window(Axis::Horizontal, false);
|
||||
let center = handle_center(&mut visual);
|
||||
click(&mut visual, center);
|
||||
|
||||
visual.simulate_keystrokes("left");
|
||||
visual.simulate_keystrokes("right");
|
||||
assert_eq!(*deltas.borrow(), vec![px(-32.), px(32.)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_click_resets_and_starts_no_drag() {
|
||||
let (_cx, deltas, resets, mut visual) = handle_window(Axis::Horizontal, false);
|
||||
let center = handle_center(&mut visual);
|
||||
|
||||
for click_count in [1usize, 2] {
|
||||
visual.simulate_event(MouseDownEvent {
|
||||
position: center,
|
||||
click_count,
|
||||
modifiers: Modifiers::default(),
|
||||
button: MouseButton::Left,
|
||||
first_mouse: false,
|
||||
});
|
||||
visual.simulate_event(MouseUpEvent {
|
||||
position: center,
|
||||
click_count,
|
||||
modifiers: Modifiers::default(),
|
||||
button: MouseButton::Left,
|
||||
});
|
||||
}
|
||||
assert_eq!(*resets.borrow(), 1);
|
||||
|
||||
// The double click must not leave a drag behind.
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(center.x + px(20.), center.y),
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
assert!(deltas.borrow().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disabled_handle_ignores_drag_and_keys() {
|
||||
let (_cx, deltas, resets, mut visual) = handle_window(Axis::Horizontal, true);
|
||||
let center = handle_center(&mut visual);
|
||||
|
||||
visual.simulate_mouse_down(center, MouseButton::Left, Modifiers::default());
|
||||
visual.simulate_event(MouseMoveEvent {
|
||||
position: point(center.x + px(30.), center.y),
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
});
|
||||
visual.simulate_mouse_up(center, MouseButton::Left, Modifiers::default());
|
||||
visual.simulate_keystrokes("left");
|
||||
|
||||
assert!(deltas.borrow().is_empty());
|
||||
assert_eq!(*resets.borrow(), 0);
|
||||
}
|
||||
Reference in New Issue
Block a user