pub mod text;
use crate::primitives::Path;
pub use kurbo::RoundedRectRadii;
use pax_engine::api::PropertyLiteral;
pub use pax_engine::api::Size;
use pax_engine::api::{Color, Numeric};
use pax_engine::*;
use pax_runtime::api::IntoableLiteral;
use piet::UnitPoint;
#[pax]
#[custom(Default)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Stroke {
pub color: Property<Color>,
pub width: Property<Size>,
}
impl From<IntoableLiteral> for Stroke {
fn from(value: IntoableLiteral) -> Self {
match value {
IntoableLiteral::Color(c) => Stroke {
color: Box::new(PropertyLiteral::new(c)),
width: Box::new(PropertyLiteral::new(Numeric::from(1).into())),
},
_ => {
unreachable!()
}
}
}
}
impl Default for Stroke {
fn default() -> Self {
Self {
color: Default::default(),
width: Box::new(PropertyLiteral::new(Size::Pixels(0.0.into()))),
}
}
}
#[cfg_attr(debug_assertions, derive(Debug))]
#[pax]
pub struct StackerCell {
pub x_px: f64,
pub y_px: f64,
pub width_px: f64,
pub height_px: f64,
}
#[pax]
pub enum StackerDirection {
Vertical,
#[default]
Horizontal,
}
#[pax]
pub enum SidebarDirection {
Left,
#[default]
Right,
}
#[cfg_attr(debug_assertions, derive(Debug))]
#[pax]
#[custom(Default)]
pub enum Fill {
Solid(Color),
LinearGradient(LinearGradient),
RadialGradient(RadialGradient),
}
impl Into<Fill> for Color {
fn into(self) -> Fill {
Fill::Solid(self)
}
}
impl From<IntoableLiteral> for Fill {
fn from(value: IntoableLiteral) -> Self {
match value {
IntoableLiteral::Color(c) => c.into(),
_ => {
unreachable!()
}
}
}
}
#[cfg_attr(debug_assertions, derive(Debug))]
#[pax]
pub struct LinearGradient {
pub start: (Size, Size),
pub end: (Size, Size),
pub stops: Vec<GradientStop>,
}
#[cfg_attr(debug_assertions, derive(Debug))]
#[pax]
pub struct RadialGradient {
pub end: (Size, Size),
pub start: (Size, Size),
pub radius: f64,
pub stops: Vec<GradientStop>,
}
#[cfg_attr(debug_assertions, derive(Debug))]
#[pax]
pub struct GradientStop {
pub position: Size,
pub color: Color,
}
impl GradientStop {
pub fn get(color: Color, position: Size) -> GradientStop {
GradientStop { position, color }
}
}
impl Default for Fill {
fn default() -> Self {
Self::Solid(Color::default())
}
}
impl Fill {
pub fn to_unit_point((x, y): (Size, Size), (width, height): (f64, f64)) -> UnitPoint {
let normalized_x = match x {
Size::Pixels(val) => val.to_float() / width,
Size::Percent(val) => val.to_float() / 100.0,
Size::Combined(pix, per) => (pix.to_float() / width) + (per.to_float() / 100.0),
};
let normalized_y = match y {
Size::Pixels(val) => val.to_float() / height,
Size::Percent(val) => val.to_float() / 100.0,
Size::Combined(pix, per) => (pix.to_float() / width) + (per.to_float() / 100.0),
};
UnitPoint::new(normalized_x, normalized_y)
}
pub fn to_piet_gradient_stops(stops: Vec<GradientStop>) -> Vec<piet::GradientStop> {
let mut ret = Vec::new();
for gradient_stop in stops {
match gradient_stop.position {
Size::Pixels(_) => {
panic!("Gradient stops must be specified in percentages");
}
Size::Percent(p) => {
ret.push(piet::GradientStop {
pos: (p.to_float() / 100.0) as f32,
color: gradient_stop.color.to_piet_color(),
});
}
Size::Combined(_, _) => {
panic!("Gradient stops must be specified in percentages");
}
}
}
ret
}
#[allow(non_snake_case)]
pub fn linearGradient(
start: (Size, Size),
end: (Size, Size),
stops: Vec<GradientStop>,
) -> Fill {
Fill::LinearGradient(LinearGradient { start, end, stops })
}
}
#[pax]
#[cfg_attr(debug_assertions, derive(Debug))]
pub enum PathElement {
#[default]
Empty,
Point(Size, Size),
Line,
Curve(Size, Size),
Close,
}
impl PathElement {
pub fn line() -> Self {
Self::Line
}
pub fn close() -> Self {
Self::Close
}
pub fn point(x: Size, y: Size) -> Self {
Self::Point(x, y)
}
pub fn curve(x: Size, y: Size) -> Self {
Self::Curve(x, y)
}
}
#[pax]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct LineSegmentData {
pub start: Point,
pub end: Point,
}
impl LineSegmentData {
pub fn new(p1: Point, p2: Point) -> Self {
Self { start: p1, end: p2 }
}
}
#[pax]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct CurveSegmentData {
pub start: Point,
pub handle: Point,
pub end: Point,
}
#[pax]
#[derive(Copy)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Point {
pub x: Size,
pub y: Size,
}
impl Point {
pub fn new(x: Size, y: Size) -> Self {
Self { x, y }
}
pub fn to_kurbo_point(self, bounds: (f64, f64)) -> kurbo::Point {
let x = self.x.evaluate(bounds, api::Axis::X);
let y = self.y.evaluate(bounds, api::Axis::Y);
kurbo::Point { x, y }
}
}
impl Path {
pub fn start(x: Size, y: Size) -> Vec<PathElement> {
let mut start: Vec<PathElement> = Vec::new();
start.push(PathElement::Point(x, y));
start
}
pub fn line_to(mut path: Vec<PathElement>, x: Size, y: Size) -> Vec<PathElement> {
path.push(PathElement::Line);
path.push(PathElement::Point(x, y));
path
}
pub fn curve_to(
mut path: Vec<PathElement>,
h_x: Size,
h_y: Size,
x: Size,
y: Size,
) -> Vec<PathElement> {
path.push(PathElement::Curve(h_x, h_y));
path.push(PathElement::Point(x, y));
path
}
}
#[pax]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct RectangleCornerRadii {
pub top_left: Property<f64>,
pub top_right: Property<f64>,
pub bottom_right: Property<f64>,
pub bottom_left: Property<f64>,
}
impl Into<RoundedRectRadii> for &RectangleCornerRadii {
fn into(self) -> RoundedRectRadii {
RoundedRectRadii::new(
*self.top_left.get(),
*self.top_right.get(),
*self.bottom_right.get(),
*self.bottom_left.get(),
)
}
}
impl RectangleCornerRadii {
pub fn radii(
top_left: Numeric,
top_right: Numeric,
bottom_right: Numeric,
bottom_left: Numeric,
) -> Self {
RectangleCornerRadii {
top_left: Box::new(PropertyLiteral::new(top_left.to_float())),
top_right: Box::new(PropertyLiteral::new(top_right.to_float())),
bottom_right: Box::new(PropertyLiteral::new(bottom_right.to_float())),
bottom_left: Box::new(PropertyLiteral::new(bottom_left.to_float())),
}
}
}