Initial explorations into docks
This commit is contained in:
parent
39d219c898
commit
b9a6336995
5 changed files with 123 additions and 135 deletions
|
@ -1,6 +1,6 @@
|
||||||
use gpui::{ModelHandle, ViewContext};
|
use gpui::{ModelHandle, ViewContext};
|
||||||
use settings::{Settings, WorkingDirectory};
|
use settings::{Settings, WorkingDirectory};
|
||||||
use workspace::{programs::Dock, Workspace};
|
use workspace::{dock::Dock, Workspace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
terminal_container_view::{
|
terminal_container_view::{
|
||||||
|
@ -10,55 +10,55 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
let possible_terminal = Dock::remove::<Terminal, _>(window, cx);
|
// let possible_terminal = Dock::remove::<Terminal, _>(window, cx);
|
||||||
|
|
||||||
if let Some(terminal_handle) = possible_terminal {
|
// if let Some(terminal_handle) = possible_terminal {
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
// workspace.toggle_modal(cx, |_, cx| {
|
||||||
// Create a view from the stored connection if the terminal modal is not already shown
|
// // 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))
|
// 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
|
// // Toggle Modal will dismiss the terminal modal if it is currently shown, so we must
|
||||||
// store the terminal back in the global
|
// // store the terminal back in the global
|
||||||
Dock::insert_or_replace::<Terminal, _>(window, terminal_handle, cx);
|
// Dock::insert_or_replace::<Terminal, _>(window, terminal_handle, cx);
|
||||||
} else {
|
// } else {
|
||||||
// No connection was stored, create a new terminal
|
// // No connection was stored, create a new terminal
|
||||||
if let Some(closed_terminal_handle) = workspace.toggle_modal(cx, |workspace, cx| {
|
// if let Some(closed_terminal_handle) = workspace.toggle_modal(cx, |workspace, cx| {
|
||||||
// No terminal modal visible, construct a new one.
|
// // No terminal modal visible, construct a new one.
|
||||||
let wd_strategy = cx
|
// let wd_strategy = cx
|
||||||
.global::<Settings>()
|
// .global::<Settings>()
|
||||||
.terminal_overrides
|
// .terminal_overrides
|
||||||
.working_directory
|
// .working_directory
|
||||||
.clone()
|
// .clone()
|
||||||
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
|
// .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 {
|
// if let TerminalContainerContent::Connected(connected) = &this.read(cx).content {
|
||||||
let terminal_handle = connected.read(cx).handle();
|
// let terminal_handle = connected.read(cx).handle();
|
||||||
cx.subscribe(&terminal_handle, on_event).detach();
|
// cx.subscribe(&terminal_handle, on_event).detach();
|
||||||
// Set the global immediately if terminal construction was successful,
|
// // Set the global immediately if terminal construction was successful,
|
||||||
// in case the user opens the command palette
|
// // in case the user opens the command palette
|
||||||
Dock::insert_or_replace::<Terminal, _>(window, terminal_handle, cx);
|
// Dock::insert_or_replace::<Terminal, _>(window, terminal_handle, cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
this
|
// this
|
||||||
}) {
|
// }) {
|
||||||
// Terminal modal was dismissed and the terminal view is connected, store the terminal
|
// // Terminal modal was dismissed and the terminal view is connected, store the terminal
|
||||||
if let TerminalContainerContent::Connected(connected) =
|
// if let TerminalContainerContent::Connected(connected) =
|
||||||
&closed_terminal_handle.read(cx).content
|
// &closed_terminal_handle.read(cx).content
|
||||||
{
|
// {
|
||||||
let terminal_handle = connected.read(cx).handle();
|
// let terminal_handle = connected.read(cx).handle();
|
||||||
// Set the global immediately if terminal construction was successful,
|
// // Set the global immediately if terminal construction was successful,
|
||||||
// in case the user opens the command palette
|
// // in case the user opens the command palette
|
||||||
Dock::insert_or_replace::<Terminal, _>(window, terminal_handle, cx);
|
// Dock::insert_or_replace::<Terminal, _>(window, terminal_handle, cx);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_event(
|
pub fn on_event(
|
||||||
|
@ -68,11 +68,11 @@ pub fn on_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 {
|
||||||
Dock::remove::<Terminal, _>(cx.window_id(), cx);
|
// Dock::remove::<Terminal, _>(cx.window_id(), cx);
|
||||||
|
|
||||||
if workspace.modal::<TerminalContainer>().is_some() {
|
// if workspace.modal::<TerminalContainer>().is_some() {
|
||||||
workspace.dismiss_modal(cx)
|
// workspace.dismiss_modal(cx)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
35
crates/workspace/src/dock.rs
Normal file
35
crates/workspace/src/dock.rs
Normal file
|
@ -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<Pane>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dock {
|
||||||
|
pub fn new(cx: &mut ViewContext<Workspace>) -> Self {
|
||||||
|
let pane = cx.add_view(Pane::new);
|
||||||
|
Self {
|
||||||
|
pane,
|
||||||
|
position: DockPosition::Bottom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, _theme: &Theme, position: DockPosition) -> Option<ElementBox> {
|
||||||
|
if position == self.position {
|
||||||
|
Some(ChildView::new(self.pane.clone()).boxed())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1412,6 +1412,10 @@ impl View for Pane {
|
||||||
.on_down(MouseButton::Left, |_, cx| {
|
.on_down(MouseButton::Left, |_, cx| {
|
||||||
cx.focus_parent_view();
|
cx.focus_parent_view();
|
||||||
})
|
})
|
||||||
|
.on_up(MouseButton::Left, {
|
||||||
|
let pane = this.clone();
|
||||||
|
move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, 0, cx)
|
||||||
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
})
|
||||||
.on_navigate_mouse_down(move |direction, cx| {
|
.on_navigate_mouse_down(move |direction, cx| {
|
||||||
|
|
|
@ -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<usize, AnyModelHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dock {
|
|
||||||
pub fn insert_or_replace<T: Entity, V: View>(
|
|
||||||
window: usize,
|
|
||||||
program: ModelHandle<T>,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) -> Option<AnyModelHandle> {
|
|
||||||
cx.update_global::<Dock, _, _>(|pm, _| pm.insert_or_replace_internal::<T>(window, program))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: Entity, V: View>(
|
|
||||||
window: usize,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) -> Option<ModelHandle<T>> {
|
|
||||||
cx.update_global::<Dock, _, _>(|pm, _| pm.remove_internal::<T>(window))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
modals: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts or replaces the model at the given location.
|
|
||||||
fn insert_or_replace_internal<T: Entity>(
|
|
||||||
&mut self,
|
|
||||||
window: usize,
|
|
||||||
program: ModelHandle<T>,
|
|
||||||
) -> Option<AnyModelHandle> {
|
|
||||||
self.modals.insert(window, AnyModelHandle::from(program))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove the program associated with this window, if it's of the given type
|
|
||||||
fn remove_internal<T: Entity>(&mut self, window: usize) -> Option<ModelHandle<T>> {
|
|
||||||
let program = self.modals.remove(&window);
|
|
||||||
if let Some(program) = program {
|
|
||||||
if program.is::<T>() {
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
/// 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.
|
||||||
///
|
///
|
||||||
|
@ -5,7 +6,6 @@
|
||||||
/// specific locations.
|
/// specific locations.
|
||||||
pub mod pane;
|
pub mod pane;
|
||||||
pub mod pane_group;
|
pub mod pane_group;
|
||||||
pub mod programs;
|
|
||||||
pub mod searchable;
|
pub mod searchable;
|
||||||
pub mod sidebar;
|
pub mod sidebar;
|
||||||
mod status_bar;
|
mod status_bar;
|
||||||
|
@ -18,6 +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};
|
||||||
use drag_and_drop::DragAndDrop;
|
use drag_and_drop::DragAndDrop;
|
||||||
use futures::{channel::oneshot, FutureExt};
|
use futures::{channel::oneshot, FutureExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -37,7 +38,6 @@ use log::error;
|
||||||
pub use pane::*;
|
pub use pane::*;
|
||||||
pub use pane_group::*;
|
pub use pane_group::*;
|
||||||
use postage::prelude::Stream;
|
use postage::prelude::Stream;
|
||||||
use programs::Dock;
|
|
||||||
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId};
|
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId};
|
||||||
use searchable::SearchableItemHandle;
|
use searchable::SearchableItemHandle;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -146,9 +146,6 @@ impl_internal_actions!(
|
||||||
impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]);
|
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) {
|
||||||
// Initialize the program manager immediately
|
|
||||||
cx.set_global(Dock::new());
|
|
||||||
|
|
||||||
pane::init(cx);
|
pane::init(cx);
|
||||||
|
|
||||||
cx.add_global_action(open);
|
cx.add_global_action(open);
|
||||||
|
@ -893,6 +890,7 @@ pub struct Workspace {
|
||||||
panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
|
panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
|
||||||
active_pane: ViewHandle<Pane>,
|
active_pane: ViewHandle<Pane>,
|
||||||
status_bar: ViewHandle<StatusBar>,
|
status_bar: ViewHandle<StatusBar>,
|
||||||
|
dock: Dock,
|
||||||
notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
|
notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
leader_state: LeaderState,
|
leader_state: LeaderState,
|
||||||
|
@ -998,10 +996,13 @@ impl Workspace {
|
||||||
drag_and_drop.register_container(weak_self.clone());
|
drag_and_drop.register_container(weak_self.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let dock = Dock::new(cx);
|
||||||
|
|
||||||
let mut this = Workspace {
|
let mut this = Workspace {
|
||||||
modal: None,
|
modal: None,
|
||||||
weak_self,
|
weak_self,
|
||||||
center: PaneGroup::new(pane.clone()),
|
center: PaneGroup::new(pane.clone()),
|
||||||
|
dock,
|
||||||
panes: vec![pane.clone()],
|
panes: vec![pane.clone()],
|
||||||
panes_by_item: Default::default(),
|
panes_by_item: Default::default(),
|
||||||
active_pane: pane.clone(),
|
active_pane: pane.clone(),
|
||||||
|
@ -2556,6 +2557,9 @@ impl View for Workspace {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.with_child(
|
||||||
|
FlexItem::new(
|
||||||
|
Flex::column()
|
||||||
.with_child(
|
.with_child(
|
||||||
FlexItem::new(self.center.render(
|
FlexItem::new(self.center.render(
|
||||||
&theme,
|
&theme,
|
||||||
|
@ -2565,6 +2569,25 @@ impl View for Workspace {
|
||||||
.flex(1., true)
|
.flex(1., true)
|
||||||
.boxed(),
|
.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(
|
.with_children(
|
||||||
if self.right_sidebar.read(cx).active_item().is_some() {
|
if self.right_sidebar.read(cx).active_item().is_some() {
|
||||||
Some(
|
Some(
|
||||||
|
@ -2578,6 +2601,7 @@ impl View for Workspace {
|
||||||
)
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
})
|
||||||
|
.with_children(self.dock.render(&theme, DockPosition::Fullscreen))
|
||||||
.with_children(self.modal.as_ref().map(|m| {
|
.with_children(self.modal.as_ref().map(|m| {
|
||||||
ChildView::new(m)
|
ChildView::new(m)
|
||||||
.contained()
|
.contained()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue