1use crate::{
2 layout::CoordsMappingScaling,
3 props::Props,
4 widget::{
5 node::WidgetNode,
6 unit::WidgetUnitData,
7 utils::{Color, Rect, Transform, Vec2},
8 WidgetId,
9 },
10 PrefabValue, Scalar,
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 Generator(
128 Arc<
129 dyn Fn(Rect, &HashMap<String, Scalar>) -> ImageBoxProceduralMeshData
130 + 'static
131 + Send
132 + Sync,
133 >,
134 ),
135}
136
137impl std::fmt::Debug for ImageBoxProceduralMesh {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 match self {
140 Self::Owned(data) => write!(f, "Owned({:?})", data),
141 Self::Shared(data) => write!(f, "Shared({:?})", data),
142 Self::Generator(_) => write!(f, "Generator(...)"),
143 }
144 }
145}
146
147impl Default for ImageBoxProceduralMesh {
148 fn default() -> Self {
149 Self::Owned(Default::default())
150 }
151}
152
153#[derive(Debug, Default, Clone, Serialize, Deserialize)]
154pub struct ImageBoxProcedural {
155 #[serde(default)]
156 pub id: String,
157 #[serde(default)]
158 #[serde(skip_serializing_if = "HashMap::is_empty")]
159 pub parameters: HashMap<String, Scalar>,
160 #[serde(default)]
161 #[serde(skip_serializing_if = "Vec::is_empty")]
162 pub images: Vec<String>,
163 #[serde(default)]
164 pub mesh: ImageBoxProceduralMesh,
165 #[serde(default)]
166 pub vertex_mapping: CoordsMappingScaling,
167}
168
169impl ImageBoxProcedural {
170 pub fn new(id: impl ToString) -> Self {
171 Self {
172 id: id.to_string(),
173 parameters: Default::default(),
174 images: Default::default(),
175 mesh: Default::default(),
176 vertex_mapping: Default::default(),
177 }
178 }
179
180 pub fn param(mut self, id: impl ToString, value: Scalar) -> Self {
181 self.parameters.insert(id.to_string(), value);
182 self
183 }
184
185 pub fn image(mut self, id: impl ToString) -> Self {
186 self.images.push(id.to_string());
187 self
188 }
189
190 pub fn mesh(mut self, mesh: ImageBoxProceduralMesh) -> Self {
191 self.mesh = mesh;
192 self
193 }
194
195 pub fn triangle(mut self, vertices: [ImageBoxProceduralVertex; 3]) -> Self {
196 if let ImageBoxProceduralMesh::Owned(mesh) = &mut self.mesh {
197 let count = mesh.vertices.len() as u32;
198 mesh.vertices.extend(vertices);
199 mesh.triangles.push([count, count + 1, count + 2]);
200 }
201 self
202 }
203
204 pub fn quad(mut self, vertices: [ImageBoxProceduralVertex; 4]) -> Self {
205 if let ImageBoxProceduralMesh::Owned(mesh) = &mut self.mesh {
206 let count = mesh.vertices.len() as u32;
207 mesh.vertices.extend(vertices);
208 mesh.triangles.push([count, count + 1, count + 2]);
209 mesh.triangles.push([count + 2, count + 3, count]);
210 }
211 self
212 }
213
214 pub fn extend(
215 mut self,
216 vertices: impl IntoIterator<Item = ImageBoxProceduralVertex>,
217 triangles: impl IntoIterator<Item = [u32; 3]>,
218 ) -> Self {
219 if let ImageBoxProceduralMesh::Owned(mesh) = &mut self.mesh {
220 let count = mesh.vertices.len() as u32;
221 mesh.vertices.extend(vertices);
222 mesh.triangles.extend(
223 triangles
224 .into_iter()
225 .map(|[a, b, c]| [a + count, b + count, c + count]),
226 );
227 }
228 self
229 }
230
231 pub fn vertex_mapping(mut self, value: CoordsMappingScaling) -> Self {
232 self.vertex_mapping = value;
233 self
234 }
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub enum ImageBoxMaterial {
239 Color(ImageBoxColor),
240 Image(ImageBoxImage),
241 Procedural(ImageBoxProcedural),
242}
243
244impl Default for ImageBoxMaterial {
245 fn default() -> Self {
246 Self::Color(Default::default())
247 }
248}
249
250#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
251pub enum ImageBoxSizeValue {
252 #[default]
253 Fill,
254 Exact(Scalar),
255}
256
257#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
258pub struct ImageBoxAspectRatio {
259 #[serde(default)]
260 pub horizontal_alignment: Scalar,
261 #[serde(default)]
262 pub vertical_alignment: Scalar,
263 #[serde(default)]
264 pub outside: bool,
265}
266
267#[derive(Debug, Default, Clone, Serialize, Deserialize)]
268pub struct ImageBox {
269 #[serde(default)]
270 pub id: WidgetId,
271 #[serde(default)]
272 pub width: ImageBoxSizeValue,
273 #[serde(default)]
274 pub height: ImageBoxSizeValue,
275 #[serde(default)]
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub content_keep_aspect_ratio: Option<ImageBoxAspectRatio>,
278 #[serde(default)]
279 pub material: ImageBoxMaterial,
280 #[serde(default)]
281 pub transform: Transform,
282}
283
284impl WidgetUnitData for ImageBox {
285 fn id(&self) -> &WidgetId {
286 &self.id
287 }
288}
289
290impl TryFrom<ImageBoxNode> for ImageBox {
291 type Error = ();
292
293 fn try_from(node: ImageBoxNode) -> Result<Self, Self::Error> {
294 let ImageBoxNode {
295 id,
296 width,
297 height,
298 content_keep_aspect_ratio,
299 material,
300 transform,
301 ..
302 } = node;
303 Ok(Self {
304 id,
305 width,
306 height,
307 content_keep_aspect_ratio,
308 material,
309 transform,
310 })
311 }
312}
313
314#[derive(Debug, Default, Clone)]
315pub struct ImageBoxNode {
316 pub id: WidgetId,
317 pub props: Props,
318 pub width: ImageBoxSizeValue,
319 pub height: ImageBoxSizeValue,
320 pub content_keep_aspect_ratio: Option<ImageBoxAspectRatio>,
321 pub material: ImageBoxMaterial,
322 pub transform: Transform,
323}
324
325impl ImageBoxNode {
326 pub fn remap_props<F>(&mut self, mut f: F)
327 where
328 F: FnMut(Props) -> Props,
329 {
330 let props = std::mem::take(&mut self.props);
331 self.props = (f)(props);
332 }
333}
334
335impl From<ImageBoxNode> for WidgetNode {
336 fn from(data: ImageBoxNode) -> Self {
337 Self::Unit(data.into())
338 }
339}
340
341#[derive(Debug, Default, Clone, Serialize, Deserialize)]
342pub(crate) struct ImageBoxNodePrefab {
343 #[serde(default)]
344 pub id: WidgetId,
345 #[serde(default)]
346 pub props: PrefabValue,
347 #[serde(default)]
348 pub width: ImageBoxSizeValue,
349 #[serde(default)]
350 pub height: ImageBoxSizeValue,
351 #[serde(default)]
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub content_keep_aspect_ratio: Option<ImageBoxAspectRatio>,
354 #[serde(default)]
355 pub material: ImageBoxMaterial,
356 #[serde(default)]
357 pub transform: Transform,
358}