From b67d3fd21b2e18c18b76dd9b5245b9bb8d113c0b Mon Sep 17 00:00:00 2001 From: 5brian Date: Wed, 9 Apr 2025 10:46:21 -0400 Subject: [PATCH] git_ui: Show disabled states in context menu (#28288) Other elements in the git panel are shown as disabled when an action is not actionable (For example: stage all, commit). Updating the context menu to match this behavior when an action does nothing. |Before|After| |--|--| |![image](https://github.com/user-attachments/assets/e517f758-216f-4451-911b-7121dce0c53b)|![image](https://github.com/user-attachments/assets/a85905c1-2f42-44c3-8b11-2f93c8a6f686)| Release Notes: - Git: Improved the Git panel context menu to show actions with no effect as disabled. --- crates/git_ui/src/git_panel.rs | 76 ++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index be9f6e1615..3b0acc4161 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -105,21 +105,56 @@ enum TrashCancel { Cancel, } +struct GitMenuState { + has_tracked_changes: bool, + has_staged_changes: bool, + has_unstaged_changes: bool, + has_new_changes: bool, +} + fn git_panel_context_menu( focus_handle: FocusHandle, + state: GitMenuState, window: &mut Window, cx: &mut App, ) -> Entity { - ContextMenu::build(window, cx, |context_menu, _, _| { + ContextMenu::build(window, cx, move |context_menu, _, _| { context_menu .context(focus_handle) - .action("Stage All", StageAll.boxed_clone()) - .action("Unstage All", UnstageAll.boxed_clone()) + .map(|menu| { + if state.has_unstaged_changes { + menu.action("Stage All", StageAll.boxed_clone()) + } else { + menu.disabled_action("Stage All", StageAll.boxed_clone()) + } + }) + .map(|menu| { + if state.has_staged_changes { + menu.action("Unstage All", UnstageAll.boxed_clone()) + } else { + menu.disabled_action("Unstage All", UnstageAll.boxed_clone()) + } + }) .separator() .action("Open Diff", project_diff::Diff.boxed_clone()) .separator() - .action("Discard Tracked Changes", RestoreTrackedFiles.boxed_clone()) - .action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone()) + .map(|menu| { + if state.has_tracked_changes { + menu.action("Discard Tracked Changes", RestoreTrackedFiles.boxed_clone()) + } else { + menu.disabled_action( + "Discard Tracked Changes", + RestoreTrackedFiles.boxed_clone(), + ) + } + }) + .map(|menu| { + if state.has_new_changes { + menu.action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone()) + } else { + menu.disabled_action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone()) + } + }) }) } @@ -2571,13 +2606,30 @@ impl GitPanel { fn render_overflow_menu(&self, id: impl Into) -> impl IntoElement { let focus_handle = self.focus_handle.clone(); + let has_tracked_changes = self.has_tracked_changes(); + let has_staged_changes = self.has_staged_changes(); + let has_unstaged_changes = self.has_unstaged_changes(); + let has_new_changes = self.new_count > 0; + PopoverMenu::new(id.into()) .trigger( IconButton::new("overflow-menu-trigger", IconName::EllipsisVertical) .icon_size(IconSize::Small) .icon_color(Color::Muted), ) - .menu(move |window, cx| Some(git_panel_context_menu(focus_handle.clone(), window, cx))) + .menu(move |window, cx| { + Some(git_panel_context_menu( + focus_handle.clone(), + GitMenuState { + has_tracked_changes, + has_staged_changes, + has_unstaged_changes, + has_new_changes, + }, + window, + cx, + )) + }) .anchor(Corner::TopRight) } @@ -3449,7 +3501,17 @@ impl GitPanel { window: &mut Window, cx: &mut Context, ) { - let context_menu = git_panel_context_menu(self.focus_handle.clone(), window, cx); + let context_menu = git_panel_context_menu( + self.focus_handle.clone(), + GitMenuState { + has_tracked_changes: self.has_tracked_changes(), + has_staged_changes: self.has_staged_changes(), + has_unstaged_changes: self.has_unstaged_changes(), + has_new_changes: self.new_count > 0, + }, + window, + cx, + ); self.set_context_menu(context_menu, position, window, cx); }