Update List to support UI Density (#18079)

Nate Butler created

Tracking issue: #18078

Improve UI Density support for List.

UI density is an unstable feature. You can read more about it in the
above issue!

| Before Normal - Before Dense - After Normal - After Dense |
|--------------------------------------------------------|
| ![Group
8](https://github.com/user-attachments/assets/bb896fcf-e4a6-4776-9308-1405906d2dbe)
| | | |

| Before Normal - Before Dense - After Normal - After Dense |
|--------------------------------------------------------|
| ![Group
9](https://github.com/user-attachments/assets/00815a1b-071b-4d02-96bc-36bf37b5ae8b)
|

Release Notes:

- N/A

Change summary

crates/ui/src/components/list/list.rs            | 12 ++-
crates/ui/src/components/list/list_header.rs     | 15 +++-
crates/ui/src/components/list/list_item.rs       |  8 +-
crates/ui/src/components/list/list_separator.rs  |  2 
crates/ui/src/components/list/list_sub_header.rs | 57 ++++++++++-------
5 files changed, 55 insertions(+), 39 deletions(-)

Detailed changes

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

@@ -52,13 +52,15 @@ impl ParentElement for List {
 }
 
 impl RenderOnce for List {
-    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
-        v_flex().w_full().py_1().children(self.header).map(|this| {
-            match (self.children.is_empty(), self.toggle) {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+        v_flex()
+            .w_full()
+            .py(Spacing::Small.rems(cx))
+            .children(self.header)
+            .map(|this| match (self.children.is_empty(), self.toggle) {
                 (false, _) => this.children(self.children),
                 (true, Some(false)) => this,
                 (true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)),
-            }
-        })
+            })
     }
 }

crates/ui/src/components/list/list_header.rs 🔗

@@ -2,6 +2,8 @@ use std::sync::Arc;
 
 use crate::{h_flex, prelude::*, Disclosure, Label};
 use gpui::{AnyElement, ClickEvent};
+use settings::Settings;
+use theme::ThemeSettings;
 
 #[derive(IntoElement)]
 pub struct ListHeader {
@@ -78,6 +80,8 @@ impl Selectable for ListHeader {
 
 impl RenderOnce for ListHeader {
     fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+        let ui_density = ThemeSettings::get_global(cx).ui_density;
+
         h_flex()
             .id(self.label.clone())
             .w_full()
@@ -85,7 +89,10 @@ impl RenderOnce for ListHeader {
             .group("list_header")
             .child(
                 div()
-                    .h_7()
+                    .map(|this| match ui_density {
+                        theme::UiDensity::Comfortable => this.h_5(),
+                        _ => this.h_7(),
+                    })
                     .when(self.inset, |this| this.px_2())
                     .when(self.selected, |this| {
                         this.bg(cx.theme().colors().ghost_element_selected)
@@ -95,10 +102,10 @@ impl RenderOnce for ListHeader {
                     .items_center()
                     .justify_between()
                     .w_full()
-                    .gap_1()
+                    .gap(Spacing::Small.rems(cx))
                     .child(
                         h_flex()
-                            .gap_1()
+                            .gap(Spacing::Small.rems(cx))
                             .children(self.toggle.map(|is_open| {
                                 Disclosure::new("toggle", is_open).on_toggle(self.on_toggle.clone())
                             }))
@@ -106,7 +113,7 @@ impl RenderOnce for ListHeader {
                                 div()
                                     .id("label_container")
                                     .flex()
-                                    .gap_1()
+                                    .gap(Spacing::Small.rems(cx))
                                     .items_center()
                                     .children(self.start_slot)
                                     .child(Label::new(self.label.clone()).color(Color::Muted))

crates/ui/src/components/list/list_item.rs 🔗

@@ -162,7 +162,7 @@ impl RenderOnce for ListItem {
             // When an item is inset draw the indent spacing outside of the item
             .when(self.inset, |this| {
                 this.ml(self.indent_level as f32 * self.indent_step_size)
-                    .px_1()
+                    .px(Spacing::Small.rems(cx))
             })
             .when(!self.inset && !self.disabled, |this| {
                 this
@@ -185,7 +185,7 @@ impl RenderOnce for ListItem {
                     .w_full()
                     .relative()
                     .gap_1()
-                    .px_1p5()
+                    .px(Spacing::Medium.rems(cx))
                     .map(|this| match self.spacing {
                         ListItemSpacing::Dense => this,
                         ListItemSpacing::Sparse => this.py_1(),
@@ -238,7 +238,7 @@ impl RenderOnce for ListItem {
                             .flex_grow()
                             .flex_shrink_0()
                             .flex_basis(relative(0.25))
-                            .gap_1()
+                            .gap(Spacing::Small.rems(cx))
                             .overflow_hidden()
                             .children(self.start_slot)
                             .children(self.children),
@@ -260,7 +260,7 @@ impl RenderOnce for ListItem {
                             h_flex()
                                 .h_full()
                                 .absolute()
-                                .right_1p5()
+                                .right(Spacing::Medium.rems(cx))
                                 .top_0()
                                 .visible_on_hover("list_item")
                                 .child(end_hover_slot),

crates/ui/src/components/list/list_sub_header.rs 🔗

@@ -39,30 +39,37 @@ impl Selectable for ListSubHeader {
 
 impl RenderOnce for ListSubHeader {
     fn render(self, cx: &mut WindowContext) -> impl IntoElement {
-        h_flex().flex_1().w_full().relative().pb_1().px_0p5().child(
-            div()
-                .h_6()
-                .when(self.inset, |this| this.px_2())
-                .when(self.selected, |this| {
-                    this.bg(cx.theme().colors().ghost_element_selected)
-                })
-                .flex()
-                .flex_1()
-                .w_full()
-                .gap_1()
-                .items_center()
-                .justify_between()
-                .child(
-                    div()
-                        .flex()
-                        .gap_1()
-                        .items_center()
-                        .children(
-                            self.start_slot
-                                .map(|i| Icon::new(i).color(Color::Muted).size(IconSize::Small)),
-                        )
-                        .child(Label::new(self.label.clone()).color(Color::Muted)),
-                ),
-        )
+        h_flex()
+            .flex_1()
+            .w_full()
+            .relative()
+            .pb(Spacing::Small.rems(cx))
+            .px(Spacing::XSmall.rems(cx))
+            .child(
+                div()
+                    .h_6()
+                    .when(self.inset, |this| this.px_2())
+                    .when(self.selected, |this| {
+                        this.bg(cx.theme().colors().ghost_element_selected)
+                    })
+                    .flex()
+                    .flex_1()
+                    .w_full()
+                    .gap_1()
+                    .items_center()
+                    .justify_between()
+                    .child(
+                        div()
+                            .flex()
+                            .gap_1()
+                            .items_center()
+                            .children(
+                                self.start_slot.map(|i| {
+                                    Icon::new(i).color(Color::Muted).size(IconSize::Small)
+                                }),
+                            )
+                            .child(Label::new(self.label.clone()).color(Color::Muted)),
+                    ),
+            )
     }
 }