730 lines
23 KiB
Rust
730 lines
23 KiB
Rust
//! Geometric primitives useful for layout
|
|
|
|
use crate::util::sys::f32_max;
|
|
use crate::{style::Dimension, util::sys::f32_min};
|
|
use core::ops::{Add, Sub};
|
|
|
|
#[cfg(feature = "flexbox")]
|
|
use crate::style::FlexDirection;
|
|
|
|
/// The simple absolute horizontal and vertical axis
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum AbsoluteAxis {
|
|
/// The horizontal axis
|
|
Horizontal,
|
|
/// The vertical axis
|
|
Vertical,
|
|
}
|
|
|
|
impl AbsoluteAxis {
|
|
/// Returns the other variant of the enum
|
|
#[inline]
|
|
pub const fn other_axis(&self) -> Self {
|
|
match *self {
|
|
AbsoluteAxis::Horizontal => AbsoluteAxis::Vertical,
|
|
AbsoluteAxis::Vertical => AbsoluteAxis::Horizontal,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Size<T> {
|
|
#[inline(always)]
|
|
/// Get either the width or height depending on the AbsoluteAxis passed in
|
|
pub fn get_abs(self, axis: AbsoluteAxis) -> T {
|
|
match axis {
|
|
AbsoluteAxis::Horizontal => self.width,
|
|
AbsoluteAxis::Vertical => self.height,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Add> Rect<T> {
|
|
#[inline(always)]
|
|
/// Get either the width or height depending on the AbsoluteAxis passed in
|
|
pub fn grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output {
|
|
match axis {
|
|
AbsoluteAxis::Horizontal => self.left + self.right,
|
|
AbsoluteAxis::Vertical => self.top + self.bottom,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The CSS abstract axis
|
|
/// <https://www.w3.org/TR/css-writing-modes-3/#abstract-axes>
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum AbstractAxis {
|
|
/// The axis in the inline dimension, i.e. the horizontal axis in horizontal writing modes and the vertical axis in vertical writing modes.
|
|
Inline,
|
|
/// The axis in the block dimension, i.e. the vertical axis in horizontal writing modes and the horizontal axis in vertical writing modes.
|
|
Block,
|
|
}
|
|
|
|
impl AbstractAxis {
|
|
/// Returns the other variant of the enum
|
|
#[inline]
|
|
pub fn other(&self) -> AbstractAxis {
|
|
match *self {
|
|
AbstractAxis::Inline => AbstractAxis::Block,
|
|
AbstractAxis::Block => AbstractAxis::Inline,
|
|
}
|
|
}
|
|
|
|
/// Convert an `AbstractAxis` into an `AbsoluteAxis` naively assuming that the Inline axis is Horizontal
|
|
/// This is currently always true, but will change if Taffy ever implements the `writing_mode` property
|
|
#[inline]
|
|
pub fn as_abs_naive(&self) -> AbsoluteAxis {
|
|
match self {
|
|
AbstractAxis::Inline => AbsoluteAxis::Horizontal,
|
|
AbstractAxis::Block => AbsoluteAxis::Vertical,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Container that holds an item in each absolute axis without specifying
|
|
/// what kind of item it is.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub(crate) struct InBothAbsAxis<T> {
|
|
/// The item in the horizontal axis
|
|
pub horizontal: T,
|
|
/// The item in the vertical axis
|
|
pub vertical: T,
|
|
}
|
|
|
|
impl<T: Copy> InBothAbsAxis<T> {
|
|
#[cfg(feature = "grid")]
|
|
/// Get the contained item based on the AbsoluteAxis passed
|
|
pub fn get(&self, axis: AbsoluteAxis) -> T {
|
|
match axis {
|
|
AbsoluteAxis::Horizontal => self.horizontal,
|
|
AbsoluteAxis::Vertical => self.vertical,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An axis-aligned UI rectangle
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct Rect<T> {
|
|
/// This can represent either the x-coordinate of the starting edge,
|
|
/// or the amount of padding on the starting side.
|
|
///
|
|
/// The starting edge is the left edge when working with LTR text,
|
|
/// and the right edge when working with RTL text.
|
|
pub left: T,
|
|
/// This can represent either the x-coordinate of the ending edge,
|
|
/// or the amount of padding on the ending side.
|
|
///
|
|
/// The ending edge is the right edge when working with LTR text,
|
|
/// and the left edge when working with RTL text.
|
|
pub right: T,
|
|
/// This can represent either the y-coordinate of the top edge,
|
|
/// or the amount of padding on the top side.
|
|
pub top: T,
|
|
/// This can represent either the y-coordinate of the bottom edge,
|
|
/// or the amount of padding on the bottom side.
|
|
pub bottom: T,
|
|
}
|
|
|
|
impl<U, T: Add<U>> Add<Rect<U>> for Rect<T> {
|
|
type Output = Rect<T::Output>;
|
|
|
|
fn add(self, rhs: Rect<U>) -> Self::Output {
|
|
Rect {
|
|
left: self.left + rhs.left,
|
|
right: self.right + rhs.right,
|
|
top: self.top + rhs.top,
|
|
bottom: self.bottom + rhs.bottom,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Rect<T> {
|
|
/// Applies the function `f` to all four sides of the rect
|
|
///
|
|
/// When applied to the left and right sides, the width is used
|
|
/// as the second parameter of `f`.
|
|
/// When applied to the top or bottom sides, the height is used instead.
|
|
#[cfg(any(feature = "flexbox", feature = "block_layout"))]
|
|
pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
|
|
where
|
|
F: Fn(T, U) -> R,
|
|
U: Copy,
|
|
{
|
|
Rect {
|
|
left: f(self.left, size.width),
|
|
right: f(self.right, size.width),
|
|
top: f(self.top, size.height),
|
|
bottom: f(self.bottom, size.height),
|
|
}
|
|
}
|
|
|
|
/// Applies the function `f` to the left, right, top, and bottom properties
|
|
///
|
|
/// This is used to transform a `Rect<T>` into a `Rect<R>`.
|
|
pub fn map<R, F>(self, f: F) -> Rect<R>
|
|
where
|
|
F: Fn(T) -> R,
|
|
{
|
|
Rect { left: f(self.left), right: f(self.right), top: f(self.top), bottom: f(self.bottom) }
|
|
}
|
|
|
|
/// Returns a `Line<T>` representing the left and right properties of the Rect
|
|
pub fn horizontal_components(self) -> Line<T> {
|
|
Line { start: self.left, end: self.right }
|
|
}
|
|
|
|
/// Returns a `Line<T>` containing the top and bottom properties of the Rect
|
|
pub fn vertical_components(self) -> Line<T> {
|
|
Line { start: self.top, end: self.bottom }
|
|
}
|
|
}
|
|
|
|
impl<T, U> Rect<T>
|
|
where
|
|
T: Add<Output = U> + Copy + Clone,
|
|
{
|
|
/// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
|
|
///
|
|
/// This is typically used when computing total padding.
|
|
///
|
|
/// **NOTE:** this is *not* the width of the rectangle.
|
|
#[inline(always)]
|
|
pub(crate) fn horizontal_axis_sum(&self) -> U {
|
|
self.left + self.right
|
|
}
|
|
|
|
/// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
|
|
///
|
|
/// This is typically used when computing total padding.
|
|
///
|
|
/// **NOTE:** this is *not* the height of the rectangle.
|
|
#[inline(always)]
|
|
pub(crate) fn vertical_axis_sum(&self) -> U {
|
|
self.top + self.bottom
|
|
}
|
|
|
|
/// Both horizontal_axis_sum and vertical_axis_sum as a Size<T>
|
|
///
|
|
/// **NOTE:** this is *not* the width/height of the rectangle.
|
|
#[inline(always)]
|
|
#[allow(dead_code)] // Fixes spurious clippy warning: this function is used!
|
|
pub(crate) fn sum_axes(&self) -> Size<U> {
|
|
Size { width: self.horizontal_axis_sum(), height: self.vertical_axis_sum() }
|
|
}
|
|
|
|
/// The sum of the two fields of the [`Rect`] representing the main axis.
|
|
///
|
|
/// This is typically used when computing total padding.
|
|
///
|
|
/// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
|
|
/// Otherwise, this is [`Rect::vertical`].
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> U {
|
|
if direction.is_row() {
|
|
self.horizontal_axis_sum()
|
|
} else {
|
|
self.vertical_axis_sum()
|
|
}
|
|
}
|
|
|
|
/// The sum of the two fields of the [`Rect`] representing the cross axis.
|
|
///
|
|
/// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
|
|
/// Otherwise, this is [`Rect::horizontal`].
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> U {
|
|
if direction.is_row() {
|
|
self.vertical_axis_sum()
|
|
} else {
|
|
self.horizontal_axis_sum()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Rect<T>
|
|
where
|
|
T: Copy + Clone,
|
|
{
|
|
/// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.left
|
|
} else {
|
|
self.top
|
|
}
|
|
}
|
|
|
|
/// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.right
|
|
} else {
|
|
self.bottom
|
|
}
|
|
}
|
|
|
|
/// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.top
|
|
} else {
|
|
self.left
|
|
}
|
|
}
|
|
|
|
/// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.bottom
|
|
} else {
|
|
self.right
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Rect<f32> {
|
|
/// Creates a new Rect with `0.0` as all parameters
|
|
pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 };
|
|
|
|
/// Creates a new Rect
|
|
#[must_use]
|
|
pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
|
|
Self { left: start, right: end, top, bottom }
|
|
}
|
|
}
|
|
|
|
/// An abstract "line". Represents any type that has a start and an end
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
#[cfg_attr(feature = "serde", serde(default))]
|
|
pub struct Line<T> {
|
|
/// The start position of a line
|
|
pub start: T,
|
|
/// The end position of a line
|
|
pub end: T,
|
|
}
|
|
|
|
impl<T> Line<T> {
|
|
/// Applies the function `f` to both the width and height
|
|
///
|
|
/// This is used to transform a `Line<T>` into a `Line<R>`.
|
|
pub fn map<R, F>(self, f: F) -> Line<R>
|
|
where
|
|
F: Fn(T) -> R,
|
|
{
|
|
Line { start: f(self.start), end: f(self.end) }
|
|
}
|
|
}
|
|
|
|
impl Line<bool> {
|
|
/// A `Line<bool>` with both start and end set to `true`
|
|
pub const TRUE: Self = Line { start: true, end: true };
|
|
/// A `Line<bool>` with both start and end set to `false`
|
|
pub const FALSE: Self = Line { start: false, end: false };
|
|
}
|
|
|
|
impl<T: Add + Copy> Line<T> {
|
|
/// Adds the start and end values together and returns the result
|
|
pub fn sum(&self) -> <T as Add>::Output {
|
|
self.start + self.end
|
|
}
|
|
}
|
|
|
|
/// The width and height of a [`Rect`]
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct Size<T> {
|
|
/// The x extent of the rectangle
|
|
pub width: T,
|
|
/// The y extent of the rectangle
|
|
pub height: T,
|
|
}
|
|
|
|
// Generic Add impl for Size<T> + Size<U> where T + U has an Add impl
|
|
impl<U, T: Add<U>> Add<Size<U>> for Size<T> {
|
|
type Output = Size<<T as Add<U>>::Output>;
|
|
|
|
fn add(self, rhs: Size<U>) -> Self::Output {
|
|
Size { width: self.width + rhs.width, height: self.height + rhs.height }
|
|
}
|
|
}
|
|
|
|
// Generic Sub impl for Size<T> + Size<U> where T + U has an Sub impl
|
|
impl<U, T: Sub<U>> Sub<Size<U>> for Size<T> {
|
|
type Output = Size<<T as Sub<U>>::Output>;
|
|
|
|
fn sub(self, rhs: Size<U>) -> Self::Output {
|
|
Size { width: self.width - rhs.width, height: self.height - rhs.height }
|
|
}
|
|
}
|
|
|
|
// Note: we allow dead_code here as we want to provide a complete API of helpers that is symmetrical in all axes,
|
|
// but sometimes we only currently have a use for the helper in a single axis
|
|
#[allow(dead_code)]
|
|
impl<T> Size<T> {
|
|
/// Applies the function `f` to both the width and height
|
|
///
|
|
/// This is used to transform a `Size<T>` into a `Size<R>`.
|
|
pub fn map<R, F>(self, f: F) -> Size<R>
|
|
where
|
|
F: Fn(T) -> R,
|
|
{
|
|
Size { width: f(self.width), height: f(self.height) }
|
|
}
|
|
|
|
/// Applies the function `f` to the width
|
|
pub fn map_width<F>(self, f: F) -> Size<T>
|
|
where
|
|
F: Fn(T) -> T,
|
|
{
|
|
Size { width: f(self.width), height: self.height }
|
|
}
|
|
|
|
/// Applies the function `f` to the height
|
|
pub fn map_height<F>(self, f: F) -> Size<T>
|
|
where
|
|
F: Fn(T) -> T,
|
|
{
|
|
Size { width: self.width, height: f(self.height) }
|
|
}
|
|
|
|
/// Applies the function `f` to both the width and height
|
|
/// of this value and another passed value
|
|
pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret>
|
|
where
|
|
Func: Fn(T, Other) -> Ret,
|
|
{
|
|
Size { width: f(self.width, other.width), height: f(self.height, other.height) }
|
|
}
|
|
|
|
/// Sets the extent of the main layout axis
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
|
|
if direction.is_row() {
|
|
self.width = value
|
|
} else {
|
|
self.height = value
|
|
}
|
|
}
|
|
|
|
/// Sets the extent of the cross layout axis
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
|
|
if direction.is_row() {
|
|
self.height = value
|
|
} else {
|
|
self.width = value
|
|
}
|
|
}
|
|
|
|
/// Creates a new value of type Self with the main axis set to value provided
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn with_main(self, direction: FlexDirection, value: T) -> Self {
|
|
let mut new = self;
|
|
if direction.is_row() {
|
|
new.width = value
|
|
} else {
|
|
new.height = value
|
|
}
|
|
new
|
|
}
|
|
|
|
/// Creates a new value of type Self with the cross axis set to value provided
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn with_cross(self, direction: FlexDirection, value: T) -> Self {
|
|
let mut new = self;
|
|
if direction.is_row() {
|
|
new.height = value
|
|
} else {
|
|
new.width = value
|
|
}
|
|
new
|
|
}
|
|
|
|
/// Creates a new value of type Self with the main axis modified by the callback provided
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
|
|
let mut new = self;
|
|
if direction.is_row() {
|
|
new.width = mapper(new.width);
|
|
} else {
|
|
new.height = mapper(new.height);
|
|
}
|
|
new
|
|
}
|
|
|
|
/// Creates a new value of type Self with the cross axis modified by the callback provided
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
|
|
let mut new = self;
|
|
if direction.is_row() {
|
|
new.height = mapper(new.height);
|
|
} else {
|
|
new.width = mapper(new.width);
|
|
}
|
|
new
|
|
}
|
|
|
|
/// Gets the extent of the main layout axis
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn main(self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.width
|
|
} else {
|
|
self.height
|
|
}
|
|
}
|
|
|
|
/// Gets the extent of the cross layout axis
|
|
///
|
|
/// Whether this is the width or height depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn cross(self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.height
|
|
} else {
|
|
self.width
|
|
}
|
|
}
|
|
|
|
/// Gets the extent of the specified layout axis
|
|
/// Whether this is the width or height depends on the `GridAxis` provided
|
|
#[cfg(feature = "grid")]
|
|
pub(crate) fn get(self, axis: AbstractAxis) -> T {
|
|
match axis {
|
|
AbstractAxis::Inline => self.width,
|
|
AbstractAxis::Block => self.height,
|
|
}
|
|
}
|
|
|
|
/// Sets the extent of the specified layout axis
|
|
/// Whether this is the width or height depends on the `GridAxis` provided
|
|
#[cfg(feature = "grid")]
|
|
pub(crate) fn set(&mut self, axis: AbstractAxis, value: T) {
|
|
match axis {
|
|
AbstractAxis::Inline => self.width = value,
|
|
AbstractAxis::Block => self.height = value,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Size<f32> {
|
|
/// A [`Size`] with zero width and height
|
|
pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
|
|
|
|
/// Applies f32_max to each component separately
|
|
#[inline(always)]
|
|
pub fn f32_max(self, rhs: Size<f32>) -> Size<f32> {
|
|
Size { width: f32_max(self.width, rhs.width), height: f32_max(self.height, rhs.height) }
|
|
}
|
|
|
|
/// Applies f32_min to each component separately
|
|
#[inline(always)]
|
|
pub fn f32_min(self, rhs: Size<f32>) -> Size<f32> {
|
|
Size { width: f32_min(self.width, rhs.width), height: f32_min(self.height, rhs.height) }
|
|
}
|
|
|
|
/// Return true if both width and height are greater than 0 else false
|
|
#[inline(always)]
|
|
pub fn has_non_zero_area(self) -> bool {
|
|
self.width > 0.0 && self.height > 0.0
|
|
}
|
|
}
|
|
|
|
impl Size<Option<f32>> {
|
|
/// A [`Size`] with `None` width and height
|
|
pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
|
|
|
|
/// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters
|
|
#[must_use]
|
|
pub const fn new(width: f32, height: f32) -> Self {
|
|
Size { width: Some(width), height: Some(height) }
|
|
}
|
|
|
|
/// Creates a new [`Size<Option<f32>>`] with either the width or height set based on the provided `direction`
|
|
#[cfg(feature = "flexbox")]
|
|
pub fn from_cross(direction: FlexDirection, value: Option<f32>) -> Self {
|
|
let mut new = Self::NONE;
|
|
if direction.is_row() {
|
|
new.height = value
|
|
} else {
|
|
new.width = value
|
|
}
|
|
new
|
|
}
|
|
|
|
/// Applies aspect_ratio (if one is supplied) to the Size:
|
|
/// - If width is `Some` but height is `None`, then height is computed from width and aspect_ratio
|
|
/// - If height is `Some` but width is `None`, then width is computed from height and aspect_ratio
|
|
///
|
|
/// If aspect_ratio is `None` then this function simply returns self.
|
|
pub fn maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>> {
|
|
match aspect_ratio {
|
|
Some(ratio) => match (self.width, self.height) {
|
|
(Some(width), None) => Size { width: Some(width), height: Some(width / ratio) },
|
|
(None, Some(height)) => Size { width: Some(height * ratio), height: Some(height) },
|
|
_ => self,
|
|
},
|
|
None => self,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Size<Option<T>> {
|
|
/// Performs Option::unwrap_or on each component separately
|
|
pub fn unwrap_or(self, alt: Size<T>) -> Size<T> {
|
|
Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) }
|
|
}
|
|
|
|
/// Performs Option::or on each component separately
|
|
pub fn or(self, alt: Size<Option<T>>) -> Size<Option<T>> {
|
|
Size { width: self.width.or(alt.width), height: self.height.or(alt.height) }
|
|
}
|
|
|
|
/// Return true if both components are Some, else false.
|
|
#[inline(always)]
|
|
pub fn both_axis_defined(&self) -> bool {
|
|
self.width.is_some() && self.height.is_some()
|
|
}
|
|
}
|
|
|
|
impl Size<Dimension> {
|
|
/// Generates a [`Size<Dimension>`] using [`Dimension::Length`] values
|
|
#[must_use]
|
|
pub const fn from_lengths(width: f32, height: f32) -> Self {
|
|
Size { width: Dimension::Length(width), height: Dimension::Length(height) }
|
|
}
|
|
|
|
/// Generates a [`Size<Dimension>`] using [`Dimension::Percent`] values
|
|
#[must_use]
|
|
pub const fn from_percent(width: f32, height: f32) -> Self {
|
|
Size { width: Dimension::Percent(width), height: Dimension::Percent(height) }
|
|
}
|
|
}
|
|
|
|
/// A 2-dimensional coordinate.
|
|
///
|
|
/// When used in association with a [`Rect`], represents the top-left corner.
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct Point<T> {
|
|
/// The x-coordinate
|
|
pub x: T,
|
|
/// The y-coordinate
|
|
pub y: T,
|
|
}
|
|
|
|
impl Point<f32> {
|
|
/// A [`Point`] with values (0,0), representing the origin
|
|
pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
|
|
}
|
|
|
|
impl Point<Option<f32>> {
|
|
/// A [`Point`] with values (None, None)
|
|
pub const NONE: Self = Self { x: None, y: None };
|
|
}
|
|
|
|
// Generic Add impl for Point<T> + Point<U> where T + U has an Add impl
|
|
impl<U, T: Add<U>> Add<Point<U>> for Point<T> {
|
|
type Output = Point<<T as Add<U>>::Output>;
|
|
|
|
fn add(self, rhs: Point<U>) -> Self::Output {
|
|
Point { x: self.x + rhs.x, y: self.y + rhs.y }
|
|
}
|
|
}
|
|
|
|
impl<T> Point<T> {
|
|
/// Applies the function `f` to both the x and y
|
|
///
|
|
/// This is used to transform a `Point<T>` into a `Point<R>`.
|
|
pub fn map<R, F>(self, f: F) -> Point<R>
|
|
where
|
|
F: Fn(T) -> R,
|
|
{
|
|
Point { x: f(self.x), y: f(self.y) }
|
|
}
|
|
|
|
/// Gets the extent of the specified layout axis
|
|
/// Whether this is the width or height depends on the `GridAxis` provided
|
|
#[cfg(feature = "grid")]
|
|
pub fn get(self, axis: AbstractAxis) -> T {
|
|
match axis {
|
|
AbstractAxis::Inline => self.x,
|
|
AbstractAxis::Block => self.y,
|
|
}
|
|
}
|
|
|
|
/// Swap x and y components
|
|
pub fn transpose(self) -> Point<T> {
|
|
Point { x: self.y, y: self.x }
|
|
}
|
|
|
|
/// Sets the extent of the specified layout axis
|
|
/// Whether this is the width or height depends on the `GridAxis` provided
|
|
#[cfg(feature = "grid")]
|
|
pub fn set(&mut self, axis: AbstractAxis, value: T) {
|
|
match axis {
|
|
AbstractAxis::Inline => self.x = value,
|
|
AbstractAxis::Block => self.y = value,
|
|
}
|
|
}
|
|
|
|
/// Gets the component in the main layout axis
|
|
///
|
|
/// Whether this is the x or y depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn main(self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.x
|
|
} else {
|
|
self.y
|
|
}
|
|
}
|
|
|
|
/// Gets the component in the cross layout axis
|
|
///
|
|
/// Whether this is the x or y depends on the `direction` provided
|
|
#[cfg(feature = "flexbox")]
|
|
pub(crate) fn cross(self, direction: FlexDirection) -> T {
|
|
if direction.is_row() {
|
|
self.y
|
|
} else {
|
|
self.x
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> From<Point<T>> for Size<T> {
|
|
fn from(value: Point<T>) -> Self {
|
|
Size { width: value.x, height: value.y }
|
|
}
|
|
}
|
|
|
|
/// Generic struct which holds a "min" value and a "max" value
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct MinMax<Min, Max> {
|
|
/// The value representing the minimum
|
|
pub min: Min,
|
|
/// The value representing the maximum
|
|
pub max: Max,
|
|
}
|