use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
elem, Cast, Content, NativeElement, Packed, Show, Smart, StyleChain,
};
use crate::layout::{Abs, BlockElem, Corners, Length, Point, Rel, Sides, Size, Sizing};
use crate::visualize::{Curve, FixedStroke, Paint, Stroke};
#[elem(title = "Rectangle", Show)]
pub struct RectElem {
pub width: Smart<Rel<Length>>,
pub height: Sizing,
pub fill: Option<Paint>,
#[resolve]
#[fold]
pub stroke: Smart<Sides<Option<Option<Stroke>>>>,
#[resolve]
#[fold]
pub radius: Corners<Option<Rel<Length>>>,
#[resolve]
#[fold]
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
#[positional]
#[borrowed]
pub body: Option<Content>,
}
impl Show for Packed<RectElem> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(BlockElem::single_layouter(self.clone(), engine.routines.layout_rect)
.with_width(self.width(styles))
.with_height(self.height(styles))
.pack()
.spanned(self.span()))
}
}
#[elem(Show)]
pub struct SquareElem {
#[external]
pub size: Smart<Length>,
#[parse(
let size = args.named::<Smart<Length>>("size")?.map(|s| s.map(Rel::from));
match size {
None => args.named("width")?,
size => size,
}
)]
pub width: Smart<Rel<Length>>,
#[parse(match size {
None => args.named("height")?,
size => size.map(Into::into),
})]
pub height: Sizing,
pub fill: Option<Paint>,
#[resolve]
#[fold]
pub stroke: Smart<Sides<Option<Option<Stroke>>>>,
#[resolve]
#[fold]
pub radius: Corners<Option<Rel<Length>>>,
#[resolve]
#[fold]
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
#[positional]
#[borrowed]
pub body: Option<Content>,
}
impl Show for Packed<SquareElem> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(BlockElem::single_layouter(self.clone(), engine.routines.layout_square)
.with_width(self.width(styles))
.with_height(self.height(styles))
.pack()
.spanned(self.span()))
}
}
#[elem(Show)]
pub struct EllipseElem {
pub width: Smart<Rel<Length>>,
pub height: Sizing,
pub fill: Option<Paint>,
#[resolve]
#[fold]
pub stroke: Smart<Option<Stroke>>,
#[resolve]
#[fold]
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
#[positional]
#[borrowed]
pub body: Option<Content>,
}
impl Show for Packed<EllipseElem> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(BlockElem::single_layouter(self.clone(), engine.routines.layout_ellipse)
.with_width(self.width(styles))
.with_height(self.height(styles))
.pack()
.spanned(self.span()))
}
}
#[elem(Show)]
pub struct CircleElem {
#[external]
pub radius: Length,
#[parse(
let size = args
.named::<Smart<Length>>("radius")?
.map(|s| s.map(|r| 2.0 * Rel::from(r)));
match size {
None => args.named("width")?,
size => size,
}
)]
pub width: Smart<Rel<Length>>,
#[parse(match size {
None => args.named("height")?,
size => size.map(Into::into),
})]
pub height: Sizing,
pub fill: Option<Paint>,
#[resolve]
#[fold]
#[default(Smart::Auto)]
pub stroke: Smart<Option<Stroke>>,
#[resolve]
#[fold]
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
#[positional]
#[borrowed]
pub body: Option<Content>,
}
impl Show for Packed<CircleElem> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(BlockElem::single_layouter(self.clone(), engine.routines.layout_circle)
.with_width(self.width(styles))
.with_height(self.height(styles))
.pack()
.spanned(self.span()))
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Shape {
pub geometry: Geometry,
pub fill: Option<Paint>,
pub fill_rule: FillRule,
pub stroke: Option<FixedStroke>,
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum FillRule {
#[default]
NonZero,
EvenOdd,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Geometry {
Line(Point),
Rect(Size),
Curve(Curve),
}
impl Geometry {
pub fn filled(self, fill: impl Into<Paint>) -> Shape {
Shape {
geometry: self,
fill: Some(fill.into()),
fill_rule: FillRule::default(),
stroke: None,
}
}
pub fn stroked(self, stroke: FixedStroke) -> Shape {
Shape {
geometry: self,
fill: None,
fill_rule: FillRule::default(),
stroke: Some(stroke),
}
}
pub fn bbox_size(&self) -> Size {
match self {
Self::Line(line) => Size::new(line.x, line.y),
Self::Rect(rect) => *rect,
Self::Curve(curve) => curve.bbox_size(),
}
}
}