fix(layouts): recover from resurrection crash and pick up swap layouts properly (#3249)
* fix(layouts): recover from issues in the constraint system * fix(keybinds): pick up swap layouts for new tab keybinding
This commit is contained in:
parent
462239b535
commit
41dbe65e26
5 changed files with 246 additions and 237 deletions
|
|
@ -124,82 +124,78 @@ impl<'a> LayoutApplier<'a> {
|
||||||
refocus_pane: bool,
|
refocus_pane: bool,
|
||||||
client_id: Option<ClientId>,
|
client_id: Option<ClientId>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let err_context = || format!("failed to apply tiled panes layout");
|
|
||||||
let free_space = self.total_space_for_tiled_panes();
|
let free_space = self.total_space_for_tiled_panes();
|
||||||
let tiled_panes_count = self.tiled_panes.visible_panes_count();
|
let tiled_panes_count = self.tiled_panes.visible_panes_count();
|
||||||
match layout.position_panes_in_space(&free_space, Some(tiled_panes_count)) {
|
let positions_in_layout =
|
||||||
Ok(positions_in_layout) => {
|
match layout.position_panes_in_space(&free_space, Some(tiled_panes_count), false) {
|
||||||
let currently_focused_pane_id =
|
Ok(positions_in_layout) => positions_in_layout,
|
||||||
client_id.and_then(|client_id| self.tiled_panes.focused_pane_id(client_id));
|
// in the error branch, we try to recover by positioning the panes in the space but
|
||||||
let mut existing_tab_state =
|
// ignoring the percentage sizes (passing true as the third argument), this is a hack
|
||||||
ExistingTabState::new(self.tiled_panes.drain(), currently_focused_pane_id);
|
// around some issues with the constraint system that should be addressed in a systemic
|
||||||
let mut pane_focuser = PaneFocuser::new(refocus_pane);
|
// manner
|
||||||
let mut positions_left = vec![];
|
Err(_e) => layout
|
||||||
for (layout, position_and_size) in positions_in_layout {
|
.position_panes_in_space(&free_space, Some(tiled_panes_count), true)
|
||||||
// first try to find panes with contents matching the layout exactly
|
.map_err(|e| anyhow!(e))?,
|
||||||
match existing_tab_state.find_and_extract_exact_match_pane(
|
};
|
||||||
&layout.run,
|
let currently_focused_pane_id =
|
||||||
&position_and_size,
|
client_id.and_then(|client_id| self.tiled_panes.focused_pane_id(client_id));
|
||||||
true,
|
let mut existing_tab_state =
|
||||||
) {
|
ExistingTabState::new(self.tiled_panes.drain(), currently_focused_pane_id);
|
||||||
Some(mut pane) => {
|
let mut pane_focuser = PaneFocuser::new(refocus_pane);
|
||||||
self.apply_layout_properties_to_pane(
|
let mut positions_left = vec![];
|
||||||
&mut pane,
|
for (layout, position_and_size) in positions_in_layout {
|
||||||
&layout,
|
// first try to find panes with contents matching the layout exactly
|
||||||
Some(position_and_size),
|
match existing_tab_state.find_and_extract_exact_match_pane(
|
||||||
);
|
&layout.run,
|
||||||
pane_focuser.set_pane_id_in_focused_location(layout.focus, &pane);
|
&position_and_size,
|
||||||
pane_focuser
|
true,
|
||||||
.set_expanded_stacked_pane(layout.is_expanded_in_stack, &pane);
|
) {
|
||||||
resize_pty!(pane, self.os_api, self.senders, self.character_cell_size)?;
|
Some(mut pane) => {
|
||||||
self.tiled_panes
|
self.apply_layout_properties_to_pane(
|
||||||
.add_pane_with_existing_geom(pane.pid(), pane);
|
&mut pane,
|
||||||
},
|
&layout,
|
||||||
None => {
|
Some(position_and_size),
|
||||||
positions_left.push((layout, position_and_size));
|
);
|
||||||
},
|
pane_focuser.set_pane_id_in_focused_location(layout.focus, &pane);
|
||||||
}
|
pane_focuser.set_expanded_stacked_pane(layout.is_expanded_in_stack, &pane);
|
||||||
}
|
resize_pty!(pane, self.os_api, self.senders, self.character_cell_size)?;
|
||||||
for (layout, position_and_size) in positions_left {
|
self.tiled_panes
|
||||||
// now let's try to find panes on a best-effort basis
|
.add_pane_with_existing_geom(pane.pid(), pane);
|
||||||
if let Some(mut pane) = existing_tab_state.find_and_extract_pane(
|
},
|
||||||
&layout.run,
|
None => {
|
||||||
&position_and_size,
|
positions_left.push((layout, position_and_size));
|
||||||
layout.focus.unwrap_or(false),
|
},
|
||||||
true,
|
}
|
||||||
) {
|
}
|
||||||
self.apply_layout_properties_to_pane(
|
for (layout, position_and_size) in positions_left {
|
||||||
&mut pane,
|
// now let's try to find panes on a best-effort basis
|
||||||
&layout,
|
if let Some(mut pane) = existing_tab_state.find_and_extract_pane(
|
||||||
Some(position_and_size),
|
&layout.run,
|
||||||
);
|
&position_and_size,
|
||||||
pane_focuser.set_pane_id_in_focused_location(layout.focus, &pane);
|
layout.focus.unwrap_or(false),
|
||||||
pane_focuser.set_expanded_stacked_pane(layout.is_expanded_in_stack, &pane);
|
true,
|
||||||
resize_pty!(pane, self.os_api, self.senders, self.character_cell_size)?;
|
) {
|
||||||
self.tiled_panes
|
self.apply_layout_properties_to_pane(&mut pane, &layout, Some(position_and_size));
|
||||||
.add_pane_with_existing_geom(pane.pid(), pane);
|
pane_focuser.set_pane_id_in_focused_location(layout.focus, &pane);
|
||||||
}
|
pane_focuser.set_expanded_stacked_pane(layout.is_expanded_in_stack, &pane);
|
||||||
}
|
resize_pty!(pane, self.os_api, self.senders, self.character_cell_size)?;
|
||||||
let remaining_pane_ids: Vec<PaneId> = existing_tab_state.pane_ids();
|
self.tiled_panes
|
||||||
for pane_id in remaining_pane_ids {
|
.add_pane_with_existing_geom(pane.pid(), pane);
|
||||||
if let Some(mut pane) = existing_tab_state.remove_pane(&pane_id) {
|
}
|
||||||
self.apply_layout_properties_to_pane(&mut pane, &layout, None);
|
}
|
||||||
self.tiled_panes.insert_pane(pane.pid(), pane);
|
let remaining_pane_ids: Vec<PaneId> = existing_tab_state.pane_ids();
|
||||||
}
|
for pane_id in remaining_pane_ids {
|
||||||
}
|
if let Some(mut pane) = existing_tab_state.remove_pane(&pane_id) {
|
||||||
pane_focuser.focus_tiled_pane(&mut self.tiled_panes);
|
self.apply_layout_properties_to_pane(&mut pane, &layout, None);
|
||||||
LayoutApplier::offset_viewport(
|
self.tiled_panes.insert_pane(pane.pid(), pane);
|
||||||
self.viewport.clone(),
|
}
|
||||||
self.tiled_panes,
|
}
|
||||||
self.draw_pane_frames,
|
pane_focuser.focus_tiled_pane(&mut self.tiled_panes);
|
||||||
);
|
LayoutApplier::offset_viewport(
|
||||||
},
|
self.viewport.clone(),
|
||||||
Err(e) => {
|
self.tiled_panes,
|
||||||
Err::<(), _>(anyError::msg(e))
|
self.draw_pane_frames,
|
||||||
.with_context(err_context)
|
);
|
||||||
.non_fatal(); // TODO: propagate this to the user
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn apply_tiled_panes_layout(
|
fn apply_tiled_panes_layout(
|
||||||
|
|
@ -211,159 +207,154 @@ impl<'a> LayoutApplier<'a> {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let err_context = || format!("failed to apply tiled panes layout");
|
let err_context = || format!("failed to apply tiled panes layout");
|
||||||
let free_space = self.total_space_for_tiled_panes();
|
let free_space = self.total_space_for_tiled_panes();
|
||||||
match layout.position_panes_in_space(&free_space, None) {
|
let mut positions_in_layout = match layout.position_panes_in_space(&free_space, None, false)
|
||||||
Ok(mut positions_in_layout) => {
|
{
|
||||||
let mut run_instructions_to_ignore = layout.run_instructions_to_ignore.clone();
|
Ok(positions_in_layout) => positions_in_layout,
|
||||||
let mut new_terminal_ids = new_terminal_ids.iter();
|
// in the error branch, we try to recover by positioning the panes in the space but
|
||||||
|
// ignoring the percentage sizes (passing true as the third argument), this is a hack
|
||||||
let mut focus_pane_id: Option<PaneId> = None;
|
// around some issues with the constraint system that should be addressed in a systemic
|
||||||
let mut set_focus_pane_id = |layout: &TiledPaneLayout, pane_id: PaneId| {
|
// manner
|
||||||
if layout.focus.unwrap_or(false) && focus_pane_id.is_none() {
|
Err(_e) => layout
|
||||||
focus_pane_id = Some(pane_id);
|
.position_panes_in_space(&free_space, None, true)
|
||||||
}
|
.map_err(|e| anyhow!(e))?,
|
||||||
};
|
|
||||||
|
|
||||||
// first, try to find rooms for the panes that are already running (represented by
|
|
||||||
// run_instructions_to_ignore), we try to either find an explicit position (the new
|
|
||||||
// layout has a pane with the exact run instruction) or an otherwise free position
|
|
||||||
// (the new layout has a pane with None as its run instruction)
|
|
||||||
for run_instruction in run_instructions_to_ignore.drain(..) {
|
|
||||||
if let Some(position) = positions_in_layout
|
|
||||||
.iter()
|
|
||||||
.position(|(layout, _position_and_size)| &layout.run == &run_instruction)
|
|
||||||
{
|
|
||||||
let (layout, position_and_size) = positions_in_layout.remove(position);
|
|
||||||
self.tiled_panes.set_geom_for_pane_with_run(
|
|
||||||
layout.run,
|
|
||||||
position_and_size,
|
|
||||||
layout.borderless,
|
|
||||||
);
|
|
||||||
} else if let Some(position) = positions_in_layout
|
|
||||||
.iter()
|
|
||||||
.position(|(layout, _position_and_size)| layout.run.is_none())
|
|
||||||
{
|
|
||||||
let (layout, position_and_size) = positions_in_layout.remove(position);
|
|
||||||
self.tiled_panes.set_geom_for_pane_with_run(
|
|
||||||
run_instruction,
|
|
||||||
position_and_size,
|
|
||||||
layout.borderless,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log::error!(
|
|
||||||
"Failed to find room for run instruction: {:?}",
|
|
||||||
run_instruction
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// then, we open new panes for each run instruction in the layout with the details
|
|
||||||
// we got from the plugin thread and pty thread
|
|
||||||
let positions_and_size = positions_in_layout.iter();
|
|
||||||
for (layout, position_and_size) in positions_and_size {
|
|
||||||
if let Some(Run::Plugin(run)) = layout.run.clone() {
|
|
||||||
let pane_title = run.location_string();
|
|
||||||
let pid = new_plugin_ids
|
|
||||||
.get_mut(&run)
|
|
||||||
.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.connected_clients.borrow().iter().copied().collect(),
|
|
||||||
self.style,
|
|
||||||
layout.run.clone(),
|
|
||||||
self.debug,
|
|
||||||
self.arrow_fonts,
|
|
||||||
self.styled_underlines,
|
|
||||||
);
|
|
||||||
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
|
|
||||||
new_plugin.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
|
||||||
new_plugin.handle_pty_bytes("\n\r".as_bytes().into());
|
|
||||||
}
|
|
||||||
|
|
||||||
new_plugin.set_borderless(layout.borderless);
|
|
||||||
if let Some(exclude_from_sync) = layout.exclude_from_sync {
|
|
||||||
new_plugin.set_exclude_from_sync(exclude_from_sync);
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
layout.run.clone(),
|
|
||||||
self.debug,
|
|
||||||
self.arrow_fonts,
|
|
||||||
self.styled_underlines,
|
|
||||||
);
|
|
||||||
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
|
|
||||||
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
|
||||||
new_pane.handle_pty_bytes("\n\r".as_bytes().into());
|
|
||||||
}
|
|
||||||
new_pane.set_borderless(layout.borderless);
|
|
||||||
if let Some(exclude_from_sync) = layout.exclude_from_sync {
|
|
||||||
new_pane.set_exclude_from_sync(exclude_from_sync);
|
|
||||||
}
|
|
||||||
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().with_context(err_context)?;
|
|
||||||
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
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
let mut run_instructions_to_ignore = layout.run_instructions_to_ignore.clone();
|
||||||
|
let mut new_terminal_ids = new_terminal_ids.iter();
|
||||||
|
|
||||||
|
let mut focus_pane_id: Option<PaneId> = None;
|
||||||
|
let mut set_focus_pane_id = |layout: &TiledPaneLayout, pane_id: PaneId| {
|
||||||
|
if layout.focus.unwrap_or(false) && focus_pane_id.is_none() {
|
||||||
|
focus_pane_id = Some(pane_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// first, try to find rooms for the panes that are already running (represented by
|
||||||
|
// run_instructions_to_ignore), we try to either find an explicit position (the new
|
||||||
|
// layout has a pane with the exact run instruction) or an otherwise free position
|
||||||
|
// (the new layout has a pane with None as its run instruction)
|
||||||
|
for run_instruction in run_instructions_to_ignore.drain(..) {
|
||||||
|
if let Some(position) = positions_in_layout
|
||||||
|
.iter()
|
||||||
|
.position(|(layout, _position_and_size)| &layout.run == &run_instruction)
|
||||||
|
{
|
||||||
|
let (layout, position_and_size) = positions_in_layout.remove(position);
|
||||||
|
self.tiled_panes.set_geom_for_pane_with_run(
|
||||||
|
layout.run,
|
||||||
|
position_and_size,
|
||||||
|
layout.borderless,
|
||||||
|
);
|
||||||
|
} else if let Some(position) = positions_in_layout
|
||||||
|
.iter()
|
||||||
|
.position(|(layout, _position_and_size)| layout.run.is_none())
|
||||||
|
{
|
||||||
|
let (layout, position_and_size) = positions_in_layout.remove(position);
|
||||||
|
self.tiled_panes.set_geom_for_pane_with_run(
|
||||||
|
run_instruction,
|
||||||
|
position_and_size,
|
||||||
|
layout.borderless,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::error!(
|
||||||
|
"Failed to find room for run instruction: {:?}",
|
||||||
|
run_instruction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then, we open new panes for each run instruction in the layout with the details
|
||||||
|
// we got from the plugin thread and pty thread
|
||||||
|
let positions_and_size = positions_in_layout.iter();
|
||||||
|
for (layout, position_and_size) in positions_and_size {
|
||||||
|
if let Some(Run::Plugin(run)) = layout.run.clone() {
|
||||||
|
let pane_title = run.location_string();
|
||||||
|
let pid = new_plugin_ids
|
||||||
|
.get_mut(&run)
|
||||||
|
.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.connected_clients.borrow().iter().copied().collect(),
|
||||||
|
self.style,
|
||||||
|
layout.run.clone(),
|
||||||
|
self.debug,
|
||||||
|
self.arrow_fonts,
|
||||||
|
self.styled_underlines,
|
||||||
|
);
|
||||||
|
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
|
||||||
|
new_plugin.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
||||||
|
new_plugin.handle_pty_bytes("\n\r".as_bytes().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
new_plugin.set_borderless(layout.borderless);
|
||||||
|
if let Some(exclude_from_sync) = layout.exclude_from_sync {
|
||||||
|
new_plugin.set_exclude_from_sync(exclude_from_sync);
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
layout.run.clone(),
|
||||||
|
self.debug,
|
||||||
|
self.arrow_fonts,
|
||||||
|
self.styled_underlines,
|
||||||
|
);
|
||||||
|
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
|
||||||
|
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
|
||||||
|
new_pane.handle_pty_bytes("\n\r".as_bytes().into());
|
||||||
|
}
|
||||||
|
new_pane.set_borderless(layout.borderless);
|
||||||
|
if let Some(exclude_from_sync) = layout.exclude_from_sync {
|
||||||
|
new_pane.set_exclude_from_sync(exclude_from_sync);
|
||||||
|
}
|
||||||
|
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().with_context(err_context)?;
|
||||||
|
self.set_focused_tiled_pane(focus_pane_id, client_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn apply_floating_panes_layout(
|
fn apply_floating_panes_layout(
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ impl SwapLayouts {
|
||||||
let pane_count = tiled_panes.visible_panes_count();
|
let pane_count = tiled_panes.visible_panes_count();
|
||||||
let display_area = PaneGeom::from(&*display_area);
|
let display_area = PaneGeom::from(&*display_area);
|
||||||
if layout
|
if layout
|
||||||
.position_panes_in_space(&display_area, Some(pane_count))
|
.position_panes_in_space(&display_area, Some(pane_count), false)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
return Some(layout.clone());
|
return Some(layout.clone());
|
||||||
|
|
@ -279,7 +279,7 @@ impl SwapLayouts {
|
||||||
let pane_count = tiled_panes.visible_panes_count();
|
let pane_count = tiled_panes.visible_panes_count();
|
||||||
let display_area = PaneGeom::from(&*display_area);
|
let display_area = PaneGeom::from(&*display_area);
|
||||||
if layout
|
if layout
|
||||||
.position_panes_in_space(&display_area, Some(pane_count))
|
.position_panes_in_space(&display_area, Some(pane_count), false)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
return Some(layout.clone());
|
return Some(layout.clone());
|
||||||
|
|
|
||||||
|
|
@ -849,6 +849,7 @@ impl TiledPaneLayout {
|
||||||
&self,
|
&self,
|
||||||
space: &PaneGeom,
|
space: &PaneGeom,
|
||||||
max_panes: Option<usize>,
|
max_panes: Option<usize>,
|
||||||
|
ignore_percent_split_sizes: bool,
|
||||||
) -> Result<Vec<(TiledPaneLayout, PaneGeom)>, &'static str> {
|
) -> Result<Vec<(TiledPaneLayout, PaneGeom)>, &'static str> {
|
||||||
let layouts = match max_panes {
|
let layouts = match max_panes {
|
||||||
Some(max_panes) => {
|
Some(max_panes) => {
|
||||||
|
|
@ -874,9 +875,9 @@ impl TiledPaneLayout {
|
||||||
layout_to_split.focus_deepest_pane();
|
layout_to_split.focus_deepest_pane();
|
||||||
}
|
}
|
||||||
|
|
||||||
split_space(space, &layout_to_split, space)?
|
split_space(space, &layout_to_split, space, ignore_percent_split_sizes)?
|
||||||
},
|
},
|
||||||
None => split_space(space, self, space)?,
|
None => split_space(space, self, space, ignore_percent_split_sizes)?,
|
||||||
};
|
};
|
||||||
for (_pane_layout, pane_geom) in layouts.iter() {
|
for (_pane_layout, pane_geom) in layouts.iter() {
|
||||||
if !pane_geom.is_at_least_minimum_size() {
|
if !pane_geom.is_at_least_minimum_size() {
|
||||||
|
|
@ -1426,6 +1427,7 @@ fn split_space(
|
||||||
space_to_split: &PaneGeom,
|
space_to_split: &PaneGeom,
|
||||||
layout: &TiledPaneLayout,
|
layout: &TiledPaneLayout,
|
||||||
total_space_to_split: &PaneGeom,
|
total_space_to_split: &PaneGeom,
|
||||||
|
ignore_percent_split_sizes: bool,
|
||||||
) -> Result<Vec<(TiledPaneLayout, PaneGeom)>, &'static str> {
|
) -> Result<Vec<(TiledPaneLayout, PaneGeom)>, &'static str> {
|
||||||
let sizes: Vec<Option<SplitSize>> = if layout.children_are_stacked {
|
let sizes: Vec<Option<SplitSize>> = if layout.children_are_stacked {
|
||||||
let index_of_expanded_pane = layout.children.iter().position(|p| p.is_expanded_in_stack);
|
let index_of_expanded_pane = layout.children.iter().position(|p| p.is_expanded_in_stack);
|
||||||
|
|
@ -1440,6 +1442,15 @@ fn split_space(
|
||||||
*last_size = None;
|
*last_size = None;
|
||||||
}
|
}
|
||||||
sizes
|
sizes
|
||||||
|
} else if ignore_percent_split_sizes {
|
||||||
|
layout
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|part| match part.split_size {
|
||||||
|
Some(SplitSize::Percent(_)) => None,
|
||||||
|
split_size => split_size,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
layout.children.iter().map(|part| part.split_size).collect()
|
layout.children.iter().map(|part| part.split_size).collect()
|
||||||
};
|
};
|
||||||
|
|
@ -1539,8 +1550,12 @@ fn split_space(
|
||||||
for (i, part) in layout.children.iter().enumerate() {
|
for (i, part) in layout.children.iter().enumerate() {
|
||||||
let part_position_and_size = split_geom.get(i).unwrap();
|
let part_position_and_size = split_geom.get(i).unwrap();
|
||||||
if !part.children.is_empty() {
|
if !part.children.is_empty() {
|
||||||
let mut part_positions =
|
let mut part_positions = split_space(
|
||||||
split_space(part_position_and_size, part, total_space_to_split)?;
|
part_position_and_size,
|
||||||
|
part,
|
||||||
|
total_space_to_split,
|
||||||
|
ignore_percent_split_sizes,
|
||||||
|
)?;
|
||||||
pane_positions.append(&mut part_positions);
|
pane_positions.append(&mut part_positions);
|
||||||
} else {
|
} else {
|
||||||
let part = part.clone();
|
let part = part.clone();
|
||||||
|
|
|
||||||
|
|
@ -860,6 +860,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let swap_tiled_layouts = Some(layout.swap_tiled_layouts.clone());
|
||||||
|
let swap_floating_layouts = Some(layout.swap_floating_layouts.clone());
|
||||||
|
|
||||||
let mut tabs = layout.tabs();
|
let mut tabs = layout.tabs();
|
||||||
if tabs.len() > 1 {
|
if tabs.len() > 1 {
|
||||||
return Err(ConfigError::new_kdl_error(
|
return Err(ConfigError::new_kdl_error(
|
||||||
|
|
@ -874,8 +877,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
|
||||||
Ok(Action::NewTab(
|
Ok(Action::NewTab(
|
||||||
Some(layout),
|
Some(layout),
|
||||||
floating_panes_layout,
|
floating_panes_layout,
|
||||||
None,
|
swap_tiled_layouts,
|
||||||
None,
|
swap_floating_layouts,
|
||||||
name,
|
name,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -884,8 +887,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
|
||||||
Ok(Action::NewTab(
|
Ok(Action::NewTab(
|
||||||
Some(layout),
|
Some(layout),
|
||||||
floating_panes_layout,
|
floating_panes_layout,
|
||||||
None,
|
swap_tiled_layouts,
|
||||||
None,
|
swap_floating_layouts,
|
||||||
name,
|
name,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ impl Dimension {
|
||||||
self.inner += by;
|
self.inner += by;
|
||||||
}
|
}
|
||||||
pub fn decrease_inner(&mut self, by: usize) {
|
pub fn decrease_inner(&mut self, by: usize) {
|
||||||
self.inner -= by;
|
self.inner = self.inner.saturating_sub(by);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fixed(&self) -> bool {
|
pub fn is_fixed(&self) -> bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue