1use crate::{
2 PrefabValue, Scalar,
3 layout::CoordsMappingScaling,
4 props::Props,
5 widget::{
6 WidgetId,
7 node::WidgetNode,
8 unit::WidgetUnitData,
9 utils::{Color, Rect, Transform, Vec2},
10 },
11};
12use serde::{Deserialize, Serialize};
13use std::{collections::HashMap, convert::TryFrom, sync::Arc};
14
15#[derive(Debug, Default, Clone, Serialize, Deserialize)]
16pub struct ImageBoxFrame {
17 #[serde(default)]
18 pub source: Rect,
19 #[serde(default)]
20 pub destination: Rect,
21 #[serde(default)]
22 pub frame_only: bool,
23 #[serde(default)]
24 pub frame_keep_aspect_ratio: bool,
25}
26
27impl From<Scalar> for ImageBoxFrame {
28 fn from(v: Scalar) -> Self {
29 Self {
30 source: v.into(),
31 destination: v.into(),
32 frame_only: false,
33 frame_keep_aspect_ratio: false,
34 }
35 }
36}
37
38impl From<(Scalar, bool)> for ImageBoxFrame {
39 fn from((v, fo): (Scalar, bool)) -> Self {
40 Self {
41 source: v.into(),
42 destination: v.into(),
43 frame_only: fo,
44 frame_keep_aspect_ratio: false,
45 }
46 }
47}
48
49#[derive(Debug, Default, Clone, Serialize, Deserialize)]
50pub enum ImageBoxImageScaling {
51 #[default]
52 Stretch,
53 Frame(ImageBoxFrame),
54}
55
56#[derive(Debug, Default, Clone, Serialize, Deserialize)]
57pub struct ImageBoxColor {
58 #[serde(default)]
59 pub color: Color,
60 #[serde(default)]
61 pub scaling: ImageBoxImageScaling,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct ImageBoxImage {
66 #[serde(default)]
67 pub id: String,
68 #[serde(default)]
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub source_rect: Option<Rect>,
71 #[serde(default)]
72 pub scaling: ImageBoxImageScaling,
73 #[serde(default = "ImageBoxImage::default_tint")]
74 pub tint: Color,
75}
76
77impl Default for ImageBoxImage {
78 fn default() -> Self {
79 Self {
80 id: Default::default(),
81 source_rect: Default::default(),
82 scaling: Default::default(),
83 tint: Self::default_tint(),
84 }
85 }
86}
87
88impl ImageBoxImage {
89 fn default_tint() -> Color {
90 Color {
91 r: 1.0,
92 g: 1.0,
93 b: 1.0,
94 a: 1.0,
95 }
96 }
97}
98
99#[derive(Debug, Default, Clone, Serialize, Deserialize)]
100pub struct ImageBoxProceduralVertex {
101 #[serde(default)]
102 pub position: Vec2,
103 #[serde(default)]
104 pub page: Scalar,
105 #[serde(default)]
106 pub tex_coord: Vec2,
107 #[serde(default)]
108 pub color: Color,
109}
110
111#[derive(Debug, Default, Clone, Serialize, Deserialize)]
112pub struct ImageBoxProceduralMeshData {
113 #[serde(default)]
114 #[serde(skip_serializing_if = "Vec::is_empty")]
115 pub vertices: Vec<ImageBoxProceduralVertex>,
116 #[serde(default)]
117 #[serde(skip_serializing_if = "Vec::is_empty")]
118 pub triangles: Vec<[u32; 3]>,
119}
120
121#[derive(Clone, Serialize, Deserialize)]
122pub enum ImageBoxProceduralMesh {
123 Owned(ImageBoxProceduralMeshData),
124 Shared(Arc<ImageBoxProceduralMeshData>),
125 #[serde(skip)]
127 #[allow(clippy::type_complexity)]
128 Generator(
129 Arc<
130 dyn Fn(Rect, &HashMap<String, Scalar>) -> ImageBoxProceduralMeshData
131 + 'static
132 + Send
133 + Sync,
134 >,
135 ),
136}
137
138impl std::fmt::Debug for ImageBoxProceduralMesh {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match self {
141 Self::Owned(data) => write!(f, "Owned({data:?})"),
142 Self::Shared(data) => write!(f, "Shared({data:?})"),
143 Self::Generator(_) => write!(f, "Generator(...)"),
144 }
145 }
146}
147
148impl Default for ImageBoxProceduralMesh {
149 fn default() -> Self {
150 Self::Owned(Default::default())
151 }
152}
153
154#[derive(Debug, Default, Clone, Serialize, Deserialize)]
155pub struct ImageBoxProcedural {
156 #[serde(default)]
157 pub id: String,
158 #[serde(default)]
159 #[serde(skip_serializing_if = "HashMap::is_empty")]
160 pub parameters: HashMap<String, Scalar>,
161 #[serde(default)]
162 #[serde(skip_serializing_if = "Vec::is_empty")]
163 pub images: Vec<String>,
164 #[serde(default)]
165 pub mesh: ImageBoxProceduralMesh,
166 #[serde(default)]
167 pub vertex_mapping: CoordsMappingScaling,
168}
169
170impl ImageBoxProcedural {
171 pub fn new(id: impl ToString) -> Self {
172 Self {
173 id: id.to_string(),
174 parameters: Default::default(),
175 images: Default::default(),
176 mesh: Default::default(),
177 vertex_mapping: Default::default(),
178 }
179 }
180
181 pub fn param(mut self, id: impl ToString, value: Scalar) -> Self {
182 self.parameters.insert(id.to_string(), value);
183 self
184 }
185
186 pub fn image(mut self, id: impl ToString) -> Self {
187 self.images.push(id.to_string());
188 self
189 }
190
191 pub fn mesh(mut self, mesh: ImageBoxProceduralMesh) -> Self {
192 self.mesh = mesh;
193 self
194 }
195
196 pub fn triangle(mut self, vertices: [ImageBoxProceduralVertex; 3]) -> Self {
197 if let ImageBoxProceduralMesh::Owned(mesh) = &mut self.mesh {
198 let count = mesh.vertices.len() as u32;
199 mesh.vertices.extend(vertices);
200 mesh.triangles.push([count, count + 1, count + 2]);
201 }
202 self
203 }
204
205 pub fn quad(mut self, vertices: [ImageBoxProceduralVertex; 4]) -> Self {
206 if let ImageBoxProceduralMesh::Owned(mesh) = &mut self.mesh {
207 let count = mesh.vertices.len() as u32;
208 mesh.vertices.extend(vertices);
209 mesh.triangles.push([count, count + 1, count + 2]);
210 mesh.triangles.push([count + 2, count + 3, count]);
211 }
212 self
213 }
214
215 pub fn extend(
216 mut self,
217 vertices: impl IntoIterator<Item = ImageBoxProceduralVertex>,
218 triangles: impl IntoIterator<Item = [u32; 3]>,
219 ) -> Self {
220 if let ImageBoxProceduralMesh::Owned(mesh) = &mut self.mesh {
221 let count = mesh.vertices.len() as u32;
222 mesh.vertices.extend(vertices);
223 mesh.triangles.extend(
224 triangles
225 .into_iter()
226 .map(|[a, b, c]| [a + count, b + count, c + count]),
227 );
228 }
229 self
230 }
231
232 pub fn vertex_mapping(mut self, value: CoordsMappingScaling) -> Self {
233 self.vertex_mapping = value;
234 self
235 }
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub enum ImageBoxMaterial {
240 Color(ImageBoxColor),
241 Image(ImageBoxImage),
242 Procedural(ImageBoxProcedural),
243}
244
245impl Default for ImageBoxMaterial {
246 fn default() -> Self {
247 Self::Color(Default::default())
248 }
249}
250
251#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
252pub enum ImageBoxSizeValue {
253 #[default]
254 Fill,
255 Exact(Scalar),
256}
257
258#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
259pub struct ImageBoxAspectRatio {
260 #[serde(default)]
261 pub horizontal_alignment: Scalar,
262 #[serde(default)]
263 pub vertical_alignment: Scalar,
264 #[serde(default)]
265 pub outside: bool,
266}
267
268#[derive(Debug, Default, Clone, Serialize, Deserialize)]
269pub struct ImageBox {
270 #[serde(default)]
271 pub id: WidgetId,
272 #[serde(default)]
273 pub width: ImageBoxSizeValue,
274 #[serde(default)]
275 pub height: ImageBoxSizeValue,
276 #[serde(default)]
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub content_keep_aspect_ratio: Option<ImageBoxAspectRatio>,
279 #[serde(default)]
280 pub material: ImageBoxMaterial,
281 #[serde(default)]
282 pub transform: Transform,
283}
284
285impl WidgetUnitData for ImageBox {
286 fn id(&self) -> &WidgetId {
287 &self.id
288 }
289}
290
291impl TryFrom<ImageBoxNode> for ImageBox {
292 type Error = ();
293
294 fn try_from(node: ImageBoxNode) -> Result<Self, Self::Error> {
295 let ImageBoxNode {
296 id,
297 width,
298 height,
299 content_keep_aspect_ratio,
300 material,
301 transform,
302 ..
303 } = node;
304 Ok(Self {
305 id,
306 width,
307 height,
308 content_keep_aspect_ratio,
309 material,
310 transform,
311 })
312 }
313}
314
315#[derive(Debug, Default, Clone)]
316pub struct ImageBoxNode {
317 pub id: WidgetId,
318 pub props: Props,
319 pub width: ImageBoxSizeValue,
320 pub height: ImageBoxSizeValue,
321 pub content_keep_aspect_ratio: Option<ImageBoxAspectRatio>,
322 pub material: ImageBoxMaterial,
323 pub transform: Transform,
324}
325
326impl ImageBoxNode {
327 pub fn remap_props<F>(&mut self, mut f: F)
328 where
329 F: FnMut(Props) -> Props,
330 {
331 let props = std::mem::take(&mut self.props);
332 self.props = (f)(props);
333 }
334}
335
336impl From<ImageBoxNode> for WidgetNode {
337 fn from(data: ImageBoxNode) -> Self {
338 Self::Unit(data.into())
339 }
340}
341
342#[derive(Debug, Default, Clone, Serialize, Deserialize)]
343pub(crate) struct ImageBoxNodePrefab {
344 #[serde(default)]
345 pub id: WidgetId,
346 #[serde(default)]
347 pub props: PrefabValue,
348 #[serde(default)]
349 pub width: ImageBoxSizeValue,
350 #[serde(default)]
351 pub height: ImageBoxSizeValue,
352 #[serde(default)]
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub content_keep_aspect_ratio: Option<ImageBoxAspectRatio>,
355 #[serde(default)]
356 pub material: ImageBoxMaterial,
357 #[serde(default)]
358 pub transform: Transform,
359}