use assert_matches::assert_matches;
use masonry_core::core::{NewWidget, WidgetTag};
use masonry_testing::{TestWidgetExt, assert_debug_panics};
use vello::kurbo::{Insets, Point, Size};
use crate::core::Widget;
use crate::properties::types::{AsUnit, Length};
use crate::testing::{ModularWidget, TestHarness};
use crate::theme::default_property_set;
use crate::widgets::{Button, ChildAlignment, Flex, SizedBox, ZStack};
#[test]
fn layout_simple() {
const BOX_WIDTH: f64 = 50.;
let tag_1 = WidgetTag::new("box1");
let tag_2 = WidgetTag::new("box2");
let box_side = Length::px(BOX_WIDTH);
let widget = Flex::column()
.with_child(
Flex::row()
.with_child(NewWidget::new_with_tag(
SizedBox::empty().width(box_side).height(box_side),
tag_1,
))
.with_child(NewWidget::new_with_tag(
SizedBox::empty().width(box_side).height(box_side),
tag_2,
))
.with_flex_spacer(1.0)
.with_auto_id(),
)
.with_flex_spacer(1.0)
.with_auto_id();
let harness = TestHarness::create(default_property_set(), widget);
let first_box_rect = harness.get_widget(tag_1).ctx().local_layout_rect();
let first_box_paint_rect = harness.get_widget(tag_1).ctx().paint_rect();
assert_eq!(first_box_rect.x0, 0.0);
assert_eq!(first_box_rect.y0, 0.0);
assert_eq!(first_box_rect.x1, BOX_WIDTH);
assert_eq!(first_box_rect.y1, BOX_WIDTH);
assert_eq!(first_box_paint_rect.x0, 0.0);
assert_eq!(first_box_paint_rect.y0, 0.0);
assert_eq!(first_box_paint_rect.x1, BOX_WIDTH);
assert_eq!(first_box_paint_rect.y1, BOX_WIDTH);
}
#[test]
fn forget_to_recurse_layout() {
let widget = ModularWidget::new_parent(Flex::row().with_auto_id())
.layout_fn(|_child, _ctx, _, _| {
Size::ZERO
})
.with_auto_id();
assert_debug_panics!(
TestHarness::create(default_property_set(), widget),
"LayoutCtx::run_layout() was not called"
);
}
#[test]
fn forget_to_call_place_child() {
let widget = ModularWidget::new_parent(Flex::row().with_auto_id())
.layout_fn(|child, ctx, _, bc| {
ctx.run_layout(child, bc)
})
.with_auto_id();
assert_debug_panics!(
TestHarness::create(default_property_set(), widget),
"LayoutCtx::place_child() was not called"
);
}
#[test]
fn call_place_child_before_layout() {
let widget = ModularWidget::new_parent(Flex::row().with_auto_id())
.layout_fn(|child, ctx, _, _| {
ctx.place_child(child, Point::ORIGIN);
Size::ZERO
})
.with_auto_id();
assert_debug_panics!(
TestHarness::create(default_property_set(), widget),
"trying to call 'place_child'"
);
}
#[test]
fn run_layout_on_stashed() {
let parent_tag = WidgetTag::new("parent");
let widget =
ModularWidget::new_parent(Flex::row().with_auto_id()).layout_fn(|child, ctx, _, bc| {
let size = ctx.run_layout(child, bc);
ctx.place_child(child, Point::ZERO);
size
});
let widget = NewWidget::new_with_tag(widget, parent_tag);
let mut harness = TestHarness::create(default_property_set(), widget);
assert_debug_panics!(
harness.edit_widget(parent_tag, |mut parent| {
parent.ctx.set_stashed(&mut parent.widget.state, true);
parent.ctx.request_layout();
}),
"trying to compute layout of stashed widget"
);
}
#[test]
fn stash_then_run_layout() {
let parent_tag = WidgetTag::new("parent");
let widget =
ModularWidget::new_parent(Flex::row().with_auto_id()).layout_fn(|child, ctx, _, bc| {
ctx.set_stashed(child, true);
let size = ctx.run_layout(child, bc);
ctx.place_child(child, Point::ZERO);
size
});
let widget = NewWidget::new_with_tag(widget, parent_tag);
assert_debug_panics!(
TestHarness::create(default_property_set(), widget),
"trying to compute layout of stashed widget"
);
}
#[test]
fn unstash_then_run_layout() {
let parent_tag = WidgetTag::new("parent");
let widget =
ModularWidget::new_parent(Flex::row().with_auto_id()).layout_fn(|child, ctx, _, bc| {
ctx.set_stashed(child, false);
let size = ctx.run_layout(child, bc);
ctx.place_child(child, Point::ZERO);
size
});
let widget = NewWidget::new_with_tag(widget, parent_tag);
let mut harness = TestHarness::create(default_property_set(), widget);
harness.edit_widget(parent_tag, |mut parent| {
parent.ctx.set_stashed(&mut parent.widget.state, true);
parent.ctx.request_layout();
});
}
#[test]
fn skip_layout_when_cached() {
let button_tag = WidgetTag::new("button");
let sibling_tag = WidgetTag::new("sibling");
let button = NewWidget::new_with_tag(Button::with_text("Foobar").record(), button_tag);
let sibling = NewWidget::new_with_tag(
SizedBox::empty().width(20.px()).height(20.px()),
sibling_tag,
);
let parent = NewWidget::new(
ZStack::new()
.with_child(button, ChildAlignment::ParentAligned)
.with_child(sibling, ChildAlignment::ParentAligned),
);
let mut harness = TestHarness::create(default_property_set(), parent);
harness.flush_records_of(button_tag);
harness.edit_widget(sibling_tag, |mut sized_box| {
SizedBox::set_width(&mut sized_box, 30.px());
SizedBox::set_height(&mut sized_box, 30.px());
});
assert_matches!(harness.take_records_of(button_tag)[..], []);
}
#[test]
fn pixel_snapping() {
let child_tag = WidgetTag::new("child");
let child = NewWidget::new_with_tag(SizedBox::empty().size(10.3.px(), 10.3.px()), child_tag);
let pos = Point::new(5.1, 5.3);
let parent = ModularWidget::new_parent(child).layout_fn(move |child, ctx, _, bc| {
let size = ctx.run_layout(child, &bc.loosen());
ctx.place_child(child, pos);
size
});
let parent = NewWidget::new(parent);
let harness = TestHarness::create(default_property_set(), parent);
let child_pos = harness.get_widget(child_tag).ctx().window_origin();
let child_size = harness.get_widget(child_tag).ctx().size();
assert_eq!(child_pos, Point::new(5.0, 5.0));
assert_eq!(child_size, Size::new(10., 11.));
}
#[test]
fn layout_insets() {
const BOX_WIDTH: f64 = 50.;
let child_tag = WidgetTag::new("child");
let parent_tag = WidgetTag::new("parent");
let child_widget = ModularWidget::new(()).layout_fn(|_, ctx, _, _| {
ctx.set_paint_insets(Insets::uniform_xy(0., 20.));
Size::new(BOX_WIDTH, BOX_WIDTH)
});
let parent_widget = NewWidget::new_with_tag(
SizedBox::new(NewWidget::new_with_tag(child_widget, child_tag)),
parent_tag,
);
let harness = TestHarness::create(default_property_set(), parent_widget);
let child_paint_rect = harness.get_widget(child_tag).ctx().paint_rect();
let parent_paint_rect = harness.get_widget(parent_tag).ctx().paint_rect();
assert_eq!(child_paint_rect.x0, 0.0);
assert_eq!(child_paint_rect.y0, -20.0);
assert_eq!(child_paint_rect.x1, BOX_WIDTH);
assert_eq!(child_paint_rect.y1, BOX_WIDTH + 20.0);
assert_eq!(parent_paint_rect.x0, 0.0);
assert_eq!(parent_paint_rect.y0, -20.0);
assert_eq!(parent_paint_rect.x1, BOX_WIDTH);
assert_eq!(parent_paint_rect.y1, BOX_WIDTH + 20.0);
}