Refine people panel styling

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

gpui/src/elements/container.rs |   5 +
zed/assets/themes/_base.toml   |  33 ++++--
zed/src/people_panel.rs        | 169 ++++++++++++++++++-----------------
zed/src/theme.rs               |  26 +++--
4 files changed, 126 insertions(+), 107 deletions(-)

Detailed changes

gpui/src/elements/container.rs 🔗

@@ -57,6 +57,11 @@ impl Container {
         self
     }
 
+    pub fn with_margin_right(mut self, margin: f32) -> Self {
+        self.style.margin.right = margin;
+        self
+    }
+
     pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
         self.style.padding.left = padding;
         self.style.padding.right = padding;

zed/assets/themes/_base.toml 🔗

@@ -125,31 +125,38 @@ color = "$text.1.color"
 
 [people_panel]
 extends = "$panel"
+host_row_height = 28
 host_avatar = { corner_radius = 10, width = 20 }
-host_username = { extends = "$text.0", padding.left = 5 }
+host_username = { extends = "$text.0", padding.left = 8 }
+tree_branch_width = 1
+tree_branch_color = "$surface.2"
+
+[people_panel.worktree]
+height = 24
+padding = { left = 8 }
 guest_avatar = { corner_radius = 8, width = 16 }
-guest_avatar_spacing = 8
+guest_avatar_spacing = 4
 
-[people_panel.unshared_worktree]
+[people_panel.worktree.name]
 extends = "$text.1"
-padding = { left = 5 }
+margin = { right = 6 }
+
+[people_panel.unshared_worktree]
+extends = "$people_panel.worktree"
 
 [people_panel.hovered_unshared_worktree]
-extends = "$people_panel.shared_worktree"
+extends = "$people_panel.unshared_worktree"
 background = "$state.hover"
 corner_radius = 6
 
 [people_panel.shared_worktree]
-extends = "$people_panel.unshared_worktree"
-color = "$text.0.color"
+extends = "$people_panel.worktree"
+name.color = "$text.0.color"
 
 [people_panel.hovered_shared_worktree]
-extends = "$people_panel.hovered_unshared_worktree"
-color = "$text.0.color"
-
-[people_panel.tree_branch]
-width = 1
-color = "$surface.2"
+extends = "$people_panel.shared_worktree"
+background = "$state.hover"
+corner_radius = 6
 
 [selector]
 background = "$surface.0"

zed/src/people_panel.rs 🔗

@@ -73,31 +73,32 @@ impl PeoplePanel {
         let theme = &theme.people_panel;
         let worktree_count = collaborator.worktrees.len();
         let font_cache = cx.font_cache();
-        let line_height = theme.unshared_worktree.text.line_height(font_cache);
-        let cap_height = theme.unshared_worktree.text.cap_height(font_cache);
-        let baseline_offset = theme.unshared_worktree.text.baseline_offset(font_cache);
-        let tree_branch = theme.tree_branch;
+        let line_height = theme.unshared_worktree.name.text.line_height(font_cache);
+        let cap_height = theme.unshared_worktree.name.text.cap_height(font_cache);
+        let baseline_offset = theme
+            .unshared_worktree
+            .name
+            .text
+            .baseline_offset(font_cache)
+            + (theme.unshared_worktree.height - line_height) / 2.;
+        let tree_branch_width = theme.tree_branch_width;
+        let tree_branch_color = theme.tree_branch_color;
         let host_avatar_height = theme
             .host_avatar
             .width
             .or(theme.host_avatar.height)
             .unwrap_or(0.);
-        let guest_avatar_height = theme
-            .guest_avatar
-            .width
-            .or(theme.guest_avatar.height)
-            .unwrap_or(0.);
 
         Flex::column()
             .with_child(
                 Flex::row()
-                    .with_children(
-                        collaborator
-                            .user
-                            .avatar
-                            .clone()
-                            .map(|avatar| Image::new(avatar).with_style(theme.host_avatar).boxed()),
-                    )
+                    .with_children(collaborator.user.avatar.clone().map(|avatar| {
+                        Image::new(avatar)
+                            .with_style(theme.host_avatar)
+                            .aligned()
+                            .left()
+                            .boxed()
+                    }))
                     .with_child(
                         Label::new(
                             collaborator.user.github_login.clone(),
@@ -107,10 +108,10 @@ impl PeoplePanel {
                         .with_style(theme.host_username.container)
                         .aligned()
                         .left()
-                        .constrained()
-                        .with_height(host_avatar_height)
                         .boxed(),
                     )
+                    .constrained()
+                    .with_height(theme.host_row_height)
                     .boxed(),
             )
             .with_children(
@@ -120,47 +121,45 @@ impl PeoplePanel {
                     .enumerate()
                     .map(|(ix, worktree)| {
                         let worktree_id = worktree.id;
+
                         Flex::row()
                             .with_child(
-                                ConstrainedBox::new(
-                                    Canvas::new(move |bounds, _, cx| {
-                                        let start_x = bounds.min_x() + (bounds.width() / 2.)
-                                            - (tree_branch.width / 2.);
-                                        let end_x = bounds.max_x();
-                                        let start_y = bounds.min_y();
-                                        let end_y =
-                                            bounds.min_y() + baseline_offset - (cap_height / 2.);
+                                Canvas::new(move |bounds, _, cx| {
+                                    let start_x = bounds.min_x() + (bounds.width() / 2.)
+                                        - (tree_branch_width / 2.);
+                                    let end_x = bounds.max_x();
+                                    let start_y = bounds.min_y();
+                                    let end_y =
+                                        bounds.min_y() + baseline_offset - (cap_height / 2.);
 
-                                        cx.scene.push_quad(gpui::Quad {
-                                            bounds: RectF::from_points(
-                                                vec2f(start_x, start_y),
-                                                vec2f(
-                                                    start_x + tree_branch.width,
-                                                    if ix + 1 == worktree_count {
-                                                        end_y
-                                                    } else {
-                                                        bounds.max_y()
-                                                    },
-                                                ),
-                                            ),
-                                            background: Some(tree_branch.color),
-                                            border: gpui::Border::default(),
-                                            corner_radius: 0.,
-                                        });
-                                        cx.scene.push_quad(gpui::Quad {
-                                            bounds: RectF::from_points(
-                                                vec2f(start_x, end_y),
-                                                vec2f(end_x, end_y + tree_branch.width),
+                                    cx.scene.push_quad(gpui::Quad {
+                                        bounds: RectF::from_points(
+                                            vec2f(start_x, start_y),
+                                            vec2f(
+                                                start_x + tree_branch_width,
+                                                if ix + 1 == worktree_count {
+                                                    end_y
+                                                } else {
+                                                    bounds.max_y()
+                                                },
                                             ),
-                                            background: Some(tree_branch.color),
-                                            border: gpui::Border::default(),
-                                            corner_radius: 0.,
-                                        });
-                                    })
-                                    .boxed(),
-                                )
-                                .with_width(20.)
-                                .with_height(line_height)
+                                        ),
+                                        background: Some(tree_branch_color),
+                                        border: gpui::Border::default(),
+                                        corner_radius: 0.,
+                                    });
+                                    cx.scene.push_quad(gpui::Quad {
+                                        bounds: RectF::from_points(
+                                            vec2f(start_x, end_y),
+                                            vec2f(end_x, end_y + tree_branch_width),
+                                        ),
+                                        background: Some(tree_branch_color),
+                                        border: gpui::Border::default(),
+                                        corner_radius: 0.,
+                                    });
+                                })
+                                .constrained()
+                                .with_width(host_avatar_height)
                                 .boxed(),
                             )
                             .with_child({
@@ -184,36 +183,38 @@ impl PeoplePanel {
                                             (true, true) => &theme.hovered_shared_worktree,
                                         };
 
-                                        Container::new(
-                                            Flex::row()
-                                                .with_child(
-                                                    Label::new(
-                                                        worktree.root_name.clone(),
-                                                        style.text.clone(),
-                                                    )
-                                                    .aligned()
-                                                    .left()
-                                                    .constrained()
-                                                    .with_height(guest_avatar_height)
-                                                    .boxed(),
+                                        Flex::row()
+                                            .with_child(
+                                                Label::new(
+                                                    worktree.root_name.clone(),
+                                                    style.name.text.clone(),
                                                 )
-                                                .with_children(worktree.guests.iter().filter_map(
-                                                    |participant| {
-                                                        participant.avatar.clone().map(|avatar| {
-                                                            Image::new(avatar)
-                                                                .with_style(theme.guest_avatar)
-                                                                .contained()
-                                                                .with_margin_left(
-                                                                    theme.guest_avatar_spacing,
-                                                                )
-                                                                .boxed()
-                                                        })
-                                                    },
-                                                ))
+                                                .aligned()
+                                                .left()
+                                                .contained()
+                                                .with_style(style.name.container)
                                                 .boxed(),
-                                        )
-                                        .with_style(style.container)
-                                        .boxed()
+                                            )
+                                            .with_children(worktree.guests.iter().filter_map(
+                                                |participant| {
+                                                    participant.avatar.clone().map(|avatar| {
+                                                        Image::new(avatar)
+                                                            .with_style(style.guest_avatar)
+                                                            .aligned()
+                                                            .left()
+                                                            .contained()
+                                                            .with_margin_right(
+                                                                style.guest_avatar_spacing,
+                                                            )
+                                                            .boxed()
+                                                    })
+                                                },
+                                            ))
+                                            .contained()
+                                            .with_style(style.container)
+                                            .constrained()
+                                            .with_height(style.height)
+                                            .boxed()
                                     },
                                 )
                                 .with_cursor_style(if is_host || is_shared {
@@ -237,6 +238,8 @@ impl PeoplePanel {
                                 .expanded(1.0)
                                 .boxed()
                             })
+                            .constrained()
+                            .with_height(theme.unshared_worktree.height)
                             .boxed()
                     }),
             )

zed/src/theme.rs 🔗

@@ -109,21 +109,25 @@ pub struct ChatPanel {
 pub struct PeoplePanel {
     #[serde(flatten)]
     pub container: ContainerStyle,
+    pub host_row_height: f32,
     pub host_avatar: ImageStyle,
     pub host_username: ContainedText,
-    pub shared_worktree: ContainedText,
-    pub hovered_shared_worktree: ContainedText,
-    pub unshared_worktree: ContainedText,
-    pub hovered_unshared_worktree: ContainedText,
-    pub guest_avatar: ImageStyle,
-    pub guest_avatar_spacing: f32,
-    pub tree_branch: TreeBranch,
+    pub tree_branch_width: f32,
+    pub tree_branch_color: Color,
+    pub shared_worktree: WorktreeRow,
+    pub hovered_shared_worktree: WorktreeRow,
+    pub unshared_worktree: WorktreeRow,
+    pub hovered_unshared_worktree: WorktreeRow,
 }
 
-#[derive(Copy, Clone, Deserialize)]
-pub struct TreeBranch {
-    pub width: f32,
-    pub color: Color,
+#[derive(Deserialize)]
+pub struct WorktreeRow {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub height: f32,
+    pub name: ContainedText,
+    pub guest_avatar: ImageStyle,
+    pub guest_avatar_spacing: f32,
 }
 
 #[derive(Deserialize)]