diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 9bcba4a5efe37..1d03b408aad44 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -447,7 +447,7 @@ impl AssistantPanel { ); let _pane = cx.view().clone(); let right_children = h_flex() - .gap(Spacing::XSmall.rems(cx)) + .gap(DynamicSpacing::Base02.rems(cx)) .child( IconButton::new("new-chat", IconName::Plus) .on_click( @@ -4838,7 +4838,7 @@ impl ConfigurationView { ) .child( div() - .p(Spacing::Large.rems(cx)) + .p(DynamicSpacing::Base08.rems(cx)) .bg(cx.theme().colors().surface_background) .border_1() .border_color(cx.theme().colors().border_variant) @@ -4872,7 +4872,7 @@ impl Render for ConfigurationView { .overflow_y_scroll() .child( v_flex() - .p(Spacing::XXLarge.rems(cx)) + .p(DynamicSpacing::Base16.rems(cx)) .border_b_1() .border_color(cx.theme().colors().border) .gap_1() @@ -4886,7 +4886,7 @@ impl Render for ConfigurationView { ) .child( v_flex() - .p(Spacing::XXLarge.rems(cx)) + .p(DynamicSpacing::Base16.rems(cx)) .mt_1() .gap_6() .flex_1() diff --git a/crates/assistant/src/prompt_library.rs b/crates/assistant/src/prompt_library.rs index 71750925def72..f9592e50d0d51 100644 --- a/crates/assistant/src/prompt_library.rs +++ b/crates/assistant/src/prompt_library.rs @@ -830,7 +830,7 @@ impl PromptLibrary { .overflow_x_hidden() .child( h_flex() - .p(Spacing::Small.rems(cx)) + .p(DynamicSpacing::Base04.rems(cx)) .h_9() .w_full() .flex_none() @@ -871,17 +871,17 @@ impl PromptLibrary { .size_full() .relative() .overflow_hidden() - .pl(Spacing::XXLarge.rems(cx)) - .pt(Spacing::Large.rems(cx)) + .pl(DynamicSpacing::Base16.rems(cx)) + .pt(DynamicSpacing::Base08.rems(cx)) .on_click(cx.listener(move |_, _, cx| { cx.focus(&focus_handle); })) .child( h_flex() .group("active-editor-header") - .pr(Spacing::XXLarge.rems(cx)) - .pt(Spacing::XSmall.rems(cx)) - .pb(Spacing::Large.rems(cx)) + .pr(DynamicSpacing::Base16.rems(cx)) + .pt(DynamicSpacing::Base02.rems(cx)) + .pb(DynamicSpacing::Base08.rems(cx)) .justify_between() .child( h_flex().gap_1().child( @@ -943,13 +943,13 @@ impl PromptLibrary { .child( h_flex() .h_full() - .gap(Spacing::XXLarge.rems(cx)) + .gap(DynamicSpacing::Base16.rems(cx)) .child(div()), ) .child( h_flex() .h_full() - .gap(Spacing::XXLarge.rems(cx)) + .gap(DynamicSpacing::Base16.rems(cx)) .children(prompt_editor.token_count.map( |token_count| { let token_count: SharedString = diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 56b1135a38696..6ebe783b5cc16 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use smol::channel; use theme::{SyntaxTheme, ThemeSettings}; -use ui::{IndentGuideColors, IndentGuideLayout}; +use ui::{DynamicSpacing, IndentGuideColors, IndentGuideLayout}; use util::{debug_panic, RangeExt, ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel, PanelEvent}, @@ -51,8 +51,8 @@ use workspace::{ ui::{ h_flex, v_flex, ActiveTheme, ButtonCommon, Clickable, Color, ContextMenu, FluentBuilder, HighlightedLabel, Icon, IconButton, IconButtonShape, IconName, IconSize, Label, - LabelCommon, ListItem, Scrollbar, ScrollbarState, Selectable, Spacing, StyledExt, - StyledTypography, Tooltip, + LabelCommon, ListItem, Scrollbar, ScrollbarState, Selectable, StyledExt, StyledTypography, + Tooltip, }, OpenInTerminal, WeakItemHandle, Workspace, }; @@ -3867,7 +3867,7 @@ impl OutlinePanel { }) .child( h_flex() - .pt(Spacing::Small.rems(cx)) + .pt(DynamicSpacing::Base04.rems(cx)) .justify_center() .child({ let keystroke = match self.position(cx) { diff --git a/crates/quick_action_bar/src/quick_action_bar.rs b/crates/quick_action_bar/src/quick_action_bar.rs index fb05065a19fc1..7849620093705 100644 --- a/crates/quick_action_bar/src/quick_action_bar.rs +++ b/crates/quick_action_bar/src/quick_action_bar.rs @@ -303,7 +303,7 @@ impl Render for QuickActionBar { h_flex() .id("quick action bar") - .gap(Spacing::Medium.rems(cx)) + .gap(DynamicSpacing::Base06.rems(cx)) .children(self.render_repl_menu(cx)) .children(self.render_toggle_markdown_preview(self.workspace.clone(), cx)) .children(search_button) diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index b094313eee271..8320bbee831a0 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -320,9 +320,9 @@ impl RenderOnce for SshConnectionHeader { }; h_flex() - .px(Spacing::XLarge.rems(cx)) - .pt(Spacing::Large.rems(cx)) - .pb(Spacing::Small.rems(cx)) + .px(DynamicSpacing::Base12.rems(cx)) + .pt(DynamicSpacing::Base08.rems(cx)) + .pb(DynamicSpacing::Base04.rems(cx)) .rounded_t_md() .w_full() .gap_1p5() diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs index f86f969a96323..055e4c09f86e7 100644 --- a/crates/repl/src/notebook/cell.rs +++ b/crates/repl/src/notebook/cell.rs @@ -260,7 +260,7 @@ pub trait RenderableCell: Render { if (cell_position == Some(&CellPosition::First) && is_first) || (cell_position == Some(&CellPosition::Last) && !is_first) { - Some(div().flex().w_full().h(Spacing::XLarge.px(cx))) + Some(div().flex().w_full().h(DynamicSpacing::Base12.px(cx))) } else { None } @@ -389,7 +389,7 @@ impl Render for MarkdownCell { .pr_6() .rounded_sm() .items_start() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .bg(self.selected_bg_color(cx)) .child(self.gutter(cx)) .child( @@ -564,7 +564,7 @@ impl Render for CodeCell { .pr_6() .rounded_sm() .items_start() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .bg(self.selected_bg_color(cx)) .child(self.gutter(cx)) .child( @@ -590,7 +590,7 @@ impl Render for CodeCell { .pr_6() .rounded_sm() .items_start() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .bg(self.selected_bg_color(cx)) .child(self.gutter_output(cx)) .child( @@ -710,7 +710,7 @@ impl Render for RawCell { .pr_2() .rounded_sm() .items_start() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .bg(self.selected_bg_color(cx)) .child(self.gutter(cx)) .child( diff --git a/crates/repl/src/notebook/notebook_ui.rs b/crates/repl/src/notebook/notebook_ui.rs index 32f07ce626d6e..d10da13fd8ac3 100644 --- a/crates/repl/src/notebook/notebook_ui.rs +++ b/crates/repl/src/notebook/notebook_ui.rs @@ -273,7 +273,7 @@ impl NotebookEditor { fn button_group(cx: &ViewContext) -> Div { v_flex() - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .items_center() .w(px(CONTROL_SIZE + 4.0)) .overflow_hidden() @@ -299,14 +299,14 @@ impl NotebookEditor { v_flex() .max_w(px(CONTROL_SIZE + 4.0)) .items_center() - .gap(Spacing::XXLarge.rems(cx)) + .gap(DynamicSpacing::Base16.rems(cx)) .justify_between() .flex_none() .h_full() - .py(Spacing::XLarge.px(cx)) + .py(DynamicSpacing::Base12.px(cx)) .child( v_flex() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .child( Self::button_group(cx) .child( @@ -390,7 +390,7 @@ impl NotebookEditor { ) .child( v_flex() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .items_center() .child(Self::render_notebook_control( "more-menu", @@ -468,8 +468,8 @@ impl Render for NotebookEditor { .items_start() .size_full() .overflow_hidden() - .px(Spacing::XLarge.px(cx)) - .gap(Spacing::XLarge.px(cx)) + .px(DynamicSpacing::Base12.px(cx)) + .gap(DynamicSpacing::Base12.px(cx)) .bg(cx.theme().colors().tab_bar_background) .child( v_flex() diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs index b5842310186b1..26f30f5588e17 100644 --- a/crates/ui/src/components/button/button.rs +++ b/crates/ui/src/components/button/button.rs @@ -1,7 +1,9 @@ #![allow(missing_docs)] use gpui::{AnyView, DefiniteLength}; -use crate::{prelude::*, ElevationIndex, IconPosition, KeyBinding, Spacing, TintColor}; +use crate::{ + prelude::*, Color, DynamicSpacing, ElevationIndex, IconPosition, KeyBinding, TintColor, +}; use crate::{ ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label, LineHeightStyle, }; @@ -398,7 +400,7 @@ impl RenderOnce for Button { self.base.child( h_flex() - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .when(self.icon_position == Some(IconPosition::Start), |this| { this.children(self.icon.map(|icon| { ButtonIcon::new(icon) @@ -412,7 +414,7 @@ impl RenderOnce for Button { }) .child( h_flex() - .gap(Spacing::Medium.rems(cx)) + .gap(DynamicSpacing::Base06.rems(cx)) .justify_between() .child( Label::new(label) diff --git a/crates/ui/src/components/button/button_like.rs b/crates/ui/src/components/button/button_like.rs index ae5730569eae1..a285388cbd6a4 100644 --- a/crates/ui/src/components/button/button_like.rs +++ b/crates/ui/src/components/button/button_like.rs @@ -3,7 +3,7 @@ use gpui::{relative, CursorStyle, DefiniteLength, MouseButton}; use gpui::{transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems}; use smallvec::SmallVec; -use crate::{prelude::*, ElevationIndex, Spacing}; +use crate::{prelude::*, DynamicSpacing, ElevationIndex}; /// A trait for buttons that can be Selected. Enables setting the [`ButtonStyle`] of a button when it is selected. pub trait SelectableButton: Selectable { @@ -491,10 +491,12 @@ impl RenderOnce for ButtonLike { ButtonLikeRounding::Left => this.rounded_l_md(), ButtonLikeRounding::Right => this.rounded_r_md(), }) - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .map(|this| match self.size { - ButtonSize::Large => this.px(Spacing::Medium.rems(cx)), - ButtonSize::Default | ButtonSize::Compact => this.px(Spacing::Small.rems(cx)), + ButtonSize::Large => this.px(DynamicSpacing::Base06.rems(cx)), + ButtonSize::Default | ButtonSize::Compact => { + this.px(DynamicSpacing::Base04.rems(cx)) + } ButtonSize::None => this, }) .bg(style.enabled(self.layer, cx).background) diff --git a/crates/ui/src/components/checkbox.rs b/crates/ui/src/components/checkbox.rs index c8113c9373b49..efa907ea20fb5 100644 --- a/crates/ui/src/components/checkbox.rs +++ b/crates/ui/src/components/checkbox.rs @@ -85,7 +85,7 @@ impl RenderOnce for Checkbox { .id(self.id) .justify_center() .items_center() - .size(crate::styles::custom_spacing(cx, 20.)) + .size(DynamicSpacing::Base20.rems(cx)) .group(group_id.clone()) .child( div() @@ -93,8 +93,8 @@ impl RenderOnce for Checkbox { .flex_none() .justify_center() .items_center() - .m(Spacing::Small.px(cx)) - .size(crate::styles::custom_spacing(cx, 16.)) + .m(DynamicSpacing::Base04.px(cx)) + .size(DynamicSpacing::Base16.rems(cx)) .rounded_sm() .bg(bg_color) .border_1() @@ -191,7 +191,7 @@ impl CheckboxWithLabel { impl RenderOnce for CheckboxWithLabel { fn render(self, cx: &mut WindowContext) -> impl IntoElement { h_flex() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .child(Checkbox::new(self.id.clone(), self.checked).on_click({ let on_click = self.on_click.clone(); move |checked, cx| { diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 5875c8891c6f1..c6c92ee9d9938 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -90,10 +90,10 @@ impl IconSize { pub fn square_components(&self, cx: &mut WindowContext) -> (Pixels, Pixels) { let icon_size = self.rems() * cx.rem_size(); let padding = match self { - IconSize::Indicator => Spacing::None.px(cx), - IconSize::XSmall => Spacing::XSmall.px(cx), - IconSize::Small => Spacing::XSmall.px(cx), - IconSize::Medium => Spacing::XSmall.px(cx), + IconSize::Indicator => DynamicSpacing::Base00.px(cx), + IconSize::XSmall => DynamicSpacing::Base02.px(cx), + IconSize::Small => DynamicSpacing::Base02.px(cx), + IconSize::Medium => DynamicSpacing::Base02.px(cx), }; (icon_size, padding) diff --git a/crates/ui/src/components/keybinding.rs b/crates/ui/src/components/keybinding.rs index c1381e6fdfe9a..770e46eafdd85 100644 --- a/crates/ui/src/components/keybinding.rs +++ b/crates/ui/src/components/keybinding.rs @@ -84,7 +84,7 @@ impl RenderOnce for KeyBinding { .join(" ") ) }) - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .flex_none() .children(self.key_binding.keystrokes().iter().map(|keystroke| { let key_icon = self.icon_for_key(keystroke); diff --git a/crates/ui/src/components/list/list.rs b/crates/ui/src/components/list/list.rs index e4cc74b2c0b2a..4733c822fb950 100644 --- a/crates/ui/src/components/list/list.rs +++ b/crates/ui/src/components/list/list.rs @@ -80,7 +80,7 @@ impl RenderOnce for List { fn render(self, cx: &mut WindowContext) -> impl IntoElement { v_flex() .w_full() - .py(Spacing::Small.rems(cx)) + .py(DynamicSpacing::Base04.rems(cx)) .children(self.header) .map(|this| match (self.children.is_empty(), self.toggle) { (false, _) => this.children(self.children), diff --git a/crates/ui/src/components/list/list_header.rs b/crates/ui/src/components/list/list_header.rs index b69426b6d1f80..0d1411e70a129 100644 --- a/crates/ui/src/components/list/list_header.rs +++ b/crates/ui/src/components/list/list_header.rs @@ -104,10 +104,10 @@ impl RenderOnce for ListHeader { .items_center() .justify_between() .w_full() - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .child( h_flex() - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .children(self.toggle.map(|is_open| { Disclosure::new("toggle", is_open).on_toggle(self.on_toggle.clone()) })) @@ -115,7 +115,7 @@ impl RenderOnce for ListHeader { div() .id("label_container") .flex() - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .items_center() .children(self.start_slot) .child(Label::new(self.label.clone()).color(Color::Muted)) diff --git a/crates/ui/src/components/list/list_item.rs b/crates/ui/src/components/list/list_item.rs index dd2467f7b04ea..c65832d3e9d07 100644 --- a/crates/ui/src/components/list/list_item.rs +++ b/crates/ui/src/components/list/list_item.rs @@ -171,7 +171,7 @@ impl RenderOnce for ListItem { // When an item is inset draw the indent spacing outside of the item .when(self.inset, |this| { this.ml(self.indent_level as f32 * self.indent_step_size) - .px(Spacing::Small.rems(cx)) + .px(DynamicSpacing::Base04.rems(cx)) }) .when(!self.inset && !self.disabled, |this| { this @@ -195,7 +195,7 @@ impl RenderOnce for ListItem { .relative() .items_center() .gap_1() - .px(Spacing::Medium.rems(cx)) + .px(DynamicSpacing::Base06.rems(cx)) .map(|this| match self.spacing { ListItemSpacing::Dense => this, ListItemSpacing::Sparse => this.py_1(), @@ -248,7 +248,7 @@ impl RenderOnce for ListItem { .flex_grow() .flex_shrink_0() .flex_basis(relative(0.25)) - .gap(Spacing::Medium.rems(cx)) + .gap(DynamicSpacing::Base06.rems(cx)) .map(|list_content| { if self.overflow_x { list_content @@ -276,7 +276,7 @@ impl RenderOnce for ListItem { h_flex() .h_full() .absolute() - .right(Spacing::Medium.rems(cx)) + .right(DynamicSpacing::Base06.rems(cx)) .top_0() .visible_on_hover("list_item") .child(end_hover_slot), diff --git a/crates/ui/src/components/list/list_separator.rs b/crates/ui/src/components/list/list_separator.rs index aa53efab372f0..4272f79591db0 100644 --- a/crates/ui/src/components/list/list_separator.rs +++ b/crates/ui/src/components/list/list_separator.rs @@ -10,7 +10,7 @@ impl RenderOnce for ListSeparator { div() .h_px() .w_full() - .my(Spacing::Medium.rems(cx)) + .my(DynamicSpacing::Base06.rems(cx)) .bg(cx.theme().colors().border_variant) } } diff --git a/crates/ui/src/components/list/list_sub_header.rs b/crates/ui/src/components/list/list_sub_header.rs index 25132b7668662..62d2ca00919d7 100644 --- a/crates/ui/src/components/list/list_sub_header.rs +++ b/crates/ui/src/components/list/list_sub_header.rs @@ -45,8 +45,8 @@ impl RenderOnce for ListSubHeader { .flex_1() .w_full() .relative() - .pb(Spacing::Small.rems(cx)) - .px(Spacing::XSmall.rems(cx)) + .pb(DynamicSpacing::Base04.rems(cx)) + .px(DynamicSpacing::Base02.rems(cx)) .child( div() .h_6() diff --git a/crates/ui/src/components/modal.rs b/crates/ui/src/components/modal.rs index f8fdaede19844..0b7ad3cccd475 100644 --- a/crates/ui/src/components/modal.rs +++ b/crates/ui/src/components/modal.rs @@ -1,8 +1,8 @@ #![allow(missing_docs)] use crate::{ - h_flex, v_flex, Clickable, Color, Headline, HeadlineSize, IconButton, IconButtonShape, - IconName, Label, LabelCommon, LabelSize, Spacing, + h_flex, v_flex, Clickable, Color, DynamicSpacing, Headline, HeadlineSize, IconButton, + IconButtonShape, IconName, Label, LabelCommon, LabelSize, }; use gpui::{prelude::FluentBuilder, *}; use smallvec::SmallVec; @@ -78,7 +78,7 @@ impl RenderOnce for Modal { .id(self.container_id.clone()) .w_full() .flex_1() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .when_some( self.container_scroll_handler, |this, container_scroll_handle| { @@ -160,10 +160,10 @@ impl RenderOnce for ModalHeader { .flex_none() .justify_between() .w_full() - .px(Spacing::XLarge.rems(cx)) - .pt(Spacing::Large.rems(cx)) - .pb(Spacing::Small.rems(cx)) - .gap(Spacing::Large.rems(cx)) + .px(DynamicSpacing::Base12.rems(cx)) + .pt(DynamicSpacing::Base08.rems(cx)) + .pb(DynamicSpacing::Base04.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .when(self.show_back_button, |this| { this.child( IconButton::new("back", IconName::ArrowLeft) @@ -253,7 +253,7 @@ impl RenderOnce for ModalFooter { h_flex() .flex_none() .w_full() - .p(Spacing::Large.rems(cx)) + .p(DynamicSpacing::Base08.rems(cx)) .justify_between() .child(div().when_some(self.start_slot, |this, start_slot| this.child(start_slot))) .child(div().when_some(self.end_slot, |this, end_slot| this.child(end_slot))) @@ -330,7 +330,7 @@ impl RenderOnce for Section { let children = if self.contained { v_flex() .flex_1() - .when(self.padded, |this| this.px(Spacing::XLarge.rems(cx))) + .when(self.padded, |this| this.px(DynamicSpacing::Base12.rems(cx))) .child( v_flex() .w_full() @@ -338,17 +338,17 @@ impl RenderOnce for Section { .border_1() .border_color(cx.theme().colors().border) .bg(section_bg) - .py(Spacing::Medium.rems(cx)) - .gap_y(Spacing::Small.rems(cx)) + .py(DynamicSpacing::Base06.rems(cx)) + .gap_y(DynamicSpacing::Base04.rems(cx)) .child(div().flex().flex_1().size_full().children(self.children)), ) } else { v_flex() .w_full() .flex_1() - .gap_y(Spacing::Small.rems(cx)) + .gap_y(DynamicSpacing::Base04.rems(cx)) .when(self.padded, |this| { - this.px(Spacing::Medium.rems(cx) + Spacing::Medium.rems(cx)) + this.px(DynamicSpacing::Base06.rems(cx) + DynamicSpacing::Base06.rems(cx)) }) .children(self.children) }; @@ -359,7 +359,7 @@ impl RenderOnce for Section { .child( v_flex() .flex_none() - .px(Spacing::XLarge.rems(cx)) + .px(DynamicSpacing::Base12.rems(cx)) .children(self.header) .when_some(self.meta, |this, meta| { this.child(Label::new(meta).size(LabelSize::Small).color(Color::Muted)) @@ -399,7 +399,7 @@ impl RenderOnce for SectionHeader { h_flex() .id(self.label.clone()) .w_full() - .px(Spacing::Large.rems(cx)) + .px(DynamicSpacing::Base08.rems(cx)) .child( div() .h_7() @@ -407,7 +407,7 @@ impl RenderOnce for SectionHeader { .items_center() .justify_between() .w_full() - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .child( div().flex_1().child( Label::new(self.label.clone()) diff --git a/crates/ui/src/components/radio.rs b/crates/ui/src/components/radio.rs index c4ceef5f8379a..3cd32ee891897 100644 --- a/crates/ui/src/components/radio.rs +++ b/crates/ui/src/components/radio.rs @@ -36,7 +36,7 @@ impl RenderOnce for RadioWithLabel { let border_width = rems_from_px(1.); h_flex() .id(self.id) - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .group("") .child( div() diff --git a/crates/ui/src/components/tab.rs b/crates/ui/src/components/tab.rs index 9540063c78dc4..1a2c1595cfada 100644 --- a/crates/ui/src/components/tab.rs +++ b/crates/ui/src/components/tab.rs @@ -158,8 +158,8 @@ impl RenderOnce for Tab { .group("") .relative() .h(rems(Self::CONTENT_HEIGHT_IN_REMS)) - .px(crate::custom_spacing(cx, 4.)) - .gap(Spacing::Small.rems(cx)) + .px(DynamicSpacing::Base04.px(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .text_color(text_color) .child(start_slot) .children(self.children) diff --git a/crates/ui/src/components/tab_bar.rs b/crates/ui/src/components/tab_bar.rs index 5657721e17a21..6aaa2fd59a396 100644 --- a/crates/ui/src/components/tab_bar.rs +++ b/crates/ui/src/components/tab_bar.rs @@ -107,8 +107,8 @@ impl RenderOnce for TabBar { this.child( h_flex() .flex_none() - .gap(Spacing::Small.rems(cx)) - .px(Spacing::Medium.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) + .px(DynamicSpacing::Base06.rems(cx)) .border_b_1() .border_r_1() .border_color(cx.theme().colors().border) @@ -145,8 +145,8 @@ impl RenderOnce for TabBar { this.child( h_flex() .flex_none() - .gap(Spacing::Small.rems(cx)) - .px(Spacing::Medium.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) + .px(DynamicSpacing::Base06.rems(cx)) .border_b_1() .border_l_1() .border_color(cx.theme().colors().border) diff --git a/crates/ui/src/components/tool_strip.rs b/crates/ui/src/components/tool_strip.rs index 78767aadf73fb..707c5ed06d126 100644 --- a/crates/ui/src/components/tool_strip.rs +++ b/crates/ui/src/components/tool_strip.rs @@ -46,8 +46,8 @@ impl RenderOnce for ToolStrip { Axis::Horizontal => element.h_flex(), }) .flex_none() - .gap(Spacing::Small.rems(cx)) - .p(Spacing::XSmall.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) + .p(DynamicSpacing::Base02.rems(cx)) .border_1() .border_color(cx.theme().colors().border) .rounded(rems_from_px(6.0)) diff --git a/crates/ui/src/prelude.rs b/crates/ui/src/prelude.rs index 8801efed5a18e..0e79475e00e2d 100644 --- a/crates/ui/src/prelude.rs +++ b/crates/ui/src/prelude.rs @@ -15,7 +15,7 @@ pub use crate::traits::fixed::*; pub use crate::traits::selectable::*; pub use crate::traits::styled_ext::*; pub use crate::traits::visible_on_hover::*; -pub use crate::Spacing; +pub use crate::DynamicSpacing; pub use crate::{h_flex, v_flex}; pub use crate::{Button, ButtonSize, ButtonStyle, IconButton, SelectableButton}; pub use crate::{ButtonCommon, Color}; diff --git a/crates/ui/src/styles/spacing.rs b/crates/ui/src/styles/spacing.rs index a2089d9586bd4..f623bc4141625 100644 --- a/crates/ui/src/styles/spacing.rs +++ b/crates/ui/src/styles/spacing.rs @@ -1,105 +1,32 @@ use gpui::{px, rems, Pixels, Rems, WindowContext}; use settings::Settings; use theme::{ThemeSettings, UiDensity}; - -use crate::{rems_from_px, BASE_REM_SIZE_IN_PX}; - -/// A dynamic spacing system that adjusts spacing based on -/// [UiDensity]. +use ui_macros::derive_dynamic_spacing; + +// Derives [DynamicSpacing]. See [ui_macros::derive_dynamic_spacing]. +derive_dynamic_spacing![ + (0, 0, 0), + (1, 1, 2), + (1, 2, 4), + (2, 3, 4), + (2, 4, 6), + (3, 6, 8), + (4, 8, 10), + (10, 12, 14), + (14, 16, 18), + (18, 20, 22), + 24, + 32, + 40, + 48 +]; + +/// Returns the current [`UiDensity`] setting. Use this to +/// modify or show something in the UI other than spacing. /// -/// When possible, [Spacing] should be used over manual -/// or built-in spacing values in places dynamic spacing is needed. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Spacing { - /// No spacing - None, - /// Usually a one pixel spacing. Grows to 2px in comfortable density. - /// @16px/rem: `1px`|`1px`|`2px` - XXSmall, - /// Extra small spacing - @16px/rem: `1px`|`2px`|`4px` - /// - /// Relative to the user's `ui_font_size` and [UiDensity] setting. - XSmall, - /// Small spacing - @16px/rem: `2px`|`4px`|`6px` - /// - /// Relative to the user's `ui_font_size` and [UiDensity] setting. - Small, - /// Medium spacing - @16px/rem: `3px`|`6px`|`8px` - /// - /// Relative to the user's `ui_font_size` and [UiDensity] setting. - Medium, - /// Large spacing - @16px/rem: `4px`|`8px`|`10px` - /// - /// Relative to the user's `ui_font_size` and [UiDensity] setting. - Large, - /// Extra Large spacing - @16px/rem: `8px`|`12px`|`16px` - /// - /// Relative to the user's `ui_font_size` and [UiDensity] setting. - XLarge, - /// 2X Large spacing - @16px/rem: `12px`|`16px`|`20px` - /// - /// Relative to the user's `ui_font_size` and [UiDensity] setting. - XXLarge, -} - -impl Spacing { - /// Returns the spacing's scaling ratio in pixels. - pub fn spacing_ratio(self, cx: &WindowContext) -> f32 { - match ThemeSettings::get_global(cx).ui_density { - UiDensity::Compact => match self { - Spacing::None => 0., - Spacing::XXSmall => 1. / BASE_REM_SIZE_IN_PX, - Spacing::XSmall => 1. / BASE_REM_SIZE_IN_PX, - Spacing::Small => 2. / BASE_REM_SIZE_IN_PX, - Spacing::Medium => 3. / BASE_REM_SIZE_IN_PX, - Spacing::Large => 4. / BASE_REM_SIZE_IN_PX, - Spacing::XLarge => 8. / BASE_REM_SIZE_IN_PX, - Spacing::XXLarge => 12. / BASE_REM_SIZE_IN_PX, - }, - UiDensity::Default => match self { - Spacing::None => 0., - Spacing::XXSmall => 1. / BASE_REM_SIZE_IN_PX, - Spacing::XSmall => 2. / BASE_REM_SIZE_IN_PX, - Spacing::Small => 4. / BASE_REM_SIZE_IN_PX, - Spacing::Medium => 6. / BASE_REM_SIZE_IN_PX, - Spacing::Large => 8. / BASE_REM_SIZE_IN_PX, - Spacing::XLarge => 12. / BASE_REM_SIZE_IN_PX, - Spacing::XXLarge => 16. / BASE_REM_SIZE_IN_PX, - }, - UiDensity::Comfortable => match self { - Spacing::None => 0., - Spacing::XXSmall => 2. / BASE_REM_SIZE_IN_PX, - Spacing::XSmall => 3. / BASE_REM_SIZE_IN_PX, - Spacing::Small => 6. / BASE_REM_SIZE_IN_PX, - Spacing::Medium => 8. / BASE_REM_SIZE_IN_PX, - Spacing::Large => 10. / BASE_REM_SIZE_IN_PX, - Spacing::XLarge => 16. / BASE_REM_SIZE_IN_PX, - Spacing::XXLarge => 20. / BASE_REM_SIZE_IN_PX, - }, - } - } - - /// Returns the spacing's value in rems. - pub fn rems(self, cx: &WindowContext) -> Rems { - rems(self.spacing_ratio(cx)) - } - - /// Returns the spacing's value in pixels. - pub fn px(self, cx: &WindowContext) -> Pixels { - let ui_font_size_f32: f32 = ThemeSettings::get_global(cx).ui_font_size.into(); - - px(ui_font_size_f32 * self.spacing_ratio(cx)) - } -} - -fn user_spacing_style(cx: &WindowContext) -> UiDensity { - ThemeSettings::get_global(cx).ui_density -} - -/// Returns a custom spacing value based on the current [`UiDensity`]. +/// Do not use this to calculate spacing values. /// -/// If you use this, talk to @iamnbutler and let me know what you're doing -/// that needs custom spacing– I'd love to understand so we can extend the system further and remove the need for this. -pub fn custom_spacing(cx: &WindowContext, size: f32) -> Rems { - rems_from_px(size * user_spacing_style(cx).spacing_ratio()) +/// Always use [DynamicSpacing] for spacing values. +pub fn ui_density(cx: &WindowContext) -> UiDensity { + ThemeSettings::get_global(cx).ui_density } diff --git a/crates/ui_macros/src/dynamic_spacing.rs b/crates/ui_macros/src/dynamic_spacing.rs new file mode 100644 index 0000000000000..4f8c41c4e48ae --- /dev/null +++ b/crates/ui_macros/src/dynamic_spacing.rs @@ -0,0 +1,163 @@ +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parse::Parse, parse::ParseStream, parse_macro_input, punctuated::Punctuated, LitInt, Token, +}; + +struct DynamicSpacingInput { + values: Punctuated, +} + +// The input for the derive macro is a list of values. +// +// When a single value is provided, the standard spacing formula is +// used to derive the of spacing values. +// +// When a tuple of three values is provided, the values are used as +// the spacing values directly. +enum DynamicSpacingValue { + Single(LitInt), + Tuple(LitInt, LitInt, LitInt), +} + +impl Parse for DynamicSpacingInput { + fn parse(input: ParseStream) -> syn::Result { + Ok(DynamicSpacingInput { + values: input.parse_terminated(DynamicSpacingValue::parse)?, + }) + } +} + +impl Parse for DynamicSpacingValue { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(syn::token::Paren) { + let content; + syn::parenthesized!(content in input); + let a: LitInt = content.parse()?; + content.parse::()?; + let b: LitInt = content.parse()?; + content.parse::()?; + let c: LitInt = content.parse()?; + Ok(DynamicSpacingValue::Tuple(a, b, c)) + } else { + Ok(DynamicSpacingValue::Single(input.parse()?)) + } + } +} + +/// Derives the spacing method for the `DynamicSpacing` enum. +pub fn derive_spacing(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DynamicSpacingInput); + + let spacing_ratios: Vec<_> = input + .values + .iter() + .map(|v| { + let variant = match v { + DynamicSpacingValue::Single(n) => { + format_ident!("Base{:02}", n.base10_parse::().unwrap()) + } + DynamicSpacingValue::Tuple(_, b, _) => { + format_ident!("Base{:02}", b.base10_parse::().unwrap()) + } + }; + match v { + DynamicSpacingValue::Single(n) => { + let n = n.base10_parse::().unwrap(); + quote! { + DynamicSpacing::#variant => match ThemeSettings::get_global(cx).ui_density { + UiDensity::Compact => (#n - 4.0).max(0.0) / BASE_REM_SIZE_IN_PX, + UiDensity::Default => #n / BASE_REM_SIZE_IN_PX, + UiDensity::Comfortable => (#n + 4.0) / BASE_REM_SIZE_IN_PX, + } + } + } + DynamicSpacingValue::Tuple(a, b, c) => { + let a = a.base10_parse::().unwrap(); + let b = b.base10_parse::().unwrap(); + let c = c.base10_parse::().unwrap(); + quote! { + DynamicSpacing::#variant => match ThemeSettings::get_global(cx).ui_density { + UiDensity::Compact => #a / BASE_REM_SIZE_IN_PX, + UiDensity::Default => #b / BASE_REM_SIZE_IN_PX, + UiDensity::Comfortable => #c / BASE_REM_SIZE_IN_PX, + } + } + } + } + }) + .collect(); + + let variant_docs: Vec<_> = input + .values + .iter() + .map(|v| { + let variant = match v { + DynamicSpacingValue::Single(n) => format_ident!("Base{:02}", n.base10_parse::().unwrap()), + DynamicSpacingValue::Tuple(_, b, _) => format_ident!("Base{:02}", b.base10_parse::().unwrap()), + }; + match v { + DynamicSpacingValue::Single(n) => { + // When a single value is passed in, derive the compact and comfortable values. + let n = n.base10_parse::().unwrap(); + let compact = (n - 4.0).max(0.0); + let comfortable = n + 4.0; + quote! { + #[doc = concat!("@16px/rem: `", stringify!(#compact), "px`|`", stringify!(#n), "px`|`", stringify!(#comfortable), "px` - Scales with the user's rem size.")] + #variant, + } + } + DynamicSpacingValue::Tuple(a, b, c) => { + let a = a.base10_parse::().unwrap(); + let b = b.base10_parse::().unwrap(); + let c = c.base10_parse::().unwrap(); + quote! { + #[doc = concat!("@16px/rem: `", stringify!(#a), "px`|`", stringify!(#b), "px`|`", stringify!(#c), "px` - Scales with the user's rem size.")] + #variant, + } + } + } + }) + .collect(); + + let expanded = quote! { + /// A dynamic spacing system that adjusts spacing based on + /// [UiDensity]. + /// + /// The number following "Base" refers to the base pixel size + /// at the default rem size and spacing settings. + /// + /// When possible, [DynamicSpacing] should be used over manual + /// or built-in spacing values in places dynamic spacing is needed. + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum DynamicSpacing { + #( + #[doc = stringify!(#variant_docs)] + #variant_docs + )* + } + + impl DynamicSpacing { + /// Returns the spacing ratio, should only be used internally. + fn spacing_ratio(&self, cx: &WindowContext) -> f32 { + const BASE_REM_SIZE_IN_PX: f32 = 16.0; + match self { + #(#spacing_ratios,)* + } + } + + /// Returns the spacing value in rems. + pub fn rems(&self, cx: &WindowContext) -> Rems { + rems(self.spacing_ratio(cx)) + } + + /// Returns the spacing value in pixels. + pub fn px(&self, cx: &WindowContext) -> Pixels { + let ui_font_size_f32: f32 = ThemeSettings::get_global(cx).ui_font_size.into(); + px(ui_font_size_f32 * self.spacing_ratio(cx)) + } + } + }; + + TokenStream::from(expanded) +} diff --git a/crates/ui_macros/src/ui_macros.rs b/crates/ui_macros/src/ui_macros.rs index a625caefd5111..cd4b852766cb5 100644 --- a/crates/ui_macros/src/ui_macros.rs +++ b/crates/ui_macros/src/ui_macros.rs @@ -1,4 +1,5 @@ mod derive_path_str; +mod dynamic_spacing; use proc_macro::TokenStream; @@ -51,3 +52,9 @@ pub fn path_str(_args: TokenStream, input: TokenStream) -> TokenStream { // This attribute doesn't modify the input, it's just a marker input } + +/// Generates the DynamicSpacing enum used for density-aware spacing in the UI. +#[proc_macro] +pub fn derive_dynamic_spacing(input: TokenStream) -> TokenStream { + dynamic_spacing::derive_spacing(input) +} diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index a0701d29e83ab..306e48c41a9f7 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -421,7 +421,7 @@ impl Pane { // `end_slot`, but due to needing a view here that isn't possible. let right_children = h_flex() // Instead we need to replicate the spacing from the [TabBar]'s `end_slot` here. - .gap(Spacing::Small.rems(cx)) + .gap(DynamicSpacing::Base04.rems(cx)) .child( PopoverMenu::new("pane-tab-bar-popover-menu") .trigger( diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index 24340e4f38bf4..00a00780329fb 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -38,9 +38,9 @@ impl Render for StatusBar { h_flex() .w_full() .justify_between() - .gap(Spacing::Large.rems(cx)) - .py(Spacing::Small.rems(cx)) - .px(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) + .py(DynamicSpacing::Base04.rems(cx)) + .px(DynamicSpacing::Base08.rems(cx)) .bg(cx.theme().colors().status_bar_background) .map(|el| match cx.window_decorations() { Decorations::Server => el, @@ -64,14 +64,14 @@ impl Render for StatusBar { impl StatusBar { fn render_left_tools(&self, cx: &mut ViewContext) -> impl IntoElement { h_flex() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .overflow_x_hidden() .children(self.left_items.iter().map(|item| item.to_any())) } fn render_right_tools(&self, cx: &mut ViewContext) -> impl IntoElement { h_flex() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .children(self.right_items.iter().rev().map(|item| item.to_any())) } } diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index 0cbeb6dd20fb4..3e3c35f815cb1 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -97,9 +97,9 @@ impl Render for Toolbar { v_flex() .group("toolbar") - .p(Spacing::Large.rems(cx)) + .p(DynamicSpacing::Base08.rems(cx)) .when(has_left_items || has_right_items, |this| { - this.gap(Spacing::Large.rems(cx)) + this.gap(DynamicSpacing::Base08.rems(cx)) }) .border_b_1() .border_color(cx.theme().colors().border_variant) @@ -109,7 +109,7 @@ impl Render for Toolbar { h_flex() .min_h(rems_from_px(24.)) .justify_between() - .gap(Spacing::Large.rems(cx)) + .gap(DynamicSpacing::Base08.rems(cx)) .when(has_left_items, |this| { this.child( h_flex()