raui_core/widget/unit/
image.rs

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    /// fn(widget local space rect, procedural image parameters map) -> mesh data
126    #[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}