File finder UI enhancement (#7364)

Andrew Lygin created

File finder looks and feels a little bulky now. It duplicates file names
and consumes too much space for each file.

This PR makes it more compact:
- File name is trimmed from the path, removing duplication
- Path is placed to the right of the file name, improving space usage
- Path is muted and printed in small size to not distract attention from
the main information (file names)

It makes search results easier to look through, consistent with the
editor tabs, and closer in terms of usage to mature editors.

Release Notes:

- File finder UI enhancement

Change summary

crates/file_finder/src/file_finder.rs               | 19 ++++++++++++--
crates/file_finder/src/file_finder_tests.rs         |  4 +-
crates/ui/src/components/label/highlighted_label.rs |  4 ++
crates/ui/src/components/label/label_like.rs        |  2 
4 files changed, 22 insertions(+), 7 deletions(-)

Detailed changes

crates/file_finder/src/file_finder.rs 🔗

@@ -566,7 +566,7 @@ impl FileFinderDelegate {
         let path = &path_match.path;
         let path_string = path.to_string_lossy();
         let full_path = [path_match.path_prefix.as_ref(), path_string.as_ref()].join("");
-        let path_positions = path_match.positions.clone();
+        let mut path_positions = path_match.positions.clone();
 
         let file_name = path.file_name().map_or_else(
             || path_match.path_prefix.to_string(),
@@ -584,6 +584,14 @@ impl FileFinderDelegate {
             })
             .collect();
 
+        // Trim file name from the full path
+        let full_path = if full_path.len() > file_name.len() {
+            full_path[..full_path.len() - file_name.len() - 1].to_string()
+        } else {
+            "".to_string()
+        };
+        path_positions.retain(|idx| *idx < full_path.len());
+
         (file_name, file_name_positions, full_path, path_positions)
     }
 
@@ -868,9 +876,14 @@ impl PickerDelegate for FileFinderDelegate {
                 .inset(true)
                 .selected(selected)
                 .child(
-                    v_flex()
+                    h_flex()
+                        .gap_2()
                         .child(HighlightedLabel::new(file_name, file_name_positions))
-                        .child(HighlightedLabel::new(full_path, full_path_positions)),
+                        .child(
+                            HighlightedLabel::new(full_path, full_path_positions)
+                                .size(LabelSize::Small)
+                                .color(Color::Muted),
+                        ),
                 ),
         )
     }

crates/file_finder/src/file_finder_tests.rs 🔗

@@ -490,8 +490,8 @@ async fn test_single_file_worktrees(cx: &mut TestAppContext) {
             delegate.labels_for_path_match(&matches[0].0);
         assert_eq!(file_name, "the-file");
         assert_eq!(file_name_positions, &[0, 1, 4]);
-        assert_eq!(full_path, "the-file");
-        assert_eq!(full_path_positions, &[0, 1, 4]);
+        assert_eq!(full_path, "");
+        assert_eq!(full_path_positions, &[0; 0]);
     });
 
     // Since the worktree root is a file, searching for its name followed by a slash does

crates/ui/src/components/label/highlighted_label.rs 🔗

@@ -79,6 +79,8 @@ impl RenderOnce for HighlightedLabel {
         let mut text_style = cx.text_style().clone();
         text_style.color = self.base.color.color(cx);
 
-        LabelLike::new().child(StyledText::new(self.label).with_highlights(&text_style, highlights))
+        LabelLike::new()
+            .size(self.base.size)
+            .child(StyledText::new(self.label).with_highlights(&text_style, highlights))
     }
 }

crates/ui/src/components/label/label_like.rs 🔗

@@ -36,7 +36,7 @@ pub trait LabelCommon {
 
 #[derive(IntoElement)]
 pub struct LabelLike {
-    size: LabelSize,
+    pub(crate) size: LabelSize,
     line_height_style: LineHeightStyle,
     pub(crate) color: Color,
     strikethrough: bool,