1use crate::{color::Color, engine::{self, ShapeRotationConfig, VisualRotationConfig}, math::BoundingBox, renderer::ImageSource, shaders::ShaderConfig};
2
3#[derive(Debug, Clone)]
5pub struct Rectangle {
6 pub color: Color,
8 pub corner_radii: CornerRadii,
10}
11
12#[derive(Debug, Clone)]
14pub struct Text {
15 pub text: String,
17 pub color: Color,
19 pub font_size: u16,
21 pub letter_spacing: u16,
23 pub line_height: u16,
25 pub font_asset: Option<&'static crate::renderer::FontAsset>,
27}
28
29#[derive(Debug, Clone)]
31pub struct CornerRadii {
32 pub top_left: f32,
34 pub top_right: f32,
36 pub bottom_left: f32,
38 pub bottom_right: f32,
40}
41
42#[derive(Debug, Clone)]
44pub struct BorderWidth {
45 pub left: u16,
47 pub right: u16,
49 pub top: u16,
51 pub bottom: u16,
53 pub between_children: u16,
55}
56
57#[derive(Debug, Clone)]
59pub struct Border {
60 pub color: Color,
62 pub corner_radii: CornerRadii,
64 pub width: BorderWidth,
66}
67
68#[derive(Debug, Clone)]
70pub struct Image {
71 pub background_color: Color,
73 pub corner_radii: CornerRadii,
75 pub data: ImageSource,
77}
78
79#[derive(Debug, Clone)]
81pub struct Custom<CustomElementData> {
82 pub background_color: Color,
84 pub corner_radii: CornerRadii,
86 pub data: CustomElementData,
88}
89
90impl CornerRadii {
91 pub fn clamp_to_size(&mut self, width: f32, height: f32) {
92 let max_r = width.min(height) / 2.0;
93 self.top_left = self.top_left.clamp(0.0, max_r);
94 self.top_right = self.top_right.clamp(0.0, max_r);
95 self.bottom_left = self.bottom_left.clamp(0.0, max_r);
96 self.bottom_right = self.bottom_right.clamp(0.0, max_r);
97 }
98}
99
100impl From<crate::layout::CornerRadius> for CornerRadii {
101 fn from(value: crate::layout::CornerRadius) -> Self {
102 Self {
103 top_left: value.top_left,
104 top_right: value.top_right,
105 bottom_left: value.bottom_left,
106 bottom_right: value.bottom_right,
107 }
108 }
109}
110
111#[derive(Debug, Clone)]
112pub enum RenderCommandConfig<CustomElementData> {
113 None(),
114 Rectangle(Rectangle),
115 Border(Border),
116 Text(Text),
117 Image(Image),
118 ScissorStart(),
119 ScissorEnd(),
120 Custom(Custom<CustomElementData>),
121 GroupBegin {
124 shader: Option<ShaderConfig>,
126 visual_rotation: Option<VisualRotationConfig>,
128 },
129 GroupEnd,
130}
131
132impl<CustomElementData: Clone + Default + std::fmt::Debug>
133 RenderCommandConfig<CustomElementData>
134{
135 pub(crate) fn from_engine_render_command(value: &engine::InternalRenderCommand<CustomElementData>) -> Self {
136 match value.command_type {
137 engine::RenderCommandType::None => Self::None(),
138 engine::RenderCommandType::Rectangle => {
139 if let engine::InternalRenderData::Rectangle { background_color, corner_radius } = &value.render_data {
140 Self::Rectangle(Rectangle {
141 color: *background_color,
142 corner_radii: (*corner_radius).into(),
143 })
144 } else {
145 Self::None()
146 }
147 }
148 engine::RenderCommandType::Text => {
149 if let engine::InternalRenderData::Text { text, text_color, font_size, letter_spacing, line_height, font_asset } = &value.render_data {
150 Self::Text(Text {
151 text: text.clone(),
152 color: *text_color,
153 font_size: *font_size,
154 letter_spacing: *letter_spacing,
155 line_height: *line_height,
156 font_asset: *font_asset,
157 })
158 } else {
159 Self::None()
160 }
161 }
162 engine::RenderCommandType::Border => {
163 if let engine::InternalRenderData::Border { color, corner_radius, width } = &value.render_data {
164 Self::Border(Border {
165 color: *color,
166 corner_radii: (*corner_radius).into(),
167 width: BorderWidth {
168 left: width.left,
169 right: width.right,
170 top: width.top,
171 bottom: width.bottom,
172 between_children: width.between_children,
173 },
174 })
175 } else {
176 Self::None()
177 }
178 }
179 engine::RenderCommandType::Image => {
180 if let engine::InternalRenderData::Image { background_color, corner_radius, image_data } = &value.render_data {
181 Self::Image(Image {
182 data: image_data.clone(),
183 corner_radii: (*corner_radius).into(),
184 background_color: *background_color,
185 })
186 } else {
187 Self::None()
188 }
189 }
190 engine::RenderCommandType::ScissorStart => Self::ScissorStart(),
191 engine::RenderCommandType::ScissorEnd => Self::ScissorEnd(),
192 engine::RenderCommandType::GroupBegin => {
193 let shader = value.effects.first().cloned();
196 let visual_rotation = value.visual_rotation;
197 Self::GroupBegin { shader, visual_rotation }
198 }
199 engine::RenderCommandType::GroupEnd => Self::GroupEnd,
200 engine::RenderCommandType::Custom => {
201 if let engine::InternalRenderData::Custom { background_color, corner_radius, custom_data } = &value.render_data {
202 Self::Custom(Custom {
203 background_color: *background_color,
204 corner_radii: (*corner_radius).into(),
205 data: custom_data.clone(),
206 })
207 } else {
208 Self::None()
209 }
210 }
211 }
212 }
213}
214
215#[derive(Debug, Clone)]
217pub struct RenderCommand<CustomElementData> {
218 pub bounding_box: BoundingBox,
220 pub config: RenderCommandConfig<CustomElementData>,
222 pub id: u32,
224 pub z_index: i16,
227 pub effects: Vec<ShaderConfig>,
229 pub shape_rotation: Option<ShapeRotationConfig>,
231}
232
233impl<CustomElementData: Clone + Default + std::fmt::Debug> RenderCommand<CustomElementData> {
234 pub(crate) fn from_engine_render_command(value: &engine::InternalRenderCommand<CustomElementData>) -> Self {
235 let mut config = RenderCommandConfig::from_engine_render_command(value);
236 let bb = value.bounding_box;
237 match &mut config {
238 RenderCommandConfig::Rectangle(r) => r.corner_radii.clamp_to_size(bb.width, bb.height),
239 RenderCommandConfig::Border(b) => b.corner_radii.clamp_to_size(bb.width, bb.height),
240 RenderCommandConfig::Image(i) => i.corner_radii.clamp_to_size(bb.width, bb.height),
241 RenderCommandConfig::Custom(c) => c.corner_radii.clamp_to_size(bb.width, bb.height),
242 _ => {}
243 }
244 Self {
245 id: value.id,
246 z_index: value.z_index,
247 bounding_box: bb,
248 config,
249 effects: value.effects.clone(),
250 shape_rotation: value.shape_rotation,
251 }
252 }
253}