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 ( $($NAME:ident = ($x:expr, $rtl:expr, $y:expr);)+ ) => {named_aligns!{$(
260 [stringify!(($x, $y))] $NAME = ($x, $rtl, $y);
261 )+}};
262
263 ( $([$doc:expr] $NAME:ident = ($x:expr, $rtl:expr, $y:expr);)+ ) => {
264 $(
265 #[doc=$doc]
266 pub const $NAME: Align = Align { x: Factor($x), x_rtl_aware: $rtl, y: Factor($y) };
267 )+
268
269 pub fn name(self) -> Option<&'static str> {
271 $(
272 if self == Self::$NAME {
273 Some(stringify!($NAME))
274 }
275 )else+
276 else {
277 None
278 }
279 }
280
281 pub fn from_name(name: &str) -> Option<Self> {
283 $(
284 if name == stringify!($NAME) {
285 Some(Self::$NAME)
286 }
287 )else+
288 else {
289 None
290 }
291 }
292 };
293}
294impl fmt::Debug for Align {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 if let Some(name) = self.name() {
297 if f.alternate() {
298 write!(f, "Align::{name}")
299 } else {
300 f.write_str(name)
301 }
302 } else {
303 f.debug_struct("Align")
304 .field("x", &self.x)
305 .field("x_rtl_aware", &self.x_rtl_aware)
306 .field("y", &self.y)
307 .finish()
308 }
309 }
310}
311impl fmt::Display for Align {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 if let Some(name) = self.name() {
314 f.write_str(name)
315 } else {
316 f.write_char('(')?;
317 if self.is_fill_x() {
318 f.write_str("FILL")?;
319 } else {
320 write!(f, "{}", FactorPercent::from(self.x))?;
321 }
322 f.write_str(", ")?;
323 if self.is_fill_y() {
324 f.write_str("FILL")?;
325 } else if self.is_baseline() {
326 f.write_str("BASELINE")?;
327 } else {
328 write!(f, "{}", FactorPercent::from(self.y))?;
329 }
330 f.write_char(')')
331 }
332 }
333}
334impl std::str::FromStr for Align {
336 type Err = ParseFloatCompositeError;
337
338 fn from_str(s: &str) -> Result<Self, Self::Err> {
339 if let Some(named) = Align::from_name(s) {
340 Ok(named)
341 } else if let Some(s) = s.strip_prefix('(')
342 && let Some(s) = s.strip_prefix(')')
343 {
344 let mut parser = ComponentParser { iter: s.split(',') };
345 let r = Self {
346 x: parser.next(false)?,
347 x_rtl_aware: false,
348 y: parser.next(true)?,
349 };
350 parser.end()?;
351 Ok(r)
352 } else {
353 Err(ParseFloatCompositeError::UnknownFormat)
354 }
355 }
356}
357struct ComponentParser<'a> {
358 iter: std::str::Split<'a, char>,
359}
360impl<'a> ComponentParser<'a> {
361 fn next(&mut self, y: bool) -> Result<Factor, ParseFloatCompositeError> {
362 let fct = match self.iter.next().ok_or(ParseFloatCompositeError::MissingComponent)?.trim() {
363 "FILL" => f32::INFINITY.fct(),
364 "BASELINE" if y => f32::NEG_INFINITY.fct(),
365 s => s.parse()?,
366 };
367 Ok(fct)
368 }
369 fn end(mut self) -> Result<(), ParseFloatCompositeError> {
370 if self.iter.next().is_some() {
371 Err(ParseFloatCompositeError::ExtraComponent)
372 } else {
373 Ok(())
374 }
375 }
376}
377impl Align {
378 named_aligns! {
379 TOP_START = (0.0, true, 0.0);
380 TOP_LEFT = (0.0, false, 0.0);
381 BOTTOM_START = (0.0, true, 1.0);
382 BOTTOM_LEFT = (0.0, false, 1.0);
383
384 TOP_END = (1.0, true, 0.0);
385 TOP_RIGHT = (1.0, false, 0.0);
386 BOTTOM_END = (1.0, true, 1.0);
387 BOTTOM_RIGHT = (1.0, false, 1.0);
388
389 START = (0.0, true, 0.5);
390 LEFT = (0.0, false, 0.5);
391 END = (1.0, true, 0.5);
392 RIGHT = (1.0, false, 0.5);
393 TOP = (0.5, false, 0.0);
394 BOTTOM = (0.5, false, 1.0);
395
396 CENTER = (0.5, false, 0.5);
397
398 FILL_TOP = (f32::INFINITY, false, 0.0);
399 FILL_BOTTOM = (f32::INFINITY, false, 1.0);
400 FILL_START = (0.0, true, f32::INFINITY);
401 FILL_LEFT = (0.0, false, f32::INFINITY);
402 FILL_RIGHT = (1.0, false, f32::INFINITY);
403 FILL_END = (1.0, true, f32::INFINITY);
404
405 FILL_X = (f32::INFINITY, false, 0.5);
406 FILL_Y = (0.5, false, f32::INFINITY);
407
408 FILL = (f32::INFINITY, false, f32::INFINITY);
409
410 BASELINE_START = (0.0, true, f32::NEG_INFINITY);
411 BASELINE_LEFT = (0.0, false, f32::NEG_INFINITY);
412 BASELINE_CENTER = (0.5, false, f32::NEG_INFINITY);
413 BASELINE_END = (1.0, true, f32::NEG_INFINITY);
414 BASELINE_RIGHT = (1.0, false, f32::NEG_INFINITY);
415
416 BASELINE = (f32::INFINITY, false, f32::NEG_INFINITY);
417 }
418}
419impl_from_and_into_var! {
420 fn from(alignment: Align) -> Point {
422 Point {
423 x: alignment.x.into(),
424 y: alignment.y.into(),
425 }
426 }
427
428 fn from(factor2d: Factor2d) -> Align {
429 Align {
430 x: factor2d.x,
431 x_rtl_aware: false,
432 y: factor2d.y,
433 }
434 }
435}
436
437impl Transitionable for Align {
438 fn lerp(mut self, to: &Self, step: EasingStep) -> Self {
439 let end = step >= 1.fct();
440
441 if end {
442 self.x_rtl_aware = to.x_rtl_aware;
443 }
444
445 if self.x.0.is_finite() && to.x.0.is_finite() {
446 self.x = self.x.lerp(&to.x, step);
447 } else if end {
448 self.x = to.x;
449 }
450
451 if self.y.0.is_finite() && to.y.0.is_finite() {
452 self.y = self.y.lerp(&to.y, step);
453 } else if end {
454 self.y = to.y;
455 }
456
457 self
458 }
459}
460
461impl<S: Into<Factor2d>> ops::Mul<S> for Align {
462 type Output = Self;
463
464 fn mul(mut self, rhs: S) -> Self {
465 self *= rhs;
466 self
467 }
468}
469impl<S: Into<Factor2d>> ops::MulAssign<S> for Align {
470 fn mul_assign(&mut self, rhs: S) {
471 let rhs = rhs.into();
472
473 if self.x.0.is_finite() {
474 self.x *= rhs.x;
475 } else if rhs.x == 0.fct() {
476 self.x = 0.fct();
477 }
478 if self.y.0.is_finite() {
479 self.y *= rhs.y;
480 } else if rhs.y == 0.fct() {
481 self.y = 0.fct()
482 }
483 }
484}
485impl<S: Into<Factor2d>> ops::Div<S> for Align {
486 type Output = Self;
487
488 fn div(mut self, rhs: S) -> Self {
489 self /= rhs;
490 self
491 }
492}
493impl<S: Into<Factor2d>> ops::DivAssign<S> for Align {
494 fn div_assign(&mut self, rhs: S) {
495 let rhs = rhs.into();
496
497 if self.x.0.is_finite() {
498 self.x /= rhs.x;
499 }
500 if self.y.0.is_finite() {
501 self.y /= rhs.y;
502 }
503 }
504}
505
506#[derive(serde::Serialize, serde::Deserialize)]
507#[serde(untagged)]
508enum AlignSerde<'s> {
509 Named(Cow<'s, str>),
510 Unnamed { x: Factor, x_rtl_aware: bool, y: Factor },
511}
512impl serde::Serialize for Align {
513 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
514 where
515 S: serde::Serializer,
516 {
517 if serializer.is_human_readable()
518 && let Some(name) = self.name()
519 {
520 return AlignSerde::Named(Cow::Borrowed(name)).serialize(serializer);
521 }
522
523 AlignSerde::Unnamed {
524 x: self.x,
525 x_rtl_aware: self.x_rtl_aware,
526 y: self.y,
527 }
528 .serialize(serializer)
529 }
530}
531impl<'de> serde::Deserialize<'de> for Align {
532 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
533 where
534 D: serde::Deserializer<'de>,
535 {
536 use serde::de::Error;
537
538 match AlignSerde::deserialize(deserializer)? {
539 AlignSerde::Named(n) => match Align::from_name(&n) {
540 Some(a) => Ok(a),
541 None => Err(D::Error::custom("unknown align name")),
542 },
543 AlignSerde::Unnamed { x, x_rtl_aware, y } => Ok(Align { x, x_rtl_aware, y }),
544 }
545 }
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551
552 #[test]
553 fn align_named() {
554 let value = serde_json::to_value(Align::TOP_START).unwrap();
555 assert_eq!(value, serde_json::Value::String("TOP_START".to_owned()));
556
557 let align: Align = serde_json::from_value(value).unwrap();
558 assert_eq!(align, Align::TOP_START);
559 }
560}