1pub mod default_layout_engine;
4
5use crate::{
6 Scalar,
7 widget::{
8 WidgetId,
9 unit::WidgetUnit,
10 utils::{Rect, Vec2},
11 },
12};
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15
16pub trait LayoutEngine<E> {
17 fn layout(&mut self, mapping: &CoordsMapping, tree: &WidgetUnit) -> Result<Layout, E>;
18}
19
20struct LayoutSortedItems<'a>(Vec<(&'a WidgetId, &'a LayoutItem)>);
21
22impl<'a> LayoutSortedItems<'a> {
23 fn new(items: &'a HashMap<WidgetId, LayoutItem>) -> Self {
24 let mut items = items.iter().collect::<Vec<_>>();
25 items.sort_by(|a, b| a.0.path().cmp(b.0.path()));
26 Self(items)
27 }
28}
29
30impl std::fmt::Debug for LayoutSortedItems<'_> {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.debug_map()
33 .entries(self.0.iter().map(|&(k, v)| (k, v)))
34 .finish()
35 }
36}
37
38#[derive(Default, Clone, Serialize, Deserialize)]
39pub struct Layout {
40 pub ui_space: Rect,
41 pub items: HashMap<WidgetId, LayoutItem>,
42}
43
44impl std::fmt::Debug for Layout {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("Layout")
47 .field("ui_space", &self.ui_space)
48 .field("items", &LayoutSortedItems::new(&self.items))
49 .finish()
50 }
51}
52
53impl Layout {
54 pub fn find(&self, mut path: &str) -> Option<&LayoutItem> {
55 loop {
56 if let Some(item) = self
57 .items
58 .iter()
59 .find_map(|(k, v)| if k.path() == path { Some(v) } else { None })
60 {
61 return Some(item);
62 } else if let Some(index) = path.rfind('/') {
63 path = &path[0..index];
64 } else {
65 break;
66 }
67 }
68 None
69 }
70
71 pub fn find_or_ui_space(&self, path: &str) -> LayoutItem {
72 match self.find(path) {
73 Some(item) => item.to_owned(),
74 None => LayoutItem {
75 local_space: self.ui_space,
76 ui_space: self.ui_space,
77 parent: None,
78 },
79 }
80 }
81
82 pub fn virtual_to_real(&self, mapping: &CoordsMapping) -> Self {
83 Self {
84 ui_space: mapping.virtual_to_real_rect(self.ui_space, false),
85 items: self
86 .items
87 .iter()
88 .map(|(k, v)| (k.to_owned(), v.virtual_to_real(mapping)))
89 .collect::<HashMap<_, _>>(),
90 }
91 }
92
93 pub fn real_to_virtual(&self, mapping: &CoordsMapping) -> Self {
94 Self {
95 ui_space: mapping.real_to_virtual_rect(self.ui_space, false),
96 items: self
97 .items
98 .iter()
99 .map(|(k, v)| (k.to_owned(), v.real_to_virtual(mapping)))
100 .collect::<HashMap<_, _>>(),
101 }
102 }
103
104 pub fn rect_relative_to(&self, id: &WidgetId, to: &WidgetId) -> Option<Rect> {
105 let a = self.items.get(id)?;
106 let b = self.items.get(to)?;
107 let x = a.ui_space.left - b.ui_space.left;
108 let y = a.ui_space.top - b.ui_space.top;
109 Some(Rect {
110 left: x,
111 right: x + a.ui_space.width(),
112 top: y,
113 bottom: y + a.ui_space.height(),
114 })
115 }
116}
117
118#[derive(Debug, Default, Clone, Serialize, Deserialize)]
119pub struct LayoutNode {
120 pub id: WidgetId,
121 pub local_space: Rect,
122 pub children: Vec<LayoutNode>,
123}
124
125impl LayoutNode {
126 pub fn count(&self) -> usize {
127 1 + self.children.iter().map(Self::count).sum::<usize>()
128 }
129}
130
131#[derive(Debug, Default, Clone, Serialize, Deserialize)]
132pub struct LayoutItem {
133 pub local_space: Rect,
134 pub ui_space: Rect,
135 pub parent: Option<WidgetId>,
136}
137
138impl LayoutItem {
139 pub fn virtual_to_real(&self, mapping: &CoordsMapping) -> Self {
140 Self {
141 local_space: mapping.virtual_to_real_rect(self.local_space, true),
142 ui_space: mapping.virtual_to_real_rect(self.ui_space, false),
143 parent: self.parent.to_owned(),
144 }
145 }
146
147 pub fn real_to_virtual(&self, mapping: &CoordsMapping) -> Self {
148 Self {
149 local_space: mapping.real_to_virtual_rect(self.local_space, true),
150 ui_space: mapping.real_to_virtual_rect(self.ui_space, false),
151 parent: self.parent.to_owned(),
152 }
153 }
154}
155
156impl LayoutEngine<()> for () {
157 fn layout(&mut self, mapping: &CoordsMapping, _: &WidgetUnit) -> Result<Layout, ()> {
158 Ok(Layout {
159 ui_space: mapping.virtual_area(),
160 items: Default::default(),
161 })
162 }
163}
164
165#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
166pub enum CoordsMappingScaling {
167 #[default]
168 None,
169 Constant(Scalar),
170 Stretch(Vec2),
171 FitHorizontal(Scalar),
172 FitVertical(Scalar),
173 FitMinimum(Vec2),
174 FitMaximum(Vec2),
175 FitToView(Vec2, bool),
176}
177
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct CoordsMapping {
180 #[serde(default)]
181 scale: Vec2,
182 #[serde(default)]
183 offset: Vec2,
184 #[serde(default)]
185 real_area: Rect,
186 #[serde(default)]
187 virtual_area: Rect,
188}
189
190impl Default for CoordsMapping {
191 fn default() -> Self {
192 Self::new(Default::default())
193 }
194}
195
196impl CoordsMapping {
197 pub fn new(real_area: Rect) -> Self {
198 Self::new_scaling(real_area, CoordsMappingScaling::None)
199 }
200
201 pub fn new_scaling(real_area: Rect, scaling: CoordsMappingScaling) -> Self {
202 match scaling {
203 CoordsMappingScaling::None => Self {
204 scale: 1.0.into(),
205 offset: Vec2::default(),
206 real_area,
207 virtual_area: Rect {
208 left: 0.0,
209 right: real_area.width(),
210 top: 0.0,
211 bottom: real_area.height(),
212 },
213 },
214 CoordsMappingScaling::Constant(value) => {
215 let value = if value > 0.0 { value } else { 1.0 };
216 Self {
217 scale: value.into(),
218 offset: Vec2::default(),
219 real_area,
220 virtual_area: Rect {
221 left: 0.0,
222 right: real_area.width() / value,
223 top: 0.0,
224 bottom: real_area.height() / value,
225 },
226 }
227 }
228 CoordsMappingScaling::Stretch(size) => {
229 let vw = size.x;
230 let vh = size.y;
231 let rw = real_area.width();
232 let rh = real_area.height();
233 let scale_x = rw / vw;
234 let scale_y = rh / vh;
235 let w = vw * scale_x;
236 let h = vh * scale_y;
237 Self {
238 scale: Vec2 {
239 x: scale_x,
240 y: scale_y,
241 },
242 offset: Vec2 {
243 x: (rw - w) * 0.5,
244 y: (rh - h) * 0.5,
245 },
246 real_area,
247 virtual_area: Rect {
248 left: 0.0,
249 right: vw,
250 top: 0.0,
251 bottom: vh,
252 },
253 }
254 }
255 CoordsMappingScaling::FitHorizontal(vw) => {
256 let rw = real_area.width();
257 let rh = real_area.height();
258 let scale = rw / vw;
259 let vh = rh / scale;
260 Self {
261 scale: scale.into(),
262 offset: Vec2::default(),
263 real_area,
264 virtual_area: Rect {
265 left: 0.0,
266 right: vw,
267 top: 0.0,
268 bottom: vh,
269 },
270 }
271 }
272 CoordsMappingScaling::FitVertical(vh) => {
273 let rw = real_area.width();
274 let rh = real_area.height();
275 let scale = rh / vh;
276 let vw = rw / scale;
277 Self {
278 scale: scale.into(),
279 offset: Vec2::default(),
280 real_area,
281 virtual_area: Rect {
282 left: 0.0,
283 right: vw,
284 top: 0.0,
285 bottom: vh,
286 },
287 }
288 }
289 CoordsMappingScaling::FitMinimum(size) => {
290 if size.x < size.y {
291 Self::new_scaling(real_area, CoordsMappingScaling::FitHorizontal(size.x))
292 } else {
293 Self::new_scaling(real_area, CoordsMappingScaling::FitVertical(size.y))
294 }
295 }
296 CoordsMappingScaling::FitMaximum(size) => {
297 if size.x > size.y {
298 Self::new_scaling(real_area, CoordsMappingScaling::FitHorizontal(size.x))
299 } else {
300 Self::new_scaling(real_area, CoordsMappingScaling::FitVertical(size.y))
301 }
302 }
303 CoordsMappingScaling::FitToView(size, keep_aspect_ratio) => {
304 let rw = real_area.width();
305 let rh = real_area.height();
306 let av = size.x / size.y;
307 let ar = rw / rh;
308 let (scale, vw, vh) = if keep_aspect_ratio {
309 let vw = size.x;
310 let vh = size.y;
311 let scale = if ar >= av { rh / vh } else { rw / vw };
312 (scale, vw, vh)
313 } else if ar >= av {
314 (rh / size.y, size.x * rw / rh, size.y)
315 } else {
316 (rw / size.x, size.x, size.y * rh / rw)
317 };
318 let w = vw * scale;
319 let h = vh * scale;
320 Self {
321 scale: scale.into(),
322 offset: Vec2 {
323 x: (rw - w) * 0.5,
324 y: (rh - h) * 0.5,
325 },
326 real_area,
327 virtual_area: Rect {
328 left: 0.0,
329 right: vw,
330 top: 0.0,
331 bottom: vh,
332 },
333 }
334 }
335 }
336 }
337
338 #[inline]
339 pub fn scale(&self) -> Vec2 {
340 self.scale
341 }
342
343 #[inline]
344 pub fn scalar_scale(&self, max: bool) -> Scalar {
345 if max {
346 self.scale.x.max(self.scale.y)
347 } else {
348 self.scale.x.min(self.scale.y)
349 }
350 }
351
352 #[inline]
353 pub fn offset(&self) -> Vec2 {
354 self.offset
355 }
356
357 #[inline]
358 pub fn virtual_area(&self) -> Rect {
359 self.virtual_area
360 }
361
362 #[inline]
363 pub fn virtual_to_real_vec2(&self, coord: Vec2, local_space: bool) -> Vec2 {
364 if local_space {
365 Vec2 {
366 x: coord.x * self.scale.x,
367 y: coord.y * self.scale.y,
368 }
369 } else {
370 Vec2 {
371 x: self.offset.x + (coord.x * self.scale.x),
372 y: self.offset.y + (coord.y * self.scale.y),
373 }
374 }
375 }
376
377 #[inline]
378 pub fn real_to_virtual_vec2(&self, coord: Vec2, local_space: bool) -> Vec2 {
379 if local_space {
380 Vec2 {
381 x: coord.x / self.scale.x,
382 y: coord.y / self.scale.y,
383 }
384 } else {
385 Vec2 {
386 x: (coord.x - self.offset.x) / self.scale.x,
387 y: (coord.y - self.offset.y) / self.scale.y,
388 }
389 }
390 }
391
392 #[inline]
393 pub fn virtual_to_real_rect(&self, area: Rect, local_space: bool) -> Rect {
394 if local_space {
395 Rect {
396 left: area.left * self.scale.x,
397 right: area.right * self.scale.x,
398 top: area.top * self.scale.y,
399 bottom: area.bottom * self.scale.y,
400 }
401 } else {
402 Rect {
403 left: self.offset.x + (area.left * self.scale.x),
404 right: self.offset.x + (area.right * self.scale.x),
405 top: self.offset.y + (area.top * self.scale.y),
406 bottom: self.offset.y + (area.bottom * self.scale.y),
407 }
408 }
409 }
410
411 #[inline]
412 pub fn real_to_virtual_rect(&self, area: Rect, local_space: bool) -> Rect {
413 if local_space {
414 Rect {
415 left: area.left / self.scale.x,
416 right: area.right / self.scale.x,
417 top: area.top / self.scale.y,
418 bottom: area.bottom / self.scale.y,
419 }
420 } else {
421 Rect {
422 left: (area.left - self.offset.x) / self.scale.x,
423 right: (area.right - self.offset.x) / self.scale.x,
424 top: (area.top - self.offset.y) / self.scale.y,
425 bottom: (area.bottom - self.offset.y) / self.scale.y,
426 }
427 }
428 }
429}