1 //! Geometric primitives useful for layout 2 3 use crate::util::sys::f32_max; 4 use crate::{style::Dimension, util::sys::f32_min}; 5 use core::ops::{Add, Sub}; 6 7 #[cfg(feature = "flexbox")] 8 use crate::style::FlexDirection; 9 10 /// The simple absolute horizontal and vertical axis 11 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 12 pub enum AbsoluteAxis { 13 /// The horizontal axis 14 Horizontal, 15 /// The vertical axis 16 Vertical, 17 } 18 19 impl AbsoluteAxis { 20 /// Returns the other variant of the enum 21 #[inline] other_axis(&self) -> Self22 pub const fn other_axis(&self) -> Self { 23 match *self { 24 AbsoluteAxis::Horizontal => AbsoluteAxis::Vertical, 25 AbsoluteAxis::Vertical => AbsoluteAxis::Horizontal, 26 } 27 } 28 } 29 30 impl<T> Size<T> { 31 #[inline(always)] 32 /// Get either the width or height depending on the AbsoluteAxis passed in get_abs(self, axis: AbsoluteAxis) -> T33 pub fn get_abs(self, axis: AbsoluteAxis) -> T { 34 match axis { 35 AbsoluteAxis::Horizontal => self.width, 36 AbsoluteAxis::Vertical => self.height, 37 } 38 } 39 } 40 41 impl<T: Add> Rect<T> { 42 #[inline(always)] 43 /// Get either the width or height depending on the AbsoluteAxis passed in grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output44 pub fn grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output { 45 match axis { 46 AbsoluteAxis::Horizontal => self.left + self.right, 47 AbsoluteAxis::Vertical => self.top + self.bottom, 48 } 49 } 50 } 51 52 /// The CSS abstract axis 53 /// <https://www.w3.org/TR/css-writing-modes-3/#abstract-axes> 54 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 55 pub enum AbstractAxis { 56 /// The axis in the inline dimension, i.e. the horizontal axis in horizontal writing modes and the vertical axis in vertical writing modes. 57 Inline, 58 /// The axis in the block dimension, i.e. the vertical axis in horizontal writing modes and the horizontal axis in vertical writing modes. 59 Block, 60 } 61 62 impl AbstractAxis { 63 /// Returns the other variant of the enum 64 #[inline] other(&self) -> AbstractAxis65 pub fn other(&self) -> AbstractAxis { 66 match *self { 67 AbstractAxis::Inline => AbstractAxis::Block, 68 AbstractAxis::Block => AbstractAxis::Inline, 69 } 70 } 71 72 /// Convert an `AbstractAxis` into an `AbsoluteAxis` naively assuming that the Inline axis is Horizontal 73 /// This is currently always true, but will change if Taffy ever implements the `writing_mode` property 74 #[inline] as_abs_naive(&self) -> AbsoluteAxis75 pub fn as_abs_naive(&self) -> AbsoluteAxis { 76 match self { 77 AbstractAxis::Inline => AbsoluteAxis::Horizontal, 78 AbstractAxis::Block => AbsoluteAxis::Vertical, 79 } 80 } 81 } 82 83 /// Container that holds an item in each absolute axis without specifying 84 /// what kind of item it is. 85 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 86 pub(crate) struct InBothAbsAxis<T> { 87 /// The item in the horizontal axis 88 pub horizontal: T, 89 /// The item in the vertical axis 90 pub vertical: T, 91 } 92 93 impl<T: Copy> InBothAbsAxis<T> { 94 #[cfg(feature = "grid")] 95 /// Get the contained item based on the AbsoluteAxis passed get(&self, axis: AbsoluteAxis) -> T96 pub fn get(&self, axis: AbsoluteAxis) -> T { 97 match axis { 98 AbsoluteAxis::Horizontal => self.horizontal, 99 AbsoluteAxis::Vertical => self.vertical, 100 } 101 } 102 } 103 104 /// An axis-aligned UI rectangle 105 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] 106 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 107 pub struct Rect<T> { 108 /// This can represent either the x-coordinate of the starting edge, 109 /// or the amount of padding on the starting side. 110 /// 111 /// The starting edge is the left edge when working with LTR text, 112 /// and the right edge when working with RTL text. 113 pub left: T, 114 /// This can represent either the x-coordinate of the ending edge, 115 /// or the amount of padding on the ending side. 116 /// 117 /// The ending edge is the right edge when working with LTR text, 118 /// and the left edge when working with RTL text. 119 pub right: T, 120 /// This can represent either the y-coordinate of the top edge, 121 /// or the amount of padding on the top side. 122 pub top: T, 123 /// This can represent either the y-coordinate of the bottom edge, 124 /// or the amount of padding on the bottom side. 125 pub bottom: T, 126 } 127 128 impl<U, T: Add<U>> Add<Rect<U>> for Rect<T> { 129 type Output = Rect<T::Output>; 130 add(self, rhs: Rect<U>) -> Self::Output131 fn add(self, rhs: Rect<U>) -> Self::Output { 132 Rect { 133 left: self.left + rhs.left, 134 right: self.right + rhs.right, 135 top: self.top + rhs.top, 136 bottom: self.bottom + rhs.bottom, 137 } 138 } 139 } 140 141 impl<T> Rect<T> { 142 /// Applies the function `f` to all four sides of the rect 143 /// 144 /// When applied to the left and right sides, the width is used 145 /// as the second parameter of `f`. 146 /// When applied to the top or bottom sides, the height is used instead. 147 #[cfg(any(feature = "flexbox", feature = "block_layout"))] zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R> where F: Fn(T, U) -> R, U: Copy,148 pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R> 149 where 150 F: Fn(T, U) -> R, 151 U: Copy, 152 { 153 Rect { 154 left: f(self.left, size.width), 155 right: f(self.right, size.width), 156 top: f(self.top, size.height), 157 bottom: f(self.bottom, size.height), 158 } 159 } 160 161 /// Applies the function `f` to the left, right, top, and bottom properties 162 /// 163 /// This is used to transform a `Rect<T>` into a `Rect<R>`. map<R, F>(self, f: F) -> Rect<R> where F: Fn(T) -> R,164 pub fn map<R, F>(self, f: F) -> Rect<R> 165 where 166 F: Fn(T) -> R, 167 { 168 Rect { left: f(self.left), right: f(self.right), top: f(self.top), bottom: f(self.bottom) } 169 } 170 171 /// Returns a `Line<T>` representing the left and right properties of the Rect horizontal_components(self) -> Line<T>172 pub fn horizontal_components(self) -> Line<T> { 173 Line { start: self.left, end: self.right } 174 } 175 176 /// Returns a `Line<T>` containing the top and bottom properties of the Rect vertical_components(self) -> Line<T>177 pub fn vertical_components(self) -> Line<T> { 178 Line { start: self.top, end: self.bottom } 179 } 180 } 181 182 impl<T, U> Rect<T> 183 where 184 T: Add<Output = U> + Copy + Clone, 185 { 186 /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect) 187 /// 188 /// This is typically used when computing total padding. 189 /// 190 /// **NOTE:** this is *not* the width of the rectangle. 191 #[inline(always)] horizontal_axis_sum(&self) -> U192 pub(crate) fn horizontal_axis_sum(&self) -> U { 193 self.left + self.right 194 } 195 196 /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect) 197 /// 198 /// This is typically used when computing total padding. 199 /// 200 /// **NOTE:** this is *not* the height of the rectangle. 201 #[inline(always)] vertical_axis_sum(&self) -> U202 pub(crate) fn vertical_axis_sum(&self) -> U { 203 self.top + self.bottom 204 } 205 206 /// Both horizontal_axis_sum and vertical_axis_sum as a Size<T> 207 /// 208 /// **NOTE:** this is *not* the width/height of the rectangle. 209 #[inline(always)] 210 #[allow(dead_code)] // Fixes spurious clippy warning: this function is used! sum_axes(&self) -> Size<U>211 pub(crate) fn sum_axes(&self) -> Size<U> { 212 Size { width: self.horizontal_axis_sum(), height: self.vertical_axis_sum() } 213 } 214 215 /// The sum of the two fields of the [`Rect`] representing the main axis. 216 /// 217 /// This is typically used when computing total padding. 218 /// 219 /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`]. 220 /// Otherwise, this is [`Rect::vertical`]. 221 #[cfg(feature = "flexbox")] main_axis_sum(&self, direction: FlexDirection) -> U222 pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> U { 223 if direction.is_row() { 224 self.horizontal_axis_sum() 225 } else { 226 self.vertical_axis_sum() 227 } 228 } 229 230 /// The sum of the two fields of the [`Rect`] representing the cross axis. 231 /// 232 /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`]. 233 /// Otherwise, this is [`Rect::horizontal`]. 234 #[cfg(feature = "flexbox")] cross_axis_sum(&self, direction: FlexDirection) -> U235 pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> U { 236 if direction.is_row() { 237 self.vertical_axis_sum() 238 } else { 239 self.horizontal_axis_sum() 240 } 241 } 242 } 243 244 impl<T> Rect<T> 245 where 246 T: Copy + Clone, 247 { 248 /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis 249 #[cfg(feature = "flexbox")] main_start(&self, direction: FlexDirection) -> T250 pub(crate) fn main_start(&self, direction: FlexDirection) -> T { 251 if direction.is_row() { 252 self.left 253 } else { 254 self.top 255 } 256 } 257 258 /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis 259 #[cfg(feature = "flexbox")] main_end(&self, direction: FlexDirection) -> T260 pub(crate) fn main_end(&self, direction: FlexDirection) -> T { 261 if direction.is_row() { 262 self.right 263 } else { 264 self.bottom 265 } 266 } 267 268 /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis 269 #[cfg(feature = "flexbox")] cross_start(&self, direction: FlexDirection) -> T270 pub(crate) fn cross_start(&self, direction: FlexDirection) -> T { 271 if direction.is_row() { 272 self.top 273 } else { 274 self.left 275 } 276 } 277 278 /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis 279 #[cfg(feature = "flexbox")] cross_end(&self, direction: FlexDirection) -> T280 pub(crate) fn cross_end(&self, direction: FlexDirection) -> T { 281 if direction.is_row() { 282 self.bottom 283 } else { 284 self.right 285 } 286 } 287 } 288 289 impl Rect<f32> { 290 /// Creates a new Rect with `0.0` as all parameters 291 pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 }; 292 293 /// Creates a new Rect 294 #[must_use] new(start: f32, end: f32, top: f32, bottom: f32) -> Self295 pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self { 296 Self { left: start, right: end, top, bottom } 297 } 298 } 299 300 /// An abstract "line". Represents any type that has a start and an end 301 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 302 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 303 #[cfg_attr(feature = "serde", serde(default))] 304 pub struct Line<T> { 305 /// The start position of a line 306 pub start: T, 307 /// The end position of a line 308 pub end: T, 309 } 310 311 impl<T> Line<T> { 312 /// Applies the function `f` to both the width and height 313 /// 314 /// This is used to transform a `Line<T>` into a `Line<R>`. map<R, F>(self, f: F) -> Line<R> where F: Fn(T) -> R,315 pub fn map<R, F>(self, f: F) -> Line<R> 316 where 317 F: Fn(T) -> R, 318 { 319 Line { start: f(self.start), end: f(self.end) } 320 } 321 } 322 323 impl Line<bool> { 324 /// A `Line<bool>` with both start and end set to `true` 325 pub const TRUE: Self = Line { start: true, end: true }; 326 /// A `Line<bool>` with both start and end set to `false` 327 pub const FALSE: Self = Line { start: false, end: false }; 328 } 329 330 impl<T: Add + Copy> Line<T> { 331 /// Adds the start and end values together and returns the result sum(&self) -> <T as Add>::Output332 pub fn sum(&self) -> <T as Add>::Output { 333 self.start + self.end 334 } 335 } 336 337 /// The width and height of a [`Rect`] 338 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] 339 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 340 pub struct Size<T> { 341 /// The x extent of the rectangle 342 pub width: T, 343 /// The y extent of the rectangle 344 pub height: T, 345 } 346 347 // Generic Add impl for Size<T> + Size<U> where T + U has an Add impl 348 impl<U, T: Add<U>> Add<Size<U>> for Size<T> { 349 type Output = Size<<T as Add<U>>::Output>; 350 add(self, rhs: Size<U>) -> Self::Output351 fn add(self, rhs: Size<U>) -> Self::Output { 352 Size { width: self.width + rhs.width, height: self.height + rhs.height } 353 } 354 } 355 356 // Generic Sub impl for Size<T> + Size<U> where T + U has an Sub impl 357 impl<U, T: Sub<U>> Sub<Size<U>> for Size<T> { 358 type Output = Size<<T as Sub<U>>::Output>; 359 sub(self, rhs: Size<U>) -> Self::Output360 fn sub(self, rhs: Size<U>) -> Self::Output { 361 Size { width: self.width - rhs.width, height: self.height - rhs.height } 362 } 363 } 364 365 // Note: we allow dead_code here as we want to provide a complete API of helpers that is symmetrical in all axes, 366 // but sometimes we only currently have a use for the helper in a single axis 367 #[allow(dead_code)] 368 impl<T> Size<T> { 369 /// Applies the function `f` to both the width and height 370 /// 371 /// This is used to transform a `Size<T>` into a `Size<R>`. map<R, F>(self, f: F) -> Size<R> where F: Fn(T) -> R,372 pub fn map<R, F>(self, f: F) -> Size<R> 373 where 374 F: Fn(T) -> R, 375 { 376 Size { width: f(self.width), height: f(self.height) } 377 } 378 379 /// Applies the function `f` to the width map_width<F>(self, f: F) -> Size<T> where F: Fn(T) -> T,380 pub fn map_width<F>(self, f: F) -> Size<T> 381 where 382 F: Fn(T) -> T, 383 { 384 Size { width: f(self.width), height: self.height } 385 } 386 387 /// Applies the function `f` to the height map_height<F>(self, f: F) -> Size<T> where F: Fn(T) -> T,388 pub fn map_height<F>(self, f: F) -> Size<T> 389 where 390 F: Fn(T) -> T, 391 { 392 Size { width: self.width, height: f(self.height) } 393 } 394 395 /// Applies the function `f` to both the width and height 396 /// of this value and another passed value zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret> where Func: Fn(T, Other) -> Ret,397 pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret> 398 where 399 Func: Fn(T, Other) -> Ret, 400 { 401 Size { width: f(self.width, other.width), height: f(self.height, other.height) } 402 } 403 404 /// Sets the extent of the main layout axis 405 /// 406 /// Whether this is the width or height depends on the `direction` provided 407 #[cfg(feature = "flexbox")] set_main(&mut self, direction: FlexDirection, value: T)408 pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) { 409 if direction.is_row() { 410 self.width = value 411 } else { 412 self.height = value 413 } 414 } 415 416 /// Sets the extent of the cross layout axis 417 /// 418 /// Whether this is the width or height depends on the `direction` provided 419 #[cfg(feature = "flexbox")] set_cross(&mut self, direction: FlexDirection, value: T)420 pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) { 421 if direction.is_row() { 422 self.height = value 423 } else { 424 self.width = value 425 } 426 } 427 428 /// Creates a new value of type Self with the main axis set to value provided 429 /// 430 /// Whether this is the width or height depends on the `direction` provided 431 #[cfg(feature = "flexbox")] with_main(self, direction: FlexDirection, value: T) -> Self432 pub(crate) fn with_main(self, direction: FlexDirection, value: T) -> Self { 433 let mut new = self; 434 if direction.is_row() { 435 new.width = value 436 } else { 437 new.height = value 438 } 439 new 440 } 441 442 /// Creates a new value of type Self with the cross axis set to value provided 443 /// 444 /// Whether this is the width or height depends on the `direction` provided 445 #[cfg(feature = "flexbox")] with_cross(self, direction: FlexDirection, value: T) -> Self446 pub(crate) fn with_cross(self, direction: FlexDirection, value: T) -> Self { 447 let mut new = self; 448 if direction.is_row() { 449 new.height = value 450 } else { 451 new.width = value 452 } 453 new 454 } 455 456 /// Creates a new value of type Self with the main axis modified by the callback provided 457 /// 458 /// Whether this is the width or height depends on the `direction` provided 459 #[cfg(feature = "flexbox")] map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self460 pub(crate) fn map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self { 461 let mut new = self; 462 if direction.is_row() { 463 new.width = mapper(new.width); 464 } else { 465 new.height = mapper(new.height); 466 } 467 new 468 } 469 470 /// Creates a new value of type Self with the cross axis modified by the callback provided 471 /// 472 /// Whether this is the width or height depends on the `direction` provided 473 #[cfg(feature = "flexbox")] map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self474 pub(crate) fn map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self { 475 let mut new = self; 476 if direction.is_row() { 477 new.height = mapper(new.height); 478 } else { 479 new.width = mapper(new.width); 480 } 481 new 482 } 483 484 /// Gets the extent of the main layout axis 485 /// 486 /// Whether this is the width or height depends on the `direction` provided 487 #[cfg(feature = "flexbox")] main(self, direction: FlexDirection) -> T488 pub(crate) fn main(self, direction: FlexDirection) -> T { 489 if direction.is_row() { 490 self.width 491 } else { 492 self.height 493 } 494 } 495 496 /// Gets the extent of the cross layout axis 497 /// 498 /// Whether this is the width or height depends on the `direction` provided 499 #[cfg(feature = "flexbox")] cross(self, direction: FlexDirection) -> T500 pub(crate) fn cross(self, direction: FlexDirection) -> T { 501 if direction.is_row() { 502 self.height 503 } else { 504 self.width 505 } 506 } 507 508 /// Gets the extent of the specified layout axis 509 /// Whether this is the width or height depends on the `GridAxis` provided 510 #[cfg(feature = "grid")] get(self, axis: AbstractAxis) -> T511 pub(crate) fn get(self, axis: AbstractAxis) -> T { 512 match axis { 513 AbstractAxis::Inline => self.width, 514 AbstractAxis::Block => self.height, 515 } 516 } 517 518 /// Sets the extent of the specified layout axis 519 /// Whether this is the width or height depends on the `GridAxis` provided 520 #[cfg(feature = "grid")] set(&mut self, axis: AbstractAxis, value: T)521 pub(crate) fn set(&mut self, axis: AbstractAxis, value: T) { 522 match axis { 523 AbstractAxis::Inline => self.width = value, 524 AbstractAxis::Block => self.height = value, 525 } 526 } 527 } 528 529 impl Size<f32> { 530 /// A [`Size`] with zero width and height 531 pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 }; 532 533 /// Applies f32_max to each component separately 534 #[inline(always)] f32_max(self, rhs: Size<f32>) -> Size<f32>535 pub fn f32_max(self, rhs: Size<f32>) -> Size<f32> { 536 Size { width: f32_max(self.width, rhs.width), height: f32_max(self.height, rhs.height) } 537 } 538 539 /// Applies f32_min to each component separately 540 #[inline(always)] f32_min(self, rhs: Size<f32>) -> Size<f32>541 pub fn f32_min(self, rhs: Size<f32>) -> Size<f32> { 542 Size { width: f32_min(self.width, rhs.width), height: f32_min(self.height, rhs.height) } 543 } 544 545 /// Return true if both width and height are greater than 0 else false 546 #[inline(always)] has_non_zero_area(self) -> bool547 pub fn has_non_zero_area(self) -> bool { 548 self.width > 0.0 && self.height > 0.0 549 } 550 } 551 552 impl Size<Option<f32>> { 553 /// A [`Size`] with `None` width and height 554 pub const NONE: Size<Option<f32>> = Self { width: None, height: None }; 555 556 /// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters 557 #[must_use] new(width: f32, height: f32) -> Self558 pub const fn new(width: f32, height: f32) -> Self { 559 Size { width: Some(width), height: Some(height) } 560 } 561 562 /// Creates a new [`Size<Option<f32>>`] with either the width or height set based on the provided `direction` 563 #[cfg(feature = "flexbox")] from_cross(direction: FlexDirection, value: Option<f32>) -> Self564 pub fn from_cross(direction: FlexDirection, value: Option<f32>) -> Self { 565 let mut new = Self::NONE; 566 if direction.is_row() { 567 new.height = value 568 } else { 569 new.width = value 570 } 571 new 572 } 573 574 /// Applies aspect_ratio (if one is supplied) to the Size: 575 /// - If width is `Some` but height is `None`, then height is computed from width and aspect_ratio 576 /// - If height is `Some` but width is `None`, then width is computed from height and aspect_ratio 577 /// 578 /// If aspect_ratio is `None` then this function simply returns self. maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>>579 pub fn maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>> { 580 match aspect_ratio { 581 Some(ratio) => match (self.width, self.height) { 582 (Some(width), None) => Size { width: Some(width), height: Some(width / ratio) }, 583 (None, Some(height)) => Size { width: Some(height * ratio), height: Some(height) }, 584 _ => self, 585 }, 586 None => self, 587 } 588 } 589 } 590 591 impl<T> Size<Option<T>> { 592 /// Performs Option::unwrap_or on each component separately unwrap_or(self, alt: Size<T>) -> Size<T>593 pub fn unwrap_or(self, alt: Size<T>) -> Size<T> { 594 Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) } 595 } 596 597 /// Performs Option::or on each component separately or(self, alt: Size<Option<T>>) -> Size<Option<T>>598 pub fn or(self, alt: Size<Option<T>>) -> Size<Option<T>> { 599 Size { width: self.width.or(alt.width), height: self.height.or(alt.height) } 600 } 601 602 /// Return true if both components are Some, else false. 603 #[inline(always)] both_axis_defined(&self) -> bool604 pub fn both_axis_defined(&self) -> bool { 605 self.width.is_some() && self.height.is_some() 606 } 607 } 608 609 impl Size<Dimension> { 610 /// Generates a [`Size<Dimension>`] using [`Dimension::Length`] values 611 #[must_use] from_lengths(width: f32, height: f32) -> Self612 pub const fn from_lengths(width: f32, height: f32) -> Self { 613 Size { width: Dimension::Length(width), height: Dimension::Length(height) } 614 } 615 616 /// Generates a [`Size<Dimension>`] using [`Dimension::Percent`] values 617 #[must_use] from_percent(width: f32, height: f32) -> Self618 pub const fn from_percent(width: f32, height: f32) -> Self { 619 Size { width: Dimension::Percent(width), height: Dimension::Percent(height) } 620 } 621 } 622 623 /// A 2-dimensional coordinate. 624 /// 625 /// When used in association with a [`Rect`], represents the top-left corner. 626 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] 627 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 628 pub struct Point<T> { 629 /// The x-coordinate 630 pub x: T, 631 /// The y-coordinate 632 pub y: T, 633 } 634 635 impl Point<f32> { 636 /// A [`Point`] with values (0,0), representing the origin 637 pub const ZERO: Self = Self { x: 0.0, y: 0.0 }; 638 } 639 640 impl Point<Option<f32>> { 641 /// A [`Point`] with values (None, None) 642 pub const NONE: Self = Self { x: None, y: None }; 643 } 644 645 // Generic Add impl for Point<T> + Point<U> where T + U has an Add impl 646 impl<U, T: Add<U>> Add<Point<U>> for Point<T> { 647 type Output = Point<<T as Add<U>>::Output>; 648 add(self, rhs: Point<U>) -> Self::Output649 fn add(self, rhs: Point<U>) -> Self::Output { 650 Point { x: self.x + rhs.x, y: self.y + rhs.y } 651 } 652 } 653 654 impl<T> Point<T> { 655 /// Applies the function `f` to both the x and y 656 /// 657 /// This is used to transform a `Point<T>` into a `Point<R>`. map<R, F>(self, f: F) -> Point<R> where F: Fn(T) -> R,658 pub fn map<R, F>(self, f: F) -> Point<R> 659 where 660 F: Fn(T) -> R, 661 { 662 Point { x: f(self.x), y: f(self.y) } 663 } 664 665 /// Gets the extent of the specified layout axis 666 /// Whether this is the width or height depends on the `GridAxis` provided 667 #[cfg(feature = "grid")] get(self, axis: AbstractAxis) -> T668 pub fn get(self, axis: AbstractAxis) -> T { 669 match axis { 670 AbstractAxis::Inline => self.x, 671 AbstractAxis::Block => self.y, 672 } 673 } 674 675 /// Swap x and y components transpose(self) -> Point<T>676 pub fn transpose(self) -> Point<T> { 677 Point { x: self.y, y: self.x } 678 } 679 680 /// Sets the extent of the specified layout axis 681 /// Whether this is the width or height depends on the `GridAxis` provided 682 #[cfg(feature = "grid")] set(&mut self, axis: AbstractAxis, value: T)683 pub fn set(&mut self, axis: AbstractAxis, value: T) { 684 match axis { 685 AbstractAxis::Inline => self.x = value, 686 AbstractAxis::Block => self.y = value, 687 } 688 } 689 690 /// Gets the component in the main layout axis 691 /// 692 /// Whether this is the x or y depends on the `direction` provided 693 #[cfg(feature = "flexbox")] main(self, direction: FlexDirection) -> T694 pub(crate) fn main(self, direction: FlexDirection) -> T { 695 if direction.is_row() { 696 self.x 697 } else { 698 self.y 699 } 700 } 701 702 /// Gets the component in the cross layout axis 703 /// 704 /// Whether this is the x or y depends on the `direction` provided 705 #[cfg(feature = "flexbox")] cross(self, direction: FlexDirection) -> T706 pub(crate) fn cross(self, direction: FlexDirection) -> T { 707 if direction.is_row() { 708 self.y 709 } else { 710 self.x 711 } 712 } 713 } 714 715 impl<T> From<Point<T>> for Size<T> { from(value: Point<T>) -> Self716 fn from(value: Point<T>) -> Self { 717 Size { width: value.x, height: value.y } 718 } 719 } 720 721 /// Generic struct which holds a "min" value and a "max" value 722 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 723 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 724 pub struct MinMax<Min, Max> { 725 /// The value representing the minimum 726 pub min: Min, 727 /// The value representing the maximum 728 pub max: Max, 729 } 730