feat: add focus attribute in layout (#958)

* feat(layout): add focus attribute in layout

* feat: add state of focus to tab

* chore: i love clippy

* test(layout): update focus options

* feat: add focus pane

* feat: apply focus-pane when layout is only loaded

* change the instruction name for focus-on-tab

* chore: apply cargo-fmt

* test: add e2e testcase
This commit is contained in:
Jae-Heon Ji 2022-02-01 02:19:21 +09:00 committed by GitHub
parent 1163189d6b
commit 1d2e303926
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 300 additions and 22 deletions

View file

@ -1663,3 +1663,40 @@ pub fn bracketed_paste() {
};
assert_snapshot!(last_snapshot);
}
#[test]
#[ignore]
pub fn focus_tab_with_layout() {
let fake_win_size = Size {
cols: 120,
rows: 24,
};
let layout_file_name = "focus-tab-layout.yaml";
let mut test_attempts = 10;
let last_snapshot = loop {
RemoteRunner::kill_running_sessions(fake_win_size);
let mut runner = RemoteRunner::new_with_layout(fake_win_size, layout_file_name);
runner.run_all_steps();
let last_snapshot = runner.take_snapshot_after(Step {
name: "Wait for app to load",
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.status_bar_appears()
&& remote_terminal.tip_appears()
&& remote_terminal.snapshot_contains("Tab #3")
&& remote_terminal.cursor_position_is(63, 2)
{
step_is_complete = true;
}
step_is_complete
},
});
if runner.test_timed_out && test_attempts > 0 {
test_attempts -= 1;
continue;
} else {
break last_snapshot;
}
};
assert_snapshot!(last_snapshot);
}

View file

@ -0,0 +1,29 @@
---
source: src/tests/e2e/cases.rs
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1  Tab #2  Tab #3  Tab #4  Tab #5  Tab #6  Tab #7  Tab #8  Tab #9 
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
│$ ││$ █ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SCROLL  <o> SESSION  <q> QUIT 
Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane.

View file

@ -0,0 +1,92 @@
---
template:
direction: Horizontal
parts:
- direction: Vertical
split_size:
Fixed: 1
run:
plugin:
location: "zellij:tab-bar"
borderless: true
- direction: Vertical
body: true
- direction: Vertical
split_size:
Fixed: 2
run:
plugin:
location: "zellij:status-bar"
borderless: true
tabs:
- direction: Vertical
parts:
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
- direction: Vertical
focus: true
parts:
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
focus: true
split_size:
Percent: 50
- direction: Vertical
parts:
- direction: Vertical
split_size:
Percent: 50
- direction: Horizontal
split_size:
Percent: 50
parts:
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
- direction: Vertical
- direction: Vertical
- direction: Vertical
parts:
- direction: Vertical
split_size:
Percent: 20
run:
plugin:
location: "zellij:strider"
- direction: Horizontal
split_size:
Percent: 80
parts:
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
parts:
- direction: Vertical
split_size:
Percent: 40
- direction: Horizontal
split_size:
Percent: 60
parts:
- direction: Vertical
split_size:
Percent: 50
- direction: Vertical
split_size:
Percent: 50

View file

@ -313,9 +313,25 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
};
if !&layout.tabs.is_empty() {
for tab_layout in layout.tabs {
for tab_layout in layout.clone().tabs {
spawn_tabs(Some(tab_layout.clone()));
}
let focused_tab = layout
.tabs
.into_iter()
.enumerate()
.find(|(_, tab_layout)| tab_layout.focus.unwrap_or(false));
if let Some((tab_index, _)) = focused_tab {
session_data
.read()
.unwrap()
.as_ref()
.unwrap()
.senders
.send_to_pty(PtyInstruction::GoToTab((tab_index + 1) as u32, client_id))
.unwrap();
}
} else {
spawn_tabs(None);
}

View file

@ -29,6 +29,7 @@ use zellij_utils::{
};
pub type VteBytes = Vec<u8>;
pub type TabIndex = u32;
#[derive(Clone, Copy, Debug)]
pub enum ClientOrTabIndex {
@ -43,6 +44,7 @@ pub(crate) enum PtyInstruction {
SpawnTerminalVertically(Option<TerminalAction>, ClientId),
SpawnTerminalHorizontally(Option<TerminalAction>, ClientId),
UpdateActivePane(Option<PaneId>, ClientId),
GoToTab(TabIndex, ClientId),
NewTab(Option<TerminalAction>, Option<TabLayout>, ClientId),
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
@ -56,6 +58,7 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically,
PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane,
PtyInstruction::GoToTab(..) => PtyContext::GoToTab,
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab(..) => PtyContext::NewTab,
@ -114,6 +117,12 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) {
PtyInstruction::UpdateActivePane(pane_id, client_id) => {
pty.set_active_pane(pane_id, client_id);
}
PtyInstruction::GoToTab(tab_index, client_id) => {
pty.bus
.senders
.send_to_screen(ScreenInstruction::GoToTab(tab_index, Some(client_id)))
.unwrap();
}
PtyInstruction::NewTab(terminal_action, tab_layout, client_id) => {
let tab_name = tab_layout.as_ref().and_then(|layout| {
if layout.name.is_empty() {

View file

@ -122,6 +122,9 @@ pub(crate) struct Tab {
pending_vte_events: HashMap<RawFd, Vec<VteBytes>>,
selecting_with_mouse: bool,
copy_command: Option<String>,
// TODO: used only to focus the pane when the layout is loaded
// it seems that optimization is possible using `active_panes`
focus_pane_id: Option<PaneId>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
@ -340,6 +343,7 @@ impl Tab {
connected_clients,
selecting_with_mouse: false,
copy_command,
focus_pane_id: None,
}
}
@ -374,6 +378,13 @@ impl Tab {
}
let mut new_pids = new_pids.iter();
let mut focus_pane_id: Option<PaneId> = None;
let mut set_focus_pane_id = |layout: &Layout, pane_id: PaneId| {
if layout.focus.unwrap_or(false) && focus_pane_id.is_none() {
focus_pane_id = Some(pane_id);
}
};
for (layout, position_and_size) in positions_and_size {
// A plugin pane
if let Some(Run::Plugin(run)) = layout.run.clone() {
@ -392,6 +403,7 @@ impl Tab {
);
new_plugin.set_borderless(layout.borderless);
self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin));
set_focus_pane_id(layout, PaneId::Plugin(pid));
} else {
// there are still panes left to fill, use the pids we received in this method
let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout
@ -406,6 +418,7 @@ impl Tab {
new_pane.set_borderless(layout.borderless);
self.panes
.insert(PaneId::Terminal(*pid), Box::new(new_pane));
set_focus_pane_id(layout, PaneId::Terminal(*pid));
}
}
for unused_pid in new_pids {
@ -435,24 +448,33 @@ impl Tab {
self.offset_viewport(&geom)
}
self.set_pane_frames(self.draw_pane_frames);
// This is the end of the nasty viewport hack...
let next_selectable_pane_id = self
.panes
.iter()
.filter(|(_id, pane)| pane.selectable())
.map(|(id, _)| id.to_owned())
.next();
match next_selectable_pane_id {
Some(active_pane_id) => {
let connected_clients: Vec<ClientId> =
self.connected_clients.iter().copied().collect();
for client_id in connected_clients {
self.active_panes.insert(client_id, active_pane_id);
}
let mut active_pane = |pane_id: PaneId| {
let connected_clients: Vec<ClientId> = self.connected_clients.iter().copied().collect();
for client_id in connected_clients {
self.active_panes.insert(client_id, pane_id);
}
None => {
// this is very likely a configuration error (layout with no selectable panes)
self.active_panes.clear();
};
if let Some(pane_id) = focus_pane_id {
self.focus_pane_id = Some(pane_id);
active_pane(pane_id);
} else {
// This is the end of the nasty viewport hack...
let next_selectable_pane_id = self
.panes
.iter()
.filter(|(_id, pane)| pane.selectable())
.map(|(id, _)| id.to_owned())
.next();
match next_selectable_pane_id {
Some(active_pane_id) => {
active_pane(active_pane_id);
}
None => {
// this is very likely a configuration error (layout with no selectable panes)
self.active_panes.clear();
}
}
}
}
@ -489,11 +511,15 @@ impl Tab {
// no panes here, bye bye
return;
}
pane_ids.sort(); // TODO: make this predictable
pane_ids.retain(|p| !self.panes_to_hide.contains(p));
let first_pane_id = pane_ids.get(0).unwrap();
self.active_panes.insert(
client_id,
self.focus_pane_id.unwrap_or_else(|| {
pane_ids.sort(); // TODO: make this predictable
pane_ids.retain(|p| !self.panes_to_hide.contains(p));
*pane_ids.get(0).unwrap()
}),
);
self.connected_clients.insert(client_id);
self.active_panes.insert(client_id, *first_pane_id);
self.mode_info.insert(
client_id,
mode_info.unwrap_or_else(|| self.default_mode_info.clone()),
@ -515,6 +541,7 @@ impl Tab {
}
}
pub fn remove_client(&mut self, client_id: ClientId) {
self.focus_pane_id = None;
self.connected_clients.remove(&client_id);
self.set_force_render();
}

View file

@ -281,6 +281,7 @@ pub enum PtyContext {
SpawnTerminalVertically,
SpawnTerminalHorizontally,
UpdateActivePane,
GoToTab,
NewTab,
ClosePane,
CloseTab,

View file

@ -140,6 +140,7 @@ pub struct Layout {
pub run: Option<Run>,
#[serde(default)]
pub borderless: bool,
pub focus: Option<bool>,
}
// The struct that is used to deserialize the layout from
@ -421,6 +422,7 @@ pub struct LayoutTemplate {
#[serde(default)]
pub body: bool,
pub split_size: Option<SplitSize>,
pub focus: Option<bool>,
pub run: Option<RunFromYaml>,
}
@ -466,6 +468,7 @@ pub struct TabLayout {
pub split_size: Option<SplitSize>,
#[serde(default)]
pub name: String,
pub focus: Option<bool>,
pub run: Option<RunFromYaml>,
}
@ -712,6 +715,7 @@ impl TryFrom<TabLayout> for Layout {
borderless: tab.borderless,
parts: Self::from_vec_tab_layout(tab.parts)?,
split_size: tab.split_size,
focus: tab.focus,
run: tab.run.map(Run::try_from).transpose()?,
})
}
@ -726,6 +730,7 @@ impl From<TabLayout> for LayoutTemplate {
parts: Self::from_vec_tab_layout(tab.parts),
body: false,
split_size: tab.split_size,
focus: tab.focus,
run: tab.run,
}
}
@ -741,6 +746,7 @@ impl TryFrom<LayoutTemplate> for Layout {
borderless: template.borderless,
parts: Self::from_vec_template_layout(template.parts)?,
split_size: template.split_size,
focus: template.focus,
run: template
.run
.map(Run::try_from)
@ -761,6 +767,7 @@ impl Default for TabLayout {
run: None,
name: String::new(),
pane_name: None,
focus: None,
}
}
}
@ -778,10 +785,12 @@ impl Default for LayoutTemplate {
body: true,
borderless: false,
split_size: None,
focus: None,
run: None,
parts: vec![],
}],
split_size: None,
focus: None,
run: None,
}
}

View file

@ -41,11 +41,13 @@ fn default_layout_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: true,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(1)),
run: Some(Run::Plugin(RunPlugin {
@ -57,6 +59,7 @@ fn default_layout_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -65,6 +68,7 @@ fn default_layout_merged_correctly() {
direction: Direction::Vertical,
borderless: true,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(2)),
run: Some(Run::Plugin(RunPlugin {
@ -89,11 +93,13 @@ fn default_layout_new_tab_correct() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: true,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(1)),
run: Some(Run::Plugin(RunPlugin {
@ -105,6 +111,7 @@ fn default_layout_new_tab_correct() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -113,6 +120,7 @@ fn default_layout_new_tab_correct() {
direction: Direction::Vertical,
borderless: true,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(2)),
run: Some(Run::Plugin(RunPlugin {
@ -177,15 +185,18 @@ fn three_panes_with_tab_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -194,11 +205,13 @@ fn three_panes_with_tab_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -207,6 +220,7 @@ fn three_panes_with_tab_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -235,10 +249,12 @@ fn three_panes_with_tab_new_tab_is_correct() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -277,11 +293,13 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(1)),
run: Some(Run::Plugin(RunPlugin {
@ -293,11 +311,13 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -306,11 +326,13 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -319,6 +341,7 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -335,6 +358,7 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(2)),
run: Some(Run::Plugin(RunPlugin {
@ -359,11 +383,13 @@ fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(1)),
run: Some(Run::Plugin(RunPlugin {
@ -375,6 +401,7 @@ fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -383,6 +410,7 @@ fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Fixed(2)),
run: Some(Run::Plugin(RunPlugin {
@ -425,16 +453,19 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(21.0)),
run: None,
@ -443,11 +474,13 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(22.0)),
run: None,
@ -456,11 +489,13 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(23.0)),
run: None,
@ -469,6 +504,7 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(24.0)),
run: None,
@ -489,6 +525,7 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(15.0)),
run: None,
@ -497,6 +534,7 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(15.0)),
run: None,
@ -505,6 +543,7 @@ fn deeply_nested_tab_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(15.0)),
run: None,
@ -544,11 +583,13 @@ fn three_tabs_tab_one_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -557,6 +598,7 @@ fn three_tabs_tab_one_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -582,16 +624,19 @@ fn three_tabs_tab_two_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -600,6 +645,7 @@ fn three_tabs_tab_two_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -612,6 +658,7 @@ fn three_tabs_tab_two_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -637,16 +684,19 @@ fn three_tabs_tab_three_merged_correctly() {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![
Layout {
direction: Direction::Vertical,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: Some(SplitSize::Percent(50.0)),
run: None,
@ -655,6 +705,7 @@ fn three_tabs_tab_three_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -667,6 +718,7 @@ fn three_tabs_tab_three_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -703,10 +755,12 @@ fn no_tabs_merged_correctly() {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![Layout {
direction: Direction::Horizontal,
borderless: false,
pane_name: None,
focus: None,
parts: vec![],
split_size: None,
run: None,
@ -754,6 +808,7 @@ fn no_layout_template_merged_correctly() {
run: None,
borderless: false,
pane_name: None,
focus: None,
},
Layout {
direction: Direction::Horizontal,
@ -762,17 +817,20 @@ fn no_layout_template_merged_correctly() {
run: None,
borderless: false,
pane_name: None,
focus: None,
},
],
split_size: None,
run: None,
borderless: false,
pane_name: None,
focus: None,
}],
split_size: None,
run: None,
borderless: false,
pane_name: None,
focus: None,
};
assert_eq!(merged_layout, tab_layout.try_into().unwrap());