Inline the macOS traffic lights with the workspace picker
The sidebar header stacked a redundant `ELY Browser ⌄` title row above the workspace picker, pushing the picker down a full 30 px and leaving the traffic lights stranded over the title text. macOS already advertises the app name in the menubar, so the in-window title is dead weight. Drop the title row, reserve `TRAFFIC_LIGHT_RESERVE = 76 px` at the start of the picker row instead, and recompute the disclosure anchor from the new geometry. Traffic-light Y from main.rs (`SHELL_INSET + 22`) lines up with the new picker row's vertical center on the first frame, so the anchor solver sums one fewer row.
This commit is contained in:
@@ -21,10 +21,6 @@ use crate::shell::chrome::animations::fade_in;
|
|||||||
const HEADER_PT: f32 = 8.0;
|
const HEADER_PT: f32 = 8.0;
|
||||||
/// Horizontal padding of the sidebar header column.
|
/// Horizontal padding of the sidebar header column.
|
||||||
const HEADER_PX: f32 = 10.0;
|
const HEADER_PX: f32 = 10.0;
|
||||||
/// Vertical gap between the title row and the picker row.
|
|
||||||
const HEADER_ROW_GAP: f32 = 8.0;
|
|
||||||
/// Height of the title row (`ELY Browser ⌄`).
|
|
||||||
const TITLE_ROW_HEIGHT: f32 = 20.0;
|
|
||||||
/// Vertical padding inside the picker row (top/bottom).
|
/// Vertical padding inside the picker row (top/bottom).
|
||||||
const PICKER_ROW_PY: f32 = 4.0;
|
const PICKER_ROW_PY: f32 = 4.0;
|
||||||
/// Horizontal padding inside the picker row (left/right).
|
/// Horizontal padding inside the picker row (left/right).
|
||||||
@@ -36,6 +32,10 @@ const PICKER_BUTTON_SIZE: f32 = 32.0;
|
|||||||
/// Visual lift applied to the popover so it nestles just below the
|
/// Visual lift applied to the popover so it nestles just below the
|
||||||
/// picker pill instead of sitting flush on its bottom edge.
|
/// picker pill instead of sitting flush on its bottom edge.
|
||||||
const DISCLOSURE_LIFT: f32 = 6.0;
|
const DISCLOSURE_LIFT: f32 = 6.0;
|
||||||
|
/// Width reserved at the start of the sidebar's picker row for the
|
||||||
|
/// macOS traffic lights so the workspaces tile never collides with
|
||||||
|
/// the close / minimize / maximize circles.
|
||||||
|
pub(crate) const TRAFFIC_LIGHT_RESERVE: f32 = 76.0;
|
||||||
|
|
||||||
pub(crate) fn render_sidebar_header(
|
pub(crate) fn render_sidebar_header(
|
||||||
shell: &ElyShell,
|
shell: &ElyShell,
|
||||||
@@ -51,36 +51,11 @@ pub(crate) fn render_sidebar_header(
|
|||||||
.pt(px(HEADER_PT))
|
.pt(px(HEADER_PT))
|
||||||
.px(px(HEADER_PX))
|
.px(px(HEADER_PX))
|
||||||
.pb(px(HEADER_PX))
|
.pb(px(HEADER_PX))
|
||||||
.gap(px(HEADER_ROW_GAP))
|
|
||||||
.flex_shrink_0()
|
.flex_shrink_0()
|
||||||
.child(render_title_row())
|
|
||||||
.child(render_workspace_picker(snapshot, active_space, picker_open, cx))
|
.child(render_workspace_picker(snapshot, active_space, picker_open, cx))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_title_row() -> AnyElement {
|
|
||||||
div()
|
|
||||||
.h(px(TITLE_ROW_HEIGHT))
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap(px(4.0))
|
|
||||||
// Reserve the macOS traffic-light group plus a calm title gap.
|
|
||||||
.pl(px(TRAFFIC_LIGHT_RESERVE))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_size(px(12.5))
|
|
||||||
.font_weight(gpui::FontWeight(500.0))
|
|
||||||
.text_color(rgb(colors::ink_2()))
|
|
||||||
.child("ELY Browser"),
|
|
||||||
)
|
|
||||||
.child(div().text_color(rgb(colors::ink_3())).opacity(0.6).child(IconName::ChevronDown))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Width reserved at the start of the sidebar's top row for the macOS
|
|
||||||
/// traffic lights plus visual breathing room.
|
|
||||||
const TRAFFIC_LIGHT_RESERVE: f32 = 90.0;
|
|
||||||
|
|
||||||
fn render_workspace_picker(
|
fn render_workspace_picker(
|
||||||
_snapshot: &BrowserSnapshot,
|
_snapshot: &BrowserSnapshot,
|
||||||
active_space: Option<&Space>,
|
active_space: Option<&Space>,
|
||||||
@@ -90,13 +65,15 @@ fn render_workspace_picker(
|
|||||||
// The disclosure renders at the window root (see render.rs) so it can
|
// The disclosure renders at the window root (see render.rs) so it can
|
||||||
// sit above a fullscreen click-out backdrop and extend past the
|
// sit above a fullscreen click-out backdrop and extend past the
|
||||||
// sidebar's overflow_hidden clip. Picker row keeps just the tile, the
|
// sidebar's overflow_hidden clip. Picker row keeps just the tile, the
|
||||||
// pill, and the add button.
|
// pill, and the add button — and reserves space at the start for the
|
||||||
|
// macOS traffic lights that float in this same horizontal band.
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap(px(PICKER_ROW_GAP))
|
.gap(px(PICKER_ROW_GAP))
|
||||||
.py(px(PICKER_ROW_PY))
|
.py(px(PICKER_ROW_PY))
|
||||||
.px(px(PICKER_ROW_PX))
|
.px(px(PICKER_ROW_PX))
|
||||||
|
.pl(px(TRAFFIC_LIGHT_RESERVE))
|
||||||
.child(render_workspaces_tile(cx))
|
.child(render_workspaces_tile(cx))
|
||||||
.child(render_picker_pill(active_space, picker_open, cx))
|
.child(render_picker_pill(active_space, picker_open, cx))
|
||||||
.child(render_add_workspace_button(cx))
|
.child(render_add_workspace_button(cx))
|
||||||
@@ -190,13 +167,15 @@ impl WorkspaceDisclosureAnchor {
|
|||||||
/// upstream, so changing one of those constants moves the popover
|
/// upstream, so changing one of those constants moves the popover
|
||||||
/// with it.
|
/// with it.
|
||||||
pub(crate) fn solve(sidebar_width_px: f32) -> Self {
|
pub(crate) fn solve(sidebar_width_px: f32) -> Self {
|
||||||
let left_px =
|
let left_px = spacing::SHELL_INSET
|
||||||
spacing::SHELL_INSET + HEADER_PX + PICKER_ROW_PX + PICKER_BUTTON_SIZE + PICKER_ROW_GAP;
|
+ HEADER_PX
|
||||||
|
+ PICKER_ROW_PX
|
||||||
|
+ TRAFFIC_LIGHT_RESERVE
|
||||||
|
+ PICKER_BUTTON_SIZE
|
||||||
|
+ PICKER_ROW_GAP;
|
||||||
|
|
||||||
let top_px = spacing::SHELL_INSET
|
let top_px = spacing::SHELL_INSET
|
||||||
+ HEADER_PT
|
+ HEADER_PT
|
||||||
+ TITLE_ROW_HEIGHT
|
|
||||||
+ HEADER_ROW_GAP
|
|
||||||
+ PICKER_ROW_PY
|
+ PICKER_ROW_PY
|
||||||
+ PICKER_BUTTON_SIZE
|
+ PICKER_BUTTON_SIZE
|
||||||
+ PICKER_ROW_PY
|
+ PICKER_ROW_PY
|
||||||
@@ -206,10 +185,13 @@ impl WorkspaceDisclosureAnchor {
|
|||||||
// padded picker row. So:
|
// padded picker row. So:
|
||||||
// pill_w = sidebar_w
|
// pill_w = sidebar_w
|
||||||
// - 2 * (HEADER_PX + PICKER_ROW_PX) (row insets)
|
// - 2 * (HEADER_PX + PICKER_ROW_PX) (row insets)
|
||||||
|
// - TRAFFIC_LIGHT_RESERVE (left reserve)
|
||||||
// - 2 * PICKER_BUTTON_SIZE (tile + add)
|
// - 2 * PICKER_BUTTON_SIZE (tile + add)
|
||||||
// - 2 * PICKER_ROW_GAP (two gaps)
|
// - 2 * PICKER_ROW_GAP (two gaps)
|
||||||
let chrome =
|
let chrome = 2.0 * (HEADER_PX + PICKER_ROW_PX)
|
||||||
2.0 * (HEADER_PX + PICKER_ROW_PX) + 2.0 * PICKER_BUTTON_SIZE + 2.0 * PICKER_ROW_GAP;
|
+ TRAFFIC_LIGHT_RESERVE
|
||||||
|
+ 2.0 * PICKER_BUTTON_SIZE
|
||||||
|
+ 2.0 * PICKER_ROW_GAP;
|
||||||
let width_px = (sidebar_width_px - chrome).max(0.0);
|
let width_px = (sidebar_width_px - chrome).max(0.0);
|
||||||
|
|
||||||
Self { top_px, left_px, width_px }
|
Self { top_px, left_px, width_px }
|
||||||
@@ -424,10 +406,12 @@ mod tests {
|
|||||||
fn anchor_lines_up_with_default_sidebar_picker_pill() {
|
fn anchor_lines_up_with_default_sidebar_picker_pill() {
|
||||||
// Default sidebar (280 px) should land the popover at the
|
// Default sidebar (280 px) should land the popover at the
|
||||||
// window-relative position the visual design was tuned for.
|
// window-relative position the visual design was tuned for.
|
||||||
|
// After the title-row removal the picker is the only header
|
||||||
|
// row, so the anchor sits closer to the top edge.
|
||||||
let anchor = WorkspaceDisclosureAnchor::solve(280.0);
|
let anchor = WorkspaceDisclosureAnchor::solve(280.0);
|
||||||
assert_eq!(anchor.left_px, 66.0);
|
assert_eq!(anchor.left_px, 142.0);
|
||||||
assert_eq!(anchor.top_px, 98.0);
|
assert_eq!(anchor.top_px, 70.0);
|
||||||
assert_eq!(anchor.width_px, 180.0);
|
assert_eq!(anchor.width_px, 104.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -438,8 +422,8 @@ mod tests {
|
|||||||
let wide = WorkspaceDisclosureAnchor::solve(360.0);
|
let wide = WorkspaceDisclosureAnchor::solve(360.0);
|
||||||
assert_eq!(narrow.left_px, wide.left_px);
|
assert_eq!(narrow.left_px, wide.left_px);
|
||||||
assert_eq!(narrow.top_px, wide.top_px);
|
assert_eq!(narrow.top_px, wide.top_px);
|
||||||
assert_eq!(narrow.width_px, 140.0);
|
assert_eq!(narrow.width_px, 64.0);
|
||||||
assert_eq!(wide.width_px, 260.0);
|
assert_eq!(wide.width_px, 184.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user