use crate::event::Rect as QRect;
use crate::primitives::layout_metrics::pixel_split_tree_layout;
use crate::primitives::split_tree::{SplitTree, SplitTreeLayout};
#[cfg(test)]
use crate::theme::Theme;
pub fn mac_split_tree_layout(tree: &SplitTree, x: f64, y: f64, w: f64, h: f64) -> SplitTreeLayout {
let bounds = QRect::new(x as f32, y as f32, w as f32, h as f32);
pixel_split_tree_layout(tree, bounds)
}
#[cfg(test)]
mod tests {
use super::super::headless::BitmapSurface;
use super::super::text::make_font;
use super::super::MacBackend;
use super::*;
use crate::event::{Point, Viewport};
use crate::primitives::split_tree::SplitDirection;
use crate::types::WidgetId;
use crate::Backend;
const W: u32 = 320;
const H: u32 = 200;
fn wid(s: &str) -> WidgetId {
WidgetId::new(s)
}
fn two_pane() -> SplitTree {
SplitTree::split(
SplitDirection::Horizontal,
0.5,
SplitTree::leaf(wid("a")),
SplitTree::leaf(wid("b")),
)
}
fn nested() -> SplitTree {
SplitTree::split(
SplitDirection::Horizontal,
0.5,
SplitTree::split(
SplitDirection::Vertical,
0.5,
SplitTree::leaf(wid("a")),
SplitTree::leaf(wid("c")),
),
SplitTree::leaf(wid("b")),
)
}
fn paint_via_backend(tree: &SplitTree, rect: QRect) -> (BitmapSurface, SplitTreeLayout) {
let surface = BitmapSurface::new(W, H);
surface.fill(1.0, 1.0, 1.0, 1.0);
let mut backend = MacBackend::new();
backend.set_current_font(make_font("Menlo", 14.0).expect("Menlo installed"));
backend.begin_frame(Viewport::new(W as f32, H as f32, 1.0));
let captured = std::cell::RefCell::new(None);
backend.enter_frame_scope(surface.context_ptr(), |b| {
*captured.borrow_mut() = Some(b.draw_split_tree(rect, tree));
});
backend.end_frame();
(surface, captured.into_inner().expect("layout captured"))
}
#[test]
fn divider_paints_separator_colour() {
let (surface, layout) =
paint_via_backend(&two_pane(), QRect::new(0.0, 0.0, W as f32, H as f32));
let theme = Theme::default();
assert_eq!(layout.dividers.len(), 1);
let d = &layout.dividers[0];
let px = (d.position + d.thickness / 2.0) as u32;
let py = (d.cross_start + d.cross_size / 2.0) as u32;
let (r, g, b, _) = surface.pixel(px, py);
assert_eq!(
(r, g, b),
(theme.separator.r, theme.separator.g, theme.separator.b),
"divider band should carry the separator colour",
);
}
#[test]
fn leaf_areas_are_left_unpainted() {
let (surface, _layout) =
paint_via_backend(&two_pane(), QRect::new(0.0, 0.0, W as f32, H as f32));
let (r, g, b, _) = surface.pixel(8, H / 2);
assert_eq!(
(r, g, b),
(255, 255, 255),
"split tree paints chrome only — leaf interiors stay untouched",
);
}
#[test]
fn nested_tree_paints_every_divider() {
let (surface, layout) =
paint_via_backend(&nested(), QRect::new(0.0, 0.0, W as f32, H as f32));
let theme = Theme::default();
assert_eq!(layout.dividers.len(), 2, "one divider per split node");
for (i, d) in layout.dividers.iter().enumerate() {
let (px, py) = match d.direction {
SplitDirection::Horizontal => (
(d.position + d.thickness / 2.0) as u32,
(d.cross_start + d.cross_size / 2.0) as u32,
),
SplitDirection::Vertical => (
(d.cross_start + d.cross_size / 2.0) as u32,
(d.position + d.thickness / 2.0) as u32,
),
};
let (r, g, b, _) = surface.pixel(px, py);
assert_eq!(
(r, g, b),
(theme.separator.r, theme.separator.g, theme.separator.b),
"divider {i} ({:?}) should be painted",
d.direction,
);
}
}
fn paint_click_round_trip_at(origin_x: f32, origin_y: f32) {
let rect = QRect::new(origin_x, origin_y, W as f32 - origin_x, H as f32 - origin_y);
let (surface, layout) = paint_via_backend(&two_pane(), rect);
let theme = Theme::default();
let d = &layout.dividers[0];
let cx = d.position + d.thickness / 2.0;
let cy = d.cross_start + d.cross_size / 2.0;
let (r, g, b, _) = surface.pixel(cx as u32, cy as u32);
assert_eq!(
(r, g, b),
(theme.separator.r, theme.separator.g, theme.separator.b),
"divider pixel at origin ({origin_x}, {origin_y}) should be painted",
);
assert_eq!(
layout.hit_test_divider(Point::new(cx, cy), 2.0),
Some(0),
"divider hit-test must resolve the painted position at origin \
({origin_x}, {origin_y})",
);
for (id, lr) in &layout.leaves {
let hit =
layout.hit_test_leaf(Point::new(lr.x + lr.width / 2.0, lr.y + lr.height / 2.0));
assert_eq!(
hit,
Some(id),
"leaf {id:?} should hit-test at its own centre (origin {origin_x}, {origin_y})",
);
}
for (id, lr) in &layout.leaves {
assert!(
lr.x >= origin_x - 0.001 && lr.y >= origin_y - 0.001,
"leaf {id:?} at ({}, {}) escaped the requested origin ({origin_x}, {origin_y})",
lr.x,
lr.y,
);
}
}
#[test]
fn paint_click_round_trip() {
paint_click_round_trip_at(0.0, 0.0);
}
#[test]
fn paint_click_round_trip_at_nonzero_origin() {
paint_click_round_trip_at(37.0, 21.0);
}
#[test]
fn layout_twin_matches_the_painted_layout() {
let rect = QRect::new(11.0, 7.0, 260.0, 150.0);
let tree = nested();
let (_surface, painted) = paint_via_backend(&tree, rect);
let mut backend = MacBackend::new();
backend.set_current_font(make_font("Menlo", 14.0).expect("Menlo installed"));
let computed = backend.split_tree_layout(rect, &tree);
assert_eq!(painted.bounds, computed.bounds);
assert_eq!(painted.leaves, computed.leaves);
assert_eq!(painted.dividers.len(), computed.dividers.len());
for (a, b) in painted.dividers.iter().zip(computed.dividers.iter()) {
assert_eq!(a.split_index, b.split_index);
assert_eq!(a.direction, b.direction);
assert!((a.position - b.position).abs() < 0.001);
assert!((a.cross_start - b.cross_start).abs() < 0.001);
assert!((a.cross_size - b.cross_size).abs() < 0.001);
assert!((a.thickness - b.thickness).abs() < 0.001);
}
}
#[test]
fn divider_thickness_matches_the_plain_split_rasteriser() {
let layout = mac_split_tree_layout(&two_pane(), 0.0, 0.0, 100.0, 50.0);
assert_eq!(
layout.dividers[0].thickness,
crate::primitives::layout_metrics::pixel::DIVIDER
);
assert!((layout.dividers[0].position - 48.0).abs() < 0.001);
}
}