Detailed changes
@@ -1340,4 +1340,12 @@
"ctrl-shift-i": "branch_picker::FilterRemotes",
},
},
+ {
+ "context": "GitPicker",
+ "bindings": {
+ "alt-1": "git_picker::ActivateBranchesTab",
+ "alt-2": "git_picker::ActivateWorktreesTab",
+ "alt-3": "git_picker::ActivateStashTab",
+ },
+ },
]
@@ -1442,4 +1442,12 @@
"cmd-shift-i": "branch_picker::FilterRemotes",
},
},
+ {
+ "context": "GitPicker",
+ "bindings": {
+ "cmd-1": "git_picker::ActivateBranchesTab",
+ "cmd-2": "git_picker::ActivateWorktreesTab",
+ "cmd-3": "git_picker::ActivateStashTab",
+ },
+ },
]
@@ -1361,4 +1361,12 @@
"ctrl-shift-i": "branch_picker::FilterRemotes",
},
},
+ {
+ "context": "GitPicker",
+ "bindings": {
+ "alt-1": "git_picker::ActivateBranchesTab",
+ "alt-2": "git_picker::ActivateWorktreesTab",
+ "alt-3": "git_picker::ActivateStashTab",
+ },
+ },
]
@@ -36,14 +36,6 @@ actions!(
]
);
-pub fn register(workspace: &mut Workspace) {
- workspace.register_action(|workspace, branch: &zed_actions::git::Branch, window, cx| {
- open(workspace, branch, window, cx);
- });
- workspace.register_action(switch);
- workspace.register_action(checkout_branch);
-}
-
pub fn checkout_branch(
workspace: &mut Workspace,
_: &zed_actions::git::CheckoutBranch,
@@ -103,6 +95,16 @@ pub fn popover(
})
}
+pub fn create_embedded(
+ workspace: WeakEntity<Workspace>,
+ repository: Option<Entity<Repository>>,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<BranchList>,
+) -> BranchList {
+ BranchList::new_embedded(workspace, repository, width, window, cx)
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum BranchListStyle {
Modal,
@@ -113,7 +115,8 @@ pub struct BranchList {
width: Rems,
pub picker: Entity<Picker<BranchListDelegate>>,
picker_focus_handle: FocusHandle,
- _subscription: Subscription,
+ _subscription: Option<Subscription>,
+ embedded: bool,
}
impl BranchList {
@@ -124,10 +127,27 @@ impl BranchList {
width: Rems,
window: &mut Window,
cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self::new_inner(workspace, repository, style, width, false, window, cx);
+ this._subscription = Some(cx.subscribe(&this.picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ }));
+ this
+ }
+
+ fn new_inner(
+ workspace: WeakEntity<Workspace>,
+ repository: Option<Entity<Repository>>,
+ style: BranchListStyle,
+ width: Rems,
+ embedded: bool,
+ window: &mut Window,
+ cx: &mut Context<Self>,
) -> Self {
let all_branches_request = repository
.clone()
.map(|repository| repository.update(cx, |repository, _| repository.branches()));
+
let default_branch_request = repository.clone().map(|repository| {
repository.update(cx, |repository, _| repository.default_branch(false))
});
@@ -186,25 +206,45 @@ impl BranchList {
.detach_and_log_err(cx);
let delegate = BranchListDelegate::new(workspace, repository, style, cx);
- let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
+ let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(!embedded));
let picker_focus_handle = picker.focus_handle(cx);
+
picker.update(cx, |picker, _| {
picker.delegate.focus_handle = picker_focus_handle.clone();
});
- let _subscription = cx.subscribe(&picker, |_, _, _, cx| {
- cx.emit(DismissEvent);
- });
-
Self {
picker,
picker_focus_handle,
width,
- _subscription,
+ _subscription: None,
+ embedded,
}
}
- fn handle_modifiers_changed(
+ fn new_embedded(
+ workspace: WeakEntity<Workspace>,
+ repository: Option<Entity<Repository>>,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self::new_inner(
+ workspace,
+ repository,
+ BranchListStyle::Modal,
+ width,
+ true,
+ window,
+ cx,
+ );
+ this._subscription = Some(cx.subscribe(&this.picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ }));
+ this
+ }
+
+ pub fn handle_modifiers_changed(
&mut self,
ev: &ModifiersChangedEvent,
_: &mut Window,
@@ -214,7 +254,7 @@ impl BranchList {
.update(cx, |picker, _| picker.delegate.modifiers = ev.modifiers)
}
- fn handle_delete(
+ pub fn handle_delete(
&mut self,
_: &branch_picker::DeleteBranch,
window: &mut Window,
@@ -227,7 +267,7 @@ impl BranchList {
})
}
- fn handle_filter(
+ pub fn handle_filter(
&mut self,
_: &branch_picker::FilterRemotes,
window: &mut Window,
@@ -259,10 +299,12 @@ impl Render for BranchList {
.on_action(cx.listener(Self::handle_delete))
.on_action(cx.listener(Self::handle_filter))
.child(self.picker.clone())
- .on_mouse_down_out({
- cx.listener(move |this, _, window, cx| {
- this.picker.update(cx, |this, cx| {
- this.cancel(&Default::default(), window, cx);
+ .when(!self.embedded, |this| {
+ this.on_mouse_down_out({
+ cx.listener(move |this, _, window, cx| {
+ this.picker.update(cx, |this, cx| {
+ this.cancel(&Default::default(), window, cx);
+ })
})
})
})
@@ -1324,7 +1366,8 @@ mod tests {
picker,
picker_focus_handle,
width: rems(34.),
- _subscription,
+ _subscription: Some(_subscription),
+ embedded: false,
}
})
})
@@ -0,0 +1,578 @@
+use std::fmt::Display;
+
+use gpui::{
+ App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
+ KeyContext, ModifiersChangedEvent, MouseButton, ParentElement, Render, Styled, Subscription,
+ WeakEntity, Window, actions, rems,
+};
+use project::git_store::Repository;
+use ui::{
+ FluentBuilder, ToggleButtonGroup, ToggleButtonGroupStyle, ToggleButtonSimple, Tooltip,
+ prelude::*,
+};
+use workspace::{ModalView, Workspace, pane};
+
+use crate::branch_picker::{self, BranchList, DeleteBranch, FilterRemotes};
+use crate::stash_picker::{self, DropStashItem, ShowStashItem, StashList};
+use crate::worktree_picker::{
+ self, WorktreeFromDefault, WorktreeFromDefaultOnWindow, WorktreeList,
+};
+
+actions!(
+ git_picker,
+ [ActivateBranchesTab, ActivateWorktreesTab, ActivateStashTab,]
+);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum GitPickerTab {
+ Branches,
+ Worktrees,
+ Stash,
+}
+
+impl Display for GitPickerTab {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let label = match self {
+ GitPickerTab::Branches => "Branches",
+ GitPickerTab::Worktrees => "Worktrees",
+ GitPickerTab::Stash => "Stash",
+ };
+ write!(f, "{}", label)
+ }
+}
+
+pub struct GitPicker {
+ tab: GitPickerTab,
+ workspace: WeakEntity<Workspace>,
+ repository: Option<Entity<Repository>>,
+ width: Rems,
+ branch_list: Option<Entity<BranchList>>,
+ worktree_list: Option<Entity<WorktreeList>>,
+ stash_list: Option<Entity<StashList>>,
+ _subscriptions: Vec<Subscription>,
+}
+
+impl GitPicker {
+ pub fn new(
+ workspace: WeakEntity<Workspace>,
+ repository: Option<Entity<Repository>>,
+ initial_tab: GitPickerTab,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self {
+ tab: initial_tab,
+ workspace,
+ repository,
+ width,
+ branch_list: None,
+ worktree_list: None,
+ stash_list: None,
+ _subscriptions: Vec::new(),
+ };
+
+ this.ensure_active_picker(window, cx);
+ this
+ }
+
+ fn ensure_active_picker(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ match self.tab {
+ GitPickerTab::Branches => {
+ self.ensure_branch_list(window, cx);
+ }
+ GitPickerTab::Worktrees => {
+ self.ensure_worktree_list(window, cx);
+ }
+ GitPickerTab::Stash => {
+ self.ensure_stash_list(window, cx);
+ }
+ }
+ }
+
+ fn ensure_branch_list(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Entity<BranchList> {
+ if self.branch_list.is_none() {
+ let branch_list = cx.new(|cx| {
+ branch_picker::create_embedded(
+ self.workspace.clone(),
+ self.repository.clone(),
+ self.width,
+ window,
+ cx,
+ )
+ });
+
+ let subscription = cx.subscribe(&branch_list, |this, _, _: &DismissEvent, cx| {
+ if this.tab == GitPickerTab::Branches {
+ cx.emit(DismissEvent);
+ }
+ });
+
+ self._subscriptions.push(subscription);
+ self.branch_list = Some(branch_list);
+ }
+ self.branch_list.clone().unwrap()
+ }
+
+ fn ensure_worktree_list(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Entity<WorktreeList> {
+ if self.worktree_list.is_none() {
+ let worktree_list = cx.new(|cx| {
+ worktree_picker::create_embedded(
+ self.repository.clone(),
+ self.workspace.clone(),
+ self.width,
+ window,
+ cx,
+ )
+ });
+
+ let subscription = cx.subscribe(&worktree_list, |this, _, _: &DismissEvent, cx| {
+ if this.tab == GitPickerTab::Worktrees {
+ cx.emit(DismissEvent);
+ }
+ });
+
+ self._subscriptions.push(subscription);
+ self.worktree_list = Some(worktree_list);
+ }
+ self.worktree_list.clone().unwrap()
+ }
+
+ fn ensure_stash_list(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Entity<StashList> {
+ if self.stash_list.is_none() {
+ let stash_list = cx.new(|cx| {
+ stash_picker::create_embedded(
+ self.repository.clone(),
+ self.workspace.clone(),
+ self.width,
+ window,
+ cx,
+ )
+ });
+
+ let subscription = cx.subscribe(&stash_list, |this, _, _: &DismissEvent, cx| {
+ if this.tab == GitPickerTab::Stash {
+ cx.emit(DismissEvent);
+ }
+ });
+
+ self._subscriptions.push(subscription);
+ self.stash_list = Some(stash_list);
+ }
+ self.stash_list.clone().unwrap()
+ }
+
+ fn activate_next_tab(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ self.tab = match self.tab {
+ GitPickerTab::Branches => GitPickerTab::Worktrees,
+ GitPickerTab::Worktrees => GitPickerTab::Stash,
+ GitPickerTab::Stash => GitPickerTab::Branches,
+ };
+ self.ensure_active_picker(window, cx);
+ self.focus_active_picker(window, cx);
+ cx.notify();
+ }
+
+ fn activate_previous_tab(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ self.tab = match self.tab {
+ GitPickerTab::Branches => GitPickerTab::Stash,
+ GitPickerTab::Worktrees => GitPickerTab::Branches,
+ GitPickerTab::Stash => GitPickerTab::Worktrees,
+ };
+ self.ensure_active_picker(window, cx);
+ self.focus_active_picker(window, cx);
+ cx.notify();
+ }
+
+ fn focus_active_picker(&self, window: &mut Window, cx: &mut App) {
+ match self.tab {
+ GitPickerTab::Branches => {
+ if let Some(branch_list) = &self.branch_list {
+ branch_list.focus_handle(cx).focus(window, cx);
+ }
+ }
+ GitPickerTab::Worktrees => {
+ if let Some(worktree_list) = &self.worktree_list {
+ worktree_list.focus_handle(cx).focus(window, cx);
+ }
+ }
+ GitPickerTab::Stash => {
+ if let Some(stash_list) = &self.stash_list {
+ stash_list.focus_handle(cx).focus(window, cx);
+ }
+ }
+ }
+ }
+
+ fn render_tab_bar(&self, cx: &mut Context<Self>) -> impl IntoElement {
+ let focus_handle = self.focus_handle(cx);
+ let branches_focus_handle = focus_handle.clone();
+ let worktrees_focus_handle = focus_handle.clone();
+ let stash_focus_handle = focus_handle;
+
+ h_flex().p_2().pb_0p5().w_full().child(
+ ToggleButtonGroup::single_row(
+ "git-picker-tabs",
+ [
+ ToggleButtonSimple::new(
+ GitPickerTab::Branches.to_string(),
+ cx.listener(|this, _, window, cx| {
+ this.tab = GitPickerTab::Branches;
+ this.ensure_active_picker(window, cx);
+ this.focus_active_picker(window, cx);
+ cx.notify();
+ }),
+ )
+ .tooltip(move |_, cx| {
+ Tooltip::for_action_in(
+ "Toggle Branch Picker",
+ &ActivateBranchesTab,
+ &branches_focus_handle,
+ cx,
+ )
+ }),
+ ToggleButtonSimple::new(
+ GitPickerTab::Worktrees.to_string(),
+ cx.listener(|this, _, window, cx| {
+ this.tab = GitPickerTab::Worktrees;
+ this.ensure_active_picker(window, cx);
+ this.focus_active_picker(window, cx);
+ cx.notify();
+ }),
+ )
+ .tooltip(move |_, cx| {
+ Tooltip::for_action_in(
+ "Toggle Worktree Picker",
+ &ActivateWorktreesTab,
+ &worktrees_focus_handle,
+ cx,
+ )
+ }),
+ ToggleButtonSimple::new(
+ GitPickerTab::Stash.to_string(),
+ cx.listener(|this, _, window, cx| {
+ this.tab = GitPickerTab::Stash;
+ this.ensure_active_picker(window, cx);
+ this.focus_active_picker(window, cx);
+ cx.notify();
+ }),
+ )
+ .tooltip(move |_, cx| {
+ Tooltip::for_action_in(
+ "Toggle Stash Picker",
+ &ActivateStashTab,
+ &stash_focus_handle,
+ cx,
+ )
+ }),
+ ],
+ )
+ .label_size(LabelSize::Default)
+ .style(ToggleButtonGroupStyle::Outlined)
+ .auto_width()
+ .selected_index(match self.tab {
+ GitPickerTab::Branches => 0,
+ GitPickerTab::Worktrees => 1,
+ GitPickerTab::Stash => 2,
+ }),
+ )
+ }
+
+ fn render_active_picker(
+ &mut self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> impl IntoElement {
+ match self.tab {
+ GitPickerTab::Branches => {
+ let branch_list = self.ensure_branch_list(window, cx);
+ branch_list.into_any_element()
+ }
+ GitPickerTab::Worktrees => {
+ let worktree_list = self.ensure_worktree_list(window, cx);
+ worktree_list.into_any_element()
+ }
+ GitPickerTab::Stash => {
+ let stash_list = self.ensure_stash_list(window, cx);
+ stash_list.into_any_element()
+ }
+ }
+ }
+
+ fn handle_modifiers_changed(
+ &mut self,
+ ev: &ModifiersChangedEvent,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ match self.tab {
+ GitPickerTab::Branches => {
+ if let Some(branch_list) = &self.branch_list {
+ branch_list.update(cx, |list, cx| {
+ list.handle_modifiers_changed(ev, window, cx);
+ });
+ }
+ }
+ GitPickerTab::Worktrees => {
+ if let Some(worktree_list) = &self.worktree_list {
+ worktree_list.update(cx, |list, cx| {
+ list.handle_modifiers_changed(ev, window, cx);
+ });
+ }
+ }
+ GitPickerTab::Stash => {
+ if let Some(stash_list) = &self.stash_list {
+ stash_list.update(cx, |list, cx| {
+ list.handle_modifiers_changed(ev, window, cx);
+ });
+ }
+ }
+ }
+ }
+
+ fn handle_delete_branch(
+ &mut self,
+ _: &DeleteBranch,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(branch_list) = &self.branch_list {
+ branch_list.update(cx, |list, cx| {
+ list.handle_delete(&DeleteBranch, window, cx);
+ });
+ }
+ }
+
+ fn handle_filter_remotes(
+ &mut self,
+ _: &FilterRemotes,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(branch_list) = &self.branch_list {
+ branch_list.update(cx, |list, cx| {
+ list.handle_filter(&FilterRemotes, window, cx);
+ });
+ }
+ }
+
+ fn handle_worktree_from_default(
+ &mut self,
+ _: &WorktreeFromDefault,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(worktree_list) = &self.worktree_list {
+ worktree_list.update(cx, |list, cx| {
+ list.handle_new_worktree(false, window, cx);
+ });
+ }
+ }
+
+ fn handle_worktree_from_default_on_window(
+ &mut self,
+ _: &WorktreeFromDefaultOnWindow,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(worktree_list) = &self.worktree_list {
+ worktree_list.update(cx, |list, cx| {
+ list.handle_new_worktree(true, window, cx);
+ });
+ }
+ }
+
+ fn handle_drop_stash(
+ &mut self,
+ _: &DropStashItem,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(stash_list) = &self.stash_list {
+ stash_list.update(cx, |list, cx| {
+ list.handle_drop_stash(&DropStashItem, window, cx);
+ });
+ }
+ }
+
+ fn handle_show_stash(
+ &mut self,
+ _: &ShowStashItem,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(stash_list) = &self.stash_list {
+ stash_list.update(cx, |list, cx| {
+ list.handle_show_stash(&ShowStashItem, window, cx);
+ });
+ }
+ }
+}
+
+impl ModalView for GitPicker {}
+impl EventEmitter<DismissEvent> for GitPicker {}
+
+impl Focusable for GitPicker {
+ fn focus_handle(&self, cx: &App) -> FocusHandle {
+ match self.tab {
+ GitPickerTab::Branches => {
+ if let Some(branch_list) = &self.branch_list {
+ return branch_list.focus_handle(cx);
+ }
+ }
+ GitPickerTab::Worktrees => {
+ if let Some(worktree_list) = &self.worktree_list {
+ return worktree_list.focus_handle(cx);
+ }
+ }
+ GitPickerTab::Stash => {
+ if let Some(stash_list) = &self.stash_list {
+ return stash_list.focus_handle(cx);
+ }
+ }
+ }
+ cx.focus_handle()
+ }
+}
+
+impl Render for GitPicker {
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ v_flex()
+ .occlude()
+ .w(self.width)
+ .elevation_3(cx)
+ .overflow_hidden()
+ .key_context({
+ let mut key_context = KeyContext::new_with_defaults();
+ key_context.add("Pane");
+ key_context.add("GitPicker");
+ match self.tab {
+ GitPickerTab::Branches => key_context.add("GitBranchSelector"),
+ GitPickerTab::Worktrees => key_context.add("GitWorktreeSelector"),
+ GitPickerTab::Stash => key_context.add("StashList"),
+ }
+ key_context
+ })
+ .on_mouse_down(MouseButton::Left, |_, _, cx| {
+ cx.stop_propagation();
+ })
+ .on_action(cx.listener(|_, _: &menu::Cancel, _, cx| {
+ cx.emit(DismissEvent);
+ }))
+ .on_action(cx.listener(|this, _: &pane::ActivateNextItem, window, cx| {
+ this.activate_next_tab(window, cx);
+ }))
+ .on_action(
+ cx.listener(|this, _: &pane::ActivatePreviousItem, window, cx| {
+ this.activate_previous_tab(window, cx);
+ }),
+ )
+ .on_action(cx.listener(|this, _: &ActivateBranchesTab, window, cx| {
+ this.tab = GitPickerTab::Branches;
+ this.ensure_active_picker(window, cx);
+ this.focus_active_picker(window, cx);
+ cx.notify();
+ }))
+ .on_action(cx.listener(|this, _: &ActivateWorktreesTab, window, cx| {
+ this.tab = GitPickerTab::Worktrees;
+ this.ensure_active_picker(window, cx);
+ this.focus_active_picker(window, cx);
+ cx.notify();
+ }))
+ .on_action(cx.listener(|this, _: &ActivateStashTab, window, cx| {
+ this.tab = GitPickerTab::Stash;
+ this.ensure_active_picker(window, cx);
+ this.focus_active_picker(window, cx);
+ cx.notify();
+ }))
+ .on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
+ .when(self.tab == GitPickerTab::Branches, |el| {
+ el.on_action(cx.listener(Self::handle_delete_branch))
+ .on_action(cx.listener(Self::handle_filter_remotes))
+ })
+ .when(self.tab == GitPickerTab::Worktrees, |el| {
+ el.on_action(cx.listener(Self::handle_worktree_from_default))
+ .on_action(cx.listener(Self::handle_worktree_from_default_on_window))
+ })
+ .when(self.tab == GitPickerTab::Stash, |el| {
+ el.on_action(cx.listener(Self::handle_drop_stash))
+ .on_action(cx.listener(Self::handle_show_stash))
+ })
+ .child(self.render_tab_bar(cx))
+ .child(self.render_active_picker(window, cx))
+ }
+}
+
+pub fn open_branches(
+ workspace: &mut Workspace,
+ _: &zed_actions::git::Branch,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ open_with_tab(workspace, GitPickerTab::Branches, window, cx);
+}
+
+pub fn open_worktrees(
+ workspace: &mut Workspace,
+ _: &zed_actions::git::Worktree,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ open_with_tab(workspace, GitPickerTab::Worktrees, window, cx);
+}
+
+pub fn open_stash(
+ workspace: &mut Workspace,
+ _: &zed_actions::git::ViewStash,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ open_with_tab(workspace, GitPickerTab::Stash, window, cx);
+}
+
+fn open_with_tab(
+ workspace: &mut Workspace,
+ tab: GitPickerTab,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ let workspace_handle = workspace.weak_handle();
+ let repository = workspace.project().read(cx).active_repository(cx);
+
+ workspace.toggle_modal(window, cx, |window, cx| {
+ GitPicker::new(workspace_handle, repository, tab, rems(34.), window, cx)
+ })
+}
+
+/// Register all git picker actions with the workspace.
+pub fn register(workspace: &mut Workspace) {
+ workspace.register_action(|workspace, _: &zed_actions::git::Branch, window, cx| {
+ open_with_tab(workspace, GitPickerTab::Branches, window, cx);
+ });
+ workspace.register_action(|workspace, _: &zed_actions::git::Switch, window, cx| {
+ open_with_tab(workspace, GitPickerTab::Branches, window, cx);
+ });
+ workspace.register_action(
+ |workspace, _: &zed_actions::git::CheckoutBranch, window, cx| {
+ open_with_tab(workspace, GitPickerTab::Branches, window, cx);
+ },
+ );
+ workspace.register_action(|workspace, _: &zed_actions::git::Worktree, window, cx| {
+ open_with_tab(workspace, GitPickerTab::Worktrees, window, cx);
+ });
+ workspace.register_action(|workspace, _: &zed_actions::git::ViewStash, window, cx| {
+ open_with_tab(workspace, GitPickerTab::Stash, window, cx);
+ });
+}
@@ -41,6 +41,7 @@ pub mod file_diff_view;
pub mod file_history_view;
pub mod git_panel;
mod git_panel_settings;
+pub mod git_picker;
pub mod onboarding;
pub mod picker_prompt;
pub mod project_diff;
@@ -73,9 +74,7 @@ pub fn init(cx: &mut App) {
CommitModal::register(workspace);
git_panel::register(workspace);
repository_selector::register(workspace);
- branch_picker::register(workspace);
- worktree_picker::register(workspace);
- stash_picker::register(workspace);
+ git_picker::register(workspace);
let project = workspace.project().read(cx);
if project.is_read_only(cx) {
@@ -29,10 +29,6 @@ actions!(
]
);
-pub fn register(workspace: &mut Workspace) {
- workspace.register_action(open);
-}
-
pub fn open(
workspace: &mut Workspace,
_: &zed_actions::git::ViewStash,
@@ -46,6 +42,16 @@ pub fn open(
})
}
+pub fn create_embedded(
+ repository: Option<Entity<Repository>>,
+ workspace: WeakEntity<Workspace>,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<StashList>,
+) -> StashList {
+ StashList::new_embedded(repository, workspace, width, window, cx)
+}
+
pub struct StashList {
width: Rems,
pub picker: Entity<Picker<StashListDelegate>>,
@@ -60,6 +66,22 @@ impl StashList {
width: Rems,
window: &mut Window,
cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self::new_inner(repository, workspace, width, false, window, cx);
+ this._subscriptions
+ .push(cx.subscribe(&this.picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ }));
+ this
+ }
+
+ fn new_inner(
+ repository: Option<Entity<Repository>>,
+ workspace: WeakEntity<Workspace>,
+ width: Rems,
+ embedded: bool,
+ window: &mut Window,
+ cx: &mut Context<Self>,
) -> Self {
let mut _subscriptions = Vec::new();
let stash_request = repository
@@ -103,16 +125,12 @@ impl StashList {
.detach_and_log_err(cx);
let delegate = StashListDelegate::new(repository, workspace, window, cx);
- let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
+ let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(!embedded));
let picker_focus_handle = picker.focus_handle(cx);
picker.update(cx, |picker, _| {
picker.delegate.focus_handle = picker_focus_handle.clone();
});
- _subscriptions.push(cx.subscribe(&picker, |_, _, _, cx| {
- cx.emit(DismissEvent);
- }));
-
Self {
picker,
picker_focus_handle,
@@ -121,7 +139,22 @@ impl StashList {
}
}
- fn handle_drop_stash(
+ fn new_embedded(
+ repository: Option<Entity<Repository>>,
+ workspace: WeakEntity<Workspace>,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self::new_inner(repository, workspace, width, true, window, cx);
+ this._subscriptions
+ .push(cx.subscribe(&this.picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ }));
+ this
+ }
+
+ pub fn handle_drop_stash(
&mut self,
_: &DropStashItem,
window: &mut Window,
@@ -135,7 +168,7 @@ impl StashList {
cx.notify();
}
- fn handle_show_stash(
+ pub fn handle_show_stash(
&mut self,
_: &ShowStashItem,
window: &mut Window,
@@ -149,7 +182,7 @@ impl StashList {
cx.notify();
}
- fn handle_modifiers_changed(
+ pub fn handle_modifiers_changed(
&mut self,
ev: &ModifiersChangedEvent,
_: &mut Window,
@@ -24,10 +24,6 @@ use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr};
actions!(git, [WorktreeFromDefault, WorktreeFromDefaultOnWindow]);
-pub fn register(workspace: &mut Workspace) {
- workspace.register_action(open);
-}
-
pub fn open(
workspace: &mut Workspace,
_: &zed_actions::git::Worktree,
@@ -41,11 +37,22 @@ pub fn open(
})
}
+pub fn create_embedded(
+ repository: Option<Entity<Repository>>,
+ workspace: WeakEntity<Workspace>,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<WorktreeList>,
+) -> WorktreeList {
+ WorktreeList::new_embedded(repository, workspace, width, window, cx)
+}
+
pub struct WorktreeList {
width: Rems,
pub picker: Entity<Picker<WorktreeListDelegate>>,
picker_focus_handle: FocusHandle,
- _subscription: Subscription,
+ _subscription: Option<Subscription>,
+ embedded: bool,
}
impl WorktreeList {
@@ -55,6 +62,21 @@ impl WorktreeList {
width: Rems,
window: &mut Window,
cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self::new_inner(repository, workspace, width, false, window, cx);
+ this._subscription = Some(cx.subscribe(&this.picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ }));
+ this
+ }
+
+ fn new_inner(
+ repository: Option<Entity<Repository>>,
+ workspace: WeakEntity<Workspace>,
+ width: Rems,
+ embedded: bool,
+ window: &mut Window,
+ cx: &mut Context<Self>,
) -> Self {
let all_worktrees_request = repository
.clone()
@@ -90,25 +112,36 @@ impl WorktreeList {
.detach_and_log_err(cx);
let delegate = WorktreeListDelegate::new(workspace, repository, window, cx);
- let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
+ let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(!embedded));
let picker_focus_handle = picker.focus_handle(cx);
picker.update(cx, |picker, _| {
picker.delegate.focus_handle = picker_focus_handle.clone();
});
- let _subscription = cx.subscribe(&picker, |_, _, _, cx| {
- cx.emit(DismissEvent);
- });
-
Self {
picker,
picker_focus_handle,
width,
- _subscription,
+ _subscription: None,
+ embedded,
}
}
- fn handle_modifiers_changed(
+ fn new_embedded(
+ repository: Option<Entity<Repository>>,
+ workspace: WeakEntity<Workspace>,
+ width: Rems,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Self {
+ let mut this = Self::new_inner(repository, workspace, width, true, window, cx);
+ this._subscription = Some(cx.subscribe(&this.picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ }));
+ this
+ }
+
+ pub fn handle_modifiers_changed(
&mut self,
ev: &ModifiersChangedEvent,
_: &mut Window,
@@ -118,7 +151,7 @@ impl WorktreeList {
.update(cx, |picker, _| picker.delegate.modifiers = ev.modifiers)
}
- fn handle_new_worktree(
+ pub fn handle_new_worktree(
&mut self,
replace_current_window: bool,
window: &mut Window,
@@ -167,10 +200,12 @@ impl Render for WorktreeList {
this.handle_new_worktree(true, w, cx)
}))
.child(self.picker.clone())
- .on_mouse_down_out({
- cx.listener(move |this, _, window, cx| {
- this.picker.update(cx, |this, cx| {
- this.cancel(&Default::default(), window, cx);
+ .when(!self.embedded, |el| {
+ el.on_mouse_down_out({
+ cx.listener(move |this, _, window, cx| {
+ this.picker.update(cx, |this, cx| {
+ this.cancel(&Default::default(), window, cx);
+ })
})
})
})
@@ -4736,6 +4736,7 @@ mod tests {
"git",
"git_onboarding",
"git_panel",
+ "git_picker",
"go_to_line",
"icon_theme_selector",
"inline_assistant",