diff --git a/crates/terminal/src/modal.rs b/crates/terminal/src/modal.rs index 63cc4316cf..2180883ad4 100644 --- a/crates/terminal/src/modal.rs +++ b/crates/terminal/src/modal.rs @@ -1,6 +1,6 @@ use gpui::{ModelHandle, ViewContext}; use settings::{Settings, WorkingDirectory}; -use workspace::{programs::Dock, Workspace}; +use workspace::{dock::Dock, Workspace}; use crate::{ terminal_container_view::{ @@ -10,55 +10,55 @@ use crate::{ }; pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext) { - let window = cx.window_id(); + // let window = cx.window_id(); - // Pull the terminal connection out of the global if it has been stored - let possible_terminal = Dock::remove::(window, cx); + // // Pull the terminal connection out of the global if it has been stored + // let possible_terminal = Dock::remove::(window, cx); - if let Some(terminal_handle) = possible_terminal { - workspace.toggle_modal(cx, |_, cx| { - // Create a view from the stored connection if the terminal modal is not already shown - cx.add_view(|cx| TerminalContainer::from_terminal(terminal_handle.clone(), true, cx)) - }); - // Toggle Modal will dismiss the terminal modal if it is currently shown, so we must - // store the terminal back in the global - Dock::insert_or_replace::(window, terminal_handle, cx); - } else { - // No connection was stored, create a new terminal - if let Some(closed_terminal_handle) = workspace.toggle_modal(cx, |workspace, cx| { - // No terminal modal visible, construct a new one. - let wd_strategy = cx - .global::() - .terminal_overrides - .working_directory - .clone() - .unwrap_or(WorkingDirectory::CurrentProjectDirectory); + // if let Some(terminal_handle) = possible_terminal { + // workspace.toggle_modal(cx, |_, cx| { + // // Create a view from the stored connection if the terminal modal is not already shown + // cx.add_view(|cx| TerminalContainer::from_terminal(terminal_handle.clone(), true, cx)) + // }); + // // Toggle Modal will dismiss the terminal modal if it is currently shown, so we must + // // store the terminal back in the global + // Dock::insert_or_replace::(window, terminal_handle, cx); + // } else { + // // No connection was stored, create a new terminal + // if let Some(closed_terminal_handle) = workspace.toggle_modal(cx, |workspace, cx| { + // // No terminal modal visible, construct a new one. + // let wd_strategy = cx + // .global::() + // .terminal_overrides + // .working_directory + // .clone() + // .unwrap_or(WorkingDirectory::CurrentProjectDirectory); - let working_directory = get_working_directory(workspace, cx, wd_strategy); + // let working_directory = get_working_directory(workspace, cx, wd_strategy); - let this = cx.add_view(|cx| TerminalContainer::new(working_directory, true, cx)); + // let this = cx.add_view(|cx| TerminalContainer::new(working_directory, true, cx)); - if let TerminalContainerContent::Connected(connected) = &this.read(cx).content { - let terminal_handle = connected.read(cx).handle(); - cx.subscribe(&terminal_handle, on_event).detach(); - // Set the global immediately if terminal construction was successful, - // in case the user opens the command palette - Dock::insert_or_replace::(window, terminal_handle, cx); - } + // if let TerminalContainerContent::Connected(connected) = &this.read(cx).content { + // let terminal_handle = connected.read(cx).handle(); + // cx.subscribe(&terminal_handle, on_event).detach(); + // // Set the global immediately if terminal construction was successful, + // // in case the user opens the command palette + // Dock::insert_or_replace::(window, terminal_handle, cx); + // } - this - }) { - // Terminal modal was dismissed and the terminal view is connected, store the terminal - if let TerminalContainerContent::Connected(connected) = - &closed_terminal_handle.read(cx).content - { - let terminal_handle = connected.read(cx).handle(); - // Set the global immediately if terminal construction was successful, - // in case the user opens the command palette - Dock::insert_or_replace::(window, terminal_handle, cx); - } - } - } + // this + // }) { + // // Terminal modal was dismissed and the terminal view is connected, store the terminal + // if let TerminalContainerContent::Connected(connected) = + // &closed_terminal_handle.read(cx).content + // { + // let terminal_handle = connected.read(cx).handle(); + // // Set the global immediately if terminal construction was successful, + // // in case the user opens the command palette + // Dock::insert_or_replace::(window, terminal_handle, cx); + // } + // } + // } } pub fn on_event( @@ -68,11 +68,11 @@ pub fn on_event( cx: &mut ViewContext, ) { // Dismiss the modal if the terminal quit - if let Event::CloseTerminal = event { - Dock::remove::(cx.window_id(), cx); + // if let Event::CloseTerminal = event { + // Dock::remove::(cx.window_id(), cx); - if workspace.modal::().is_some() { - workspace.dismiss_modal(cx) - } - } + // if workspace.modal::().is_some() { + // workspace.dismiss_modal(cx) + // } + // } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs new file mode 100644 index 0000000000..9fe55a99ec --- /dev/null +++ b/crates/workspace/src/dock.rs @@ -0,0 +1,35 @@ +use gpui::{elements::ChildView, Element, ElementBox, ViewContext, ViewHandle}; +use theme::Theme; + +use crate::{Pane, Workspace}; + +#[derive(PartialEq, Eq)] +pub enum DockPosition { + Bottom, + Right, + Fullscreen, + Hidden, +} + +pub struct Dock { + position: DockPosition, + pane: ViewHandle, +} + +impl Dock { + pub fn new(cx: &mut ViewContext) -> Self { + let pane = cx.add_view(Pane::new); + Self { + pane, + position: DockPosition::Bottom, + } + } + + pub fn render(&self, _theme: &Theme, position: DockPosition) -> Option { + if position == self.position { + Some(ChildView::new(self.pane.clone()).boxed()) + } else { + None + } + } +} diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ceb2385ab8..671f6a9f92 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1412,6 +1412,10 @@ impl View for Pane { .on_down(MouseButton::Left, |_, cx| { cx.focus_parent_view(); }) + .on_up(MouseButton::Left, { + let pane = this.clone(); + move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, 0, cx) + }) .boxed() }) .on_navigate_mouse_down(move |direction, cx| { diff --git a/crates/workspace/src/programs.rs b/crates/workspace/src/programs.rs deleted file mode 100644 index 015d37f00e..0000000000 --- a/crates/workspace/src/programs.rs +++ /dev/null @@ -1,75 +0,0 @@ -// TODO: Need to put this basic structure in workspace, and make 'program handles' -// based off of the 'searchable item' pattern except with models. This way, the workspace's clients -// can register their models as programs with a specific identity and capable of notifying the workspace -// Programs are: -// - Kept alive by the program manager, they need to emit an event to get dropped from it -// - Can be interacted with directly, (closed, activated, etc.) by the program manager, bypassing -// associated view(s) -// - Have special rendering methods that the program manager requires them to implement to fill out -// the status bar -// - Can emit events for the program manager which: -// - Add a jewel (notification, change, etc.) -// - Drop the program -// - ??? -// - Program Manager is kept in a global, listens for window drop so it can drop all it's program handles - -use collections::HashMap; -use gpui::{AnyModelHandle, Entity, ModelHandle, View, ViewContext}; - -/// This struct is going to be the starting point for the 'program manager' feature that will -/// eventually be implemented to provide a collaborative way of engaging with identity-having -/// features like the terminal. -pub struct Dock { - // TODO: Make this a hashset or something - modals: HashMap, -} - -impl Dock { - pub fn insert_or_replace( - window: usize, - program: ModelHandle, - cx: &mut ViewContext, - ) -> Option { - cx.update_global::(|pm, _| pm.insert_or_replace_internal::(window, program)) - } - - pub fn remove( - window: usize, - cx: &mut ViewContext, - ) -> Option> { - cx.update_global::(|pm, _| pm.remove_internal::(window)) - } - - pub fn new() -> Self { - Self { - modals: Default::default(), - } - } - - /// Inserts or replaces the model at the given location. - fn insert_or_replace_internal( - &mut self, - window: usize, - program: ModelHandle, - ) -> Option { - self.modals.insert(window, AnyModelHandle::from(program)) - } - - /// Remove the program associated with this window, if it's of the given type - fn remove_internal(&mut self, window: usize) -> Option> { - let program = self.modals.remove(&window); - if let Some(program) = program { - if program.is::() { - // Guaranteed to be some, but leave it in the option - // anyway for the API - program.downcast() - } else { - // Model is of the incorrect type, put it back - self.modals.insert(window, program); - None - } - } else { - None - } - } -} diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5e010a1d25..5e782a50e8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1,3 +1,4 @@ +pub mod dock; /// 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. /// @@ -5,7 +6,6 @@ /// specific locations. pub mod pane; pub mod pane_group; -pub mod programs; pub mod searchable; pub mod sidebar; mod status_bar; @@ -18,6 +18,7 @@ use client::{ }; use clock::ReplicaId; use collections::{hash_map, HashMap, HashSet}; +use dock::{Dock, DockPosition}; use drag_and_drop::DragAndDrop; use futures::{channel::oneshot, FutureExt}; use gpui::{ @@ -37,7 +38,6 @@ use log::error; pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; -use programs::Dock; use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use searchable::SearchableItemHandle; use serde::Deserialize; @@ -146,9 +146,6 @@ impl_internal_actions!( impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]); pub fn init(app_state: Arc, cx: &mut MutableAppContext) { - // Initialize the program manager immediately - cx.set_global(Dock::new()); - pane::init(cx); cx.add_global_action(open); @@ -893,6 +890,7 @@ pub struct Workspace { panes_by_item: HashMap>, active_pane: ViewHandle, status_bar: ViewHandle, + dock: Dock, notifications: Vec<(TypeId, usize, Box)>, project: ModelHandle, leader_state: LeaderState, @@ -998,10 +996,13 @@ impl Workspace { drag_and_drop.register_container(weak_self.clone()); }); + let dock = Dock::new(cx); + let mut this = Workspace { modal: None, weak_self, center: PaneGroup::new(pane.clone()), + dock, panes: vec![pane.clone()], panes_by_item: Default::default(), active_pane: pane.clone(), @@ -2557,14 +2558,36 @@ impl View for Workspace { }, ) .with_child( - FlexItem::new(self.center.render( - &theme, - &self.follower_states_by_leader, - self.project.read(cx).collaborators(), - )) + FlexItem::new( + Flex::column() + .with_child( + FlexItem::new(self.center.render( + &theme, + &self.follower_states_by_leader, + self.project.read(cx).collaborators(), + )) + .flex(1., true) + .boxed(), + ) + .with_children( + self.dock + .render(&theme, DockPosition::Bottom) + .map(|dock| { + FlexItem::new(dock) + .flex(1., true) + .boxed() + }), + ) + .boxed(), + ) .flex(1., true) .boxed(), ) + .with_children( + self.dock + .render(&theme, DockPosition::Right) + .map(|dock| FlexItem::new(dock).flex(1., true).boxed()), + ) .with_children( if self.right_sidebar.read(cx).active_item().is_some() { Some( @@ -2578,6 +2601,7 @@ impl View for Workspace { ) .boxed() }) + .with_children(self.dock.render(&theme, DockPosition::Fullscreen)) .with_children(self.modal.as_ref().map(|m| { ChildView::new(m) .contained()