mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-11-06 00:15:06 +00:00
fix(titlebar): the title bar's lead moves with the traffic lights (#1146)
* fix(titlebar): the title bar's lead moves with the traffic lights #1139 moved the lights from 9pt to 16.5pt in, but the 80pt `TITLE_BAR_LEAD` the tab strip, editor header and search band start at stayed put, so the gap after the zoom button shrank from about 17pt to 9.5pt. On macOS the lead is now derived from the lights: their inset, their 54pt span (three 14pt buttons on a 20pt pitch), and the inset again, 87pt. The strip-width calculation that hard-coded 80 uses the constant, the search-band tests use it instead of 80, and a test checks the lights end, with their inset, before the lead. Off macOS nothing changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(titlebar): TitleBar pads the strip by the lead, not its own 80pt gpui-component's TitleBar hard-codes an 80pt left padding on macOS, so raising TITLE_BAR_LEAD to the traffic lights' 87pt left the tab strip at 80 while the strip width, search band and document headers assumed 87. Set the padding from TITLE_BAR_LEAD so they all start at the same x. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
l0ng-ai
parent
d2d3793f50
commit
8c5ea523b3
+8
-1
@@ -298,7 +298,11 @@ pub(crate) fn tile_trailing_inset_sm() -> f32 {
|
||||
(CONTENT_INSET - TILE_PAD_SM).max(TILE_EDGE_GAP)
|
||||
}
|
||||
|
||||
pub(crate) const TITLE_BAR_LEAD: f32 = if cfg!(target_os = "macos") { 80. } else { 12. };
|
||||
pub(crate) const TITLE_BAR_LEAD: f32 = if cfg!(target_os = "macos") {
|
||||
crate::ui::theme::TRAFFIC_LIGHTS_LEAD
|
||||
} else {
|
||||
12.
|
||||
};
|
||||
|
||||
pub(crate) const WINDOW_CONTROLS_W: f32 = if cfg!(target_os = "macos") { 0. } else { 102. };
|
||||
|
||||
@@ -9498,8 +9502,11 @@ impl Render for Tty7App {
|
||||
// the chrome tiles and the docked document's header.
|
||||
let title_bar =
|
||||
if window_controls_drawn(window.is_fullscreen()) || cfg!(target_os = "macos") {
|
||||
// `TitleBar` pads its own fixed 80pt; the lead is ours, since
|
||||
// it follows where the traffic lights are put.
|
||||
TitleBar::new()
|
||||
.h(px(TITLE_BAR_HEIGHT))
|
||||
.pl(px(TITLE_BAR_LEAD))
|
||||
.bg(cx.theme().transparent)
|
||||
.border_color(cx.theme().transparent)
|
||||
.child(strip)
|
||||
|
||||
+13
-10
@@ -775,10 +775,10 @@ const TITLEBAR_SEARCH_CLEAR_GAP: f32 = 8.;
|
||||
/// is drawn inside.
|
||||
///
|
||||
/// The strip does not start at the terminal column's left edge. The title bar
|
||||
/// leaves a lead before it — 80pt on macOS for the traffic lights, whether or
|
||||
/// leaves a lead before it — `TRAFFIC_LIGHTS_LEAD` on macOS, for the traffic lights, whether or
|
||||
/// not the rail is standing in front of them, plus the bar's own inset in
|
||||
/// fullscreen; 12 elsewhere — and a box centred on the strip was centred that
|
||||
/// much too far right by half: 40pt on macOS, which is plainly visible with
|
||||
/// much too far right by half: 40pt on macOS then, which is plainly visible with
|
||||
/// the rail collapsed and the box standing alone in an empty bar (#1033).
|
||||
/// So the band reaches back over the lead, and its left edge is the column's.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -828,7 +828,7 @@ pub(crate) fn search_band(
|
||||
}
|
||||
|
||||
/// How far the title bar's lead reaches before the tab strip starts: the room
|
||||
/// `TitleBar` leaves for the traffic lights on macOS, and the bar's own inset
|
||||
/// we have `TitleBar` leave for the traffic lights on macOS, and the bar's own inset
|
||||
/// that gpui-component adds to it in fullscreen, where the lights are gone but
|
||||
/// the room is not; `TITLE_BAR_LEAD` elsewhere, in both of the rows the strip
|
||||
/// can sit in.
|
||||
@@ -2319,7 +2319,9 @@ impl Tty7App {
|
||||
let document_w = self.document_dock_px(window, cx).unwrap_or(0.);
|
||||
let controls_w = crate::ui::app::window_controls_w(window.is_fullscreen());
|
||||
let strip_w = if cfg!(target_os = "macos") {
|
||||
(window.viewport_size().width - px(80. + panel_w + document_w)).max(px(160.))
|
||||
(window.viewport_size().width
|
||||
- px(crate::ui::app::TITLE_BAR_LEAD + panel_w + document_w))
|
||||
.max(px(160.))
|
||||
} else {
|
||||
(window.viewport_size().width - px(crate::ui::app::TITLE_BAR_LEAD + controls_w))
|
||||
.max(px(140.))
|
||||
@@ -3113,6 +3115,7 @@ mod ssh_host_row_tests {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ui::theme::TRAFFIC_LIGHTS_LEAD;
|
||||
use gpui::TestAppContext;
|
||||
use std::path::Path;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
@@ -3124,11 +3127,11 @@ mod tests {
|
||||
}
|
||||
|
||||
/// #1033, with the reporter's window: 1946pt wide, the rail collapsed.
|
||||
/// The strip starts 80pt in, after the traffic lights, and centred on it
|
||||
/// The strip started 80pt in, after the traffic lights, and centred on it
|
||||
/// the box sat at 1013 — 40pt right of the window's middle at 973.
|
||||
#[test]
|
||||
fn with_the_rail_collapsed_on_macos_the_search_box_centres_on_the_window() {
|
||||
let (w, lead) = (1946., 80.);
|
||||
let (w, lead) = (1946., TRAFFIC_LIGHTS_LEAD);
|
||||
let band = search_band(true, lead, 0., 0., Some(58.), Some(49.));
|
||||
let (l, r) = band_in_column(band, lead, w);
|
||||
assert_eq!((l, r), (0., w));
|
||||
@@ -3139,12 +3142,12 @@ mod tests {
|
||||
}
|
||||
|
||||
/// With the rail open, the title bar sits in the terminal column but
|
||||
/// still leaves the traffic lights' 80pt before the strip, over nothing
|
||||
/// still leaves the traffic lights' lead before the strip, over nothing
|
||||
/// — the lights are over the rail. The band reaches back over that too,
|
||||
/// and centres on the column.
|
||||
#[test]
|
||||
fn with_the_rail_open_on_macos_the_search_box_centres_on_the_terminal_column() {
|
||||
let (column, lead) = (1200., 80.);
|
||||
let (column, lead) = (1200., TRAFFIC_LIGHTS_LEAD);
|
||||
let band = search_band(true, lead, 0., 0., None, Some(49.));
|
||||
assert_eq!(band_in_column(band, lead, column), (0., column));
|
||||
assert_eq!(band.clear, 49. + TITLEBAR_SEARCH_CLEAR_GAP);
|
||||
@@ -3156,10 +3159,10 @@ mod tests {
|
||||
assert_eq!(docked.clear, 0.);
|
||||
}
|
||||
|
||||
/// Fullscreen keeps `TitleBar`'s 80pt and adds the bar's own inset.
|
||||
/// Fullscreen keeps the traffic lights' lead and adds the bar's own inset.
|
||||
#[test]
|
||||
fn fullscreen_on_macos_reaches_back_over_the_bars_extra_inset_as_well() {
|
||||
let lead = 80. + 12.;
|
||||
let lead = TRAFFIC_LIGHTS_LEAD + 12.;
|
||||
let band = search_band(true, lead, 0., 0., Some(58.), Some(49.));
|
||||
assert_eq!(band_in_column(band, lead, 1440.), (0., 1440.));
|
||||
}
|
||||
|
||||
+27
-13
@@ -19,11 +19,8 @@ use crate::ui::presets::Fill;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Where macOS puts the window's traffic lights over the tab strip: centred on
|
||||
/// the row of chrome tiles they share the bar with, and as far in from the left
|
||||
/// edge as they sit below the top, which is where AppKit's own toolbar windows
|
||||
/// put them (19 and 19 in a 52-point unified bar, 12 and 13 in a 40-point
|
||||
/// compact one). Nine points in is the plain 28-point title bar's inset, and
|
||||
/// in a 48-point bar it crowds the lights into the corner.
|
||||
/// the row of chrome tiles they share the bar with. `x` matches `y`, as AppKit's
|
||||
/// unified toolbar does (19 and 19 in a 52-point bar).
|
||||
///
|
||||
/// `y` is the gap between the window's top edge and the *top* of the button
|
||||
/// frame, and macOS draws those buttons 14 points tall — so the lights' centre
|
||||
@@ -35,15 +32,24 @@ use std::sync::OnceLock;
|
||||
/// to 48, without the border in it, and left the lights half a point — one
|
||||
/// device pixel on a Retina panel — below everything beside them.
|
||||
pub(crate) fn traffic_light_position() -> Point<Pixels> {
|
||||
// AppKit's own button frame, and the border `TitleBar` adds to the bar it
|
||||
// wraps. Neither moves with the bar's height.
|
||||
const BUTTON_H: f32 = 14.;
|
||||
const BAR_BORDER: f32 = 1.;
|
||||
|
||||
let top = (TITLE_BAR_HEIGHT - BAR_BORDER - BUTTON_H) / 2.;
|
||||
point(px(top), px(top))
|
||||
point(px(TRAFFIC_LIGHT_INSET), px(TRAFFIC_LIGHT_INSET))
|
||||
}
|
||||
|
||||
// AppKit's own button frame, and the border `TitleBar` adds to the bar it
|
||||
// wraps. Neither moves with the bar's height.
|
||||
const TRAFFIC_LIGHT_H: f32 = 14.;
|
||||
const TITLE_BAR_BORDER: f32 = 1.;
|
||||
|
||||
const TRAFFIC_LIGHT_INSET: f32 = (TITLE_BAR_HEIGHT - TITLE_BAR_BORDER - TRAFFIC_LIGHT_H) / 2.;
|
||||
|
||||
/// Close to zoom: three 14-point buttons on a 20-point pitch.
|
||||
const TRAFFIC_LIGHTS_W: f32 = 54.;
|
||||
|
||||
/// The room the lights take at the bar's leading edge on macOS: their span,
|
||||
/// with the inset they sit at on either side.
|
||||
pub(crate) const TRAFFIC_LIGHTS_LEAD: f32 =
|
||||
TRAFFIC_LIGHT_INSET + TRAFFIC_LIGHTS_W + TRAFFIC_LIGHT_INSET;
|
||||
|
||||
pub(crate) fn set_menus(cx: &mut App) {
|
||||
cx.set_menus([
|
||||
Menu::new("tty7").items([
|
||||
@@ -1098,10 +1104,18 @@ mod tests {
|
||||
#[test]
|
||||
fn the_traffic_lights_sit_on_the_chrome_rows_centre() {
|
||||
let position = traffic_light_position();
|
||||
assert_eq!(position.x, position.y);
|
||||
assert_eq!(position.y + px(7.), px((TITLE_BAR_HEIGHT - 1.) / 2.));
|
||||
}
|
||||
|
||||
/// The strip, editor header and search band start at the lead, so the
|
||||
/// lights have to end, with their inset after them, before it does.
|
||||
#[test]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn the_title_bar_lead_clears_the_traffic_lights() {
|
||||
let right = traffic_light_position().x + px(TRAFFIC_LIGHTS_W);
|
||||
assert!(right + px(TRAFFIC_LIGHT_INSET) <= px(crate::ui::app::TITLE_BAR_LEAD));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn effective_preset_follows_the_cached_system_appearance(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
|
||||
Reference in New Issue
Block a user