Git commit modal branch list (#26417)

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
This commit is contained in:
Conrad Irwin 2025-03-10 22:10:52 -06:00 committed by GitHub
parent bf11b888c3
commit c2e4fdf63d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 53 deletions

View file

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

View file

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

View file

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

View file

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