Follow-up tweaks to new git panel footer (#25832)

Cole Miller created

- Use a popover for the branch picker
- Don't deploy a repository selector if there's only one repo

Release Notes:

- N/A

Change summary

crates/git_ui/src/git_panel.rs           | 88 +++++++++++++++++--------
crates/git_ui/src/repository_selector.rs |  4 +
2 files changed, 64 insertions(+), 28 deletions(-)

Detailed changes

crates/git_ui/src/git_panel.rs 🔗

@@ -1,3 +1,4 @@
+use crate::branch_picker::{self, BranchList};
 use crate::git_panel_settings::StatusStyle;
 use crate::repository_selector::RepositorySelectorPopoverMenu;
 use crate::{
@@ -39,7 +40,7 @@ use strum::{IntoEnumIterator, VariantNames};
 use time::OffsetDateTime;
 use ui::{
     prelude::*, ButtonLike, Checkbox, ContextMenu, ElevationIndex, ListItem, ListItemSpacing,
-    PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
+    PopoverButton, PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
 };
 use util::{maybe, post_inc, ResultExt, TryFutureExt};
 use workspace::{
@@ -1887,7 +1888,7 @@ impl GitPanel {
             };
             let editor_focus_handle = self.commit_editor.focus_handle(cx);
 
-            let branch = active_repo.read(cx).current_branch()?;
+            let branch = active_repo.read(cx).current_branch()?.clone();
 
             let footer_size = px(32.);
             let gap = px(8.0);
@@ -1903,12 +1904,14 @@ impl GitPanel {
                     .display_name(project, cx)
                     .trim_end_matches("/"),
             ));
+            let branches = branch_picker::popover(self.project.clone(), window, cx);
             let footer = v_flex()
-                .child(PanelRepoHeader::new(
-                    "header-button",
+                .child(PanelRepoFooter::new(
+                    "footer-button",
                     display_name,
-                    Some(branch.clone()),
+                    Some(branch),
                     Some(git_panel),
+                    Some(branches),
                 ))
                 .child(
                     panel_editor_container(window, cx)
@@ -2851,7 +2854,7 @@ fn render_git_action_menu(id: impl Into<ElementId>) -> impl IntoElement {
 
 #[derive(IntoElement, IntoComponent)]
 #[component(scope = "git_panel")]
-pub struct PanelRepoHeader {
+pub struct PanelRepoFooter {
     id: SharedString,
     active_repository: SharedString,
     branch: Option<Branch>,
@@ -2859,20 +2862,23 @@ pub struct PanelRepoHeader {
     //
     // For now just take an option here, and we won't bind handlers to buttons in previews.
     git_panel: Option<Entity<GitPanel>>,
+    branches: Option<Entity<BranchList>>,
 }
 
-impl PanelRepoHeader {
+impl PanelRepoFooter {
     pub fn new(
         id: impl Into<SharedString>,
         active_repository: SharedString,
         branch: Option<Branch>,
         git_panel: Option<Entity<GitPanel>>,
+        branches: Option<Entity<BranchList>>,
     ) -> Self {
         Self {
             id: id.into(),
             active_repository,
             branch,
             git_panel,
+            branches,
         }
     }
 
@@ -2886,6 +2892,7 @@ impl PanelRepoHeader {
             active_repository,
             branch,
             git_panel: None,
+            branches: None,
         }
     }
 
@@ -3067,22 +3074,32 @@ impl PanelRepoHeader {
     }
 }
 
-impl RenderOnce for PanelRepoHeader {
-    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
+impl RenderOnce for PanelRepoFooter {
+    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
         let active_repo = self.active_repository.clone();
         let overflow_menu_id: SharedString = format!("overflow-menu-{}", active_repo).into();
 
         let repo_selector = if let Some(panel) = self.git_panel.clone() {
-            RepositorySelectorPopoverMenu::new(
-                panel.read(cx).repository_selector.clone(),
-                Button::new("repo-selector", active_repo.clone())
-                    .style(ButtonStyle::Transparent)
-                    .size(ButtonSize::None)
-                    .label_size(LabelSize::Small)
-                    .color(Color::Muted),
-                Tooltip::text("Choose a repository"),
-            )
-            .into_any_element()
+            let repo_selector = panel.read(cx).repository_selector.clone();
+            let repo_count = repo_selector.read(cx).repositories_len(cx);
+            if repo_count > 1 {
+                RepositorySelectorPopoverMenu::new(
+                    panel.read(cx).repository_selector.clone(),
+                    Button::new("repo-selector", active_repo)
+                        .style(ButtonStyle::Transparent)
+                        .size(ButtonSize::None)
+                        .label_size(LabelSize::Small)
+                        .color(Color::Muted),
+                    Tooltip::text("Choose a repository"),
+                )
+                .into_any_element()
+            } else {
+                Label::new(active_repo)
+                    .size(LabelSize::Small)
+                    .color(Color::Muted)
+                    .line_height_style(LineHeightStyle::UiLabel)
+                    .into_any_element()
+            }
         } else {
             Button::new("repo-selector", active_repo.clone())
                 .style(ButtonStyle::Transparent)
@@ -3097,7 +3114,9 @@ impl RenderOnce for PanelRepoHeader {
             .as_ref()
             .map_or("<no branch>".into(), |branch| branch.name.clone());
 
-        let branch_selector = Button::new("branch-selector", branch_name)
+        let branches = self.branches.clone();
+
+        let branch_selector_button = Button::new("branch-selector", branch_name)
             .style(ButtonStyle::Transparent)
             .size(ButtonSize::None)
             .label_size(LabelSize::Small)
@@ -3109,6 +3128,19 @@ impl RenderOnce for PanelRepoHeader {
                 window.dispatch_action(zed_actions::git::Branch.boxed_clone(), cx);
             });
 
+        let branch_selector = if let Some(branches) = branches {
+            PopoverButton::new(
+                branches,
+                Corner::BottomLeft,
+                branch_selector_button,
+                Tooltip::for_action_title("Switch Branch", &zed_actions::git::Branch),
+            )
+            .render(window, cx)
+            .into_any_element()
+        } else {
+            branch_selector_button.into_any_element()
+        };
+
         let spinner = self
             .git_panel
             .as_ref()
@@ -3162,7 +3194,7 @@ impl RenderOnce for PanelRepoHeader {
     }
 }
 
-impl ComponentPreview for PanelRepoHeader {
+impl ComponentPreview for PanelRepoFooter {
     fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
         let unknown_upstream = None;
         let no_remote_upstream = Some(UpstreamTracking::Gone);
@@ -3225,7 +3257,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "No Branch",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "no-branch",
                                 active_repository(1).clone(),
                                 None,
@@ -3236,7 +3268,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "Remote status unknown",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "unknown-upstream",
                                 active_repository(2).clone(),
                                 Some(branch(unknown_upstream)),
@@ -3247,7 +3279,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "No Remote Upstream",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "no-remote-upstream",
                                 active_repository(3).clone(),
                                 Some(branch(no_remote_upstream)),
@@ -3258,7 +3290,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "Not Ahead or Behind",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "not-ahead-or-behind",
                                 active_repository(4).clone(),
                                 Some(branch(not_ahead_or_behind_upstream)),
@@ -3269,7 +3301,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "Behind remote",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "behind-remote",
                                 active_repository(5).clone(),
                                 Some(branch(behind_upstream)),
@@ -3280,7 +3312,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "Ahead of remote",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "ahead-of-remote",
                                 active_repository(6).clone(),
                                 Some(branch(ahead_of_upstream)),
@@ -3291,7 +3323,7 @@ impl ComponentPreview for PanelRepoHeader {
                         "Ahead and behind remote",
                         div()
                             .w(px(180.))
-                            .child(PanelRepoHeader::new_preview(
+                            .child(PanelRepoFooter::new_preview(
                                 "ahead-and-behind",
                                 active_repository(7).clone(),
                                 Some(branch(ahead_and_behind_upstream)),

crates/git_ui/src/repository_selector.rs 🔗

@@ -47,6 +47,10 @@ impl RepositorySelector {
         }
     }
 
+    pub(crate) fn repositories_len(&self, cx: &App) -> usize {
+        self.picker.read(cx).delegate.repository_entries.len()
+    }
+
     fn handle_project_git_event(
         &mut self,
         git_store: &Entity<GitStore>,