Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/elements/stack/offset_positioning_test.rs
StratoSDK / crates / strato-ui-core / src / elements / stack / offset_positioning_test.rs
1use super::*;
2use lazy_static::lazy_static;
3 
4lazy_static! {
5 static ref OFFSET: Vector2F = vec2f(5., 10.);
6 static ref WINDOW_SIZE: Vector2F = vec2f(100., 100.);
7 
8 static ref SMALL_CHILD_SIZE: Vector2F = vec2f(10., 10.);
9 // Use a size for the child that is sufficiently large to test various bounding behavior.
10 static ref CHILD_SIZE: Vector2F = vec2f(75., 75.);
11 static ref DEFAULT_SIZE_CONSTRAINT: SizeConstraint = SizeConstraint {
12 min: vec2f(0., 0.),
13 max: vec2f(50., 50.)
14 };
15 static ref POSITIONED_ELEMENT_RECT: RectF = RectF::new(vec2f(50., 50.), vec2f(15., 15.));
16 static ref SMALL_PARENT_RECT: RectF = RectF::new(vec2f(50., 50.), vec2f(25., 25.));
17 static ref PARENT_RECT: RectF = RectF::new(vec2f(25., 25.), vec2f(50., 50.));
18 static ref PARENT_ANCHORS: Vec<ParentAnchor> = vec![
19 ParentAnchor::TopLeft,
20 ParentAnchor::TopMiddle,
21 ParentAnchor::TopRight,
22 ParentAnchor::MiddleLeft,
23 ParentAnchor::MiddleRight,
24 ParentAnchor::Center,
25 ParentAnchor::BottomLeft,
26 ParentAnchor::BottomMiddle,
27 ParentAnchor::BottomRight,
28 ];
29 static ref POSITIONED_ELEMENT_ANCHORS: Vec<PositionedElementAnchor> = vec![
30 PositionedElementAnchor::TopLeft,
31 PositionedElementAnchor::TopMiddle,
32 PositionedElementAnchor::TopRight,
33 PositionedElementAnchor::MiddleLeft,
34 PositionedElementAnchor::MiddleRight,
35 PositionedElementAnchor::Center,
36 PositionedElementAnchor::BottomLeft,
37 PositionedElementAnchor::BottomMiddle,
38 PositionedElementAnchor::BottomRight,
39 ];
40 static ref CHILD_ANCHORS: Vec<ChildAnchor> = vec![
41 ChildAnchor::TopLeft,
42 ChildAnchor::TopMiddle,
43 ChildAnchor::TopRight,
44 ChildAnchor::MiddleLeft,
45 ChildAnchor::MiddleRight,
46 ChildAnchor::Center,
47 ChildAnchor::BottomLeft,
48 ChildAnchor::BottomMiddle,
49 ChildAnchor::BottomRight,
50 ];
51}
52const SAVE_POSITION_ID: &str = "SAVE_POSITION_ID";
53 
54/// Returns the coordinates of the parent's anchor point used to position the child.
55fn parent_anchor_point(anchor: ParentAnchor) -> (f32, f32) {
56 (
57 match anchor {
58 ParentAnchor::TopLeft | ParentAnchor::MiddleLeft | ParentAnchor::BottomLeft => {
59 PARENT_RECT.min_x()
60 }
61 ParentAnchor::TopMiddle | ParentAnchor::Center | ParentAnchor::BottomMiddle => {
62 PARENT_RECT.center().x()
63 }
64 ParentAnchor::TopRight | ParentAnchor::MiddleRight | ParentAnchor::BottomRight => {
65 PARENT_RECT.max_x()
66 }
67 } + OFFSET.x(),
68 match anchor {
69 ParentAnchor::TopLeft | ParentAnchor::TopMiddle | ParentAnchor::TopRight => {
70 PARENT_RECT.min_y()
71 }
72 ParentAnchor::MiddleLeft | ParentAnchor::MiddleRight | ParentAnchor::Center => {
73 PARENT_RECT.center().y()
74 }
75 ParentAnchor::BottomLeft | ParentAnchor::BottomMiddle | ParentAnchor::BottomRight => {
76 PARENT_RECT.max_y()
77 }
78 } + OFFSET.y(),
79 )
80}
81 
82/// Returns the coordinates of the `SavePositioned` element's anchor point used to position the
83/// child.
84fn positioned_element_anchor_point(anchor: PositionedElementAnchor) -> (f32, f32) {
85 (
86 match anchor {
87 PositionedElementAnchor::TopLeft
88 | PositionedElementAnchor::MiddleLeft
89 | PositionedElementAnchor::BottomLeft => POSITIONED_ELEMENT_RECT.min_x(),
90 PositionedElementAnchor::TopMiddle
91 | PositionedElementAnchor::Center
92 | PositionedElementAnchor::BottomMiddle => POSITIONED_ELEMENT_RECT.center().x(),
93 PositionedElementAnchor::TopRight
94 | PositionedElementAnchor::MiddleRight
95 | PositionedElementAnchor::BottomRight => POSITIONED_ELEMENT_RECT.max_x(),
96 } + OFFSET.x(),
97 match anchor {
98 PositionedElementAnchor::TopLeft
99 | PositionedElementAnchor::TopMiddle
100 | PositionedElementAnchor::TopRight => POSITIONED_ELEMENT_RECT.min_y(),
101 PositionedElementAnchor::MiddleLeft
102 | PositionedElementAnchor::MiddleRight
103 | PositionedElementAnchor::Center => POSITIONED_ELEMENT_RECT.center().y(),
104 PositionedElementAnchor::BottomLeft
105 | PositionedElementAnchor::BottomMiddle
106 | PositionedElementAnchor::BottomRight => POSITIONED_ELEMENT_RECT.max_y(),
107 } + OFFSET.y(),
108 )
109}
110 
111/// Returns the absolute (x, y) position of a child element rendered against a given parent
112/// using `PositionedElementOffsetBounds::ParentByPosition` bounds.
113fn get_absolute_x_y_position_for_child_element(
114 child_size: Vector2F,
115 parent_rect: RectF,
116 positioned_element_anchor: PositionedElementAnchor,
117 child_anchor: ChildAnchor,
118) -> Vector2F {
119 let offset_positioning = OffsetPositioning::offset_from_save_position_element(
120 SAVE_POSITION_ID,
121 *OFFSET,
122 PositionedElementOffsetBounds::ParentByPosition,
123 positioned_element_anchor,
124 child_anchor,
125 );
126 
127 let mut position_cache = PositionCache::new();
128 position_cache.start();
129 position_cache
130 .cache_position_indefinitely(SAVE_POSITION_ID.to_owned(), *POSITIONED_ELEMENT_RECT);
131 position_cache.end();
132 
133 let child_position_x = offset_positioning
134 .x_axis
135 .compute_child_position(child_size, parent_rect, *WINDOW_SIZE, &position_cache)
136 .expect("Failed to compute child position x.");
137 
138 let child_position_y = offset_positioning
139 .y_axis
140 .compute_child_position(child_size, parent_rect, *WINDOW_SIZE, &position_cache)
141 .expect("Failed to compute child position y.");
142 
143 vec2f(child_position_x, child_position_y)
144}
145 
146#[test]
147fn test_offset_from_parent_unbounded() {
148 for parent_anchor in PARENT_ANCHORS.iter() {
149 for child_anchor in CHILD_ANCHORS.iter() {
150 let offset_positioning = OffsetPositioning::offset_from_parent(
151 *OFFSET,
152 ParentOffsetBounds::Unbounded,
153 *parent_anchor,
154 *child_anchor,
155 );
156 
157 let position_cache = PositionCache::new();
158 
159 let size_constraint = offset_positioning.size_constraint(
160 PARENT_RECT.size(),
161 *WINDOW_SIZE,
162 *DEFAULT_SIZE_CONSTRAINT,
163 &position_cache,
164 );
165 // The size constraint should be unchanged since there is no bounding behavior.
166 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
167 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
168 
169 let (anchor_x, anchor_y) = parent_anchor_point(*parent_anchor);
170 
171 // Compute the expected x-axis position of the child relative to the parent's
172 // anchor point.
173 let expected_child_x = anchor_x
174 - match child_anchor {
175 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
176 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
177 CHILD_SIZE.x() / 2.
178 }
179 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
180 CHILD_SIZE.x()
181 }
182 };
183 let child_position_x = offset_positioning.x_axis.compute_child_position(
184 *CHILD_SIZE,
185 *PARENT_RECT,
186 *WINDOW_SIZE,
187 &position_cache,
188 );
189 assert!(child_position_x.is_ok());
190 assert_eq!(child_position_x.unwrap(), expected_child_x);
191 
192 // Compute the expected y-axis position of the child relative to the parent's
193 // anchor point.
194 let expected_child_y = anchor_y
195 - match child_anchor {
196 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
197 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
198 CHILD_SIZE.y() / 2.
199 }
200 ChildAnchor::BottomLeft
201 | ChildAnchor::BottomMiddle
202 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
203 };
204 let child_position_y = offset_positioning.y_axis.compute_child_position(
205 *CHILD_SIZE,
206 *PARENT_RECT,
207 *WINDOW_SIZE,
208 &position_cache,
209 );
210 assert!(child_position_y.is_ok());
211 assert_eq!(child_position_y.unwrap(), expected_child_y);
212 }
213 }
214}
215 
216#[test]
217fn test_offset_from_parent_bound_to_parent_with_position() {
218 for parent_anchor in PARENT_ANCHORS.iter() {
219 for child_anchor in CHILD_ANCHORS.iter() {
220 let offset_positioning = OffsetPositioning::offset_from_parent(
221 *OFFSET,
222 ParentOffsetBounds::ParentByPosition,
223 *parent_anchor,
224 *child_anchor,
225 );
226 
227 let position_cache = PositionCache::new();
228 
229 let size_constraint = offset_positioning.size_constraint(
230 PARENT_RECT.size(),
231 *WINDOW_SIZE,
232 *DEFAULT_SIZE_CONSTRAINT,
233 &position_cache,
234 );
235 // The size constraint should be unchanged since the bounding behavior adjusts
236 // position.
237 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
238 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
239 
240 let (anchor_x, anchor_y) = parent_anchor_point(*parent_anchor);
241 
242 // Compute the expected x-axis position of the child relative to the parent's
243 // anchor point.
244 let mut expected_child_x = anchor_x
245 - match child_anchor {
246 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
247 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
248 CHILD_SIZE.x() / 2.
249 }
250 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
251 CHILD_SIZE.x()
252 }
253 };
254 expected_child_x = expected_child_x
255 .min(PARENT_RECT.max_x() - CHILD_SIZE.x())
256 .max(PARENT_RECT.min_x());
257 
258 let child_position_x = offset_positioning.x_axis.compute_child_position(
259 *CHILD_SIZE,
260 *PARENT_RECT,
261 *WINDOW_SIZE,
262 &position_cache,
263 );
264 assert!(child_position_x.is_ok());
265 assert_eq!(child_position_x.unwrap(), expected_child_x);
266 
267 // Compute the expected y-axis position of the child relative to the parent anchor
268 // point.
269 let mut expected_child_y = anchor_y
270 - match child_anchor {
271 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
272 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
273 CHILD_SIZE.y() / 2.
274 }
275 ChildAnchor::BottomLeft
276 | ChildAnchor::BottomMiddle
277 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
278 };
279 expected_child_y = expected_child_y
280 .min(PARENT_RECT.max_y() - CHILD_SIZE.y())
281 .max(PARENT_RECT.min_y());
282 
283 let child_position_y = offset_positioning.y_axis.compute_child_position(
284 *CHILD_SIZE,
285 *PARENT_RECT,
286 *WINDOW_SIZE,
287 &position_cache,
288 );
289 assert!(child_position_y.is_ok());
290 assert_eq!(child_position_y.unwrap(), expected_child_y);
291 }
292 }
293}
294 
295#[test]
296fn test_offset_from_positioned_element_bound_to_anchor() {
297 for ratio in 0..10 {
298 let normalized_ratio = ratio as f32 / 10.;
299 let x_axis = PositioningAxis::relative_to_stack_child(
300 SAVE_POSITION_ID,
301 PositionedElementOffsetBounds::AnchoredElement,
302 OffsetType::Percentage(normalized_ratio),
303 AnchorPair::new(XAxisAnchor::Left, XAxisAnchor::Left),
304 );
305 let y_axis = PositioningAxis::relative_to_stack_child(
306 SAVE_POSITION_ID,
307 PositionedElementOffsetBounds::Unbounded,
308 OffsetType::Pixel(0.),
309 AnchorPair::new(YAxisAnchor::Middle, YAxisAnchor::Middle),
310 );
311 
312 let offset_positioning = OffsetPositioning::from_axes(x_axis, y_axis);
313 
314 let mut position_cache = PositionCache::new();
315 position_cache.start();
316 position_cache
317 .cache_position_indefinitely(SAVE_POSITION_ID.to_owned(), *POSITIONED_ELEMENT_RECT);
318 position_cache.end();
319 
320 // Use a smaller child size to make sure it's not clipped by the anchored element.
321 let expected_child_x = POSITIONED_ELEMENT_RECT.origin_x()
322 + (POSITIONED_ELEMENT_RECT.width() - SMALL_CHILD_SIZE.x()) * normalized_ratio;
323 let expected_child_y = POSITIONED_ELEMENT_RECT.center().y() - SMALL_CHILD_SIZE.y() / 2.;
324 
325 let size_constraint = offset_positioning.size_constraint(
326 PARENT_RECT.size(),
327 *WINDOW_SIZE,
328 *DEFAULT_SIZE_CONSTRAINT,
329 &position_cache,
330 );
331 // The size constraint should be unchanged since there is no bounding behavior.
332 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
333 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
334 
335 let child_position_x = offset_positioning.x_axis.compute_child_position(
336 *SMALL_CHILD_SIZE,
337 *PARENT_RECT,
338 *WINDOW_SIZE,
339 &position_cache,
340 );
341 assert!(child_position_x.is_ok());
342 assert_eq!(child_position_x.unwrap(), expected_child_x);
343 
344 let child_position_y = offset_positioning.y_axis.compute_child_position(
345 *SMALL_CHILD_SIZE,
346 *PARENT_RECT,
347 *WINDOW_SIZE,
348 &position_cache,
349 );
350 
351 assert!(child_position_y.is_ok());
352 assert_eq!(child_position_y.unwrap(), expected_child_y);
353 }
354}
355 
356#[test]
357fn test_offset_from_parent_bound_to_window_with_position() {
358 for parent_anchor in PARENT_ANCHORS.iter() {
359 for child_anchor in CHILD_ANCHORS.iter() {
360 let offset_positioning = OffsetPositioning::offset_from_parent(
361 *OFFSET,
362 ParentOffsetBounds::WindowByPosition,
363 *parent_anchor,
364 *child_anchor,
365 );
366 
367 let position_cache = PositionCache::new();
368 let (anchor_x, anchor_y) = parent_anchor_point(*parent_anchor);
369 
370 let size_constraint = offset_positioning.size_constraint(
371 PARENT_RECT.size(),
372 *WINDOW_SIZE,
373 *DEFAULT_SIZE_CONSTRAINT,
374 &position_cache,
375 );
376 // The size constraint should be unchanged since the bounding behavior adjusts
377 // position.
378 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
379 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
380 
381 // Compute the expected x-axis position of the child relative to the parent's
382 // anchor point.
383 let mut expected_child_x = anchor_x
384 - match child_anchor {
385 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
386 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
387 CHILD_SIZE.x() / 2.
388 }
389 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
390 CHILD_SIZE.x()
391 }
392 };
393 expected_child_x = expected_child_x
394 .min(WINDOW_SIZE.x() - CHILD_SIZE.x())
395 .max(0.);
396 
397 let child_position_x = offset_positioning.x_axis.compute_child_position(
398 *CHILD_SIZE,
399 *PARENT_RECT,
400 *WINDOW_SIZE,
401 &position_cache,
402 );
403 assert!(child_position_x.is_ok());
404 assert_eq!(child_position_x.unwrap(), expected_child_x);
405 
406 // Compute the expected y-axis position of the child relative to the parent anchor
407 // point.
408 let mut expected_child_y = anchor_y
409 - match child_anchor {
410 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
411 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
412 CHILD_SIZE.y() / 2.
413 }
414 ChildAnchor::BottomLeft
415 | ChildAnchor::BottomMiddle
416 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
417 };
418 expected_child_y = expected_child_y
419 .min(WINDOW_SIZE.y() - CHILD_SIZE.y())
420 .max(0.);
421 
422 let child_position_y = offset_positioning.y_axis.compute_child_position(
423 *CHILD_SIZE,
424 *PARENT_RECT,
425 *WINDOW_SIZE,
426 &position_cache,
427 );
428 
429 assert!(child_position_y.is_ok());
430 assert_eq!(child_position_y.unwrap(), expected_child_y);
431 }
432 }
433}
434 
435#[test]
436fn test_offset_from_parent_bound_to_parent_with_size() {
437 for parent_anchor in PARENT_ANCHORS.iter() {
438 for child_anchor in CHILD_ANCHORS.iter() {
439 let offset_positioning = OffsetPositioning::offset_from_parent(
440 *OFFSET,
441 ParentOffsetBounds::ParentBySize,
442 *parent_anchor,
443 *child_anchor,
444 );
445 
446 let position_cache = PositionCache::new();
447 let (anchor_x, anchor_y) = parent_anchor_point(*parent_anchor);
448 
449 // Compute the expected size constraint based on the parent's size and expected
450 // position of child element within the parent's bounding rect.
451 let mut expected_size_constraint_max_width = match child_anchor {
452 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => {
453 PARENT_RECT.max_x() - anchor_x
454 }
455 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
456 (anchor_x - PARENT_RECT.min_x()).min(PARENT_RECT.max_x() - anchor_x) * 2.
457 }
458 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
459 anchor_x - PARENT_RECT.min_x()
460 }
461 };
462 expected_size_constraint_max_width =
463 expected_size_constraint_max_width.clamp(0., PARENT_RECT.width());
464 
465 let mut expected_size_constraint_max_height = match child_anchor {
466 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => {
467 PARENT_RECT.max_y() - anchor_y
468 }
469 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
470 (anchor_y - PARENT_RECT.min_y()).min(PARENT_RECT.max_y() - anchor_y) * 2.
471 }
472 ChildAnchor::BottomLeft | ChildAnchor::BottomMiddle | ChildAnchor::BottomRight => {
473 anchor_y - PARENT_RECT.min_y()
474 }
475 };
476 expected_size_constraint_max_height =
477 expected_size_constraint_max_height.clamp(0., PARENT_RECT.height());
478 
479 let size_constraint = offset_positioning.size_constraint(
480 PARENT_RECT.size(),
481 *WINDOW_SIZE,
482 *DEFAULT_SIZE_CONSTRAINT,
483 &position_cache,
484 );
485 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
486 assert_eq!(
487 size_constraint.max,
488 vec2f(
489 expected_size_constraint_max_width,
490 expected_size_constraint_max_height
491 )
492 );
493 
494 // Compute the expected x-axis position of the child relative to the parent's
495 // anchor point.
496 let mut expected_child_x = anchor_x
497 - match child_anchor {
498 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
499 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
500 CHILD_SIZE.x() / 2.
501 }
502 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
503 CHILD_SIZE.x()
504 }
505 };
506 expected_child_x = expected_child_x.clamp(PARENT_RECT.min_x(), PARENT_RECT.max_x());
507 
508 let child_position_x = offset_positioning.x_axis.compute_child_position(
509 *CHILD_SIZE,
510 *PARENT_RECT,
511 *WINDOW_SIZE,
512 &position_cache,
513 );
514 assert!(child_position_x.is_ok());
515 assert_eq!(child_position_x.unwrap(), expected_child_x);
516 
517 // Compute the expected y-axis position of the child relative to the positioned
518 // element's anchor point.
519 let mut expected_child_y = anchor_y
520 - match child_anchor {
521 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
522 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
523 CHILD_SIZE.y() / 2.
524 }
525 ChildAnchor::BottomLeft
526 | ChildAnchor::BottomMiddle
527 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
528 };
529 expected_child_y = expected_child_y.clamp(PARENT_RECT.min_y(), PARENT_RECT.max_y());
530 
531 let child_position_y = offset_positioning.y_axis.compute_child_position(
532 *CHILD_SIZE,
533 *PARENT_RECT,
534 *WINDOW_SIZE,
535 &position_cache,
536 );
537 
538 assert!(child_position_y.is_ok());
539 assert_eq!(child_position_y.unwrap(), expected_child_y);
540 }
541 }
542}
543 
544#[test]
545fn test_offset_from_positioned_element_unbounded() {
546 for positioned_element_anchor in POSITIONED_ELEMENT_ANCHORS.iter() {
547 for child_anchor in CHILD_ANCHORS.iter() {
548 let offset_positioning = OffsetPositioning::offset_from_save_position_element(
549 SAVE_POSITION_ID,
550 *OFFSET,
551 PositionedElementOffsetBounds::Unbounded,
552 *positioned_element_anchor,
553 *child_anchor,
554 );
555 
556 let mut position_cache = PositionCache::new();
557 position_cache.start();
558 position_cache
559 .cache_position_indefinitely(SAVE_POSITION_ID.to_owned(), *POSITIONED_ELEMENT_RECT);
560 position_cache.end();
561 let (anchor_x, anchor_y) = positioned_element_anchor_point(*positioned_element_anchor);
562 
563 let size_constraint = offset_positioning.size_constraint(
564 PARENT_RECT.size(),
565 *WINDOW_SIZE,
566 *DEFAULT_SIZE_CONSTRAINT,
567 &position_cache,
568 );
569 // The size constraint should be unchanged since there is no bounding behavior.
570 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
571 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
572 
573 // Compute the expected x-axis position of the child relative to the positioned
574 // element's anchor point.
575 let expected_child_x = anchor_x
576 - match child_anchor {
577 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
578 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
579 CHILD_SIZE.x() / 2.
580 }
581 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
582 CHILD_SIZE.x()
583 }
584 };
585 let child_position_x = offset_positioning.x_axis.compute_child_position(
586 *CHILD_SIZE,
587 *PARENT_RECT,
588 *WINDOW_SIZE,
589 &position_cache,
590 );
591 assert!(child_position_x.is_ok());
592 assert_eq!(child_position_x.unwrap(), expected_child_x);
593 
594 // Compute the expected y-axis position of the child relative to the positioned
595 // element's anchor point.
596 let expected_child_y = anchor_y
597 - match child_anchor {
598 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
599 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
600 CHILD_SIZE.y() / 2.
601 }
602 ChildAnchor::BottomLeft
603 | ChildAnchor::BottomMiddle
604 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
605 };
606 let child_position_y = offset_positioning.y_axis.compute_child_position(
607 *CHILD_SIZE,
608 *PARENT_RECT,
609 *WINDOW_SIZE,
610 &position_cache,
611 );
612 
613 assert!(child_position_y.is_ok());
614 assert_eq!(child_position_y.unwrap(), expected_child_y);
615 }
616 }
617}
618 
619#[test]
620fn test_offset_from_positioned_element_bound_to_parent_with_position() {
621 for positioned_element_anchor in POSITIONED_ELEMENT_ANCHORS.iter() {
622 for child_anchor in CHILD_ANCHORS.iter() {
623 let offset_positioning = OffsetPositioning::offset_from_save_position_element(
624 SAVE_POSITION_ID,
625 *OFFSET,
626 PositionedElementOffsetBounds::ParentByPosition,
627 *positioned_element_anchor,
628 *child_anchor,
629 );
630 
631 let mut position_cache = PositionCache::new();
632 position_cache.start();
633 position_cache
634 .cache_position_indefinitely(SAVE_POSITION_ID.to_owned(), *POSITIONED_ELEMENT_RECT);
635 position_cache.end();
636 let (anchor_x, anchor_y) = positioned_element_anchor_point(*positioned_element_anchor);
637 
638 let size_constraint = offset_positioning.size_constraint(
639 PARENT_RECT.size(),
640 *WINDOW_SIZE,
641 *DEFAULT_SIZE_CONSTRAINT,
642 &position_cache,
643 );
644 // The size constraint should be unchanged since the bounding behavior adjusts
645 // position.
646 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
647 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
648 
649 // Compute the expected x-axis position of the child relative to the positioned
650 // element's anchor point.
651 let mut expected_child_x = anchor_x
652 - match child_anchor {
653 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
654 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
655 CHILD_SIZE.x() / 2.
656 }
657 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
658 CHILD_SIZE.x()
659 }
660 };
661 expected_child_x = expected_child_x
662 .min(PARENT_RECT.max_x() - CHILD_SIZE.x())
663 .max(PARENT_RECT.min_x());
664 
665 let child_position_x = offset_positioning
666 .x_axis
667 .compute_child_position(*CHILD_SIZE, *PARENT_RECT, *WINDOW_SIZE, &position_cache)
668 .expect("Failed to compute child position x.");
669 assert_eq!(child_position_x, expected_child_x);
670 
671 // Compute the expected y-axis position of the child relative to the positioned
672 // element's anchor point.
673 let mut expected_child_y = anchor_y
674 - match child_anchor {
675 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
676 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
677 CHILD_SIZE.y() / 2.
678 }
679 ChildAnchor::BottomLeft
680 | ChildAnchor::BottomMiddle
681 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
682 };
683 expected_child_y = expected_child_y
684 .min(PARENT_RECT.max_y() - CHILD_SIZE.y())
685 .max(PARENT_RECT.min_y());
686 
687 let child_position_y = offset_positioning
688 .y_axis
689 .compute_child_position(*CHILD_SIZE, *PARENT_RECT, *WINDOW_SIZE, &position_cache)
690 .expect("Failed to compute child position y.");
691 assert_eq!(child_position_y, expected_child_y);
692 }
693 }
694}
695 
696#[test]
697fn test_offset_from_positioned_element_bound_to_parent_with_position_with_window_overflow() {
698 for positioned_element_anchor in POSITIONED_ELEMENT_ANCHORS.iter() {
699 for child_anchor in CHILD_ANCHORS.iter() {
700 assert_eq!(
701 // positioning a 60x60 rect relatively between (50,50) and (75,75) will result in
702 // the element getting pushed up+left until it aligns with its parent's max bounds.
703 get_absolute_x_y_position_for_child_element(
704 vec2f(60., 60.),
705 *SMALL_PARENT_RECT,
706 *positioned_element_anchor,
707 *child_anchor
708 ),
709 vec2f(15.0, 15.0)
710 );
711 }
712 }
713}
714 
715#[test]
716fn test_offset_from_positioned_element_bound_to_parent_with_position_with_double_window_overflow() {
717 for positioned_element_anchor in POSITIONED_ELEMENT_ANCHORS.iter() {
718 for child_anchor in CHILD_ANCHORS.iter() {
719 assert_eq!(
720 // positioning an 80x80 rect relatively between (25,25) and (75,75) will result in
721 // the element not being able to align itself either with the lower or upper bounds
722 // of the parent - in both cases it would be pushed offscreen.
723 // so instead, it centers itself.
724 get_absolute_x_y_position_for_child_element(
725 vec2f(80., 80.),
726 *PARENT_RECT,
727 *positioned_element_anchor,
728 *child_anchor
729 ),
730 vec2f(10.0, 10.0)
731 );
732 }
733 }
734}
735 
736#[test]
737fn test_offset_from_positioned_element_bound_to_window_with_position() {
738 for positioned_element_anchor in POSITIONED_ELEMENT_ANCHORS.iter() {
739 for child_anchor in CHILD_ANCHORS.iter() {
740 let offset_positioning = OffsetPositioning::offset_from_save_position_element(
741 SAVE_POSITION_ID,
742 *OFFSET,
743 PositionedElementOffsetBounds::WindowByPosition,
744 *positioned_element_anchor,
745 *child_anchor,
746 );
747 
748 let mut position_cache = PositionCache::new();
749 position_cache.start();
750 position_cache
751 .cache_position_indefinitely(SAVE_POSITION_ID.to_owned(), *POSITIONED_ELEMENT_RECT);
752 position_cache.end();
753 let (anchor_x, anchor_y) = positioned_element_anchor_point(*positioned_element_anchor);
754 
755 let size_constraint = offset_positioning.size_constraint(
756 PARENT_RECT.size(),
757 *WINDOW_SIZE,
758 *DEFAULT_SIZE_CONSTRAINT,
759 &position_cache,
760 );
761 // The size constraint should be unchanged since the bounding behavior adjusts
762 // position.
763 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
764 assert_eq!(size_constraint.max, DEFAULT_SIZE_CONSTRAINT.max);
765 
766 // Compute the expected x-axis position of the child relative to the positioned
767 // element's anchor point.
768 let mut expected_child_x = anchor_x
769 - match child_anchor {
770 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
771 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
772 CHILD_SIZE.x() / 2.
773 }
774 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
775 CHILD_SIZE.x()
776 }
777 };
778 expected_child_x = expected_child_x
779 .min(WINDOW_SIZE.x() - CHILD_SIZE.x())
780 .max(0.);
781 
782 let child_position_x = offset_positioning
783 .x_axis
784 .compute_child_position(
785 *CHILD_SIZE,
786 *POSITIONED_ELEMENT_RECT,
787 *WINDOW_SIZE,
788 &position_cache,
789 )
790 .expect("Failed to compute child position x.");
791 assert_eq!(child_position_x, expected_child_x);
792 
793 // Compute the expected y-axis position of the child relative to the positioned
794 // element's anchor point.
795 let mut expected_child_y = anchor_y
796 - match child_anchor {
797 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
798 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
799 CHILD_SIZE.y() / 2.
800 }
801 ChildAnchor::BottomLeft
802 | ChildAnchor::BottomMiddle
803 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
804 };
805 expected_child_y = expected_child_y
806 .min(WINDOW_SIZE.y() - CHILD_SIZE.y())
807 .max(0.);
808 
809 let child_position_y = offset_positioning
810 .y_axis
811 .compute_child_position(
812 *CHILD_SIZE,
813 *POSITIONED_ELEMENT_RECT,
814 *WINDOW_SIZE,
815 &position_cache,
816 )
817 .expect("Failed to compute child position y.");
818 assert_eq!(child_position_y, expected_child_y);
819 }
820 }
821}
822 
823#[test]
824fn test_offset_from_positioned_element_bound_to_window_with_size() {
825 for positioned_element_anchor in POSITIONED_ELEMENT_ANCHORS.iter() {
826 for child_anchor in CHILD_ANCHORS.iter() {
827 let offset_positioning = OffsetPositioning::offset_from_save_position_element(
828 SAVE_POSITION_ID,
829 *OFFSET,
830 PositionedElementOffsetBounds::WindowBySize,
831 *positioned_element_anchor,
832 *child_anchor,
833 );
834 
835 let mut position_cache = PositionCache::new();
836 position_cache.start();
837 position_cache
838 .cache_position_indefinitely(SAVE_POSITION_ID.to_owned(), *POSITIONED_ELEMENT_RECT);
839 position_cache.end();
840 let (anchor_x, anchor_y) = positioned_element_anchor_point(*positioned_element_anchor);
841 
842 // Compute the expected size constraint based on the window bounds and expected
843 // position of the child element.
844 let expected_size_constraint_max_width = match child_anchor {
845 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => {
846 WINDOW_SIZE.x() - anchor_x
847 }
848 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
849 anchor_x.min(WINDOW_SIZE.x() - anchor_x) * 2.
850 }
851 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
852 anchor_x
853 }
854 };
855 let expected_size_constraint_max_height = match child_anchor {
856 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => {
857 WINDOW_SIZE.y() - anchor_y
858 }
859 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
860 anchor_y.min(WINDOW_SIZE.y() - anchor_y) * 2.
861 }
862 ChildAnchor::BottomLeft | ChildAnchor::BottomMiddle | ChildAnchor::BottomRight => {
863 anchor_y
864 }
865 };
866 
867 let size_constraint = offset_positioning.size_constraint(
868 PARENT_RECT.size(),
869 *WINDOW_SIZE,
870 *DEFAULT_SIZE_CONSTRAINT,
871 &position_cache,
872 );
873 
874 assert_eq!(size_constraint.min, DEFAULT_SIZE_CONSTRAINT.min);
875 assert_eq!(
876 size_constraint.max,
877 vec2f(
878 expected_size_constraint_max_width,
879 expected_size_constraint_max_height
880 )
881 );
882 
883 // Compute the expected x-axis position of the child relative to the positioned
884 // element's anchor point.
885 let mut expected_child_x = anchor_x
886 - match child_anchor {
887 ChildAnchor::TopLeft | ChildAnchor::MiddleLeft | ChildAnchor::BottomLeft => 0.,
888 ChildAnchor::TopMiddle | ChildAnchor::Center | ChildAnchor::BottomMiddle => {
889 CHILD_SIZE.x() / 2.
890 }
891 ChildAnchor::TopRight | ChildAnchor::MiddleRight | ChildAnchor::BottomRight => {
892 CHILD_SIZE.x()
893 }
894 };
895 expected_child_x = expected_child_x.clamp(0., WINDOW_SIZE.x());
896 
897 let child_position_x = offset_positioning
898 .x_axis
899 .compute_child_position(
900 *CHILD_SIZE,
901 *POSITIONED_ELEMENT_RECT,
902 *WINDOW_SIZE,
903 &position_cache,
904 )
905 .expect("Failed to compute child position x.");
906 assert_eq!(child_position_x, expected_child_x);
907 
908 // Compute the expected y-axis position of the child relative to the positioned
909 // element's anchor point.
910 let mut expected_child_y = anchor_y
911 - match child_anchor {
912 ChildAnchor::TopLeft | ChildAnchor::TopMiddle | ChildAnchor::TopRight => 0.,
913 ChildAnchor::MiddleLeft | ChildAnchor::MiddleRight | ChildAnchor::Center => {
914 CHILD_SIZE.y() / 2.
915 }
916 ChildAnchor::BottomLeft
917 | ChildAnchor::BottomMiddle
918 | ChildAnchor::BottomRight => CHILD_SIZE.y(),
919 };
920 expected_child_y = expected_child_y.clamp(0., WINDOW_SIZE.y());
921 
922 let child_position_y = offset_positioning
923 .y_axis
924 .compute_child_position(
925 *CHILD_SIZE,
926 *POSITIONED_ELEMENT_RECT,
927 *WINDOW_SIZE,
928 &position_cache,
929 )
930 .expect("Failed to compute child position y.");
931 assert_eq!(child_position_y, expected_child_y);
932 }
933 }
934}
935