Fix usability issues with new panel system. (#2544)

This PR updates the dock key bindings according to the following model:

There are three bits: 
Visible: Opened / closed.
Focus: Panel focused / center focused.
Zoom: Zoomed / Not zoomed.

Each of these variables is 'sticky' in that they won't effect each other
unless they need to. 'Zooming' a panel conceptually merges the visible
and focus bits.

cmd-shift-j/b/r have all been removed.

cmd-j/b/r have been updated to mean 'toggle visibility of a certain
dock', firing them should *always* reveal the panel to you (where you
last left it), or hide it, without moving focus (unless the focused
element is invisible). This means that, when the terminal panel is
zoomed, cmd-j has the same effect as ctrl-`

ctrl-` and cmd-shift-e now toggle a panel's focus, without updating the
zoom state of a panel. Toggling the focus of a zoomed panel causes it to
automatically hide itself, without losing the zoom bit.

When focused or made visible, panels which cannot be zoomed
automatically unzoom everything else so as to preserve user intent of
'show me this panel' and 'everything stays where it is if I don't take
an action'

Release Notes:

- cmd-shift-j/b/r have been removed.  (preview only)
- cmd-j/b/r unconditionally show or hide their associated dock,
respecting zoom settings. (preview only)
- ctrl-` and cmd-shift-e now retain zoom state. (preview only)
- Fixed a bug where terminal dock tab would always be in the active
state (preview only)
- Fixed a bug where terminals would not always open in the terminal
panel
- Changed the look of zoomed panels to fill more of the screen (preview
only)
This commit is contained in:
Mikayla Maki 2023-05-30 16:39:06 -07:00 committed by Mikayla Maki
parent 8a3a0245e0
commit fc0bfd75ad
No known key found for this signature in database
11 changed files with 396 additions and 198 deletions

View file

@ -373,30 +373,9 @@
"workspace::ActivatePane", "workspace::ActivatePane",
8 8
], ],
"cmd-b": [ "cmd-b": "workspace::ToggleLeftDock",
"workspace::ToggleLeftDock", "cmd-r": "workspace::ToggleRightDock",
{ "focus": true } "cmd-j": "workspace::ToggleBottomDock",
],
"cmd-shift-b": [
"workspace::ToggleLeftDock",
{ "focus": false }
],
"cmd-r": [
"workspace::ToggleRightDock",
{ "focus": true }
],
"cmd-shift-r": [
"workspace::ToggleRightDock",
{ "focus": false }
],
"cmd-j": [
"workspace::ToggleBottomDock",
{ "focus": true }
],
"cmd-shift-j": [
"workspace::ToggleBottomDock",
{ "focus": false }
],
"cmd-shift-f": "workspace::NewSearch", "cmd-shift-f": "workspace::NewSearch",
"cmd-k cmd-t": "theme_selector::Toggle", "cmd-k cmd-t": "theme_selector::Toggle",
"cmd-k cmd-s": "zed::OpenKeymap", "cmd-k cmd-s": "zed::OpenKeymap",

View file

@ -22,7 +22,7 @@ const TERMINAL_PANEL_KEY: &'static str = "TerminalPanel";
actions!(terminal_panel, [ToggleFocus]); actions!(terminal_panel, [ToggleFocus]);
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.add_action(TerminalPanel::add_terminal); cx.add_action(TerminalPanel::new_terminal);
} }
pub enum Event { pub enum Event {
@ -79,7 +79,7 @@ impl TerminalPanel {
cx.window_context().defer(move |cx| { cx.window_context().defer(move |cx| {
if let Some(this) = this.upgrade(cx) { if let Some(this) = this.upgrade(cx) {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.add_terminal(&Default::default(), cx); this.add_terminal(cx);
}); });
} }
}) })
@ -220,7 +220,19 @@ impl TerminalPanel {
} }
} }
fn add_terminal(&mut self, _: &workspace::NewTerminal, cx: &mut ViewContext<Self>) { fn new_terminal(
workspace: &mut Workspace,
_: &workspace::NewTerminal,
cx: &mut ViewContext<Workspace>,
) {
let Some(this) = workspace.focus_panel::<Self>(cx) else {
return;
};
this.update(cx, |this, cx| this.add_terminal(cx))
}
fn add_terminal(&mut self, cx: &mut ViewContext<Self>) {
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let pane = this.read_with(&cx, |this, _| this.pane.clone())?; let pane = this.read_with(&cx, |this, _| this.pane.clone())?;
@ -361,7 +373,7 @@ impl Panel for TerminalPanel {
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) { fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
if active && self.pane.read(cx).items_len() == 0 { if active && self.pane.read(cx).items_len() == 0 {
self.add_terminal(&Default::default(), cx) self.add_terminal(cx)
} }
} }

View file

@ -38,7 +38,7 @@ use workspace::{
notifications::NotifyResultExt, notifications::NotifyResultExt,
pane, register_deserializable_item, pane, register_deserializable_item,
searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle}, searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle},
Pane, ToolbarItemLocation, Workspace, WorkspaceId, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
}; };
pub use terminal::TerminalSettings; pub use terminal::TerminalSettings;
@ -66,10 +66,10 @@ pub fn init(cx: &mut AppContext) {
terminal_panel::init(cx); terminal_panel::init(cx);
terminal::init(cx); terminal::init(cx);
cx.add_action(TerminalView::deploy);
register_deserializable_item::<TerminalView>(cx); register_deserializable_item::<TerminalView>(cx);
cx.add_action(TerminalView::deploy);
//Useful terminal views //Useful terminal views
cx.add_action(TerminalView::send_text); cx.add_action(TerminalView::send_text);
cx.add_action(TerminalView::send_keystroke); cx.add_action(TerminalView::send_keystroke);
@ -101,7 +101,7 @@ impl TerminalView {
///Create a new Terminal in the current working directory or the user's home directory ///Create a new Terminal in the current working directory or the user's home directory
pub fn deploy( pub fn deploy(
workspace: &mut Workspace, workspace: &mut Workspace,
_: &workspace::NewTerminal, _: &NewCenterTerminal,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) { ) {
let strategy = settings::get::<TerminalSettings>(cx); let strategy = settings::get::<TerminalSettings>(cx);

View file

@ -89,7 +89,8 @@ pub struct Workspace {
pub breadcrumbs: Interactive<ContainedText>, pub breadcrumbs: Interactive<ContainedText>,
pub disconnected_overlay: ContainedText, pub disconnected_overlay: ContainedText,
pub modal: ContainerStyle, pub modal: ContainerStyle,
pub zoomed_foreground: ContainerStyle, pub zoomed_panel_foreground: ContainerStyle,
pub zoomed_pane_foreground: ContainerStyle,
pub zoomed_background: ContainerStyle, pub zoomed_background: ContainerStyle,
pub notification: ContainerStyle, pub notification: ContainerStyle,
pub notifications: Notifications, pub notifications: Notifications,

View file

@ -32,7 +32,7 @@ pub fn init(cx: &mut AppContext) {
pub fn show_welcome_experience(app_state: &Arc<AppState>, cx: &mut AppContext) { pub fn show_welcome_experience(app_state: &Arc<AppState>, cx: &mut AppContext) {
open_new(&app_state, cx, |workspace, cx| { open_new(&app_state, cx, |workspace, cx| {
workspace.toggle_dock(DockPosition::Left, false, cx); workspace.toggle_dock(DockPosition::Left, cx);
let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx));
workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); workspace.add_item_to_center(Box::new(welcome_page.clone()), cx);
cx.focus(&welcome_page); cx.focus(&welcome_page);

View file

@ -180,7 +180,7 @@ impl Dock {
} }
pub fn has_focus(&self, cx: &WindowContext) -> bool { pub fn has_focus(&self, cx: &WindowContext) -> bool {
self.active_panel() self.visible_panel()
.map_or(false, |panel| panel.has_focus(cx)) .map_or(false, |panel| panel.has_focus(cx))
} }
@ -201,7 +201,7 @@ impl Dock {
self.active_panel_index self.active_panel_index
} }
pub fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) { pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
if open != self.is_open { if open != self.is_open {
self.is_open = open; self.is_open = open;
if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) { if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
@ -212,11 +212,6 @@ impl Dock {
} }
} }
pub fn toggle_open(&mut self, cx: &mut ViewContext<Self>) {
self.set_open(!self.is_open, cx);
cx.notify();
}
pub fn set_panel_zoomed( pub fn set_panel_zoomed(
&mut self, &mut self,
panel: &AnyViewHandle, panel: &AnyViewHandle,
@ -259,7 +254,7 @@ impl Dock {
cx.focus(&panel); cx.focus(&panel);
} }
} else if T::should_close_on_event(event) } else if T::should_close_on_event(event)
&& this.active_panel().map_or(false, |p| p.id() == panel.id()) && this.visible_panel().map_or(false, |p| p.id() == panel.id())
{ {
this.set_open(false, cx); this.set_open(false, cx);
} }
@ -315,12 +310,16 @@ impl Dock {
} }
} }
pub fn active_panel(&self) -> Option<&Rc<dyn PanelHandle>> { pub fn visible_panel(&self) -> Option<&Rc<dyn PanelHandle>> {
let entry = self.active_entry()?; let entry = self.visible_entry()?;
Some(&entry.panel) Some(&entry.panel)
} }
fn active_entry(&self) -> Option<&PanelEntry> { pub fn active_panel(&self) -> Option<&Rc<dyn PanelHandle>> {
Some(&self.panel_entries.get(self.active_panel_index)?.panel)
}
fn visible_entry(&self) -> Option<&PanelEntry> {
if self.is_open { if self.is_open {
self.panel_entries.get(self.active_panel_index) self.panel_entries.get(self.active_panel_index)
} else { } else {
@ -329,7 +328,7 @@ impl Dock {
} }
pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Rc<dyn PanelHandle>> { pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Rc<dyn PanelHandle>> {
let entry = self.active_entry()?; let entry = self.visible_entry()?;
if entry.panel.is_zoomed(cx) { if entry.panel.is_zoomed(cx) {
Some(entry.panel.clone()) Some(entry.panel.clone())
} else { } else {
@ -362,7 +361,7 @@ impl Dock {
} }
pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> { pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> {
if let Some(active_entry) = self.active_entry() { if let Some(active_entry) = self.visible_entry() {
Empty::new() Empty::new()
.into_any() .into_any()
.contained() .contained()
@ -399,7 +398,7 @@ impl View for Dock {
} }
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> { fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if let Some(active_entry) = self.active_entry() { if let Some(active_entry) = self.visible_entry() {
let style = self.style(cx); let style = self.style(cx);
ChildView::new(active_entry.panel.as_any(), cx) ChildView::new(active_entry.panel.as_any(), cx)
.contained() .contained()
@ -417,7 +416,7 @@ impl View for Dock {
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) { fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
if cx.is_self_focused() { if cx.is_self_focused() {
if let Some(active_entry) = self.active_entry() { if let Some(active_entry) = self.visible_entry() {
cx.focus(active_entry.panel.as_any()); cx.focus(active_entry.panel.as_any());
} else { } else {
cx.focus_parent(); cx.focus_parent();
@ -504,13 +503,22 @@ impl View for PanelButtons {
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, { .on_click(MouseButton::Left, {
let tooltip_action =
tooltip_action.as_ref().map(|action| action.boxed_clone());
move |_, this, cx| { move |_, this, cx| {
if let Some(workspace) = this.workspace.upgrade(cx) { if let Some(tooltip_action) = &tooltip_action {
cx.window_context().defer(move |cx| { let window_id = cx.window_id();
workspace.update(cx, |workspace, cx| { let view_id = this.workspace.id();
workspace.toggle_panel(dock_position, panel_ix, cx) let tooltip_action = tooltip_action.boxed_clone();
}); cx.spawn(|_, mut cx| async move {
}); cx.dispatch_action(
window_id,
view_id,
&*tooltip_action,
)
.ok();
})
.detach();
} }
} }
}) })

View file

@ -2,8 +2,8 @@ mod dragged_item_receiver;
use super::{ItemHandle, SplitDirection}; use super::{ItemHandle, SplitDirection};
use crate::{ use crate::{
item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewFile, NewSearch, NewTerminal, item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewCenterTerminal, NewFile,
ToggleZoom, Workspace, WorkspaceSettings, NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
@ -150,7 +150,6 @@ pub enum Event {
pub struct Pane { pub struct Pane {
items: Vec<Box<dyn ItemHandle>>, items: Vec<Box<dyn ItemHandle>>,
activation_history: Vec<usize>, activation_history: Vec<usize>,
is_active: bool,
zoomed: bool, zoomed: bool,
active_item_index: usize, active_item_index: usize,
last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>, last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>,
@ -255,7 +254,6 @@ impl Pane {
Self { Self {
items: Vec::new(), items: Vec::new(),
activation_history: Vec::new(), activation_history: Vec::new(),
is_active: true,
zoomed: false, zoomed: false,
active_item_index: 0, active_item_index: 0,
last_focused_view_by_item: Default::default(), last_focused_view_by_item: Default::default(),
@ -323,15 +321,6 @@ impl Pane {
&self.workspace &self.workspace
} }
pub fn is_active(&self) -> bool {
self.is_active
}
pub fn set_active(&mut self, is_active: bool, cx: &mut ViewContext<Self>) {
self.is_active = is_active;
cx.notify();
}
pub fn has_focus(&self) -> bool { pub fn has_focus(&self) -> bool {
self.has_focus self.has_focus
} }
@ -1219,7 +1208,7 @@ impl Pane {
AnchorCorner::TopRight, AnchorCorner::TopRight,
vec![ vec![
ContextMenuItem::action("New File", NewFile), ContextMenuItem::action("New File", NewFile),
ContextMenuItem::action("New Terminal", NewTerminal), ContextMenuItem::action("New Terminal", NewCenterTerminal),
ContextMenuItem::action("New Search", NewSearch), ContextMenuItem::action("New Search", NewSearch),
], ],
cx, cx,
@ -1343,7 +1332,7 @@ impl Pane {
None None
}; };
let pane_active = self.is_active; let pane_active = self.has_focus;
enum Tabs {} enum Tabs {}
let mut row = Flex::row().scrollable::<Tabs>(1, autoscroll, cx); let mut row = Flex::row().scrollable::<Tabs>(1, autoscroll, cx);
@ -1722,7 +1711,7 @@ impl View for Pane {
let mut tab_row = Flex::row() let mut tab_row = Flex::row()
.with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs")); .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs"));
if self.is_active { if self.has_focus {
let render_tab_bar_buttons = self.render_tab_bar_buttons.clone(); let render_tab_bar_buttons = self.render_tab_bar_buttons.clone();
tab_row.add_child( tab_row.add_child(
(render_tab_bar_buttons)(self, cx) (render_tab_bar_buttons)(self, cx)
@ -1813,6 +1802,7 @@ impl View for Pane {
if !self.has_focus { if !self.has_focus {
self.has_focus = true; self.has_focus = true;
cx.emit(Event::Focus); cx.emit(Event::Focus);
cx.notify();
} }
self.toolbar.update(cx, |toolbar, cx| { self.toolbar.update(cx, |toolbar, cx| {
@ -1847,6 +1837,7 @@ impl View for Pane {
self.toolbar.update(cx, |toolbar, cx| { self.toolbar.update(cx, |toolbar, cx| {
toolbar.pane_focus_update(false, cx); toolbar.pane_focus_update(false, cx);
}); });
cx.notify();
} }
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) { fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {

View file

@ -53,6 +53,7 @@ use std::{
cmp, env, cmp, env,
future::Future, future::Future,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc,
str, str,
sync::{atomic::AtomicUsize, Arc}, sync::{atomic::AtomicUsize, Arc},
time::Duration, time::Duration,
@ -103,24 +104,6 @@ pub trait Modal: View {
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct RemoveWorktreeFromProject(pub WorktreeId); pub struct RemoveWorktreeFromProject(pub WorktreeId);
#[derive(Copy, Clone, Default, Deserialize, PartialEq)]
pub struct ToggleLeftDock {
#[serde(default = "default_true")]
pub focus: bool,
}
#[derive(Copy, Clone, Default, Deserialize, PartialEq)]
pub struct ToggleBottomDock {
#[serde(default = "default_true")]
pub focus: bool,
}
#[derive(Copy, Clone, Default, Deserialize, PartialEq)]
pub struct ToggleRightDock {
#[serde(default = "default_true")]
pub focus: bool,
}
actions!( actions!(
workspace, workspace,
[ [
@ -137,22 +120,21 @@ actions!(
ActivateNextPane, ActivateNextPane,
FollowNextCollaborator, FollowNextCollaborator,
NewTerminal, NewTerminal,
NewCenterTerminal,
ToggleTerminalFocus, ToggleTerminalFocus,
NewSearch, NewSearch,
Feedback, Feedback,
Restart, Restart,
Welcome, Welcome,
ToggleZoom, ToggleZoom,
ToggleLeftDock,
ToggleRightDock,
ToggleBottomDock,
] ]
); );
actions!(zed, [OpenSettings]); actions!(zed, [OpenSettings]);
impl_actions!(
workspace,
[ToggleLeftDock, ToggleBottomDock, ToggleRightDock]
);
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct OpenPaths { pub struct OpenPaths {
pub paths: Vec<PathBuf>, pub paths: Vec<PathBuf>,
@ -268,14 +250,14 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
workspace.activate_next_pane(cx) workspace.activate_next_pane(cx)
}); });
cx.add_action(|workspace: &mut Workspace, action: &ToggleLeftDock, cx| { cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| {
workspace.toggle_dock(DockPosition::Left, action.focus, cx); workspace.toggle_dock(DockPosition::Left, cx);
}); });
cx.add_action(|workspace: &mut Workspace, action: &ToggleRightDock, cx| { cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
workspace.toggle_dock(DockPosition::Right, action.focus, cx); workspace.toggle_dock(DockPosition::Right, cx);
}); });
cx.add_action(|workspace: &mut Workspace, action: &ToggleBottomDock, cx| { cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
workspace.toggle_dock(DockPosition::Bottom, action.focus, cx); workspace.toggle_dock(DockPosition::Bottom, cx);
}); });
cx.add_action(Workspace::activate_pane_at_index); cx.add_action(Workspace::activate_pane_at_index);
@ -485,6 +467,7 @@ pub struct Workspace {
remote_entity_subscription: Option<client::Subscription>, remote_entity_subscription: Option<client::Subscription>,
modal: Option<AnyViewHandle>, modal: Option<AnyViewHandle>,
zoomed: Option<AnyWeakViewHandle>, zoomed: Option<AnyWeakViewHandle>,
zoomed_position: Option<DockPosition>,
center: PaneGroup, center: PaneGroup,
left_dock: ViewHandle<Dock>, left_dock: ViewHandle<Dock>,
bottom_dock: ViewHandle<Dock>, bottom_dock: ViewHandle<Dock>,
@ -689,6 +672,7 @@ impl Workspace {
weak_self: weak_handle.clone(), weak_self: weak_handle.clone(),
modal: None, modal: None,
zoomed: None, zoomed: None,
zoomed_position: None,
center: PaneGroup::new(center_pane.clone()), center: PaneGroup::new(center_pane.clone()),
panes: vec![center_pane.clone()], panes: vec![center_pane.clone()],
panes_by_item: Default::default(), panes_by_item: Default::default(),
@ -887,10 +871,15 @@ impl Workspace {
was_visible = dock.is_open() was_visible = dock.is_open()
&& dock && dock
.active_panel() .visible_panel()
.map_or(false, |active_panel| active_panel.id() == panel.id()); .map_or(false, |active_panel| active_panel.id() == panel.id());
dock.remove_panel(&panel, cx); dock.remove_panel(&panel, cx);
}); });
if panel.is_zoomed(cx) {
this.zoomed_position = Some(new_position);
}
dock = match panel.read(cx).position(cx) { dock = match panel.read(cx).position(cx) {
DockPosition::Left => &this.left_dock, DockPosition::Left => &this.left_dock,
DockPosition::Bottom => &this.bottom_dock, DockPosition::Bottom => &this.bottom_dock,
@ -909,14 +898,17 @@ impl Workspace {
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx)); dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
if panel.has_focus(cx) { if panel.has_focus(cx) {
this.zoomed = Some(panel.downgrade().into_any()); this.zoomed = Some(panel.downgrade().into_any());
this.zoomed_position = Some(panel.read(cx).position(cx));
} }
} else if T::should_zoom_out_on_event(event) { } else if T::should_zoom_out_on_event(event) {
this.zoom_out(cx); this.zoom_out(cx);
} else if T::is_focus_event(event) { } else if T::is_focus_event(event) {
if panel.is_zoomed(cx) { if panel.is_zoomed(cx) {
this.zoomed = Some(panel.downgrade().into_any()); this.zoomed = Some(panel.downgrade().into_any());
this.zoomed_position = Some(panel.read(cx).position(cx));
} else { } else {
this.zoomed = None; this.zoomed = None;
this.zoomed_position = None;
} }
cx.notify(); cx.notify();
} }
@ -1486,89 +1478,110 @@ impl Workspace {
} }
} }
pub fn toggle_dock( pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext<Self>) {
&mut self,
dock_side: DockPosition,
focus: bool,
cx: &mut ViewContext<Self>,
) {
let dock = match dock_side { let dock = match dock_side {
DockPosition::Left => &self.left_dock, DockPosition::Left => &self.left_dock,
DockPosition::Bottom => &self.bottom_dock, DockPosition::Bottom => &self.bottom_dock,
DockPosition::Right => &self.right_dock, DockPosition::Right => &self.right_dock,
}; };
let mut focus_center = false;
let mut zoom_out = false;
dock.update(cx, |dock, cx| { dock.update(cx, |dock, cx| {
let open = !dock.is_open(); let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
dock.set_open(open, cx); let was_visible = dock.is_open() && !other_is_zoomed;
dock.set_open(!was_visible, cx);
if let Some(active_panel) = dock.active_panel() {
if was_visible {
if active_panel.has_focus(cx) {
focus_center = true;
}
} else {
if active_panel.is_zoomed(cx) {
cx.focus(active_panel.as_any());
}
zoom_out = true;
}
}
}); });
if dock.read(cx).is_open() && focus { if zoom_out {
cx.focus(dock); self.zoom_out_everything_except(dock_side, cx);
} else { }
if focus_center {
cx.focus_self(); cx.focus_self();
} }
cx.notify(); cx.notify();
self.serialize_workspace(cx); self.serialize_workspace(cx);
} }
pub fn toggle_panel( pub fn focus_panel<T: Panel>(&mut self, cx: &mut ViewContext<Self>) -> Option<ViewHandle<T>> {
&mut self, self.show_or_hide_panel::<T>(cx, |_, _| true)?
position: DockPosition, .as_any()
panel_index: usize, .clone()
cx: &mut ViewContext<Self>, .downcast()
) {
let dock = match position {
DockPosition::Left => &mut self.left_dock,
DockPosition::Bottom => &mut self.bottom_dock,
DockPosition::Right => &mut self.right_dock,
};
let active_item = dock.update(cx, move |dock, cx| {
if dock.is_open() && dock.active_panel_index() == panel_index {
dock.set_open(false, cx);
None
} else {
dock.set_open(true, cx);
dock.activate_panel(panel_index, cx);
dock.active_panel().cloned()
}
});
if let Some(active_item) = active_item {
if active_item.has_focus(cx) {
cx.focus_self();
} else {
cx.focus(active_item.as_any());
}
} else {
cx.focus_self();
}
self.serialize_workspace(cx);
cx.notify();
} }
pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) { pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { self.show_or_hide_panel::<T>(cx, |panel, cx| !panel.has_focus(cx));
if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
let active_item = dock.update(cx, |dock, cx| {
dock.set_open(true, cx);
dock.activate_panel(panel_index, cx);
dock.active_panel().cloned()
});
if let Some(active_item) = active_item {
if active_item.has_focus(cx) {
cx.focus_self();
} else {
cx.focus(active_item.as_any());
} }
fn show_or_hide_panel<T: Panel>(
&mut self,
cx: &mut ViewContext<Self>,
show: impl Fn(&dyn PanelHandle, &mut ViewContext<Dock>) -> bool,
) -> Option<Rc<dyn PanelHandle>> {
for (dock, position) in [
self.left_dock.clone(),
self.bottom_dock.clone(),
self.right_dock.clone(),
]
.into_iter()
.zip(
[
DockPosition::Left,
DockPosition::Bottom,
DockPosition::Right,
]
.into_iter(),
) {
if let Some(panel_index) = dock.read(cx).panel_index_for_type::<T>() {
let mut focus_center = false;
let mut zoom_out = false;
let panel = dock.update(cx, |dock, cx| {
dock.activate_panel(panel_index, cx);
let panel = dock.active_panel().cloned();
if let Some(panel) = panel.as_ref() {
let should_show = show(&**panel, cx);
if should_show {
dock.set_open(true, cx);
cx.focus(panel.as_any());
zoom_out = true;
} else {
if panel.is_zoomed(cx) {
dock.set_open(false, cx);
}
focus_center = true;
}
}
panel
});
if zoom_out {
self.zoom_out_everything_except(position, cx);
}
if focus_center {
cx.focus_self();
} }
self.serialize_workspace(cx); self.serialize_workspace(cx);
cx.notify(); cx.notify();
break; return panel;
} }
} }
None
} }
fn zoom_out(&mut self, cx: &mut ViewContext<Self>) { fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
@ -1580,6 +1593,36 @@ impl Workspace {
self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx)); self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx));
self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx)); self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx));
self.zoomed = None; self.zoomed = None;
self.zoomed_position = None;
cx.notify();
}
fn zoom_out_everything_except(
&mut self,
except_position: DockPosition,
cx: &mut ViewContext<Self>,
) {
for pane in &self.panes {
pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
}
if except_position != DockPosition::Left {
self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx));
}
if except_position != DockPosition::Bottom {
self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx));
}
if except_position != DockPosition::Right {
self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx));
}
if self.zoomed_position != Some(except_position) {
self.zoomed = None;
self.zoomed_position = None;
}
cx.notify(); cx.notify();
} }
@ -1780,11 +1823,7 @@ impl Workspace {
fn handle_pane_focused(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) { fn handle_pane_focused(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
if self.active_pane != pane { if self.active_pane != pane {
self.active_pane
.update(cx, |pane, cx| pane.set_active(false, cx));
self.active_pane = pane.clone(); self.active_pane = pane.clone();
self.active_pane
.update(cx, |pane, cx| pane.set_active(true, cx));
self.status_bar.update(cx, |status_bar, cx| { self.status_bar.update(cx, |status_bar, cx| {
status_bar.set_active_pane(&self.active_pane, cx); status_bar.set_active_pane(&self.active_pane, cx);
}); });
@ -1797,6 +1836,7 @@ impl Workspace {
} else { } else {
self.zoomed = None; self.zoomed = None;
} }
self.zoomed_position = None;
self.update_followers( self.update_followers(
proto::update_followers::Variant::UpdateActiveView(proto::UpdateActiveView { proto::update_followers::Variant::UpdateActiveView(proto::UpdateActiveView {
@ -1855,6 +1895,7 @@ impl Workspace {
pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); pane.update(cx, |pane, cx| pane.set_zoomed(true, cx));
if pane.read(cx).has_focus() { if pane.read(cx).has_focus() {
self.zoomed = Some(pane.downgrade().into_any()); self.zoomed = Some(pane.downgrade().into_any());
self.zoomed_position = None;
} }
cx.notify(); cx.notify();
} }
@ -2663,7 +2704,7 @@ impl Workspace {
}) })
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
pane.is_active(), pane.has_focus(),
) )
}; };
@ -2691,7 +2732,7 @@ impl Workspace {
fn build_serialized_docks(this: &Workspace, cx: &AppContext) -> DockStructure { fn build_serialized_docks(this: &Workspace, cx: &AppContext) -> DockStructure {
let left_dock = this.left_dock.read(cx); let left_dock = this.left_dock.read(cx);
let left_visible = left_dock.is_open(); let left_visible = left_dock.is_open();
let left_active_panel = left_dock.active_panel().and_then(|panel| { let left_active_panel = left_dock.visible_panel().and_then(|panel| {
Some( Some(
cx.view_ui_name(panel.as_any().window_id(), panel.id())? cx.view_ui_name(panel.as_any().window_id(), panel.id())?
.to_string(), .to_string(),
@ -2700,7 +2741,7 @@ impl Workspace {
let right_dock = this.right_dock.read(cx); let right_dock = this.right_dock.read(cx);
let right_visible = right_dock.is_open(); let right_visible = right_dock.is_open();
let right_active_panel = right_dock.active_panel().and_then(|panel| { let right_active_panel = right_dock.visible_panel().and_then(|panel| {
Some( Some(
cx.view_ui_name(panel.as_any().window_id(), panel.id())? cx.view_ui_name(panel.as_any().window_id(), panel.id())?
.to_string(), .to_string(),
@ -2709,7 +2750,7 @@ impl Workspace {
let bottom_dock = this.bottom_dock.read(cx); let bottom_dock = this.bottom_dock.read(cx);
let bottom_visible = bottom_dock.is_open(); let bottom_visible = bottom_dock.is_open();
let bottom_active_panel = bottom_dock.active_panel().and_then(|panel| { let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| {
Some( Some(
cx.view_ui_name(panel.as_any().window_id(), panel.id())? cx.view_ui_name(panel.as_any().window_id(), panel.id())?
.to_string(), .to_string(),
@ -2891,7 +2932,7 @@ impl Workspace {
DockPosition::Right => &self.right_dock, DockPosition::Right => &self.right_dock,
DockPosition::Bottom => &self.bottom_dock, DockPosition::Bottom => &self.bottom_dock,
}; };
let active_panel = dock.read(cx).active_panel()?; let active_panel = dock.read(cx).visible_panel()?;
let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) { let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) {
dock.read(cx).render_placeholder(cx) dock.read(cx).render_placeholder(cx)
} else { } else {
@ -3092,10 +3133,40 @@ impl View for Workspace {
.with_children(self.zoomed.as_ref().and_then(|zoomed| { .with_children(self.zoomed.as_ref().and_then(|zoomed| {
enum ZoomBackground {} enum ZoomBackground {}
let zoomed = zoomed.upgrade(cx)?; let zoomed = zoomed.upgrade(cx)?;
let mut foreground_style;
match self.zoomed_position {
Some(DockPosition::Left) => {
foreground_style =
theme.workspace.zoomed_panel_foreground;
foreground_style.margin.left = 0.;
foreground_style.margin.top = 0.;
foreground_style.margin.bottom = 0.;
}
Some(DockPosition::Right) => {
foreground_style =
theme.workspace.zoomed_panel_foreground;
foreground_style.margin.right = 0.;
foreground_style.margin.top = 0.;
foreground_style.margin.bottom = 0.;
}
Some(DockPosition::Bottom) => {
foreground_style =
theme.workspace.zoomed_panel_foreground;
foreground_style.margin.left = 0.;
foreground_style.margin.right = 0.;
foreground_style.margin.bottom = 0.;
}
None => {
foreground_style =
theme.workspace.zoomed_pane_foreground;
}
}
Some( Some(
ChildView::new(&zoomed, cx) ChildView::new(&zoomed, cx)
.contained() .contained()
.with_style(theme.workspace.zoomed_foreground) .with_style(foreground_style)
.aligned() .aligned()
.contained() .contained()
.with_style(theme.workspace.zoomed_background) .with_style(theme.workspace.zoomed_background)
@ -3445,10 +3516,6 @@ fn parse_pixel_position_env_var(value: &str) -> Option<Vector2F> {
Some(vec2f(width as f32, height as f32)) Some(vec2f(width as f32, height as f32))
} }
fn default_true() -> bool {
true
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -4029,6 +4096,128 @@ mod tests {
}); });
} }
#[gpui::test]
async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
let panel = workspace.update(cx, |workspace, cx| {
let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right));
workspace.add_panel(panel.clone(), cx);
workspace
.right_dock()
.update(cx, |right_dock, cx| right_dock.set_open(true, cx));
panel
});
// Transfer focus from center to panel
workspace.update(cx, |workspace, cx| {
workspace.toggle_panel_focus::<TestPanel>(cx);
});
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(!panel.is_zoomed(cx));
assert!(panel.has_focus(cx));
});
// Transfer focus from panel to center
workspace.update(cx, |workspace, cx| {
workspace.toggle_panel_focus::<TestPanel>(cx);
});
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(!panel.is_zoomed(cx));
assert!(!panel.has_focus(cx));
});
// Close the dock
workspace.update(cx, |workspace, cx| {
workspace.toggle_dock(DockPosition::Right, cx);
});
workspace.read_with(cx, |workspace, cx| {
assert!(!workspace.right_dock().read(cx).is_open());
assert!(!panel.is_zoomed(cx));
assert!(!panel.has_focus(cx));
});
// Open the dock
workspace.update(cx, |workspace, cx| {
workspace.toggle_dock(DockPosition::Right, cx);
});
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(!panel.is_zoomed(cx));
assert!(!panel.has_focus(cx));
});
// Focus and zoom panel
panel.update(cx, |panel, cx| {
cx.focus_self();
panel.set_zoomed(true, cx)
});
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(panel.is_zoomed(cx));
assert!(panel.has_focus(cx));
});
// Transfer focus to the center closes the dock
workspace.update(cx, |workspace, cx| {
workspace.toggle_panel_focus::<TestPanel>(cx);
});
workspace.read_with(cx, |workspace, cx| {
assert!(!workspace.right_dock().read(cx).is_open());
assert!(panel.is_zoomed(cx));
assert!(!panel.has_focus(cx));
});
// Transfering focus back to the panel keeps it zoomed
workspace.update(cx, |workspace, cx| {
workspace.toggle_panel_focus::<TestPanel>(cx);
});
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(panel.is_zoomed(cx));
assert!(panel.has_focus(cx));
});
// Close the dock while it is zoomed
workspace.update(cx, |workspace, cx| {
workspace.toggle_dock(DockPosition::Right, cx)
});
workspace.read_with(cx, |workspace, cx| {
assert!(!workspace.right_dock().read(cx).is_open());
assert!(panel.is_zoomed(cx));
assert!(workspace.zoomed.is_none());
assert!(!panel.has_focus(cx));
});
// Opening the dock, when it's zoomed, retains focus
workspace.update(cx, |workspace, cx| {
workspace.toggle_dock(DockPosition::Right, cx)
});
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.right_dock().read(cx).is_open());
assert!(panel.is_zoomed(cx));
assert!(workspace.zoomed.is_some());
assert!(panel.has_focus(cx));
});
}
#[gpui::test] #[gpui::test]
async fn test_panels(cx: &mut gpui::TestAppContext) { async fn test_panels(cx: &mut gpui::TestAppContext) {
init_test(cx); init_test(cx);
@ -4052,7 +4241,7 @@ mod tests {
let left_dock = workspace.left_dock(); let left_dock = workspace.left_dock();
assert_eq!( assert_eq!(
left_dock.read(cx).active_panel().unwrap().id(), left_dock.read(cx).visible_panel().unwrap().id(),
panel_1.id() panel_1.id()
); );
assert_eq!( assert_eq!(
@ -4062,7 +4251,12 @@ mod tests {
left_dock.update(cx, |left_dock, cx| left_dock.resize_active_panel(1337., cx)); left_dock.update(cx, |left_dock, cx| left_dock.resize_active_panel(1337., cx));
assert_eq!( assert_eq!(
workspace.right_dock().read(cx).active_panel().unwrap().id(), workspace
.right_dock()
.read(cx)
.visible_panel()
.unwrap()
.id(),
panel_2.id() panel_2.id()
); );
@ -4078,10 +4272,10 @@ mod tests {
// Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right. // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right.
// Since it was the only panel on the left, the left dock should now be closed. // Since it was the only panel on the left, the left dock should now be closed.
assert!(!workspace.left_dock().read(cx).is_open()); assert!(!workspace.left_dock().read(cx).is_open());
assert!(workspace.left_dock().read(cx).active_panel().is_none()); assert!(workspace.left_dock().read(cx).visible_panel().is_none());
let right_dock = workspace.right_dock(); let right_dock = workspace.right_dock();
assert_eq!( assert_eq!(
right_dock.read(cx).active_panel().unwrap().id(), right_dock.read(cx).visible_panel().unwrap().id(),
panel_1.id() panel_1.id()
); );
assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
@ -4096,7 +4290,12 @@ mod tests {
// And the right dock is unaffected in it's displaying of panel_1 // And the right dock is unaffected in it's displaying of panel_1
assert!(workspace.right_dock().read(cx).is_open()); assert!(workspace.right_dock().read(cx).is_open());
assert_eq!( assert_eq!(
workspace.right_dock().read(cx).active_panel().unwrap().id(), workspace
.right_dock()
.read(cx)
.visible_panel()
.unwrap()
.id(),
panel_1.id() panel_1.id()
); );
}); });
@ -4111,7 +4310,7 @@ mod tests {
let left_dock = workspace.left_dock(); let left_dock = workspace.left_dock();
assert!(left_dock.read(cx).is_open()); assert!(left_dock.read(cx).is_open());
assert_eq!( assert_eq!(
left_dock.read(cx).active_panel().unwrap().id(), left_dock.read(cx).visible_panel().unwrap().id(),
panel_1.id() panel_1.id()
); );
assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.);
@ -4145,7 +4344,7 @@ mod tests {
let left_dock = workspace.left_dock(); let left_dock = workspace.left_dock();
assert!(left_dock.read(cx).is_open()); assert!(left_dock.read(cx).is_open());
assert_eq!( assert_eq!(
left_dock.read(cx).active_panel().unwrap().id(), left_dock.read(cx).visible_panel().unwrap().id(),
panel_1.id() panel_1.id()
); );
assert!(panel_1.is_focused(cx)); assert!(panel_1.is_focused(cx));
@ -4159,7 +4358,7 @@ mod tests {
let left_dock = workspace.left_dock(); let left_dock = workspace.left_dock();
assert!(left_dock.read(cx).is_open()); assert!(left_dock.read(cx).is_open());
assert_eq!( assert_eq!(
left_dock.read(cx).active_panel().unwrap().id(), left_dock.read(cx).visible_panel().unwrap().id(),
panel_1.id() panel_1.id()
); );
}); });
@ -4168,6 +4367,14 @@ mod tests {
panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn)); panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn));
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
assert_eq!(workspace.zoomed_position, Some(DockPosition::Left));
});
// Move panel to another dock while it is zoomed
panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx));
workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
}); });
// If focus is transferred to another view that's not a panel or another pane, we still show // If focus is transferred to another view that's not a panel or another pane, we still show
@ -4176,12 +4383,14 @@ mod tests {
focus_receiver.update(cx, |_, cx| cx.focus_self()); focus_receiver.update(cx, |_, cx| cx.focus_self());
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
}); });
// If focus is transferred elsewhere in the workspace, the panel is no longer zoomed. // If focus is transferred elsewhere in the workspace, the panel is no longer zoomed.
workspace.update(cx, |_, cx| cx.focus_self()); workspace.update(cx, |_, cx| cx.focus_self());
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, None); assert_eq!(workspace.zoomed, None);
assert_eq!(workspace.zoomed_position, None);
}); });
// If focus is transferred again to another view that's not a panel or a pane, we won't // If focus is transferred again to another view that's not a panel or a pane, we won't
@ -4189,18 +4398,21 @@ mod tests {
focus_receiver.update(cx, |_, cx| cx.focus_self()); focus_receiver.update(cx, |_, cx| cx.focus_self());
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, None); assert_eq!(workspace.zoomed, None);
assert_eq!(workspace.zoomed_position, None);
}); });
// When focus is transferred back to the panel, it is zoomed again. // When focus is transferred back to the panel, it is zoomed again.
panel_1.update(cx, |_, cx| cx.focus_self()); panel_1.update(cx, |_, cx| cx.focus_self());
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any()));
assert_eq!(workspace.zoomed_position, Some(DockPosition::Right));
}); });
// Emitting a ZoomOut event unzooms the panel. // Emitting a ZoomOut event unzooms the panel.
panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut)); panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut));
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
assert_eq!(workspace.zoomed, None); assert_eq!(workspace.zoomed, None);
assert_eq!(workspace.zoomed_position, None);
}); });
// Emit closed event on panel 1, which is active // Emit closed event on panel 1, which is active
@ -4208,8 +4420,8 @@ mod tests {
// Now the left dock is closed, because panel_1 was the active panel // Now the left dock is closed, because panel_1 was the active panel
workspace.read_with(cx, |workspace, cx| { workspace.read_with(cx, |workspace, cx| {
let left_dock = workspace.left_dock(); let right_dock = workspace.right_dock();
assert!(!left_dock.read(cx).is_open()); assert!(!right_dock.read(cx).is_open());
}); });
} }

View file

@ -89,18 +89,9 @@ pub fn menus() -> Vec<Menu<'static>> {
MenuItem::action("Zoom Out", super::DecreaseBufferFontSize), MenuItem::action("Zoom Out", super::DecreaseBufferFontSize),
MenuItem::action("Reset Zoom", super::ResetBufferFontSize), MenuItem::action("Reset Zoom", super::ResetBufferFontSize),
MenuItem::separator(), MenuItem::separator(),
MenuItem::action( MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock),
"Toggle Left Dock", MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock),
workspace::ToggleLeftDock { focus: false }, MenuItem::action("Toggle Bottom Dock", workspace::ToggleBottomDock),
),
MenuItem::action(
"Toggle Right Dock",
workspace::ToggleRightDock { focus: false },
),
MenuItem::action(
"Toggle Bottom Dock",
workspace::ToggleBottomDock { focus: false },
),
MenuItem::submenu(Menu { MenuItem::submenu(Menu {
name: "Editor Layout", name: "Editor Layout",
items: vec![ items: vec![

View file

@ -354,7 +354,7 @@ pub fn initialize_workspace(
.map_or(false, |entry| entry.is_dir()) .map_or(false, |entry| entry.is_dir())
}) })
{ {
workspace.toggle_dock(project_panel_position, false, cx); workspace.toggle_dock(project_panel_position, cx);
} }
workspace.add_panel(terminal_panel, cx) workspace.add_panel(terminal_panel, cx)

View file

@ -119,14 +119,18 @@ export default function workspace(colorScheme: ColorScheme) {
cursor: "Arrow", cursor: "Arrow",
}, },
zoomedBackground: { zoomedBackground: {
padding: 10,
cursor: "Arrow", cursor: "Arrow",
background: withOpacity(background(colorScheme.lowest), 0.5) background: withOpacity(background(colorScheme.lowest), 0.85)
}, },
zoomedForeground: { zoomedPaneForeground: {
margin: 10,
shadow: colorScheme.modalShadow, shadow: colorScheme.modalShadow,
border: border(colorScheme.highest, { overlay: true }), border: border(colorScheme.highest, { overlay: true }),
}, },
zoomedPanelForeground: {
margin: 18,
border: border(colorScheme.highest, { overlay: true }),
},
dock: { dock: {
left: { left: {
border: border(layer, { right: true }), border: border(layer, { right: true }),