bezel-ui 0.2.21

SwiftUI-flavored components for gpui — popovers, menus, buttons, toggles, and loaders
Documentation
use gpui::{
    Context, Modifiers, MouseButton, Render, ScrollDelta, ScrollWheelEvent, TestAppContext,
    VisualTestContext, Window, div, point, prelude::*, px, size,
};
use ui::{AppExt as _, scroll::Visibility, tabs};

struct Host {
    strip: tabs::Strip<&'static str>,
    reorder: tabs::Reorder<&'static str>,
    sortable: bool,
    changes: usize,
    outside: Vec<tabs::OutsideDrop<&'static str>>,
}

impl Render for Host {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let theme = theme::Theme::of(cx);
        let tabs: Vec<_> = self
            .strip
            .tabs()
            .iter()
            .map(|&id| {
                (
                    id,
                    tabs::tab(theme, id, tabs::Label::new(id), tabs::State::Resting)
                        .w(px(100.))
                        .debug_selector(move || id.into()),
                )
            })
            .collect();
        let bar = if self.sortable {
            self.reorder
                .bar("strip", &self.strip, tabs)
                .on_reorder(cx.listener(|view, event: &tabs::Move, _, cx| {
                    view.strip.reorder(event.from, event.to);
                    view.changes += 1;
                    cx.notify();
                }))
                .on_drop_outside(cx.listener(
                    |view, event: &tabs::OutsideDrop<&'static str>, _, _| {
                        view.outside.push(event.clone())
                    },
                ))
                .into_any_element()
        } else {
            tabs::bar("strip")
                .children(tabs.into_iter().map(|(_, tab)| tab))
                .into_any_element()
        };
        div().w_full().child(bar)
    }
}

fn open(sortable: bool, cx: &mut TestAppContext) -> (gpui::Entity<Host>, VisualTestContext) {
    cx.update(|cx| {
        theme::Theme::install(theme::Appearance::Dark, cx);
        cx.set_scrollbar_visibility(Visibility::Always);
    });
    let window = cx.add_window(|_, _| Host {
        strip: ["a", "b", "c"].into_iter().collect(),
        reorder: tabs::Reorder::new(),
        sortable,
        changes: 0,
        outside: Vec::new(),
    });
    let view = window.root(cx).unwrap();
    let mut visual = VisualTestContext::from_window(window.into(), cx);
    visual.simulate_resize(size(px(180.), px(140.)));
    visual.run_until_parked();
    visual.update(|window, _| window.refresh());
    visual.run_until_parked();
    (view, visual)
}

fn wheel(cx: &mut VisualTestContext) {
    cx.simulate_event(ScrollWheelEvent {
        position: point(px(90.), px(10.)),
        delta: ScrollDelta::Pixels(point(px(-40.), px(0.))),
        modifiers: Default::default(),
        touch_phase: Default::default(),
    });
    cx.run_until_parked();
}

#[gpui::test]
fn an_overflowing_strip_scrolls_without_a_scrollbar(cx: &mut TestAppContext) {
    let (_, mut cx) = open(false, cx);
    assert!(cx.debug_bounds("tab-scrollbar-thumb").is_none());
    let before = cx.debug_bounds("a").unwrap();
    wheel(&mut cx);
    let scrolled = cx.debug_bounds("a").unwrap();
    assert!(scrolled.left() < before.left());
    assert_eq!(scrolled.size.height, before.size.height);
}

#[gpui::test]
fn reorder_bar_scrolls_without_reordering_and_forwards_outside_coordinates(
    cx: &mut TestAppContext,
) {
    let (view, mut cx) = open(true, cx);
    wheel(&mut cx);
    assert!(cx.debug_bounds("a").unwrap().left() < px(0.));
    assert_eq!(cx.update(|_, cx| view.read(cx).changes), 0);
    let source = cx.debug_bounds("b").unwrap().center();
    cx.simulate_mouse_down(source, MouseButton::Left, Modifiers::default());
    let end = point(px(130.), px(100.));
    cx.simulate_mouse_move(end, MouseButton::Left, Modifiers::default());
    cx.simulate_mouse_up(end, MouseButton::Left, Modifiers::default());
    cx.run_until_parked();
    cx.update(|_, cx| {
        let view = view.read(cx);
        assert_eq!(view.outside.len(), 1);
        assert_eq!(view.outside[0].id, "b");
        assert_eq!(view.outside[0].position, end);
    });
}