DOCK WORKING!
Update editor element to use mouse regions instead of dispatch event for mouse events Fix bug in presenter where mouse region handlers were stored on click and called instead of more up to date handlers from subsequent renders Changed MouseRegion to require discriminants in all cases Add scroll wheel event to MouseRegion Polished a bunch of dock inconsistencies Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
59fd967793
commit
69ecbb644d
18 changed files with 618 additions and 498 deletions
|
@ -1,14 +1,14 @@
|
|||
use gpui::{
|
||||
actions,
|
||||
elements::{ChildView, MouseEventHandler, Svg},
|
||||
elements::{ChildView, Container, FlexItem, Margin, MouseEventHandler, Svg},
|
||||
impl_internal_actions, CursorStyle, Element, ElementBox, Entity, MouseButton,
|
||||
MutableAppContext, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use settings::Settings;
|
||||
use settings::{DockAnchor, Settings};
|
||||
use theme::Theme;
|
||||
|
||||
use crate::{pane, ItemHandle, Pane, StatusItemView, Workspace};
|
||||
use crate::{ItemHandle, Pane, StatusItemView, Workspace};
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize)]
|
||||
pub struct MoveDock(pub DockAnchor);
|
||||
|
@ -24,14 +24,6 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_action(Dock::move_dock);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Default, Copy, Clone, Deserialize)]
|
||||
pub enum DockAnchor {
|
||||
#[default]
|
||||
Bottom,
|
||||
Right,
|
||||
Expanded,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DockPosition {
|
||||
Shown(DockAnchor),
|
||||
|
@ -79,63 +71,56 @@ pub struct Dock {
|
|||
impl Dock {
|
||||
pub fn new(cx: &mut ViewContext<Workspace>, default_item_factory: DefaultItemFactory) -> Self {
|
||||
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();
|
||||
}
|
||||
let pane_id = pane.id();
|
||||
cx.subscribe(&pane, move |workspace, _, event, cx| {
|
||||
workspace.handle_pane_event(pane_id, event, cx);
|
||||
})
|
||||
.detach();
|
||||
|
||||
Self {
|
||||
pane,
|
||||
position: Default::default(),
|
||||
position: DockPosition::Hidden(cx.global::<Settings>().default_dock_anchor),
|
||||
default_item_factory,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pane(&self) -> ViewHandle<Pane> {
|
||||
self.pane.clone()
|
||||
pub fn pane(&self) -> &ViewHandle<Pane> {
|
||||
&self.pane
|
||||
}
|
||||
|
||||
fn hide(&mut self) {
|
||||
self.position = self.position.hide();
|
||||
pub fn visible_pane(&self) -> Option<&ViewHandle<Pane>> {
|
||||
self.position.visible().map(|_| self.pane())
|
||||
}
|
||||
|
||||
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);
|
||||
fn set_dock_position(
|
||||
workspace: &mut Workspace,
|
||||
new_position: DockPosition,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
workspace.dock.position = new_position;
|
||||
let now_visible = workspace.dock.visible_pane().is_some();
|
||||
if now_visible {
|
||||
// Ensure that the pane has at least one item or construct a default item to put in it
|
||||
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);
|
||||
}
|
||||
cx.focus(pane);
|
||||
} else {
|
||||
if let Some(last_active_center_pane) = workspace.last_active_center_pane.clone() {
|
||||
cx.focus(last_active_center_pane);
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn hide(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||
Self::set_dock_position(workspace, workspace.dock.position.hide(), cx);
|
||||
}
|
||||
|
||||
fn toggle(workspace: &mut Workspace, _: &ToggleDock, cx: &mut ViewContext<Workspace>) {
|
||||
// Shift-escape ON
|
||||
// Get or insert the dock's last focused terminal
|
||||
// Open the dock in fullscreen
|
||||
// Focus that terminal
|
||||
|
||||
// Shift-escape OFF
|
||||
// Close the 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.focus(workspace.dock.pane.clone());
|
||||
} else {
|
||||
cx.focus_self();
|
||||
}
|
||||
cx.notify();
|
||||
workspace.status_bar().update(cx, |_, cx| cx.notify());
|
||||
Self::set_dock_position(workspace, workspace.dock.position.toggle(), cx);
|
||||
}
|
||||
|
||||
fn move_dock(
|
||||
|
@ -143,17 +128,54 @@ impl Dock {
|
|||
&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();
|
||||
Self::set_dock_position(workspace, DockPosition::Shown(new_anchor), cx);
|
||||
}
|
||||
|
||||
pub fn render(&self, _theme: &Theme, anchor: DockAnchor) -> Option<ElementBox> {
|
||||
pub fn render(
|
||||
&self,
|
||||
theme: &Theme,
|
||||
anchor: DockAnchor,
|
||||
cx: &mut RenderContext<Workspace>,
|
||||
) -> Option<ElementBox> {
|
||||
let style = &theme.workspace.dock;
|
||||
self.position
|
||||
.visible()
|
||||
.filter(|current_anchor| *current_anchor == anchor)
|
||||
.map(|_| ChildView::new(self.pane.clone()).boxed())
|
||||
.map(|anchor| match anchor {
|
||||
DockAnchor::Bottom | DockAnchor::Right => {
|
||||
let mut panel_style = style.panel.clone();
|
||||
if anchor == DockAnchor::Bottom {
|
||||
panel_style.margin = Margin {
|
||||
top: panel_style.margin.top,
|
||||
..Default::default()
|
||||
};
|
||||
} else {
|
||||
panel_style.margin = Margin {
|
||||
left: panel_style.margin.left,
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
FlexItem::new(
|
||||
Container::new(ChildView::new(self.pane.clone()).boxed())
|
||||
.with_style(style.panel)
|
||||
.boxed(),
|
||||
)
|
||||
.flex(style.flex, true)
|
||||
.boxed()
|
||||
}
|
||||
DockAnchor::Expanded => Container::new(
|
||||
MouseEventHandler::new::<Dock, _, _>(0, cx, |_state, _cx| {
|
||||
Container::new(ChildView::new(self.pane.clone()).boxed())
|
||||
.with_style(style.maximized)
|
||||
.boxed()
|
||||
})
|
||||
.capture_all()
|
||||
.with_cursor_style(CursorStyle::Arrow)
|
||||
.boxed(),
|
||||
)
|
||||
.with_background_color(style.wash_color)
|
||||
.boxed(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use super::{ItemHandle, SplitDirection};
|
||||
use crate::{
|
||||
dock::{DockAnchor, MoveDock},
|
||||
toolbar::Toolbar,
|
||||
Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace,
|
||||
dock::MoveDock, toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHandle,
|
||||
Workspace,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
|
@ -25,7 +24,7 @@ use gpui::{
|
|||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use serde::Deserialize;
|
||||
use settings::{Autosave, Settings};
|
||||
use settings::{Autosave, DockAnchor, Settings};
|
||||
use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc};
|
||||
use theme::Theme;
|
||||
use util::ResultExt;
|
||||
|
@ -187,6 +186,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
});
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
Focused,
|
||||
ActivateItem { local: bool },
|
||||
|
@ -1392,7 +1392,7 @@ impl View for Pane {
|
|||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_down(MouseButton::Left, |e, cx| {
|
||||
cx.dispatch_action(DeployNewMenu {
|
||||
position: e.position,
|
||||
position: e.region.lower_right(),
|
||||
});
|
||||
})
|
||||
.boxed(),
|
||||
|
@ -1422,11 +1422,11 @@ impl View for Pane {
|
|||
.on_down(MouseButton::Left, move |e, cx| {
|
||||
if is_dock {
|
||||
cx.dispatch_action(DeployDockMenu {
|
||||
position: e.position,
|
||||
position: e.region.lower_right(),
|
||||
});
|
||||
} else {
|
||||
cx.dispatch_action(DeploySplitMenu {
|
||||
position: e.position,
|
||||
position: e.region.lower_right(),
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -1613,7 +1613,8 @@ mod tests {
|
|||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||
let (_, workspace) =
|
||||
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// 1. Add with a destination index
|
||||
|
@ -1701,7 +1702,8 @@ mod tests {
|
|||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||
let (_, workspace) =
|
||||
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// 1. Add with a destination index
|
||||
|
@ -1777,7 +1779,8 @@ mod tests {
|
|||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||
let (_, workspace) =
|
||||
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// singleton view
|
||||
|
|
|
@ -18,7 +18,7 @@ use client::{
|
|||
};
|
||||
use clock::ReplicaId;
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use dock::{DefaultItemFactory, Dock, DockAnchor, ToggleDockButton};
|
||||
use dock::{DefaultItemFactory, Dock, ToggleDockButton};
|
||||
use drag_and_drop::DragAndDrop;
|
||||
use futures::{channel::oneshot, FutureExt};
|
||||
use gpui::{
|
||||
|
@ -41,7 +41,7 @@ use postage::prelude::Stream;
|
|||
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId};
|
||||
use searchable::SearchableItemHandle;
|
||||
use serde::Deserialize;
|
||||
use settings::{Autosave, Settings};
|
||||
use settings::{Autosave, DockAnchor, Settings};
|
||||
use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem};
|
||||
use smallvec::SmallVec;
|
||||
use status_bar::StatusBar;
|
||||
|
@ -892,6 +892,7 @@ pub struct Workspace {
|
|||
panes: Vec<ViewHandle<Pane>>,
|
||||
panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
|
||||
active_pane: ViewHandle<Pane>,
|
||||
last_active_center_pane: Option<ViewHandle<Pane>>,
|
||||
status_bar: ViewHandle<StatusBar>,
|
||||
dock: Dock,
|
||||
notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
|
||||
|
@ -987,6 +988,7 @@ impl Workspace {
|
|||
cx.emit_global(WorkspaceCreated(weak_self.clone()));
|
||||
|
||||
let dock = Dock::new(cx, dock_default_factory);
|
||||
let dock_pane = dock.pane().clone();
|
||||
|
||||
let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left));
|
||||
let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right));
|
||||
|
@ -1011,9 +1013,10 @@ impl Workspace {
|
|||
weak_self,
|
||||
center: PaneGroup::new(center_pane.clone()),
|
||||
dock,
|
||||
panes: vec![center_pane.clone()],
|
||||
panes: vec![center_pane.clone(), dock_pane],
|
||||
panes_by_item: Default::default(),
|
||||
active_pane: center_pane.clone(),
|
||||
last_active_center_pane: Some(center_pane.clone()),
|
||||
status_bar,
|
||||
notifications: Default::default(),
|
||||
client,
|
||||
|
@ -1556,10 +1559,6 @@ impl Workspace {
|
|||
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(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
|
@ -1696,6 +1695,10 @@ impl Workspace {
|
|||
status_bar.set_active_pane(&self.active_pane, cx);
|
||||
});
|
||||
self.active_item_path_changed(cx);
|
||||
|
||||
if &pane != self.dock.pane() {
|
||||
self.last_active_center_pane = Some(pane.clone());
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1715,21 +1718,19 @@ impl Workspace {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if let Some(pane) = self.pane(pane_id) {
|
||||
let is_dock = &pane == self.dock.pane();
|
||||
match event {
|
||||
pane::Event::Split(direction) => {
|
||||
pane::Event::Split(direction) if !is_dock => {
|
||||
self.split_pane(pane, *direction, cx);
|
||||
}
|
||||
pane::Event::Remove => {
|
||||
self.remove_pane(pane, cx);
|
||||
}
|
||||
pane::Event::Focused => {
|
||||
self.handle_pane_focused(pane, cx);
|
||||
}
|
||||
pane::Event::Remove if !is_dock => self.remove_pane(pane, cx),
|
||||
pane::Event::Remove if is_dock => Dock::hide(self, cx),
|
||||
pane::Event::Focused => self.handle_pane_focused(pane, cx),
|
||||
pane::Event::ActivateItem { local } => {
|
||||
if *local {
|
||||
self.unfollow(&pane, cx);
|
||||
}
|
||||
if pane == self.active_pane {
|
||||
if &pane == self.active_pane() {
|
||||
self.active_item_path_changed(cx);
|
||||
}
|
||||
}
|
||||
|
@ -1747,8 +1748,9 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
} else if self.dock.visible_pane().is_none() {
|
||||
error!("pane {} not found", pane_id);
|
||||
}
|
||||
}
|
||||
|
@ -1779,6 +1781,10 @@ impl Workspace {
|
|||
for removed_item in pane.read(cx).items() {
|
||||
self.panes_by_item.remove(&removed_item.id());
|
||||
}
|
||||
if self.last_active_center_pane == Some(pane) {
|
||||
self.last_active_center_pane = None;
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
} else {
|
||||
self.active_item_path_changed(cx);
|
||||
|
@ -1797,6 +1803,10 @@ impl Workspace {
|
|||
&self.active_pane
|
||||
}
|
||||
|
||||
pub fn dock_pane(&self) -> &ViewHandle<Pane> {
|
||||
self.dock.pane()
|
||||
}
|
||||
|
||||
fn project_remote_id_changed(&mut self, remote_id: Option<u64>, cx: &mut ViewContext<Self>) {
|
||||
if let Some(remote_id) = remote_id {
|
||||
self.remote_entity_subscription =
|
||||
|
@ -2582,25 +2592,17 @@ impl View for Workspace {
|
|||
.flex(1., true)
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(
|
||||
self.dock
|
||||
.render(&theme, DockAnchor::Bottom)
|
||||
.map(|dock| {
|
||||
FlexItem::new(dock)
|
||||
.flex(1., true)
|
||||
.boxed()
|
||||
}),
|
||||
)
|
||||
.with_children(self.dock.render(
|
||||
&theme,
|
||||
DockAnchor::Bottom,
|
||||
cx,
|
||||
))
|
||||
.boxed(),
|
||||
)
|
||||
.flex(1., true)
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(
|
||||
self.dock
|
||||
.render(&theme, DockAnchor::Right)
|
||||
.map(|dock| FlexItem::new(dock).flex(1., true).boxed()),
|
||||
)
|
||||
.with_children(self.dock.render(&theme, DockAnchor::Right, cx))
|
||||
.with_children(
|
||||
if self.right_sidebar.read(cx).active_item().is_some() {
|
||||
Some(
|
||||
|
@ -2614,13 +2616,7 @@ impl View for Workspace {
|
|||
)
|
||||
.boxed()
|
||||
})
|
||||
.with_children(self.dock.render(&theme, DockAnchor::Expanded).map(
|
||||
|dock| {
|
||||
Container::new(dock)
|
||||
.with_style(theme.workspace.fullscreen_dock)
|
||||
.boxed()
|
||||
},
|
||||
))
|
||||
.with_children(self.dock.render(&theme, DockAnchor::Expanded, cx))
|
||||
.with_children(self.modal.as_ref().map(|m| {
|
||||
ChildView::new(m)
|
||||
.contained()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue