Merge branch 'n/gpui2ui-settings' into gpui2

Nate Butler created

Change summary

crates/gpui3/src/styled.rs                  | 12 +++
crates/ui2/src/components/list.rs           | 12 +--
crates/ui2/src/components/panel.rs          |  8 +-
crates/ui2/src/components/title_bar.rs      | 10 ++
crates/ui2/src/components/traffic_lights.rs |  3 
crates/ui2/src/elements/button.rs           |  8 +
crates/ui2/src/lib.rs                       |  1 
crates/ui2/src/prelude.rs                   | 45 +++++++++++---
crates/ui2/src/settings.rs                  | 70 +++++++++++++++++++++++
9 files changed, 138 insertions(+), 31 deletions(-)

Detailed changes

crates/gpui3/src/styled.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     self as gpui3, hsla, point, px, relative, rems, AlignItems, Display, Fill, FlexDirection, Hsla,
-    JustifyContent, Length, Position, SharedString, StyleRefinement,
+    JustifyContent, Length, Position, Rems, SharedString, StyleRefinement,
 };
 use crate::{BoxShadow, TextStyleRefinement};
 use smallvec::smallvec;
@@ -350,6 +350,16 @@ pub trait Styled {
         self
     }
 
+    fn text_size(mut self, size: impl Into<Rems>) -> Self
+    where
+        Self: Sized,
+    {
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(size.into());
+        self
+    }
+
     fn text_xs(mut self) -> Self
     where
         Self: Sized,

crates/ui2/src/components/list.rs 🔗

@@ -3,10 +3,10 @@ use std::marker::PhantomData;
 use gpui3::{div, Div};
 
 use crate::prelude::*;
+use crate::settings::user_settings;
 use crate::theme::theme;
 use crate::{
-    h_stack, token, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label, LabelColor,
-    LabelSize,
+    h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label, LabelColor, LabelSize,
 };
 
 #[derive(Clone, Copy, Default, Debug, PartialEq)]
@@ -94,7 +94,6 @@ impl<S: 'static + Send + Sync + Clone> ListHeader<S> {
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         let theme = theme(cx);
-        let token = token();
         let system_color = SystemColor::new();
         let color = ThemeColor::new(cx);
 
@@ -166,7 +165,6 @@ impl<S: 'static + Send + Sync + Clone> ListSubHeader<S> {
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         let theme = theme(cx);
-        let token = token();
 
         h_stack().flex_1().w_full().relative().py_1().child(
             div()
@@ -351,7 +349,6 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
         cx: &mut ViewContext<S>,
     ) -> Option<impl Element<ViewState = S>> {
         let theme = theme(cx);
-        let token = token();
 
         let disclosure_control_icon = if let Some(ToggleState::Toggled) = self.toggle {
             IconElement::new(Icon::ChevronDown)
@@ -374,9 +371,9 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         let theme = theme(cx);
-        let token = token();
         let system_color = SystemColor::new();
         let color = ThemeColor::new(cx);
+        let setting = user_settings();
 
         let left_content = match self.left_content.clone() {
             Some(LeftContent::Icon(i)) => Some(
@@ -408,7 +405,7 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
                     // .ml(rems(0.75 * self.indent_level as f32))
                     .children((0..self.indent_level).map(|_| {
                         div()
-                            .w(token.list_indent_depth)
+                            .w(*setting.list_indent_depth)
                             .h_full()
                             .flex()
                             .justify_center()
@@ -484,7 +481,6 @@ impl<S: 'static + Send + Sync + Clone> List<S> {
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         let theme = theme(cx);
-        let token = token();
         let is_toggleable = self.toggleable != Toggleable::NotToggleable;
         let is_toggled = Toggleable::is_toggled(&self.toggleable);
 

crates/ui2/src/components/panel.rs 🔗

@@ -3,8 +3,9 @@ use std::marker::PhantomData;
 use gpui3::{AbsoluteLength, AnyElement};
 use smallvec::SmallVec;
 
+use crate::settings::user_settings;
+use crate::v_stack;
 use crate::{prelude::*, theme};
-use crate::{token, v_stack};
 
 #[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
 pub enum PanelAllowedSides {
@@ -53,14 +54,14 @@ pub struct Panel<S: 'static + Send + Sync> {
 
 impl<S: 'static + Send + Sync> Panel<S> {
     pub fn new(scroll_state: ScrollState) -> Self {
-        let token = token();
+        let setting = user_settings();
 
         Self {
             state_type: PhantomData,
             scroll_state,
             current_side: PanelSide::default(),
             allowed_sides: PanelAllowedSides::default(),
-            initial_width: token.default_panel_size,
+            initial_width: *setting.default_panel_size,
             width: None,
             children: SmallVec::new(),
         }
@@ -96,7 +97,6 @@ impl<S: 'static + Send + Sync> Panel<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let token = token();
         let theme = theme(cx);
 
         let panel_base;

crates/ui2/src/components/title_bar.rs 🔗

@@ -4,6 +4,7 @@ use std::sync::Arc;
 use gpui3::{view, Context, View};
 
 use crate::prelude::*;
+use crate::settings::user_settings;
 use crate::{
     random_players_with_call_status, theme, Avatar, Button, Icon, IconButton, IconColor, MicStatus,
     PlayerStack, PlayerWithCallStatus, ScreenShareStatus, ToolDivider, TrafficLights,
@@ -93,6 +94,9 @@ impl TitleBar {
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
         let theme = theme(cx);
+        let color = ThemeColor::new(cx);
+        let setting = user_settings();
+
         // let has_focus = cx.window_is_active();
         let has_focus = true;
 
@@ -107,8 +111,7 @@ impl TitleBar {
             .items_center()
             .justify_between()
             .w_full()
-            .h_8()
-            .bg(theme.lowest.base.default.background)
+            .bg(color.background)
             .child(
                 div()
                     .flex()
@@ -123,6 +126,9 @@ impl TitleBar {
                             .flex()
                             .items_center()
                             .gap_1()
+                            .when(*setting.titlebar.show_project_owner, |this| {
+                                this.child(Button::new("iamnbutler"))
+                            })
                             .child(Button::new("zed"))
                             .child(Button::new("nate/gpui2-ui-components")),
                     )

crates/ui2/src/components/traffic_lights.rs 🔗

@@ -1,7 +1,7 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::{theme, token, SystemColor};
+use crate::{theme, SystemColor};
 
 #[derive(Clone, Copy)]
 enum TrafficLightColor {
@@ -62,7 +62,6 @@ impl<S: 'static + Send + Sync> TrafficLights<S> {
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         let theme = theme(cx);
-        let token = token();
 
         div()
             .flex()

crates/ui2/src/elements/button.rs 🔗

@@ -1,9 +1,11 @@
 use std::marker::PhantomData;
 use std::sync::Arc;
 
+use gpui3::rems;
 use gpui3::{DefiniteLength, Hsla, Interactive, MouseButton, WindowContext};
 
 use crate::prelude::*;
+use crate::settings::user_settings;
 use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor, LabelSize};
 
 #[derive(Default, PartialEq, Clone, Copy)]
@@ -148,11 +150,11 @@ impl<S: 'static + Send + Sync + Clone> Button<S> {
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         let icon_color = self.icon_color();
         let border_color = self.border_color(cx);
+        let setting = user_settings();
 
         let mut el = h_stack()
-            .h_6()
-            .px_1()
-            .items_center()
+            .p_1()
+            .text_size(ui_size(1.125))
             .rounded_md()
             .border()
             .border_color(border_color)

crates/ui2/src/lib.rs 🔗

@@ -4,6 +4,7 @@ mod components;
 mod element_ext;
 mod elements;
 pub mod prelude;
+mod settings;
 mod static_data;
 mod theme;
 

crates/ui2/src/prelude.rs 🔗

@@ -3,40 +3,35 @@ pub use gpui3::{
     ViewContext, WindowContext,
 };
 
+use crate::settings::user_settings;
 pub use crate::{theme, ButtonVariant, ElementExt, Theme};
 
-use gpui3::{hsla, rems, rgb, AbsoluteLength, Hsla};
+use gpui3::{hsla, rems, rgb, AbsoluteLength, Hsla, Rems};
 use strum::EnumIter;
 
 #[derive(Clone, Copy)]
-pub struct Token {
+pub struct FakeSettings {
     pub list_indent_depth: AbsoluteLength,
     pub default_panel_size: AbsoluteLength,
-    pub state_hover_background: Hsla,
-    pub state_active_background: Hsla,
 }
 
-impl Default for Token {
+impl Default for FakeSettings {
     fn default() -> Self {
         Self {
             list_indent_depth: rems(0.3).into(),
             default_panel_size: AbsoluteLength::Rems(rems(16.)),
-            state_hover_background: hsla(0.0, 0.0, 0.0, 0.08),
-            state_active_background: hsla(0.0, 0.0, 0.0, 0.16),
         }
     }
 }
 
-pub fn token() -> Token {
-    Token::default()
-}
-
 #[derive(Default)]
 pub struct SystemColor {
     pub transparent: Hsla,
     pub mac_os_traffic_light_red: Hsla,
     pub mac_os_traffic_light_yellow: Hsla,
     pub mac_os_traffic_light_green: Hsla,
+    pub state_hover_background: Hsla,
+    pub state_active_background: Hsla,
 }
 
 impl SystemColor {
@@ -46,6 +41,8 @@ impl SystemColor {
             mac_os_traffic_light_red: rgb::<Hsla>(0xEC695E),
             mac_os_traffic_light_yellow: rgb::<Hsla>(0xF4BF4F),
             mac_os_traffic_light_green: rgb::<Hsla>(0x62C554),
+            state_hover_background: hsla(0.0, 0.0, 0.0, 0.08),
+            state_active_background: hsla(0.0, 0.0, 0.0, 0.16),
         }
     }
     pub fn color(&self) -> Hsla {
@@ -62,6 +59,8 @@ pub struct ThemeColor {
     /// The background color of an elevated surface, like a modal, tooltip or toast.
     pub elevated_surface: Hsla,
     pub surface: Hsla,
+    /// Window background color
+    pub background: Hsla,
     /// Default background for elements like filled buttons,
     /// text fields, checkboxes, radio buttons, etc.
     /// - TODO: Map to step 3.
@@ -99,6 +98,7 @@ impl ThemeColor {
             border_transparent: system_color.transparent,
             elevated_surface: theme.middle.base.default.background,
             surface: theme.middle.base.default.background,
+            background: theme.lowest.base.default.background,
             filled_element: theme.lowest.base.default.background,
             filled_element_hover: theme.lowest.base.hovered.background,
             filled_element_active: theme.lowest.base.active.background,
@@ -157,6 +157,12 @@ impl HighlightColor {
     }
 }
 
+pub fn ui_size(size: f32) -> Rems {
+    let setting = user_settings();
+
+    rems(*setting.ui_scale * size)
+}
+
 #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
 pub enum FileSystemStatus {
     #[default]
@@ -251,6 +257,23 @@ pub enum DisclosureControlVisibility {
     Always,
 }
 
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
+pub enum DisclosureControlStyle {
+    /// Shows the disclosure control only when hovered where possible.
+    ///
+    /// More compact, but not available everywhere.
+    ChevronOnHover,
+    /// Shows an icon where possible, otherwise shows a chevron.
+    ///
+    /// For example, in a file tree a folder or file icon is shown
+    /// instead of a chevron
+    Icon,
+    /// Always shows a chevron.
+    Chevron,
+    /// Completely hides the disclosure control where possible.
+    None,
+}
+
 #[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
 pub enum InteractionState {
     #[default]

crates/ui2/src/settings.rs 🔗

@@ -0,0 +1,70 @@
+use std::ops::Deref;
+
+use gpui3::{rems, AbsoluteLength};
+
+use crate::DisclosureControlStyle;
+
+// This is a fake static example of user settings overriding the default settings
+pub fn user_settings() -> Settings {
+    let mut settings = Settings::default();
+    settings.list_indent_depth = SettingValue::UserDefined(rems(0.5).into());
+    settings
+}
+
+#[derive(Clone)]
+pub enum SettingValue<T> {
+    UserDefined(T),
+    Default(T),
+}
+
+impl<T> Deref for SettingValue<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            Self::UserDefined(value) => value,
+            Self::Default(value) => value,
+        }
+    }
+}
+
+#[derive(Clone)]
+pub struct TitlebarSettings {
+    pub show_project_owner: SettingValue<bool>,
+    pub show_git_status: SettingValue<bool>,
+    pub show_git_controls: SettingValue<bool>,
+}
+
+impl Default for TitlebarSettings {
+    fn default() -> Self {
+        Self {
+            show_project_owner: SettingValue::Default(true),
+            show_git_status: SettingValue::Default(true),
+            show_git_controls: SettingValue::Default(true),
+        }
+    }
+}
+
+// These should be merged into settings
+#[derive(Clone)]
+pub struct Settings {
+    pub default_panel_size: SettingValue<AbsoluteLength>,
+    pub list_disclosure_style: SettingValue<DisclosureControlStyle>,
+    pub list_indent_depth: SettingValue<AbsoluteLength>,
+    pub titlebar: TitlebarSettings,
+    pub ui_scale: SettingValue<f32>,
+}
+
+impl Default for Settings {
+    fn default() -> Self {
+        Self {
+            titlebar: TitlebarSettings::default(),
+            list_disclosure_style: SettingValue::Default(DisclosureControlStyle::ChevronOnHover),
+            list_indent_depth: SettingValue::Default(rems(0.3).into()),
+            default_panel_size: SettingValue::Default(rems(16.).into()),
+            ui_scale: SettingValue::Default(1.),
+        }
+    }
+}
+
+impl Settings {}