image viewer: Show path in breadcrumbs (#20155)

Bennet Bo Fenner created

Closes #10057

<img width="354" alt="image"
src="https://github.com/user-attachments/assets/47afe8fd-c8ac-45af-be9a-9ca8c5e066f6">

Release Notes:

- Show path in breadcrumbs/toolbar when opening an image

Change summary

Cargo.lock                              |  1 
crates/image_viewer/Cargo.toml          |  7 ++--
crates/image_viewer/src/image_viewer.rs | 40 +++++++++++++++++++++++++-
3 files changed, 43 insertions(+), 5 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5797,6 +5797,7 @@ dependencies = [
  "gpui",
  "project",
  "settings",
+ "theme",
  "ui",
  "workspace",
 ]

crates/image_viewer/Cargo.toml 🔗

@@ -15,9 +15,10 @@ doctest = false
 [dependencies]
 anyhow.workspace = true
 db.workspace = true
-gpui.workspace = true
 file_icons.workspace = true
-ui.workspace = true
+gpui.workspace = true
+project.workspace = true
 settings.workspace = true
+theme.workspace = true
+ui.workspace = true
 workspace.workspace = true
-project.workspace = true

crates/image_viewer/src/image_viewer.rs 🔗

@@ -6,6 +6,7 @@ use gpui::{
     WindowContext,
 };
 use persistence::IMAGE_VIEWER;
+use theme::Theme;
 use ui::prelude::*;
 
 use file_icons::FileIcons;
@@ -13,8 +14,8 @@ use project::{Project, ProjectEntryId, ProjectPath};
 use settings::Settings;
 use std::{ffi::OsStr, path::PathBuf};
 use workspace::{
-    item::{Item, ProjectItem, SerializableItem, TabContentParams},
-    ItemId, ItemSettings, Pane, Workspace, WorkspaceId,
+    item::{BreadcrumbText, Item, ProjectItem, SerializableItem, TabContentParams},
+    ItemId, ItemSettings, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
 };
 
 const IMAGE_VIEWER_KIND: &str = "ImageView";
@@ -23,6 +24,7 @@ pub struct ImageItem {
     id: ProjectEntryId,
     path: PathBuf,
     project_path: ProjectPath,
+    project: Model<Project>,
 }
 
 impl project::Item for ImageItem {
@@ -56,6 +58,7 @@ impl project::Item for ImageItem {
                     .id;
 
                 cx.new_model(|_| ImageItem {
+                    project,
                     path: abs_path,
                     project_path: path,
                     id,
@@ -118,6 +121,19 @@ impl Item for ImageView {
             .map(Icon::from_path)
     }
 
+    fn breadcrumb_location(&self) -> ToolbarItemLocation {
+        ToolbarItemLocation::PrimaryLeft
+    }
+
+    fn breadcrumbs(&self, _theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+        let text = breadcrumbs_text_for_image(self.image.read(cx), cx);
+        Some(vec![BreadcrumbText {
+            text,
+            highlights: None,
+            font: None,
+        }])
+    }
+
     fn clone_on_split(
         &self,
         _workspace_id: Option<WorkspaceId>,
@@ -133,6 +149,25 @@ impl Item for ImageView {
     }
 }
 
+fn breadcrumbs_text_for_image(image: &ImageItem, cx: &AppContext) -> String {
+    let path = &image.project_path.path;
+    let project = image.project.read(cx);
+
+    if project.visible_worktrees(cx).count() <= 1 {
+        return path.to_string_lossy().to_string();
+    }
+
+    project
+        .worktree_for_entry(image.id, cx)
+        .map(|worktree| {
+            PathBuf::from(worktree.read(cx).root_name())
+                .join(path)
+                .to_string_lossy()
+                .to_string()
+        })
+        .unwrap_or_else(|| path.to_string_lossy().to_string())
+}
+
 impl SerializableItem for ImageView {
     fn serialized_item_kind() -> &'static str {
         IMAGE_VIEWER_KIND
@@ -175,6 +210,7 @@ impl SerializableItem for ImageView {
                     id,
                     path: image_path,
                     project_path,
+                    project,
                 });
 
                 Ok(cx.new_view(|cx| ImageView {