use crate::{color::Color, engine::{self, ShapeRotationConfig, VisualRotationConfig}, math::BoundingBox, elements::BorderPosition, renderer::ImageSource, shaders::ShaderConfig};
#[derive(Debug, Clone)]
pub struct Rectangle {
pub color: Color,
pub corner_radii: CornerRadii,
}
#[derive(Debug, Clone)]
pub struct Text {
pub text: String,
pub color: Color,
pub font_size: u16,
pub letter_spacing: u16,
pub line_height: u16,
pub font_asset: Option<&'static crate::renderer::FontAsset>,
}
#[derive(Debug, Clone)]
pub struct CornerRadii {
pub top_left: f32,
pub top_right: f32,
pub bottom_left: f32,
pub bottom_right: f32,
}
#[derive(Debug, Clone)]
pub struct BorderWidth {
pub left: u16,
pub right: u16,
pub top: u16,
pub bottom: u16,
pub between_children: u16,
}
#[derive(Debug, Clone)]
pub struct Border {
pub color: Color,
pub corner_radii: CornerRadii,
pub width: BorderWidth,
pub position: BorderPosition,
}
#[derive(Debug, Clone)]
pub struct Image {
pub background_color: Color,
pub corner_radii: CornerRadii,
pub data: ImageSource,
}
#[derive(Debug, Clone)]
pub struct Custom<CustomElementData> {
pub background_color: Color,
pub corner_radii: CornerRadii,
pub data: CustomElementData,
}
impl CornerRadii {
pub fn clamp_to_size(&mut self, width: f32, height: f32) {
let max_r = width.min(height) / 2.0;
self.top_left = self.top_left.clamp(0.0, max_r);
self.top_right = self.top_right.clamp(0.0, max_r);
self.bottom_left = self.bottom_left.clamp(0.0, max_r);
self.bottom_right = self.bottom_right.clamp(0.0, max_r);
}
}
impl From<crate::layout::CornerRadius> for CornerRadii {
fn from(value: crate::layout::CornerRadius) -> Self {
Self {
top_left: value.top_left,
top_right: value.top_right,
bottom_left: value.bottom_left,
bottom_right: value.bottom_right,
}
}
}
#[derive(Debug, Clone)]
pub enum RenderCommandConfig<CustomElementData> {
None(),
Rectangle(Rectangle),
Border(Border),
Text(Text),
Image(Image),
ScissorStart(),
ScissorEnd(),
Custom(Custom<CustomElementData>),
GroupBegin {
shader: Option<ShaderConfig>,
visual_rotation: Option<VisualRotationConfig>,
},
GroupEnd,
}
impl<CustomElementData: Clone + Default + std::fmt::Debug>
RenderCommandConfig<CustomElementData>
{
pub(crate) fn from_engine_render_command(value: &engine::InternalRenderCommand<CustomElementData>) -> Self {
match value.command_type {
engine::RenderCommandType::None => Self::None(),
engine::RenderCommandType::Rectangle => {
if let engine::InternalRenderData::Rectangle { background_color, corner_radius } = &value.render_data {
Self::Rectangle(Rectangle {
color: *background_color,
corner_radii: (*corner_radius).into(),
})
} else {
Self::None()
}
}
engine::RenderCommandType::Text => {
if let engine::InternalRenderData::Text { text, text_color, font_size, letter_spacing, line_height, font_asset } = &value.render_data {
Self::Text(Text {
text: text.clone(),
color: *text_color,
font_size: *font_size,
letter_spacing: *letter_spacing,
line_height: *line_height,
font_asset: *font_asset,
})
} else {
Self::None()
}
}
engine::RenderCommandType::Border => {
if let engine::InternalRenderData::Border { color, corner_radius, width, position } = &value.render_data {
Self::Border(Border {
color: *color,
corner_radii: (*corner_radius).into(),
width: BorderWidth {
left: width.left,
right: width.right,
top: width.top,
bottom: width.bottom,
between_children: width.between_children,
},
position: *position,
})
} else {
Self::None()
}
}
engine::RenderCommandType::Image => {
if let engine::InternalRenderData::Image { background_color, corner_radius, image_data } = &value.render_data {
Self::Image(Image {
data: image_data.clone(),
corner_radii: (*corner_radius).into(),
background_color: *background_color,
})
} else {
Self::None()
}
}
engine::RenderCommandType::ScissorStart => Self::ScissorStart(),
engine::RenderCommandType::ScissorEnd => Self::ScissorEnd(),
engine::RenderCommandType::GroupBegin => {
let shader = value.effects.first().cloned();
let visual_rotation = value.visual_rotation;
Self::GroupBegin { shader, visual_rotation }
}
engine::RenderCommandType::GroupEnd => Self::GroupEnd,
engine::RenderCommandType::Custom => {
if let engine::InternalRenderData::Custom { background_color, corner_radius, custom_data } = &value.render_data {
Self::Custom(Custom {
background_color: *background_color,
corner_radii: (*corner_radius).into(),
data: custom_data.clone(),
})
} else {
Self::None()
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct RenderCommand<CustomElementData> {
pub bounding_box: BoundingBox,
pub config: RenderCommandConfig<CustomElementData>,
pub id: u32,
pub z_index: i16,
pub effects: Vec<ShaderConfig>,
pub shape_rotation: Option<ShapeRotationConfig>,
}
impl<CustomElementData: Clone + Default + std::fmt::Debug> RenderCommand<CustomElementData> {
pub(crate) fn from_engine_render_command(value: &engine::InternalRenderCommand<CustomElementData>) -> Self {
let mut config = RenderCommandConfig::from_engine_render_command(value);
let bb = value.bounding_box;
match &mut config {
RenderCommandConfig::Rectangle(r) => r.corner_radii.clamp_to_size(bb.width, bb.height),
RenderCommandConfig::Border(b) => b.corner_radii.clamp_to_size(bb.width, bb.height),
RenderCommandConfig::Image(i) => i.corner_radii.clamp_to_size(bb.width, bb.height),
RenderCommandConfig::Custom(c) => c.corner_radii.clamp_to_size(bb.width, bb.height),
_ => {}
}
Self {
id: value.id,
z_index: value.z_index,
bounding_box: bb,
config,
effects: value.effects.clone(),
shape_rotation: value.shape_rotation,
}
}
}