mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 11:01:54 +00:00
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:
parent
8a3a0245e0
commit
fc0bfd75ad
11 changed files with 396 additions and 198 deletions
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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![
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 }),
|
||||||
|
|
Loading…
Reference in a new issue