feat(layouts): exact panes constraint (#2206)

* style(fmt): remove warnings

* fix(swap-layouts): introduce exact panes constraint

* fix(swap-layouts): improve floating pane swap layout ux

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2023-02-28 22:08:01 +01:00 committed by GitHub
parent 6a8cf847da
commit ea2d9ced62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 250 additions and 80 deletions

View File

@ -467,7 +467,7 @@ impl Pty {
TerminalAction::RunCommand(ref mut command) => {
command.cwd = Some(cwd);
},
TerminalAction::OpenFile(ref file, line_number, ref mut edit_cwd) => {
TerminalAction::OpenFile(ref _file, _line_number, ref mut edit_cwd) => {
match edit_cwd.as_mut() {
Some(edit_cwd) => {
*edit_cwd = cwd.join(&edit_cwd);

View File

@ -40,12 +40,14 @@ impl SwapLayouts {
let mut base_swap_floating_layout = BTreeMap::new();
let tiled_panes_count = layout.0.pane_count();
let floating_panes_count = layout.1.len();
// we set MaxPanes to the current panes in the layout, because the base layout is not
// we set ExactPanes to the current panes in the layout, because the base layout is not
// intended to be progressive - i.e. to have additional panes added to it
// we also don't want it to be applied for less than the expected amount of panes, because
// then unintended things can happen
// we still want to keep it around in case we'd like to swap layouts without adding panes
base_swap_tiled_layout.insert(LayoutConstraint::MaxPanes(tiled_panes_count), layout.0);
base_swap_tiled_layout.insert(LayoutConstraint::ExactPanes(tiled_panes_count), layout.0);
base_swap_floating_layout
.insert(LayoutConstraint::MaxPanes(floating_panes_count), layout.1);
.insert(LayoutConstraint::ExactPanes(floating_panes_count), layout.1);
self.swap_tiled_layouts
.insert(0, (base_swap_tiled_layout, Some("BASE".into())));
self.swap_floating_layouts
@ -174,6 +176,9 @@ impl SwapLayouts {
LayoutConstraint::MinPanes(min_panes) => {
tiled_panes.visible_panes_count() >= *min_panes
},
LayoutConstraint::ExactPanes(pane_count) => {
tiled_panes.visible_panes_count() == *pane_count
},
LayoutConstraint::NoConstraint => true,
}
}
@ -189,6 +194,9 @@ impl SwapLayouts {
LayoutConstraint::MinPanes(min_panes) => {
floating_panes.visible_panes_count() >= *min_panes
},
LayoutConstraint::ExactPanes(pane_count) => {
floating_panes.visible_panes_count() == *pane_count
},
LayoutConstraint::NoConstraint => true,
}
}

View File

@ -1,26 +1,26 @@
---
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
assertion_line: 5333
assertion_line: 5700
expression: snapshot
---
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
01 (C): │
02 (C): │
03 (C): │
04 (C): │
05 (C): │
06 (C): │
07 (C): │
08 (C): │
09 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
11 (C): │
12 (C): │
13 (C): │
14 (C): │
15 (C): │
16 (C): │
17 (C): │
18 (C): │
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
01 (C): │ ││
02 (C): │ ││
03 (C): │ ││
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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘

View File

@ -5681,8 +5681,8 @@ fn when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane() {
)),
true,
);
tab.move_focus_down(client_id);
tab.move_focus_down(client_id);
let _ = tab.move_focus_down(client_id);
let _ = tab.move_focus_down(client_id);
tab.close_pane(PaneId::Terminal(3), false, Some(client_id));
tab.render(&mut output, None).unwrap();
@ -5694,7 +5694,7 @@ fn when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane() {
);
assert_eq!(
cursor_coordinates,
Some((1, 11)),
Some((62, 1)),
"cursor coordinates moved to the new pane",
);
assert_snapshot!(snapshot);

View File

@ -62,15 +62,15 @@ swap_floating_layout name="staggered" {
swap_floating_layout name="enlarged" {
floating_panes max_panes=10 {
pane { x 1; y 1; width "90%"; height "90%"; }
pane { x 2; y 2; width "90%"; height "90%"; }
pane { x 3; y 3; width "90%"; height "90%"; }
pane { x 4; y 4; width "90%"; height "90%"; }
pane { x 5; y 5; width "90%"; height "90%"; }
pane { x 6; y 6; width "90%"; height "90%"; }
pane { x 7; y 7; width "90%"; height "90%"; }
pane { x 8; y 8; width "90%"; height "90%"; }
pane { x 9; y 9; width "90%"; height "90%"; }
pane { x "5%"; y 1; width "90%"; height "90%"; }
pane { x "5%"; y 2; width "90%"; height "90%"; }
pane { x "5%"; y 3; width "90%"; height "90%"; }
pane { x "5%"; y 4; width "90%"; height "90%"; }
pane { x "5%"; y 5; width "90%"; height "90%"; }
pane { x "5%"; y 6; width "90%"; height "90%"; }
pane { x "5%"; y 7; width "90%"; height "90%"; }
pane { x "5%"; y 8; width "90%"; height "90%"; }
pane { x "5%"; y 9; width "90%"; height "90%"; }
pane focus=true { x 10; y 10; width "90%"; height "90%"; }
}
}
@ -88,4 +88,10 @@ swap_floating_layout name="spread" {
pane { x "1%"; y "1%"; width "45%"; }
pane { x "50%"; y "1%"; width "45%"; }
}
floating_panes max_panes=4 {
pane { x "1%"; y "55%"; width "45%"; height "45%"; }
pane focus=true { x "50%"; y "55%"; width "45%"; height "45%"; }
pane { x "1%"; y "1%"; width "45%"; height "45%"; }
pane { x "50%"; y "1%"; width "45%"; height "45%"; }
}
}

View File

@ -65,15 +65,15 @@ swap_floating_layout name="staggered" {
swap_floating_layout name="enlarged" {
floating_panes max_panes=10 {
pane { x 1; y 1; width "90%"; height "90%"; }
pane { x 2; y 2; width "90%"; height "90%"; }
pane { x 3; y 3; width "90%"; height "90%"; }
pane { x 4; y 4; width "90%"; height "90%"; }
pane { x 5; y 5; width "90%"; height "90%"; }
pane { x 6; y 6; width "90%"; height "90%"; }
pane { x 7; y 7; width "90%"; height "90%"; }
pane { x 8; y 8; width "90%"; height "90%"; }
pane { x 9; y 9; width "90%"; height "90%"; }
pane { x "5%"; y 1; width "90%"; height "90%"; }
pane { x "5%"; y 2; width "90%"; height "90%"; }
pane { x "5%"; y 3; width "90%"; height "90%"; }
pane { x "5%"; y 4; width "90%"; height "90%"; }
pane { x "5%"; y 5; width "90%"; height "90%"; }
pane { x "5%"; y 6; width "90%"; height "90%"; }
pane { x "5%"; y 7; width "90%"; height "90%"; }
pane { x "5%"; y 8; width "90%"; height "90%"; }
pane { x "5%"; y 9; width "90%"; height "90%"; }
pane focus=true { x 10; y 10; width "90%"; height "90%"; }
}
}
@ -91,4 +91,10 @@ swap_floating_layout name="spread" {
pane { x "1%"; y "1%"; width "45%"; }
pane { x "50%"; y "1%"; width "45%"; }
}
floating_panes max_panes=4 {
pane { x "1%"; y "55%"; width "45%"; height "45%"; }
pane focus=true { x "50%"; y "55%"; width "45%"; height "45%"; }
pane { x "1%"; y "1%"; width "45%"; height "45%"; }
pane { x "50%"; y "1%"; width "45%"; height "45%"; }
}
}

View File

@ -61,7 +61,7 @@ swap_tiled_layout name="horizontal" {
swap_tiled_layout name="stacked" {
ui min_panes=6 {
pane split_direction="vertical" {
pane focus=true
pane
pane stacked=true { children; }
}
}
@ -73,15 +73,15 @@ swap_floating_layout name="staggered" {
swap_floating_layout name="enlarged" {
floating_panes max_panes=10 {
pane { x 1; y 1; width "90%"; height "90%"; }
pane { x 2; y 2; width "90%"; height "90%"; }
pane { x 3; y 3; width "90%"; height "90%"; }
pane { x 4; y 4; width "90%"; height "90%"; }
pane { x 5; y 5; width "90%"; height "90%"; }
pane { x 6; y 6; width "90%"; height "90%"; }
pane { x 7; y 7; width "90%"; height "90%"; }
pane { x 8; y 8; width "90%"; height "90%"; }
pane { x 9; y 9; width "90%"; height "90%"; }
pane { x "5%"; y 1; width "90%"; height "90%"; }
pane { x "5%"; y 2; width "90%"; height "90%"; }
pane { x "5%"; y 3; width "90%"; height "90%"; }
pane { x "5%"; y 4; width "90%"; height "90%"; }
pane { x "5%"; y 5; width "90%"; height "90%"; }
pane { x "5%"; y 6; width "90%"; height "90%"; }
pane { x "5%"; y 7; width "90%"; height "90%"; }
pane { x "5%"; y 8; width "90%"; height "90%"; }
pane { x "5%"; y 9; width "90%"; height "90%"; }
pane focus=true { x 10; y 10; width "90%"; height "90%"; }
}
}
@ -99,4 +99,10 @@ swap_floating_layout name="spread" {
pane { x "1%"; y "1%"; width "45%"; }
pane { x "50%"; y "1%"; width "45%"; }
}
floating_panes max_panes=4 {
pane { x "1%"; y "55%"; width "45%"; height "45%"; }
pane focus=true { x "50%"; y "55%"; width "45%"; height "45%"; }
pane { x "1%"; y "1%"; width "45%"; height "45%"; }
pane { x "50%"; y "1%"; width "45%"; height "45%"; }
}
}

View File

@ -252,6 +252,7 @@ impl fmt::Display for RunPluginLocation {
pub enum LayoutConstraint {
MaxPanes(usize),
MinPanes(usize),
ExactPanes(usize),
NoConstraint,
}

View File

@ -119,6 +119,7 @@ impl<'a> KdlLayoutParser<'a> {
|| property_name == "children"
|| property_name == "max_panes"
|| property_name == "min_panes"
|| property_name == "exact_panes"
}
fn assert_legal_node_name(&self, name: &str, kdl_node: &KdlNode) -> Result<(), ConfigError> {
if name.contains(char::is_whitespace) {
@ -1747,6 +1748,12 @@ impl<'a> KdlLayoutParser<'a> {
tab_template_kdl_node,
)?;
swap_tiled_layout.insert(layout_constraint, layout);
} else {
return Err(ConfigError::new_layout_kdl_error(
format!("Unknown layout node: '{}'", layout_node_name),
layout.span().offset(),
layout.span().len(),
));
}
}
swap_tiled_layouts.push((swap_tiled_layout, swap_layout_name));
@ -1787,6 +1794,12 @@ impl<'a> KdlLayoutParser<'a> {
tab_template_kdl_node,
)?;
swap_floating_layout.insert(layout_constraint, layout);
} else {
return Err(ConfigError::new_layout_kdl_error(
format!("Unknown layout node: '{}'", layout_node_name),
layout.span().offset(),
layout.span().len(),
));
}
}
swap_floating_layouts.push((swap_floating_layout, swap_layout_name));
@ -1814,17 +1827,41 @@ impl<'a> KdlLayoutParser<'a> {
layout_node
));
};
if let Some(exact_panes) =
kdl_get_string_property_or_child_value!(layout_node, "exact_panes")
{
return Err(kdl_parsing_error!(
format!(
"exact_panes should be a fixed number (eg. 1) and not a quoted string (\"{}\")",
exact_panes,
),
layout_node
));
};
let max_panes = kdl_get_int_property_or_child_value!(layout_node, "max_panes");
let min_panes = kdl_get_int_property_or_child_value!(layout_node, "min_panes");
match (min_panes, max_panes) {
(Some(_min_panes), Some(_max_panes)) => Err(kdl_parsing_error!(
let exact_panes = kdl_get_int_property_or_child_value!(layout_node, "exact_panes");
let mut constraint_count = 0;
let mut constraint = None;
if let Some(max_panes) = max_panes {
constraint_count += 1;
constraint = Some(LayoutConstraint::MaxPanes(max_panes as usize));
}
if let Some(min_panes) = min_panes {
constraint_count += 1;
constraint = Some(LayoutConstraint::MinPanes(min_panes as usize));
}
if let Some(exact_panes) = exact_panes {
constraint_count += 1;
constraint = Some(LayoutConstraint::ExactPanes(exact_panes as usize));
}
if constraint_count > 1 {
return Err(kdl_parsing_error!(
format!("cannot have more than one constraint (eg. max_panes + min_panes)'"),
layout_node
)),
(Some(min_panes), None) => Ok(LayoutConstraint::MinPanes(min_panes as usize)),
(None, Some(max_panes)) => Ok(LayoutConstraint::MaxPanes(max_panes as usize)),
_ => Ok(LayoutConstraint::NoConstraint),
));
}
Ok(constraint.unwrap_or(LayoutConstraint::NoConstraint))
}
fn populate_one_swap_tiled_layout(
&self,

View File

@ -1,6 +1,6 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 601
assertion_line: 622
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@ -1213,8 +1213,8 @@ Layout {
),
),
x: Some(
Fixed(
1,
Percent(
5,
),
),
y: Some(
@ -1238,8 +1238,8 @@ Layout {
),
),
x: Some(
Fixed(
2,
Percent(
5,
),
),
y: Some(
@ -1263,8 +1263,8 @@ Layout {
),
),
x: Some(
Fixed(
3,
Percent(
5,
),
),
y: Some(
@ -1288,8 +1288,8 @@ Layout {
),
),
x: Some(
Fixed(
4,
Percent(
5,
),
),
y: Some(
@ -1313,7 +1313,7 @@ Layout {
),
),
x: Some(
Fixed(
Percent(
5,
),
),
@ -1338,8 +1338,8 @@ Layout {
),
),
x: Some(
Fixed(
6,
Percent(
5,
),
),
y: Some(
@ -1363,8 +1363,8 @@ Layout {
),
),
x: Some(
Fixed(
7,
Percent(
5,
),
),
y: Some(
@ -1388,8 +1388,8 @@ Layout {
),
),
x: Some(
Fixed(
8,
Percent(
5,
),
),
y: Some(
@ -1413,8 +1413,8 @@ Layout {
),
),
x: Some(
Fixed(
9,
Percent(
5,
),
),
y: Some(
@ -1596,6 +1596,112 @@ Layout {
focus: None,
},
],
MaxPanes(
4,
): [
FloatingPaneLayout {
name: None,
height: Some(
Percent(
45,
),
),
width: Some(
Percent(
45,
),
),
x: Some(
Percent(
1,
),
),
y: Some(
Percent(
55,
),
),
run: None,
focus: None,
},
FloatingPaneLayout {
name: None,
height: Some(
Percent(
45,
),
),
width: Some(
Percent(
45,
),
),
x: Some(
Percent(
50,
),
),
y: Some(
Percent(
55,
),
),
run: None,
focus: Some(
true,
),
},
FloatingPaneLayout {
name: None,
height: Some(
Percent(
45,
),
),
width: Some(
Percent(
45,
),
),
x: Some(
Percent(
1,
),
),
y: Some(
Percent(
1,
),
),
run: None,
focus: None,
},
FloatingPaneLayout {
name: None,
height: Some(
Percent(
45,
),
),
width: Some(
Percent(
45,
),
),
x: Some(
Percent(
50,
),
),
y: Some(
Percent(
1,
),
),
run: None,
focus: None,
},
],
},
Some(
"spread",