Merge

Antonio Scandurra created

Change summary

crates/ui2/src/components/assistant_panel.rs |   2 
crates/ui2/src/components/breadcrumb.rs      |  32 +-
crates/ui2/src/components/buffer.rs          |  20 
crates/ui2/src/components/buffer_search.rs   |  30 -
crates/ui2/src/components/chat_panel.rs      |   3 
crates/ui2/src/components/collab_panel.rs    |  53 +-
crates/ui2/src/components/context_menu.rs    |  20 
crates/ui2/src/components/editor_pane.rs     |   4 
crates/ui2/src/components/facepile.rs        |  10 
crates/ui2/src/components/icon_button.rs     |  25 +
crates/ui2/src/components/keybinding.rs      |   7 
crates/ui2/src/components/list.rs            |  55 +-
crates/ui2/src/components/multi_buffer.rs    |  16 
crates/ui2/src/components/palette.rs         |  15 
crates/ui2/src/components/panel.rs           |  61 +--
crates/ui2/src/components/panes.rs           |   8 
crates/ui2/src/components/project_panel.rs   |   5 
crates/ui2/src/components/status_bar.rs      |  16 
crates/ui2/src/components/tab.rs             |  65 +++-
crates/ui2/src/components/tab_bar.rs         |  36 +
crates/ui2/src/components/terminal.rs        |  12 
crates/ui2/src/components/title_bar.rs       |   6 
crates/ui2/src/components/toolbar.rs         |  15 
crates/ui2/src/components/traffic_lights.rs  |   8 
crates/ui2/src/elements/avatar.rs            |   5 
crates/ui2/src/elements/button.rs            |   4 
crates/ui2/src/elements/details.rs           |  17 
crates/ui2/src/elements/icon.rs              |  14 
crates/ui2/src/elements/input.rs             |  89 +++--
crates/ui2/src/elements/label.rs             |  32 +-
crates/ui2/src/elements/player.rs            |  10 
crates/ui2/src/elements/tool_divider.rs      |   5 
crates/ui2/src/prelude.rs                    | 148 +++++++++
crates/ui2/src/settings.rs                   | 328 +++++++++------------
crates/ui2/src/static_data.rs                | 136 ++++----
crates/ui2/src/story.rs                      |  13 
36 files changed, 727 insertions(+), 598 deletions(-)

Detailed changes

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

@@ -27,7 +27,7 @@ impl<S: 'static + Send + Sync + Clone> AssistantPanel<S> {
     }
 
     fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         Panel::new(cx)
             .children(vec![div()

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

@@ -25,10 +25,9 @@ impl<S: 'static + Send + Sync + Clone> Breadcrumb<S> {
         }
     }
 
-    fn render_separator(&self, theme: &Theme) -> Div<S> {
-        div()
-            .child(" › ")
-            .text_color(HighlightColor::Default.hsla(theme))
+    fn render_separator(&self, cx: &WindowContext) -> Div<S> {
+        let color = ThemeColor::new(cx);
+        div().child(" › ").text_color(color.text_muted)
     }
 
     fn render(
@@ -36,21 +35,22 @@ impl<S: 'static + Send + Sync + Clone> Breadcrumb<S> {
         view_state: &mut S,
         cx: &mut ViewContext<S>,
     ) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
+        let color = ThemeColor::new(cx);
 
         let symbols_len = self.symbols.len();
 
         h_stack()
+            .id("breadcrumb")
             .px_1()
-            // TODO: Read font from theme (or settings?).
-            .font("Zed Mono Extended")
             .text_sm()
-            .text_color(theme.middle.base.default.foreground)
+            .text_color(color.text_muted)
             .rounded_md()
-            .hover(|style| style.bg(theme.highest.base.hovered.background))
+            .hover(|style| style.bg(color.ghost_element_hover))
+            .active(|style| style.bg(color.ghost_element_active))
             .child(self.path.clone().to_str().unwrap().to_string())
             .child(if !self.symbols.is_empty() {
-                self.render_separator(&theme)
+                self.render_separator(cx)
             } else {
                 div()
             })
@@ -68,7 +68,7 @@ impl<S: 'static + Send + Sync + Clone> Breadcrumb<S> {
 
                             let is_last_segment = ix == symbols_len - 1;
                             if !is_last_segment {
-                                items.push(self.render_separator(&theme));
+                                items.push(self.render_separator(cx));
                             }
 
                             items
@@ -107,7 +107,7 @@ mod stories {
             view_state: &mut S,
             cx: &mut ViewContext<S>,
         ) -> impl Element<ViewState = S> {
-            let theme = theme(cx);
+            let color = ThemeColor::new(cx);
 
             Story::container(cx)
                 .child(Story::title_for::<_, Breadcrumb<S>>(cx))
@@ -118,21 +118,21 @@ mod stories {
                         Symbol(vec![
                             HighlightedText {
                                 text: "impl ".to_string(),
-                                color: HighlightColor::Keyword.hsla(&theme),
+                                color: color.syntax.keyword,
                             },
                             HighlightedText {
                                 text: "BreadcrumbStory".to_string(),
-                                color: HighlightColor::Function.hsla(&theme),
+                                color: color.syntax.function,
                             },
                         ]),
                         Symbol(vec![
                             HighlightedText {
                                 text: "fn ".to_string(),
-                                color: HighlightColor::Keyword.hsla(&theme),
+                                color: color.syntax.keyword,
                             },
                             HighlightedText {
                                 text: "render".to_string(),
-                                color: HighlightColor::Function.hsla(&theme),
+                                color: color.syntax.function,
                             },
                         ]),
                     ],

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

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
 use gpui3::{Hsla, WindowContext};
 
 use crate::prelude::*;
-use crate::{h_stack, theme, v_stack, Icon, IconElement};
+use crate::{h_stack, v_stack, Icon, IconElement};
 
 #[derive(Default, PartialEq, Copy, Clone)]
 pub struct PlayerCursor {
@@ -163,19 +163,19 @@ impl<S: 'static + Send + Sync + Clone> Buffer<S> {
     }
 
     fn render_row(row: BufferRow, cx: &WindowContext) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
         let system_color = SystemColor::new();
+        let color = ThemeColor::new(cx);
 
         let line_background = if row.current {
-            theme.middle.base.default.background
+            color.editor_active_line
         } else {
             system_color.transparent
         };
 
         let line_number_color = if row.current {
-            HighlightColor::Default.hsla(&theme)
+            color.text
         } else {
-            HighlightColor::Comment.hsla(&theme)
+            color.syntax.comment
         };
 
         h_stack()
@@ -225,14 +225,14 @@ impl<S: 'static + Send + Sync + Clone> Buffer<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let rows = self.render_rows(cx);
 
         v_stack()
             .flex_1()
             .w_full()
             .h_full()
-            .bg(theme.highest.base.default.background)
+            .bg(color.editor)
             .children(rows)
     }
 }
@@ -268,7 +268,7 @@ mod stories {
             _view: &mut S,
             cx: &mut ViewContext<S>,
         ) -> impl Element<ViewState = S> {
-            let theme = theme(cx);
+            let color = ThemeColor::new(cx);
 
             Story::container(cx)
                 .child(Story::title_for::<_, Buffer<S>>(cx))
@@ -279,14 +279,14 @@ mod stories {
                     div()
                         .w(rems(64.))
                         .h_96()
-                        .child(hello_world_rust_buffer_example(&theme)),
+                        .child(hello_world_rust_buffer_example(&color)),
                 )
                 .child(Story::label(cx, "Hello World (Rust) with Status"))
                 .child(
                     div()
                         .w(rems(64.))
                         .h_96()
-                        .child(hello_world_rust_buffer_with_status_example(&theme)),
+                        .child(hello_world_rust_buffer_with_status_example(&color)),
                 )
         }
     }

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

@@ -3,6 +3,7 @@ use gpui3::{view, Context, View};
 use crate::prelude::*;
 use crate::{h_stack, Icon, IconButton, IconColor, Input};
 
+#[derive(Clone)]
 pub struct BufferSearch {
     is_replace_open: bool,
 }
@@ -21,27 +22,22 @@ impl BufferSearch {
     }
 
     pub fn view(cx: &mut WindowContext) -> View<Self> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         view(cx.entity(|cx| Self::new()), Self::render)
     }
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
-        let theme = theme(cx);
-
-        h_stack()
-            .bg(theme.highest.base.default.background)
-            .p_2()
-            .child(
-                h_stack()
-                    .child(Input::new("Search (↑/↓ for previous/next query)"))
-                    .child(
-                        IconButton::<Self>::new(Icon::Replace)
-                            .when(self.is_replace_open, |this| this.color(IconColor::Accent))
-                            .on_click(|buffer_search, cx| {
-                                buffer_search.toggle_replace(cx);
-                            }),
-                    ),
-            )
+        let color = ThemeColor::new(cx);
+
+        h_stack().bg(color.toolbar).p_2().child(
+            h_stack().child(Input::new("Search")).child(
+                IconButton::<Self>::new(Icon::Replace)
+                    .when(self.is_replace_open, |this| this.color(IconColor::Accent))
+                    .on_click(|buffer_search, cx| {
+                        buffer_search.toggle_replace(cx);
+                    }),
+            ),
+        )
     }
 }

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

@@ -3,7 +3,6 @@ use std::marker::PhantomData;
 use chrono::NaiveDateTime;
 
 use crate::prelude::*;
-use crate::theme::theme;
 use crate::{Icon, IconButton, Input, Label, LabelColor};
 
 #[derive(Element)]
@@ -26,8 +25,6 @@ impl<S: 'static + Send + Sync + Clone> ChatPanel<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
-
         div()
             .flex()
             .flex_col()

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

@@ -3,7 +3,6 @@ use std::marker::PhantomData;
 use gpui3::{img, svg, SharedString};
 
 use crate::prelude::*;
-use crate::theme::{theme, Theme};
 use crate::{
     static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List,
     ListHeader, ToggleState,
@@ -24,7 +23,7 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let color = ThemeColor::new(cx);
 
         v_stack()
@@ -35,20 +34,15 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
                     .w_full()
                     .overflow_y_scroll(self.scroll_state.clone())
                     .child(
-                        div()
-                            .bg(theme.lowest.base.default.background)
-                            .pb_1()
-                            .border_color(theme.lowest.base.default.border)
-                            .border_b()
-                            .child(
-                                List::new(static_collab_panel_current_call())
-                                    .header(
-                                        ListHeader::new("CRDB")
-                                            .set_left_icon(Icon::Hash.into())
-                                            .set_toggle(ToggleState::Toggled),
-                                    )
-                                    .set_toggle(ToggleState::Toggled),
-                            ),
+                        div().pb_1().border_color(color.border).border_b().child(
+                            List::new(static_collab_panel_current_call())
+                                .header(
+                                    ListHeader::new("CRDB")
+                                        .set_left_icon(Icon::Hash.into())
+                                        .set_toggle(ToggleState::Toggled),
+                                )
+                                .set_toggle(ToggleState::Toggled),
+                        ),
                     )
                     .child(
                         v_stack().py_1().child(
@@ -86,13 +80,13 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
                     .h_7()
                     .px_2()
                     .border_t()
-                    .border_color(theme.middle.variant.default.border)
+                    .border_color(color.border)
                     .flex()
                     .items_center()
                     .child(
                         div()
                             .text_sm()
-                            .text_color(theme.middle.variant.default.foreground)
+                            .text_color(color.text_placeholder)
                             .child("Find..."),
                     ),
             )
@@ -102,8 +96,9 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
         &self,
         label: impl Into<SharedString>,
         expanded: bool,
-        theme: &Theme,
+        cx: &WindowContext,
     ) -> impl Element<ViewState = S> {
+        let color = ThemeColor::new(cx);
         div()
             .h_7()
             .px_2()
@@ -121,7 +116,7 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
                         })
                         .w_3p5()
                         .h_3p5()
-                        .text_color(theme.middle.variant.default.foreground),
+                        .text_color(color.icon_muted),
                 ),
             )
     }
@@ -130,16 +125,18 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
         &self,
         avatar_uri: impl Into<SharedString>,
         label: impl Into<SharedString>,
-        theme: &Theme,
+        cx: &WindowContext,
     ) -> impl Element<ViewState = S> {
+        let color = ThemeColor::new(cx);
+
         div()
             .id("list_item")
             .h_7()
             .px_2()
             .flex()
             .items_center()
-            .hover(|style| style.bg(theme.lowest.variant.hovered.background))
-            .active(|style| style.bg(theme.lowest.variant.pressed.background))
+            .hover(|style| style.bg(color.ghost_element_hover))
+            .active(|style| style.bg(color.ghost_element_active))
             .child(
                 div()
                     .flex()
@@ -147,11 +144,11 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
                     .gap_1()
                     .text_sm()
                     .child(
-                        img().uri(avatar_uri).size_3p5().rounded_full().bg(theme
-                            .middle
-                            .positive
-                            .default
-                            .foreground),
+                        img()
+                            .uri(avatar_uri)
+                            .size_3p5()
+                            .rounded_full()
+                            .bg(color.image_fallback_background),
                     )
                     .child(label.into()),
             )

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

@@ -1,14 +1,13 @@
 use crate::{prelude::*, ListItemVariant};
-use crate::{theme, v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
+use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
 
-#[derive(Clone)]
-pub enum ContextMenuItem<S: 'static + Send + Sync + Clone> {
+pub enum ContextMenuItem<S: 'static + Send + Sync> {
     Header(SharedString),
     Entry(Label<S>),
     Separator,
 }
 
-impl<S: 'static + Send + Sync + Clone> ContextMenuItem<S> {
+impl<S: 'static + Send + Sync> ContextMenuItem<S> {
     fn to_list_item(self) -> ListItem<S> {
         match self {
             ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
@@ -33,29 +32,28 @@ impl<S: 'static + Send + Sync + Clone> ContextMenuItem<S> {
 }
 
 #[derive(Element)]
-pub struct ContextMenu<S: 'static + Send + Sync + Clone> {
+pub struct ContextMenu<S: 'static + Send + Sync> {
     items: Vec<ContextMenuItem<S>>,
 }
 
-impl<S: 'static + Send + Sync + Clone> ContextMenu<S> {
+impl<S: 'static + Send + Sync> ContextMenu<S> {
     pub fn new(items: impl IntoIterator<Item = ContextMenuItem<S>>) -> Self {
         Self {
             items: items.into_iter().collect(),
         }
     }
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         v_stack()
             .flex()
-            .bg(theme.lowest.base.default.background)
+            .bg(color.elevated_surface)
             .border()
-            .border_color(theme.lowest.base.default.border)
+            .border_color(color.border)
             .child(
                 List::new(
                     self.items
-                        .clone()
-                        .into_iter()
+                        .drain(..)
                         .map(ContextMenuItem::to_list_item)
                         .collect(),
                 )

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

@@ -43,7 +43,7 @@ impl EditorPane {
     }
 
     pub fn view(cx: &mut WindowContext) -> View<Self> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         view(
             cx.entity(|cx| hello_world_rust_editor_with_status_example(cx)),
@@ -56,7 +56,7 @@ impl EditorPane {
             .w_full()
             .h_full()
             .flex_1()
-            .child(TabBar::new(self.tabs.clone()))
+            .child(TabBar::new(self.tabs.clone()).can_navigate((false, true)))
             .child(
                 Toolbar::new()
                     .left_item(Breadcrumb::new(self.path.clone(), self.symbols.clone()))

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

@@ -1,7 +1,7 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::{theme, Avatar, Player};
+use crate::{Avatar, Player};
 
 #[derive(Element)]
 pub struct Facepile<S: 'static + Send + Sync> {
@@ -18,7 +18,7 @@ impl<S: 'static + Send + Sync> Facepile<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let player_count = self.players.len();
         let player_list = self.players.iter().enumerate().map(|(ix, player)| {
             let isnt_last = ix < player_count - 1;
@@ -52,7 +52,11 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             let players = static_players();
 
             Story::container(cx)

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

@@ -4,7 +4,7 @@ use std::sync::Arc;
 use gpui3::{MouseButton, StatelesslyInteractive};
 
 use crate::{h_stack, prelude::*};
-use crate::{theme, ClickHandler, Icon, IconColor, IconElement};
+use crate::{ClickHandler, Icon, IconColor, IconElement};
 
 struct IconButtonHandlers<S: 'static + Send + Sync> {
     click: Option<ClickHandler<S>>,
@@ -67,7 +67,6 @@ impl<S: 'static + Send + Sync> IconButton<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
         let color = ThemeColor::new(cx);
 
         let icon_color = match (self.state, self.color) {
@@ -75,15 +74,29 @@ impl<S: 'static + Send + Sync> IconButton<S> {
             _ => self.color,
         };
 
+        let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
+            ButtonVariant::Filled => (
+                color.filled_element,
+                color.filled_element_hover,
+                color.filled_element_active,
+            ),
+            ButtonVariant::Ghost => (
+                color.ghost_element,
+                color.ghost_element_hover,
+                color.ghost_element_active,
+            ),
+        };
+
         let mut button = h_stack()
+            // TODO: We probably need a more robust method for differentiating `IconButton`s from one another.
+            .id(SharedString::from(format!("{:?}", self.icon)))
             .justify_center()
             .rounded_md()
             .py(ui_size(cx, 0.25))
             .px(ui_size(cx, 6. / 14.))
-            .when(self.variant == ButtonVariant::Filled, |this| {
-                this.bg(color.filled_element)
-            })
-            .hover(|style| style.bg(theme.highest.base.hovered.background))
+            .bg(bg_color)
+            .hover(|style| style.bg(bg_hover_color))
+            .active(|style| style.bg(bg_active_color))
             .child(IconElement::new(self.icon).color(icon_color));
 
         if let Some(click_handler) = self.handlers.click.clone() {

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

@@ -4,7 +4,6 @@ use std::marker::PhantomData;
 use strum::{EnumIter, IntoEnumIterator};
 
 use crate::prelude::*;
-use crate::theme;
 
 #[derive(Element, Clone)]
 pub struct Keybinding<S: 'static + Send + Sync + Clone> {
@@ -70,15 +69,15 @@ impl<S: 'static + Send + Sync> Key<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .px_2()
             .py_0()
             .rounded_md()
             .text_sm()
-            .text_color(theme.lowest.on.default.foreground)
-            .bg(theme.lowest.on.default.background)
+            .text_color(color.text)
+            .bg(color.filled_element)
             .child(self.key.clone())
     }
 }

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

@@ -4,7 +4,6 @@ use gpui3::{div, Div};
 
 use crate::prelude::*;
 use crate::settings::user_settings;
-use crate::theme::theme;
 use crate::{h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label, LabelColor};
 
 #[derive(Clone, Copy, Default, Debug, PartialEq)]
@@ -15,8 +14,8 @@ pub enum ListItemVariant {
     Inset,
 }
 
-#[derive(Element, Clone)]
-pub struct ListHeader<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub struct ListHeader<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     label: SharedString,
     left_icon: Option<Icon>,
@@ -25,7 +24,7 @@ pub struct ListHeader<S: 'static + Send + Sync + Clone> {
     toggleable: Toggleable,
 }
 
-impl<S: 'static + Send + Sync + Clone> ListHeader<S> {
+impl<S: 'static + Send + Sync> ListHeader<S> {
     pub fn new(label: impl Into<SharedString>) -> Self {
         Self {
             state_type: PhantomData,
@@ -91,7 +90,7 @@ 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 color = ThemeColor::new(cx);
         let system_color = SystemColor::new();
         let color = ThemeColor::new(cx);
 
@@ -134,15 +133,15 @@ impl<S: 'static + Send + Sync + Clone> ListHeader<S> {
     }
 }
 
-#[derive(Element, Clone)]
-pub struct ListSubHeader<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub struct ListSubHeader<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     label: SharedString,
     left_icon: Option<Icon>,
     variant: ListItemVariant,
 }
 
-impl<S: 'static + Send + Sync + Clone> ListSubHeader<S> {
+impl<S: 'static + Send + Sync> ListSubHeader<S> {
     pub fn new(label: impl Into<SharedString>) -> Self {
         Self {
             state_type: PhantomData,
@@ -158,7 +157,7 @@ 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 color = ThemeColor::new(cx);
 
         h_stack().flex_1().w_full().relative().py_1().child(
             div()
@@ -199,32 +198,32 @@ pub enum ListEntrySize {
     Medium,
 }
 
-#[derive(Clone, Element)]
-pub enum ListItem<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub enum ListItem<S: 'static + Send + Sync> {
     Entry(ListEntry<S>),
     Separator(ListSeparator<S>),
     Header(ListSubHeader<S>),
 }
 
-impl<S: 'static + Send + Sync + Clone> From<ListEntry<S>> for ListItem<S> {
+impl<S: 'static + Send + Sync> From<ListEntry<S>> for ListItem<S> {
     fn from(entry: ListEntry<S>) -> Self {
         Self::Entry(entry)
     }
 }
 
-impl<S: 'static + Send + Sync + Clone> From<ListSeparator<S>> for ListItem<S> {
+impl<S: 'static + Send + Sync> From<ListSeparator<S>> for ListItem<S> {
     fn from(entry: ListSeparator<S>) -> Self {
         Self::Separator(entry)
     }
 }
 
-impl<S: 'static + Send + Sync + Clone> From<ListSubHeader<S>> for ListItem<S> {
+impl<S: 'static + Send + Sync> From<ListSubHeader<S>> for ListItem<S> {
     fn from(entry: ListSubHeader<S>) -> Self {
         Self::Header(entry)
     }
 }
 
-impl<S: 'static + Send + Sync + Clone> ListItem<S> {
+impl<S: 'static + Send + Sync> ListItem<S> {
     fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
         match self {
             ListItem::Entry(entry) => div().child(entry.render(view, cx)),
@@ -246,11 +245,11 @@ impl<S: 'static + Send + Sync + Clone> ListItem<S> {
     }
 }
 
-#[derive(Element, Clone)]
-pub struct ListEntry<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub struct ListEntry<S: 'static + Send + Sync> {
     disclosure_control_style: DisclosureControlVisibility,
     indent_level: u32,
-    label: Label<S>,
+    label: Option<Label<S>>,
     left_content: Option<LeftContent>,
     variant: ListItemVariant,
     size: ListEntrySize,
@@ -258,12 +257,12 @@ pub struct ListEntry<S: 'static + Send + Sync + Clone> {
     toggle: Option<ToggleState>,
 }
 
-impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
+impl<S: 'static + Send + Sync> ListEntry<S> {
     pub fn new(label: Label<S>) -> Self {
         Self {
             disclosure_control_style: DisclosureControlVisibility::default(),
             indent_level: 0,
-            label,
+            label: Some(label),
             variant: ListItemVariant::default(),
             left_content: None,
             size: ListEntrySize::default(),
@@ -338,7 +337,7 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
         &mut self,
         cx: &mut ViewContext<S>,
     ) -> Option<impl Element<ViewState = S>> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         let disclosure_control_icon = if let Some(ToggleState::Toggled) = self.toggle {
             IconElement::new(Icon::ChevronDown)
@@ -360,7 +359,7 @@ 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 color = ThemeColor::new(cx);
         let system_color = SystemColor::new();
         let color = ThemeColor::new(cx);
         let settings = user_settings(cx);
@@ -412,7 +411,7 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
                     .relative()
                     .children(self.disclosure_control(cx))
                     .children(left_content)
-                    .child(self.label.clone()),
+                    .children(self.label.take()),
             )
     }
 }
@@ -437,14 +436,14 @@ impl<S: 'static + Send + Sync> ListSeparator<S> {
 }
 
 #[derive(Element)]
-pub struct List<S: 'static + Send + Sync + Clone> {
+pub struct List<S: 'static + Send + Sync> {
     items: Vec<ListItem<S>>,
     empty_message: SharedString,
     header: Option<ListHeader<S>>,
     toggleable: Toggleable,
 }
 
-impl<S: 'static + Send + Sync + Clone> List<S> {
+impl<S: 'static + Send + Sync> List<S> {
     pub fn new(items: Vec<ListItem<S>>) -> Self {
         Self {
             items,
@@ -470,13 +469,13 @@ 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 color = ThemeColor::new(cx);
         let is_toggleable = self.toggleable != Toggleable::NotToggleable;
         let is_toggled = Toggleable::is_toggled(&self.toggleable);
 
         let list_content = match (self.items.is_empty(), is_toggled) {
             (_, false) => div(),
-            (false, _) => div().children(self.items.iter().cloned()),
+            (false, _) => div().children(self.items.drain(..)),
             (true, _) => {
                 div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted))
             }
@@ -486,7 +485,7 @@ impl<S: 'static + Send + Sync + Clone> List<S> {
             .py_1()
             .children(
                 self.header
-                    .clone()
+                    .take()
                     .map(|header| header.set_toggleable(self.toggleable)),
             )
             .child(list_content)

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

@@ -18,7 +18,7 @@ impl<S: 'static + Send + Sync + Clone> MultiBuffer<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         v_stack()
             .w_full()
@@ -32,7 +32,7 @@ impl<S: 'static + Send + Sync + Clone> MultiBuffer<S> {
                             .items_center()
                             .justify_between()
                             .p_4()
-                            .bg(theme.lowest.base.default.background)
+                            .bg(color.editor_subheader)
                             .child(Label::new("main.rs"))
                             .child(IconButton::new(Icon::ArrowUpRight)),
                     )
@@ -67,17 +67,17 @@ mod stories {
             _view: &mut S,
             cx: &mut ViewContext<S>,
         ) -> impl Element<ViewState = S> {
-            let theme = theme(cx);
+            let color = ThemeColor::new(cx);
 
             Story::container(cx)
                 .child(Story::title_for::<_, MultiBuffer<S>>(cx))
                 .child(Story::label(cx, "Default"))
                 .child(MultiBuffer::new(vec![
-                    hello_world_rust_buffer_example(&theme),
-                    hello_world_rust_buffer_example(&theme),
-                    hello_world_rust_buffer_example(&theme),
-                    hello_world_rust_buffer_example(&theme),
-                    hello_world_rust_buffer_example(&theme),
+                    hello_world_rust_buffer_example(&color),
+                    hello_world_rust_buffer_example(&color),
+                    hello_world_rust_buffer_example(&color),
+                    hello_world_rust_buffer_example(&color),
+                    hello_world_rust_buffer_example(&color),
                 ]))
         }
     }

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

@@ -1,7 +1,6 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::theme::theme;
 use crate::{h_stack, v_stack, Keybinding, Label, LabelColor};
 
 #[derive(Element)]
@@ -48,21 +47,21 @@ impl<S: 'static + Send + Sync + Clone> Palette<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         v_stack()
             .w_96()
             .rounded_lg()
-            .bg(theme.lowest.base.default.background)
+            .bg(color.elevated_surface)
             .border()
-            .border_color(theme.lowest.base.default.border)
+            .border_color(color.border)
             .child(
                 v_stack()
                     .gap_px()
                     .child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child(
                         Label::new(self.input_placeholder.clone()).color(LabelColor::Placeholder),
                     )))
-                    .child(div().h_px().w_full().bg(theme.lowest.base.default.border))
+                    .child(div().h_px().w_full().bg(color.filled_element))
                     .child(
                         v_stack()
                             .py_0p5()
@@ -91,8 +90,8 @@ impl<S: 'static + Send + Sync + Clone> Palette<S> {
                                     .px_2()
                                     .py_0p5()
                                     .rounded_lg()
-                                    .hover(|style| style.bg(theme.lowest.base.hovered.background))
-                                    .active(|style| style.bg(theme.lowest.base.pressed.background))
+                                    .hover(|style| style.bg(color.ghost_element_hover))
+                                    .active(|style| style.bg(color.ghost_element_active))
                                     .child(item.clone())
                             })),
                     ),
@@ -135,7 +134,7 @@ impl<S: 'static + Send + Sync + Clone> PaletteItem<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .flex()

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

@@ -3,9 +3,9 @@ use std::marker::PhantomData;
 use gpui3::{AbsoluteLength, AnyElement};
 use smallvec::SmallVec;
 
+use crate::prelude::*;
 use crate::settings::user_settings;
 use crate::v_stack;
-use crate::{prelude::*, theme};
 
 #[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
 pub enum PanelAllowedSides {
@@ -97,45 +97,26 @@ impl<S: 'static + Send + Sync> Panel<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
-
-        let panel_base;
-        let current_width = self.width.unwrap_or(self.initial_width);
-
-        match self.current_side {
-            PanelSide::Left => {
-                panel_base = v_stack()
-                    .flex_initial()
-                    .h_full()
-                    // .w(current_width)
-                    .w_64()
-                    .bg(theme.middle.base.default.background)
-                    .border_r()
-                    .border_color(theme.middle.base.default.border);
-            }
-            PanelSide::Right => {
-                panel_base = v_stack()
-                    .flex_initial()
-                    .h_full()
-                    // .w(current_width)
-                    .w_64()
-                    .bg(theme.middle.base.default.background)
-                    .border_l()
-                    .border_color(theme.middle.base.default.border);
-            }
-            PanelSide::Bottom => {
-                panel_base = v_stack()
-                    .flex_initial()
-                    .w_full()
-                    // .h(current_width)
-                    .h_64()
-                    .bg(theme.middle.base.default.background)
-                    .border_t()
-                    .border_color(theme.middle.base.default.border);
-            }
-        }
-
-        panel_base.children(self.children.drain(..))
+        let color = ThemeColor::new(cx);
+
+        let current_size = self.width.unwrap_or(self.initial_width);
+
+        v_stack()
+            .flex_initial()
+            .when(
+                self.current_side == PanelSide::Left || self.current_side == PanelSide::Right,
+                |this| this.h_full().w(current_size),
+            )
+            .when(self.current_side == PanelSide::Left, |this| this.border_r())
+            .when(self.current_side == PanelSide::Right, |this| {
+                this.border_l()
+            })
+            .when(self.current_side == PanelSide::Bottom, |this| {
+                this.border_b().w_full().h(current_size)
+            })
+            .bg(color.surface)
+            .border_color(color.border)
+            .children(self.children.drain(..))
     }
 }
 

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

@@ -4,7 +4,6 @@ use gpui3::{hsla, AnyElement, Hsla, Length, Size};
 use smallvec::SmallVec;
 
 use crate::prelude::*;
-use crate::theme;
 
 #[derive(Default, PartialEq)]
 pub enum SplitDirection {
@@ -43,7 +42,7 @@ impl<S: 'static + Send + Sync> Pane<S> {
     }
 
     fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .flex()
@@ -90,7 +89,7 @@ impl<S: 'static + Send + Sync> PaneGroup<S> {
     }
 
     fn render(&mut self, view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         if !self.panes.is_empty() {
             let el = div()
@@ -99,7 +98,6 @@ impl<S: 'static + Send + Sync> PaneGroup<S> {
                 .gap_px()
                 .w_full()
                 .h_full()
-                .bg(theme.lowest.base.default.background)
                 .children(self.panes.iter_mut().map(|pane| pane.render(view, cx)));
 
             if self.split_direction == SplitDirection::Horizontal {
@@ -116,7 +114,7 @@ impl<S: 'static + Send + Sync> PaneGroup<S> {
                 .gap_px()
                 .w_full()
                 .h_full()
-                .bg(theme.lowest.base.default.background)
+                .bg(color.editor)
                 .children(self.groups.iter_mut().map(|group| group.render(view, cx)));
 
             if self.split_direction == SplitDirection::Horizontal {

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

@@ -2,8 +2,7 @@ use std::marker::PhantomData;
 
 use crate::prelude::*;
 use crate::{
-    static_project_panel_project_items, static_project_panel_single_items, theme, Input, List,
-    ListHeader,
+    static_project_panel_project_items, static_project_panel_single_items, Input, List, ListHeader,
 };
 
 #[derive(Element)]
@@ -21,7 +20,7 @@ impl<S: 'static + Send + Sync + Clone> ProjectPanel<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let color = ThemeColor::new(cx);
 
         div()

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

@@ -87,7 +87,7 @@ impl StatusBar {
         view: &mut Workspace,
         cx: &mut ViewContext<Workspace>,
     ) -> impl Element<ViewState = Workspace> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .py_0p5()
@@ -96,16 +96,18 @@ impl StatusBar {
             .items_center()
             .justify_between()
             .w_full()
-            .bg(theme.lowest.base.default.background)
-            .child(self.left_tools(view, &theme))
-            .child(self.right_tools(view, &theme))
+            .bg(color.status_bar)
+            .child(self.left_tools(view, cx))
+            .child(self.right_tools(view, cx))
     }
 
     fn left_tools(
         &self,
         workspace: &mut Workspace,
-        theme: &Theme,
+        cx: &WindowContext,
     ) -> impl Element<ViewState = Workspace> {
+        let color = ThemeColor::new(cx);
+
         div()
             .flex()
             .items_center()
@@ -135,8 +137,10 @@ impl StatusBar {
     fn right_tools(
         &self,
         workspace: &mut Workspace,
-        theme: &Theme,
+        cx: &WindowContext,
     ) -> impl Element<ViewState = Workspace> {
+        let color = ThemeColor::new(cx);
+
         div()
             .flex()
             .items_center()

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

@@ -1,11 +1,12 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::{theme, Icon, IconColor, IconElement, Label, LabelColor};
+use crate::{Icon, IconColor, IconElement, Label, LabelColor};
 
 #[derive(Element, Clone)]
 pub struct Tab<S: 'static + Send + Sync + Clone> {
     state_type: PhantomData<S>,
+    id: ElementId,
     title: String,
     icon: Option<Icon>,
     current: bool,
@@ -17,9 +18,10 @@ pub struct Tab<S: 'static + Send + Sync + Clone> {
 }
 
 impl<S: 'static + Send + Sync + Clone> Tab<S> {
-    pub fn new() -> Self {
+    pub fn new(id: impl Into<ElementId>) -> Self {
         Self {
             state_type: PhantomData,
+            id: id.into(),
             title: "untitled".to_string(),
             icon: None,
             current: false,
@@ -75,7 +77,7 @@ impl<S: 'static + Send + Sync + Clone> Tab<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
         let is_deleted = self.fs_status == FileSystemStatus::Deleted;
 
@@ -94,19 +96,31 @@ impl<S: 'static + Send + Sync + Clone> Tab<S> {
             (GitStatus::Conflict, false) => Label::new(self.title.clone()),
         };
 
-        let close_icon = IconElement::new(Icon::Close).color(IconColor::Muted);
+        let close_icon = || IconElement::new(Icon::Close).color(IconColor::Muted);
+
+        let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
+            true => (
+                color.ghost_element,
+                color.ghost_element_hover,
+                color.ghost_element_active,
+            ),
+            false => (
+                color.filled_element,
+                color.filled_element_hover,
+                color.filled_element_active,
+            ),
+        };
 
         div()
+            .id(self.id.clone())
             .px_2()
             .py_0p5()
             .flex()
             .items_center()
             .justify_center()
-            .bg(if self.current {
-                theme.highest.base.default.background
-            } else {
-                theme.middle.base.default.background
-            })
+            .bg(tab_bg)
+            .hover(|h| h.bg(tab_hover_bg))
+            .active(|a| a.bg(tab_active_bg))
             .child(
                 div()
                     .px_1()
@@ -120,13 +134,13 @@ impl<S: 'static + Send + Sync + Clone> Tab<S> {
                     }))
                     .children(self.icon.map(IconElement::new))
                     .children(if self.close_side == IconSide::Left {
-                        Some(close_icon.clone())
+                        Some(close_icon())
                     } else {
                         None
                     })
                     .child(label)
                     .children(if self.close_side == IconSide::Right {
-                        Some(close_icon)
+                        Some(close_icon())
                     } else {
                         None
                     }),
@@ -134,6 +148,7 @@ impl<S: 'static + Send + Sync + Clone> Tab<S> {
     }
 }
 
+use gpui3::ElementId;
 #[cfg(feature = "stories")]
 pub use stories::*;
 
@@ -172,7 +187,7 @@ mod stories {
                         v_stack()
                             .gap_2()
                             .child(Story::label(cx, "Default"))
-                            .child(Tab::new()),
+                            .child(Tab::new("default")),
                     ),
                 )
                 .child(
@@ -180,8 +195,16 @@ mod stories {
                         v_stack().gap_2().child(Story::label(cx, "Current")).child(
                             h_stack()
                                 .gap_4()
-                                .child(Tab::new().title("Current".to_string()).current(true))
-                                .child(Tab::new().title("Not Current".to_string()).current(false)),
+                                .child(
+                                    Tab::new("current")
+                                        .title("Current".to_string())
+                                        .current(true),
+                                )
+                                .child(
+                                    Tab::new("not_current")
+                                        .title("Not Current".to_string())
+                                        .current(false),
+                                ),
                         ),
                     ),
                 )
@@ -190,7 +213,7 @@ mod stories {
                         v_stack()
                             .gap_2()
                             .child(Story::label(cx, "Titled"))
-                            .child(Tab::new().title("label".to_string())),
+                            .child(Tab::new("titled").title("label".to_string())),
                     ),
                 )
                 .child(
@@ -199,7 +222,7 @@ mod stories {
                             .gap_2()
                             .child(Story::label(cx, "With Icon"))
                             .child(
-                                Tab::new()
+                                Tab::new("with_icon")
                                     .title("label".to_string())
                                     .icon(Some(Icon::Envelope)),
                             ),
@@ -214,11 +237,11 @@ mod stories {
                                 h_stack()
                                     .gap_4()
                                     .child(
-                                        Tab::new()
+                                        Tab::new("left")
                                             .title("Left".to_string())
                                             .close_side(IconSide::Left),
                                     )
-                                    .child(Tab::new().title("Right".to_string())),
+                                    .child(Tab::new("right").title("Right".to_string())),
                             ),
                     ),
                 )
@@ -227,7 +250,7 @@ mod stories {
                         .gap_2()
                         .child(Story::label(cx, "Git Status"))
                         .child(h_stack().gap_4().children(git_statuses.map(|git_status| {
-                            Tab::new()
+                            Tab::new("git_status")
                                 .title(git_status.to_string())
                                 .git_status(git_status)
                         }))),
@@ -237,7 +260,9 @@ mod stories {
                         .gap_2()
                         .child(Story::label(cx, "File System Status"))
                         .child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
-                            Tab::new().title(fs_status.to_string()).fs_status(fs_status)
+                            Tab::new("file_system_status")
+                                .title(fs_status.to_string())
+                                .fs_status(fs_status)
                         }))),
                 )
         }

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

@@ -1,12 +1,14 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::{theme, Icon, IconButton, Tab};
+use crate::{Icon, IconButton, Tab};
 
 #[derive(Element)]
 pub struct TabBar<S: 'static + Send + Sync + Clone> {
     state_type: PhantomData<S>,
     scroll_state: ScrollState,
+    /// Backwards, Forwards
+    can_navigate: (bool, bool),
     tabs: Vec<Tab<S>>,
 }
 
@@ -15,6 +17,7 @@ impl<S: 'static + Send + Sync + Clone> TabBar<S> {
         Self {
             state_type: PhantomData,
             scroll_state: ScrollState::default(),
+            can_navigate: (false, false),
             tabs,
         }
     }
@@ -23,15 +26,20 @@ impl<S: 'static + Send + Sync + Clone> TabBar<S> {
         self.scroll_state = scroll_state;
     }
 
+    pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
+        self.can_navigate = can_navigate;
+        self
+    }
+
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
-        let can_navigate_back = true;
-        let can_navigate_forward = false;
+        let color = ThemeColor::new(cx);
+
+        let (can_navigate_back, can_navigate_forward) = self.can_navigate;
 
         div()
             .w_full()
             .flex()
-            .bg(theme.middle.base.default.background)
+            .bg(color.tab_bar)
             // Left Side
             .child(
                 div()
@@ -114,33 +122,33 @@ mod stories {
                 .child(Story::title_for::<_, TabBar<S>>(cx))
                 .child(Story::label(cx, "Default"))
                 .child(TabBar::new(vec![
-                    Tab::new()
+                    Tab::new(1)
                         .title("Cargo.toml".to_string())
                         .current(false)
                         .git_status(GitStatus::Modified),
-                    Tab::new()
+                    Tab::new(2)
                         .title("Channels Panel".to_string())
                         .current(false),
-                    Tab::new()
+                    Tab::new(3)
                         .title("channels_panel.rs".to_string())
                         .current(true)
                         .git_status(GitStatus::Modified),
-                    Tab::new()
+                    Tab::new(4)
                         .title("workspace.rs".to_string())
                         .current(false)
                         .git_status(GitStatus::Modified),
-                    Tab::new()
+                    Tab::new(5)
                         .title("icon_button.rs".to_string())
                         .current(false),
-                    Tab::new()
+                    Tab::new(6)
                         .title("storybook.rs".to_string())
                         .current(false)
                         .git_status(GitStatus::Created),
-                    Tab::new().title("theme.rs".to_string()).current(false),
-                    Tab::new()
+                    Tab::new(7).title("theme.rs".to_string()).current(false),
+                    Tab::new(8)
                         .title("theme_registry.rs".to_string())
                         .current(false),
-                    Tab::new()
+                    Tab::new(9)
                         .title("styleable_helpers.rs".to_string())
                         .current(false),
                 ]))

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

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
 use gpui3::{relative, rems, Size};
 
 use crate::prelude::*;
-use crate::{theme, Icon, IconButton, Pane, Tab};
+use crate::{Icon, IconButton, Pane, Tab};
 
 #[derive(Element)]
 pub struct Terminal<S: 'static + Send + Sync + Clone> {
@@ -18,7 +18,7 @@ impl<S: 'static + Send + Sync + Clone> Terminal<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         let can_navigate_back = true;
         let can_navigate_forward = false;
@@ -32,7 +32,7 @@ impl<S: 'static + Send + Sync + Clone> Terminal<S> {
                 div()
                     .w_full()
                     .flex()
-                    .bg(theme.middle.base.default.background)
+                    .bg(color.surface)
                     .child(
                         div().px_1().flex().flex_none().gap_2().child(
                             div()
@@ -54,14 +54,14 @@ impl<S: 'static + Send + Sync + Clone> Terminal<S> {
                             div()
                                 .flex()
                                 .child(
-                                    Tab::new()
+                                    Tab::new(1)
                                         .title("zed — fish".to_string())
                                         .icon(Icon::Terminal)
                                         .close_side(IconSide::Right)
                                         .current(true),
                                 )
                                 .child(
-                                    Tab::new()
+                                    Tab::new(2)
                                         .title("zed — fish".to_string())
                                         .icon(Icon::Terminal)
                                         .close_side(IconSide::Right)
@@ -79,7 +79,7 @@ impl<S: 'static + Send + Sync + Clone> Terminal<S> {
                         height: rems(36.).into(),
                     },
                 )
-                .child(crate::static_data::terminal_buffer(&theme)),
+                .child(crate::static_data::terminal_buffer(&color)),
             )
     }
 }

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

@@ -6,8 +6,8 @@ use gpui3::{view, Context, View};
 use crate::prelude::*;
 use crate::settings::user_settings;
 use crate::{
-    theme, Avatar, Button, Icon, IconButton, IconColor, MicStatus, PlayerStack,
-    PlayerWithCallStatus, ScreenShareStatus, ToolDivider, TrafficLights,
+    Avatar, Button, Icon, IconButton, IconColor, MicStatus, PlayerStack, PlayerWithCallStatus,
+    ScreenShareStatus, ToolDivider, TrafficLights,
 };
 
 #[derive(Clone)]
@@ -88,7 +88,7 @@ impl TitleBar {
     }
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let color = ThemeColor::new(cx);
         let settings = user_settings(cx);
 

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

@@ -2,7 +2,6 @@ use gpui3::AnyElement;
 use smallvec::SmallVec;
 
 use crate::prelude::*;
-use crate::theme;
 
 #[derive(Clone)]
 pub struct ToolbarItem {}
@@ -56,10 +55,10 @@ impl<S: 'static + Send + Sync> Toolbar<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
-            .bg(theme.highest.base.default.background)
+            .bg(color.toolbar)
             .p_2()
             .flex()
             .justify_between()
@@ -98,7 +97,7 @@ mod stories {
             _view: &mut S,
             cx: &mut ViewContext<S>,
         ) -> impl Element<ViewState = S> {
-            let theme = theme(cx);
+            let color = ThemeColor::new(cx);
 
             Story::container(cx)
                 .child(Story::title_for::<_, Toolbar<S>>(cx))
@@ -111,21 +110,21 @@ mod stories {
                                 Symbol(vec![
                                     HighlightedText {
                                         text: "impl ".to_string(),
-                                        color: HighlightColor::Keyword.hsla(&theme),
+                                        color: color.syntax.keyword,
                                     },
                                     HighlightedText {
                                         text: "ToolbarStory".to_string(),
-                                        color: HighlightColor::Function.hsla(&theme),
+                                        color: color.syntax.function,
                                     },
                                 ]),
                                 Symbol(vec![
                                     HighlightedText {
                                         text: "fn ".to_string(),
-                                        color: HighlightColor::Keyword.hsla(&theme),
+                                        color: color.syntax.keyword,
                                     },
                                     HighlightedText {
                                         text: "render".to_string(),
-                                        color: HighlightColor::Function.hsla(&theme),
+                                        color: color.syntax.function,
                                     },
                                 ]),
                             ],

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

@@ -1,7 +1,7 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::{theme, SystemColor};
+use crate::SystemColor;
 
 #[derive(Clone, Copy)]
 enum TrafficLightColor {
@@ -27,14 +27,14 @@ impl<S: 'static + Send + Sync> TrafficLight<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let system_color = SystemColor::new();
 
         let fill = match (self.window_has_focus, self.color) {
             (true, TrafficLightColor::Red) => system_color.mac_os_traffic_light_red,
             (true, TrafficLightColor::Yellow) => system_color.mac_os_traffic_light_yellow,
             (true, TrafficLightColor::Green) => system_color.mac_os_traffic_light_green,
-            (false, _) => theme.lowest.base.active.background,
+            (false, _) => color.filled_element,
         };
 
         div().w_3().h_3().rounded_full().bg(fill)
@@ -61,7 +61,7 @@ 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 color = ThemeColor::new(cx);
 
         div()
             .flex()

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

@@ -3,7 +3,6 @@ use std::marker::PhantomData;
 use gpui3::img;
 
 use crate::prelude::*;
-use crate::theme::theme;
 
 #[derive(Element, Clone)]
 pub struct Avatar<S: 'static + Send + Sync> {
@@ -27,7 +26,7 @@ impl<S: 'static + Send + Sync> Avatar<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         let mut img = img();
 
@@ -39,7 +38,7 @@ impl<S: 'static + Send + Sync> Avatar<S> {
 
         img.uri(self.src.clone())
             .size_4()
-            .bg(theme.middle.warning.default.foreground)
+            .bg(color.image_fallback_background)
     }
 }
 

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

@@ -34,7 +34,7 @@ impl<S: 'static + Send + Sync> Default for ButtonHandlers<S> {
 }
 
 #[derive(Element)]
-pub struct Button<S: 'static + Send + Sync + Clone> {
+pub struct Button<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     label: SharedString,
     variant: ButtonVariant,
@@ -45,7 +45,7 @@ pub struct Button<S: 'static + Send + Sync + Clone> {
     handlers: ButtonHandlers<S>,
 }
 
-impl<S: 'static + Send + Sync + Clone> Button<S> {
+impl<S: 'static + Send + Sync> Button<S> {
     pub fn new(label: impl Into<SharedString>) -> Self {
         Self {
             state_type: PhantomData,

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

@@ -1,16 +1,15 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::theme;
 
-#[derive(Element, Clone)]
-pub struct Details<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub struct Details<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     text: &'static str,
     meta: Option<&'static str>,
 }
 
-impl<S: 'static + Send + Sync + Clone> Details<S> {
+impl<S: 'static + Send + Sync> Details<S> {
     pub fn new(text: &'static str) -> Self {
         Self {
             state_type: PhantomData,
@@ -25,7 +24,7 @@ impl<S: 'static + Send + Sync + Clone> Details<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             // .flex()
@@ -33,7 +32,7 @@ impl<S: 'static + Send + Sync + Clone> Details<S> {
             .p_1()
             .gap_0p5()
             .text_xs()
-            .text_color(theme.lowest.base.default.foreground)
+            .text_color(color.text)
             .child(self.text)
             .children(self.meta.map(|m| m))
     }
@@ -60,7 +59,11 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             Story::container(cx)
                 .child(Story::title_for::<_, Details<S>>(cx))
                 .child(Story::label(cx, "Default"))

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

@@ -1,11 +1,10 @@
 use std::marker::PhantomData;
-use std::sync::Arc;
 
 use gpui3::{svg, Hsla};
 use strum::EnumIter;
 
 use crate::prelude::*;
-use crate::theme::{theme, Theme};
+use crate::theme::theme;
 
 #[derive(Default, PartialEq, Copy, Clone)]
 pub enum IconSize {
@@ -29,7 +28,8 @@ pub enum IconColor {
 }
 
 impl IconColor {
-    pub fn color(self, theme: Arc<Theme>) -> Hsla {
+    pub fn color(self, cx: &WindowContext) -> Hsla {
+        let theme = theme(cx);
         match self {
             IconColor::Default => theme.lowest.base.default.foreground,
             IconColor::Muted => theme.lowest.variant.default.foreground,
@@ -44,7 +44,7 @@ impl IconColor {
     }
 }
 
-#[derive(Default, PartialEq, Copy, Clone, EnumIter)]
+#[derive(Debug, Default, PartialEq, Copy, Clone, EnumIter)]
 pub enum Icon {
     Ai,
     ArrowLeft,
@@ -148,7 +148,7 @@ impl Icon {
     }
 }
 
-#[derive(Element, Clone)]
+#[derive(Element)]
 pub struct IconElement<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     icon: Icon,
@@ -177,8 +177,8 @@ impl<S: 'static + Send + Sync> IconElement<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
-        let fill = self.color.color(theme);
+        let color = ThemeColor::new(cx);
+        let fill = self.color.color(cx);
         let svg_size = match self.size {
             IconSize::Small => ui_size(cx, 12. / 14.),
             IconSize::Medium => ui_size(cx, 15. / 14.),

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

@@ -1,7 +1,8 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::theme;
+use crate::Label;
+use crate::LabelColor;
 
 #[derive(Default, PartialEq)]
 pub enum InputVariant {
@@ -17,6 +18,8 @@ pub struct Input<S: 'static + Send + Sync> {
     value: String,
     state: InteractionState,
     variant: InputVariant,
+    disabled: bool,
+    is_active: bool,
 }
 
 impl<S: 'static + Send + Sync> Input<S> {
@@ -27,6 +30,8 @@ impl<S: 'static + Send + Sync> Input<S> {
             value: "".to_string(),
             state: InteractionState::default(),
             variant: InputVariant::default(),
+            disabled: false,
+            is_active: false,
         }
     }
 
@@ -45,41 +50,44 @@ impl<S: 'static + Send + Sync> Input<S> {
         self
     }
 
-    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
-
-        let text_el;
-        let text_color;
-        let background_color_default;
-        let background_color_active;
+    pub fn disabled(mut self, disabled: bool) -> Self {
+        self.disabled = disabled;
+        self
+    }
 
-        let mut border_color_default = theme.middle.base.default.border;
-        let mut border_color_hover = theme.middle.base.hovered.border;
-        let border_color_focus = theme.middle.base.pressed.background;
+    pub fn is_active(mut self, is_active: bool) -> Self {
+        self.is_active = is_active;
+        self
+    }
 
-        match self.variant {
-            InputVariant::Ghost => {
-                background_color_default = theme.middle.base.default.background;
-                background_color_active = theme.middle.base.active.background;
-            }
-            InputVariant::Filled => {
-                background_color_default = theme.middle.on.default.background;
-                background_color_active = theme.middle.on.active.background;
-            }
+    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        let color = ThemeColor::new(cx);
+        let system_color = SystemColor::new();
+
+        let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
+            InputVariant::Ghost => (
+                color.ghost_element,
+                color.ghost_element_hover,
+                color.ghost_element_active,
+            ),
+            InputVariant::Filled => (
+                color.filled_element,
+                color.filled_element_hover,
+                color.filled_element_active,
+            ),
         };
 
-        if self.state == InteractionState::Focused {
-            border_color_default = theme.players[0].cursor;
-            border_color_hover = theme.players[0].cursor;
-        }
+        let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
+            LabelColor::Disabled
+        } else {
+            LabelColor::Placeholder
+        });
 
-        if self.state == InteractionState::Focused || self.state == InteractionState::Active {
-            text_el = self.value.clone();
-            text_color = theme.lowest.base.default.foreground;
+        let label = Label::new(self.value.clone()).color(if self.disabled {
+            LabelColor::Disabled
         } else {
-            text_el = self.placeholder.to_string().clone();
-            text_color = theme.lowest.base.disabled.foreground;
-        }
+            LabelColor::Default
+        });
 
         div()
             .id("input")
@@ -87,14 +95,10 @@ impl<S: 'static + Send + Sync> Input<S> {
             .w_full()
             .px_2()
             .border()
-            .border_color(border_color_default)
-            .bg(background_color_default)
-            .hover(|style| {
-                style
-                    .border_color(border_color_hover)
-                    .bg(background_color_active)
-            })
-            .active(|style| style.border_color(theme.middle.base.active.border))
+            .border_color(system_color.transparent)
+            .bg(input_bg)
+            .hover(|style| style.bg(input_hover_bg))
+            .active(|style| style.bg(input_active_bg))
             .flex()
             .items_center()
             .child(
@@ -102,9 +106,8 @@ impl<S: 'static + Send + Sync> Input<S> {
                     .flex()
                     .items_center()
                     .text_sm()
-                    .text_color(text_color)
-                    .child(text_el)
-                    .child(div().text_color(theme.players[0].cursor).child("|")),
+                    .when(self.value.is_empty(), |this| this.child(placeholder_label))
+                    .when(!self.value.is_empty(), |this| this.child(label)),
             )
     }
 }
@@ -119,11 +122,11 @@ mod stories {
     use super::*;
 
     #[derive(Element)]
-    pub struct InputStory<S: 'static + Send + Sync + Clone> {
+    pub struct InputStory<S: 'static + Send + Sync> {
         state_type: PhantomData<S>,
     }
 
-    impl<S: 'static + Send + Sync + Clone> InputStory<S> {
+    impl<S: 'static + Send + Sync> InputStory<S> {
         pub fn new() -> Self {
             Self {
                 state_type: PhantomData,

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

@@ -22,17 +22,19 @@ pub enum LabelColor {
 
 impl LabelColor {
     pub fn hsla(&self, cx: &WindowContext) -> Hsla {
+        let color = ThemeColor::new(cx);
+        // TODO: Remove
         let theme = theme(cx);
 
         match self {
-            Self::Default => theme.middle.base.default.foreground,
-            Self::Muted => theme.middle.variant.default.foreground,
+            Self::Default => color.text,
+            Self::Muted => color.text_muted,
             Self::Created => theme.middle.positive.default.foreground,
             Self::Modified => theme.middle.warning.default.foreground,
             Self::Deleted => theme.middle.negative.default.foreground,
-            Self::Disabled => theme.middle.base.disabled.foreground,
+            Self::Disabled => color.text_disabled,
             Self::Hidden => theme.middle.variant.default.foreground,
-            Self::Placeholder => theme.middle.base.disabled.foreground,
+            Self::Placeholder => color.text_placeholder,
             Self::Accent => theme.middle.accent.default.foreground,
         }
     }
@@ -46,8 +48,8 @@ pub enum LineHeightStyle {
     UILabel,
 }
 
-#[derive(Element, Clone)]
-pub struct Label<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub struct Label<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     label: SharedString,
     line_height_style: LineHeightStyle,
@@ -55,7 +57,7 @@ pub struct Label<S: 'static + Send + Sync + Clone> {
     strikethrough: bool,
 }
 
-impl<S: 'static + Send + Sync + Clone> Label<S> {
+impl<S: 'static + Send + Sync> Label<S> {
     pub fn new(label: impl Into<SharedString>) -> Self {
         Self {
             state_type: PhantomData,
@@ -82,7 +84,7 @@ impl<S: 'static + Send + Sync + Clone> Label<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .when(self.strikethrough, |this| {
@@ -105,8 +107,8 @@ impl<S: 'static + Send + Sync + Clone> Label<S> {
     }
 }
 
-#[derive(Element, Clone)]
-pub struct HighlightedLabel<S: 'static + Send + Sync + Clone> {
+#[derive(Element)]
+pub struct HighlightedLabel<S: 'static + Send + Sync> {
     state_type: PhantomData<S>,
     label: SharedString,
     color: LabelColor,
@@ -114,7 +116,7 @@ pub struct HighlightedLabel<S: 'static + Send + Sync + Clone> {
     strikethrough: bool,
 }
 
-impl<S: 'static + Send + Sync + Clone> HighlightedLabel<S> {
+impl<S: 'static + Send + Sync> HighlightedLabel<S> {
     pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
         Self {
             state_type: PhantomData,
@@ -136,9 +138,9 @@ impl<S: 'static + Send + Sync + Clone> HighlightedLabel<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
-        let highlight_color = theme.lowest.accent.default.foreground;
+        let highlight_color = color.text_accent;
 
         let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
 
@@ -212,11 +214,11 @@ mod stories {
     use super::*;
 
     #[derive(Element)]
-    pub struct LabelStory<S: 'static + Send + Sync + Clone> {
+    pub struct LabelStory<S: 'static + Send + Sync> {
         state_type: PhantomData<S>,
     }
 
-    impl<S: 'static + Send + Sync + Clone> LabelStory<S> {
+    impl<S: 'static + Send + Sync> LabelStory<S> {
         pub fn new() -> Self {
             Self {
                 state_type: PhantomData,

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

@@ -1,6 +1,6 @@
 use gpui3::{Hsla, ViewContext};
 
-use crate::theme;
+use crate::ThemeColor;
 
 #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
 pub enum PlayerStatus {
@@ -139,15 +139,15 @@ impl Player {
     }
 
     pub fn cursor_color<S: 'static>(&self, cx: &mut ViewContext<S>) -> Hsla {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let index = self.index % 8;
-        theme.players[self.index].cursor
+        color.player[self.index].cursor
     }
 
     pub fn selection_color<S: 'static>(&self, cx: &mut ViewContext<S>) -> Hsla {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let index = self.index % 8;
-        theme.players[self.index].selection
+        color.player[self.index].selection
     }
 
     pub fn avatar_src(&self) -> &str {

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

@@ -1,7 +1,6 @@
 use std::marker::PhantomData;
 
 use crate::prelude::*;
-use crate::theme;
 
 #[derive(Element)]
 pub struct ToolDivider<S: 'static + Send + Sync> {
@@ -16,8 +15,8 @@ impl<S: 'static + Send + Sync> ToolDivider<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
-        div().w_px().h_3().bg(theme.lowest.base.default.border)
+        div().w_px().h_3().bg(color.border)
     }
 }

crates/ui2/src/prelude.rs 🔗

@@ -9,6 +9,7 @@ pub use crate::{theme, ButtonVariant, ElementExt, Theme};
 use gpui3::{hsla, rems, rgb, Hsla, Rems};
 use strum::EnumIter;
 
+// TODO Remove uses in favor of ThemeColor
 #[derive(Default)]
 pub struct SystemColor {
     pub transparent: Hsla,
@@ -35,8 +36,73 @@ impl SystemColor {
     }
 }
 
+#[derive(Clone, Copy)]
+pub struct PlayerThemeColors {
+    pub cursor: Hsla,
+    pub selection: Hsla,
+}
+
+impl PlayerThemeColors {
+    pub fn new(cx: &WindowContext, ix: usize) -> Self {
+        let theme = theme(cx);
+
+        if ix < theme.players.len() {
+            Self {
+                cursor: theme.players[ix].cursor,
+                selection: theme.players[ix].selection,
+            }
+        } else {
+            Self {
+                cursor: rgb::<Hsla>(0xff00ff),
+                selection: rgb::<Hsla>(0xff00ff),
+            }
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+pub struct SyntaxColor {
+    pub comment: Hsla,
+    pub string: Hsla,
+    pub function: Hsla,
+    pub keyword: Hsla,
+}
+
+impl SyntaxColor {
+    pub fn new(cx: &WindowContext) -> Self {
+        let theme = theme(cx);
+
+        Self {
+            comment: theme
+                .syntax
+                .get("comment")
+                .cloned()
+                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
+            string: theme
+                .syntax
+                .get("string")
+                .cloned()
+                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
+            function: theme
+                .syntax
+                .get("function")
+                .cloned()
+                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
+            keyword: theme
+                .syntax
+                .get("keyword")
+                .cloned()
+                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
+        }
+    }
+}
+
 #[derive(Clone, Copy)]
 pub struct ThemeColor {
+    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 border: Hsla,
     pub border_variant: Hsla,
     pub border_focused: Hsla,
@@ -63,12 +129,40 @@ pub struct ThemeColor {
     pub filled_element_selected: Hsla,
     pub filled_element_disabled: Hsla,
     pub ghost_element: Hsla,
+    /// The background color of a hovered element with no default background,
+    /// like a ghost-style button or an interactable list item.
     /// - TODO: Map to step 3.
     pub ghost_element_hover: Hsla,
     /// - TODO: Map to step 4.
     pub ghost_element_active: Hsla,
     pub ghost_element_selected: Hsla,
     pub ghost_element_disabled: Hsla,
+    pub text: Hsla,
+    pub text_muted: Hsla,
+    pub text_placeholder: Hsla,
+    pub text_disabled: Hsla,
+    pub text_accent: Hsla,
+    pub icon_muted: Hsla,
+    pub syntax: SyntaxColor,
+
+    pub status_bar: Hsla,
+    pub title_bar: Hsla,
+    pub toolbar: Hsla,
+    pub tab_bar: Hsla,
+    pub editor: Hsla,
+    pub editor_subheader: Hsla,
+    pub editor_active_line: Hsla,
+    pub terminal: Hsla,
+    pub image_fallback_background: Hsla,
+
+    pub git_created: Hsla,
+    pub git_modified: Hsla,
+    pub git_deleted: Hsla,
+    pub git_conflict: Hsla,
+    pub git_ignored: Hsla,
+    pub git_renamed: Hsla,
+
+    pub player: [PlayerThemeColors; 8],
 }
 
 impl ThemeColor {
@@ -76,7 +170,22 @@ impl ThemeColor {
         let theme = theme(cx);
         let system_color = SystemColor::new();
 
+        let players = [
+            PlayerThemeColors::new(cx, 0),
+            PlayerThemeColors::new(cx, 1),
+            PlayerThemeColors::new(cx, 2),
+            PlayerThemeColors::new(cx, 3),
+            PlayerThemeColors::new(cx, 4),
+            PlayerThemeColors::new(cx, 5),
+            PlayerThemeColors::new(cx, 6),
+            PlayerThemeColors::new(cx, 7),
+        ];
+
         Self {
+            transparent: hsla(0.0, 0.0, 0.0, 0.0),
+            mac_os_traffic_light_red: rgb::<Hsla>(0xEC695E),
+            mac_os_traffic_light_yellow: rgb::<Hsla>(0xF4BF4F),
+            mac_os_traffic_light_green: rgb::<Hsla>(0x62C554),
             border: theme.lowest.base.default.border,
             border_variant: theme.lowest.variant.default.border,
             border_focused: theme.lowest.accent.default.border,
@@ -94,6 +203,33 @@ impl ThemeColor {
             ghost_element_active: theme.lowest.base.hovered.background,
             ghost_element_selected: theme.lowest.accent.default.background,
             ghost_element_disabled: system_color.transparent,
+            text: theme.lowest.base.default.foreground,
+            text_muted: theme.lowest.variant.default.foreground,
+            /// TODO: map this to a real value
+            text_placeholder: theme.lowest.negative.default.foreground,
+            text_disabled: theme.lowest.base.disabled.foreground,
+            text_accent: theme.lowest.accent.default.foreground,
+            icon_muted: theme.lowest.variant.default.foreground,
+            syntax: SyntaxColor::new(cx),
+
+            status_bar: theme.lowest.base.default.background,
+            title_bar: theme.lowest.base.default.background,
+            toolbar: theme.highest.base.default.background,
+            tab_bar: theme.middle.base.default.background,
+            editor: theme.highest.base.default.background,
+            editor_subheader: theme.middle.base.default.background,
+            terminal: theme.highest.base.default.background,
+            editor_active_line: theme.highest.on.default.background,
+            image_fallback_background: theme.lowest.base.default.background,
+
+            git_created: theme.lowest.positive.default.foreground,
+            git_modified: theme.lowest.accent.default.foreground,
+            git_deleted: theme.lowest.negative.default.foreground,
+            git_conflict: theme.lowest.warning.default.foreground,
+            git_ignored: theme.lowest.base.disabled.foreground,
+            git_renamed: theme.lowest.warning.default.foreground,
+
+            player: players,
         }
     }
 }
@@ -192,16 +328,16 @@ impl GitStatus {
     }
 
     pub fn hsla(&self, cx: &WindowContext) -> Hsla {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
         let system_color = SystemColor::new();
 
         match self {
             Self::None => system_color.transparent,
-            Self::Created => theme.lowest.positive.default.foreground,
-            Self::Modified => theme.lowest.warning.default.foreground,
-            Self::Deleted => theme.lowest.negative.default.foreground,
-            Self::Conflict => theme.lowest.warning.default.foreground,
-            Self::Renamed => theme.lowest.accent.default.foreground,
+            Self::Created => color.git_created,
+            Self::Modified => color.git_modified,
+            Self::Deleted => color.git_deleted,
+            Self::Conflict => color.git_conflict,
+            Self::Renamed => color.git_renamed,
         }
     }
 }

crates/ui2/src/settings.rs 🔗

@@ -1,179 +1,149 @@
-// use std::ops::Deref;
-// use std::sync::Arc;
-
-// use gpui3::{
-//     rems, AbsoluteLength, AnyElement, BorrowAppContext, Bounds, LayoutId, Pixels, WindowContext,
-// };
-
-// use crate::prelude::*;
-
-// /// Returns the user settings.
-// pub fn user_settings(cx: &WindowContext) -> FakeSettings {
-//     cx.global::<FakeSettings>().clone()
-// }
-
-// pub fn user_settings_mut<'cx>(cx: &'cx mut WindowContext) -> &'cx mut FakeSettings {
-//     cx.global_mut::<FakeSettings>()
-// }
-
-// #[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 FakeSettings {
-//     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 FakeSettings {
-//     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 FakeSettings {}
-
-// pub fn with_settings<E, F>(
-//     settings: FakeSettings,
-//     cx: &mut ViewContext<E::ViewState>,
-//     build_child: F,
-// ) -> WithSettings<E>
-// where
-//     E: Element,
-//     F: FnOnce(&mut ViewContext<E::ViewState>) -> E,
-// {
-//     let child = cx.with_global(settings.clone(), |cx| build_child(cx));
-//     WithSettings { settings, child }
-// }
-
-// pub struct WithSettings<E> {
-//     pub(crate) settings: FakeSettings,
-//     pub(crate) child: E,
-// }
-
-// impl<E> IntoAnyElement<E::ViewState> for WithSettings<E>
-// where
-//     E: Element,
-// {
-//     fn into_any(self) -> AnyElement<E::ViewState> {
-//         AnyElement::new(self)
-//     }
-// }
-
-// impl<E: Element> Element for WithSettings<E> {
-//     type ViewState = E::ViewState;
-//     type ElementState = E::ElementState;
-
-//     fn id(&self) -> Option<gpui3::ElementId> {
-//         None
-//     }
-
-//     fn initialize(
-//         &mut self,
-//         view_state: &mut Self::ViewState,
-//         element_state: Option<Self::ElementState>,
-//         cx: &mut ViewContext<Self::ViewState>,
-//     ) -> Self::ElementState {
-//         cx.with_global(self.settings.clone(), |cx| {
-//             self.child.initialize(view_state, element_state, cx)
-//         })
-//     }
-
-//     fn layout(
-//         &mut self,
-//         view_state: &mut E::ViewState,
-//         element_state: &mut Self::ElementState,
-//         cx: &mut ViewContext<E::ViewState>,
-//     ) -> LayoutId
-//     where
-//         Self: Sized,
-//     {
-//         cx.with_global(self.settings.clone(), |cx| {
-//             self.child.layout(view_state, element_state, cx)
-//         })
-//     }
-
-//     fn paint(
-//         &mut self,
-//         bounds: Bounds<Pixels>,
-//         view_state: &mut Self::ViewState,
-//         frame_state: &mut Self::ElementState,
-//         cx: &mut ViewContext<Self::ViewState>,
-//     ) where
-//         Self: Sized,
-//     {
-//         cx.with_global(self.settings.clone(), |cx| {
-//             self.child.paint(bounds, view_state, frame_state, cx);
-//         });
-//     }
-// }
-
-// impl<E: Element + Interactive> Interactive for WithSettings<E> {
-//     fn listeners(&mut self) -> &mut gpui3::EventListeners<Self::ViewState> {
-//         self.child.listeners()
-//     }
-
-//     fn on_mouse_down(
-//         mut self,
-//         button: gpui3::MouseButton,
-//         handler: impl Fn(&mut Self::ViewState, &gpui3::MouseDownEvent, &mut ViewContext<Self::ViewState>)
-//             + Send
-//             + Sync
-//             + 'static,
-//     ) -> Self
-//     where
-//         Self: Sized,
-//     {
-//         println!("WithSettings on_mouse_down");
-
-//         let settings = self.settings.clone();
-
-//         self.listeners()
-//             .mouse_down
-//             .push(Arc::new(move |view, event, bounds, phase, cx| {
-//                 cx.with_global(settings.clone(), |cx| handler(view, event, cx))
-//             }));
-//         self
-//     }
-// }
+use std::ops::Deref;
+
+use gpui3::{
+    rems, AbsoluteLength, AnyElement, BorrowAppContext, Bounds, LayoutId, Pixels, WindowContext,
+};
+
+use crate::prelude::*;
+
+/// Returns the user settings.
+pub fn user_settings(cx: &WindowContext) -> FakeSettings {
+    cx.global::<FakeSettings>().clone()
+}
+
+pub fn user_settings_mut<'cx>(cx: &'cx mut WindowContext) -> &'cx mut FakeSettings {
+    cx.global_mut::<FakeSettings>()
+}
+
+#[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 FakeSettings {
+    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 FakeSettings {
+    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 FakeSettings {}
+
+pub fn with_settings<E, F>(
+    settings: FakeSettings,
+    cx: &mut ViewContext<E::ViewState>,
+    build_child: F,
+) -> WithSettings<E>
+where
+    E: Element,
+    F: FnOnce(&mut ViewContext<E::ViewState>) -> E,
+{
+    let child = cx.with_global(settings.clone(), |cx| build_child(cx));
+    WithSettings { settings, child }
+}
+
+pub struct WithSettings<E> {
+    pub(crate) settings: FakeSettings,
+    pub(crate) child: E,
+}
+
+impl<E> IntoAnyElement<E::ViewState> for WithSettings<E>
+where
+    E: Element,
+{
+    fn into_any(self) -> AnyElement<E::ViewState> {
+        AnyElement::new(self)
+    }
+}
+
+impl<E: Element> Element for WithSettings<E> {
+    type ViewState = E::ViewState;
+    type ElementState = E::ElementState;
+
+    fn id(&self) -> Option<gpui3::ElementId> {
+        None
+    }
+
+    fn initialize(
+        &mut self,
+        view_state: &mut Self::ViewState,
+        element_state: Option<Self::ElementState>,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> Self::ElementState {
+        cx.with_global(self.settings.clone(), |cx| {
+            self.child.initialize(view_state, element_state, cx)
+        })
+    }
+
+    fn layout(
+        &mut self,
+        view_state: &mut E::ViewState,
+        element_state: &mut Self::ElementState,
+        cx: &mut ViewContext<E::ViewState>,
+    ) -> LayoutId
+    where
+        Self: Sized,
+    {
+        cx.with_global(self.settings.clone(), |cx| {
+            self.child.layout(view_state, element_state, cx)
+        })
+    }
+
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        view_state: &mut Self::ViewState,
+        frame_state: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) where
+        Self: Sized,
+    {
+        cx.with_global(self.settings.clone(), |cx| {
+            self.child.paint(bounds, view_state, frame_state, cx);
+        });
+    }
+}

crates/ui2/src/static_data.rs 🔗

@@ -4,58 +4,58 @@ use std::str::FromStr;
 use gpui3::WindowContext;
 use rand::Rng;
 
+use crate::HighlightedText;
 use crate::{
-    theme, Buffer, BufferRow, BufferRows, EditorPane, FileSystemStatus, GitStatus, HighlightColor,
-    HighlightedLine, HighlightedText, Icon, Keybinding, Label, LabelColor, ListEntry,
-    ListEntrySize, ListItem, Livestream, MicStatus, ModifierKeys, PaletteItem, Player,
-    PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, Theme, ToggleState,
-    VideoStatus,
+    Buffer, BufferRow, BufferRows, EditorPane, FileSystemStatus, GitStatus, HighlightedLine, Icon,
+    Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem, Livestream, MicStatus,
+    ModifierKeys, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus,
+    Symbol, Tab, ThemeColor, ToggleState, VideoStatus,
 };
 
 pub fn static_tabs_example<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
     vec![
-        Tab::new()
+        Tab::new("wip.rs")
             .title("wip.rs".to_string())
             .icon(Icon::FileRust)
             .current(false)
             .fs_status(FileSystemStatus::Deleted),
-        Tab::new()
+        Tab::new("Cargo.toml")
             .title("Cargo.toml".to_string())
             .icon(Icon::FileToml)
             .current(false)
             .git_status(GitStatus::Modified),
-        Tab::new()
+        Tab::new("Channels Panel")
             .title("Channels Panel".to_string())
             .icon(Icon::Hash)
             .current(false),
-        Tab::new()
+        Tab::new("channels_panel.rs")
             .title("channels_panel.rs".to_string())
             .icon(Icon::FileRust)
             .current(true)
             .git_status(GitStatus::Modified),
-        Tab::new()
+        Tab::new("workspace.rs")
             .title("workspace.rs".to_string())
             .current(false)
             .icon(Icon::FileRust)
             .git_status(GitStatus::Modified),
-        Tab::new()
+        Tab::new("icon_button.rs")
             .title("icon_button.rs".to_string())
             .icon(Icon::FileRust)
             .current(false),
-        Tab::new()
+        Tab::new("storybook.rs")
             .title("storybook.rs".to_string())
             .icon(Icon::FileRust)
             .current(false)
             .git_status(GitStatus::Created),
-        Tab::new()
+        Tab::new("theme.rs")
             .title("theme.rs".to_string())
             .icon(Icon::FileRust)
             .current(false),
-        Tab::new()
+        Tab::new("theme_registry.rs")
             .title("theme_registry.rs".to_string())
             .icon(Icon::FileRust)
             .current(false),
-        Tab::new()
+        Tab::new("styleable_helpers.rs")
             .title("styleable_helpers.rs".to_string())
             .icon(Icon::FileRust)
             .current(false),
@@ -64,21 +64,21 @@ pub fn static_tabs_example<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
 
 pub fn static_tabs_1<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
     vec![
-        Tab::new()
+        Tab::new("project_panel.rs")
             .title("project_panel.rs".to_string())
             .icon(Icon::FileRust)
             .current(false)
             .fs_status(FileSystemStatus::Deleted),
-        Tab::new()
+        Tab::new("tab_bar.rs")
             .title("tab_bar.rs".to_string())
             .icon(Icon::FileRust)
             .current(false)
             .git_status(GitStatus::Modified),
-        Tab::new()
+        Tab::new("workspace.rs")
             .title("workspace.rs".to_string())
             .icon(Icon::FileRust)
             .current(false),
-        Tab::new()
+        Tab::new("tab.rs")
             .title("tab.rs".to_string())
             .icon(Icon::FileRust)
             .current(true)
@@ -88,12 +88,12 @@ pub fn static_tabs_1<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
 
 pub fn static_tabs_2<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
     vec![
-        Tab::new()
+        Tab::new("tab_bar.rs")
             .title("tab_bar.rs".to_string())
             .icon(Icon::FileRust)
             .current(false)
             .fs_status(FileSystemStatus::Deleted),
-        Tab::new()
+        Tab::new("static_data.rs")
             .title("static_data.rs".to_string())
             .icon(Icon::FileRust)
             .current(true)
@@ -102,7 +102,9 @@ pub fn static_tabs_2<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
 }
 
 pub fn static_tabs_3<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
-    vec![Tab::new().git_status(GitStatus::Created).current(true)]
+    vec![Tab::new("static_tabs_3")
+        .git_status(GitStatus::Created)
+        .current(true)]
 }
 
 pub fn static_players() -> Vec<Player> {
@@ -613,7 +615,7 @@ pub fn empty_buffer_example<S: 'static + Send + Sync + Clone>() -> Buffer<S> {
 }
 
 pub fn hello_world_rust_editor_example(cx: &mut WindowContext) -> EditorPane {
-    let theme = theme(cx);
+    let color = ThemeColor::new(cx);
 
     EditorPane::new(
         cx,
@@ -622,19 +624,19 @@ pub fn hello_world_rust_editor_example(cx: &mut WindowContext) -> EditorPane {
         vec![Symbol(vec![
             HighlightedText {
                 text: "fn ".to_string(),
-                color: HighlightColor::Keyword.hsla(&theme),
+                color: color.syntax.keyword,
             },
             HighlightedText {
                 text: "main".to_string(),
-                color: HighlightColor::Function.hsla(&theme),
+                color: color.syntax.function,
             },
         ])],
-        hello_world_rust_buffer_example(&theme),
+        hello_world_rust_buffer_example(&color),
     )
 }
 
 pub fn hello_world_rust_buffer_example<S: 'static + Send + Sync + Clone>(
-    theme: &Theme,
+    color: &ThemeColor,
 ) -> Buffer<S> {
     Buffer::new()
         .set_title("hello_world.rs".to_string())
@@ -642,11 +644,11 @@ pub fn hello_world_rust_buffer_example<S: 'static + Send + Sync + Clone>(
         .set_language("rust".to_string())
         .set_rows(Some(BufferRows {
             show_line_numbers: true,
-            rows: hello_world_rust_buffer_rows(theme),
+            rows: hello_world_rust_buffer_rows(color),
         }))
 }
 
-pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
+pub fn hello_world_rust_buffer_rows(color: &ThemeColor) -> Vec<BufferRow> {
     let show_line_number = true;
 
     vec![
@@ -658,15 +660,15 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
                 highlighted_texts: vec![
                     HighlightedText {
                         text: "fn ".to_string(),
-                        color: HighlightColor::Keyword.hsla(&theme),
+                        color: color.syntax.keyword,
                     },
                     HighlightedText {
                         text: "main".to_string(),
-                        color: HighlightColor::Function.hsla(&theme),
+                        color: color.syntax.function,
                     },
                     HighlightedText {
                         text: "() {".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                 ],
             }),
@@ -682,7 +684,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
                 highlighted_texts: vec![HighlightedText {
                     text: "    // Statements here are executed when the compiled binary is called."
                         .to_string(),
-                    color: HighlightColor::Comment.hsla(&theme),
+                    color: color.syntax.comment,
                 }],
             }),
             cursors: None,
@@ -705,7 +707,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "    // Print text to the console.".to_string(),
-                    color: HighlightColor::Comment.hsla(&theme),
+                    color: color.syntax.comment,
                 }],
             }),
             cursors: None,
@@ -720,15 +722,15 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
                 highlighted_texts: vec![
                     HighlightedText {
                         text: "    println!(".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                     HighlightedText {
                         text: "\"Hello, world!\"".to_string(),
-                        color: HighlightColor::String.hsla(&theme),
+                        color: color.syntax.string,
                     },
                     HighlightedText {
                         text: ");".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                 ],
             }),
@@ -743,7 +745,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "}".to_string(),
-                    color: HighlightColor::Default.hsla(&theme),
+                    color: color.text,
                 }],
             }),
             cursors: None,
@@ -754,7 +756,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
 }
 
 pub fn hello_world_rust_editor_with_status_example(cx: &mut WindowContext) -> EditorPane {
-    let theme = theme(cx);
+    let color = ThemeColor::new(cx);
 
     EditorPane::new(
         cx,
@@ -763,19 +765,19 @@ pub fn hello_world_rust_editor_with_status_example(cx: &mut WindowContext) -> Ed
         vec![Symbol(vec![
             HighlightedText {
                 text: "fn ".to_string(),
-                color: HighlightColor::Keyword.hsla(&theme),
+                color: color.syntax.keyword,
             },
             HighlightedText {
                 text: "main".to_string(),
-                color: HighlightColor::Function.hsla(&theme),
+                color: color.syntax.function,
             },
         ])],
-        hello_world_rust_buffer_with_status_example(&theme),
+        hello_world_rust_buffer_with_status_example(&color),
     )
 }
 
 pub fn hello_world_rust_buffer_with_status_example<S: 'static + Send + Sync + Clone>(
-    theme: &Theme,
+    color: &ThemeColor,
 ) -> Buffer<S> {
     Buffer::new()
         .set_title("hello_world.rs".to_string())
@@ -783,11 +785,11 @@ pub fn hello_world_rust_buffer_with_status_example<S: 'static + Send + Sync + Cl
         .set_language("rust".to_string())
         .set_rows(Some(BufferRows {
             show_line_numbers: true,
-            rows: hello_world_rust_with_status_buffer_rows(theme),
+            rows: hello_world_rust_with_status_buffer_rows(color),
         }))
 }
 
-pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
+pub fn hello_world_rust_with_status_buffer_rows(color: &ThemeColor) -> Vec<BufferRow> {
     let show_line_number = true;
 
     vec![
@@ -799,15 +801,15 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
                 highlighted_texts: vec![
                     HighlightedText {
                         text: "fn ".to_string(),
-                        color: HighlightColor::Keyword.hsla(&theme),
+                        color: color.syntax.keyword,
                     },
                     HighlightedText {
                         text: "main".to_string(),
-                        color: HighlightColor::Function.hsla(&theme),
+                        color: color.syntax.function,
                     },
                     HighlightedText {
                         text: "() {".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                 ],
             }),
@@ -823,7 +825,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
                 highlighted_texts: vec![HighlightedText {
                     text: "// Statements here are executed when the compiled binary is called."
                         .to_string(),
-                    color: HighlightColor::Comment.hsla(&theme),
+                    color: color.syntax.comment,
                 }],
             }),
             cursors: None,
@@ -846,7 +848,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "    // Print text to the console.".to_string(),
-                    color: HighlightColor::Comment.hsla(&theme),
+                    color: color.syntax.comment,
                 }],
             }),
             cursors: None,
@@ -861,15 +863,15 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
                 highlighted_texts: vec![
                     HighlightedText {
                         text: "    println!(".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                     HighlightedText {
                         text: "\"Hello, world!\"".to_string(),
-                        color: HighlightColor::String.hsla(&theme),
+                        color: color.syntax.string,
                     },
                     HighlightedText {
                         text: ");".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                 ],
             }),
@@ -884,7 +886,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "}".to_string(),
-                    color: HighlightColor::Default.hsla(&theme),
+                    color: color.text,
                 }],
             }),
             cursors: None,
@@ -898,7 +900,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "".to_string(),
-                    color: HighlightColor::Default.hsla(&theme),
+                    color: color.text,
                 }],
             }),
             cursors: None,
@@ -912,7 +914,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "// Marshall and Nate were here".to_string(),
-                    color: HighlightColor::Comment.hsla(&theme),
+                    color: color.syntax.comment,
                 }],
             }),
             cursors: None,
@@ -922,16 +924,16 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow>
     ]
 }
 
-pub fn terminal_buffer<S: 'static + Send + Sync + Clone>(theme: &Theme) -> Buffer<S> {
+pub fn terminal_buffer<S: 'static + Send + Sync + Clone>(color: &ThemeColor) -> Buffer<S> {
     Buffer::new()
         .set_title("zed — fish".to_string())
         .set_rows(Some(BufferRows {
             show_line_numbers: false,
-            rows: terminal_buffer_rows(theme),
+            rows: terminal_buffer_rows(color),
         }))
 }
 
-pub fn terminal_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
+pub fn terminal_buffer_rows(color: &ThemeColor) -> Vec<BufferRow> {
     let show_line_number = false;
 
     vec![
@@ -943,31 +945,31 @@ pub fn terminal_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
                 highlighted_texts: vec![
                     HighlightedText {
                         text: "maxdeviant ".to_string(),
-                        color: HighlightColor::Keyword.hsla(&theme),
+                        color: color.syntax.keyword,
                     },
                     HighlightedText {
                         text: "in ".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                     HighlightedText {
                         text: "profaned-capital ".to_string(),
-                        color: HighlightColor::Function.hsla(&theme),
+                        color: color.syntax.function,
                     },
                     HighlightedText {
                         text: "in ".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                     HighlightedText {
                         text: "~/p/zed ".to_string(),
-                        color: HighlightColor::Function.hsla(&theme),
+                        color: color.syntax.function,
                     },
                     HighlightedText {
                         text: "on ".to_string(),
-                        color: HighlightColor::Default.hsla(&theme),
+                        color: color.text,
                     },
                     HighlightedText {
                         text: " gpui2-ui ".to_string(),
-                        color: HighlightColor::Keyword.hsla(&theme),
+                        color: color.syntax.keyword,
                     },
                 ],
             }),
@@ -982,7 +984,7 @@ pub fn terminal_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
             line: Some(HighlightedLine {
                 highlighted_texts: vec![HighlightedText {
                     text: "λ ".to_string(),
-                    color: HighlightColor::String.hsla(&theme),
+                    color: color.syntax.string,
                 }],
             }),
             cursors: None,

crates/ui2/src/story.rs 🔗

@@ -1,13 +1,12 @@
 use gpui3::Div;
 
 use crate::prelude::*;
-use crate::theme;
 
 pub struct Story {}
 
 impl Story {
     pub fn container<S: 'static + Send + Sync>(cx: &mut ViewContext<S>) -> Div<S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .size_full()
@@ -16,18 +15,18 @@ impl Story {
             .pt_2()
             .px_4()
             .font("Zed Mono Extended")
-            .bg(theme.lowest.base.default.background)
+            .bg(color.background)
     }
 
     pub fn title<S: 'static + Send + Sync>(
         cx: &mut ViewContext<S>,
         title: &str,
     ) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .text_xl()
-            .text_color(theme.lowest.base.default.foreground)
+            .text_color(color.text)
             .child(title.to_owned())
     }
 
@@ -41,13 +40,13 @@ impl Story {
         cx: &mut ViewContext<S>,
         label: &str,
     ) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
+        let color = ThemeColor::new(cx);
 
         div()
             .mt_4()
             .mb_2()
             .text_xs()
-            .text_color(theme.lowest.base.default.foreground)
+            .text_color(color.text)
             .child(label.to_owned())
     }
 }