diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 73ddf48f91213352dc3cc5b79f8b3ef4a7217dca..1693f3262d1dc900a0b2746c08cd1744e1001404 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -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", + }, + }, ] diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index d4249adfb1db4d678af7e7b3ae2960cbab1772c1..cfe13134ab282cb60db9e8bcde43227ff384be6f 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -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", + }, + }, ] diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json index 672ad3741358ba3f1ecba54988f28836b7ca083b..2fbf8c51f03048dfa89bb90b282dbd32dfc52366 100644 --- a/assets/keymaps/default-windows.json +++ b/assets/keymaps/default-windows.json @@ -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", + }, + }, ] diff --git a/crates/git_ui/src/branch_picker.rs b/crates/git_ui/src/branch_picker.rs index befcf83d1f93432ef8e05aa8d15bf77c6ea7d0e0..423431b2232ccd8cdd2ffadd921456d49623de4e 100644 --- a/crates/git_ui/src/branch_picker.rs +++ b/crates/git_ui/src/branch_picker.rs @@ -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, + repository: Option>, + width: Rems, + window: &mut Window, + cx: &mut Context, +) -> 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_focus_handle: FocusHandle, - _subscription: Subscription, + _subscription: Option, + embedded: bool, } impl BranchList { @@ -124,10 +127,27 @@ impl BranchList { width: Rems, window: &mut Window, cx: &mut Context, + ) -> 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, + repository: Option>, + style: BranchListStyle, + width: Rems, + embedded: bool, + window: &mut Window, + cx: &mut Context, ) -> 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, + repository: Option>, + width: Rems, + window: &mut Window, + cx: &mut Context, + ) -> 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, } }) }) diff --git a/crates/git_ui/src/git_picker.rs b/crates/git_ui/src/git_picker.rs new file mode 100644 index 0000000000000000000000000000000000000000..44fe62f5a02249005dc6518c7ec50b940faae972 --- /dev/null +++ b/crates/git_ui/src/git_picker.rs @@ -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, + repository: Option>, + width: Rems, + branch_list: Option>, + worktree_list: Option>, + stash_list: Option>, + _subscriptions: Vec, +} + +impl GitPicker { + pub fn new( + workspace: WeakEntity, + repository: Option>, + initial_tab: GitPickerTab, + width: Rems, + window: &mut Window, + cx: &mut Context, + ) -> 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) { + 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, + ) -> Entity { + 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, + ) -> Entity { + 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, + ) -> Entity { + 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.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.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) -> 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, + ) -> 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, + ) { + 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, + ) { + 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, + ) { + 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, + ) { + 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, + ) { + 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, + ) { + 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, + ) { + 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 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) -> 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, +) { + 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, +) { + 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, +) { + open_with_tab(workspace, GitPickerTab::Stash, window, cx); +} + +fn open_with_tab( + workspace: &mut Workspace, + tab: GitPickerTab, + window: &mut Window, + cx: &mut Context, +) { + 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); + }); +} diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index 5cb99689e0c23304ec21c1b4fe935ac543299a75..0c7da7e90e10260e7ce645716ce31da98f902252 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -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) { diff --git a/crates/git_ui/src/stash_picker.rs b/crates/git_ui/src/stash_picker.rs index a688c250a63e09deab03677efda92e34a38aebc4..c0e077262eda9bfc12581f9b23108c2f74928f76 100644 --- a/crates/git_ui/src/stash_picker.rs +++ b/crates/git_ui/src/stash_picker.rs @@ -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>, + workspace: WeakEntity, + width: Rems, + window: &mut Window, + cx: &mut Context, +) -> StashList { + StashList::new_embedded(repository, workspace, width, window, cx) +} + pub struct StashList { width: Rems, pub picker: Entity>, @@ -60,6 +66,22 @@ impl StashList { width: Rems, window: &mut Window, cx: &mut Context, + ) -> 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>, + workspace: WeakEntity, + width: Rems, + embedded: bool, + window: &mut Window, + cx: &mut Context, ) -> 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>, + workspace: WeakEntity, + width: Rems, + window: &mut Window, + cx: &mut Context, + ) -> 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, diff --git a/crates/git_ui/src/worktree_picker.rs b/crates/git_ui/src/worktree_picker.rs index 3d71ff4abc254a689f72e81d388b30f2aa2b3873..a66c640305ffa0efff0c99d81a0b78b32baa3fd6 100644 --- a/crates/git_ui/src/worktree_picker.rs +++ b/crates/git_ui/src/worktree_picker.rs @@ -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>, + workspace: WeakEntity, + width: Rems, + window: &mut Window, + cx: &mut Context, +) -> WorktreeList { + WorktreeList::new_embedded(repository, workspace, width, window, cx) +} + pub struct WorktreeList { width: Rems, pub picker: Entity>, picker_focus_handle: FocusHandle, - _subscription: Subscription, + _subscription: Option, + embedded: bool, } impl WorktreeList { @@ -55,6 +62,21 @@ impl WorktreeList { width: Rems, window: &mut Window, cx: &mut Context, + ) -> 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>, + workspace: WeakEntity, + width: Rems, + embedded: bool, + window: &mut Window, + cx: &mut Context, ) -> 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>, + workspace: WeakEntity, + width: Rems, + window: &mut Window, + cx: &mut Context, + ) -> 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); + }) }) }) }) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3f789fbaaaeec49ec29b9a2c102550f92b16c027..783742d54a23649716237eefd105358aa90ffee1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4736,6 +4736,7 @@ mod tests { "git", "git_onboarding", "git_panel", + "git_picker", "go_to_line", "icon_theme_selector", "inline_assistant",