#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs)]
#[doc(hidden)]
pub use paste::paste as __paste;
pub use taffy::TaffyError;
use taffy::{Layout, NodeId, TaffyTree};
use thiserror::Error;
#[doc(hidden)]
pub use winio_primitive::{Failable, HAlign, Layoutable, VAlign};
use winio_primitive::{Margin, Point, Rect, Size};
#[doc(hidden)]
pub trait LayoutChild: Failable {
fn child_preferred_size(&self) -> Result<Size, LayoutError<Self::Error>>;
fn child_min_size(&self) -> Result<Size, LayoutError<Self::Error>>;
fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>>;
fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>>;
fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>>;
fn layout(&mut self, layout: &Layout, loc: Point) -> Result<(), LayoutError<Self::Error>> {
self.set_child_rect(offset(rect_t2e(layout), loc))
}
}
impl<T: Layoutable> LayoutChild for T {
fn child_preferred_size(&self) -> Result<Size, LayoutError<Self::Error>> {
self.preferred_size().map_err(LayoutError::Child)
}
fn child_min_size(&self) -> Result<Size, LayoutError<Self::Error>> {
self.min_size().map_err(LayoutError::Child)
}
fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>> {
self.set_loc(p).map_err(LayoutError::Child)
}
fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>> {
self.set_size(s).map_err(LayoutError::Child)
}
fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>> {
self.set_rect(r).map_err(LayoutError::Child)
}
}
mod grid;
pub use grid::*;
mod stack_panel;
pub use stack_panel::*;
#[cfg(test)]
mod test;
fn rect_t2e(rect: &taffy::Layout) -> Rect {
Rect::new(
Point::new(rect.location.x as f64, rect.location.y as f64),
Size::new(rect.size.width as f64, rect.size.height as f64),
)
}
fn offset(mut a: Rect, offset: Point) -> Rect {
a.origin += offset.to_vector();
a
}
fn size_add_margin(size: Size, margin: Margin) -> Size {
Size::new(
size.width + margin.horizontal(),
size.height + margin.vertical(),
)
}
fn loc_sub_margin(loc: Point, margin: Margin) -> Point {
Point::new(loc.x + margin.left, loc.y + margin.top)
}
fn size_sub_margin(size: Size, margin: Margin) -> Size {
Size::new(
size.width - margin.horizontal(),
size.height - margin.vertical(),
)
}
fn rect_sub_margin(rect: Rect, margin: Margin) -> Rect {
Rect::new(
loc_sub_margin(rect.origin, margin),
size_sub_margin(rect.size, margin),
)
}
macro_rules! __layout_child {
($(#[$sm:meta])* struct $name:ident { $($(#[$m:meta])* $f:ident: $t:ty = $e:expr),*$(,)? }) => {
struct $name<'a, E> {
widget: &'a mut dyn $crate::LayoutChild<Error = E>,
width: Option<f64>,
height: Option<f64>,
margin: $crate::Margin,
halign: $crate::HAlign,
valign: $crate::VAlign,
$(
$(#[$m])*
$f: $t,
)*
}
impl<'a, E> $name<'a, E> {
#[allow(unused_doc_comments)]
pub fn new(widget: &'a mut dyn $crate::LayoutChild<Error = E>) -> Self {
Self {
widget,
width: None,
height: None,
margin: $crate::Margin::zero(),
halign: $crate::HAlign::Stretch,
valign: $crate::VAlign::Stretch,
$(
$(#[$m])*
$f: $e,
)*
}
}
}
impl<E> $crate::Failable for $name<'_, E> {
type Error = E;
}
impl<E> $crate::LayoutChild for $name<'_, E> {
fn child_preferred_size(&self) -> Result<$crate::Size, LayoutError<Self::Error>> {
Ok($crate::size_add_margin(self.widget.child_preferred_size()?, self.margin))
}
fn child_min_size(&self) -> Result<$crate::Size, LayoutError<Self::Error>> {
Ok($crate::size_add_margin(self.widget.child_min_size()?, self.margin))
}
fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>> {
self.widget.set_child_loc($crate::loc_sub_margin(p, self.margin))
}
fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>> {
self.widget.set_child_size($crate::size_sub_margin(s, self.margin))
}
fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>> {
self.widget.set_child_rect($crate::rect_sub_margin(r, self.margin))
}
}
$crate::__paste! {
$(#[$sm])*
pub struct [<$name Builder>]<'a, 'b, E> {
child: $name<'a, E>,
children: &'b mut Vec<$name<'a, E>>,
}
impl<E> [<$name Builder>]<'_, '_, E> {
pub fn width(mut self, v: f64) -> Self {
self.child.width = Some(v);
self
}
pub fn height(mut self, v: f64) -> Self {
self.child.height = Some(v);
self
}
pub fn size(self, s: $crate::Size) -> Self {
self.width(s.width).height(s.height)
}
pub fn margin(mut self, m: $crate::Margin) -> Self {
self.child.margin = m;
self
}
pub fn halign(mut self, v: $crate::HAlign) -> Self {
self.child.halign = v;
self
}
pub fn valign(mut self, v: $crate::VAlign) -> Self {
self.child.valign = v;
self
}
$(
$(#[$m])*
pub fn $f(mut self, v: $t) -> Self {
self.child.$f = v;
self
}
)*
pub fn finish(self) {
self.children.push(self.child);
}
}
}
};
}
pub(crate) use __layout_child as layout_child;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LayoutError<E> {
#[error("Taffy layout error: {0}")]
Taffy(#[from] TaffyError),
#[error("Child layout error: {0}")]
Child(E),
}
fn render<E>(
mut tree: TaffyTree,
root: NodeId,
nodes: Vec<NodeId>,
loc: Point,
size: Size,
children: &mut [impl LayoutChild<Error = E>],
) -> Result<(), LayoutError<E>> {
tree.compute_layout(
root,
taffy::Size {
width: taffy::AvailableSpace::Definite(size.width as _),
height: taffy::AvailableSpace::Definite(size.height as _),
},
)?;
for (id, child) in nodes.iter().zip(children) {
let layout = tree.layout(*id)?;
child.layout(layout, loc)?;
}
Ok(())
}
#[macro_export]
macro_rules! layout {
($root:expr, $($e:expr $(=> { $($t:tt)* })?),+$(,)?) => {{
#[allow(unused_mut)]
let mut root = $root;
$(
$crate::__layout_push!(root, &mut $e, $($($t)*)?);
)+
root
}};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __layout_push {
($root:expr, $e:expr,) => {
$root.push($e).finish();
};
($root:expr, $e:expr, $($(#[$me:meta])* $p:ident : $v:expr),+$(,)?) => {
let builder = $root.push($e);
$(
$(#[$me])*
let builder = builder.$p($v);
)+
builder.finish();
};
}