diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index 84e85431f7..a85cf80fd8 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -532,7 +532,7 @@ impl FloatingPanes { pub fn focus_pane_on_edge(&mut self, direction: Direction, client_id: ClientId) { let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); - let mut floating_pane_grid = FloatingPaneGrid::new( + let floating_pane_grid = FloatingPaneGrid::new( &mut self.panes, &mut self.desired_pane_positions, display_area, diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index d4eb66b22a..89a329bb44 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -176,13 +176,14 @@ impl TiledPanes { *self.display_area.borrow(), *self.viewport.borrow(), ); - pane_grid + let has_room_for_new_pane = pane_grid .find_room_for_new_pane(cursor_height_width_ratio) - .is_some() + .is_some(); + has_room_for_new_pane || pane_grid.has_room_for_new_stacked_pane() } fn add_pane(&mut self, pane_id: PaneId, mut pane: Box, should_relayout: bool) { let cursor_height_width_ratio = self.cursor_height_width_ratio(); - let pane_grid = TiledPaneGrid::new( + let mut pane_grid = TiledPaneGrid::new( &mut self.panes, &self.panes_to_hide, *self.display_area.borrow(), @@ -190,18 +191,34 @@ impl TiledPanes { ); let pane_id_and_split_direction = pane_grid.find_room_for_new_pane(cursor_height_width_ratio); - if let Some((pane_id_to_split, split_direction)) = pane_id_and_split_direction { - // this unwrap is safe because floating panes should not be visible if there are no floating panes - let pane_to_split = self.panes.get_mut(&pane_id_to_split).unwrap(); - let size_of_both_panes = pane_to_split.position_and_size(); - if let Some((first_geom, second_geom)) = split(split_direction, &size_of_both_panes) { - pane_to_split.set_geom(first_geom); - pane.set_geom(second_geom); - self.panes.insert(pane_id, pane); - if should_relayout { - self.relayout(!split_direction); + match pane_id_and_split_direction { + Some((pane_id_to_split, split_direction)) => { + // this unwrap is safe because floating panes should not be visible if there are no floating panes + let pane_to_split = self.panes.get_mut(&pane_id_to_split).unwrap(); + let size_of_both_panes = pane_to_split.position_and_size(); + if let Some((first_geom, second_geom)) = split(split_direction, &size_of_both_panes) + { + pane_to_split.set_geom(first_geom); + pane.set_geom(second_geom); + self.panes.insert(pane_id, pane); + if should_relayout { + self.relayout(!split_direction); + } } - } + }, + None => { + // we couldn't add the pane normally, let's see if there's room in one of the + // stacks... + match pane_grid.make_room_in_stack_for_pane() { + Ok(new_pane_geom) => { + pane.set_geom(new_pane_geom); + self.panes.insert(pane_id, pane); // TODO: is set_geom the right one? + }, + Err(e) => { + log::error!("Failed to add pane to stack: {:?}", e); + }, + } + }, } } pub fn fixed_pane_geoms(&self) -> Vec { diff --git a/zellij-server/src/panes/tiled_panes/stacked_panes.rs b/zellij-server/src/panes/tiled_panes/stacked_panes.rs index 00b193f690..cc0b0f151d 100644 --- a/zellij-server/src/panes/tiled_panes/stacked_panes.rs +++ b/zellij-server/src/panes/tiled_panes/stacked_panes.rs @@ -430,6 +430,58 @@ impl<'a> StackedPanes<'a> { stacked_pane_ids_over_flexible_panes, )) } + pub fn make_room_for_new_pane(&mut self) -> Result { + let err_context = || format!("Failed to add pane to stack"); + let all_stacks = self.get_all_stacks()?; + for stack in all_stacks { + if let Some((id_of_flexible_pane_in_stack, _flexible_pane_in_stack)) = stack + .iter() + .find(|(_p_id, p)| !p.rows.is_fixed() && p.rows.as_usize() > 1) + { + self.make_lowest_pane_in_stack_flexible(id_of_flexible_pane_in_stack)?; + let all_stacked_pane_positions = + self.positions_in_stack(id_of_flexible_pane_in_stack)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + let (flexible_pane_id, mut flexible_pane_geom) = *all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + let mut position_for_new_pane = flexible_pane_geom.clone(); + position_for_new_pane + .rows + .set_inner(position_for_new_pane.rows.as_usize() - 1); + position_for_new_pane.y = position_for_new_pane.y + 1; + flexible_pane_geom.rows = Dimension::fixed(1); + self.panes + .borrow_mut() + .get_mut(&flexible_pane_id) + .with_context(err_context)? + .set_geom(flexible_pane_geom); + return Ok(position_for_new_pane); + } + } + Err(anyhow!("Not enough room for another pane!")) + } + fn get_all_stacks(&self) -> Result>> { + let err_context = || "Failed to get positions in stack"; + let panes = self.panes.borrow(); + let all_flexible_panes_in_stack: Vec = panes + .iter() + .filter(|(_pid, p)| { + p.position_and_size().is_stacked && !p.position_and_size().rows.is_fixed() + }) + .map(|(pid, _p)| *pid) + .collect(); + let mut stacks = vec![]; + for pane_id in all_flexible_panes_in_stack { + stacks.push( + self.positions_in_stack(&pane_id) + .with_context(err_context)?, + ); + } + Ok(stacks) + } fn fill_space_over_one_liner_pane(&mut self, id: &PaneId) -> Result { let (position_of_current_pane, position_of_flexible_pane) = self.position_of_current_and_flexible_pane(id)?; diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs index 5ee5cf3ca7..7da65d712f 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -1366,6 +1366,21 @@ impl<'a> TiledPaneGrid<'a> { direction.map(|direction| (*t_id_to_split, direction)) }) } + pub fn has_room_for_new_stacked_pane(&self) -> bool { + let panes = self.panes.borrow(); + let flexible_pane_in_stack: Vec<(&PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| { + p.selectable() && p.current_geom().is_stacked && !p.current_geom().rows.is_fixed() + }) + .collect(); + flexible_pane_in_stack + .iter() + .any(|(_p_id, p)| p.current_geom().rows.as_usize() > 1) + } + pub fn make_room_in_stack_for_pane(&mut self) -> Result { + StackedPanes::new(self.panes.clone()).make_room_for_new_pane() + } } pub fn split(direction: SplitDirection, rect: &PaneGeom) -> Option<(PaneGeom, PaneGeom)> { diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 521e1dbbd5..ae3dc52ecd 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -1916,8 +1916,6 @@ impl Tab { self.tiled_panes.focus_previous_pane(client_id); } pub fn focus_pane_on_edge(&mut self, direction: Direction, client_id: ClientId) { - let err_context = || format!("failed to move focus left for client {}", client_id); - if self.floating_panes.panes_are_visible() { self.floating_panes.focus_pane_on_edge(direction, client_id); } else if self.has_selectable_panes() && !self.tiled_panes.fullscreen_is_active() { diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_only_stacked_children.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_only_stacked_children.snap new file mode 100644 index 0000000000..704f019c4c --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_only_stacked_children.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3253 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): ┌ Pane #4 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 1652eb5ae3..8ccce263e8 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -3206,6 +3206,53 @@ fn swap_tiled_layout_with_stacked_children() { assert_snapshot!(snapshot); } +#[test] +fn swap_tiled_layout_with_only_stacked_children() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane stacked=true { children; } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + 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] fn swap_tiled_layout_with_stacked_children_and_no_pane_frames() { let size = Size {