use super::interface::{
component::{Component, ComponentContext, RenderParams},
render_error,
style::{ComponentAlign, ComponentStyle, RawComponentStyle, Style},
};
use crate::{
edges::{edge::Edge, padding::Padding},
utils::blur::{apply, ImageRefMut},
};
use rgb::FromSlice;
use tiny_skia::{
Color, FillRule, Paint, PathBuilder, Pixmap, PixmapPaint, Rect as SkiaRect, Transform,
};
#[derive(Debug)]
struct ShadowConfig {
x: f32,
y: f32,
blur: f32,
color: Color,
}
pub struct Rect {
radius: f32,
min_width: f32,
padding: Padding,
color: Color,
children: Vec<Box<dyn Component>>,
shadow: Option<ShadowConfig>,
component_name: &'static str,
}
impl Component for Rect {
fn name(&self) -> &'static str {
self.component_name
}
fn children(&self) -> &Vec<Box<dyn Component>> {
&self.children
}
fn style(&self, _context: &ComponentContext) -> RawComponentStyle {
Style::default()
.min_width(self.min_width)
.align(ComponentAlign::Column)
.padding(self.padding.clone())
}
fn draw_self(
&self,
pixmap: &mut Pixmap,
context: &ComponentContext,
render_params: &RenderParams,
style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> {
let mut path_builder = PathBuilder::new();
let x = render_params.x;
let y = render_params.y;
let w = style.width;
let h = style.height;
let rect_width = w - 2. * self.radius;
let rect_height = h - 2. * self.radius;
path_builder.move_to(x + self.radius, y);
path_builder.line_to(x + self.radius + rect_width, y);
path_builder.line_to(x + self.radius + rect_width, y + self.radius);
path_builder.line_to(x + rect_width + self.radius * 2., y + self.radius);
path_builder.line_to(
x + rect_width + self.radius * 2.,
y + rect_height + self.radius,
);
path_builder.line_to(x + rect_width + self.radius, y + rect_height + self.radius);
path_builder.line_to(
x + rect_width + self.radius,
y + rect_height + self.radius * 2.,
);
path_builder.line_to(x + self.radius, y + rect_height + self.radius * 2.);
path_builder.line_to(x + self.radius, y + rect_height + self.radius);
path_builder.line_to(x, y + rect_height + self.radius);
path_builder.line_to(x, y + self.radius);
path_builder.line_to(x + self.radius, y + self.radius);
path_builder.line_to(x + self.radius, y);
path_builder.line_to(x + self.radius + rect_width, y);
path_builder.push_circle(
x + rect_width + self.radius,
y + rect_height + self.radius,
self.radius,
);
path_builder.push_circle(x + self.radius + rect_width, y + self.radius, self.radius);
path_builder.push_circle(x + self.radius, y + self.radius, self.radius);
path_builder.push_circle(x + self.radius, y + rect_height + self.radius, self.radius);
path_builder.close();
let path = path_builder.finish().unwrap();
let mut paint = Paint::default();
let transform = Transform::from_scale(context.scale_factor, context.scale_factor);
paint.set_color(self.color);
if let Some(shadow) = &self.shadow {
self.draw_shadow(pixmap, render_params, shadow, transform, style, context);
}
pixmap.fill_path(&path, &paint, FillRule::Winding, transform, None);
Ok(())
}
}
impl Rect {
pub fn new(
radius: f32,
color: Color,
min_width: Option<f32>,
padding: Padding,
component_name: &'static str,
children: Vec<Box<dyn Component>>,
) -> Rect {
Rect {
radius,
color,
children,
padding,
min_width: min_width.unwrap_or(0.),
component_name,
shadow: None,
}
}
fn draw_shadow(
&self,
parent_pixmap: &mut Pixmap,
render_params: &RenderParams,
shadow: &ShadowConfig,
transform: Transform,
host_element_style: &ComponentStyle,
context: &ComponentContext,
) {
let w = host_element_style.width;
let h = host_element_style.height;
let config = context.take_snapshot_params.clone();
let background_padding: Padding = config.window.margin.clone().into();
let pixmap_w = w + background_padding.horizontal();
let pixmap_h = h + background_padding.vertical();
let pixmap_offset_x = (pixmap_w - w) / 2.;
let pixmap_offset_y = (pixmap_h - h) / 2.;
let mut pixmap = Pixmap::new(pixmap_w as u32, pixmap_h as u32).unwrap();
let mut paint = Paint::default();
let shadow_pixmap_x = render_params.x - pixmap_offset_x;
let shadow_pixmap_y = render_params.y - pixmap_offset_y;
paint.set_color(shadow.color);
pixmap.fill_rect(
SkiaRect::from_xywh(shadow.x + pixmap_offset_x, shadow.y + pixmap_offset_y, w, h)
.unwrap(),
&paint,
Transform::identity(),
None,
);
let rgba = pixmap.data_mut().as_rgba_mut();
apply(
shadow.blur as f64,
shadow.blur as f64,
ImageRefMut::new(pixmap_w as u32, pixmap_h as u32, rgba),
);
parent_pixmap.draw_pixmap(
shadow_pixmap_x as i32,
shadow_pixmap_y as i32,
pixmap.as_ref(),
&PixmapPaint::default(),
transform,
None,
);
}
pub fn create_with_border(
radius: f32,
color: Color,
min_width: f32,
padding: Padding,
border_width: f32,
border_color: Color,
children: Vec<Box<dyn Component>>,
) -> Rect {
Rect::new(
radius,
color,
Some(min_width + border_width * 2.),
Padding::from_value(border_width),
"RectUnderLayer",
vec![Box::new(Rect::new(
radius - border_width,
border_color,
Some(min_width + border_width),
Padding::from_value(border_width),
"RectBorderLayer",
vec![Box::new(Rect::new(
radius - border_width * 2.,
color,
Some(min_width),
padding,
"RectInnerLayer",
children,
))],
))],
)
}
pub fn shadow(mut self, x: f32, y: f32, blur: f32, color: Color) -> Rect {
self.shadow = Some(ShadowConfig { x, y, blur, color });
self
}
}