149 lines
4.7 KiB
Rust
149 lines
4.7 KiB
Rust
use std::{cell::RefCell, rc::Rc};
|
|
|
|
use gpui::{
|
|
Context, IntoElement, MouseButton, ParentElement as _, Render, Styled as _, TestAppContext,
|
|
VisualTestContext, Window, div, point, px,
|
|
};
|
|
use gpui_native_core::segmented::Segmented;
|
|
use gpui_native_theme::Theme;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
enum Filter {
|
|
All,
|
|
Popular,
|
|
Recent,
|
|
}
|
|
|
|
const FILTERS: [(Filter, &str); 3] = [
|
|
(Filter::All, "All"),
|
|
(Filter::Popular, "Popular"),
|
|
(Filter::Recent, "Recent"),
|
|
];
|
|
|
|
type Changes = Rc<RefCell<Vec<Filter>>>;
|
|
|
|
struct Harness {
|
|
filter: Filter,
|
|
changes: Changes,
|
|
}
|
|
|
|
impl Render for Harness {
|
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
let changes = self.changes.clone();
|
|
let view = cx.entity();
|
|
div().size_full().p_4().child(
|
|
Segmented::new("filter", self.filter)
|
|
.options(FILTERS)
|
|
.on_change(move |next, _window, cx| {
|
|
changes.borrow_mut().push(next);
|
|
view.update(cx, |harness, cx| {
|
|
harness.filter = next;
|
|
cx.notify();
|
|
});
|
|
}),
|
|
)
|
|
}
|
|
}
|
|
|
|
fn filter_window() -> (TestAppContext, VisualTestContext, Changes) {
|
|
let mut cx = TestAppContext::single();
|
|
cx.update(Theme::init);
|
|
let changes = Rc::new(RefCell::new(Vec::new()));
|
|
let harness_changes = changes.clone();
|
|
let window = cx.add_window(move |_, _| Harness {
|
|
filter: Filter::All,
|
|
changes: harness_changes,
|
|
});
|
|
let mut visual = VisualTestContext::from_window(window.into(), &cx);
|
|
visual.update(|window, cx| {
|
|
window.draw(cx).clear(cx);
|
|
});
|
|
(cx, visual, changes)
|
|
}
|
|
|
|
fn click(visual: &mut VisualTestContext, position: gpui::Point<gpui::Pixels>) {
|
|
visual.simulate_mouse_down(position, MouseButton::Left, gpui::Modifiers::default());
|
|
visual.simulate_mouse_up(position, MouseButton::Left, gpui::Modifiers::default());
|
|
}
|
|
|
|
/// The strip hugs its labels in the `p_4` band: the three segments span
|
|
/// roughly `18..142` horizontally and `17.6..33.9` vertically (macOS's 13 pt
|
|
/// root font), so clicks land near `32` (All), `70` (Popular), and `118`
|
|
/// (Recent).
|
|
#[test]
|
|
fn clicking_a_segment_reports_it() {
|
|
let (_cx, mut visual, changes) = filter_window();
|
|
|
|
click(&mut visual, point(px(70.), px(27.)));
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular]);
|
|
|
|
click(&mut visual, point(px(118.), px(27.)));
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular, Filter::Recent]);
|
|
}
|
|
|
|
#[test]
|
|
fn re_clicking_the_selected_segment_reports_nothing() {
|
|
let (_cx, mut visual, changes) = filter_window();
|
|
|
|
click(&mut visual, point(px(70.), px(27.)));
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular]);
|
|
|
|
click(&mut visual, point(px(70.), px(27.)));
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular]);
|
|
}
|
|
|
|
#[test]
|
|
fn arrows_move_the_selection_when_the_strip_has_focus() {
|
|
let (_cx, mut visual, changes) = filter_window();
|
|
|
|
// Clicking a segment hands the strip the keyboard, like the list.
|
|
click(&mut visual, point(px(32.), px(27.)));
|
|
visual.simulate_keystrokes("right");
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular]);
|
|
|
|
visual.simulate_keystrokes("right");
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular, Filter::Recent]);
|
|
|
|
// The strip never wraps: right on the last segment reports nothing.
|
|
visual.simulate_keystrokes("right");
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular, Filter::Recent]);
|
|
|
|
// Home and end jump to the ends.
|
|
visual.simulate_keystrokes("home");
|
|
assert_eq!(
|
|
*changes.borrow(),
|
|
vec![Filter::Popular, Filter::Recent, Filter::All]
|
|
);
|
|
|
|
visual.simulate_keystrokes("end");
|
|
assert_eq!(
|
|
*changes.borrow(),
|
|
vec![Filter::Popular, Filter::Recent, Filter::All, Filter::Recent]
|
|
);
|
|
}
|
|
|
|
/// The selected plate exists after a choice and settles on the chosen
|
|
/// segment's slot; without it the active background disappears entirely.
|
|
#[test]
|
|
fn the_plate_settles_on_the_chosen_segment() {
|
|
let (_cx, mut visual, changes) = filter_window();
|
|
|
|
click(&mut visual, point(px(70.), px(27.)));
|
|
assert_eq!(*changes.borrow(), vec![Filter::Popular]);
|
|
|
|
// Let the spring settle in real time (it steps on Instant::now()), then
|
|
// check the plate landed in Popular's slot (~58..124).
|
|
std::thread::sleep(std::time::Duration::from_millis(600));
|
|
visual.update(|window, cx| {
|
|
window.draw(cx).clear(cx);
|
|
});
|
|
let plate = visual
|
|
.debug_bounds("segmented-plate")
|
|
.expect("plate missing");
|
|
assert!(
|
|
plate.left() >= px(44.) && plate.left() <= px(94.),
|
|
"plate settled at x {:?}",
|
|
plate.origin
|
|
);
|
|
}
|