layout: make strips the titlebar with window controls
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
//! The titlebar duties: traffic-light pads and window controls, asserted per
|
||||
//! platform — macOS draws only pads, Windows/Linux only draw their controls.
|
||||
|
||||
use gpui::{
|
||||
AppContext as _, Entity, IntoElement, ParentElement as _, Pixels, Render, Styled as _,
|
||||
TestAppContext, VisualTestContext, Window, div, px,
|
||||
};
|
||||
use gpui_native_layout::dock::{Dock, DockState, PanelDescriptor};
|
||||
use gpui_native_layout::window_controls::TRAFFIC_LIGHT_PAD;
|
||||
use gpui_native_theme::Theme;
|
||||
|
||||
struct Panel {
|
||||
tag: &'static str,
|
||||
}
|
||||
|
||||
impl Render for Panel {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context<Self>) -> impl IntoElement {
|
||||
div().size_full().child(self.tag)
|
||||
}
|
||||
}
|
||||
|
||||
fn descriptor(tag: &'static str) -> PanelDescriptor {
|
||||
PanelDescriptor::new(tag, tag, move |_, cx| cx.new(|_| Panel { tag }).into())
|
||||
}
|
||||
|
||||
struct Harness {
|
||||
dock: Entity<DockState>,
|
||||
window_controls: bool,
|
||||
traffic_light_pad: bool,
|
||||
}
|
||||
|
||||
impl Render for Harness {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context<Self>) -> impl IntoElement {
|
||||
div().size_full().child(
|
||||
Dock::new("test-dock", self.dock.clone())
|
||||
.window_controls(self.window_controls)
|
||||
.traffic_light_pad(self.traffic_light_pad),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn dock_window(
|
||||
window_controls: bool,
|
||||
traffic_light_pad: bool,
|
||||
cx: &mut TestAppContext,
|
||||
) -> (Entity<DockState>, VisualTestContext) {
|
||||
cx.update(Theme::init);
|
||||
let dock = cx.update(DockState::new);
|
||||
let dock_for_window = dock.clone();
|
||||
let window = cx.add_window(move |_, _| Harness {
|
||||
dock: dock_for_window,
|
||||
window_controls,
|
||||
traffic_light_pad,
|
||||
});
|
||||
let mut visual = VisualTestContext::from_window(window.into(), cx);
|
||||
visual.update(|window, cx| window.draw(cx).clear(cx));
|
||||
(dock, visual)
|
||||
}
|
||||
|
||||
fn bounds(visual: &mut VisualTestContext, selector: &str) -> Option<gpui::Bounds<Pixels>> {
|
||||
visual.debug_bounds(Box::leak(selector.to_string().into_boxed_str()))
|
||||
}
|
||||
|
||||
fn open(visual: &mut VisualTestContext, dock: &Entity<DockState>, tag: &'static str) {
|
||||
visual.update(|window, cx| {
|
||||
dock.update(cx, |dock, cx| dock.open_panel(tag, window, cx));
|
||||
});
|
||||
visual.update(|window, cx| window.draw(cx).clear(cx));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn the_pill_bar_pads_for_traffic_lights_when_it_leads_the_window() {
|
||||
let mut cx = TestAppContext::single();
|
||||
let (dock, mut visual) = dock_window(false, false, &mut cx);
|
||||
cx.update(|cx| {
|
||||
dock.update(cx, |dock, cx| {
|
||||
dock.register_panel(descriptor("overview"), cx)
|
||||
});
|
||||
});
|
||||
open(&mut visual, &dock, "overview");
|
||||
|
||||
let bar = bounds(&mut visual, "test-dock-pane-0-bar").expect("pill bar missing");
|
||||
let tab = bounds(&mut visual, "test-dock-pane-0-tabs-tab-overview").expect("tab missing");
|
||||
let bare_inset = tab.left() - bar.left();
|
||||
|
||||
let (dock, mut visual) = dock_window(false, true, &mut cx);
|
||||
let _ = &dock;
|
||||
cx.update(|cx| {
|
||||
dock.update(cx, |dock, cx| {
|
||||
dock.register_panel(descriptor("overview"), cx)
|
||||
});
|
||||
});
|
||||
open(&mut visual, &dock, "overview");
|
||||
|
||||
let padded_bar = bounds(&mut visual, "test-dock-pane-0-bar").expect("padded bar missing");
|
||||
let padded_tab =
|
||||
bounds(&mut visual, "test-dock-pane-0-tabs-tab-overview").expect("tab missing");
|
||||
let padded_inset = padded_tab.left() - padded_bar.left();
|
||||
|
||||
assert!(
|
||||
padded_inset - bare_inset >= px(TRAFFIC_LIGHT_PAD) - px(1.),
|
||||
"the pad must clear the traffic lights (bare {bare_inset:?}, padded {padded_inset:?})"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_controls_render_only_when_the_flag_is_set() {
|
||||
let mut cx = TestAppContext::single();
|
||||
let (dock, mut visual) = dock_window(false, false, &mut cx);
|
||||
cx.update(|cx| {
|
||||
dock.update(cx, |dock, cx| {
|
||||
dock.register_panel(descriptor("overview"), cx)
|
||||
});
|
||||
});
|
||||
open(&mut visual, &dock, "overview");
|
||||
|
||||
// macOS: the system traffic lights own the controls — nothing renders;
|
||||
// the pad test above covers that mode.
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
assert!(
|
||||
bounds(&mut visual, "window-control-close").is_none(),
|
||||
"controls must not render without the flag"
|
||||
);
|
||||
#[cfg(target_os = "macos")]
|
||||
let _ = (&dock, &mut visual);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[test]
|
||||
fn the_flag_hosts_the_platform_controls_in_the_bar() {
|
||||
let mut cx = TestAppContext::single();
|
||||
let (dock, mut visual) = dock_window(true, false, &mut cx);
|
||||
cx.update(|cx| {
|
||||
dock.update(cx, |dock, cx| {
|
||||
dock.register_panel(descriptor("overview"), cx)
|
||||
});
|
||||
});
|
||||
open(&mut visual, &dock, "overview");
|
||||
|
||||
// The maximize control exists once; its restore flip follows the
|
||||
// window's maximized state, not a separate control.
|
||||
assert!(bounds(&mut visual, "window-control-close").is_some());
|
||||
assert!(bounds(&mut visual, "window-control-maximize").is_some());
|
||||
}
|
||||
Reference in New Issue
Block a user