Floating panes in layouts (#2047)
* work * tests passing * tests: floating panes in layouts * panes(plugins): floating plugins working * refactor(tab): layout applier * style(comment): remove outdated * style(fmt): rustfmt
This commit is contained in:
parent
17205793e4
commit
799fa5de8d
68 changed files with 2051 additions and 576 deletions
|
|
@ -179,7 +179,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
(s("Move focus"), s("Move"), focus_keys),
|
(s("Move focus"), s("Move"), focus_keys),
|
||||||
(s("New"), s("New"), action_key(&km, &[A::NewTab(None, None), TO_NORMAL])),
|
(s("New"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None), TO_NORMAL])),
|
||||||
(s("Close"), s("Close"), action_key(&km, &[A::CloseTab, TO_NORMAL])),
|
(s("Close"), s("Close"), action_key(&km, &[A::CloseTab, TO_NORMAL])),
|
||||||
(s("Rename"), s("Rename"),
|
(s("Rename"), s("Rename"),
|
||||||
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
|
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
|
||||||
|
|
@ -253,7 +253,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
||||||
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])),
|
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])),
|
||||||
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])),
|
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])),
|
||||||
(s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
|
(s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
|
||||||
(s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, None), TO_NORMAL])),
|
(s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None), TO_NORMAL])),
|
||||||
(s("Rename tab"), s("Rename"),
|
(s("Rename tab"), s("Rename"),
|
||||||
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
|
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
|
||||||
(s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
|
(s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let spawn_tabs = |tab_layout, tab_name| {
|
let spawn_tabs = |tab_layout, floating_panes_layout, tab_name| {
|
||||||
session_data
|
session_data
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -342,6 +342,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
.send_to_screen(ScreenInstruction::NewTab(
|
.send_to_screen(ScreenInstruction::NewTab(
|
||||||
default_shell.clone(),
|
default_shell.clone(),
|
||||||
tab_layout,
|
tab_layout,
|
||||||
|
floating_panes_layout,
|
||||||
tab_name,
|
tab_name,
|
||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
|
|
@ -349,8 +350,12 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if layout.has_tabs() {
|
if layout.has_tabs() {
|
||||||
for (tab_name, tab_layout) in layout.tabs() {
|
for (tab_name, tab_layout, floating_panes_layout) in layout.tabs() {
|
||||||
spawn_tabs(Some(tab_layout.clone()), tab_name);
|
spawn_tabs(
|
||||||
|
Some(tab_layout.clone()),
|
||||||
|
floating_panes_layout.clone(),
|
||||||
|
tab_name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(focused_tab_index) = layout.focused_tab_index() {
|
if let Some(focused_tab_index) = layout.focused_tab_index() {
|
||||||
|
|
@ -367,7 +372,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
spawn_tabs(None, None);
|
spawn_tabs(None, layout.floating_panes_template.clone(), None);
|
||||||
}
|
}
|
||||||
session_data
|
session_data
|
||||||
.read()
|
.read()
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ use zellij_utils::{
|
||||||
data::{ModeInfo, Style},
|
data::{ModeInfo, Style},
|
||||||
errors::prelude::*,
|
errors::prelude::*,
|
||||||
input::command::RunCommand,
|
input::command::RunCommand,
|
||||||
pane_size::{Offset, PaneGeom, Size, Viewport},
|
input::layout::FloatingPanesLayout,
|
||||||
|
pane_size::{Dimension, Offset, PaneGeom, Size, Viewport},
|
||||||
};
|
};
|
||||||
|
|
||||||
const RESIZE_INCREMENT_WIDTH: usize = 5;
|
const RESIZE_INCREMENT_WIDTH: usize = 5;
|
||||||
|
|
@ -224,9 +225,55 @@ impl FloatingPanes {
|
||||||
);
|
);
|
||||||
floating_pane_grid.find_room_for_new_pane()
|
floating_pane_grid.find_room_for_new_pane()
|
||||||
}
|
}
|
||||||
|
pub fn position_floating_pane_layout(
|
||||||
|
&mut self,
|
||||||
|
floating_pane_layout: &FloatingPanesLayout,
|
||||||
|
) -> PaneGeom {
|
||||||
|
let display_area = *self.display_area.borrow();
|
||||||
|
let viewport = *self.viewport.borrow();
|
||||||
|
let floating_pane_grid = FloatingPaneGrid::new(
|
||||||
|
&mut self.panes,
|
||||||
|
&mut self.desired_pane_positions,
|
||||||
|
display_area,
|
||||||
|
viewport,
|
||||||
|
);
|
||||||
|
let mut position = floating_pane_grid.find_room_for_new_pane().unwrap(); // TODO: no unwrap
|
||||||
|
if let Some(x) = &floating_pane_layout.x {
|
||||||
|
position.x = x.to_position(display_area.cols);
|
||||||
|
}
|
||||||
|
if let Some(y) = &floating_pane_layout.y {
|
||||||
|
position.y = y.to_position(display_area.rows);
|
||||||
|
}
|
||||||
|
if let Some(width) = &floating_pane_layout.width {
|
||||||
|
position.cols = Dimension::fixed(width.to_position(display_area.cols));
|
||||||
|
}
|
||||||
|
if let Some(height) = &floating_pane_layout.height {
|
||||||
|
position.rows = Dimension::fixed(height.to_position(display_area.rows));
|
||||||
|
}
|
||||||
|
if position.cols.as_usize() > display_area.cols {
|
||||||
|
position.cols = Dimension::fixed(display_area.cols);
|
||||||
|
}
|
||||||
|
if position.rows.as_usize() > display_area.rows {
|
||||||
|
position.rows = Dimension::fixed(display_area.rows);
|
||||||
|
}
|
||||||
|
if position.x + position.cols.as_usize() > display_area.cols {
|
||||||
|
position.x = position
|
||||||
|
.x
|
||||||
|
.saturating_sub((position.x + position.cols.as_usize()) - display_area.cols);
|
||||||
|
}
|
||||||
|
if position.y + position.rows.as_usize() > display_area.rows {
|
||||||
|
position.y = position
|
||||||
|
.y
|
||||||
|
.saturating_sub((position.y + position.rows.as_usize()) - display_area.rows);
|
||||||
|
}
|
||||||
|
position
|
||||||
|
}
|
||||||
pub fn first_floating_pane_id(&self) -> Option<PaneId> {
|
pub fn first_floating_pane_id(&self) -> Option<PaneId> {
|
||||||
self.panes.keys().next().copied()
|
self.panes.keys().next().copied()
|
||||||
}
|
}
|
||||||
|
pub fn last_floating_pane_id(&self) -> Option<PaneId> {
|
||||||
|
self.panes.keys().last().copied()
|
||||||
|
}
|
||||||
pub fn first_active_floating_pane_id(&self) -> Option<PaneId> {
|
pub fn first_active_floating_pane_id(&self) -> Option<PaneId> {
|
||||||
self.active_panes.values().next().copied()
|
self.active_panes.values().next().copied()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -539,10 +539,13 @@ impl TiledPanes {
|
||||||
viewport.cols = (viewport.cols as isize + column_difference) as usize;
|
viewport.cols = (viewport.cols as isize + column_difference) as usize;
|
||||||
display_area.cols = cols;
|
display_area.cols = cols;
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => match e.downcast_ref::<ZellijError>() {
|
||||||
Err::<(), _>(anyError::msg(e))
|
Some(ZellijError::PaneSizeUnchanged) => {}, // ignore unchanged layout
|
||||||
.context("failed to resize tab horizontally")
|
_ => {
|
||||||
.non_fatal();
|
Err::<(), _>(anyError::msg(e))
|
||||||
|
.context("failed to resize tab horizontally")
|
||||||
|
.non_fatal();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
match pane_grid.layout(SplitDirection::Vertical, rows) {
|
match pane_grid.layout(SplitDirection::Vertical, rows) {
|
||||||
|
|
@ -551,10 +554,13 @@ impl TiledPanes {
|
||||||
viewport.rows = (viewport.rows as isize + row_difference) as usize;
|
viewport.rows = (viewport.rows as isize + row_difference) as usize;
|
||||||
display_area.rows = rows;
|
display_area.rows = rows;
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => match e.downcast_ref::<ZellijError>() {
|
||||||
Err::<(), _>(anyError::msg(e))
|
Some(ZellijError::PaneSizeUnchanged) => {}, // ignore unchanged layout
|
||||||
.context("failed to resize tab vertically")
|
_ => {
|
||||||
.non_fatal();
|
Err::<(), _>(anyError::msg(e))
|
||||||
|
.context("failed to resize tab vertically")
|
||||||
|
.non_fatal();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use zellij_utils::{
|
||||||
errors::{prelude::*, ContextType, PluginContext},
|
errors::{prelude::*, ContextType, PluginContext},
|
||||||
input::{
|
input::{
|
||||||
command::TerminalAction,
|
command::TerminalAction,
|
||||||
layout::{Layout, PaneLayout, Run, RunPlugin, RunPluginLocation},
|
layout::{FloatingPanesLayout, Layout, PaneLayout, Run, RunPlugin, RunPluginLocation},
|
||||||
plugins::PluginsConfig,
|
plugins::PluginsConfig,
|
||||||
},
|
},
|
||||||
pane_size::Size,
|
pane_size::Size,
|
||||||
|
|
@ -29,6 +29,7 @@ pub enum PluginInstruction {
|
||||||
NewTab(
|
NewTab(
|
||||||
Option<TerminalAction>,
|
Option<TerminalAction>,
|
||||||
Option<PaneLayout>,
|
Option<PaneLayout>,
|
||||||
|
Vec<FloatingPanesLayout>,
|
||||||
Option<String>, // tab name
|
Option<String>, // tab name
|
||||||
usize, // tab_index
|
usize, // tab_index
|
||||||
ClientId,
|
ClientId,
|
||||||
|
|
@ -69,7 +70,6 @@ pub(crate) fn plugin_thread_main(
|
||||||
let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
|
let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
|
||||||
err_ctx.add_call(ContextType::Plugin((&event).into()));
|
err_ctx.add_call(ContextType::Plugin((&event).into()));
|
||||||
match event {
|
match event {
|
||||||
// TODO: remove pid_tx from here
|
|
||||||
PluginInstruction::Load(run, tab_index, client_id, size) => {
|
PluginInstruction::Load(run, tab_index, client_id, size) => {
|
||||||
wasm_bridge.load_plugin(&run, tab_index, size, client_id)?;
|
wasm_bridge.load_plugin(&run, tab_index, size, client_id)?;
|
||||||
},
|
},
|
||||||
|
|
@ -91,16 +91,22 @@ pub(crate) fn plugin_thread_main(
|
||||||
PluginInstruction::NewTab(
|
PluginInstruction::NewTab(
|
||||||
terminal_action,
|
terminal_action,
|
||||||
tab_layout,
|
tab_layout,
|
||||||
|
floating_panes_layout,
|
||||||
tab_name,
|
tab_name,
|
||||||
tab_index,
|
tab_index,
|
||||||
client_id,
|
client_id,
|
||||||
) => {
|
) => {
|
||||||
let mut plugin_ids: HashMap<RunPluginLocation, Vec<u32>> = HashMap::new();
|
let mut plugin_ids: HashMap<RunPluginLocation, Vec<u32>> = HashMap::new();
|
||||||
let extracted_run_instructions = tab_layout
|
let mut extracted_run_instructions = tab_layout
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| layout.new_tab())
|
.unwrap_or_else(|| layout.new_tab().0)
|
||||||
.extract_run_instructions();
|
.extract_run_instructions();
|
||||||
let size = Size::default(); // TODO: is this bad?
|
let size = Size::default();
|
||||||
|
let mut extracted_floating_plugins: Vec<Option<Run>> = floating_panes_layout
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.run.clone())
|
||||||
|
.collect();
|
||||||
|
extracted_run_instructions.append(&mut extracted_floating_plugins);
|
||||||
for run_instruction in extracted_run_instructions {
|
for run_instruction in extracted_run_instructions {
|
||||||
if let Some(Run::Plugin(run)) = run_instruction {
|
if let Some(Run::Plugin(run)) = run_instruction {
|
||||||
let plugin_id =
|
let plugin_id =
|
||||||
|
|
@ -111,6 +117,7 @@ pub(crate) fn plugin_thread_main(
|
||||||
drop(bus.senders.send_to_pty(PtyInstruction::NewTab(
|
drop(bus.senders.send_to_pty(PtyInstruction::NewTab(
|
||||||
terminal_action,
|
terminal_action,
|
||||||
tab_layout,
|
tab_layout,
|
||||||
|
floating_panes_layout,
|
||||||
tab_name,
|
tab_name,
|
||||||
tab_index,
|
tab_index,
|
||||||
plugin_ids,
|
plugin_ids,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use zellij_utils::{
|
||||||
errors::{ContextType, PtyContext},
|
errors::{ContextType, PtyContext},
|
||||||
input::{
|
input::{
|
||||||
command::{RunCommand, TerminalAction},
|
command::{RunCommand, TerminalAction},
|
||||||
layout::{Layout, PaneLayout, Run, RunPluginLocation},
|
layout::{FloatingPanesLayout, Layout, PaneLayout, Run, RunPluginLocation},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -50,6 +50,7 @@ pub enum PtyInstruction {
|
||||||
NewTab(
|
NewTab(
|
||||||
Option<TerminalAction>,
|
Option<TerminalAction>,
|
||||||
Option<PaneLayout>,
|
Option<PaneLayout>,
|
||||||
|
Vec<FloatingPanesLayout>,
|
||||||
Option<String>,
|
Option<String>,
|
||||||
usize, // tab_index
|
usize, // tab_index
|
||||||
HashMap<RunPluginLocation, Vec<u32>>, // plugin_ids
|
HashMap<RunPluginLocation, Vec<u32>>, // plugin_ids
|
||||||
|
|
@ -335,6 +336,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
||||||
PtyInstruction::NewTab(
|
PtyInstruction::NewTab(
|
||||||
terminal_action,
|
terminal_action,
|
||||||
tab_layout,
|
tab_layout,
|
||||||
|
floating_panes_layout,
|
||||||
tab_name,
|
tab_name,
|
||||||
tab_index,
|
tab_index,
|
||||||
plugin_ids,
|
plugin_ids,
|
||||||
|
|
@ -342,8 +344,14 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
||||||
) => {
|
) => {
|
||||||
let err_context = || format!("failed to open new tab for client {}", client_id);
|
let err_context = || format!("failed to open new tab for client {}", client_id);
|
||||||
|
|
||||||
|
let floating_panes_layout = if floating_panes_layout.is_empty() {
|
||||||
|
layout.new_tab().1
|
||||||
|
} else {
|
||||||
|
floating_panes_layout
|
||||||
|
};
|
||||||
pty.spawn_terminals_for_layout(
|
pty.spawn_terminals_for_layout(
|
||||||
tab_layout.unwrap_or_else(|| layout.new_tab()),
|
tab_layout.unwrap_or_else(|| layout.new_tab().0),
|
||||||
|
floating_panes_layout,
|
||||||
terminal_action.clone(),
|
terminal_action.clone(),
|
||||||
plugin_ids,
|
plugin_ids,
|
||||||
tab_index,
|
tab_index,
|
||||||
|
|
@ -562,6 +570,7 @@ impl Pty {
|
||||||
pub fn spawn_terminals_for_layout(
|
pub fn spawn_terminals_for_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: PaneLayout,
|
layout: PaneLayout,
|
||||||
|
floating_panes_layout: Vec<FloatingPanesLayout>,
|
||||||
default_shell: Option<TerminalAction>,
|
default_shell: Option<TerminalAction>,
|
||||||
plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
tab_index: usize,
|
tab_index: usize,
|
||||||
|
|
@ -572,176 +581,28 @@ impl Pty {
|
||||||
let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None));
|
let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None));
|
||||||
self.fill_cwd(&mut default_shell, client_id);
|
self.fill_cwd(&mut default_shell, client_id);
|
||||||
let extracted_run_instructions = layout.extract_run_instructions();
|
let extracted_run_instructions = layout.extract_run_instructions();
|
||||||
|
let extracted_floating_run_instructions =
|
||||||
|
floating_panes_layout.iter().map(|f| f.run.clone());
|
||||||
let mut new_pane_pids: Vec<(u32, bool, Option<RunCommand>, Result<RawFd>)> = vec![]; // (terminal_id,
|
let mut new_pane_pids: Vec<(u32, bool, Option<RunCommand>, Result<RawFd>)> = vec![]; // (terminal_id,
|
||||||
// starts_held,
|
// starts_held,
|
||||||
// run_command,
|
// run_command,
|
||||||
// file_descriptor)
|
// file_descriptor)
|
||||||
|
let mut new_floating_panes_pids: Vec<(u32, bool, Option<RunCommand>, Result<RawFd>)> =
|
||||||
|
vec![]; // same
|
||||||
|
// as
|
||||||
|
// new_pane_pids
|
||||||
for run_instruction in extracted_run_instructions {
|
for run_instruction in extracted_run_instructions {
|
||||||
let quit_cb = Box::new({
|
if let Some(new_pane_data) =
|
||||||
let senders = self.bus.senders.clone();
|
self.apply_run_instruction(run_instruction, default_shell.clone())?
|
||||||
move |pane_id, _exit_status, _command| {
|
{
|
||||||
let _ = senders.send_to_screen(ScreenInstruction::ClosePane(pane_id, None));
|
new_pane_pids.push(new_pane_data);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
match run_instruction {
|
for run_instruction in extracted_floating_run_instructions {
|
||||||
Some(Run::Command(command)) => {
|
if let Some(new_pane_data) =
|
||||||
let starts_held = command.hold_on_start;
|
self.apply_run_instruction(run_instruction, default_shell.clone())?
|
||||||
let hold_on_close = command.hold_on_close;
|
{
|
||||||
let quit_cb = Box::new({
|
new_floating_panes_pids.push(new_pane_data);
|
||||||
let senders = self.bus.senders.clone();
|
|
||||||
move |pane_id, exit_status, command| {
|
|
||||||
if hold_on_close {
|
|
||||||
let _ = senders.send_to_screen(ScreenInstruction::HoldPane(
|
|
||||||
pane_id,
|
|
||||||
exit_status,
|
|
||||||
command,
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let _ = senders
|
|
||||||
.send_to_screen(ScreenInstruction::ClosePane(pane_id, None));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let cmd = TerminalAction::RunCommand(command.clone());
|
|
||||||
if starts_held {
|
|
||||||
// we don't actually open a terminal in this case, just wait for the user to run it
|
|
||||||
match self
|
|
||||||
.bus
|
|
||||||
.os_input
|
|
||||||
.as_mut()
|
|
||||||
.context("no OS I/O interface found")
|
|
||||||
.with_context(err_context)?
|
|
||||||
.reserve_terminal_id()
|
|
||||||
{
|
|
||||||
Ok(terminal_id) => {
|
|
||||||
new_pane_pids.push((
|
|
||||||
terminal_id,
|
|
||||||
starts_held,
|
|
||||||
Some(command.clone()),
|
|
||||||
Ok(terminal_id as i32), // this is not actually correct but gets
|
|
||||||
// stripped later
|
|
||||||
));
|
|
||||||
},
|
|
||||||
Err(e) => Err::<(), _>(e).with_context(err_context).non_fatal(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match self
|
|
||||||
.bus
|
|
||||||
.os_input
|
|
||||||
.as_mut()
|
|
||||||
.context("no OS I/O interface found")
|
|
||||||
.with_context(err_context)?
|
|
||||||
.spawn_terminal(cmd, quit_cb, self.default_editor.clone())
|
|
||||||
.with_context(err_context)
|
|
||||||
{
|
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
|
||||||
self.id_to_child_pid.insert(terminal_id, child_fd);
|
|
||||||
new_pane_pids.push((
|
|
||||||
terminal_id,
|
|
||||||
starts_held,
|
|
||||||
Some(command.clone()),
|
|
||||||
Ok(pid_primary),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
Err(err) => match err.downcast_ref::<ZellijError>() {
|
|
||||||
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
|
||||||
new_pane_pids.push((
|
|
||||||
*terminal_id,
|
|
||||||
starts_held,
|
|
||||||
Some(command.clone()),
|
|
||||||
Err(err),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
Err::<(), _>(err).non_fatal();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Run::Cwd(cwd)) => {
|
|
||||||
let starts_held = false; // we do not hold Cwd panes
|
|
||||||
let shell = self.get_default_terminal(Some(cwd));
|
|
||||||
match self
|
|
||||||
.bus
|
|
||||||
.os_input
|
|
||||||
.as_mut()
|
|
||||||
.context("no OS I/O interface found")
|
|
||||||
.with_context(err_context)?
|
|
||||||
.spawn_terminal(shell, quit_cb, self.default_editor.clone())
|
|
||||||
.with_context(err_context)
|
|
||||||
{
|
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
|
||||||
self.id_to_child_pid.insert(terminal_id, child_fd);
|
|
||||||
new_pane_pids.push((terminal_id, starts_held, None, Ok(pid_primary)));
|
|
||||||
},
|
|
||||||
Err(err) => match err.downcast_ref::<ZellijError>() {
|
|
||||||
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
|
||||||
new_pane_pids.push((*terminal_id, starts_held, None, Err(err)));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
Err::<(), _>(err).non_fatal();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Run::EditFile(path_to_file, line_number)) => {
|
|
||||||
let starts_held = false; // we do not hold edit panes (for now?)
|
|
||||||
match self
|
|
||||||
.bus
|
|
||||||
.os_input
|
|
||||||
.as_mut()
|
|
||||||
.context("no OS I/O interface found")
|
|
||||||
.with_context(err_context)?
|
|
||||||
.spawn_terminal(
|
|
||||||
TerminalAction::OpenFile(path_to_file, line_number),
|
|
||||||
quit_cb,
|
|
||||||
self.default_editor.clone(),
|
|
||||||
)
|
|
||||||
.with_context(err_context)
|
|
||||||
{
|
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
|
||||||
self.id_to_child_pid.insert(terminal_id, child_fd);
|
|
||||||
new_pane_pids.push((terminal_id, starts_held, None, Ok(pid_primary)));
|
|
||||||
},
|
|
||||||
Err(err) => match err.downcast_ref::<ZellijError>() {
|
|
||||||
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
|
||||||
new_pane_pids.push((*terminal_id, starts_held, None, Err(err)));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
Err::<(), _>(err).non_fatal();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
let starts_held = false;
|
|
||||||
match self
|
|
||||||
.bus
|
|
||||||
.os_input
|
|
||||||
.as_mut()
|
|
||||||
.context("no OS I/O interface found")
|
|
||||||
.with_context(err_context)?
|
|
||||||
.spawn_terminal(default_shell.clone(), quit_cb, self.default_editor.clone())
|
|
||||||
.with_context(err_context)
|
|
||||||
{
|
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
|
||||||
self.id_to_child_pid.insert(terminal_id, child_fd);
|
|
||||||
new_pane_pids.push((terminal_id, starts_held, None, Ok(pid_primary)));
|
|
||||||
},
|
|
||||||
Err(err) => match err.downcast_ref::<ZellijError>() {
|
|
||||||
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
|
||||||
new_pane_pids.push((*terminal_id, starts_held, None, Err(err)));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
Err::<(), _>(err).non_fatal();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Investigate moving plugin loading to here.
|
|
||||||
Some(Run::Plugin(_)) => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Option<RunCommand> should only be Some if the pane starts held
|
// Option<RunCommand> should only be Some if the pane starts held
|
||||||
|
|
@ -755,17 +616,32 @@ impl Pty {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
let new_tab_floating_pane_ids: Vec<(u32, Option<RunCommand>)> = new_floating_panes_pids
|
||||||
|
.iter()
|
||||||
|
.map(|(terminal_id, starts_held, run_command, _)| {
|
||||||
|
if *starts_held {
|
||||||
|
(*terminal_id, run_command.clone())
|
||||||
|
} else {
|
||||||
|
(*terminal_id, None)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.bus
|
self.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::ApplyLayout(
|
.send_to_screen(ScreenInstruction::ApplyLayout(
|
||||||
layout,
|
layout,
|
||||||
|
floating_panes_layout,
|
||||||
new_tab_pane_ids,
|
new_tab_pane_ids,
|
||||||
|
new_tab_floating_pane_ids,
|
||||||
plugin_ids,
|
plugin_ids,
|
||||||
tab_index,
|
tab_index,
|
||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
for (terminal_id, starts_held, run_command, pid_primary) in new_pane_pids {
|
let mut terminals_to_start = vec![];
|
||||||
|
terminals_to_start.append(&mut new_pane_pids);
|
||||||
|
terminals_to_start.append(&mut new_floating_panes_pids);
|
||||||
|
for (terminal_id, starts_held, run_command, pid_primary) in terminals_to_start {
|
||||||
if starts_held {
|
if starts_held {
|
||||||
// we do not run a command or start listening for bytes on held panes
|
// we do not run a command or start listening for bytes on held panes
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -820,6 +696,172 @@ impl Pty {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
fn apply_run_instruction(
|
||||||
|
&mut self,
|
||||||
|
run_instruction: Option<Run>,
|
||||||
|
default_shell: TerminalAction,
|
||||||
|
) -> Result<Option<(u32, bool, Option<RunCommand>, Result<i32>)>> {
|
||||||
|
// terminal_id,
|
||||||
|
// starts_held,
|
||||||
|
// command
|
||||||
|
// successfully opened
|
||||||
|
let err_context = || format!("failed to apply run instruction");
|
||||||
|
let quit_cb = Box::new({
|
||||||
|
let senders = self.bus.senders.clone();
|
||||||
|
move |pane_id, _exit_status, _command| {
|
||||||
|
let _ = senders.send_to_screen(ScreenInstruction::ClosePane(pane_id, None));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
match run_instruction {
|
||||||
|
Some(Run::Command(command)) => {
|
||||||
|
let starts_held = command.hold_on_start;
|
||||||
|
let hold_on_close = command.hold_on_close;
|
||||||
|
let quit_cb = Box::new({
|
||||||
|
let senders = self.bus.senders.clone();
|
||||||
|
move |pane_id, exit_status, command| {
|
||||||
|
if hold_on_close {
|
||||||
|
let _ = senders.send_to_screen(ScreenInstruction::HoldPane(
|
||||||
|
pane_id,
|
||||||
|
exit_status,
|
||||||
|
command,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
let _ =
|
||||||
|
senders.send_to_screen(ScreenInstruction::ClosePane(pane_id, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let cmd = TerminalAction::RunCommand(command.clone());
|
||||||
|
if starts_held {
|
||||||
|
// we don't actually open a terminal in this case, just wait for the user to run it
|
||||||
|
match self
|
||||||
|
.bus
|
||||||
|
.os_input
|
||||||
|
.as_mut()
|
||||||
|
.context("no OS I/O interface found")
|
||||||
|
.with_context(err_context)?
|
||||||
|
.reserve_terminal_id()
|
||||||
|
{
|
||||||
|
Ok(terminal_id) => {
|
||||||
|
Ok(Some((
|
||||||
|
terminal_id,
|
||||||
|
starts_held,
|
||||||
|
Some(command.clone()),
|
||||||
|
Ok(terminal_id as i32), // this is not actually correct but gets
|
||||||
|
// stripped later
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match self
|
||||||
|
.bus
|
||||||
|
.os_input
|
||||||
|
.as_mut()
|
||||||
|
.context("no OS I/O interface found")
|
||||||
|
.with_context(err_context)?
|
||||||
|
.spawn_terminal(cmd, quit_cb, self.default_editor.clone())
|
||||||
|
.with_context(err_context)
|
||||||
|
{
|
||||||
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
|
self.id_to_child_pid.insert(terminal_id, child_fd);
|
||||||
|
Ok(Some((
|
||||||
|
terminal_id,
|
||||||
|
starts_held,
|
||||||
|
Some(command.clone()),
|
||||||
|
Ok(pid_primary),
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
match err.downcast_ref::<ZellijError>() {
|
||||||
|
Some(ZellijError::CommandNotFound { terminal_id, .. }) => Ok(Some(
|
||||||
|
(*terminal_id, starts_held, Some(command.clone()), Err(err)),
|
||||||
|
)),
|
||||||
|
_ => Err(err),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(Run::Cwd(cwd)) => {
|
||||||
|
let starts_held = false; // we do not hold Cwd panes
|
||||||
|
let shell = self.get_default_terminal(Some(cwd));
|
||||||
|
match self
|
||||||
|
.bus
|
||||||
|
.os_input
|
||||||
|
.as_mut()
|
||||||
|
.context("no OS I/O interface found")
|
||||||
|
.with_context(err_context)?
|
||||||
|
.spawn_terminal(shell, quit_cb, self.default_editor.clone())
|
||||||
|
.with_context(err_context)
|
||||||
|
{
|
||||||
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
|
self.id_to_child_pid.insert(terminal_id, child_fd);
|
||||||
|
Ok(Some((terminal_id, starts_held, None, Ok(pid_primary))))
|
||||||
|
},
|
||||||
|
Err(err) => match err.downcast_ref::<ZellijError>() {
|
||||||
|
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
||||||
|
Ok(Some((*terminal_id, starts_held, None, Err(err))))
|
||||||
|
},
|
||||||
|
_ => Err(err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(Run::EditFile(path_to_file, line_number)) => {
|
||||||
|
let starts_held = false; // we do not hold edit panes (for now?)
|
||||||
|
match self
|
||||||
|
.bus
|
||||||
|
.os_input
|
||||||
|
.as_mut()
|
||||||
|
.context("no OS I/O interface found")
|
||||||
|
.with_context(err_context)?
|
||||||
|
.spawn_terminal(
|
||||||
|
TerminalAction::OpenFile(path_to_file, line_number),
|
||||||
|
quit_cb,
|
||||||
|
self.default_editor.clone(),
|
||||||
|
)
|
||||||
|
.with_context(err_context)
|
||||||
|
{
|
||||||
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
|
self.id_to_child_pid.insert(terminal_id, child_fd);
|
||||||
|
Ok(Some((terminal_id, starts_held, None, Ok(pid_primary))))
|
||||||
|
},
|
||||||
|
Err(err) => match err.downcast_ref::<ZellijError>() {
|
||||||
|
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
||||||
|
Ok(Some((*terminal_id, starts_held, None, Err(err))))
|
||||||
|
},
|
||||||
|
_ => Err(err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let starts_held = false;
|
||||||
|
match self
|
||||||
|
.bus
|
||||||
|
.os_input
|
||||||
|
.as_mut()
|
||||||
|
.context("no OS I/O interface found")
|
||||||
|
.with_context(err_context)?
|
||||||
|
.spawn_terminal(default_shell.clone(), quit_cb, self.default_editor.clone())
|
||||||
|
.with_context(err_context)
|
||||||
|
{
|
||||||
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
|
self.id_to_child_pid.insert(terminal_id, child_fd);
|
||||||
|
Ok(Some((terminal_id, starts_held, None, Ok(pid_primary))))
|
||||||
|
},
|
||||||
|
Err(err) => match err.downcast_ref::<ZellijError>() {
|
||||||
|
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
|
||||||
|
Ok(Some((*terminal_id, starts_held, None, Err(err))))
|
||||||
|
},
|
||||||
|
_ => Err(err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Investigate moving plugin loading to here.
|
||||||
|
Some(Run::Plugin(_)) => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn close_pane(&mut self, id: PaneId) -> Result<()> {
|
pub fn close_pane(&mut self, id: PaneId) -> Result<()> {
|
||||||
let err_context = || format!("failed to close for pane {id:?}");
|
let err_context = || format!("failed to close for pane {id:?}");
|
||||||
match id {
|
match id {
|
||||||
|
|
|
||||||
|
|
@ -430,12 +430,16 @@ pub(crate) fn route_action(
|
||||||
.send_to_screen(ScreenInstruction::CloseFocusedPane(client_id))
|
.send_to_screen(ScreenInstruction::CloseFocusedPane(client_id))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Action::NewTab(tab_layout, tab_name) => {
|
Action::NewTab(tab_layout, floating_panes_layout, tab_name) => {
|
||||||
let shell = session.default_shell.clone();
|
let shell = session.default_shell.clone();
|
||||||
session
|
session
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::NewTab(
|
.send_to_screen(ScreenInstruction::NewTab(
|
||||||
shell, tab_layout, tab_name, client_id,
|
shell,
|
||||||
|
tab_layout,
|
||||||
|
floating_panes_layout,
|
||||||
|
tab_name,
|
||||||
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use zellij_utils::input::options::Clipboard;
|
||||||
use zellij_utils::pane_size::{Size, SizeInPixels};
|
use zellij_utils::pane_size::{Size, SizeInPixels};
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
input::command::TerminalAction,
|
input::command::TerminalAction,
|
||||||
input::layout::{PaneLayout, RunPluginLocation},
|
input::layout::{FloatingPanesLayout, PaneLayout, RunPluginLocation},
|
||||||
position::Position,
|
position::Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -177,12 +177,15 @@ pub enum ScreenInstruction {
|
||||||
NewTab(
|
NewTab(
|
||||||
Option<TerminalAction>,
|
Option<TerminalAction>,
|
||||||
Option<PaneLayout>,
|
Option<PaneLayout>,
|
||||||
|
Vec<FloatingPanesLayout>,
|
||||||
Option<String>,
|
Option<String>,
|
||||||
ClientId,
|
ClientId,
|
||||||
),
|
),
|
||||||
ApplyLayout(
|
ApplyLayout(
|
||||||
PaneLayout,
|
PaneLayout,
|
||||||
Vec<(u32, HoldForCommand)>,
|
Vec<FloatingPanesLayout>,
|
||||||
|
Vec<(u32, HoldForCommand)>, // new pane pids
|
||||||
|
Vec<(u32, HoldForCommand)>, // new floating pane pids
|
||||||
HashMap<RunPluginLocation, Vec<u32>>,
|
HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
usize, // tab_index
|
usize, // tab_index
|
||||||
ClientId,
|
ClientId,
|
||||||
|
|
@ -920,7 +923,9 @@ impl Screen {
|
||||||
pub fn apply_layout(
|
pub fn apply_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: PaneLayout,
|
layout: PaneLayout,
|
||||||
|
floating_panes_layout: Vec<FloatingPanesLayout>,
|
||||||
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
|
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
tab_index: usize,
|
tab_index: usize,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
|
|
@ -968,9 +973,10 @@ impl Screen {
|
||||||
let tab = self.tabs.get_mut(&tab_index).unwrap(); // TODO: no unwrap
|
let tab = self.tabs.get_mut(&tab_index).unwrap(); // TODO: no unwrap
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
layout,
|
layout,
|
||||||
|
floating_panes_layout,
|
||||||
new_terminal_ids,
|
new_terminal_ids,
|
||||||
|
new_floating_terminal_ids,
|
||||||
new_plugin_ids,
|
new_plugin_ids,
|
||||||
tab_index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
|
|
@ -1878,7 +1884,13 @@ pub(crate) fn screen_thread_main(
|
||||||
screen.unblock_input()?;
|
screen.unblock_input()?;
|
||||||
screen.render()?;
|
screen.render()?;
|
||||||
},
|
},
|
||||||
ScreenInstruction::NewTab(default_shell, layout, tab_name, client_id) => {
|
ScreenInstruction::NewTab(
|
||||||
|
default_shell,
|
||||||
|
layout,
|
||||||
|
floating_panes_layout,
|
||||||
|
tab_name,
|
||||||
|
client_id,
|
||||||
|
) => {
|
||||||
let tab_index = screen.get_new_tab_index();
|
let tab_index = screen.get_new_tab_index();
|
||||||
screen.new_tab(tab_index, client_id)?;
|
screen.new_tab(tab_index, client_id)?;
|
||||||
screen
|
screen
|
||||||
|
|
@ -1887,6 +1899,7 @@ pub(crate) fn screen_thread_main(
|
||||||
.send_to_plugin(PluginInstruction::NewTab(
|
.send_to_plugin(PluginInstruction::NewTab(
|
||||||
default_shell,
|
default_shell,
|
||||||
layout,
|
layout,
|
||||||
|
floating_panes_layout,
|
||||||
tab_name,
|
tab_name,
|
||||||
tab_index,
|
tab_index,
|
||||||
client_id,
|
client_id,
|
||||||
|
|
@ -1894,12 +1907,22 @@ pub(crate) fn screen_thread_main(
|
||||||
},
|
},
|
||||||
ScreenInstruction::ApplyLayout(
|
ScreenInstruction::ApplyLayout(
|
||||||
layout,
|
layout,
|
||||||
|
floating_panes_layout,
|
||||||
new_pane_pids,
|
new_pane_pids,
|
||||||
|
new_floating_pane_pids,
|
||||||
new_plugin_ids,
|
new_plugin_ids,
|
||||||
tab_index,
|
tab_index,
|
||||||
client_id,
|
client_id,
|
||||||
) => {
|
) => {
|
||||||
screen.apply_layout(layout, new_pane_pids, new_plugin_ids, tab_index, client_id)?;
|
screen.apply_layout(
|
||||||
|
layout,
|
||||||
|
floating_panes_layout,
|
||||||
|
new_pane_pids,
|
||||||
|
new_floating_pane_pids,
|
||||||
|
new_plugin_ids,
|
||||||
|
tab_index,
|
||||||
|
client_id,
|
||||||
|
)?;
|
||||||
screen.unblock_input()?;
|
screen.unblock_input()?;
|
||||||
screen.render()?;
|
screen.render()?;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
376
zellij-server/src/tab/layout_applier.rs
Normal file
376
zellij-server/src/tab/layout_applier.rs
Normal file
|
|
@ -0,0 +1,376 @@
|
||||||
|
use zellij_utils::errors::prelude::*;
|
||||||
|
|
||||||
|
use crate::resize_pty;
|
||||||
|
use crate::tab::{get_next_terminal_position, HoldForCommand, Pane};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
os_input_output::ServerOsApi,
|
||||||
|
panes::sixel::SixelImageStore,
|
||||||
|
panes::{FloatingPanes, TiledPanes},
|
||||||
|
panes::{LinkHandler, PaneId, PluginPane, TerminalPane},
|
||||||
|
plugins::PluginInstruction,
|
||||||
|
pty::PtyInstruction,
|
||||||
|
thread_bus::ThreadSenders,
|
||||||
|
ClientId,
|
||||||
|
};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use zellij_utils::{
|
||||||
|
data::{Palette, Style},
|
||||||
|
input::layout::{FloatingPanesLayout, PaneLayout, Run, RunPluginLocation},
|
||||||
|
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct LayoutApplier<'a> {
|
||||||
|
viewport: Rc<RefCell<Viewport>>, // includes all non-UI panes
|
||||||
|
senders: ThreadSenders,
|
||||||
|
sixel_image_store: Rc<RefCell<SixelImageStore>>,
|
||||||
|
link_handler: Rc<RefCell<LinkHandler>>,
|
||||||
|
terminal_emulator_colors: Rc<RefCell<Palette>>,
|
||||||
|
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
|
||||||
|
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
|
||||||
|
style: Style,
|
||||||
|
display_area: Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
|
||||||
|
tiled_panes: &'a mut TiledPanes,
|
||||||
|
floating_panes: &'a mut FloatingPanes,
|
||||||
|
draw_pane_frames: bool,
|
||||||
|
focus_pane_id: &'a mut Option<PaneId>,
|
||||||
|
os_api: Box<dyn ServerOsApi>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LayoutApplier<'a> {
|
||||||
|
pub fn new(
|
||||||
|
viewport: &Rc<RefCell<Viewport>>,
|
||||||
|
senders: &ThreadSenders,
|
||||||
|
sixel_image_store: &Rc<RefCell<SixelImageStore>>,
|
||||||
|
link_handler: &Rc<RefCell<LinkHandler>>,
|
||||||
|
terminal_emulator_colors: &Rc<RefCell<Palette>>,
|
||||||
|
terminal_emulator_color_codes: &Rc<RefCell<HashMap<usize, String>>>,
|
||||||
|
character_cell_size: &Rc<RefCell<Option<SizeInPixels>>>,
|
||||||
|
style: &Style,
|
||||||
|
display_area: &Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
|
||||||
|
tiled_panes: &'a mut TiledPanes,
|
||||||
|
floating_panes: &'a mut FloatingPanes,
|
||||||
|
draw_pane_frames: bool,
|
||||||
|
focus_pane_id: &'a mut Option<PaneId>,
|
||||||
|
os_api: &Box<dyn ServerOsApi>,
|
||||||
|
) -> Self {
|
||||||
|
let viewport = viewport.clone();
|
||||||
|
let senders = senders.clone();
|
||||||
|
let sixel_image_store = sixel_image_store.clone();
|
||||||
|
let link_handler = link_handler.clone();
|
||||||
|
let terminal_emulator_colors = terminal_emulator_colors.clone();
|
||||||
|
let terminal_emulator_color_codes = terminal_emulator_color_codes.clone();
|
||||||
|
let character_cell_size = character_cell_size.clone();
|
||||||
|
let style = style.clone();
|
||||||
|
let display_area = display_area.clone();
|
||||||
|
let os_api = os_api.clone();
|
||||||
|
LayoutApplier {
|
||||||
|
viewport,
|
||||||
|
senders,
|
||||||
|
sixel_image_store,
|
||||||
|
link_handler,
|
||||||
|
terminal_emulator_colors,
|
||||||
|
terminal_emulator_color_codes,
|
||||||
|
character_cell_size,
|
||||||
|
style,
|
||||||
|
display_area,
|
||||||
|
tiled_panes,
|
||||||
|
floating_panes,
|
||||||
|
draw_pane_frames,
|
||||||
|
focus_pane_id,
|
||||||
|
os_api,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn apply_layout(
|
||||||
|
&mut self,
|
||||||
|
layout: PaneLayout,
|
||||||
|
floating_panes_layout: Vec<FloatingPanesLayout>,
|
||||||
|
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
|
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
|
mut new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
|
client_id: ClientId,
|
||||||
|
) -> Result<bool> {
|
||||||
|
// true => layout has floating panes
|
||||||
|
let layout_name = layout.name.clone();
|
||||||
|
self.apply_tiled_panes_layout(layout, new_terminal_ids, &mut new_plugin_ids, client_id)?;
|
||||||
|
let layout_has_floating_panes = self.apply_floating_panes_layout(
|
||||||
|
floating_panes_layout,
|
||||||
|
new_floating_terminal_ids,
|
||||||
|
&mut new_plugin_ids,
|
||||||
|
layout_name,
|
||||||
|
)?;
|
||||||
|
return Ok(layout_has_floating_panes);
|
||||||
|
}
|
||||||
|
fn apply_tiled_panes_layout(
|
||||||
|
&mut self,
|
||||||
|
layout: PaneLayout,
|
||||||
|
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
|
new_plugin_ids: &mut HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
|
client_id: ClientId,
|
||||||
|
) -> Result<()> {
|
||||||
|
let err_context = || format!("failed to apply tiled panes layout");
|
||||||
|
let (viewport_cols, viewport_rows) = {
|
||||||
|
let viewport = self.viewport.borrow();
|
||||||
|
(viewport.cols, viewport.rows)
|
||||||
|
};
|
||||||
|
let mut free_space = PaneGeom::default();
|
||||||
|
free_space.cols.set_inner(viewport_cols);
|
||||||
|
free_space.rows.set_inner(viewport_rows);
|
||||||
|
match layout.position_panes_in_space(&free_space) {
|
||||||
|
Ok(positions_in_layout) => {
|
||||||
|
let positions_and_size = positions_in_layout.iter();
|
||||||
|
let mut new_terminal_ids = new_terminal_ids.iter();
|
||||||
|
|
||||||
|
let mut focus_pane_id: Option<PaneId> = None;
|
||||||
|
let mut set_focus_pane_id = |layout: &PaneLayout, 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() {
|
||||||
|
let pane_title = run.location.to_string();
|
||||||
|
let pid = new_plugin_ids
|
||||||
|
.get_mut(&run.location)
|
||||||
|
.and_then(|ids| ids.pop())
|
||||||
|
.with_context(err_context)?;
|
||||||
|
let mut new_plugin = PluginPane::new(
|
||||||
|
pid,
|
||||||
|
*position_and_size,
|
||||||
|
self.senders
|
||||||
|
.to_plugin
|
||||||
|
.as_ref()
|
||||||
|
.with_context(err_context)?
|
||||||
|
.clone(),
|
||||||
|
pane_title,
|
||||||
|
layout.name.clone().unwrap_or_default(),
|
||||||
|
self.sixel_image_store.clone(),
|
||||||
|
self.terminal_emulator_colors.clone(),
|
||||||
|
self.terminal_emulator_color_codes.clone(),
|
||||||
|
self.link_handler.clone(),
|
||||||
|
self.character_cell_size.clone(),
|
||||||
|
self.style,
|
||||||
|
);
|
||||||
|
new_plugin.set_borderless(layout.borderless);
|
||||||
|
self.tiled_panes
|
||||||
|
.add_pane_with_existing_geom(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
|
||||||
|
if let Some((pid, hold_for_command)) = new_terminal_ids.next() {
|
||||||
|
let next_terminal_position =
|
||||||
|
get_next_terminal_position(&self.tiled_panes, &self.floating_panes);
|
||||||
|
let initial_title = match &layout.run {
|
||||||
|
Some(Run::Command(run_command)) => Some(run_command.to_string()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let mut new_pane = TerminalPane::new(
|
||||||
|
*pid,
|
||||||
|
*position_and_size,
|
||||||
|
self.style,
|
||||||
|
next_terminal_position,
|
||||||
|
layout.name.clone().unwrap_or_default(),
|
||||||
|
self.link_handler.clone(),
|
||||||
|
self.character_cell_size.clone(),
|
||||||
|
self.sixel_image_store.clone(),
|
||||||
|
self.terminal_emulator_colors.clone(),
|
||||||
|
self.terminal_emulator_color_codes.clone(),
|
||||||
|
initial_title,
|
||||||
|
);
|
||||||
|
new_pane.set_borderless(layout.borderless);
|
||||||
|
if let Some(held_command) = hold_for_command {
|
||||||
|
new_pane.hold(None, true, held_command.clone());
|
||||||
|
}
|
||||||
|
self.tiled_panes.add_pane_with_existing_geom(
|
||||||
|
PaneId::Terminal(*pid),
|
||||||
|
Box::new(new_pane),
|
||||||
|
);
|
||||||
|
set_focus_pane_id(layout, PaneId::Terminal(*pid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unused_pid, _) in new_terminal_ids {
|
||||||
|
self.senders
|
||||||
|
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid)))
|
||||||
|
.with_context(err_context)?;
|
||||||
|
}
|
||||||
|
self.adjust_viewport();
|
||||||
|
self.set_focused_tiled_pane(focus_pane_id, client_id);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
for (unused_pid, _) in new_terminal_ids {
|
||||||
|
self.senders
|
||||||
|
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(unused_pid)))
|
||||||
|
.with_context(err_context)?;
|
||||||
|
}
|
||||||
|
Err::<(), _>(anyError::msg(e))
|
||||||
|
.with_context(err_context)
|
||||||
|
.non_fatal(); // TODO: propagate this to the user
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn apply_floating_panes_layout(
|
||||||
|
&mut self,
|
||||||
|
floating_panes_layout: Vec<FloatingPanesLayout>,
|
||||||
|
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
|
new_plugin_ids: &mut HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
|
layout_name: Option<String>,
|
||||||
|
) -> Result<bool> {
|
||||||
|
// true => has floating panes
|
||||||
|
let err_context = || format!("Failed to apply_floating_panes_layout");
|
||||||
|
let mut layout_has_floating_panes = false;
|
||||||
|
let floating_panes_layout = floating_panes_layout.iter();
|
||||||
|
let mut focused_floating_pane = None;
|
||||||
|
let mut new_floating_terminal_ids = new_floating_terminal_ids.iter();
|
||||||
|
for floating_pane_layout in floating_panes_layout {
|
||||||
|
layout_has_floating_panes = true;
|
||||||
|
if let Some(Run::Plugin(run)) = floating_pane_layout.run.clone() {
|
||||||
|
let position_and_size = self
|
||||||
|
.floating_panes
|
||||||
|
.position_floating_pane_layout(&floating_pane_layout);
|
||||||
|
let pane_title = run.location.to_string();
|
||||||
|
let pid = new_plugin_ids
|
||||||
|
.get_mut(&run.location)
|
||||||
|
.and_then(|ids| ids.pop())
|
||||||
|
.with_context(err_context)?;
|
||||||
|
let mut new_pane = PluginPane::new(
|
||||||
|
pid,
|
||||||
|
position_and_size,
|
||||||
|
self.senders
|
||||||
|
.to_plugin
|
||||||
|
.as_ref()
|
||||||
|
.with_context(err_context)?
|
||||||
|
.clone(),
|
||||||
|
pane_title,
|
||||||
|
layout_name.clone().unwrap_or_default(),
|
||||||
|
self.sixel_image_store.clone(),
|
||||||
|
self.terminal_emulator_colors.clone(),
|
||||||
|
self.terminal_emulator_color_codes.clone(),
|
||||||
|
self.link_handler.clone(),
|
||||||
|
self.character_cell_size.clone(),
|
||||||
|
self.style,
|
||||||
|
);
|
||||||
|
new_pane.set_borderless(false);
|
||||||
|
new_pane.set_content_offset(Offset::frame(1));
|
||||||
|
resize_pty!(new_pane, self.os_api, self.senders)?;
|
||||||
|
self.floating_panes
|
||||||
|
.add_pane(PaneId::Plugin(pid), Box::new(new_pane));
|
||||||
|
if floating_pane_layout.focus.unwrap_or(false) {
|
||||||
|
focused_floating_pane = Some(PaneId::Plugin(pid));
|
||||||
|
}
|
||||||
|
} else if let Some((pid, hold_for_command)) = new_floating_terminal_ids.next() {
|
||||||
|
let position_and_size = self
|
||||||
|
.floating_panes
|
||||||
|
.position_floating_pane_layout(&floating_pane_layout);
|
||||||
|
let next_terminal_position =
|
||||||
|
get_next_terminal_position(&self.tiled_panes, &self.floating_panes);
|
||||||
|
let initial_title = match &floating_pane_layout.run {
|
||||||
|
Some(Run::Command(run_command)) => Some(run_command.to_string()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let mut new_pane = TerminalPane::new(
|
||||||
|
*pid,
|
||||||
|
position_and_size,
|
||||||
|
self.style,
|
||||||
|
next_terminal_position,
|
||||||
|
floating_pane_layout.name.clone().unwrap_or_default(),
|
||||||
|
self.link_handler.clone(),
|
||||||
|
self.character_cell_size.clone(),
|
||||||
|
self.sixel_image_store.clone(),
|
||||||
|
self.terminal_emulator_colors.clone(),
|
||||||
|
self.terminal_emulator_color_codes.clone(),
|
||||||
|
initial_title,
|
||||||
|
);
|
||||||
|
new_pane.set_borderless(false);
|
||||||
|
new_pane.set_content_offset(Offset::frame(1));
|
||||||
|
if let Some(held_command) = hold_for_command {
|
||||||
|
new_pane.hold(None, true, held_command.clone());
|
||||||
|
}
|
||||||
|
resize_pty!(new_pane, self.os_api, self.senders)?;
|
||||||
|
self.floating_panes
|
||||||
|
.add_pane(PaneId::Terminal(*pid), Box::new(new_pane));
|
||||||
|
if floating_pane_layout.focus.unwrap_or(false) {
|
||||||
|
focused_floating_pane = Some(PaneId::Terminal(*pid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(focused_floating_pane) = focused_floating_pane {
|
||||||
|
self.floating_panes
|
||||||
|
.focus_pane_for_all_clients(focused_floating_pane);
|
||||||
|
}
|
||||||
|
if layout_has_floating_panes {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn resize_whole_tab(&mut self, new_screen_size: Size) {
|
||||||
|
self.floating_panes.resize(new_screen_size);
|
||||||
|
self.floating_panes
|
||||||
|
.resize_pty_all_panes(&mut self.os_api)
|
||||||
|
.unwrap(); // we need to do this explicitly because floating_panes.resize does not do this
|
||||||
|
self.tiled_panes.resize(new_screen_size);
|
||||||
|
}
|
||||||
|
fn offset_viewport(&mut self, position_and_size: &Viewport) {
|
||||||
|
let mut viewport = self.viewport.borrow_mut();
|
||||||
|
if position_and_size.x == viewport.x
|
||||||
|
&& position_and_size.x + position_and_size.cols == viewport.x + viewport.cols
|
||||||
|
{
|
||||||
|
if position_and_size.y == viewport.y {
|
||||||
|
viewport.y += position_and_size.rows;
|
||||||
|
viewport.rows -= position_and_size.rows;
|
||||||
|
} else if position_and_size.y + position_and_size.rows == viewport.y + viewport.rows {
|
||||||
|
viewport.rows -= position_and_size.rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if position_and_size.y == viewport.y
|
||||||
|
&& position_and_size.y + position_and_size.rows == viewport.y + viewport.rows
|
||||||
|
{
|
||||||
|
if position_and_size.x == viewport.x {
|
||||||
|
viewport.x += position_and_size.cols;
|
||||||
|
viewport.cols -= position_and_size.cols;
|
||||||
|
} else if position_and_size.x + position_and_size.cols == viewport.x + viewport.cols {
|
||||||
|
viewport.cols -= position_and_size.cols;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn adjust_viewport(&mut self) {
|
||||||
|
// here we offset the viewport after applying a tiled panes layout
|
||||||
|
// from borderless panes that are on the edges of the
|
||||||
|
// screen, this is so that when we don't have pane boundaries (eg. when they were
|
||||||
|
// disabled by the user) boundaries won't be drawn around these panes
|
||||||
|
// geometrically, we can only do this with panes that are on the edges of the
|
||||||
|
// screen - so it's mostly a best-effort thing
|
||||||
|
let display_area = {
|
||||||
|
let display_area = self.display_area.borrow();
|
||||||
|
*display_area
|
||||||
|
};
|
||||||
|
self.resize_whole_tab(display_area);
|
||||||
|
let boundary_geoms = self.tiled_panes.borderless_pane_geoms();
|
||||||
|
for geom in boundary_geoms {
|
||||||
|
self.offset_viewport(&geom)
|
||||||
|
}
|
||||||
|
self.tiled_panes.set_pane_frames(self.draw_pane_frames);
|
||||||
|
}
|
||||||
|
fn set_focused_tiled_pane(&mut self, focus_pane_id: Option<PaneId>, client_id: ClientId) {
|
||||||
|
if let Some(pane_id) = focus_pane_id {
|
||||||
|
*self.focus_pane_id = Some(pane_id);
|
||||||
|
self.tiled_panes.focus_pane(pane_id, client_id);
|
||||||
|
} else {
|
||||||
|
let next_selectable_pane_id = self.tiled_panes.first_selectable_pane_id();
|
||||||
|
match next_selectable_pane_id {
|
||||||
|
Some(active_pane_id) => {
|
||||||
|
self.tiled_panes.focus_pane(active_pane_id, client_id);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.tiled_panes.clear_active_panes();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
mod clipboard;
|
mod clipboard;
|
||||||
mod copy_command;
|
mod copy_command;
|
||||||
|
mod layout_applier;
|
||||||
|
|
||||||
use copy_command::CopyCommand;
|
use copy_command::CopyCommand;
|
||||||
use std::env::temp_dir;
|
use std::env::temp_dir;
|
||||||
|
|
@ -17,6 +18,7 @@ use crate::background_jobs::BackgroundJob;
|
||||||
use crate::pty_writer::PtyWriteInstruction;
|
use crate::pty_writer::PtyWriteInstruction;
|
||||||
use crate::screen::CopyOptions;
|
use crate::screen::CopyOptions;
|
||||||
use crate::ui::pane_boundaries_frame::FrameParams;
|
use crate::ui::pane_boundaries_frame::FrameParams;
|
||||||
|
use layout_applier::LayoutApplier;
|
||||||
|
|
||||||
use self::clipboard::ClipboardProvider;
|
use self::clipboard::ClipboardProvider;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -24,7 +26,7 @@ use crate::{
|
||||||
output::{CharacterChunk, Output, SixelImageChunk},
|
output::{CharacterChunk, Output, SixelImageChunk},
|
||||||
panes::sixel::SixelImageStore,
|
panes::sixel::SixelImageStore,
|
||||||
panes::{FloatingPanes, TiledPanes},
|
panes::{FloatingPanes, TiledPanes},
|
||||||
panes::{LinkHandler, PaneId, PluginPane, TerminalPane},
|
panes::{LinkHandler, PaneId, TerminalPane},
|
||||||
plugins::PluginInstruction,
|
plugins::PluginInstruction,
|
||||||
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
|
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
|
||||||
thread_bus::ThreadSenders,
|
thread_bus::ThreadSenders,
|
||||||
|
|
@ -42,7 +44,7 @@ use zellij_utils::{
|
||||||
data::{Event, InputMode, ModeInfo, Palette, PaletteColor, Style},
|
data::{Event, InputMode, ModeInfo, Palette, PaletteColor, Style},
|
||||||
input::{
|
input::{
|
||||||
command::TerminalAction,
|
command::TerminalAction,
|
||||||
layout::{PaneLayout, Run, RunPluginLocation},
|
layout::{FloatingPanesLayout, PaneLayout, RunPluginLocation},
|
||||||
parse_keys,
|
parse_keys,
|
||||||
},
|
},
|
||||||
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
|
pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport},
|
||||||
|
|
@ -398,6 +400,26 @@ pub enum AdjustedInput {
|
||||||
ReRunCommandInThisPane(RunCommand),
|
ReRunCommandInThisPane(RunCommand),
|
||||||
CloseThisPane,
|
CloseThisPane,
|
||||||
}
|
}
|
||||||
|
pub fn get_next_terminal_position(
|
||||||
|
tiled_panes: &TiledPanes,
|
||||||
|
floating_panes: &FloatingPanes,
|
||||||
|
) -> usize {
|
||||||
|
let tiled_panes_count = tiled_panes
|
||||||
|
.get_panes()
|
||||||
|
.filter(|(k, _)| match k {
|
||||||
|
PaneId::Plugin(_) => false,
|
||||||
|
PaneId::Terminal(_) => true,
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
let floating_panes_count = floating_panes
|
||||||
|
.get_panes()
|
||||||
|
.filter(|(k, _)| match k {
|
||||||
|
PaneId::Plugin(_) => false,
|
||||||
|
PaneId::Terminal(_) => true,
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
tiled_panes_count + floating_panes_count + 1
|
||||||
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
// FIXME: Still too many arguments for clippy to be happy...
|
// FIXME: Still too many arguments for clippy to be happy...
|
||||||
|
|
@ -509,170 +531,45 @@ impl Tab {
|
||||||
pub fn apply_layout(
|
pub fn apply_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: PaneLayout,
|
layout: PaneLayout,
|
||||||
|
floating_panes_layout: Vec<FloatingPanesLayout>,
|
||||||
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
mut new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||||
tab_index: usize,
|
new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let err_context = || {
|
let layout_has_floating_panes = LayoutApplier::new(
|
||||||
format!(
|
&self.viewport,
|
||||||
"failed to apply layout {layout:#?} in tab {tab_index} for client id {client_id}"
|
&self.senders,
|
||||||
)
|
&self.sixel_image_store,
|
||||||
};
|
&self.link_handler,
|
||||||
|
&self.terminal_emulator_colors,
|
||||||
if self.tiled_panes.has_panes() {
|
&self.terminal_emulator_color_codes,
|
||||||
Err::<(), _>(anyhow!(
|
&self.character_cell_size,
|
||||||
"Applying a layout to a tab with existing panes - this is not yet supported!"
|
&self.style,
|
||||||
))
|
&self.display_area,
|
||||||
.with_context(err_context)
|
&mut self.tiled_panes,
|
||||||
.non_fatal();
|
&mut self.floating_panes,
|
||||||
}
|
self.draw_pane_frames,
|
||||||
let (viewport_cols, viewport_rows) = {
|
&mut self.focus_pane_id,
|
||||||
let viewport = self.viewport.borrow();
|
&self.os_api,
|
||||||
(viewport.cols, viewport.rows)
|
)
|
||||||
};
|
.apply_layout(
|
||||||
let mut free_space = PaneGeom::default();
|
layout,
|
||||||
free_space.cols.set_inner(viewport_cols);
|
floating_panes_layout,
|
||||||
free_space.rows.set_inner(viewport_rows);
|
new_terminal_ids,
|
||||||
|
new_floating_terminal_ids,
|
||||||
match layout.position_panes_in_space(&free_space) {
|
new_plugin_ids,
|
||||||
Ok(positions_in_layout) => {
|
client_id,
|
||||||
let positions_and_size = positions_in_layout.iter();
|
)?;
|
||||||
let mut new_terminal_ids = new_terminal_ids.iter();
|
if layout_has_floating_panes {
|
||||||
|
if !self.floating_panes.panes_are_visible() {
|
||||||
let mut focus_pane_id: Option<PaneId> = None;
|
self.toggle_floating_panes(client_id, None)?;
|
||||||
let mut set_focus_pane_id = |layout: &PaneLayout, 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() {
|
|
||||||
// let (pid_tx, pid_rx) = channel();
|
|
||||||
let pane_title = run.location.to_string();
|
|
||||||
let pid = new_plugin_ids
|
|
||||||
.get_mut(&run.location)
|
|
||||||
.unwrap()
|
|
||||||
.pop()
|
|
||||||
.unwrap(); // TODO:
|
|
||||||
// err_context
|
|
||||||
// and
|
|
||||||
// stuff
|
|
||||||
let mut new_plugin = PluginPane::new(
|
|
||||||
pid,
|
|
||||||
*position_and_size,
|
|
||||||
self.senders
|
|
||||||
.to_plugin
|
|
||||||
.as_ref()
|
|
||||||
.with_context(err_context)?
|
|
||||||
.clone(),
|
|
||||||
pane_title,
|
|
||||||
layout.name.clone().unwrap_or_default(),
|
|
||||||
self.sixel_image_store.clone(),
|
|
||||||
self.terminal_emulator_colors.clone(),
|
|
||||||
self.terminal_emulator_color_codes.clone(),
|
|
||||||
self.link_handler.clone(),
|
|
||||||
self.character_cell_size.clone(),
|
|
||||||
self.style,
|
|
||||||
);
|
|
||||||
new_plugin.set_borderless(layout.borderless);
|
|
||||||
self.tiled_panes
|
|
||||||
.add_pane_with_existing_geom(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
|
|
||||||
if let Some((pid, hold_for_command)) = new_terminal_ids.next() {
|
|
||||||
let next_terminal_position = self.get_next_terminal_position();
|
|
||||||
let initial_title = match &layout.run {
|
|
||||||
Some(Run::Command(run_command)) => Some(run_command.to_string()),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let mut new_pane = TerminalPane::new(
|
|
||||||
*pid,
|
|
||||||
*position_and_size,
|
|
||||||
self.style,
|
|
||||||
next_terminal_position,
|
|
||||||
layout.name.clone().unwrap_or_default(),
|
|
||||||
self.link_handler.clone(),
|
|
||||||
self.character_cell_size.clone(),
|
|
||||||
self.sixel_image_store.clone(),
|
|
||||||
self.terminal_emulator_colors.clone(),
|
|
||||||
self.terminal_emulator_color_codes.clone(),
|
|
||||||
initial_title,
|
|
||||||
);
|
|
||||||
new_pane.set_borderless(layout.borderless);
|
|
||||||
if let Some(held_command) = hold_for_command {
|
|
||||||
new_pane.hold(None, true, held_command.clone());
|
|
||||||
}
|
|
||||||
self.tiled_panes.add_pane_with_existing_geom(
|
|
||||||
PaneId::Terminal(*pid),
|
|
||||||
Box::new(new_pane),
|
|
||||||
);
|
|
||||||
set_focus_pane_id(layout, PaneId::Terminal(*pid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (unused_pid, _) in new_terminal_ids {
|
|
||||||
// this is a bit of a hack and happens because we don't have any central location that
|
|
||||||
// can query the screen as to how many panes it needs to create a layout
|
|
||||||
// fixing this will require a bit of an architecture change
|
|
||||||
self.senders
|
|
||||||
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid)))
|
|
||||||
.with_context(err_context)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// here we offset the viewport from borderless panes that are on the edges of the
|
|
||||||
// screen, this is so that when we don't have pane boundaries (eg. when they were
|
|
||||||
// disabled by the user) boundaries won't be drawn around these panes
|
|
||||||
// geometrically, we can only do this with panes that are on the edges of the
|
|
||||||
// screen - so it's mostly a best-effort thing
|
|
||||||
let display_area = {
|
|
||||||
let display_area = self.display_area.borrow();
|
|
||||||
*display_area
|
|
||||||
};
|
|
||||||
self.resize_whole_tab(display_area);
|
|
||||||
let boundary_geoms = self.tiled_panes.borderless_pane_geoms();
|
|
||||||
for geom in boundary_geoms {
|
|
||||||
self.offset_viewport(&geom)
|
|
||||||
}
|
|
||||||
self.tiled_panes.set_pane_frames(self.draw_pane_frames);
|
|
||||||
self.should_clear_display_before_rendering = true;
|
|
||||||
|
|
||||||
if let Some(pane_id) = focus_pane_id {
|
|
||||||
self.focus_pane_id = Some(pane_id);
|
|
||||||
self.tiled_panes.focus_pane(pane_id, client_id);
|
|
||||||
} else {
|
|
||||||
// This is the end of the nasty viewport hack...
|
|
||||||
let next_selectable_pane_id = self.tiled_panes.first_selectable_pane_id();
|
|
||||||
match next_selectable_pane_id {
|
|
||||||
Some(active_pane_id) => {
|
|
||||||
self.tiled_panes.focus_pane(active_pane_id, client_id);
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// this is very likely a configuration error (layout with no selectable panes)
|
|
||||||
self.tiled_panes.clear_active_panes();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.is_pending = false;
|
|
||||||
self.apply_buffered_instructions()?;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
for (unused_pid, _) in new_terminal_ids {
|
|
||||||
self.senders
|
|
||||||
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(unused_pid)))
|
|
||||||
.with_context(err_context)?;
|
|
||||||
}
|
|
||||||
self.is_pending = false;
|
|
||||||
Err::<(), _>(anyError::msg(e))
|
|
||||||
.with_context(err_context)
|
|
||||||
.non_fatal(); // TODO: propagate this to the user
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
self.tiled_panes.set_pane_frames(self.draw_pane_frames);
|
||||||
|
self.is_pending = false;
|
||||||
|
self.apply_buffered_instructions()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn apply_buffered_instructions(&mut self) -> Result<()> {
|
pub fn apply_buffered_instructions(&mut self) -> Result<()> {
|
||||||
let buffered_instructions: Vec<BufferedTabInstruction> =
|
let buffered_instructions: Vec<BufferedTabInstruction> =
|
||||||
|
|
@ -852,7 +749,7 @@ impl Tab {
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
} else {
|
} else {
|
||||||
self.show_floating_panes();
|
self.show_floating_panes();
|
||||||
match self.floating_panes.first_floating_pane_id() {
|
match self.floating_panes.last_floating_pane_id() {
|
||||||
Some(first_floating_pane_id) => {
|
Some(first_floating_pane_id) => {
|
||||||
if !self.floating_panes.active_panes_contain(&client_id) {
|
if !self.floating_panes.active_panes_contain(&client_id) {
|
||||||
self.floating_panes
|
self.floating_panes
|
||||||
|
|
@ -2708,30 +2605,6 @@ impl Tab {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn offset_viewport(&mut self, position_and_size: &Viewport) {
|
|
||||||
let mut viewport = self.viewport.borrow_mut();
|
|
||||||
if position_and_size.x == viewport.x
|
|
||||||
&& position_and_size.x + position_and_size.cols == viewport.x + viewport.cols
|
|
||||||
{
|
|
||||||
if position_and_size.y == viewport.y {
|
|
||||||
viewport.y += position_and_size.rows;
|
|
||||||
viewport.rows -= position_and_size.rows;
|
|
||||||
} else if position_and_size.y + position_and_size.rows == viewport.y + viewport.rows {
|
|
||||||
viewport.rows -= position_and_size.rows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if position_and_size.y == viewport.y
|
|
||||||
&& position_and_size.y + position_and_size.rows == viewport.y + viewport.rows
|
|
||||||
{
|
|
||||||
if position_and_size.x == viewport.x {
|
|
||||||
viewport.x += position_and_size.cols;
|
|
||||||
viewport.cols -= position_and_size.cols;
|
|
||||||
} else if position_and_size.x + position_and_size.cols == viewport.x + viewport.cols {
|
|
||||||
viewport.cols -= position_and_size.cols;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visible(&self, visible: bool) -> Result<()> {
|
pub fn visible(&self, visible: bool) -> Result<()> {
|
||||||
let pids_in_this_tab = self.tiled_panes.pane_ids().filter_map(|p| match p {
|
let pids_in_this_tab = self.tiled_panes.pane_ids().filter_map(|p| match p {
|
||||||
PaneId::Plugin(pid) => Some(pid),
|
PaneId::Plugin(pid) => Some(pid),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2560
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
01 (C): │ │
|
||||||
|
02 (C): │ │
|
||||||
|
03 (C): │ │
|
||||||
|
04 (C): │ │
|
||||||
|
05 (C): │ ┌ Pane #2 ─────────────────────────────────────────────────┐ │
|
||||||
|
06 (C): │ │ │ │
|
||||||
|
07 (C): │ │ ┌ Pane #3 ─────────────────────────────────────────────────┐ │
|
||||||
|
08 (C): │ │ │ │ │
|
||||||
|
09 (C): │ │ │ │ │
|
||||||
|
10 (C): │ │ │ │ │
|
||||||
|
11 (C): │ │ │ │ │
|
||||||
|
12 (C): │ │ │ │ │
|
||||||
|
13 (C): │ │ │ │ │
|
||||||
|
14 (C): │ └─│ │ │
|
||||||
|
15 (C): │ │ │ │
|
||||||
|
16 (C): │ └──────────────────────────────────────────────────────────┘ │
|
||||||
|
17 (C): │ │
|
||||||
|
18 (C): │ │
|
||||||
|
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -229,9 +229,10 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![],
|
||||||
vec![(1, None)],
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -285,9 +286,10 @@ fn create_new_tab_with_os_api(
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![],
|
||||||
vec![(1, None)],
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -316,7 +318,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
|
||||||
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
|
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
|
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
|
||||||
let layout = Layout::from_str(layout, "layout_file_name".into(), None).unwrap();
|
let layout = Layout::from_str(layout, "layout_file_name".into(), None).unwrap();
|
||||||
let tab_layout = layout.new_tab();
|
let (tab_layout, floating_panes_layout) = layout.new_tab();
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
|
|
@ -343,8 +345,20 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| (i as u32, None))
|
.map(|(i, _)| (i as u32, None))
|
||||||
.collect();
|
.collect();
|
||||||
tab.apply_layout(tab_layout, pane_ids, HashMap::new(), index, client_id)
|
let floating_pane_ids = floating_panes_layout
|
||||||
.unwrap();
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, _)| (i as u32, None))
|
||||||
|
.collect();
|
||||||
|
tab.apply_layout(
|
||||||
|
tab_layout,
|
||||||
|
floating_panes_layout,
|
||||||
|
pane_ids,
|
||||||
|
floating_pane_ids,
|
||||||
|
HashMap::new(),
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,9 +410,10 @@ fn create_new_tab_with_mock_pty_writer(
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![],
|
||||||
vec![(1, None)],
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -455,9 +470,10 @@ fn create_new_tab_with_sixel_support(
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![],
|
||||||
vec![(1, None)],
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -2519,6 +2535,34 @@ fn tab_with_basic_layout() {
|
||||||
assert_snapshot!(snapshot);
|
assert_snapshot!(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tab_with_layout_that_has_floating_panes() {
|
||||||
|
let layout = r#"
|
||||||
|
layout {
|
||||||
|
pane
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout);
|
||||||
|
let mut output = Output::default();
|
||||||
|
tab.render(&mut output, None).unwrap();
|
||||||
|
let snapshot = take_snapshot(
|
||||||
|
output.serialize().unwrap().get(&client_id).unwrap(),
|
||||||
|
size.rows,
|
||||||
|
size.cols,
|
||||||
|
Palette::default(),
|
||||||
|
);
|
||||||
|
assert_snapshot!(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tab_with_nested_layout() {
|
fn tab_with_nested_layout() {
|
||||||
let layout = r#"
|
let layout = r#"
|
||||||
|
|
|
||||||
|
|
@ -178,9 +178,10 @@ fn create_new_tab(size: Size) -> Tab {
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![],
|
||||||
vec![(1, None)],
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -231,8 +232,15 @@ fn create_new_tab_with_layout(size: Size, layout: PaneLayout) -> Tab {
|
||||||
for i in 0..layout.extract_run_instructions().len() {
|
for i in 0..layout.extract_run_instructions().len() {
|
||||||
new_terminal_ids.push((i as u32, None));
|
new_terminal_ids.push((i as u32, None));
|
||||||
}
|
}
|
||||||
tab.apply_layout(layout, new_terminal_ids, HashMap::new(), index, client_id)
|
tab.apply_layout(
|
||||||
.unwrap();
|
layout,
|
||||||
|
vec![],
|
||||||
|
new_terminal_ids,
|
||||||
|
vec![],
|
||||||
|
HashMap::new(),
|
||||||
|
client_id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,9 +288,10 @@ fn create_new_tab_with_cell_size(
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![],
|
||||||
vec![(1, None)],
|
vec![(1, None)],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
index,
|
|
||||||
client_id,
|
client_id,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use zellij_utils::data::Resize;
|
||||||
use zellij_utils::errors::{prelude::*, ErrorContext};
|
use zellij_utils::errors::{prelude::*, ErrorContext};
|
||||||
use zellij_utils::input::actions::Action;
|
use zellij_utils::input::actions::Action;
|
||||||
use zellij_utils::input::command::{RunCommand, TerminalAction};
|
use zellij_utils::input::command::{RunCommand, TerminalAction};
|
||||||
use zellij_utils::input::layout::{PaneLayout, SplitDirection, SplitSize};
|
use zellij_utils::input::layout::{PaneLayout, SplitDirection};
|
||||||
use zellij_utils::input::options::Options;
|
use zellij_utils::input::options::Options;
|
||||||
use zellij_utils::ipc::IpcReceiverWithContext;
|
use zellij_utils::ipc::IpcReceiverWithContext;
|
||||||
use zellij_utils::pane_size::{Size, SizeInPixels};
|
use zellij_utils::pane_size::{Size, SizeInPixels};
|
||||||
|
|
@ -297,12 +297,15 @@ impl MockScreen {
|
||||||
let _ = self.to_screen.send(ScreenInstruction::NewTab(
|
let _ = self.to_screen.send(ScreenInstruction::NewTab(
|
||||||
default_shell,
|
default_shell,
|
||||||
Some(pane_layout.clone()),
|
Some(pane_layout.clone()),
|
||||||
|
vec![], // floating_panes_layout
|
||||||
tab_name,
|
tab_name,
|
||||||
self.main_client_id,
|
self.main_client_id,
|
||||||
));
|
));
|
||||||
let _ = self.to_screen.send(ScreenInstruction::ApplyLayout(
|
let _ = self.to_screen.send(ScreenInstruction::ApplyLayout(
|
||||||
pane_layout,
|
pane_layout,
|
||||||
|
vec![], // floating panes layout
|
||||||
pane_ids,
|
pane_ids,
|
||||||
|
vec![], // floating pane ids
|
||||||
plugin_ids,
|
plugin_ids,
|
||||||
tab_index,
|
tab_index,
|
||||||
self.main_client_id,
|
self.main_client_id,
|
||||||
|
|
@ -323,12 +326,15 @@ impl MockScreen {
|
||||||
let _ = self.to_screen.send(ScreenInstruction::NewTab(
|
let _ = self.to_screen.send(ScreenInstruction::NewTab(
|
||||||
default_shell,
|
default_shell,
|
||||||
Some(tab_layout.clone()),
|
Some(tab_layout.clone()),
|
||||||
|
vec![], // floating_panes_layout
|
||||||
tab_name,
|
tab_name,
|
||||||
self.main_client_id,
|
self.main_client_id,
|
||||||
));
|
));
|
||||||
let _ = self.to_screen.send(ScreenInstruction::ApplyLayout(
|
let _ = self.to_screen.send(ScreenInstruction::ApplyLayout(
|
||||||
tab_layout,
|
tab_layout,
|
||||||
|
vec![], // floating_panes_layout
|
||||||
pane_ids,
|
pane_ids,
|
||||||
|
vec![], // floating panes ids
|
||||||
plugin_ids,
|
plugin_ids,
|
||||||
0,
|
0,
|
||||||
self.main_client_id,
|
self.main_client_id,
|
||||||
|
|
@ -472,7 +478,9 @@ fn new_tab(screen: &mut Screen, pid: u32, tab_index: usize) {
|
||||||
screen
|
screen
|
||||||
.apply_layout(
|
.apply_layout(
|
||||||
PaneLayout::default(),
|
PaneLayout::default(),
|
||||||
|
vec![], // floating panes layout
|
||||||
new_terminal_ids,
|
new_terminal_ids,
|
||||||
|
vec![], // new floating terminal ids
|
||||||
new_plugin_ids,
|
new_plugin_ids,
|
||||||
tab_index,
|
tab_index,
|
||||||
client_id,
|
client_id,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-server/src/./unit/screen_tests.rs
|
source: zellij-server/src/./unit/screen_tests.rs
|
||||||
assertion_line: 2272
|
assertion_line: 2300
|
||||||
expression: "format!(\"{:#?}\", new_tab_action)"
|
expression: "format!(\"{:#?}\", new_tab_action)"
|
||||||
---
|
---
|
||||||
Some(
|
Some(
|
||||||
|
|
@ -39,6 +39,7 @@ Some(
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-server/src/./unit/screen_tests.rs
|
source: zellij-server/src/./unit/screen_tests.rs
|
||||||
assertion_line: 2322
|
assertion_line: 2350
|
||||||
expression: "format!(\"{:#?}\", new_tab_instruction)"
|
expression: "format!(\"{:#?}\", new_tab_instruction)"
|
||||||
---
|
---
|
||||||
NewTab(
|
NewTab(
|
||||||
|
|
@ -60,6 +60,7 @@ NewTab(
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
[],
|
||||||
Some(
|
Some(
|
||||||
"my-awesome-tab-name",
|
"my-awesome-tab-name",
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-server/src/./unit/screen_tests.rs
|
source: zellij-server/src/./unit/screen_tests.rs
|
||||||
assertion_line: 2511
|
assertion_line: 2534
|
||||||
expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
|
|
@ -61,6 +61,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
|
|
@ -207,6 +208,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-server/src/./unit/screen_tests.rs
|
source: zellij-server/src/./unit/screen_tests.rs
|
||||||
assertion_line: 2554
|
assertion_line: 2577
|
||||||
expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
|
|
@ -61,6 +61,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
|
|
@ -207,6 +208,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())"
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! Definition of the actions that can be bound to keys.
|
//! Definition of the actions that can be bound to keys.
|
||||||
|
|
||||||
use super::command::RunCommandAction;
|
use super::command::RunCommandAction;
|
||||||
use super::layout::{Layout, PaneLayout};
|
use super::layout::{FloatingPanesLayout, Layout, PaneLayout};
|
||||||
use crate::cli::CliAction;
|
use crate::cli::CliAction;
|
||||||
use crate::data::InputMode;
|
use crate::data::InputMode;
|
||||||
use crate::data::{Direction, Resize};
|
use crate::data::{Direction, Resize};
|
||||||
|
|
@ -162,7 +162,7 @@ pub enum Action {
|
||||||
PaneNameInput(Vec<u8>),
|
PaneNameInput(Vec<u8>),
|
||||||
UndoRenamePane,
|
UndoRenamePane,
|
||||||
/// Create a new tab, optionally with a specified tab layout.
|
/// Create a new tab, optionally with a specified tab layout.
|
||||||
NewTab(Option<PaneLayout>, Option<String>), // the String is the tab name
|
NewTab(Option<PaneLayout>, Vec<FloatingPanesLayout>, Option<String>), // the String is the tab name
|
||||||
/// Do nothing.
|
/// Do nothing.
|
||||||
NoOp,
|
NoOp,
|
||||||
/// Go to the next tab.
|
/// Go to the next tab.
|
||||||
|
|
@ -368,15 +368,24 @@ impl Action {
|
||||||
if tabs.len() > 1 {
|
if tabs.len() > 1 {
|
||||||
return Err(format!("Tab layout cannot itself have tabs"));
|
return Err(format!("Tab layout cannot itself have tabs"));
|
||||||
} else if !tabs.is_empty() {
|
} else if !tabs.is_empty() {
|
||||||
let (tab_name, layout) = tabs.drain(..).next().unwrap();
|
let (tab_name, layout, floating_panes_layout) =
|
||||||
|
tabs.drain(..).next().unwrap();
|
||||||
let name = tab_name.or(name);
|
let name = tab_name.or(name);
|
||||||
Ok(vec![Action::NewTab(Some(layout), name)])
|
Ok(vec![Action::NewTab(
|
||||||
|
Some(layout),
|
||||||
|
floating_panes_layout,
|
||||||
|
name,
|
||||||
|
)])
|
||||||
} else {
|
} else {
|
||||||
let layout = layout.new_tab();
|
let (layout, floating_panes_layout) = layout.new_tab();
|
||||||
Ok(vec![Action::NewTab(Some(layout), name)])
|
Ok(vec![Action::NewTab(
|
||||||
|
Some(layout),
|
||||||
|
floating_panes_layout,
|
||||||
|
name,
|
||||||
|
)])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(vec![Action::NewTab(None, name)])
|
Ok(vec![Action::NewTab(None, vec![], name)])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,9 +213,94 @@ impl fmt::Display for RunPluginLocation {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub tabs: Vec<(Option<String>, PaneLayout)>,
|
pub tabs: Vec<(Option<String>, PaneLayout, Vec<FloatingPanesLayout>)>,
|
||||||
pub focused_tab_index: Option<usize>,
|
pub focused_tab_index: Option<usize>,
|
||||||
pub template: Option<PaneLayout>,
|
pub template: Option<PaneLayout>,
|
||||||
|
pub floating_panes_template: Vec<FloatingPanesLayout>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PercentOrFixed {
|
||||||
|
Percent(usize), // 1 to 100
|
||||||
|
Fixed(usize), // An absolute number of columns or rows
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PercentOrFixed {
|
||||||
|
pub fn to_position(&self, whole: usize) -> usize {
|
||||||
|
match self {
|
||||||
|
PercentOrFixed::Percent(percent) => {
|
||||||
|
(whole as f64 / 100.0 * *percent as f64).ceil() as usize
|
||||||
|
},
|
||||||
|
PercentOrFixed::Fixed(fixed) => {
|
||||||
|
if *fixed > whole {
|
||||||
|
whole
|
||||||
|
} else {
|
||||||
|
*fixed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PercentOrFixed {
|
||||||
|
pub fn is_zero(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
PercentOrFixed::Percent(percent) => *percent == 0,
|
||||||
|
PercentOrFixed::Fixed(fixed) => *fixed == 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PercentOrFixed {
|
||||||
|
type Err = Box<dyn std::error::Error>;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.chars().last() == Some('%') {
|
||||||
|
let char_count = s.chars().count();
|
||||||
|
let percent_size = usize::from_str_radix(&s[..char_count.saturating_sub(1)], 10)?;
|
||||||
|
if percent_size <= 100 {
|
||||||
|
Ok(PercentOrFixed::Percent(percent_size))
|
||||||
|
} else {
|
||||||
|
Err("Percent must be between 0 and 100".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let fixed_size = usize::from_str_radix(s, 10)?;
|
||||||
|
Ok(PercentOrFixed::Fixed(fixed_size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub struct FloatingPanesLayout {
|
||||||
|
// TODO: change name to singular
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub height: Option<PercentOrFixed>,
|
||||||
|
pub width: Option<PercentOrFixed>,
|
||||||
|
pub x: Option<PercentOrFixed>,
|
||||||
|
pub y: Option<PercentOrFixed>,
|
||||||
|
pub run: Option<Run>,
|
||||||
|
pub focus: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatingPanesLayout {
|
||||||
|
pub fn add_cwd_to_layout(&mut self, cwd: &PathBuf) {
|
||||||
|
match self.run.as_mut() {
|
||||||
|
Some(run) => run.add_cwd(cwd),
|
||||||
|
None => {
|
||||||
|
self.run = Some(Run::Cwd(cwd.clone()));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PaneLayout> for FloatingPanesLayout {
|
||||||
|
fn from(pane_layout: &PaneLayout) -> Self {
|
||||||
|
FloatingPanesLayout {
|
||||||
|
name: pane_layout.name.clone(),
|
||||||
|
run: pane_layout.run.clone(),
|
||||||
|
focus: pane_layout.focus,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
|
||||||
|
|
@ -276,6 +361,8 @@ impl PaneLayout {
|
||||||
Ok(layouts)
|
Ok(layouts)
|
||||||
}
|
}
|
||||||
pub fn extract_run_instructions(&self) -> Vec<Option<Run>> {
|
pub fn extract_run_instructions(&self) -> Vec<Option<Run>> {
|
||||||
|
// the order of these run instructions is significant and needs to be the same
|
||||||
|
// as the order of the "flattened" layout panes received from eg. position_panes_in_space
|
||||||
let mut run_instructions = vec![];
|
let mut run_instructions = vec![];
|
||||||
if self.children.is_empty() {
|
if self.children.is_empty() {
|
||||||
run_instructions.push(self.run.clone());
|
run_instructions.push(self.run.clone());
|
||||||
|
|
@ -452,11 +539,12 @@ impl Layout {
|
||||||
Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)
|
Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_tab(&self) -> PaneLayout {
|
pub fn new_tab(&self) -> (PaneLayout, Vec<FloatingPanesLayout>) {
|
||||||
match &self.template {
|
let template = match &self.template {
|
||||||
Some(template) => template.clone(),
|
Some(template) => template.clone(),
|
||||||
None => PaneLayout::default(),
|
None => PaneLayout::default(),
|
||||||
}
|
};
|
||||||
|
(template, self.floating_panes_template.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
|
|
@ -467,7 +555,7 @@ impl Layout {
|
||||||
!self.tabs.is_empty()
|
!self.tabs.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout)> {
|
pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout, Vec<FloatingPanesLayout>)> {
|
||||||
// String is the tab name
|
// String is the tab name
|
||||||
self.tabs.clone()
|
self.tabs.clone()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,175 @@ fn layout_with_nested_panes() {
|
||||||
assert_eq!(layout, expected_layout);
|
assert_eq!(layout, expected_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layout_with_floating_panes() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
pane {
|
||||||
|
x 10
|
||||||
|
y "10%"
|
||||||
|
width 10
|
||||||
|
height "10%"
|
||||||
|
}
|
||||||
|
pane x=10 y="10%"
|
||||||
|
pane command="htop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
let expected_layout = Layout {
|
||||||
|
template: Some(PaneLayout::default()),
|
||||||
|
floating_panes_template: vec![
|
||||||
|
FloatingPanesLayout::default(),
|
||||||
|
FloatingPanesLayout {
|
||||||
|
x: Some(PercentOrFixed::Fixed(10)),
|
||||||
|
y: Some(PercentOrFixed::Percent(10)),
|
||||||
|
width: Some(PercentOrFixed::Fixed(10)),
|
||||||
|
height: Some(PercentOrFixed::Percent(10)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
FloatingPanesLayout {
|
||||||
|
x: Some(PercentOrFixed::Fixed(10)),
|
||||||
|
y: Some(PercentOrFixed::Percent(10)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
FloatingPanesLayout {
|
||||||
|
run: Some(Run::Command(RunCommand {
|
||||||
|
command: PathBuf::from("htop"),
|
||||||
|
hold_on_close: true,
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(layout, expected_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layout_with_mixed_panes_and_floating_panes() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
pane
|
||||||
|
pane
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
let expected_layout = Layout {
|
||||||
|
template: Some(PaneLayout {
|
||||||
|
children: vec![PaneLayout::default(), PaneLayout::default()],
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
floating_panes_template: vec![FloatingPanesLayout::default()],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(layout, expected_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layout_with_floating_panes_template() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
pane_template name="my_cool_template" {
|
||||||
|
x 10
|
||||||
|
y "10%"
|
||||||
|
}
|
||||||
|
pane
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
my_cool_template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
let expected_layout = Layout {
|
||||||
|
template: Some(PaneLayout {
|
||||||
|
children: vec![PaneLayout::default()],
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
floating_panes_template: vec![
|
||||||
|
FloatingPanesLayout::default(),
|
||||||
|
FloatingPanesLayout {
|
||||||
|
x: Some(PercentOrFixed::Fixed(10)),
|
||||||
|
y: Some(PercentOrFixed::Percent(10)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(layout, expected_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layout_with_shared_tiled_and_floating_panes_template() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
pane_template name="htop" {
|
||||||
|
command "htop"
|
||||||
|
}
|
||||||
|
htop
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
htop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
let expected_layout = Layout {
|
||||||
|
template: Some(PaneLayout {
|
||||||
|
children: vec![PaneLayout {
|
||||||
|
run: Some(Run::Command(RunCommand {
|
||||||
|
command: PathBuf::from("htop"),
|
||||||
|
hold_on_close: true,
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
floating_panes_template: vec![
|
||||||
|
FloatingPanesLayout::default(),
|
||||||
|
FloatingPanesLayout {
|
||||||
|
run: Some(Run::Command(RunCommand {
|
||||||
|
command: PathBuf::from("htop"),
|
||||||
|
hold_on_close: true,
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(layout, expected_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layout_with_tabs_and_floating_panes() {
|
||||||
|
let kdl_layout = r#"
|
||||||
|
layout {
|
||||||
|
tab {
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tab {
|
||||||
|
floating_panes {
|
||||||
|
pane
|
||||||
|
pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
|
assert_snapshot!(format!("{:#?}", layout));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn layout_with_tabs() {
|
fn layout_with_tabs() {
|
||||||
let kdl_layout = r#"
|
let kdl_layout = r#"
|
||||||
|
|
@ -98,7 +267,7 @@ fn layout_with_tabs() {
|
||||||
"#;
|
"#;
|
||||||
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
let expected_layout = Layout {
|
let expected_layout = Layout {
|
||||||
tabs: vec![(None, PaneLayout::default())],
|
tabs: vec![(None, PaneLayout::default(), vec![])],
|
||||||
template: Some(PaneLayout::default()),
|
template: Some(PaneLayout::default()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
@ -134,6 +303,7 @@ fn layout_with_nested_differing_tabs() {
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
|
@ -142,6 +312,7 @@ fn layout_with_nested_differing_tabs() {
|
||||||
children: vec![PaneLayout::default(), PaneLayout::default()],
|
children: vec![PaneLayout::default(), PaneLayout::default()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
template: Some(PaneLayout::default()),
|
template: Some(PaneLayout::default()),
|
||||||
|
|
@ -412,6 +583,7 @@ fn layout_with_tab_names() {
|
||||||
children: vec![],
|
children: vec![],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Some("my cool tab name 2".into()),
|
Some("my cool tab name 2".into()),
|
||||||
|
|
@ -419,6 +591,7 @@ fn layout_with_tab_names() {
|
||||||
children: vec![],
|
children: vec![],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
template: Some(PaneLayout::default()),
|
template: Some(PaneLayout::default()),
|
||||||
|
|
@ -439,9 +612,9 @@ fn layout_with_focused_tab() {
|
||||||
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
|
||||||
let expected_layout = Layout {
|
let expected_layout = Layout {
|
||||||
tabs: vec![
|
tabs: vec![
|
||||||
(None, PaneLayout::default()),
|
(None, PaneLayout::default(), vec![]),
|
||||||
(None, PaneLayout::default()),
|
(None, PaneLayout::default(), vec![]),
|
||||||
(None, PaneLayout::default()),
|
(None, PaneLayout::default(), vec![]),
|
||||||
],
|
],
|
||||||
template: Some(PaneLayout::default()),
|
template: Some(PaneLayout::default()),
|
||||||
focused_tab_index: Some(1),
|
focused_tab_index: Some(1),
|
||||||
|
|
@ -488,6 +661,7 @@ fn layout_with_tab_templates() {
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Some("my second tab".into()),
|
Some("my second tab".into()),
|
||||||
|
|
@ -504,6 +678,7 @@ fn layout_with_tab_templates() {
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
|
@ -516,6 +691,7 @@ fn layout_with_tab_templates() {
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
vec![], // floating panes
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
template: Some(PaneLayout::default()),
|
template: Some(PaneLayout::default()),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1081
|
assertion_line: 1101
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -62,4 +62,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1046
|
assertion_line: 1066
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -65,4 +65,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 711
|
assertion_line: 744
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -138,4 +138,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 801
|
assertion_line: 696
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -82,6 +82,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
|
@ -129,6 +130,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -144,4 +146,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1098
|
assertion_line: 1118
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -59,4 +59,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1064
|
assertion_line: 1084
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -59,4 +59,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 899
|
assertion_line: 796
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -105,6 +105,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
|
@ -183,6 +184,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -198,4 +200,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1133
|
assertion_line: 1153
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -61,4 +61,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1116
|
assertion_line: 1136
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -63,4 +63,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1516
|
assertion_line: 1536
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -58,6 +58,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -73,4 +74,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1537
|
assertion_line: 1557
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -102,6 +102,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -117,4 +118,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1556
|
assertion_line: 1576
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -87,6 +87,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -102,4 +103,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1403
|
assertion_line: 1423
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -55,4 +55,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1435
|
assertion_line: 1455
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -55,4 +55,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1455
|
assertion_line: 1475
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -55,4 +55,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1416
|
assertion_line: 1436
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -55,4 +55,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1471
|
assertion_line: 1491
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -58,6 +58,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -73,4 +74,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 281
|
assertion_line: 283
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -39,4 +39,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 294
|
assertion_line: 296
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -39,4 +39,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 549
|
assertion_line: 555
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -71,6 +71,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Some(
|
Some(
|
||||||
|
|
@ -138,6 +139,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
|
@ -182,6 +184,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -228,4 +231,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 756
|
assertion_line: 650
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -122,4 +122,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 729
|
assertion_line: 624
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -91,4 +91,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 626
|
assertion_line: 582
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -236,4 +236,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 583
|
assertion_line: 603
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -80,6 +80,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -95,4 +96,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
|
assertion_line: 263
|
||||||
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
|
---
|
||||||
|
Layout {
|
||||||
|
tabs: [
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
FloatingPanesLayout {
|
||||||
|
name: None,
|
||||||
|
height: None,
|
||||||
|
width: None,
|
||||||
|
x: None,
|
||||||
|
y: None,
|
||||||
|
run: None,
|
||||||
|
focus: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
FloatingPanesLayout {
|
||||||
|
name: None,
|
||||||
|
height: None,
|
||||||
|
width: None,
|
||||||
|
x: None,
|
||||||
|
y: None,
|
||||||
|
run: None,
|
||||||
|
focus: None,
|
||||||
|
},
|
||||||
|
FloatingPanesLayout {
|
||||||
|
name: None,
|
||||||
|
height: None,
|
||||||
|
width: None,
|
||||||
|
x: None,
|
||||||
|
y: None,
|
||||||
|
run: None,
|
||||||
|
focus: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
focused_tab_index: None,
|
||||||
|
template: Some(
|
||||||
|
PaneLayout {
|
||||||
|
children_split_direction: Horizontal,
|
||||||
|
name: None,
|
||||||
|
children: [],
|
||||||
|
split_size: None,
|
||||||
|
run: None,
|
||||||
|
borderless: false,
|
||||||
|
focus: None,
|
||||||
|
external_children_index: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1287
|
assertion_line: 1307
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1233
|
assertion_line: 1253
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1250
|
assertion_line: 1270
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1268
|
assertion_line: 1288
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1305
|
assertion_line: 1325
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1235
|
assertion_line: 1343
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -33,4 +33,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1357
|
assertion_line: 1377
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1339
|
assertion_line: 1359
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -41,4 +41,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1298
|
assertion_line: 1393
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -34,4 +34,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1314
|
assertion_line: 1409
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -34,4 +34,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1486
|
assertion_line: 1506
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -58,6 +58,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -73,4 +74,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1501
|
assertion_line: 1521
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -58,6 +58,7 @@ Layout {
|
||||||
focus: None,
|
focus: None,
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
|
[],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
focused_tab_index: None,
|
focused_tab_index: None,
|
||||||
|
|
@ -73,4 +74,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -738,7 +738,7 @@ impl TryFrom<&KdlNode> for Action {
|
||||||
"PaneNameInput" => {
|
"PaneNameInput" => {
|
||||||
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
|
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
|
||||||
},
|
},
|
||||||
"NewTab" => Ok(Action::NewTab(None, None)),
|
"NewTab" => Ok(Action::NewTab(None, vec![], None)),
|
||||||
"GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action),
|
"GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action),
|
||||||
"TabNameInput" => {
|
"TabNameInput" => {
|
||||||
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
|
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 509
|
assertion_line: 626
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -18,4 +18,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 492
|
assertion_line: 583
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -79,4 +79,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
|
assertion_line: 582
|
||||||
expression: "format!(\"{:#?}\", config)"
|
expression: "format!(\"{:#?}\", config)"
|
||||||
---
|
---
|
||||||
Config {
|
Config {
|
||||||
|
|
@ -975,6 +976,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
@ -3049,6 +3051,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
|
assertion_line: 640
|
||||||
expression: "format!(\"{:#?}\", config)"
|
expression: "format!(\"{:#?}\", config)"
|
||||||
---
|
---
|
||||||
Config {
|
Config {
|
||||||
|
|
@ -975,6 +976,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
@ -3049,6 +3051,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
assertion_line: 492
|
assertion_line: 608
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
|
|
@ -18,4 +18,5 @@ Layout {
|
||||||
external_children_index: None,
|
external_children_index: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
floating_panes_template: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
|
assertion_line: 668
|
||||||
expression: "format!(\"{:#?}\", config)"
|
expression: "format!(\"{:#?}\", config)"
|
||||||
---
|
---
|
||||||
Config {
|
Config {
|
||||||
|
|
@ -975,6 +976,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
@ -3049,6 +3051,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
|
assertion_line: 682
|
||||||
expression: "format!(\"{:#?}\", config)"
|
expression: "format!(\"{:#?}\", config)"
|
||||||
---
|
---
|
||||||
Config {
|
Config {
|
||||||
|
|
@ -975,6 +976,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
@ -3049,6 +3051,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
source: zellij-utils/src/setup.rs
|
source: zellij-utils/src/setup.rs
|
||||||
|
assertion_line: 654
|
||||||
expression: "format!(\"{:#?}\", config)"
|
expression: "format!(\"{:#?}\", config)"
|
||||||
---
|
---
|
||||||
Config {
|
Config {
|
||||||
|
|
@ -975,6 +976,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
@ -3049,6 +3051,7 @@ Config {
|
||||||
): [
|
): [
|
||||||
NewTab(
|
NewTab(
|
||||||
None,
|
None,
|
||||||
|
[],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
SwitchToMode(
|
SwitchToMode(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue