Git commit modal branch list (#26417)

Conrad Irwin created

Closes #26273

Release Notes:

- git: Fixes opening the branch selector in the commit modal with
cmd-option-b
- git: Truncates the branch selector in the commit modal

Change summary

crates/git_ui/src/branch_picker.rs  |  7 --
crates/git_ui/src/commit_modal.rs   | 73 ++++++++++++++----------------
crates/git_ui/src/git_panel.rs      | 11 +---
crates/workspace/src/modal_layer.rs |  1 
4 files changed, 39 insertions(+), 53 deletions(-)

Detailed changes

crates/git_ui/src/branch_picker.rs 🔗

@@ -12,9 +12,7 @@ use project::git::Repository;
 use std::sync::Arc;
 use time::OffsetDateTime;
 use time_format::format_local_timestamp;
-use ui::{
-    prelude::*, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, PopoverMenuHandle, Tooltip,
-};
+use ui::{prelude::*, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, Tooltip};
 use util::ResultExt;
 use workspace::notifications::DetachAndPromptErr;
 use workspace::{ModalView, Workspace};
@@ -79,7 +77,6 @@ enum BranchListStyle {
 
 pub struct BranchList {
     width: Rems,
-    pub popover_handle: PopoverMenuHandle<Self>,
     pub picker: Entity<Picker<BranchListDelegate>>,
     _subscription: Subscription,
 }
@@ -92,7 +89,6 @@ impl BranchList {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Self {
-        let popover_handle = PopoverMenuHandle::default();
         let all_branches_request = repository
             .clone()
             .map(|repository| repository.read(cx).branches());
@@ -130,7 +126,6 @@ impl BranchList {
         Self {
             picker,
             width,
-            popover_handle,
             _subscription,
         }
     }

crates/git_ui/src/commit_modal.rs 🔗

@@ -4,7 +4,7 @@ use crate::branch_picker::{self, BranchList};
 use crate::git_panel::{commit_message_editor, GitPanel};
 use git::{Commit, GenerateCommitMessage};
 use panel::{panel_button, panel_editor_style, panel_filled_button};
-use ui::{prelude::*, KeybindingHint, PopoverMenu, Tooltip};
+use ui::{prelude::*, KeybindingHint, PopoverMenu, PopoverMenuHandle, Tooltip};
 
 use editor::{Editor, EditorElement};
 use gpui::*;
@@ -65,11 +65,11 @@ pub fn init(cx: &mut App) {
 }
 
 pub struct CommitModal {
-    branch_list: Entity<BranchList>,
     git_panel: Entity<GitPanel>,
     commit_editor: Entity<Editor>,
     restore_dock: RestoreDock,
     properties: ModalContainerProperties,
+    branch_list_handle: PopoverMenuHandle<BranchList>,
 }
 
 impl Focusable for CommitModal {
@@ -146,7 +146,6 @@ impl CommitModal {
         cx: &mut Context<Self>,
     ) -> Self {
         let panel = git_panel.read(cx);
-        let active_repository = panel.active_repository.clone();
         let suggested_commit_message = panel.suggest_commit_message();
 
         let commit_editor = git_panel.update(cx, |git_panel, cx| {
@@ -177,11 +176,7 @@ impl CommitModal {
         let focus_handle = commit_editor.focus_handle(cx);
 
         cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {
-            if !this
-                .branch_list
-                .focus_handle(cx)
-                .contains_focused(window, cx)
-            {
+            if !this.branch_list_handle.is_focused(window, cx) {
                 cx.emit(DismissEvent);
             }
         })
@@ -190,11 +185,11 @@ impl CommitModal {
         let properties = ModalContainerProperties::new(window, 50);
 
         Self {
-            branch_list: branch_picker::popover(active_repository.clone(), window, cx),
             git_panel,
             commit_editor,
             restore_dock,
             properties,
+            branch_list_handle: PopoverMenuHandle::default(),
         }
     }
 
@@ -232,32 +227,29 @@ impl CommitModal {
     }
 
     pub fn render_footer(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        let (branch, can_commit, tooltip, commit_label, co_authors, generate_commit_message) =
+        let (can_commit, tooltip, commit_label, co_authors, generate_commit_message, active_repo) =
             self.git_panel.update(cx, |git_panel, cx| {
-                let branch = git_panel
-                    .active_repository
-                    .as_ref()
-                    .and_then(|repo| {
-                        repo.read(cx)
-                            .repository_entry
-                            .branch()
-                            .map(|b| b.name.clone())
-                    })
-                    .unwrap_or_else(|| "<no branch>".into());
                 let (can_commit, tooltip) = git_panel.configure_commit_button(cx);
                 let title = git_panel.commit_button_title();
                 let co_authors = git_panel.render_co_authors(cx);
                 let generate_commit_message = git_panel.render_generate_commit_message_button(cx);
+                let active_repo = git_panel.active_repository.clone();
                 (
-                    branch,
                     can_commit,
                     tooltip,
                     title,
                     co_authors,
                     generate_commit_message,
+                    active_repo,
                 )
             });
 
+        let branch = active_repo
+            .as_ref()
+            .and_then(|repo| repo.read(cx).repository_entry.branch())
+            .map(|b| b.name.clone())
+            .unwrap_or_else(|| "<no branch>".into());
+
         let branch_picker_button = panel_button(branch)
             .icon(IconName::GitBranch)
             .icon_size(IconSize::Small)
@@ -274,10 +266,8 @@ impl CommitModal {
             .style(ButtonStyle::Transparent);
 
         let branch_picker = PopoverMenu::new("popover-button")
-            .menu({
-                let branch_list = self.branch_list.clone();
-                move |_window, _cx| Some(branch_list.clone())
-            })
+            .menu(move |window, cx| Some(branch_picker::popover(active_repo.clone(), window, cx)))
+            .with_handle(self.branch_list_handle.clone())
             .trigger_with_tooltip(
                 branch_picker_button,
                 Tooltip::for_action_title("Switch Branch", &zed_actions::git::Branch),
@@ -326,7 +316,14 @@ impl CommitModal {
             .child(
                 h_flex()
                     .gap_1()
-                    .child(branch_picker)
+                    .flex_shrink()
+                    .overflow_x_hidden()
+                    .child(
+                        h_flex()
+                            .flex_shrink()
+                            .overflow_x_hidden()
+                            .child(branch_picker),
+                    )
                     .children(generate_commit_message)
                     .children(co_authors),
             )
@@ -353,6 +350,14 @@ impl CommitModal {
             .update(cx, |git_panel, cx| git_panel.commit_changes(window, cx));
         cx.emit(DismissEvent);
     }
+
+    fn toggle_branch_selector(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        if self.branch_list_handle.is_focused(window, cx) {
+            self.focus_handle(cx).focus(window)
+        } else {
+            self.branch_list_handle.toggle(window, cx);
+        }
+    }
 }
 
 impl Render for CommitModal {
@@ -375,17 +380,17 @@ impl Render for CommitModal {
             }))
             .on_action(
                 cx.listener(|this, _: &zed_actions::git::Branch, window, cx| {
-                    toggle_branch_picker(this, window, cx);
+                    this.toggle_branch_selector(window, cx);
                 }),
             )
             .on_action(
                 cx.listener(|this, _: &zed_actions::git::CheckoutBranch, window, cx| {
-                    toggle_branch_picker(this, window, cx);
+                    this.toggle_branch_selector(window, cx);
                 }),
             )
             .on_action(
                 cx.listener(|this, _: &zed_actions::git::Switch, window, cx| {
-                    toggle_branch_picker(this, window, cx);
+                    this.toggle_branch_selector(window, cx);
                 }),
             )
             .elevation_3(cx)
@@ -424,13 +429,3 @@ impl Render for CommitModal {
             )
     }
 }
-
-fn toggle_branch_picker(
-    this: &mut CommitModal,
-    window: &mut Window,
-    cx: &mut Context<'_, CommitModal>,
-) {
-    this.branch_list.update(cx, |branch_list, cx| {
-        branch_list.popover_handle.toggle(window, cx);
-    })
-}

crates/git_ui/src/git_panel.rs 🔗

@@ -2361,10 +2361,7 @@ impl GitPanel {
                             cx,
                         )
                     } else {
-                        Tooltip::simple(
-                            "You must have either staged changes or tracked files to generate a commit message",
-                            cx,
-                        )
+                        Tooltip::simple("No changes to commit", cx)
                     }
                 })
                 .disabled(!can_commit)
@@ -2414,10 +2411,7 @@ impl GitPanel {
         if self.has_unstaged_conflicts() {
             (false, "You must resolve conflicts before committing")
         } else if !self.has_staged_changes() && !self.has_tracked_changes() {
-            (
-                false,
-                "You must have either staged changes or tracked files to commit",
-            )
+            (false, "No changes to commit")
         } else if self.pending_commit.is_some() {
             (false, "Commit in progress")
         } else if self.custom_or_suggested_commit_message(cx).is_none() {
@@ -3579,6 +3573,7 @@ impl RenderOnce for PanelRepoFooter {
             .h(px(36.))
             .items_center()
             .justify_between()
+            .gap_1()
             .child(
                 h_flex()
                     .flex_1()

crates/workspace/src/modal_layer.rs 🔗

@@ -1,6 +1,7 @@
 use gpui::{AnyView, DismissEvent, Entity, FocusHandle, Focusable as _, ManagedView, Subscription};
 use ui::prelude::*;
 
+#[derive(Debug)]
 pub enum DismissDecision {
     Dismiss(bool),
     Pending,