1use crate::{
2 PrefabValue, PropsData, Scalar,
3 props::Props,
4 widget::{
5 WidgetId,
6 node::{WidgetNode, WidgetNodePrefab},
7 unit::{WidgetUnit, WidgetUnitData},
8 utils::{Rect, Transform, Vec2},
9 },
10};
11use serde::{Deserialize, Serialize};
12use std::convert::TryFrom;
13
14#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct ContentBoxItemPreserveInBounds {
16 #[serde(default)]
17 pub width: bool,
18 #[serde(default)]
19 pub height: bool,
20}
21
22impl From<bool> for ContentBoxItemPreserveInBounds {
23 fn from(value: bool) -> Self {
24 Self {
25 width: value,
26 height: value,
27 }
28 }
29}
30
31#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
32pub struct ContentBoxItemCutInBounds {
33 #[serde(default)]
34 pub left: bool,
35 #[serde(default)]
36 pub right: bool,
37 #[serde(default)]
38 pub top: bool,
39 #[serde(default)]
40 pub bottom: bool,
41}
42
43impl From<bool> for ContentBoxItemCutInBounds {
44 fn from(value: bool) -> Self {
45 Self {
46 left: value,
47 right: value,
48 top: value,
49 bottom: value,
50 }
51 }
52}
53
54#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct ContentBoxItemKeepInBounds {
56 #[serde(default)]
57 pub preserve: ContentBoxItemPreserveInBounds,
58 #[serde(default)]
59 pub cut: ContentBoxItemCutInBounds,
60}
61
62impl From<bool> for ContentBoxItemKeepInBounds {
63 fn from(value: bool) -> Self {
64 Self {
65 preserve: value.into(),
66 cut: value.into(),
67 }
68 }
69}
70
71#[derive(PropsData, Debug, Clone, Serialize, Deserialize)]
75#[props_data(crate::props::PropsData)]
76#[prefab(crate::Prefab)]
77pub struct ContentBoxItemLayout {
78 #[serde(default = "ContentBoxItemLayout::default_anchors")]
79 pub anchors: Rect,
80 #[serde(default)]
82 pub margin: Rect,
83 #[serde(default)]
85 pub align: Vec2,
86 #[serde(default)]
88 pub offset: Vec2,
89 #[serde(default)]
91 pub depth: Scalar,
92 #[serde(default)]
94 pub keep_in_bounds: ContentBoxItemKeepInBounds,
95}
96
97impl ContentBoxItemLayout {
98 fn default_anchors() -> Rect {
99 Rect {
100 left: 0.0,
101 right: 1.0,
102 top: 0.0,
103 bottom: 1.0,
104 }
105 }
106}
107
108impl Default for ContentBoxItemLayout {
109 fn default() -> Self {
110 Self {
111 anchors: Self::default_anchors(),
112 margin: Default::default(),
113 align: Default::default(),
114 offset: Default::default(),
115 depth: 0.0,
116 keep_in_bounds: Default::default(),
117 }
118 }
119}
120
121#[derive(Debug, Default, Clone, Serialize, Deserialize)]
122pub struct ContentBoxItem {
123 #[serde(default)]
124 pub slot: WidgetUnit,
125 #[serde(default)]
126 pub layout: ContentBoxItemLayout,
127}
128
129impl TryFrom<ContentBoxItemNode> for ContentBoxItem {
130 type Error = ();
131
132 fn try_from(node: ContentBoxItemNode) -> Result<Self, Self::Error> {
133 let ContentBoxItemNode { slot, layout } = node;
134 Ok(Self {
135 slot: WidgetUnit::try_from(slot)?,
136 layout,
137 })
138 }
139}
140
141#[derive(Debug, Default, Clone)]
142pub struct ContentBoxItemNode {
143 pub slot: WidgetNode,
144 pub layout: ContentBoxItemLayout,
145}
146
147#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
148pub struct ContentBoxContentReposition {
149 #[serde(default)]
150 pub offset: Vec2,
151 #[serde(default = "ContentBoxContentReposition::default_scale")]
152 pub scale: Vec2,
153}
154
155impl Default for ContentBoxContentReposition {
156 fn default() -> Self {
157 Self {
158 offset: Default::default(),
159 scale: Self::default_scale(),
160 }
161 }
162}
163
164impl ContentBoxContentReposition {
165 fn default_scale() -> Vec2 {
166 1.0.into()
167 }
168}
169
170#[derive(Debug, Default, Clone, Serialize, Deserialize)]
171pub struct ContentBox {
172 #[serde(default)]
173 pub id: WidgetId,
174 #[serde(default)]
175 #[serde(skip_serializing_if = "Vec::is_empty")]
176 pub items: Vec<ContentBoxItem>,
177 #[serde(default)]
178 pub clipping: bool,
179 #[serde(default)]
180 pub content_reposition: ContentBoxContentReposition,
181 #[serde(default)]
182 pub transform: Transform,
183}
184
185impl WidgetUnitData for ContentBox {
186 fn id(&self) -> &WidgetId {
187 &self.id
188 }
189
190 fn get_children(&self) -> Vec<&WidgetUnit> {
191 self.items.iter().map(|item| &item.slot).collect()
192 }
193}
194
195impl TryFrom<ContentBoxNode> for ContentBox {
196 type Error = ();
197
198 fn try_from(node: ContentBoxNode) -> Result<Self, Self::Error> {
199 let ContentBoxNode {
200 id,
201 items,
202 clipping,
203 content_reposition,
204 transform,
205 ..
206 } = node;
207 let items = items
208 .into_iter()
209 .map(ContentBoxItem::try_from)
210 .collect::<Result<_, _>>()?;
211 Ok(Self {
212 id,
213 items,
214 clipping,
215 content_reposition,
216 transform,
217 })
218 }
219}
220
221#[derive(Debug, Default, Clone)]
222pub struct ContentBoxNode {
223 pub id: WidgetId,
224 pub props: Props,
225 pub items: Vec<ContentBoxItemNode>,
226 pub clipping: bool,
227 pub content_reposition: ContentBoxContentReposition,
228 pub transform: Transform,
229}
230
231impl ContentBoxNode {
232 pub fn remap_props<F>(&mut self, mut f: F)
233 where
234 F: FnMut(Props) -> Props,
235 {
236 let props = std::mem::take(&mut self.props);
237 self.props = (f)(props);
238 }
239}
240
241impl From<ContentBoxNode> for WidgetNode {
242 fn from(data: ContentBoxNode) -> Self {
243 Self::Unit(data.into())
244 }
245}
246
247#[derive(Debug, Default, Clone, Serialize, Deserialize)]
248pub(crate) struct ContentBoxNodePrefab {
249 #[serde(default)]
250 pub id: WidgetId,
251 #[serde(default)]
252 pub props: PrefabValue,
253 #[serde(default)]
254 #[serde(skip_serializing_if = "Vec::is_empty")]
255 pub items: Vec<ContentBoxItemNodePrefab>,
256 #[serde(default)]
257 pub clipping: bool,
258 #[serde(default)]
259 pub content_reposition: ContentBoxContentReposition,
260 #[serde(default)]
261 pub transform: Transform,
262}
263
264#[derive(Debug, Default, Clone, Serialize, Deserialize)]
265pub(crate) struct ContentBoxItemNodePrefab {
266 #[serde(default)]
267 pub slot: WidgetNodePrefab,
268 #[serde(default)]
269 pub layout: ContentBoxItemLayout,
270}