use std::ops::ControlFlow;
use anathema_geometry::{Pos, Size};
use anathema_value_resolver::AttributeStorage;
use anathema_widgets::error::Result;
use anathema_widgets::layout::{Constraints, LayoutCtx, PositionCtx};
use anathema_widgets::{LayoutForEach, PositionChildren, Widget, WidgetId};
use crate::layout::alignment::{ALIGNMENT, Alignment};
#[derive(Default)]
pub struct Align;
impl Widget for Align {
fn layout<'bp>(
&mut self,
mut children: LayoutForEach<'_, 'bp>,
constraints: Constraints,
_: WidgetId,
ctx: &mut LayoutCtx<'_, 'bp>,
) -> Result<Size> {
_ = children.each(ctx, |ctx, widget, children| {
_ = widget.layout(children, constraints, ctx);
Ok(ControlFlow::Break(()))
})?;
Ok(constraints.max_size())
}
fn position<'bp>(
&mut self,
mut children: PositionChildren<'_, 'bp>,
id: WidgetId,
attribute_storage: &AttributeStorage<'bp>,
ctx: PositionCtx,
) {
let attributes = attribute_storage.get(id);
let alignment = attributes.get_as::<Alignment>(ALIGNMENT).unwrap_or_default();
_ = children.each(|child, children| {
let width = ctx.inner_size.width as i32;
let height = ctx.inner_size.height as i32;
let child_width = child.size().width as i32;
let child_height = child.size().height as i32;
let child_offset = match alignment {
Alignment::TopLeft => Pos::ZERO,
Alignment::Top => Pos::new(width / 2 - child_width / 2, 0),
Alignment::TopRight => Pos::new(width - child_width, 0),
Alignment::Right => Pos::new(width - child_width, height / 2 - child_height / 2),
Alignment::BottomRight => Pos::new(width - child_width, height - child_height),
Alignment::Bottom => Pos::new(width / 2 - child_width / 2, height - child_height),
Alignment::BottomLeft => Pos::new(0, height - child_height),
Alignment::Left => Pos::new(0, height / 2 - child_height / 2),
Alignment::Centre => Pos::new(width / 2 - child_width / 2, height / 2 - child_height / 2),
};
child.position(children, ctx.pos + child_offset, attribute_storage, ctx.viewport);
ControlFlow::Break(())
});
}
}
#[cfg(test)]
mod test {
use crate::testing::TestRunner;
#[test]
fn top_left() {
let tpl = "
align [alignment: 'top_left']
text 'x'
";
let expected = "
╔═══╗
║x ║
║ ║
║ ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn top() {
let tpl = "
align [alignment: 'top']
text 'x'
";
let expected = "
╔═══╗
║ x ║
║ ║
║ ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn top_right() {
let tpl = "
align [alignment: 'top_right']
text 'x'
";
let expected = "
╔═══╗
║ x║
║ ║
║ ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn right() {
let tpl = "
align [alignment: 'right']
text 'x'
";
let expected = "
╔═══╗
║ ║
║ x║
║ ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn bottom_right() {
let tpl = "
align [alignment: 'bottom_right']
text 'x'
";
let expected = "
╔═══╗
║ ║
║ ║
║ x║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn bottom() {
let tpl = "
align [alignment: 'bottom']
text 'x'
";
let expected = "
╔═══╗
║ ║
║ ║
║ x ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn bottom_left() {
let tpl = "
align [alignment: 'bottom_left']
text 'x'
";
let expected = "
╔═══╗
║ ║
║ ║
║x ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn left() {
let tpl = "
align [alignment: 'left']
text 'x'
";
let expected = "
╔═══╗
║ ║
║x ║
║ ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
#[test]
fn centre() {
let tpl = "
align [alignment: 'centre']
text 'x'
";
let expected = "
╔═══╗
║ ║
║ x ║
║ ║
╚═══╝
";
TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
}
}