1use std::{
2 borrow::Cow,
3 fmt::{self, Write},
4 ops,
5};
6
7use crate::{context::LayoutDirection, unit::ParseFloatCompositeError};
8use zng_var::{
9 animation::{Transitionable, easing::EasingStep},
10 impl_from_and_into_var,
11};
12
13use super::{Factor, Factor2d, FactorPercent, FactorUnits, Point, Px, PxConstraints, PxConstraints2d, PxSize, PxVector};
14
15#[derive(Clone, Copy, PartialEq, Eq, Hash)]
47pub struct Align {
48 pub x: Factor,
50 pub x_rtl_aware: bool,
52
53 pub y: Factor,
55}
56impl Default for Align {
57 fn default() -> Self {
59 Align::START
60 }
61}
62impl Align {
63 pub fn x(self, direction: LayoutDirection) -> Factor {
69 let x = if self.x.0.is_finite() { self.x } else { 0.fct() };
70
71 if self.x_rtl_aware && direction.is_rtl() { x.flip() } else { x }
72 }
73
74 pub fn y(self) -> Factor {
81 if self.y.0.is_finite() {
82 self.y
83 } else if self.is_baseline() {
84 1.fct()
85 } else {
86 0.fct()
87 }
88 }
89
90 pub fn xy(self, direction: LayoutDirection) -> Factor2d {
95 Factor2d::new(self.x(direction), self.y())
96 }
97
98 pub fn is_fill_x(self) -> bool {
102 self.x.0.is_infinite() && self.x.0.is_sign_positive()
103 }
104
105 pub fn is_fill_y(self) -> bool {
109 self.y.0.is_infinite() && self.y.0.is_sign_positive()
110 }
111
112 pub fn is_baseline(self) -> bool {
118 self.y.0.is_infinite() && self.y.0.is_sign_negative()
119 }
120
121 pub fn fill_vector(self) -> super::euclid::BoolVector2D {
123 super::euclid::BoolVector2D {
124 x: self.is_fill_x(),
125 y: self.is_fill_y(),
126 }
127 }
128
129 pub fn child_constraints(self, parent_constraints: PxConstraints2d) -> PxConstraints2d {
136 parent_constraints
138 .with_new_min(
139 if self.is_fill_x() { parent_constraints.x.min() } else { Px(0) },
140 if self.is_fill_y() { parent_constraints.y.min() } else { Px(0) },
141 )
142 .with_fill_and(self.is_fill_x(), self.is_fill_y())
143 }
144
145 pub fn child_offset(self, child_size: PxSize, parent_size: PxSize, direction: LayoutDirection) -> PxVector {
151 let mut offset = PxVector::zero();
152 if !self.is_fill_x() {
153 let x = if self.x_rtl_aware && direction.is_rtl() {
154 self.x.flip().0
155 } else {
156 self.x.0
157 };
158
159 offset.x = (parent_size.width - child_size.width) * x;
160 }
161
162 let baseline = self.is_baseline();
163
164 if !self.is_fill_y() {
165 let y = if baseline { 1.0 } else { self.y.0 };
166
167 offset.y = (parent_size.height - child_size.height) * y;
168 }
169 offset
170 }
171
172 pub fn measure(self, child_size: PxSize, parent_constraints: PxConstraints2d) -> PxSize {
180 PxSize::new(
181 self.measure_x(child_size.width, parent_constraints.x),
182 self.measure_y(child_size.height, parent_constraints.y),
183 )
184 }
185
186 pub fn measure_x(self, child_width: Px, parent_constraints_x: PxConstraints) -> Px {
192 if parent_constraints_x.is_inner() {
193 child_width
194 } else {
195 let width = parent_constraints_x.fill().max(child_width);
196 parent_constraints_x.clamp(width)
197 }
198 }
199
200 pub fn measure_y(self, child_height: Px, parent_constraints_y: PxConstraints) -> Px {
206 if parent_constraints_y.is_inner() {
207 child_height
208 } else {
209 let height = parent_constraints_y.fill().max(child_height);
210 parent_constraints_y.clamp(height)
211 }
212 }
213
214 pub fn layout(self, child_size: PxSize, parent_constraints: PxConstraints2d, direction: LayoutDirection) -> (PxSize, PxVector, bool) {
224 let size = self.measure(child_size, parent_constraints);
225 let offset = self.child_offset(child_size, size, direction);
226 (size, offset, self.is_baseline())
227 }
228}
229impl_from_and_into_var! {
230 fn from<X: Into<Factor>, Y: Into<Factor>>((x, y): (X, Y)) -> Align {
231 Align {
232 x: x.into(),
233 x_rtl_aware: false,
234 y: y.into(),
235 }
236 }
237
238 fn from<X: Into<Factor>, Y: Into<Factor>>((x, rtl, y): (X, bool, Y)) -> Align {
239 Align {
240 x: x.into(),
241 x_rtl_aware: rtl,
242 y: y.into(),
243 }
244 }
245
246 fn from(xy: Factor) -> Align {
247 Align {
248 x: xy,
249 x_rtl_aware: false,
250 y: xy,
251 }
252 }
253
254 fn from(xy: FactorPercent) -> Align {
255 xy.fct().into()
256 }
257}
258macro_rules! named_aligns {
259
260 ( $($(#[$doc:meta])* $NAME:ident = ($x:expr, $rtl:expr, $y:expr);)+ ) => {
261 $(
262 $(#[$doc])*
263 pub const $NAME: Align = Align { x: Factor($x), x_rtl_aware: $rtl, y: Factor($y) };
264 )+
265
266 pub fn name(self) -> Option<&'static str> {
268 $(
269 if self == Self::$NAME {
270 Some(stringify!($NAME))
271 }
272 )else+
273 else {
274 None
275 }
276 }
277
278 pub fn from_name(name: &str) -> Option<Self> {
280 $(
281 if name == stringify!($NAME) {
282 Some(Self::$NAME)
283 }
284 )else+
285 else {
286 None
287 }
288 }
289 };
290}
291impl fmt::Debug for Align {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 if let Some(name) = self.name() {
294 if f.alternate() {
295 write!(f, "Align::{name}")
296 } else {
297 f.write_str(name)
298 }
299 } else {
300 f.debug_struct("Align")
301 .field("x", &self.x)
302 .field("x_rtl_aware", &self.x_rtl_aware)
303 .field("y", &self.y)
304 .finish()
305 }
306 }
307}
308impl fmt::Display for Align {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 if let Some(name) = self.name() {
311 f.write_str(name)
312 } else {
313 f.write_char('(')?;
314 if self.is_fill_x() {
315 f.write_str("FILL")?;
316 } else {
317 write!(f, "{}", FactorPercent::from(self.x))?;
318 }
319 f.write_str(", ")?;
320 if self.is_fill_y() {
321 f.write_str("FILL")?;
322 } else if self.is_baseline() {
323 f.write_str("BASELINE")?;
324 } else {
325 write!(f, "{}", FactorPercent::from(self.y))?;
326 }
327 f.write_char(')')
328 }
329 }
330}
331impl std::str::FromStr for Align {
333 type Err = ParseFloatCompositeError;
334
335 fn from_str(s: &str) -> Result<Self, Self::Err> {
336 if let Some(named) = Align::from_name(s) {
337 Ok(named)
338 } else if let Some(s) = s.strip_prefix('(')
339 && let Some(s) = s.strip_prefix(')')
340 {
341 let mut parser = ComponentParser { iter: s.split(',') };
342 let r = Self {
343 x: parser.next(false)?,
344 x_rtl_aware: false,
345 y: parser.next(true)?,
346 };
347 parser.end()?;
348 Ok(r)
349 } else {
350 Err(ParseFloatCompositeError::UnknownFormat)
351 }
352 }
353}
354struct ComponentParser<'a> {
355 iter: std::str::Split<'a, char>,
356}
357impl<'a> ComponentParser<'a> {
358 fn next(&mut self, y: bool) -> Result<Factor, ParseFloatCompositeError> {
359 let fct = match self.iter.next().ok_or(ParseFloatCompositeError::MissingComponent)?.trim() {
360 "FILL" => f32::INFINITY.fct(),
361 "BASELINE" if y => f32::NEG_INFINITY.fct(),
362 s => s.parse()?,
363 };
364 Ok(fct)
365 }
366 fn end(mut self) -> Result<(), ParseFloatCompositeError> {
367 if self.iter.next().is_some() {
368 Err(ParseFloatCompositeError::ExtraComponent)
369 } else {
370 Ok(())
371 }
372 }
373}
374impl Align {
375 named_aligns! {
376 TOP_START = (0.0, true, 0.0);
380 TOP_LEFT = (0.0, false, 0.0);
382 BOTTOM_START = (0.0, true, 1.0);
386 BOTTOM_LEFT = (0.0, false, 1.0);
388
389 TOP_END = (1.0, true, 0.0);
393 TOP_RIGHT = (1.0, false, 0.0);
395 BOTTOM_END = (1.0, true, 1.0);
399 BOTTOM_RIGHT = (1.0, false, 1.0);
401
402 START = (0.0, true, 0.5);
406 LEFT = (0.0, false, 0.5);
408 END = (1.0, true, 0.5);
412 RIGHT = (1.0, false, 0.5);
414 TOP = (0.5, false, 0.0);
416 BOTTOM = (0.5, false, 1.0);
418
419 CENTER = (0.5, false, 0.5);
421
422 FILL_TOP = (f32::INFINITY, false, 0.0);
424 FILL_BOTTOM = (f32::INFINITY, false, 1.0);
426 FILL_START = (0.0, true, f32::INFINITY);
430 FILL_LEFT = (0.0, false, f32::INFINITY);
432 FILL_RIGHT = (1.0, false, f32::INFINITY);
434 FILL_END = (1.0, true, f32::INFINITY);
438
439 FILL_X = (f32::INFINITY, false, 0.5);
441 FILL_Y = (0.5, false, f32::INFINITY);
443
444 FILL = (f32::INFINITY, false, f32::INFINITY);
446
447 BASELINE_START = (0.0, true, f32::NEG_INFINITY);
451 BASELINE_LEFT = (0.0, false, f32::NEG_INFINITY);
453 BASELINE_CENTER = (0.5, false, f32::NEG_INFINITY);
455 BASELINE_END = (1.0, true, f32::NEG_INFINITY);
459 BASELINE_RIGHT = (1.0, false, f32::NEG_INFINITY);
461
462 BASELINE = (f32::INFINITY, false, f32::NEG_INFINITY);
464 }
465}
466impl_from_and_into_var! {
467 fn from(alignment: Align) -> Point {
469 Point {
470 x: alignment.x.into(),
471 y: alignment.y.into(),
472 }
473 }
474
475 fn from(factor2d: Factor2d) -> Align {
476 Align {
477 x: factor2d.x,
478 x_rtl_aware: false,
479 y: factor2d.y,
480 }
481 }
482}
483
484impl Transitionable for Align {
485 fn lerp(mut self, to: &Self, step: EasingStep) -> Self {
486 let end = step >= 1.fct();
487
488 if end {
489 self.x_rtl_aware = to.x_rtl_aware;
490 }
491
492 if self.x.0.is_finite() && to.x.0.is_finite() {
493 self.x = self.x.lerp(&to.x, step);
494 } else if end {
495 self.x = to.x;
496 }
497
498 if self.y.0.is_finite() && to.y.0.is_finite() {
499 self.y = self.y.lerp(&to.y, step);
500 } else if end {
501 self.y = to.y;
502 }
503
504 self
505 }
506}
507
508impl<S: Into<Factor2d>> ops::Mul<S> for Align {
509 type Output = Self;
510
511 fn mul(mut self, rhs: S) -> Self {
512 self *= rhs;
513 self
514 }
515}
516impl<S: Into<Factor2d>> ops::MulAssign<S> for Align {
517 fn mul_assign(&mut self, rhs: S) {
518 let rhs = rhs.into();
519
520 if self.x.0.is_finite() {
521 self.x *= rhs.x;
522 } else if rhs.x == 0.fct() {
523 self.x = 0.fct();
524 }
525 if self.y.0.is_finite() {
526 self.y *= rhs.y;
527 } else if rhs.y == 0.fct() {
528 self.y = 0.fct()
529 }
530 }
531}
532impl<S: Into<Factor2d>> ops::Div<S> for Align {
533 type Output = Self;
534
535 fn div(mut self, rhs: S) -> Self {
536 self /= rhs;
537 self
538 }
539}
540impl<S: Into<Factor2d>> ops::DivAssign<S> for Align {
541 fn div_assign(&mut self, rhs: S) {
542 let rhs = rhs.into();
543
544 if self.x.0.is_finite() {
545 self.x /= rhs.x;
546 }
547 if self.y.0.is_finite() {
548 self.y /= rhs.y;
549 }
550 }
551}
552
553#[derive(serde::Serialize, serde::Deserialize)]
554#[serde(untagged)]
555enum AlignSerde<'s> {
556 Named(Cow<'s, str>),
557 Unnamed { x: Factor, x_rtl_aware: bool, y: Factor },
558}
559impl serde::Serialize for Align {
560 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
561 where
562 S: serde::Serializer,
563 {
564 if serializer.is_human_readable()
565 && let Some(name) = self.name()
566 {
567 return AlignSerde::Named(Cow::Borrowed(name)).serialize(serializer);
568 }
569
570 AlignSerde::Unnamed {
571 x: self.x,
572 x_rtl_aware: self.x_rtl_aware,
573 y: self.y,
574 }
575 .serialize(serializer)
576 }
577}
578impl<'de> serde::Deserialize<'de> for Align {
579 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
580 where
581 D: serde::Deserializer<'de>,
582 {
583 use serde::de::Error;
584
585 match AlignSerde::deserialize(deserializer)? {
586 AlignSerde::Named(n) => match Align::from_name(&n) {
587 Some(a) => Ok(a),
588 None => Err(D::Error::custom("unknown align name")),
589 },
590 AlignSerde::Unnamed { x, x_rtl_aware, y } => Ok(Align { x, x_rtl_aware, y }),
591 }
592 }
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 #[test]
600 fn align_named() {
601 let value = serde_json::to_value(Align::TOP_START).unwrap();
602 assert_eq!(value, serde_json::Value::String("TOP_START".to_owned()));
603
604 let align: Align = serde_json::from_value(value).unwrap();
605 assert_eq!(align, Align::TOP_START);
606 }
607}