Update pickers (#3529)

Nate Butler created

[[PR Description]]

- Update the size of all pickers
- Additional styling fixes for File Finder and Outline palettes
- Extend the ui prelude to include common imports

Release Notes:

- N/A

Change summary

crates/collab_ui2/src/collab_panel/channel_modal.rs  |  4 
crates/collab_ui2/src/collab_panel/contact_finder.rs |  4 
crates/command_palette2/src/command_palette.rs       |  2 
crates/file_finder2/src/file_finder.rs               | 10 +-
crates/language_selector2/src/language_selector.rs   |  4 
crates/outline2/src/outline.rs                       | 51 ++++++++++---
crates/picker2/src/picker2.rs                        | 32 +++++---
crates/recent_projects2/src/recent_projects.rs       |  2 
crates/theme2/src/theme2.rs                          |  6 +
crates/theme_selector2/src/theme_selector.rs         |  2 
crates/ui2/src/prelude.rs                            | 10 +
crates/ui2/src/styled_ext.rs                         | 26 ++++++
crates/ui2/src/styles/elevation.rs                   | 14 ++-
13 files changed, 122 insertions(+), 45 deletions(-)

Detailed changes

crates/collab_ui2/src/collab_panel/channel_modal.rs 🔗

@@ -11,7 +11,7 @@ use gpui::{
 };
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
-use ui::v_stack;
+use ui::prelude::*;
 use util::TryFutureExt;
 
 actions!(
@@ -151,7 +151,7 @@ impl Render for ChannelModal {
     type Element = Div;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().min_w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
         // let theme = &theme::current(cx).collab_panel.tabbed_modal;
 
         // let mode = self.picker.read(cx).delegate().mode;

crates/collab_ui2/src/collab_panel/contact_finder.rs 🔗

@@ -7,7 +7,7 @@ use gpui::{
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
 use theme::ActiveTheme as _;
-use ui::{h_stack, v_stack, Label};
+use ui::prelude::*;
 use util::{ResultExt as _, TryFutureExt};
 
 pub fn init(cx: &mut AppContext) {
@@ -58,7 +58,7 @@ impl Render for ContactFinder {
                     .bg(cx.theme().colors().element_background),
             )
             .child(self.picker.clone())
-            .w_96()
+            .w(rems(34.))
     }
 
     // fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/command_palette2/src/command_palette.rs 🔗

@@ -84,7 +84,7 @@ impl Render for CommandPalette {
     type Element = Div;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().min_w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }
 

crates/file_finder2/src/file_finder.rs 🔗

@@ -2,7 +2,7 @@ use collections::HashMap;
 use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
 use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
 use gpui::{
-    actions, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Model,
+    actions, rems, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Model,
     ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
@@ -15,7 +15,7 @@ use std::{
     },
 };
 use text::Point;
-use ui::{prelude::*, v_stack, HighlightedLabel, ListItem};
+use ui::{prelude::*, HighlightedLabel, ListItem};
 use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
 use workspace::Workspace;
 
@@ -119,7 +119,7 @@ impl Render for FileFinder {
     type Element = Div;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }
 
@@ -720,7 +720,9 @@ impl PickerDelegate for FileFinderDelegate {
             ListItem::new(ix).inset(true).selected(selected).child(
                 v_stack()
                     .child(HighlightedLabel::new(file_name, file_name_positions))
-                    .child(HighlightedLabel::new(full_path, full_path_positions)),
+                    .child(
+                        HighlightedLabel::new(full_path, full_path_positions).color(Color::Muted),
+                    ),
             ),
         )
     }

crates/language_selector2/src/language_selector.rs 🔗

@@ -12,7 +12,7 @@ use language::{Buffer, LanguageRegistry};
 use picker::{Picker, PickerDelegate};
 use project::Project;
 use std::sync::Arc;
-use ui::{v_stack, HighlightedLabel, ListItem, Selectable};
+use ui::{prelude::*, HighlightedLabel, ListItem};
 use util::ResultExt;
 use workspace::Workspace;
 
@@ -70,7 +70,7 @@ impl Render for LanguageSelector {
     type Element = Div;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().min_w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }
 

crates/outline2/src/outline.rs 🔗

@@ -5,18 +5,20 @@ use editor::{
 use fuzzy::StringMatch;
 use gpui::{
     actions, div, rems, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
-    FontWeight, ParentElement, Point, Render, Styled, StyledText, Task, TextStyle, View,
-    ViewContext, VisualContext, WeakView, WindowContext,
+    FontStyle, FontWeight, HighlightStyle, ParentElement, Point, Render, Styled, StyledText, Task,
+    TextStyle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
 };
 use language::Outline;
 use ordered_float::OrderedFloat;
 use picker::{Picker, PickerDelegate};
+use settings::Settings;
 use std::{
     cmp::{self, Reverse},
     sync::Arc,
 };
-use theme::ActiveTheme;
-use ui::{v_stack, ListItem, Selectable};
+
+use theme::{color_alpha, ActiveTheme, ThemeSettings};
+use ui::{prelude::*, ListItem};
 use util::ResultExt;
 use workspace::Workspace;
 
@@ -60,7 +62,7 @@ impl Render for OutlineView {
     type Element = Div;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().min_w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }
 
@@ -253,24 +255,47 @@ impl PickerDelegate for OutlineViewDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _: &mut ViewContext<Picker<Self>>,
+        cx: &mut ViewContext<Picker<Self>>,
     ) -> Option<Self::ListItem> {
+        let settings = ThemeSettings::get_global(cx);
+
+        // TODO: We probably shouldn't need to build a whole new text style here
+        // but I'm not sure how to get the current one and modify it.
+        // Before this change TextStyle::default() was used here, which was giving us the wrong font and text color.
+        let text_style = TextStyle {
+            color: cx.theme().colors().text,
+            font_family: settings.buffer_font.family.clone(),
+            font_features: settings.buffer_font.features,
+            font_size: settings.buffer_font_size(cx).into(),
+            font_weight: FontWeight::NORMAL,
+            font_style: FontStyle::Normal,
+            line_height: relative(1.).into(),
+            background_color: None,
+            underline: None,
+            white_space: WhiteSpace::Normal,
+        };
+
+        let mut highlight_style = HighlightStyle::default();
+        highlight_style.background_color = Some(color_alpha(cx.theme().colors().text_accent, 0.3));
+
         let mat = &self.matches[ix];
         let outline_item = &self.outline.items[mat.candidate_id];
 
         let highlights = gpui::combine_highlights(
-            mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
+            mat.ranges().map(|range| (range, highlight_style)),
             outline_item.highlight_ranges.iter().cloned(),
         );
 
-        let styled_text = StyledText::new(outline_item.text.clone())
-            .with_highlights(&TextStyle::default(), highlights);
+        let styled_text =
+            StyledText::new(outline_item.text.clone()).with_highlights(&text_style, highlights);
 
         Some(
-            ListItem::new(ix)
-                .inset(true)
-                .selected(selected)
-                .child(div().pl(rems(outline_item.depth as f32)).child(styled_text)),
+            ListItem::new(ix).inset(true).selected(selected).child(
+                div()
+                    .text_ui()
+                    .pl(rems(outline_item.depth as f32))
+                    .child(styled_text),
+            ),
         )
     }
 }

crates/picker2/src/picker2.rs 🔗

@@ -1,6 +1,6 @@
 use editor::Editor;
 use gpui::{
-    div, prelude::*, uniform_list, AnyElement, AppContext, Div, FocusHandle, FocusableView,
+    div, prelude::*, rems, uniform_list, AnyElement, AppContext, Div, FocusHandle, FocusableView,
     MouseButton, MouseDownEvent, Render, Task, UniformListScrollHandle, View, ViewContext,
     WindowContext,
 };
@@ -193,10 +193,27 @@ impl<D: PickerDelegate> Render for Picker<D> {
     type Element = Div;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        let picker_editor = h_stack()
+            .overflow_hidden()
+            .flex_none()
+            .h_9()
+            .px_3()
+            .child(self.editor.clone());
+
+        let empty_state = div().p_1().child(
+            h_stack()
+                // TODO: This number matches the height of the uniform list items.
+                // Align these two with a less magic number.
+                .h(rems(1.4375))
+                .px_2()
+                .child(Label::new("No matches").color(Color::Muted)),
+        );
+
         div()
             .key_context("picker")
             .size_full()
-            .elevation_2(cx)
+            .overflow_hidden()
+            .elevation_3(cx)
             .on_action(cx.listener(Self::select_next))
             .on_action(cx.listener(Self::select_prev))
             .on_action(cx.listener(Self::select_first))
@@ -205,10 +222,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
             .on_action(cx.listener(Self::confirm))
             .on_action(cx.listener(Self::secondary_confirm))
             .child(
-                v_stack()
-                    .py_0p5()
-                    .px_1()
-                    .child(div().px_1().py_0p5().child(self.editor.clone())),
+                picker_editor
             )
             .child(Divider::horizontal())
             .when(self.delegate.match_count() > 0, |el| {
@@ -256,11 +270,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
             })
             .when(self.delegate.match_count() == 0, |el| {
                 el.child(
-                    v_stack().p_1().grow().child(
-                        div()
-                            .px_1()
-                            .child(Label::new("No matches").color(Color::Muted)),
-                    ),
+                    empty_state
                 )
             })
     }

crates/recent_projects2/src/recent_projects.rs 🔗

@@ -97,7 +97,7 @@ impl Render for RecentProjects {
     type Element = Div;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }
 

crates/theme2/src/theme2.rs 🔗

@@ -150,3 +150,9 @@ pub struct DiagnosticStyle {
     pub hint: Hsla,
     pub ignored: Hsla,
 }
+
+pub fn color_alpha(color: Hsla, alpha: f32) -> Hsla {
+    let mut color = color;
+    color.a = alpha;
+    color
+}

crates/theme_selector2/src/theme_selector.rs 🔗

@@ -68,7 +68,7 @@ impl Render for ThemeSelector {
     type Element = Div;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        v_stack().min_w_96().child(self.picker.clone())
+        v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }
 

crates/ui2/src/prelude.rs 🔗

@@ -1,7 +1,8 @@
 pub use gpui::prelude::*;
 pub use gpui::{
-    div, Element, ElementId, InteractiveElement, ParentElement, RenderOnce, SharedString, Styled,
-    ViewContext, WindowContext,
+    div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementId,
+    InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, ViewContext,
+    WindowContext,
 };
 
 pub use crate::clickable::*;
@@ -9,5 +10,8 @@ pub use crate::disableable::*;
 pub use crate::fixed::*;
 pub use crate::selectable::*;
 pub use crate::{h_stack, v_stack};
-pub use crate::{ButtonCommon, Color, LabelCommon, StyledExt};
+pub use crate::{Button, ButtonSize, ButtonStyle, IconButton};
+pub use crate::{ButtonCommon, Color, StyledExt};
+pub use crate::{Icon, IconElement, IconSize};
+pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle};
 pub use theme::ActiveTheme;

crates/ui2/src/styled_ext.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{px, Styled, WindowContext};
+use gpui::{hsla, px, Styled, WindowContext};
 use settings::Settings;
 use theme::ThemeSettings;
 
@@ -103,6 +103,30 @@ pub trait StyledExt: Styled + Sized {
     fn elevation_3(self, cx: &mut WindowContext) -> Self {
         elevated(self, cx, ElevationIndex::ModalSurface)
     }
+
+    fn debug_bg_red(self) -> Self {
+        self.bg(gpui::red())
+    }
+
+    fn debug_bg_green(self) -> Self {
+        self.bg(gpui::green())
+    }
+
+    fn debug_bg_blue(self) -> Self {
+        self.bg(gpui::blue())
+    }
+
+    fn debug_bg_yellow(self) -> Self {
+        self.bg(hsla(60. / 360., 1., 0.5, 1.))
+    }
+
+    fn debug_bg_cyan(self) -> Self {
+        self.bg(hsla(160. / 360., 1., 0.5, 1.))
+    }
+
+    fn debug_bg_magenta(self) -> Self {
+        self.bg(hsla(300. / 360., 1., 0.5, 1.))
+    }
 }
 
 impl<E: Styled> StyledExt for E {}

crates/ui2/src/styles/elevation.rs 🔗

@@ -37,7 +37,7 @@ impl ElevationIndex {
 
             ElevationIndex::ElevatedSurface => smallvec![BoxShadow {
                 color: hsla(0., 0., 0., 0.12),
-                offset: point(px(0.), px(1.)),
+                offset: point(px(0.), px(2.)),
                 blur_radius: px(3.),
                 spread_radius: px(0.),
             }],
@@ -45,13 +45,19 @@ impl ElevationIndex {
             ElevationIndex::ModalSurface => smallvec![
                 BoxShadow {
                     color: hsla(0., 0., 0., 0.12),
-                    offset: point(px(0.), px(1.)),
+                    offset: point(px(0.), px(2.)),
                     blur_radius: px(3.),
                     spread_radius: px(0.),
                 },
                 BoxShadow {
-                    color: hsla(0., 0., 0., 0.20),
-                    offset: point(px(3.), px(1.)),
+                    color: hsla(0., 0., 0., 0.08),
+                    offset: point(px(0.), px(3.)),
+                    blur_radius: px(6.),
+                    spread_radius: px(0.),
+                },
+                BoxShadow {
+                    color: hsla(0., 0., 0., 0.04),
+                    offset: point(px(0.), px(6.)),
                     blur_radius: px(12.),
                     spread_radius: px(0.),
                 },