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