use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use skia_safe::{Canvas, PaintStyle, Rect};
use crate::engine::renderer::paint_from_hex;
use crate::layout::{layout_flex, Constraints, LayoutNode};
use crate::schema::{LayerStyle, SizeDimension};
use crate::traits::{
Border, Bordered, BorderedMut, Container, FlexConfig, FlexContainer,
FlexContainerMut, RenderContext, Rounded, RoundedMut, Shadow, Shadowed, ShadowedMut,
TimingConfig, Widget,
};
use super::ChildComponent;
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct FlexSize {
pub width: SizeDimension,
pub height: SizeDimension,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct Flex {
#[serde(default)]
pub children: Vec<ChildComponent>,
#[serde(default)]
pub size: Option<FlexSize>,
#[serde(flatten)]
pub timing: TimingConfig,
#[serde(default)]
pub style: LayerStyle,
}
crate::impl_traits!(Flex {
Animatable => style,
Timed => timing,
Styled => style,
});
impl Container for Flex {
fn children(&self) -> &[ChildComponent] {
&self.children
}
}
impl FlexContainer for Flex {
fn flex_config(&self) -> &FlexConfig {
unreachable!("Use style directly for flex config")
}
}
impl FlexContainerMut for Flex {
fn flex_config_mut(&mut self) -> &mut FlexConfig {
unreachable!("Use style directly for flex config")
}
}
impl Bordered for Flex {
fn border(&self) -> Option<&Border> {
None }
}
impl BorderedMut for Flex {
fn set_border(&mut self, _border: Option<Border>) {}
}
impl Rounded for Flex {
fn corner_radius(&self) -> f32 {
self.style.border_radius_or(12.0)
}
}
impl RoundedMut for Flex {
fn set_corner_radius(&mut self, radius: f32) {
self.style.border_radius = Some(radius);
}
}
impl Shadowed for Flex {
fn shadow(&self) -> Option<&Shadow> {
None }
}
impl ShadowedMut for Flex {
fn set_shadow(&mut self, _shadow: Option<Shadow>) {}
}
impl crate::traits::Backgrounded for Flex {
fn background(&self) -> Option<&str> {
self.style.background.as_deref()
}
}
impl crate::traits::BackgroundedMut for Flex {
fn set_background(&mut self, bg: Option<String>) {
self.style.background = bg;
}
}
impl crate::traits::Clipped for Flex {
fn clip(&self) -> bool {
true
}
}
impl Widget for Flex {
fn render(&self, canvas: &Canvas, layout: &LayoutNode, ctx: &RenderContext, _props: &crate::engine::animator::AnimatedProperties) -> Result<()> {
let corner_radius = self.style.border_radius_or(12.0);
let rect = Rect::from_xywh(0.0, 0.0, layout.width, layout.height);
let rrect = skia_safe::RRect::new_rect_xy(rect, corner_radius, corner_radius);
if let Some(ref shadow) = self.style.box_shadow {
let shadow_rect = Rect::from_xywh(
shadow.offset_x, shadow.offset_y,
layout.width, layout.height,
);
let shadow_rrect = skia_safe::RRect::new_rect_xy(
shadow_rect, corner_radius, corner_radius,
);
let mut shadow_paint = paint_from_hex(&shadow.color);
if shadow.blur > 0.0 {
shadow_paint.set_mask_filter(skia_safe::MaskFilter::blur(
skia_safe::BlurStyle::Normal,
shadow.blur / 2.0,
false,
));
}
canvas.draw_rrect(shadow_rrect, &shadow_paint);
}
if let Some(ref bg) = self.style.background {
let bg_paint = paint_from_hex(bg);
canvas.draw_rrect(rrect, &bg_paint);
}
canvas.save();
canvas.clip_rrect(rrect, skia_safe::ClipOp::Intersect, true);
crate::engine::render_v2::render_children_with_stagger(canvas, &self.children, layout, ctx, self.style.stagger)?;
canvas.restore();
if let Some(ref border) = self.style.border {
let mut border_paint = paint_from_hex(&border.color);
border_paint.set_style(PaintStyle::Stroke);
border_paint.set_stroke_width(border.width);
canvas.draw_rrect(rrect, &border_paint);
}
if let Some(ref gb) = self.style.gradient_border {
super::draw_gradient_border(canvas, &rrect, gb);
}
Ok(())
}
fn measure(&self, constraints: &Constraints) -> (f32, f32) {
let layout = self.layout(constraints);
(layout.width, layout.height)
}
fn layout(&self, constraints: &Constraints) -> LayoutNode {
let c = resolve_size_constraints(&self.size, constraints);
layout_flex(self, &c)
}
}
pub(crate) fn resolve_size_constraints(size: &Option<FlexSize>, constraints: &Constraints) -> Constraints {
let (w_min, w_max) = match size.as_ref().map(|s| &s.width) {
Some(SizeDimension::Fixed(v)) => (*v, *v),
Some(SizeDimension::Auto) => (0.0, constraints.max_width),
_ => (constraints.min_width, constraints.max_width),
};
let (h_min, h_max) = match size.as_ref().map(|s| &s.height) {
Some(SizeDimension::Fixed(v)) => (*v, *v),
Some(SizeDimension::Auto) => (0.0, constraints.max_height),
_ => (constraints.min_height, constraints.max_height),
};
Constraints {
min_width: w_min,
max_width: w_max,
min_height: h_min,
max_height: h_max,
}
}