Theme · Platform: caption buttons of their own height, titles that give way
Windows caption buttons take the bar's height less its hairline, a new token, instead of their row's, so they keep their height beside caps that wrap below them. A title bar's title is an Ellipsis: on Windows it takes the free width before the buttons and gives way; a centered title sits inset past the lights on both sides. The controls never shrink. Tests hold a long Windows title's close button inside a 280px bar and wrapped caps inside their row.
This commit is contained in:
1 parent
d3cfc71ead
commit
ff1e97132e
5 files changed
+80
-8
No files matched your search
@@ -424,3 +424,4 @@ Per-component lines live in `tasks/`. Tags there:
|
|||||||
| T39a | 3 | PASS | |
|
| T39a | 3 | PASS | |
|
||||||
| T39b | 1 | FAIL | a long theme name pushed its badge past 280px; four-digit colors refused the theme |
|
| T39b | 1 | FAIL | a long theme name pushed its badge past 280px; four-digit colors refused the theme |
|
||||||
| T39b | 2 | PASS | |
|
| T39b | 2 | PASS | |
|
||||||
|
| T39c | 1 | FAIL | Windows caption buttons took their row's height; a long Windows title pushed the buttons past the bar |
|
||||||
@@ -59,7 +59,7 @@ pub fn title_bar(cx: &App) -> impl IntoElement + use<> {
|
|||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.child(code(
|
.child(code(
|
||||||
"TitleBar::new(id) with no style leaves room for the real macOS lights.",
|
"TitleBar::new(id) with no platform leaves room for the real macOS lights.",
|
||||||
cx,
|
cx,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-7
@@ -10,6 +10,7 @@ use smallvec::SmallVec;
|
|||||||
use crate::{
|
use crate::{
|
||||||
primitives::{Icon, IconName},
|
primitives::{Icon, IconName},
|
||||||
theme::{ActiveTheme, ControlSize, IconSize, Platform, TextSize},
|
theme::{ActiveTheme, ControlSize, IconSize, Platform, TextSize},
|
||||||
|
typography::Ellipsis,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Drags the window, and zooms it on double-click.
|
/// Drags the window, and zooms it on double-click.
|
||||||
@@ -174,7 +175,7 @@ fn caption_buttons(
|
|||||||
div()
|
div()
|
||||||
.id(id)
|
.id(id)
|
||||||
.flex()
|
.flex()
|
||||||
.h_full()
|
.flex_none()
|
||||||
.children(buttons.map(|(control, icon, area)| {
|
.children(buttons.map(|(control, icon, area)| {
|
||||||
let on_close = on_close.clone();
|
let on_close = on_close.clone();
|
||||||
let group = SharedString::from(format!("caption-{control:?}"));
|
let group = SharedString::from(format!("caption-{control:?}"));
|
||||||
@@ -189,7 +190,8 @@ fn caption_buttons(
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.w(theme.caption_button_width())
|
.w(theme.caption_button_width())
|
||||||
.h_full()
|
.h(theme.caption_button_height())
|
||||||
|
.debug_selector(move || format!("caption-{control:?}"))
|
||||||
.window_control_area(area)
|
.window_control_area(area)
|
||||||
.hover(|style| style.bg(hover))
|
.hover(|style| style.bg(hover))
|
||||||
.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
|
.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
|
||||||
@@ -357,10 +359,11 @@ impl RenderOnce for TitleBar {
|
|||||||
};
|
};
|
||||||
let title = self.title.map(|title| {
|
let title = self.title.map(|title| {
|
||||||
div()
|
div()
|
||||||
|
.min_w_0()
|
||||||
.text_size(theme.text_size(TextSize::Sm))
|
.text_size(theme.text_size(TextSize::Sm))
|
||||||
.font_weight(FontWeight::MEDIUM)
|
.font_weight(FontWeight::MEDIUM)
|
||||||
.text_color(theme.colors.fg_muted)
|
.text_color(theme.colors.fg_muted)
|
||||||
.child(title)
|
.child(Ellipsis::new(title))
|
||||||
});
|
});
|
||||||
let centered = style != Platform::Windows;
|
let centered = style != Platform::Windows;
|
||||||
let inset = theme.traffic_light_inset();
|
let inset = theme.traffic_light_inset();
|
||||||
@@ -385,12 +388,15 @@ impl RenderOnce for TitleBar {
|
|||||||
};
|
};
|
||||||
let bar = bar.children(self.leading);
|
let bar = bar.children(self.leading);
|
||||||
let bar = match (centered, title) {
|
let bar = match (centered, title) {
|
||||||
(false, Some(title)) => bar.child(title),
|
(false, Some(title)) => bar.child(title.flex_1()),
|
||||||
(_, title) => bar.when_some(title, |bar, title| {
|
(_, title) => bar.when_some(title, |bar, title| {
|
||||||
bar.child(
|
bar.child(
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.absolute()
|
||||||
.inset_0()
|
.top_0()
|
||||||
|
.bottom_0()
|
||||||
|
.left(inset)
|
||||||
|
.right(inset)
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
@@ -398,8 +404,8 @@ impl RenderOnce for TitleBar {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
bar.child(div().flex_1())
|
bar.when(centered, |bar| bar.child(div().flex_1()))
|
||||||
.children(self.actions)
|
.children(self.actions)
|
||||||
.children(trailing)
|
.children(trailing.map(|controls| div().flex_none().child(controls)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,3 +130,63 @@ fn the_themes_platform_draws_the_buttons_and_the_caps(cx: &mut TestAppContext) {
|
|||||||
"Ctrl wider than the command mark"
|
"Ctrl wider than the command mark"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LongTitle;
|
||||||
|
|
||||||
|
impl Render for LongTitle {
|
||||||
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
div().w(px(280.0)).flex().flex_col().child(
|
||||||
|
super::TitleBar::new("bar")
|
||||||
|
.title("Ely — Projects at a narrow width, and more besides")
|
||||||
|
.platform(crate::theme::Platform::Windows),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// At 280px a Windows bar's long title gives way, and its close button ends inside the bar.
|
||||||
|
#[gpui::test]
|
||||||
|
fn a_long_title_leaves_the_caption_buttons_in_the_bar(cx: &mut TestAppContext) {
|
||||||
|
cx.update(Theme::init);
|
||||||
|
let (_, cx) = cx.add_window_view(|_, _| LongTitle);
|
||||||
|
settle(cx);
|
||||||
|
let close = cx.debug_bounds("caption-Close").expect("a close button");
|
||||||
|
assert!(close.right() <= px(280.0), "{close:?} inside the bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wrapped;
|
||||||
|
|
||||||
|
impl Render for Wrapped {
|
||||||
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let windows = crate::theme::Platform::Windows;
|
||||||
|
div()
|
||||||
|
.w(px(280.0))
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.debug_selector(|| "row".into())
|
||||||
|
.flex()
|
||||||
|
.flex_wrap()
|
||||||
|
.items_center()
|
||||||
|
.gap_3()
|
||||||
|
.child(super::WindowControls::new("controls").platform(windows))
|
||||||
|
.child(div().debug_selector(|| "combo".into()).child(
|
||||||
|
crate::typography::KbdCombo::new("secondary-shift-k secondary-shift-s"),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.child(div().h(px(20.0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Caption buttons keep their own height beside caps that wrap below them, so the caps end inside the row.
|
||||||
|
#[gpui::test]
|
||||||
|
fn caption_buttons_keep_their_height_in_a_wrapping_row(cx: &mut TestAppContext) {
|
||||||
|
cx.update(Theme::init);
|
||||||
|
cx.update(|cx| Theme::update(cx, |theme| theme.platform = crate::theme::Platform::Windows));
|
||||||
|
let (_, cx) = cx.add_window_view(|_, _| Wrapped);
|
||||||
|
settle(cx);
|
||||||
|
let row = cx.debug_bounds("row").expect("the row");
|
||||||
|
let combo = cx.debug_bounds("combo").expect("the caps");
|
||||||
|
assert!(combo.top() > row.top(), "the caps wrap below the buttons");
|
||||||
|
assert!(combo.bottom() <= row.bottom(), "{combo:?} inside {row:?}");
|
||||||
|
}
|
||||||
@@ -230,6 +230,11 @@ impl Theme {
|
|||||||
px_to_rems(46.0)
|
px_to_rems(46.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Windows caption button's height: the title bar's, less its bottom hairline.
|
||||||
|
pub fn caption_button_height(&self) -> Pixels {
|
||||||
|
self.titlebar_height().to_pixels(self.base_rem()) - px(1.0)
|
||||||
|
}
|
||||||
|
|
||||||
/// Resize margin of a client-drawn window.
|
/// Resize margin of a client-drawn window.
|
||||||
pub fn window_inset(&self) -> Rems {
|
pub fn window_inset(&self) -> Rems {
|
||||||
px_to_rems(10.0)
|
px_to_rems(10.0)
|
||||||
|
|||||||
Reference in new issue
Block a user