raui_core/widget/unit/
image.rs

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}