WIP dock split button and default item

This commit is contained in:
K Simmons 2022-09-07 19:44:36 -07:00
parent d87fb20170
commit b88abcacac
17 changed files with 436 additions and 194 deletions

View file

@ -298,7 +298,8 @@ async fn test_host_disconnect(
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
let (_, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx)); let (_, workspace_b) =
cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
let editor_b = workspace_b let editor_b = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "b.txt"), true, cx) workspace.open_path((worktree_id, "b.txt"), true, cx)
@ -2786,7 +2787,8 @@ async fn test_collaborating_with_code_actions(
// Join the project as client B. // Join the project as client B.
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx)); let (_window_b, workspace_b) =
cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
let editor_b = workspace_b let editor_b = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "main.rs"), true, cx) workspace.open_path((worktree_id, "main.rs"), true, cx)
@ -3001,7 +3003,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx)); let (_window_b, workspace_b) =
cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
let editor_b = workspace_b let editor_b = workspace_b
.update(cx_b, |workspace, cx| { .update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "one.rs"), true, cx) workspace.open_path((worktree_id, "one.rs"), true, cx)
@ -5224,6 +5227,7 @@ impl TestServer {
fs: fs.clone(), fs: fs.clone(),
build_window_options: Default::default, build_window_options: Default::default,
initialize_workspace: |_, _, _| unimplemented!(), initialize_workspace: |_, _, _| unimplemented!(),
default_item_factory: |_, _| unimplemented!(),
}); });
Channel::init(&client); Channel::init(&client);
@ -5459,7 +5463,9 @@ impl TestClient {
cx: &mut TestAppContext, cx: &mut TestAppContext,
) -> ViewHandle<Workspace> { ) -> ViewHandle<Workspace> {
let (_, root_view) = cx.add_window(|_| EmptyView); let (_, root_view) = cx.add_window(|_| EmptyView);
cx.add_view(&root_view, |cx| Workspace::new(project.clone(), cx)) cx.add_view(&root_view, |cx| {
Workspace::new(project.clone(), |_, _| unimplemented!(), cx)
})
} }
async fn simulate_host( async fn simulate_host(

View file

@ -350,7 +350,8 @@ mod tests {
}); });
let project = Project::test(app_state.fs.clone(), [], cx).await; let project = Project::test(app_state.fs.clone(), [], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let editor = cx.add_view(&workspace, |cx| { let editor = cx.add_view(&workspace, |cx| {
let mut editor = Editor::single_line(None, cx); let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx); editor.set_text("abc", cx);

View file

@ -1247,7 +1247,8 @@ mod tests {
.0 .0
.read_with(cx, |worktree, _| worktree.id().to_proto()); .read_with(cx, |worktree, _| worktree.id().to_proto());
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
let panel = cx.add_view(&workspace, |cx| { let panel = cx.add_view(&workspace, |cx| {
ContactsPanel::new( ContactsPanel::new(
user_store.clone(), user_store.clone(),

View file

@ -776,7 +776,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
// Create some diagnostics // Create some diagnostics
project.update(cx, |project, cx| { project.update(cx, |project, cx| {

View file

@ -7100,7 +7100,7 @@ mod tests {
fn test_navigation_history(cx: &mut gpui::MutableAppContext) { fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
cx.set_global(Settings::test(cx)); cx.set_global(Settings::test(cx));
use workspace::Item; use workspace::Item;
let (_, pane) = cx.add_window(Default::default(), Pane::new); let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(false, cx));
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
cx.add_view(&pane, |cx| { cx.add_view(&pane, |cx| {

View file

@ -364,7 +364,8 @@ impl<'a> EditorLspTestContext<'a> {
.insert_tree("/root", json!({ "dir": { file_name: "" }})) .insert_tree("/root", json!({ "dir": { file_name: "" }}))
.await; .await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
project project
.update(cx, |project, cx| { .update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx) project.find_or_create_local_worktree("/root", true, cx)

View file

@ -316,7 +316,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
cx.dispatch_action(window_id, Toggle); cx.dispatch_action(window_id, Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
@ -370,7 +371,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let (_, finder) = let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx)); cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@ -444,7 +446,8 @@ mod tests {
cx, cx,
) )
.await; .await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let (_, finder) = let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx)); cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
finder finder
@ -468,7 +471,8 @@ mod tests {
cx, cx,
) )
.await; .await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let (_, finder) = let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx)); cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@ -520,7 +524,8 @@ mod tests {
cx, cx,
) )
.await; .await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let (_, finder) = let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx)); cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@ -558,7 +563,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let (_, finder) = let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx)); cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
finder finder

View file

@ -1243,7 +1243,8 @@ mod tests {
.await; .await;
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
assert_eq!( assert_eq!(
visible_entries_as_strings(&panel, 0..50, cx), visible_entries_as_strings(&panel, 0..50, cx),
@ -1335,7 +1336,8 @@ mod tests {
.await; .await;
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
select_path(&panel, "root1", cx); select_path(&panel, "root1", cx);

View file

@ -1,15 +1,9 @@
use gpui::{ModelHandle, ViewContext}; use gpui::{ModelHandle, ViewContext};
use settings::{Settings, WorkingDirectory}; use workspace::Workspace;
use workspace::{dock::Dock, Workspace};
use crate::{ use crate::{terminal_container_view::DeployModal, Event, Terminal};
terminal_container_view::{
get_working_directory, DeployModal, TerminalContainer, TerminalContainerContent,
},
Event, Terminal,
};
pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext<Workspace>) { pub fn deploy_modal(_workspace: &mut Workspace, _: &DeployModal, _cx: &mut ViewContext<Workspace>) {
// let window = cx.window_id(); // let window = cx.window_id();
// // Pull the terminal connection out of the global if it has been stored // // Pull the terminal connection out of the global if it has been stored
@ -62,10 +56,10 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
} }
pub fn on_event( pub fn on_event(
workspace: &mut Workspace, _workspace: &mut Workspace,
_: ModelHandle<Terminal>, _: ModelHandle<Terminal>,
event: &Event, _event: &Event,
cx: &mut ViewContext<Workspace>, _cx: &mut ViewContext<Workspace>,
) { ) {
// Dismiss the modal if the terminal quit // Dismiss the modal if the terminal quit
// if let Event::CloseTerminal = event { // if let Event::CloseTerminal = event {

View file

@ -21,7 +21,9 @@ impl<'a> TerminalTestContext<'a> {
let params = self.cx.update(AppState::test); let params = self.cx.update(AppState::test);
let project = Project::test(params.fs.clone(), [], self.cx).await; let project = Project::test(params.fs.clone(), [], self.cx).await;
let (_, workspace) = self.cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) = self
.cx
.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
(project, workspace) (project, workspace)
} }

View file

@ -39,7 +39,8 @@ impl<'a> VimTestContext<'a> {
.insert_tree("/root", json!({ "dir": { "test.txt": "" } })) .insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
.await; .await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
// Setup search toolbars // Setup search toolbars
workspace.update(cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {

View file

@ -1,83 +1,210 @@
use std::sync::Arc; use gpui::{
actions,
use gpui::{elements::ChildView, Element, ElementBox, Entity, View, ViewContext, ViewHandle}; elements::{ChildView, MouseEventHandler, Svg},
impl_internal_actions, CursorStyle, Element, ElementBox, Entity, MouseButton,
MutableAppContext, View, ViewContext, ViewHandle, WeakViewHandle,
};
use serde::Deserialize;
use settings::Settings;
use theme::Theme; use theme::Theme;
use crate::{Pane, StatusItemView, Workspace}; use crate::{pane, ItemHandle, Pane, StatusItemView, Workspace};
#[derive(PartialEq, Eq, Default, Copy, Clone)] #[derive(PartialEq, Clone, Deserialize)]
pub enum DockPosition { pub struct MoveDock(pub DockAnchor);
#[derive(PartialEq, Clone)]
pub struct AddDefaultItemToDock;
actions!(workspace, [ToggleDock]);
impl_internal_actions!(workspace, [MoveDock, AddDefaultItemToDock]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(Dock::toggle);
cx.add_action(Dock::move_dock);
}
#[derive(PartialEq, Eq, Default, Copy, Clone, Deserialize)]
pub enum DockAnchor {
#[default] #[default]
Bottom, Bottom,
Right, Right,
Fullscreen, Expanded,
} }
#[derive(Copy, Clone)]
pub enum DockPosition {
Shown(DockAnchor),
Hidden(DockAnchor),
}
impl Default for DockPosition {
fn default() -> Self {
DockPosition::Hidden(Default::default())
}
}
impl DockPosition {
fn toggle(self) -> Self {
match self {
DockPosition::Shown(anchor) => DockPosition::Hidden(anchor),
DockPosition::Hidden(anchor) => DockPosition::Shown(anchor),
}
}
fn visible(&self) -> Option<DockAnchor> {
match self {
DockPosition::Shown(anchor) => Some(*anchor),
DockPosition::Hidden(_) => None,
}
}
fn hide(self) -> Self {
match self {
DockPosition::Shown(anchor) => DockPosition::Hidden(anchor),
DockPosition::Hidden(_) => self,
}
}
}
pub type DefaultItemFactory =
fn(&mut Workspace, &mut ViewContext<Workspace>) -> Box<dyn ItemHandle>;
pub struct Dock { pub struct Dock {
position: Option<DockPosition>, position: DockPosition,
pane: ViewHandle<Pane>, pane: ViewHandle<Pane>,
default_item_factory: DefaultItemFactory,
} }
impl Dock { impl Dock {
pub fn new(cx: &mut ViewContext<Workspace>) -> Self { pub fn new(cx: &mut ViewContext<Workspace>, default_item_factory: DefaultItemFactory) -> Self {
let pane = cx.add_view(Pane::new); let pane = cx.add_view(|cx| Pane::new(true, cx));
cx.subscribe(&pane.clone(), |workspace, _, event, cx| {
if let pane::Event::Remove = event {
workspace.dock.hide();
cx.notify();
}
})
.detach();
Self { Self {
pane, pane,
position: None, position: Default::default(),
default_item_factory,
} }
} }
pub fn render(&self, _theme: &Theme, position: DockPosition) -> Option<ElementBox> { pub fn pane(&self) -> ViewHandle<Pane> {
if self.position.is_some() && self.position.unwrap() == position { self.pane.clone()
Some(ChildView::new(self.pane.clone()).boxed()) }
} else {
None fn hide(&mut self) {
self.position = self.position.hide();
}
fn ensure_not_empty(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
let pane = workspace.dock.pane.clone();
if !pane.read(cx).items().next().is_none() {
let item_to_add = (workspace.dock.default_item_factory)(workspace, cx);
Pane::add_item(workspace, &pane, item_to_add, true, true, None, cx);
} }
} }
}
pub struct ToggleDock { fn toggle(workspace: &mut Workspace, _: &ToggleDock, cx: &mut ViewContext<Workspace>) {
dock: Arc<Dock>, // Shift-escape ON
} // Get or insert the dock's last focused terminal
// Open the dock in fullscreen
// Focus that terminal
impl ToggleDock { // Shift-escape OFF
pub fn new(dock: Arc<Dock>, _cx: &mut ViewContext<Self>) -> Self { // Close the dock
Self { dock } // Return focus to center
// Behaviors:
// If the dock is shown, hide it
// If the dock is hidden, show it
// If the dock was full screen, open it in last position (bottom or right)
// If the dock was bottom or right, re-open it in that context (and with the previous % width)
workspace.dock.position = workspace.dock.position.toggle();
if workspace.dock.position.visible().is_some() {
Self::ensure_not_empty(workspace, cx);
}
cx.notify();
}
fn move_dock(
workspace: &mut Workspace,
&MoveDock(new_anchor): &MoveDock,
cx: &mut ViewContext<Workspace>,
) {
// Clear the previous position if the dock is not visible.
workspace.dock.position = DockPosition::Shown(new_anchor);
Self::ensure_not_empty(workspace, cx);
cx.notify();
}
pub fn render(&self, _theme: &Theme, anchor: DockAnchor) -> Option<ElementBox> {
self.position
.visible()
.filter(|current_anchor| *current_anchor == anchor)
.map(|_| ChildView::new(self.pane.clone()).boxed())
} }
} }
impl Entity for ToggleDock { pub struct ToggleDockButton {
workspace: WeakViewHandle<Workspace>,
}
impl ToggleDockButton {
pub fn new(workspace: WeakViewHandle<Workspace>, _cx: &mut ViewContext<Self>) -> Self {
Self { workspace }
}
}
impl Entity for ToggleDockButton {
type Event = (); type Event = ();
} }
impl View for ToggleDock { impl View for ToggleDockButton {
fn ui_name() -> &'static str { fn ui_name() -> &'static str {
"Dock Toggle" "Dock Toggle"
} }
// Shift-escape ON
// Get or insert the dock's last focused terminal
// Open the dock in fullscreen
// Focus that terminal
// Shift-escape OFF fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
// Close the dock let dock_is_open = self
// Return focus to center .workspace
.upgrade(cx)
.map(|workspace| workspace.read(cx).dock.position.visible().is_some())
.unwrap_or(false);
// Behaviors: MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
// If the dock is shown, hide it let theme = &cx
// If the dock is hidden, show it .global::<Settings>()
// If the dock was full screen, open it in last position (bottom or right) .theme
// If the dock was bottom or right, re-open it in that context (and with the previous % width) .workspace
// On hover, change color and background .status_bar
// On shown, change color and background .sidebar_buttons;
// On hidden, change color and background let style = theme.item.style_for(state, dock_is_open);
// Show tool tip
fn render(&mut self, _cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { Svg::new("icons/terminal_16.svg")
todo!() .with_color(style.icon_color)
.constrained()
.with_width(style.icon_size)
.with_height(style.icon_size)
.contained()
.with_style(style.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, cx| cx.dispatch_action(ToggleDock))
// TODO: Add tooltip
.boxed()
} }
} }
impl StatusItemView for ToggleDock { impl StatusItemView for ToggleDockButton {
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
_active_pane_item: Option<&dyn crate::ItemHandle>, _active_pane_item: Option<&dyn crate::ItemHandle>,

View file

@ -1,5 +1,9 @@
use super::{ItemHandle, SplitDirection}; use super::{ItemHandle, SplitDirection};
use crate::{toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace}; use crate::{
dock::{DockAnchor, MoveDock},
toolbar::Toolbar,
Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace,
};
use anyhow::Result; use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
use context_menu::{ContextMenu, ContextMenuItem}; use context_menu::{ContextMenu, ContextMenuItem};
@ -76,13 +80,27 @@ pub struct DeploySplitMenu {
position: Vector2F, position: Vector2F,
} }
#[derive(Clone, PartialEq)]
pub struct DeployDockMenu {
position: Vector2F,
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct DeployNewMenu { pub struct DeployNewMenu {
position: Vector2F, position: Vector2F,
} }
impl_actions!(pane, [GoBack, GoForward, ActivateItem]); impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
impl_internal_actions!(pane, [CloseItem, DeploySplitMenu, DeployNewMenu, MoveItem]); impl_internal_actions!(
pane,
[
CloseItem,
DeploySplitMenu,
DeployNewMenu,
DeployDockMenu,
MoveItem
]
);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@ -141,6 +159,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)); cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
cx.add_action(Pane::deploy_split_menu); cx.add_action(Pane::deploy_split_menu);
cx.add_action(Pane::deploy_new_menu); cx.add_action(Pane::deploy_new_menu);
cx.add_action(Pane::deploy_dock_menu);
cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
Pane::reopen_closed_item(workspace, cx).detach(); Pane::reopen_closed_item(workspace, cx).detach();
}); });
@ -186,6 +205,7 @@ pub struct Pane {
nav_history: Rc<RefCell<NavHistory>>, nav_history: Rc<RefCell<NavHistory>>,
toolbar: ViewHandle<Toolbar>, toolbar: ViewHandle<Toolbar>,
context_menu: ViewHandle<ContextMenu>, context_menu: ViewHandle<ContextMenu>,
is_dock: bool,
} }
pub struct ItemNavHistory { pub struct ItemNavHistory {
@ -235,7 +255,7 @@ pub enum ReorderBehavior {
} }
impl Pane { impl Pane {
pub fn new(cx: &mut ViewContext<Self>) -> Self { pub fn new(is_dock: bool, cx: &mut ViewContext<Self>) -> Self {
let handle = cx.weak_handle(); let handle = cx.weak_handle();
let context_menu = cx.add_view(ContextMenu::new); let context_menu = cx.add_view(ContextMenu::new);
Self { Self {
@ -254,6 +274,7 @@ impl Pane {
})), })),
toolbar: cx.add_view(|_| Toolbar::new(handle)), toolbar: cx.add_view(|_| Toolbar::new(handle)),
context_menu, context_menu,
is_dock,
} }
} }
@ -976,6 +997,20 @@ impl Pane {
}); });
} }
fn deploy_dock_menu(&mut self, action: &DeployDockMenu, cx: &mut ViewContext<Self>) {
self.context_menu.update(cx, |menu, cx| {
menu.show(
action.position,
vec![
ContextMenuItem::item("Move Dock Right", MoveDock(DockAnchor::Right)),
ContextMenuItem::item("Move Dock Bottom", MoveDock(DockAnchor::Bottom)),
ContextMenuItem::item("Move Dock Maximized", MoveDock(DockAnchor::Expanded)),
],
cx,
);
});
}
fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) { fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
self.context_menu.update(cx, |menu, cx| { self.context_menu.update(cx, |menu, cx| {
menu.show( menu.show(
@ -1320,6 +1355,8 @@ impl View for Pane {
let this = cx.handle(); let this = cx.handle();
let is_dock = self.is_dock;
Stack::new() Stack::new()
.with_child( .with_child(
EventHandler::new(if let Some(active_item) = self.active_item() { EventHandler::new(if let Some(active_item) = self.active_item() {
@ -1382,10 +1419,16 @@ impl View for Pane {
}, },
) )
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)
.on_down(MouseButton::Left, |e, cx| { .on_down(MouseButton::Left, move |e, cx| {
cx.dispatch_action(DeploySplitMenu { if is_dock {
position: e.position, cx.dispatch_action(DeployDockMenu {
}); position: e.position,
});
} else {
cx.dispatch_action(DeploySplitMenu {
position: e.position,
});
}
}) })
.boxed(), .boxed(),
]) ])
@ -1570,7 +1613,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, crate::tests::default_item_factory, cx));
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
// 1. Add with a destination index // 1. Add with a destination index
@ -1658,7 +1702,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, crate::tests::default_item_factory, cx));
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
// 1. Add with a destination index // 1. Add with a destination index
@ -1734,7 +1779,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, crate::tests::default_item_factory, cx));
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
// singleton view // singleton view

View file

@ -74,82 +74,84 @@ impl WaitingRoom {
) -> Self { ) -> Self {
let project_id = contact.projects[project_index].id; let project_id = contact.projects[project_index].id;
let client = app_state.client.clone(); let client = app_state.client.clone();
let _join_task = let _join_task = cx.spawn_weak({
cx.spawn_weak({ let contact = contact.clone();
let contact = contact.clone(); |this, mut cx| async move {
|this, mut cx| async move { let project = Project::remote(
let project = Project::remote( project_id,
project_id, app_state.client.clone(),
app_state.client.clone(), app_state.user_store.clone(),
app_state.user_store.clone(), app_state.project_store.clone(),
app_state.project_store.clone(), app_state.languages.clone(),
app_state.languages.clone(), app_state.fs.clone(),
app_state.fs.clone(), cx.clone(),
cx.clone(), )
) .await;
.await;
if let Some(this) = this.upgrade(&cx) { if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.waiting = false; this.waiting = false;
match project { match project {
Ok(project) => { Ok(project) => {
cx.replace_root_view(|cx| { cx.replace_root_view(|cx| {
let mut workspace = Workspace::new(project, cx); let mut workspace =
(app_state.initialize_workspace)( Workspace::new(project, app_state.default_item_factory, cx);
&mut workspace, (app_state.initialize_workspace)(
&app_state, &mut workspace,
cx, &app_state,
); cx,
workspace.toggle_sidebar(Side::Left, cx); );
if let Some((host_peer_id, _)) = workspace.toggle_sidebar(Side::Left, cx);
workspace.project.read(cx).collaborators().iter().find( if let Some((host_peer_id, _)) = workspace
|(_, collaborator)| collaborator.replica_id == 0, .project
) .read(cx)
.collaborators()
.iter()
.find(|(_, collaborator)| collaborator.replica_id == 0)
{
if let Some(follow) = workspace
.toggle_follow(&ToggleFollow(*host_peer_id), cx)
{ {
if let Some(follow) = workspace follow.detach_and_log_err(cx);
.toggle_follow(&ToggleFollow(*host_peer_id), cx)
{
follow.detach_and_log_err(cx);
}
} }
workspace }
}); workspace
} });
Err(error) => {
let login = &contact.user.github_login;
let message = match error {
project::JoinProjectError::HostDeclined => {
format!("@{} declined your request.", login)
}
project::JoinProjectError::HostClosedProject => {
format!(
"@{} closed their copy of {}.",
login,
humanize_list(
&contact.projects[project_index]
.visible_worktree_root_names
)
)
}
project::JoinProjectError::HostWentOffline => {
format!("@{} went offline.", login)
}
project::JoinProjectError::Other(error) => {
log::error!("error joining project: {}", error);
"An error occurred.".to_string()
}
};
this.message = message;
cx.notify();
}
} }
}) Err(error) => {
} let login = &contact.user.github_login;
let message = match error {
Ok(()) project::JoinProjectError::HostDeclined => {
format!("@{} declined your request.", login)
}
project::JoinProjectError::HostClosedProject => {
format!(
"@{} closed their copy of {}.",
login,
humanize_list(
&contact.projects[project_index]
.visible_worktree_root_names
)
)
}
project::JoinProjectError::HostWentOffline => {
format!("@{} went offline.", login)
}
project::JoinProjectError::Other(error) => {
log::error!("error joining project: {}", error);
"An error occurred.".to_string()
}
};
this.message = message;
cx.notify();
}
}
})
} }
});
Ok(())
}
});
Self { Self {
project_id, project_id,

View file

@ -1,9 +1,9 @@
pub mod dock;
/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in /// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in
/// which the workspace uses to change the activated pane. /// which the workspace uses to change the activated pane.
/// ///
/// This may cause issues when you're trying to write tests that use workspace focus to add items at /// This may cause issues when you're trying to write tests that use workspace focus to add items at
/// specific locations. /// specific locations.
pub mod dock;
pub mod pane; pub mod pane;
pub mod pane_group; pub mod pane_group;
pub mod searchable; pub mod searchable;
@ -18,7 +18,7 @@ use client::{
}; };
use clock::ReplicaId; use clock::ReplicaId;
use collections::{hash_map, HashMap, HashSet}; use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockPosition, ToggleDock}; use dock::{DefaultItemFactory, Dock, DockAnchor, ToggleDockButton};
use drag_and_drop::DragAndDrop; use drag_and_drop::DragAndDrop;
use futures::{channel::oneshot, FutureExt}; use futures::{channel::oneshot, FutureExt};
use gpui::{ use gpui::{
@ -147,6 +147,7 @@ impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]);
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) { pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
pane::init(cx); pane::init(cx);
dock::init(cx);
cx.add_global_action(open); cx.add_global_action(open);
cx.add_global_action({ cx.add_global_action({
@ -262,6 +263,7 @@ pub struct AppState {
pub fs: Arc<dyn fs::Fs>, pub fs: Arc<dyn fs::Fs>,
pub build_window_options: fn() -> WindowOptions<'static>, pub build_window_options: fn() -> WindowOptions<'static>,
pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>), pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>),
pub default_item_factory: DefaultItemFactory,
} }
#[derive(Eq, PartialEq, Hash)] #[derive(Eq, PartialEq, Hash)]
@ -867,6 +869,7 @@ impl AppState {
project_store, project_store,
initialize_workspace: |_, _, _| {}, initialize_workspace: |_, _, _| {},
build_window_options: Default::default, build_window_options: Default::default,
default_item_factory: |_, _| unimplemented!(),
}) })
} }
} }
@ -920,7 +923,11 @@ enum FollowerItem {
} }
impl Workspace { impl Workspace {
pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self { pub fn new(
project: ModelHandle<Project>,
dock_default_factory: DefaultItemFactory,
cx: &mut ViewContext<Self>,
) -> Self {
cx.observe_fullscreen(|_, _, cx| cx.notify()).detach(); cx.observe_fullscreen(|_, _, cx| cx.notify()).detach();
cx.observe_window_activation(Self::on_window_activation_changed) cx.observe_window_activation(Self::on_window_activation_changed)
@ -947,14 +954,14 @@ impl Workspace {
}) })
.detach(); .detach();
let pane = cx.add_view(Pane::new); let center_pane = cx.add_view(|cx| Pane::new(false, cx));
let pane_id = pane.id(); let pane_id = center_pane.id();
cx.subscribe(&pane, move |this, _, event, cx| { cx.subscribe(&center_pane, move |this, _, event, cx| {
this.handle_pane_event(pane_id, event, cx) this.handle_pane_event(pane_id, event, cx)
}) })
.detach(); .detach();
cx.focus(&pane); cx.focus(&center_pane);
cx.emit(Event::PaneAdded(pane.clone())); cx.emit(Event::PaneAdded(center_pane.clone()));
let fs = project.read(cx).fs().clone(); let fs = project.read(cx).fs().clone();
let user_store = project.read(cx).user_store(); let user_store = project.read(cx).user_store();
@ -977,19 +984,18 @@ impl Workspace {
}); });
let weak_self = cx.weak_handle(); let weak_self = cx.weak_handle();
cx.emit_global(WorkspaceCreated(weak_self.clone())); cx.emit_global(WorkspaceCreated(weak_self.clone()));
let dock = Dock::new(cx); let dock = Dock::new(cx, dock_default_factory);
let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left)); let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left));
let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right)); let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right));
let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx)); let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx));
let toggle_dock = cx.add_view(|cx| ToggleDock::new(Arc::new(dock), cx)); let toggle_dock = cx.add_view(|cx| ToggleDockButton::new(weak_self.clone(), cx));
let right_sidebar_buttons = let right_sidebar_buttons =
cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx)); cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx));
let status_bar = cx.add_view(|cx| { let status_bar = cx.add_view(|cx| {
let mut status_bar = StatusBar::new(&pane.clone(), cx); let mut status_bar = StatusBar::new(&center_pane.clone(), cx);
status_bar.add_left_item(left_sidebar_buttons, cx); status_bar.add_left_item(left_sidebar_buttons, cx);
status_bar.add_right_item(right_sidebar_buttons, cx); status_bar.add_right_item(right_sidebar_buttons, cx);
status_bar.add_right_item(toggle_dock, cx); status_bar.add_right_item(toggle_dock, cx);
@ -1003,11 +1009,11 @@ impl Workspace {
let mut this = Workspace { let mut this = Workspace {
modal: None, modal: None,
weak_self, weak_self,
center: PaneGroup::new(pane.clone()), center: PaneGroup::new(center_pane.clone()),
dock, dock,
panes: vec![pane.clone()], panes: vec![center_pane.clone()],
panes_by_item: Default::default(), panes_by_item: Default::default(),
active_pane: pane.clone(), active_pane: center_pane.clone(),
status_bar, status_bar,
notifications: Default::default(), notifications: Default::default(),
client, client,
@ -1081,6 +1087,7 @@ impl Workspace {
app_state.fs.clone(), app_state.fs.clone(),
cx, cx,
), ),
app_state.default_item_factory,
cx, cx,
); );
(app_state.initialize_workspace)(&mut workspace, &app_state, cx); (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
@ -1532,7 +1539,7 @@ impl Workspace {
} }
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> { fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
let pane = cx.add_view(Pane::new); let pane = cx.add_view(|cx| Pane::new(false, cx));
let pane_id = pane.id(); let pane_id = pane.id();
cx.subscribe(&pane, move |this, _, event, cx| { cx.subscribe(&pane, move |this, _, event, cx| {
this.handle_pane_event(pane_id, event, cx) this.handle_pane_event(pane_id, event, cx)
@ -1549,6 +1556,10 @@ impl Workspace {
Pane::add_item(self, &active_pane, item, true, true, None, cx); Pane::add_item(self, &active_pane, item, true, true, None, cx);
} }
pub fn add_item_to_dock(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
Pane::add_item(self, &self.dock.pane(), item, true, true, None, cx);
}
pub fn open_path( pub fn open_path(
&mut self, &mut self,
path: impl Into<ProjectPath>, path: impl Into<ProjectPath>,
@ -2573,7 +2584,7 @@ impl View for Workspace {
) )
.with_children( .with_children(
self.dock self.dock
.render(&theme, DockPosition::Bottom) .render(&theme, DockAnchor::Bottom)
.map(|dock| { .map(|dock| {
FlexItem::new(dock) FlexItem::new(dock)
.flex(1., true) .flex(1., true)
@ -2587,7 +2598,7 @@ impl View for Workspace {
) )
.with_children( .with_children(
self.dock self.dock
.render(&theme, DockPosition::Right) .render(&theme, DockAnchor::Right)
.map(|dock| FlexItem::new(dock).flex(1., true).boxed()), .map(|dock| FlexItem::new(dock).flex(1., true).boxed()),
) )
.with_children( .with_children(
@ -2603,7 +2614,7 @@ impl View for Workspace {
) )
.boxed() .boxed()
}) })
.with_children(self.dock.render(&theme, DockPosition::Fullscreen)) .with_children(self.dock.render(&theme, DockAnchor::Expanded))
.with_children(self.modal.as_ref().map(|m| { .with_children(self.modal.as_ref().map(|m| {
ChildView::new(m) ChildView::new(m)
.contained() .contained()
@ -2811,7 +2822,7 @@ pub fn open_paths(
cx, cx,
); );
new_project = Some(project.clone()); new_project = Some(project.clone());
let mut workspace = Workspace::new(project, cx); let mut workspace = Workspace::new(project, app_state.default_item_factory, cx);
(app_state.initialize_workspace)(&mut workspace, &app_state, cx); (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
if contains_directory { if contains_directory {
workspace.toggle_sidebar(Side::Left, cx); workspace.toggle_sidebar(Side::Left, cx);
@ -2872,6 +2883,7 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
app_state.fs.clone(), app_state.fs.clone(),
cx, cx,
), ),
app_state.default_item_factory,
cx, cx,
); );
(app_state.initialize_workspace)(&mut workspace, app_state, cx); (app_state.initialize_workspace)(&mut workspace, app_state, cx);
@ -2889,6 +2901,13 @@ mod tests {
use project::{FakeFs, Project, ProjectEntryId}; use project::{FakeFs, Project, ProjectEntryId};
use serde_json::json; use serde_json::json;
pub fn default_item_factory(
_workspace: &mut Workspace,
_cx: &mut ViewContext<Workspace>,
) -> Box<dyn ItemHandle> {
unimplemented!();
}
#[gpui::test] #[gpui::test]
async fn test_tab_disambiguation(cx: &mut TestAppContext) { async fn test_tab_disambiguation(cx: &mut TestAppContext) {
cx.foreground().forbid_parking(); cx.foreground().forbid_parking();
@ -2896,7 +2915,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
// Adding an item with no ambiguity renders the tab without detail. // Adding an item with no ambiguity renders the tab without detail.
let item1 = cx.add_view(&workspace, |_| { let item1 = cx.add_view(&workspace, |_| {
@ -2960,7 +2980,8 @@ mod tests {
.await; .await;
let project = Project::test(fs, ["root1".as_ref()], cx).await; let project = Project::test(fs, ["root1".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
let worktree_id = project.read_with(cx, |project, cx| { let worktree_id = project.read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id() project.worktrees(cx).next().unwrap().read(cx).id()
}); });
@ -3056,7 +3077,8 @@ mod tests {
fs.insert_tree("/root", json!({ "one": "" })).await; fs.insert_tree("/root", json!({ "one": "" })).await;
let project = Project::test(fs, ["root".as_ref()], cx).await; let project = Project::test(fs, ["root".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
// When there are no dirty items, there's nothing to do. // When there are no dirty items, there's nothing to do.
let item1 = cx.add_view(&workspace, |_| TestItem::new()); let item1 = cx.add_view(&workspace, |_| TestItem::new());
@ -3096,7 +3118,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await; let project = Project::test(fs, None, cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
let item1 = cx.add_view(&workspace, |_| { let item1 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new(); let mut item = TestItem::new();
@ -3191,7 +3214,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
// Create several workspace items with single project entries, and two // Create several workspace items with single project entries, and two
// workspace items with multiple project entries. // workspace items with multiple project entries.
@ -3292,7 +3316,8 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
let item = cx.add_view(&workspace, |_| { let item = cx.add_view(&workspace, |_| {
let mut item = TestItem::new(); let mut item = TestItem::new();
@ -3409,7 +3434,7 @@ mod tests {
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
let item = cx.add_view(&workspace, |_| { let item = cx.add_view(&workspace, |_| {
let mut item = TestItem::new(); let mut item = TestItem::new();

View file

@ -19,20 +19,21 @@ use futures::{
channel::{mpsc, oneshot}, channel::{mpsc, oneshot},
FutureExt, SinkExt, StreamExt, FutureExt, SinkExt, StreamExt,
}; };
use gpui::{executor::Background, App, AssetSource, AsyncAppContext, Task}; use gpui::{executor::Background, App, AssetSource, AsyncAppContext, Task, ViewContext};
use isahc::{config::Configurable, AsyncBody, Request}; use isahc::{config::Configurable, AsyncBody, Request};
use language::LanguageRegistry; use language::LanguageRegistry;
use log::LevelFilter; use log::LevelFilter;
use parking_lot::Mutex; use parking_lot::Mutex;
use project::{Fs, ProjectStore}; use project::{Fs, ProjectStore};
use serde_json::json; use serde_json::json;
use settings::{self, KeymapFileContent, Settings, SettingsFileContent}; use settings::{self, KeymapFileContent, Settings, SettingsFileContent, WorkingDirectory};
use smol::process::Command; use smol::process::Command;
use std::{env, ffi::OsStr, fs, panic, path::PathBuf, sync::Arc, thread, time::Duration}; use std::{env, ffi::OsStr, fs, panic, path::PathBuf, sync::Arc, thread, time::Duration};
use terminal::terminal_container_view::{get_working_directory, TerminalContainer};
use theme::ThemeRegistry; use theme::ThemeRegistry;
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
use workspace::{self, AppState, NewFile, OpenPaths}; use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace};
use zed::{ use zed::{
self, build_window_options, self, build_window_options,
fs::RealFs, fs::RealFs,
@ -148,6 +149,7 @@ fn main() {
fs, fs,
build_window_options, build_window_options,
initialize_workspace, initialize_workspace,
default_item_factory,
}); });
auto_update::init(db, http, client::ZED_SERVER_URL.clone(), cx); auto_update::init(db, http, client::ZED_SERVER_URL.clone(), cx);
workspace::init(app_state.clone(), cx); workspace::init(app_state.clone(), cx);
@ -591,3 +593,20 @@ async fn handle_cli_connection(
} }
} }
} }
pub fn default_item_factory(
workspace: &mut Workspace,
cx: &mut ViewContext<Workspace>,
) -> Box<dyn ItemHandle> {
let strategy = cx
.global::<Settings>()
.terminal_overrides
.working_directory
.clone()
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
let working_directory = get_working_directory(workspace, cx, strategy);
let terminal_handle = cx.add_view(|cx| TerminalContainer::new(working_directory, false, cx));
Box::new(terminal_handle)
}

View file

@ -723,7 +723,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone(); let file1 = entries[0].clone();
@ -842,7 +843,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
// Open a file within an existing worktree. // Open a file within an existing worktree.
cx.update(|cx| { cx.update(|cx| {
@ -1001,7 +1003,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
// Open a file within an existing worktree. // Open a file within an existing worktree.
cx.update(|cx| { cx.update(|cx| {
@ -1043,7 +1046,8 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(rust_lang())); project.update(cx, |project, _| project.languages().add(rust_lang()));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
// Create a new untitled buffer // Create a new untitled buffer
@ -1132,7 +1136,8 @@ mod tests {
let project = Project::test(app_state.fs.clone(), [], cx).await; let project = Project::test(app_state.fs.clone(), [], cx).await;
project.update(cx, |project, _| project.languages().add(rust_lang())); project.update(cx, |project, _| project.languages().add(rust_lang()));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
// Create a new untitled buffer // Create a new untitled buffer
cx.dispatch_action(window_id, NewFile); cx.dispatch_action(window_id, NewFile);
@ -1185,7 +1190,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone(); let file1 = entries[0].clone();
@ -1258,7 +1264,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone(); let file1 = entries[0].clone();
@ -1522,7 +1529,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); let (_, workspace) =
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));