From 3f71867af8e07fb412437a550fe1aafa74f730a4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 14 Apr 2021 15:14:40 -0700 Subject: [PATCH] Improve styling of tabs * Enforce a min width per tab * Center the title within tab, regardless of icon * Render icon over the top of the tab title * Ensure there is always a fixed minimum amount of filler to the right of all tabs Co-Authored-By: Nathan Sobo --- gpui/src/elements/align.rs | 11 +++- gpui/src/elements/constrained_box.rs | 12 ++++ gpui/src/elements/container.rs | 12 ++++ gpui/src/elements/flex.rs | 10 ++- zed/src/file_finder.rs | 2 +- zed/src/workspace/pane.rs | 93 ++++++++++++++++------------ 6 files changed, 95 insertions(+), 45 deletions(-) diff --git a/gpui/src/elements/align.rs b/gpui/src/elements/align.rs index a879870cab..8bb59169e2 100644 --- a/gpui/src/elements/align.rs +++ b/gpui/src/elements/align.rs @@ -3,7 +3,7 @@ use crate::{ LayoutContext, PaintContext, SizeConstraint, }; use json::ToJson; -use pathfinder_geometry::vector::{vec2f, Vector2F}; +use pathfinder_geometry::vector::Vector2F; use serde_json::json; pub struct Align { @@ -19,8 +19,13 @@ impl Align { } } - pub fn top_center(mut self) -> Self { - self.alignment = vec2f(0.0, -1.0); + pub fn top(mut self) -> Self { + self.alignment.set_y(-1.0); + self + } + + pub fn right(mut self) -> Self { + self.alignment.set_x(1.0); self } } diff --git a/gpui/src/elements/constrained_box.rs b/gpui/src/elements/constrained_box.rs index 47e83e27a9..a705be3612 100644 --- a/gpui/src/elements/constrained_box.rs +++ b/gpui/src/elements/constrained_box.rs @@ -23,6 +23,11 @@ impl ConstrainedBox { } } + pub fn with_min_width(mut self, min_width: f32) -> Self { + self.constraint.min.set_x(min_width); + self + } + pub fn with_max_width(mut self, max_width: f32) -> Self { self.constraint.max.set_x(max_width); self @@ -33,6 +38,12 @@ impl ConstrainedBox { self } + pub fn with_width(mut self, width: f32) -> Self { + self.constraint.min.set_x(width); + self.constraint.max.set_x(width); + self + } + pub fn with_height(mut self, height: f32) -> Self { self.constraint.min.set_y(height); self.constraint.max.set_y(height); @@ -51,6 +62,7 @@ impl Element for ConstrainedBox { ) -> (Vector2F, Self::LayoutState) { constraint.min = constraint.min.max(self.constraint.min); constraint.max = constraint.max.min(self.constraint.max); + constraint.max = constraint.max.max(constraint.min); let size = self.child.layout(constraint, ctx); (size, ()) } diff --git a/gpui/src/elements/container.rs b/gpui/src/elements/container.rs index 9554f3b353..d228c54d07 100644 --- a/gpui/src/elements/container.rs +++ b/gpui/src/elements/container.rs @@ -43,6 +43,18 @@ impl Container { self } + pub fn with_horizontal_padding(mut self, padding: f32) -> Self { + self.padding.left = padding; + self.padding.right = padding; + self + } + + pub fn with_vertical_padding(mut self, padding: f32) -> Self { + self.padding.top = padding; + self.padding.bottom = padding; + self + } + pub fn with_uniform_padding(mut self, padding: f32) -> Self { self.padding = Padding { top: padding, diff --git a/gpui/src/elements/flex.rs b/gpui/src/elements/flex.rs index c9426c713c..cbce89ed2f 100644 --- a/gpui/src/elements/flex.rs +++ b/gpui/src/elements/flex.rs @@ -1,4 +1,4 @@ -use std::any::Any; +use std::{any::Any, f32::INFINITY}; use crate::{ json::{self, ToJson, Value}, @@ -88,9 +88,13 @@ impl Element for Flex { let mut remaining_space = constraint.max_along(self.axis) - fixed_space; let mut remaining_flex = total_flex; for child in &mut self.children { - let space_per_flex = remaining_space / remaining_flex; if let Some(flex) = Self::child_flex(&child) { - let child_max = space_per_flex * flex; + let child_max = if remaining_flex == 0.0 { + remaining_space + } else { + let space_per_flex = remaining_space / remaining_flex; + space_per_flex * flex + }; let child_constraint = match self.axis { Axis::Horizontal => SizeConstraint::new( vec2f(0.0, constraint.min.y()), diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index e153951bf1..954b31b892 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -77,7 +77,7 @@ impl View for FileFinder { .with_max_height(400.0) .boxed(), ) - .top_center() + .top() .named("file finder") } diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index e0b0109ea4..08c9864fa0 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -192,33 +192,28 @@ impl Pane { let padding = 6.; let mut container = Container::new( - Align::new( - Flex::row() - .with_child( + Stack::new() + .with_child( + Align::new( Label::new(title, settings.ui_font_family, settings.ui_font_size) .boxed(), ) - .with_child( - Container::new( - LineBox::new( - settings.ui_font_family, - settings.ui_font_size, - ConstrainedBox::new(Self::render_modified_icon( - item.is_dirty(app), - )) - .with_max_width(12.) - .boxed(), - ) + .boxed(), + ) + .with_child( + LineBox::new( + settings.ui_font_family, + settings.ui_font_size, + Align::new(Self::render_modified_icon(item.is_dirty(app))) + .right() .boxed(), - ) - .with_margin_left(20.) - .boxed(), ) .boxed(), - ) - .boxed(), + ) + .boxed(), ) - .with_uniform_padding(padding) + .with_vertical_padding(padding) + .with_horizontal_padding(10.) .with_border(border); if ix == self.active_item { @@ -240,6 +235,7 @@ impl Pane { }) .boxed(), ) + .with_min_width(80.0) .with_max_width(264.0) .boxed(), ) @@ -247,9 +243,29 @@ impl Pane { ); } + // Ensure there's always a minimum amount of space after the last tab, + // so that the tab's border doesn't abut the window's border. + row.add_child( + ConstrainedBox::new( + Container::new( + LineBox::new( + settings.ui_font_family, + settings.ui_font_size, + Empty::new().boxed(), + ) + .boxed(), + ) + .with_uniform_padding(6.0) + .with_border(Border::bottom(1.0, border_color)) + .boxed(), + ) + .with_min_width(20.) + .named("fixed-filler"), + ); + row.add_child( Expanded::new( - 1.0, + 0.0, Container::new( LineBox::new( settings.ui_font_family, @@ -269,23 +285,24 @@ impl Pane { } fn render_modified_icon(is_modified: bool) -> ElementBox { - Canvas::new(move |bounds, ctx| { - if is_modified { - let padding = if bounds.height() < bounds.width() { - vec2f(bounds.width() - bounds.height(), 0.0) - } else { - vec2f(0.0, bounds.height() - bounds.width()) - }; - let square = RectF::new(bounds.origin() + padding / 2., bounds.size() - padding); - ctx.scene.push_quad(Quad { - bounds: square, - background: Some(ColorF::new(0.639, 0.839, 1.0, 1.0).to_u8()), - border: Default::default(), - corner_radius: square.width() / 2., - }); - } - }) - .boxed() + let diameter = 8.; + ConstrainedBox::new( + Canvas::new(move |bounds, ctx| { + if is_modified { + let square = RectF::new(bounds.origin(), vec2f(diameter, diameter)); + ctx.scene.push_quad(Quad { + bounds: square, + background: Some(ColorF::new(0.639, 0.839, 1.0, 1.0).to_u8()), + border: Default::default(), + corner_radius: diameter / 2., + }); + } + }) + .boxed(), + ) + .with_width(diameter) + .with_height(diameter) + .named("tab-right-icon") } }