blob: 230c2bbac45e154e336e99bec40a16b4289eb1b5 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::Error;
use carnelian::{
color::Color,
drawing::{load_font, path_for_rectangle, FontFace},
input::{self},
make_app_assistant,
render::{BlendMode, Context as RenderContext, Fill, FillRule, Layer, Raster, Style},
scene::{
facets::{
Facet, FacetId, TextFacetOptions, TextHorizontalAlignment, TextVerticalAlignment,
},
group::GroupMemberData,
layout::{
Alignment, CrossAxisAlignment, FlexMemberData, MainAxisAlignment, MainAxisSize,
StackMemberDataBuilder,
},
scene::{Scene, SceneBuilder, SceneOrder},
LayerGroup,
},
App, AppAssistant, Coord, Point, Rect, Size, ViewAssistant, ViewAssistantContext,
ViewAssistantPtr, ViewKey,
};
use euclid::size2;
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Default)]
struct LayoutsAppAssistant;
impl AppAssistant for LayoutsAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
LayoutsViewAssistant::new()
}
}
const ALIGNS: &[fn() -> Alignment] = &[
Alignment::top_left,
Alignment::top_center,
Alignment::top_right,
Alignment::center_left,
Alignment::center,
Alignment::center_right,
Alignment::bottom_left,
Alignment::bottom_center,
Alignment::bottom_right,
];
const MAIN_ALIGNS: &[MainAxisAlignment] = &[
MainAxisAlignment::Start,
MainAxisAlignment::End,
MainAxisAlignment::Center,
MainAxisAlignment::SpaceBetween,
MainAxisAlignment::SpaceAround,
MainAxisAlignment::SpaceEvenly,
];
const CROSS_ALIGNS: &[CrossAxisAlignment] =
&[CrossAxisAlignment::Start, CrossAxisAlignment::End, CrossAxisAlignment::Center];
const MAIN_SIZES: &[MainAxisSize] = &[MainAxisSize::Min, MainAxisSize::Max];
const TEXT_H_ALIGNS: &[TextHorizontalAlignment] = &[
TextHorizontalAlignment::Left,
TextHorizontalAlignment::Center,
TextHorizontalAlignment::Right,
];
const TEXT_V_ALIGNS: &[TextVerticalAlignment] =
&[TextVerticalAlignment::Top, TextVerticalAlignment::Center, TextVerticalAlignment::Bottom];
struct SceneDetails {
scene: Scene,
}
enum Mode {
Stack(usize),
Flex(usize, usize, usize),
Button,
OneThirdTwoThird,
OneThirdTwoThirdNoCol,
Text(usize, usize, usize),
}
impl Mode {
pub fn next(&self) -> Self {
match self {
Mode::Stack(..) => Mode::Flex(0, 0, 0),
Mode::Flex(..) => Mode::Button,
Mode::Button => Mode::OneThirdTwoThird,
Mode::OneThirdTwoThird => Mode::OneThirdTwoThirdNoCol,
Mode::OneThirdTwoThirdNoCol => Mode::Text(0, 0, 0),
Mode::Text(..) => Mode::Stack(0),
}
}
pub fn previous(&self) -> Self {
match self {
Mode::Stack(..) => Mode::Text(0, 0, 0),
Mode::Flex(..) => Mode::Stack(0),
Mode::Button => Mode::Flex(0, 0, 0),
Mode::OneThirdTwoThird => Mode::Button,
Mode::OneThirdTwoThirdNoCol => Mode::OneThirdTwoThird,
Mode::Text(..) => Mode::OneThirdTwoThird,
}
}
}
impl Default for Mode {
fn default() -> Self {
Self::Stack(0)
}
}
struct LayoutsViewAssistant {
face: FontFace,
scene_details: Option<SceneDetails>,
mode: Mode,
background: bool,
}
#[derive(Serialize, Deserialize)]
struct BoundsHolder {
bounds: Vec<Rect>,
}
impl LayoutsViewAssistant {
fn new() -> Result<ViewAssistantPtr, Error> {
let face = load_font(PathBuf::from("/pkg/data/fonts/RobotoSlab-Regular.ttf"))?;
Ok(Box::new(LayoutsViewAssistant {
face,
mode: Mode::default(),
background: false,
scene_details: None,
}))
}
fn cycle1(&mut self) {
match &mut self.mode {
Mode::Stack(alignment) => {
*alignment = (*alignment + 1) % ALIGNS.len();
}
Mode::Flex(main_size, ..) => {
*main_size = (*main_size + 1) % MAIN_SIZES.len();
}
Mode::Text(h, ..) => {
*h = (*h + 1) % TEXT_H_ALIGNS.len();
}
_ => (),
}
self.refresh();
}
fn cycle2(&mut self) {
match &mut self.mode {
Mode::Flex(_, main_align, ..) => {
*main_align = (*main_align + 1) % MAIN_ALIGNS.len();
}
Mode::Text(_, v, ..) => {
*v = (*v + 1) % TEXT_V_ALIGNS.len();
}
_ => (),
}
self.refresh();
}
fn cycle3(&mut self) {
match &mut self.mode {
Mode::Flex(_, _, cross_align) => {
*cross_align = (*cross_align + 1) % CROSS_ALIGNS.len();
}
Mode::Text(_, _, visual) => {
*visual = (*visual + 1) % 4;
}
_ => (),
}
self.refresh();
}
fn cycle_mode(&mut self, go_next: bool) {
self.mode = if go_next { self.mode.next() } else { self.mode.previous() };
self.refresh();
}
fn refresh(&mut self) {
self.scene_details = None;
}
fn dump_bounds(&mut self) {
if let Some(scene_details) = self.scene_details.as_ref() {
let bounds = scene_details.scene.all_facet_bounds();
let bounds_holder = BoundsHolder { bounds: bounds[1..].to_vec() };
let toml = toml::to_string(&bounds_holder).unwrap();
println!("let expected_text = r#\"{}\"#;", toml);
}
}
fn toggle_background(&mut self) {
self.background = !self.background;
self.refresh();
}
}
impl ViewAssistant for LayoutsViewAssistant {
fn resize(&mut self, _new_size: &Size) -> Result<(), Error> {
self.scene_details = None;
Ok(())
}
fn get_scene(&mut self, size: Size) -> Option<&mut Scene> {
let scene_details = self.scene_details.take().unwrap_or_else(|| {
let mut builder =
SceneBuilder::new().background_color(Color::white()).enable_mouse_cursor(false);
builder.group().stack().expand().align(Alignment::top_center()).contents(|builder| {
let label = match self.mode {
Mode::Stack(alignment) => format!("make_two_boxes_{:?}", ALIGNS[alignment]()),
Mode::Flex(main_size, main_align, cross_align) => format!(
"make_column_with_two_boxes_{:?}_{:?}_{:?}",
MAIN_SIZES[main_size], MAIN_ALIGNS[main_align], CROSS_ALIGNS[cross_align],
),
Mode::Button => String::from("button"),
Mode::OneThirdTwoThird => String::from("one_third_two_third"),
Mode::OneThirdTwoThirdNoCol => String::from("one_third_two_third_no_col"),
Mode::Text(..) => String::from("text"),
}
.to_lowercase();
builder.text(
self.face.clone(),
&label,
18.0,
Point::zero(),
TextFacetOptions {
color: Color::fuchsia(),
horizontal_alignment: TextHorizontalAlignment::Left,
vertical_alignment: TextVerticalAlignment::Top,
..TextFacetOptions::default()
},
);
match self.mode {
Mode::Stack(alignment) => make_two_boxes(builder, ALIGNS[alignment]()),
Mode::Flex(main_size, main_align, cross_align) => make_column_with_two_boxes(
builder,
MAIN_SIZES[main_size],
MAIN_ALIGNS[main_align],
CROSS_ALIGNS[cross_align],
),
Mode::Button => make_fake_button(builder),
Mode::OneThirdTwoThird => make_one_third_two_third(builder),
Mode::OneThirdTwoThirdNoCol => make_one_third_two_third_no_col(builder),
Mode::Text(h, v, visual) => make_text_alignment(
builder,
&self.face,
size,
TEXT_H_ALIGNS[h],
TEXT_V_ALIGNS[v],
visual,
),
}
if self.background {
build_flexible_test_facet(
builder,
Size::zero(),
size2(Coord::MAX, Coord::MAX),
None,
StackMemberDataBuilder::new()
.top(50.0)
.left(100.0)
.bottom(150.0)
.right(200.0)
.build(),
);
}
});
let scene = builder.build();
SceneDetails { scene }
});
self.scene_details = Some(scene_details);
Some(&mut self.scene_details.as_mut().unwrap().scene)
}
fn handle_keyboard_event(
&mut self,
context: &mut ViewAssistantContext,
_event: &input::Event,
keyboard_event: &input::keyboard::Event,
) -> Result<(), Error> {
const M: u32 = 109;
const CAPITAL_M: u32 = 77;
const ONE: u32 = 49;
const TWO: u32 = 50;
const THREE: u32 = 51;
const D: u32 = 100;
const R: u32 = 114;
const B: u32 = 98;
if let Some(code_point) = keyboard_event.code_point {
if keyboard_event.phase == input::keyboard::Phase::Pressed
|| keyboard_event.phase == input::keyboard::Phase::Repeat
{
match code_point {
ONE => self.cycle1(),
TWO => self.cycle2(),
THREE => self.cycle3(),
M => self.cycle_mode(true),
CAPITAL_M => self.cycle_mode(false),
D => self.dump_bounds(),
R => self.refresh(),
B => self.toggle_background(),
_ => println!("code_point = {}", code_point),
}
context.request_render();
}
}
Ok(())
}
}
fn main() -> Result<(), Error> {
fuchsia_trace_provider::trace_provider_create_with_fdio();
App::run(make_app_assistant::<LayoutsAppAssistant>())
}
fn random_color_element() -> u8 {
let mut rng = thread_rng();
let e: u8 = rng.gen_range(0..128);
e + 128
}
fn random_color() -> Color {
Color {
r: random_color_element(),
g: random_color_element(),
b: random_color_element(),
a: 0xff,
}
}
#[allow(unused)]
struct TestFacet {
min_size: Size,
max_size: Size,
size: Size,
color: Color,
raster: Option<Raster>,
}
impl TestFacet {
fn new(min_size: Size, max_size: Size, color: Option<Color>) -> Self {
Self {
min_size,
max_size,
size: min_size,
color: color.unwrap_or_else(|| random_color()),
raster: None,
}
}
}
impl Facet for TestFacet {
fn update_layers(
&mut self,
size: Size,
layer_group: &mut dyn LayerGroup,
render_context: &mut RenderContext,
_view_context: &ViewAssistantContext,
) -> Result<(), Error> {
let desired_size = size.max(self.min_size).min(self.max_size);
if self.size != desired_size {
self.raster = None;
self.size = desired_size;
}
let line_raster = self.raster.take().unwrap_or_else(|| {
let line_path = path_for_rectangle(&Rect::from_size(self.size), render_context);
let mut raster_builder = render_context.raster_builder().expect("raster_builder");
raster_builder.add(&line_path, None);
raster_builder.build()
});
let raster = line_raster.clone();
self.raster = Some(line_raster);
layer_group.insert(
SceneOrder::default(),
Layer {
raster: raster,
clip: None,
style: Style {
fill_rule: FillRule::NonZero,
fill: Fill::Solid(self.color),
blend_mode: BlendMode::Over,
},
},
);
Ok(())
}
fn calculate_size(&self, _: Size) -> Size {
self.size
}
}
fn build_test_facet_with_member_data(
builder: &mut SceneBuilder,
min_size: Size,
max_size: Size,
color: Option<Color>,
member_data: Option<GroupMemberData>,
) -> FacetId {
let facet = TestFacet::new(min_size, max_size, color);
builder.facet_with_data(Box::new(facet), member_data)
}
fn build_test_facet(builder: &mut SceneBuilder, size: Size, color: Option<Color>) -> FacetId {
build_test_facet_with_member_data(builder, size, size, color, None)
}
fn build_flexible_test_facet(
builder: &mut SceneBuilder,
min_size: Size,
max_size: Size,
color: Option<Color>,
member_data: Option<GroupMemberData>,
) -> FacetId {
build_test_facet_with_member_data(builder, min_size, max_size, color, member_data)
}
const OUTER_FACET_SIZE: Size = size2(300.0, 100.0);
const INNER_FACET_SIZE: Size = size2(200.0, 50.0);
fn make_two_boxes(builder: &mut SceneBuilder, align: Alignment) {
builder.group().stack().align(align).expand().contents(|builder| {
let _inner = build_test_facet(builder, INNER_FACET_SIZE, Some(Color::new()));
let _outer = build_test_facet(builder, OUTER_FACET_SIZE, Some(Color::red()));
});
}
fn make_column_with_two_boxes(
builder: &mut SceneBuilder,
main_size: MainAxisSize,
main_align: MainAxisAlignment,
cross_align: CrossAxisAlignment,
) {
builder
.group()
.column()
.main_size(main_size)
.main_align(main_align)
.cross_align(cross_align)
.contents(|builder| {
let _inner = build_test_facet(builder, INNER_FACET_SIZE, Some(Color::new()));
let _outer = build_test_facet(builder, OUTER_FACET_SIZE, Some(Color::red()));
});
}
const INDICATOR_FACET_SIZE: Size = size2(80.0, 40.0);
const INDICATOR_FACET_DELTA: Size = size2(-10.0, 10.0);
fn make_fake_button(builder: &mut SceneBuilder) {
builder.group().column().max_size().space_evenly().contents(|builder| {
builder.group().row().max_size().space_evenly().contents(|builder| {
let mut indicator_size = INDICATOR_FACET_SIZE;
let _ = build_test_facet(builder, indicator_size, Some(Color::new()));
indicator_size += INDICATOR_FACET_DELTA;
let _ = build_test_facet(builder, indicator_size, Some(Color::red()));
indicator_size += INDICATOR_FACET_DELTA;
let _ = build_test_facet(builder, indicator_size, Some(Color::green()));
indicator_size += INDICATOR_FACET_DELTA;
let _ = build_test_facet(builder, indicator_size, Some(Color::blue()));
});
builder.group().stack().center().contents(|builder| {
let _ = build_test_facet(builder, INNER_FACET_SIZE, Some(Color::white()));
let _ = build_test_facet(builder, OUTER_FACET_SIZE, Some(Color::fuchsia()));
});
});
}
fn make_one_third_two_third(builder: &mut SceneBuilder) {
builder.group().column().max_size().space_evenly().contents(|builder| {
builder.group().row().max_size().space_evenly().contents(|builder| {
let mut indicator_size = INDICATOR_FACET_SIZE;
builder
.group()
.column_with_member_data(FlexMemberData::new(1))
.cross_align(CrossAxisAlignment::End)
.space_evenly()
.contents(|builder| {
let _ = build_test_facet(builder, indicator_size, Some(Color::new()));
});
indicator_size += INDICATOR_FACET_DELTA;
builder
.group()
.column_with_member_data(FlexMemberData::new(2))
.space_evenly()
.cross_align(CrossAxisAlignment::Start)
.contents(|builder| {
let _ = build_test_facet(builder, indicator_size, Some(Color::red()));
});
});
});
}
fn make_one_third_two_third_no_col(builder: &mut SceneBuilder) {
builder.group().column().max_size().space_evenly().contents(|builder| {
builder.group().row().max_size().space_evenly().contents(|builder| {
let mut indicator_size = INDICATOR_FACET_SIZE;
let _ = build_flexible_test_facet(
builder,
indicator_size,
size2(f32::MAX, indicator_size.height),
Some(Color::new()),
FlexMemberData::new(1),
);
indicator_size += INDICATOR_FACET_DELTA;
let _ = build_flexible_test_facet(
builder,
indicator_size,
size2(f32::MAX, indicator_size.height),
Some(Color::red()),
FlexMemberData::new(2),
);
});
});
}
fn make_text_alignment(
builder: &mut SceneBuilder,
font: &FontFace,
size: Size,
h: TextHorizontalAlignment,
v: TextVerticalAlignment,
visual_mode: usize,
) {
const INSET: f32 = 50.0;
let visual = (visual_mode & 0x1) != 0;
let sized = (visual_mode & 0x2) != 0;
let font_size = size.width.max(size.height) / 8.0;
builder.group().stack().align(Alignment::center()).expand().contents(|builder| {
let guide_color = Color::from_hash_code("#81b29a").expect("guide_color");
builder.h_line(size.width, 3.0, guide_color, None);
builder.v_line(size.height, 3.0, guide_color, None);
builder.text_with_data(
font.clone(),
"Älign Me",
font_size,
Point::zero(),
TextFacetOptions {
color: Color::from_hash_code("#3d405b").expect("color"),
background_color: Color::from_hash_code("#f2cc8f").ok(),
horizontal_alignment: h,
vertical_alignment: v,
visual,
..TextFacetOptions::default()
},
if sized {
StackMemberDataBuilder::new()
.top(INSET)
.left(INSET)
.bottom(INSET)
.right(INSET)
.build()
} else {
None
},
);
});
}
#[cfg(test)]
mod test {
use super::*;
use carnelian::{
scene::{layout::MainAxisSize, scene::SceneBuilder},
Rect, Size,
};
use euclid::size2;
use itertools::assert_equal;
const TEST_LAYOUT_SIZE: Size = size2(1024.0, 600.00);
fn stack_test(align: Alignment, expected_bounds: &[Rect]) {
let mut builder = SceneBuilder::new();
make_two_boxes(&mut builder, align);
let mut scene = builder.build();
scene.layout(TEST_LAYOUT_SIZE);
let bounds = scene.all_facet_bounds();
assert_equal(&bounds, expected_bounds);
}
fn stack_test_txt(align: Alignment, expected_text: &str) {
let expected_bounds: BoundsHolder = toml::from_str(expected_text).unwrap();
stack_test(align, &expected_bounds.bounds);
}
#[test]
fn stack_two_boxes_top_left() {
let expected_text = r#"[[bounds]]
origin = [0.0, 0.0]
size = [200.0, 50.0]
[[bounds]]
origin = [0.0, 0.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::top_left(), expected_text);
}
#[test]
fn stack_two_boxes_top_center() {
let expected_text = r#"
[[bounds]]
origin = [412.0, 0.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 0.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::top_center(), expected_text);
}
#[test]
fn stack_two_boxes_top_right() {
let expected_text = r#"[[bounds]]
origin = [824.0, 0.0]
size = [200.0, 50.0]
[[bounds]]
origin = [724.0, 0.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::top_right(), expected_text);
}
#[test]
fn stack_two_boxes_center_left() {
let expected_text = r#"[[bounds]]
origin = [0.0, 275.0]
size = [200.0, 50.0]
[[bounds]]
origin = [0.0, 250.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::center_left(), expected_text);
}
#[test]
fn stack_two_boxes_center() {
let expected_text = r#"[[bounds]]
origin = [412.0, 275.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 250.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::center(), expected_text);
}
#[test]
fn stack_two_boxes_center_right() {
let expected_text = r#"[[bounds]]
origin = [824.0, 275.0]
size = [200.0, 50.0]
[[bounds]]
origin = [724.0, 250.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::center_right(), expected_text);
}
#[test]
fn stack_two_boxes_bottom_left() {
let expected_text = r#"[[bounds]]
origin = [0.0, 550.0]
size = [200.0, 50.0]
[[bounds]]
origin = [0.0, 500.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::bottom_left(), expected_text);
}
#[test]
fn stack_two_boxes_bottom_center() {
let expected_text = r#"[[bounds]]
origin = [412.0, 550.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 500.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::bottom_center(), expected_text);
}
#[test]
fn stack_two_boxes_bottom_right() {
let expected_text = r#"[[bounds]]
origin = [824.0, 550.0]
size = [200.0, 50.0]
[[bounds]]
origin = [724.0, 500.0]
size = [300.0, 100.0]
"#;
stack_test_txt(Alignment::bottom_right(), expected_text);
}
const COLUMN_TEST_LAYOUT_SIZE: Size = size2(1024.0, 600.0);
fn colummn_test(
main_size: MainAxisSize,
main_align: MainAxisAlignment,
cross_align: CrossAxisAlignment,
expected_bounds: &[Rect],
) {
let mut builder = SceneBuilder::new();
builder.group().stack().expand().align(Alignment::top_center()).contents(|builder| {
make_column_with_two_boxes(builder, main_size, main_align, cross_align);
});
let mut scene = builder.build();
scene.layout(COLUMN_TEST_LAYOUT_SIZE);
let bounds = scene.all_facet_bounds();
assert_equal(&bounds, expected_bounds);
}
fn colummn_test_txt(
main_size: MainAxisSize,
main_align: MainAxisAlignment,
cross_align: CrossAxisAlignment,
expected_text: &str,
) {
let expected_bounds: BoundsHolder = toml::from_str(expected_text).unwrap();
colummn_test(main_size, main_align, cross_align, &expected_bounds.bounds);
}
#[test]
fn column_two_boxes_main_max() {
let expected_text = r#"[[bounds]]
origin = [412.0, 0.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 50.0]
size = [300.0, 100.0]
"#;
colummn_test_txt(
MainAxisSize::Max,
MainAxisAlignment::Start,
CrossAxisAlignment::Center,
expected_text,
);
}
#[test]
fn column_two_boxes_main_min() {
let expected_text = r#"[[bounds]]
origin = [412.0, 0.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 50.0]
size = [300.0, 100.0]
"#;
colummn_test_txt(
MainAxisSize::Min,
MainAxisAlignment::Start,
CrossAxisAlignment::Center,
expected_text,
);
}
#[test]
fn column_two_boxes_main_space_evenly() {
let expected_text = r#"[[bounds]]
origin = [362.0, 150.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 350.0]
size = [300.0, 100.0]
"#;
colummn_test_txt(
MainAxisSize::Max,
MainAxisAlignment::SpaceEvenly,
CrossAxisAlignment::Start,
expected_text,
);
}
#[test]
fn column_two_boxes_main_center_center() {
let expected_text = r#"[[bounds]]
origin = [412.0, 225.0]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 275.0]
size = [300.0, 100.0]
"#;
colummn_test_txt(
MainAxisSize::Max,
MainAxisAlignment::Center,
CrossAxisAlignment::Center,
expected_text,
);
}
const BUTTON_TEST_LAYOUT_SIZE: Size = size2(1024.0, 600.0);
#[test]
fn fake_button() {
let expected_text = r#"[[bounds]]
origin = [152.8, 158.33333]
size = [80.0, 40.0]
[[bounds]]
origin = [385.6, 153.33333]
size = [70.0, 50.0]
[[bounds]]
origin = [608.4, 148.33333]
size = [60.0, 60.0]
[[bounds]]
origin = [821.2, 143.33333]
size = [50.0, 70.0]
[[bounds]]
origin = [412.0, 381.66666]
size = [200.0, 50.0]
[[bounds]]
origin = [362.0, 356.66666]
size = [300.0, 100.0]
"#;
let expected_bounds: BoundsHolder = toml::from_str(expected_text).unwrap();
let mut builder = SceneBuilder::new();
builder.group().stack().expand().align(Alignment::top_center()).contents(|builder| {
make_fake_button(builder);
});
let mut scene = builder.build();
scene.layout(BUTTON_TEST_LAYOUT_SIZE);
let bounds = scene.all_facet_bounds();
assert_equal(bounds, expected_bounds.bounds);
}
const ONE_THIRD_TWO_THIRDS_TEST_LAYOUT_SIZE: Size = size2(1024.0, 600.0);
#[test]
fn one_third_two_thirds() {
let expected_text = r#"[[bounds]]
origin = [291.33334, 280.0]
size = [80.0, 40.0]
[[bounds]]
origin = [371.33334, 275.0]
size = [70.0, 50.0]
"#;
let expected_bounds: BoundsHolder = toml::from_str(expected_text).unwrap();
let mut builder = SceneBuilder::new();
builder.group().stack().expand().align(Alignment::top_center()).contents(|builder| {
make_one_third_two_third(builder);
});
let mut scene = builder.build();
scene.layout(ONE_THIRD_TWO_THIRDS_TEST_LAYOUT_SIZE);
let bounds = scene.all_facet_bounds();
assert_equal(bounds, expected_bounds.bounds);
}
}