Files
2026-11-01 14:01:49 +07:00

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
);
}