#![allow(clippy::unwrap_used, clippy::panic)]
use panes::{CardSpan, Grid, Layout, PanelInputKind, Rect};
#[test]
fn master_stack_basic() {
let resolved = Layout::master_stack(["editor", "chat", "status"])
.resolve(80.0, 24.0)
.unwrap();
let editor = resolved.by_kind("editor")[0];
let chat = resolved.by_kind("chat")[0];
let status = resolved.by_kind("status")[0];
assert_eq!(
*resolved.get(editor).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 40.0,
h: 24.0
}
);
assert_eq!(
*resolved.get(chat).unwrap(),
Rect {
x: 40.0,
y: 0.0,
w: 40.0,
h: 12.0
}
);
assert_eq!(
*resolved.get(status).unwrap(),
Rect {
x: 40.0,
y: 12.0,
w: 40.0,
h: 12.0
}
);
}
#[test]
fn master_stack_custom_ratio() {
let resolved = Layout::master_stack(["editor", "chat", "status"])
.master_ratio(0.6)
.resolve(100.0, 24.0)
.unwrap();
let editor = resolved.by_kind("editor")[0];
let chat = resolved.by_kind("chat")[0];
assert_eq!(resolved.get(editor).unwrap().w, 60.0);
assert_eq!(resolved.get(chat).unwrap().w, 40.0);
}
#[test]
fn master_stack_with_gap() {
let resolved = Layout::master_stack(["a", "b", "c"])
.gap(10.0)
.resolve(100.0, 30.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
let a_rect = resolved.get(a).unwrap();
let b_rect = resolved.get(b).unwrap();
let c_rect = resolved.get(c).unwrap();
assert!(b_rect.x > a_rect.x + a_rect.w);
assert!(c_rect.y > b_rect.y + b_rect.h);
}
#[test]
fn master_stack_single_panel() {
let resolved = Layout::master_stack(["solo"]).resolve(80.0, 24.0).unwrap();
let solo = resolved.by_kind("solo")[0];
assert_eq!(
*resolved.get(solo).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 80.0,
h: 24.0
}
);
}
#[test]
fn sidebar_basic() {
let resolved = Layout::sidebar("nav", "content")
.resolve(100.0, 24.0)
.unwrap();
let nav = resolved.by_kind("nav")[0];
let content = resolved.by_kind("content")[0];
assert_eq!(
*resolved.get(nav).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 20.0,
h: 24.0
}
);
assert_eq!(
*resolved.get(content).unwrap(),
Rect {
x: 20.0,
y: 0.0,
w: 80.0,
h: 24.0
}
);
}
#[test]
fn sidebar_custom_width() {
let resolved = Layout::sidebar("nav", "content")
.sidebar_width(30.0)
.resolve(100.0, 24.0)
.unwrap();
let nav = resolved.by_kind("nav")[0];
let content = resolved.by_kind("content")[0];
assert_eq!(resolved.get(nav).unwrap().w, 30.0);
assert_eq!(resolved.get(content).unwrap().w, 70.0);
}
#[test]
fn split_horizontal() {
let resolved = Layout::split("left", "right").resolve(100.0, 24.0).unwrap();
let left = resolved.by_kind("left")[0];
let right = resolved.by_kind("right")[0];
assert_eq!(
*resolved.get(left).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 50.0,
h: 24.0
}
);
assert_eq!(
*resolved.get(right).unwrap(),
Rect {
x: 50.0,
y: 0.0,
w: 50.0,
h: 24.0
}
);
}
#[test]
fn split_vertical() {
let resolved = Layout::split("top", "bottom")
.vertical()
.resolve(80.0, 24.0)
.unwrap();
let top = resolved.by_kind("top")[0];
let bottom = resolved.by_kind("bottom")[0];
assert_eq!(
*resolved.get(top).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 80.0,
h: 12.0
}
);
assert_eq!(
*resolved.get(bottom).unwrap(),
Rect {
x: 0.0,
y: 12.0,
w: 80.0,
h: 12.0
}
);
}
#[test]
fn split_custom_ratio() {
let resolved = Layout::split("left", "right")
.ratio(0.7)
.resolve(100.0, 24.0)
.unwrap();
let left = resolved.by_kind("left")[0];
let right = resolved.by_kind("right")[0];
assert_eq!(resolved.get(left).unwrap().w, 70.0);
assert_eq!(resolved.get(right).unwrap().w, 30.0);
}
#[test]
fn master_stack_try_from() {
let preset = Layout::master_stack(["a", "b"]);
let layout: Layout = preset.build().unwrap();
let resolved = layout.resolve(80.0, 24.0).unwrap();
assert_eq!(resolved.by_kind("a").len(), 1);
assert_eq!(resolved.by_kind("b").len(), 1);
}
#[test]
fn dashboard_uniform() {
let resolved = Layout::dashboard([("a", 1), ("b", 1), ("c", 1), ("d", 1)])
.resolve(100.0, 100.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a).unwrap().w, 25.0);
assert_eq!(resolved.get(b).unwrap().w, 25.0);
}
#[test]
fn dashboard_mixed_spans() {
let resolved = Layout::dashboard([("wide", 2), ("narrow1", 1), ("narrow2", 1)])
.resolve(100.0, 100.0)
.unwrap();
let wide = resolved.by_kind("wide")[0];
let n1 = resolved.by_kind("narrow1")[0];
assert_eq!(resolved.get(wide).unwrap().w, 50.0);
assert_eq!(resolved.get(n1).unwrap().w, 25.0);
}
#[test]
fn dashboard_auto_fill_resolves() {
let resolved = Layout::dashboard([("a", 1), ("b", 1), ("c", 1), ("d", 1)])
.auto_fill(200.0)
.resolve(800.0, 600.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a).unwrap().w, 200.0);
assert_eq!(resolved.get(b).unwrap().w, 200.0);
}
#[test]
fn dashboard_auto_fill_narrow_viewport() {
let resolved = Layout::dashboard([("a", 1), ("b", 1), ("c", 1), ("d", 1)])
.auto_fill(200.0)
.resolve(300.0, 600.0)
.unwrap();
let a = resolved.by_kind("a")[0];
assert_eq!(resolved.get(a).unwrap().w, 300.0);
}
#[test]
fn dashboard_auto_fill_with_spans() {
let resolved = Layout::dashboard([("wide", 2), ("narrow", 1)])
.auto_fill(100.0)
.resolve(400.0, 400.0)
.unwrap();
let wide = resolved.by_kind("wide")[0];
let narrow = resolved.by_kind("narrow")[0];
assert!(resolved.get(wide).unwrap().w > resolved.get(narrow).unwrap().w);
}
#[test]
fn dashboard_full_width_spans_all_columns() {
let resolved = Layout::dashboard([
("narrow", CardSpan::Columns(1)),
("wide", CardSpan::FullWidth),
])
.columns(4)
.resolve(100.0, 100.0)
.unwrap();
let wide = resolved.by_kind("wide")[0];
assert_eq!(resolved.get(wide).unwrap().w, 100.0);
}
#[test]
fn dashboard_full_width_with_auto_fill() {
let resolved = Layout::dashboard([
("sidebar", CardSpan::Columns(1)),
("content", CardSpan::FullWidth),
("footer", CardSpan::Columns(1)),
])
.auto_fill(200.0)
.resolve(800.0, 600.0)
.unwrap();
let content = resolved.by_kind("content")[0];
assert_eq!(resolved.get(content).unwrap().w, 800.0);
}
#[test]
fn dashboard_auto_fill_rejects_zero() {
let err = Layout::dashboard([("a", 1)])
.auto_fill(0.0)
.build()
.unwrap_err();
assert!(
err.to_string().contains("min_column_width"),
"expected min_column_width error, got: {err}"
);
}
#[test]
fn dashboard_auto_fit_resolves() {
let resolved = Layout::dashboard([("a", 1), ("b", 1)])
.auto_fit(200.0)
.resolve(800.0, 600.0)
.unwrap();
let a = resolved.by_kind("a")[0];
assert!(resolved.get(a).unwrap().w >= 200.0);
}
#[test]
fn dwindle_two_panels() {
let resolved = Layout::dwindle(["a", "b"]).resolve(100.0, 100.0).unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(
*resolved.get(a).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 50.0,
h: 100.0
}
);
assert_eq!(
*resolved.get(b).unwrap(),
Rect {
x: 50.0,
y: 0.0,
w: 50.0,
h: 100.0
}
);
}
#[test]
fn dwindle_four_panels() {
let resolved = Layout::dwindle(["a", "b", "c", "d"])
.resolve(100.0, 100.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
let d = resolved.by_kind("d")[0];
assert_eq!(resolved.get(a).unwrap().w, 50.0);
assert_eq!(resolved.get(a).unwrap().h, 100.0);
assert_eq!(resolved.get(b).unwrap().x, 50.0);
assert_eq!(resolved.get(b).unwrap().h, 50.0);
assert_eq!(resolved.get(c).unwrap().y, 50.0);
assert_eq!(resolved.get(d).unwrap().y, 50.0);
assert_eq!(resolved.get(c).unwrap().w, 25.0);
assert_eq!(resolved.get(d).unwrap().w, 25.0);
}
#[test]
fn dwindle_custom_ratio() {
let resolved = Layout::dwindle(["a", "b"])
.ratio(0.6)
.resolve(100.0, 100.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a).unwrap().w, 60.0);
assert_eq!(resolved.get(b).unwrap().w, 40.0);
}
#[test]
fn spiral_two_panels() {
let resolved = Layout::spiral(["a", "b"]).resolve(100.0, 100.0).unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(
*resolved.get(a).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 50.0,
h: 100.0
}
);
assert_eq!(
*resolved.get(b).unwrap(),
Rect {
x: 50.0,
y: 0.0,
w: 50.0,
h: 100.0
}
);
}
#[test]
fn spiral_four_panels() {
let resolved = Layout::spiral(["a", "b", "c", "d"])
.resolve(100.0, 100.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
let d = resolved.by_kind("d")[0];
assert_eq!(resolved.get(a).unwrap().w, 50.0);
assert_eq!(resolved.get(b).unwrap().x, 50.0);
assert_eq!(resolved.get(b).unwrap().h, 50.0);
assert!(resolved.get(d).unwrap().x < resolved.get(c).unwrap().x);
}
#[test]
fn dwindle_single_panel() {
let resolved = Layout::dwindle(["solo"]).resolve(80.0, 24.0).unwrap();
let solo = resolved.by_kind("solo")[0];
assert_eq!(
*resolved.get(solo).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 80.0,
h: 24.0
}
);
}
#[test]
fn centered_master_basic() {
let resolved = Layout::centered_master(["master", "a", "b", "c", "d"])
.resolve(100.0, 100.0)
.unwrap();
let master = resolved.by_kind("master")[0];
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let master_rect = resolved.get(master).unwrap();
let a_rect = resolved.get(a).unwrap();
let b_rect = resolved.get(b).unwrap();
assert!(master_rect.x > 0.0);
assert!(a_rect.x < master_rect.x);
assert!(b_rect.x > master_rect.x);
}
#[test]
fn centered_master_three_panels() {
let resolved = Layout::centered_master(["master", "left", "right"])
.resolve(100.0, 100.0)
.unwrap();
let master = resolved.by_kind("master")[0];
let left = resolved.by_kind("left")[0];
let right = resolved.by_kind("right")[0];
assert!(resolved.get(left).unwrap().x < resolved.get(master).unwrap().x);
assert!(resolved.get(right).unwrap().x > resolved.get(master).unwrap().x);
}
#[test]
fn centered_master_custom_ratio() {
let resolved = Layout::centered_master(["master", "a", "b"])
.master_ratio(0.4)
.resolve(100.0, 100.0)
.unwrap();
let master = resolved.by_kind("master")[0];
assert_eq!(resolved.get(master).unwrap().w, 40.0);
}
#[test]
fn holy_grail_basic() {
let resolved = Layout::holy_grail("header", "footer", "left", "main", "right")
.resolve(100.0, 100.0)
.unwrap();
let header = resolved.by_kind("header")[0];
let footer = resolved.by_kind("footer")[0];
let left = resolved.by_kind("left")[0];
let main = resolved.by_kind("main")[0];
let right = resolved.by_kind("right")[0];
assert_eq!(resolved.get(header).unwrap().y, 0.0);
assert_eq!(resolved.get(header).unwrap().w, 100.0);
assert_eq!(resolved.get(header).unwrap().h, 1.0);
assert_eq!(resolved.get(footer).unwrap().w, 100.0);
assert_eq!(resolved.get(footer).unwrap().h, 1.0);
assert_eq!(resolved.get(left).unwrap().w, 20.0);
assert!(resolved.get(main).unwrap().w > resolved.get(left).unwrap().w);
assert_eq!(resolved.get(right).unwrap().w, 20.0);
}
#[test]
fn holy_grail_custom_sizes() {
let resolved = Layout::holy_grail("header", "footer", "left", "main", "right")
.header_height(3.0)
.footer_height(2.0)
.sidebar_width(15.0)
.resolve(100.0, 100.0)
.unwrap();
let header = resolved.by_kind("header")[0];
let footer = resolved.by_kind("footer")[0];
let left = resolved.by_kind("left")[0];
assert_eq!(resolved.get(header).unwrap().h, 3.0);
assert_eq!(resolved.get(footer).unwrap().h, 2.0);
assert_eq!(resolved.get(left).unwrap().w, 15.0);
}
#[test]
fn monocle_shows_active() {
let resolved = Layout::monocle(["a", "b", "c"])
.resolve(80.0, 24.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
assert_eq!(
*resolved.get(a).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 80.0,
h: 24.0
}
);
assert_eq!(resolved.get(b).unwrap().h, 0.0);
assert_eq!(resolved.get(c).unwrap().h, 0.0);
}
#[test]
fn monocle_second_active() {
let resolved = Layout::monocle(["a", "b", "c"])
.active(1)
.resolve(80.0, 24.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a).unwrap().h, 0.0);
assert_eq!(
*resolved.get(b).unwrap(),
Rect {
x: 0.0,
y: 0.0,
w: 80.0,
h: 24.0
}
);
}
#[test]
fn deck_basic() {
let resolved = Layout::deck(["master", "a", "b"])
.resolve(80.0, 24.0)
.unwrap();
let master = resolved.by_kind("master")[0];
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(resolved.get(master).unwrap().w, 40.0);
assert_eq!(resolved.get(master).unwrap().h, 24.0);
assert_eq!(resolved.get(a).unwrap().h, 24.0);
assert_eq!(resolved.get(b).unwrap().h, 0.0);
}
#[test]
fn deck_switch_active() {
let resolved = Layout::deck(["master", "a", "b"])
.active(1)
.resolve(80.0, 24.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a).unwrap().h, 0.0);
assert_eq!(resolved.get(b).unwrap().h, 24.0);
}
#[test]
fn tabbed_basic() {
let resolved = Layout::tabbed(["a", "b"]).resolve(80.0, 24.0).unwrap();
assert!(resolved.by_kind("a_tab").is_empty());
let a_content = resolved.by_kind("a")[0];
let b_content = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a_content).unwrap().h, 23.0);
assert_eq!(resolved.get(b_content).unwrap().h, 0.0);
let tab_decorations: Vec<_> = resolved
.decoration_panels()
.iter()
.filter(|d| d.role == panes::DecorationRole::Tab)
.collect();
assert_eq!(tab_decorations.len(), 2);
let a_tab_rect = resolved.get(tab_decorations[0].id).unwrap();
assert_eq!(a_tab_rect.h, 1.0);
}
#[test]
fn tabbed_switch() {
let resolved = Layout::tabbed(["a", "b"])
.active(1)
.resolve(80.0, 24.0)
.unwrap();
let a_content = resolved.by_kind("a")[0];
let b_content = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a_content).unwrap().h, 0.0);
assert_eq!(resolved.get(b_content).unwrap().h, 23.0);
}
#[test]
fn stacked_basic() {
let resolved = Layout::stacked(["a", "b"]).resolve(80.0, 24.0).unwrap();
assert!(resolved.by_kind("a_title").is_empty());
assert!(resolved.by_kind("b_title").is_empty());
let a_content = resolved.by_kind("a")[0];
let b_content = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a_content).unwrap().h, 22.0);
assert_eq!(resolved.get(b_content).unwrap().h, 0.0);
let title_decorations: Vec<_> = resolved
.decoration_panels()
.iter()
.filter(|d| d.role == panes::DecorationRole::Title)
.collect();
assert_eq!(title_decorations.len(), 2);
for d in &title_decorations {
assert_eq!(resolved.get(d.id).unwrap().h, 1.0);
}
}
#[test]
fn stacked_switch() {
let resolved = Layout::stacked(["a", "b"])
.active(1)
.resolve(80.0, 24.0)
.unwrap();
let a_content = resolved.by_kind("a")[0];
let b_content = resolved.by_kind("b")[0];
assert_eq!(resolved.get(a_content).unwrap().h, 0.0);
assert_eq!(resolved.get(b_content).unwrap().h, 22.0);
}
#[test]
fn tabbed_content_kinds_resolve_without_synthesized_tab_kinds() {
let resolved = Layout::tabbed(["tab", "editor_tab", "logs"])
.resolve(80.0, 24.0)
.unwrap();
assert_eq!(resolved.by_kind("tab").len(), 1);
assert_eq!(resolved.by_kind("editor_tab").len(), 1);
assert_eq!(resolved.by_kind("logs").len(), 1);
assert!(resolved.by_kind("tab_tab").is_empty());
assert!(resolved.by_kind("editor_tab_tab").is_empty());
assert!(resolved.by_kind("logs_tab").is_empty());
let decorations = resolved.decoration_panels();
assert_eq!(decorations.len(), 3);
for d in decorations {
assert_eq!(d.role, panes::DecorationRole::Tab);
}
}
#[test]
fn stacked_content_kinds_resolve_without_synthesized_title_kinds() {
let resolved = Layout::stacked(["title", "editor_title", "logs"])
.resolve(80.0, 24.0)
.unwrap();
assert_eq!(resolved.by_kind("title").len(), 1);
assert_eq!(resolved.by_kind("editor_title").len(), 1);
assert_eq!(resolved.by_kind("logs").len(), 1);
assert!(resolved.by_kind("title_title").is_empty());
assert!(resolved.by_kind("editor_title_title").is_empty());
assert!(resolved.by_kind("logs_title").is_empty());
let decorations = resolved.decoration_panels();
assert_eq!(decorations.len(), 3);
for d in decorations {
assert_eq!(d.role, panes::DecorationRole::Title);
}
}
#[test]
fn scrollable_focus_zero_shows_first_pair() {
let resolved = Layout::scrollable(["a", "b", "c"])
.active(0)
.resolve(100.0, 24.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
assert!(resolved.get(a).unwrap().w > 0.0);
assert!(resolved.get(b).unwrap().w > 0.0);
assert_eq!(resolved.get(c).unwrap().w, 0.0);
}
#[test]
fn scrollable_focus_one_stays_in_first_pair() {
let resolved = Layout::scrollable(["a", "b", "c"])
.active(1)
.resolve(100.0, 24.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
assert!(resolved.get(a).unwrap().w > 0.0);
assert!(resolved.get(b).unwrap().w > 0.0);
assert_eq!(resolved.get(c).unwrap().w, 0.0);
}
#[test]
fn scrollable_focus_two_shifts_window() {
let resolved = Layout::scrollable(["a", "b", "c"])
.active(2)
.resolve(100.0, 24.0)
.unwrap();
let a = resolved.by_kind("a")[0];
let b = resolved.by_kind("b")[0];
let c = resolved.by_kind("c")[0];
assert_eq!(resolved.get(a).unwrap().w, 0.0);
assert!(resolved.get(b).unwrap().w > 0.0);
assert!(resolved.get(c).unwrap().w > 0.0);
}
#[test]
fn scrollable_single_panel_fills_viewport() {
let resolved = Layout::scrollable(["a"]).resolve(100.0, 24.0).unwrap();
let a = resolved.by_kind("a")[0];
assert_eq!(resolved.get(a).unwrap().w, 100.0);
}
#[test]
fn presets_returns_13_entries() {
assert_eq!(Layout::presets().len(), 13);
}
#[test]
fn presets_names_are_sorted() {
let names: Vec<&str> = Layout::presets().iter().map(|p| p.name).collect();
let mut sorted = names.clone();
sorted.sort_unstable();
assert_eq!(names, sorted);
}
#[test]
fn presets_fixed_slots_are_sidebar_holy_grail_split() {
let mut fixed: Vec<&str> = Layout::presets()
.iter()
.filter(|p| p.input == PanelInputKind::FixedSlots)
.map(|p| p.name)
.collect();
fixed.sort_unstable();
assert_eq!(fixed, vec!["holy-grail", "sidebar", "split"]);
}
#[test]
fn tabbed_and_stacked_builders_remain_behaviorally_equivalent_under_shared_shell() {
let kinds = ["alpha", "beta", "gamma"];
let active = 1;
let bar_h = 2.0;
let gap = 4.0;
let vp_w = 120.0;
let vp_h = 60.0;
let tabbed = Layout::tabbed(kinds)
.active(active)
.bar_height(bar_h)
.gap(gap)
.resolve(vp_w, vp_h)
.unwrap();
let stacked = Layout::stacked(kinds)
.active(active)
.bar_height(bar_h)
.gap(gap)
.resolve(vp_w, vp_h)
.unwrap();
for kind in &kinds {
assert_eq!(tabbed.by_kind(kind).len(), 1);
assert_eq!(stacked.by_kind(kind).len(), 1);
}
for kind in &kinds {
let t_id = tabbed.by_kind(kind)[0];
let s_id = stacked.by_kind(kind)[0];
let t_rect = tabbed.get(t_id).unwrap();
let s_rect = stacked.get(s_id).unwrap();
assert_eq!(t_rect.w, vp_w);
assert_eq!(s_rect.w, vp_w);
match *kind == kinds[active] {
true => {
assert!(t_rect.h > 0.0, "tabbed active panel height should be > 0");
assert!(s_rect.h > 0.0, "stacked active panel height should be > 0");
}
false => {
assert_eq!(t_rect.h, 0.0, "tabbed inactive panel should be hidden");
assert_eq!(s_rect.h, 0.0, "stacked inactive panel should be hidden");
}
}
}
let tab_decorations: Vec<_> = tabbed
.decoration_panels()
.iter()
.filter(|d| d.role == panes::DecorationRole::Tab)
.collect();
assert_eq!(tab_decorations.len(), kinds.len());
let title_decorations: Vec<_> = stacked
.decoration_panels()
.iter()
.filter(|d| d.role == panes::DecorationRole::Title)
.collect();
assert_eq!(title_decorations.len(), kinds.len());
for d in &tab_decorations {
assert_eq!(tabbed.get(d.id).unwrap().h, bar_h);
}
for d in &title_decorations {
assert_eq!(stacked.get(d.id).unwrap().h, bar_h);
}
}
#[test]
fn active_panel_presets_preserve_runtime_focus_and_sequence_behavior() {
let kinds = ["x", "y", "z"];
let mut tabbed_rt = Layout::tabbed(kinds).into_runtime().unwrap();
let mut stacked_rt = Layout::stacked(kinds).into_runtime().unwrap();
assert_eq!(tabbed_rt.sequence().len(), 3);
assert_eq!(stacked_rt.sequence().len(), 3);
let _ = tabbed_rt.resolve(80.0, 24.0).unwrap();
let _ = stacked_rt.resolve(80.0, 24.0).unwrap();
assert_eq!(tabbed_rt.focused_kind(), Some("x"));
assert_eq!(stacked_rt.focused_kind(), Some("x"));
tabbed_rt.focus_next();
stacked_rt.focus_next();
let _ = tabbed_rt.resolve(80.0, 24.0).unwrap();
let _ = stacked_rt.resolve(80.0, 24.0).unwrap();
assert_eq!(tabbed_rt.focused_kind(), Some("y"));
assert_eq!(stacked_rt.focused_kind(), Some("y"));
let t_frame = tabbed_rt.resolve(80.0, 24.0).unwrap();
let s_frame = stacked_rt.resolve(80.0, 24.0).unwrap();
let t_y = t_frame.layout().by_kind("y")[0];
let s_y = s_frame.layout().by_kind("y")[0];
assert!(t_frame.layout().get(t_y).unwrap().h > 0.0);
assert!(s_frame.layout().get(s_y).unwrap().h > 0.0);
let t_x = t_frame.layout().by_kind("x")[0];
let s_x = s_frame.layout().by_kind("x")[0];
assert_eq!(t_frame.layout().get(t_x).unwrap().h, 0.0);
assert_eq!(s_frame.layout().get(s_x).unwrap().h, 0.0);
tabbed_rt.focus_prev();
stacked_rt.focus_prev();
assert_eq!(tabbed_rt.focused_kind(), Some("x"));
assert_eq!(stacked_rt.focused_kind(), Some("x"));
let _ = tabbed_rt.resolve(80.0, 24.0).unwrap();
let _ = stacked_rt.resolve(80.0, 24.0).unwrap();
let expected_order = ["x", "y", "z"];
for expected in &expected_order {
assert_eq!(tabbed_rt.focused_kind(), Some(*expected));
assert_eq!(stacked_rt.focused_kind(), Some(*expected));
tabbed_rt.focus_next();
stacked_rt.focus_next();
}
assert_eq!(tabbed_rt.focused_kind(), Some("x"));
assert_eq!(stacked_rt.focused_kind(), Some("x"));
}
#[test]
fn dashboard_build_matches_shared_grid_primitive_behavior() {
let dashboard_resolved = Layout::dashboard([
("a", CardSpan::Columns(1)),
("b", CardSpan::Columns(2)),
("c", CardSpan::Columns(1)),
("d", CardSpan::FullWidth),
])
.columns(4)
.gap(8.0)
.auto_rows()
.resolve(400.0, 300.0)
.unwrap();
let grid_layout = Layout::build_grid(Grid::columns(4).gap(8.0).auto_rows(), |g| {
g.panel("a");
g.panel_span("b", CardSpan::Columns(2));
g.panel("c");
g.panel_span("d", CardSpan::FullWidth);
})
.unwrap();
let grid_resolved = grid_layout.resolve(400.0, 300.0).unwrap();
for kind in &["a", "b", "c", "d"] {
let dr = dashboard_resolved
.get(dashboard_resolved.by_kind(kind)[0])
.unwrap();
let gr = grid_resolved.get(grid_resolved.by_kind(kind)[0]).unwrap();
assert!(
(dr.x - gr.x).abs() < 1.0
&& (dr.y - gr.y).abs() < 1.0
&& (dr.w - gr.w).abs() < 1.0
&& (dr.h - gr.h).abs() < 1.0,
"panel '{kind}' geometry differs: dashboard={dr:?}, grid={gr:?}"
);
}
}