Merge pull request #1257 from zed-industries/vscode-pane-bindings

Adjust pane, tab, panel management bindings to match VS Code
This commit is contained in:
Max Brunsfeld 2022-06-29 15:49:25 -07:00 committed by GitHub
commit 87124b959d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 234 additions and 85 deletions

View file

@ -13,6 +13,8 @@
"ctrl-c": "menu::Cancel", "ctrl-c": "menu::Cancel",
"shift-cmd-{": "pane::ActivatePrevItem", "shift-cmd-{": "pane::ActivatePrevItem",
"shift-cmd-}": "pane::ActivateNextItem", "shift-cmd-}": "pane::ActivateNextItem",
"alt-cmd-left": "pane::ActivatePrevItem",
"alt-cmd-right": "pane::ActivateNextItem",
"cmd-w": "pane::CloseActiveItem", "cmd-w": "pane::CloseActiveItem",
"cmd-shift-W": "workspace::CloseWindow", "cmd-shift-W": "workspace::CloseWindow",
"alt-cmd-t": "pane::CloseInactiveItems", "alt-cmd-t": "pane::CloseInactiveItems",
@ -210,6 +212,43 @@
{ {
"context": "Pane", "context": "Pane",
"bindings": { "bindings": {
"ctrl-1": [
"pane::ActivateItem",
0
],
"ctrl-2": [
"pane::ActivateItem",
1
],
"ctrl-3": [
"pane::ActivateItem",
2
],
"ctrl-4": [
"pane::ActivateItem",
3
],
"ctrl-5": [
"pane::ActivateItem",
4
],
"ctrl-6": [
"pane::ActivateItem",
5
],
"ctrl-7": [
"pane::ActivateItem",
6
],
"ctrl-8": [
"pane::ActivateItem",
7
],
"ctrl-9": [
"pane::ActivateItem",
8
],
"ctrl-0": "pane::ActivateLastItem",
"ctrl--": "pane::GoBack", "ctrl--": "pane::GoBack",
"shift-ctrl-_": "pane::GoForward", "shift-ctrl-_": "pane::GoForward",
"cmd-shift-T": "pane::ReopenClosedItem", "cmd-shift-T": "pane::ReopenClosedItem",
@ -219,6 +258,43 @@
{ {
"context": "Workspace", "context": "Workspace",
"bindings": { "bindings": {
"cmd-1": [
"workspace::ActivatePane",
0
],
"cmd-2": [
"workspace::ActivatePane",
1
],
"cmd-3": [
"workspace::ActivatePane",
2
],
"cmd-4": [
"workspace::ActivatePane",
3
],
"cmd-5": [
"workspace::ActivatePane",
4
],
"cmd-6": [
"workspace::ActivatePane",
5
],
"cmd-7": [
"workspace::ActivatePane",
6
],
"cmd-8": [
"workspace::ActivatePane",
7
],
"cmd-9": [
"workspace::ActivatePane",
8
],
"cmd-b": "workspace::ToggleLeftSidebar",
"cmd-shift-F": "project_search::Deploy", "cmd-shift-F": "project_search::Deploy",
"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",
@ -226,6 +302,7 @@
"cmd-p": "file_finder::Toggle", "cmd-p": "file_finder::Toggle",
"cmd-shift-P": "command_palette::Toggle", "cmd-shift-P": "command_palette::Toggle",
"cmd-shift-M": "diagnostics::Deploy", "cmd-shift-M": "diagnostics::Deploy",
"cmd-shift-E": "project_panel::Toggle",
"cmd-alt-s": "workspace::SaveAll" "cmd-alt-s": "workspace::SaveAll"
} }
}, },
@ -310,34 +387,8 @@
{ {
"context": "Workspace", "context": "Workspace",
"bindings": { "bindings": {
"cmd-1": [ "cmd-shift-C": "contacts_panel::Toggle",
"workspace::ToggleSidebarItemFocus", "cmd-shift-B": "workspace::ToggleRightSidebar"
{
"side": "Left",
"item_index": 0
}
],
"cmd-shift-!": [
"workspace::ToggleSidebarItem",
{
"side": "Left",
"item_index": 0
}
],
"cmd-9": [
"workspace::ToggleSidebarItemFocus",
{
"side": "Right",
"item_index": 0
}
],
"cmd-shift-(": [
"workspace::ToggleSidebarItem",
{
"side": "Right",
"item_index": 0
}
]
} }
}, },
{ {

View file

@ -8,6 +8,7 @@ use contact_notification::ContactNotification;
use editor::{Cancel, Editor}; use editor::{Cancel, Editor};
use fuzzy::{match_strings, StringMatchCandidate}; use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{ use gpui::{
actions,
elements::*, elements::*,
geometry::{rect::RectF, vector::vec2f}, geometry::{rect::RectF, vector::vec2f},
impl_actions, impl_internal_actions, impl_actions, impl_internal_actions,
@ -24,6 +25,8 @@ use std::{ops::DerefMut, sync::Arc};
use theme::IconButton; use theme::IconButton;
use workspace::{sidebar::SidebarItem, JoinProject, ToggleProjectOnline, Workspace}; use workspace::{sidebar::SidebarItem, JoinProject, ToggleProjectOnline, Workspace};
actions!(contacts_panel, [Toggle]);
impl_actions!( impl_actions!(
contacts_panel, contacts_panel,
[RequestContact, RemoveContact, RespondToContactRequest] [RequestContact, RemoveContact, RespondToContactRequest]

View file

@ -108,7 +108,8 @@ actions!(
Cut, Cut,
Paste, Paste,
Delete, Delete,
Rename Rename,
Toggle
] ]
); );
impl_internal_actions!(project_panel, [Open, ToggleExpanded, DeployContextMenu]); impl_internal_actions!(project_panel, [Open, ToggleExpanded, DeployContextMenu]);

View file

@ -18,11 +18,15 @@ use settings::Settings;
use std::{any::Any, cell::RefCell, mem, path::Path, rc::Rc}; use std::{any::Any, cell::RefCell, mem, path::Path, rc::Rc};
use util::ResultExt; use util::ResultExt;
#[derive(Clone, Deserialize, PartialEq)]
pub struct ActivateItem(pub usize);
actions!( actions!(
pane, pane,
[ [
ActivatePrevItem, ActivatePrevItem,
ActivateNextItem, ActivateNextItem,
ActivateLastItem,
CloseActiveItem, CloseActiveItem,
CloseInactiveItems, CloseInactiveItems,
ReopenClosedItem, ReopenClosedItem,
@ -39,9 +43,6 @@ pub struct CloseItem {
pub pane: WeakViewHandle<Pane>, pub pane: WeakViewHandle<Pane>,
} }
#[derive(Clone, Deserialize, PartialEq)]
pub struct ActivateItem(pub usize);
#[derive(Clone, Deserialize, PartialEq)] #[derive(Clone, Deserialize, PartialEq)]
pub struct GoBack { pub struct GoBack {
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
@ -54,8 +55,8 @@ pub struct GoForward {
pub pane: Option<WeakViewHandle<Pane>>, pub pane: Option<WeakViewHandle<Pane>>,
} }
impl_actions!(pane, [GoBack, GoForward]); impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
impl_internal_actions!(pane, [CloseItem, ActivateItem]); impl_internal_actions!(pane, [CloseItem]);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@ -63,6 +64,9 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
pane.activate_item(action.0, true, true, cx); pane.activate_item(action.0, true, true, cx);
}); });
cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
pane.activate_item(pane.items.len() - 1, true, true, cx);
});
cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
pane.activate_prev_item(cx); pane.activate_prev_item(cx);
}); });

View file

@ -55,7 +55,8 @@ impl Into<AnyViewHandle> for &dyn SidebarItemHandle {
pub struct Sidebar { pub struct Sidebar {
side: Side, side: Side,
items: Vec<Item>, items: Vec<Item>,
active_item_ix: Option<usize>, is_open: bool,
active_item_ix: usize,
actual_width: Rc<RefCell<f32>>, actual_width: Rc<RefCell<f32>>,
custom_width: Rc<RefCell<f32>>, custom_width: Rc<RefCell<f32>>,
} }
@ -83,25 +84,41 @@ pub struct ToggleSidebarItem {
pub item_index: usize, pub item_index: usize,
} }
#[derive(Clone, Debug, Deserialize, PartialEq)] impl_actions!(workspace, [ToggleSidebarItem]);
pub struct ToggleSidebarItemFocus {
pub side: Side,
pub item_index: usize,
}
impl_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
impl Sidebar { impl Sidebar {
pub fn new(side: Side) -> Self { pub fn new(side: Side) -> Self {
Self { Self {
side, side,
items: Default::default(), items: Default::default(),
active_item_ix: None, active_item_ix: 0,
is_open: false,
actual_width: Rc::new(RefCell::new(260.)), actual_width: Rc::new(RefCell::new(260.)),
custom_width: Rc::new(RefCell::new(260.)), custom_width: Rc::new(RefCell::new(260.)),
} }
} }
pub fn is_open(&self) -> bool {
self.is_open
}
pub fn active_item_ix(&self) -> usize {
self.active_item_ix
}
pub fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
if open != self.is_open {
self.is_open = open;
cx.notify();
}
}
pub fn toggle_open(&mut self, cx: &mut ViewContext<Self>) {
if self.is_open {}
self.is_open = !self.is_open;
cx.notify();
}
pub fn add_item<T: SidebarItem>( pub fn add_item<T: SidebarItem>(
&mut self, &mut self,
icon_path: &'static str, icon_path: &'static str,
@ -133,23 +150,25 @@ impl Sidebar {
} }
pub fn activate_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) { pub fn activate_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
self.active_item_ix = Some(item_ix); self.active_item_ix = item_ix;
cx.notify(); cx.notify();
} }
pub fn toggle_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) { pub fn toggle_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
if self.active_item_ix == Some(item_ix) { if self.active_item_ix == item_ix {
self.active_item_ix = None; self.is_open = false;
} else { } else {
self.active_item_ix = Some(item_ix); self.active_item_ix = item_ix;
} }
cx.notify(); cx.notify();
} }
pub fn active_item(&self) -> Option<&Rc<dyn SidebarItemHandle>> { pub fn active_item(&self) -> Option<&Rc<dyn SidebarItemHandle>> {
self.active_item_ix if self.is_open {
.and_then(|ix| self.items.get(ix)) self.items.get(self.active_item_ix).map(|item| &item.view)
.map(|item| &item.view) } else {
None
}
} }
fn render_resize_handle(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox { fn render_resize_handle(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
@ -249,6 +268,7 @@ impl View for SidebarButtons {
let item_style = theme.item; let item_style = theme.item;
let badge_style = theme.badge; let badge_style = theme.badge;
let active_ix = sidebar.active_item_ix; let active_ix = sidebar.active_item_ix;
let is_open = sidebar.is_open;
let side = sidebar.side; let side = sidebar.side;
let group_style = match side { let group_style = match side {
Side::Left => theme.group_left, Side::Left => theme.group_left,
@ -267,7 +287,7 @@ impl View for SidebarButtons {
item_index: ix, item_index: ix,
}; };
MouseEventHandler::new::<Self, _, _>(ix, cx, move |state, cx| { MouseEventHandler::new::<Self, _, _>(ix, cx, move |state, cx| {
let is_active = Some(ix) == active_ix; let is_active = is_open && ix == active_ix;
let style = item_style.style_for(state, is_active); let style = item_style.style_for(state, is_active);
Stack::new() Stack::new()
.with_child(Svg::new(icon_path).with_color(style.icon_color).boxed()) .with_child(Svg::new(icon_path).with_color(style.icon_color).boxed())

View file

@ -1,7 +1,4 @@
use crate::{ use crate::{sidebar::Side, AppState, ToggleFollow, Workspace};
sidebar::{Side, ToggleSidebarItem},
AppState, ToggleFollow, Workspace,
};
use anyhow::Result; use anyhow::Result;
use client::{proto, Client, Contact}; use client::{proto, Client, Contact};
use gpui::{ use gpui::{
@ -104,13 +101,7 @@ impl WaitingRoom {
&app_state, &app_state,
cx, cx,
); );
workspace.toggle_sidebar_item( workspace.toggle_sidebar(Side::Left, cx);
&ToggleSidebarItem {
side: Side::Left,
item_index: 0,
},
cx,
);
if let Some((host_peer_id, _)) = if let Some((host_peer_id, _)) =
workspace.project.read(cx).collaborators().iter().find( workspace.project.read(cx).collaborators().iter().find(
|(_, collaborator)| collaborator.replica_id == 0, |(_, collaborator)| collaborator.replica_id == 0,

View file

@ -31,7 +31,7 @@ use postage::prelude::Stream;
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId};
use serde::Deserialize; use serde::Deserialize;
use settings::Settings; use settings::Settings;
use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem, ToggleSidebarItemFocus}; use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem};
use smallvec::SmallVec; use smallvec::SmallVec;
use status_bar::StatusBar; use status_bar::StatusBar;
pub use status_bar::StatusItemView; pub use status_bar::StatusItemView;
@ -90,6 +90,8 @@ actions!(
ActivatePreviousPane, ActivatePreviousPane,
ActivateNextPane, ActivateNextPane,
FollowNextCollaborator, FollowNextCollaborator,
ToggleLeftSidebar,
ToggleRightSidebar,
] ]
); );
@ -104,6 +106,9 @@ pub struct ToggleProjectOnline {
pub project: Option<ModelHandle<Project>>, pub project: Option<ModelHandle<Project>>,
} }
#[derive(Clone, Deserialize, PartialEq)]
pub struct ActivatePane(pub usize);
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct ToggleFollow(pub PeerId); pub struct ToggleFollow(pub PeerId);
@ -122,7 +127,7 @@ impl_internal_actions!(
RemoveWorktreeFromProject RemoveWorktreeFromProject
] ]
); );
impl_actions!(workspace, [ToggleProjectOnline]); impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]);
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) { pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
pane::init(cx); pane::init(cx);
@ -185,7 +190,6 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
}, },
); );
cx.add_action(Workspace::toggle_sidebar_item); cx.add_action(Workspace::toggle_sidebar_item);
cx.add_action(Workspace::toggle_sidebar_item_focus);
cx.add_action(Workspace::focus_center); cx.add_action(Workspace::focus_center);
cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
workspace.activate_previous_pane(cx) workspace.activate_previous_pane(cx)
@ -193,6 +197,13 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
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, _: &ToggleLeftSidebar, cx| {
workspace.toggle_sidebar(Side::Left, cx);
});
cx.add_action(|workspace: &mut Workspace, _: &ToggleRightSidebar, cx| {
workspace.toggle_sidebar(Side::Right, cx);
});
cx.add_action(Workspace::activate_pane_at_index);
let client = &app_state.client; let client = &app_state.client;
client.add_view_request_handler(Workspace::handle_follow); client.add_view_request_handler(Workspace::handle_follow);
@ -1248,17 +1259,39 @@ impl Workspace {
} }
} }
pub fn toggle_sidebar(&mut self, side: Side, cx: &mut ViewContext<Self>) {
let sidebar = match side {
Side::Left => &mut self.left_sidebar,
Side::Right => &mut self.right_sidebar,
};
sidebar.update(cx, |sidebar, cx| {
sidebar.set_open(!sidebar.is_open(), cx);
});
cx.focus_self();
cx.notify();
}
pub fn toggle_sidebar_item(&mut self, action: &ToggleSidebarItem, cx: &mut ViewContext<Self>) { pub fn toggle_sidebar_item(&mut self, action: &ToggleSidebarItem, cx: &mut ViewContext<Self>) {
let sidebar = match action.side { let sidebar = match action.side {
Side::Left => &mut self.left_sidebar, Side::Left => &mut self.left_sidebar,
Side::Right => &mut self.right_sidebar, Side::Right => &mut self.right_sidebar,
}; };
let active_item = sidebar.update(cx, |sidebar, cx| { let active_item = sidebar.update(cx, |sidebar, cx| {
sidebar.toggle_item(action.item_index, cx); if sidebar.is_open() && sidebar.active_item_ix() == action.item_index {
sidebar.active_item().map(|item| item.to_any()) sidebar.set_open(false, cx);
None
} else {
sidebar.set_open(true, cx);
sidebar.activate_item(action.item_index, cx);
sidebar.active_item().cloned()
}
}); });
if let Some(active_item) = active_item { if let Some(active_item) = active_item {
cx.focus(active_item); if active_item.is_focused(cx) {
cx.focus_self();
} else {
cx.focus(active_item.to_any());
}
} else { } else {
cx.focus_self(); cx.focus_self();
} }
@ -1267,15 +1300,17 @@ impl Workspace {
pub fn toggle_sidebar_item_focus( pub fn toggle_sidebar_item_focus(
&mut self, &mut self,
action: &ToggleSidebarItemFocus, side: Side,
item_index: usize,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
let sidebar = match action.side { let sidebar = match side {
Side::Left => &mut self.left_sidebar, Side::Left => &mut self.left_sidebar,
Side::Right => &mut self.right_sidebar, Side::Right => &mut self.right_sidebar,
}; };
let active_item = sidebar.update(cx, |sidebar, cx| { let active_item = sidebar.update(cx, |sidebar, cx| {
sidebar.activate_item(action.item_index, cx); sidebar.set_open(true, cx);
sidebar.activate_item(item_index, cx);
sidebar.active_item().cloned() sidebar.active_item().cloned()
}); });
if let Some(active_item) = active_item { if let Some(active_item) = active_item {
@ -1405,6 +1440,15 @@ impl Workspace {
} }
} }
fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext<Self>) {
let panes = self.center.panes();
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
self.activate_pane(pane, cx);
} else {
self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx);
}
}
pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) { pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
let next_pane = { let next_pane = {
let panes = self.center.panes(); let panes = self.center.panes();
@ -2481,13 +2525,7 @@ pub fn open_paths(
let mut workspace = Workspace::new(project, cx); let mut workspace = Workspace::new(project, cx);
(app_state.initialize_workspace)(&mut workspace, &app_state, cx); (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
if contains_directory { if contains_directory {
workspace.toggle_sidebar_item( workspace.toggle_sidebar(Side::Left, cx);
&ToggleSidebarItem {
side: Side::Left,
item_index: 0,
},
cx,
);
} }
workspace workspace
}) })

View file

@ -187,11 +187,42 @@ pub fn menus() -> Vec<Menu<'static>> {
}, },
MenuItem::Separator, MenuItem::Separator,
MenuItem::Action { MenuItem::Action {
name: "Project Browser", name: "Toggle Left Sidebar",
action: Box::new(workspace::sidebar::ToggleSidebarItemFocus { action: Box::new(workspace::ToggleLeftSidebar),
side: workspace::sidebar::Side::Left, },
item_index: 0, MenuItem::Action {
name: "Toggle Right Sidebar",
action: Box::new(workspace::ToggleRightSidebar),
},
MenuItem::Submenu(Menu {
name: "Editor Layout",
items: vec![
MenuItem::Action {
name: "Split Up",
action: Box::new(workspace::SplitUp),
},
MenuItem::Action {
name: "Split Down",
action: Box::new(workspace::SplitDown),
},
MenuItem::Action {
name: "Split Left",
action: Box::new(workspace::SplitLeft),
},
MenuItem::Action {
name: "Split Right",
action: Box::new(workspace::SplitRight),
},
],
}), }),
MenuItem::Separator,
MenuItem::Action {
name: "Project Panel",
action: Box::new(project_panel::Toggle),
},
MenuItem::Action {
name: "Contacts Panel",
action: Box::new(contacts_panel::Toggle),
}, },
MenuItem::Action { MenuItem::Action {
name: "Command Palette", name: "Command Palette",

View file

@ -34,7 +34,7 @@ use std::{
}; };
use util::ResultExt; use util::ResultExt;
pub use workspace; pub use workspace;
use workspace::{AppState, Workspace}; use workspace::{sidebar::Side, AppState, Workspace};
#[derive(Deserialize, Clone, PartialEq)] #[derive(Deserialize, Clone, PartialEq)]
struct OpenBrowser { struct OpenBrowser {
@ -128,6 +128,16 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
} }
}, },
); );
cx.add_action(
|workspace: &mut Workspace, _: &project_panel::Toggle, cx: &mut ViewContext<Workspace>| {
workspace.toggle_sidebar_item_focus(Side::Left, 0, cx);
},
);
cx.add_action(
|workspace: &mut Workspace, _: &contacts_panel::Toggle, cx: &mut ViewContext<Workspace>| {
workspace.toggle_sidebar_item_focus(Side::Right, 0, cx);
},
);
lsp_status::init(cx); lsp_status::init(cx);
settings::KeymapFileContent::load_defaults(cx); settings::KeymapFileContent::load_defaults(cx);
@ -429,7 +439,7 @@ mod tests {
let workspace_1 = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap(); let workspace_1 = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap();
workspace_1.update(cx, |workspace, cx| { workspace_1.update(cx, |workspace, cx| {
assert_eq!(workspace.worktrees(cx).count(), 2); assert_eq!(workspace.worktrees(cx).count(), 2);
assert!(workspace.left_sidebar().read(cx).active_item().is_some()); assert!(workspace.left_sidebar().read(cx).is_open());
assert!(workspace.active_pane().is_focused(cx)); assert!(workspace.active_pane().is_focused(cx));
}); });