typst_library/layout/
fr.rs

1use std::fmt::{self, Debug, Formatter};
2use std::iter::Sum;
3use std::ops::{Add, Div, Mul, Neg};
4
5use ecow::EcoString;
6use typst_utils::{Numeric, Scalar};
7
8use crate::foundations::{Repr, repr, ty};
9use crate::layout::Abs;
10
11/// Defines how the remaining space in a layout is distributed.
12///
13/// Each fractionally sized element gets space based on the ratio of its
14/// fraction to the sum of all fractions.
15///
16/// For more details, also see the [h] and [v] functions and the
17/// [grid function]($grid).
18///
19/// # Example
20/// ```example
21/// Left #h(1fr) Left-ish #h(2fr) Right
22/// ```
23#[ty(cast, name = "fraction")]
24#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
25pub struct Fr(Scalar);
26
27impl Fr {
28    /// Takes up zero space: `0fr`.
29    pub const fn zero() -> Self {
30        Self(Scalar::ZERO)
31    }
32
33    /// Takes up as much space as all other items with this fraction: `1fr`.
34    pub const fn one() -> Self {
35        Self(Scalar::ONE)
36    }
37
38    /// Create a new fraction.
39    pub const fn new(ratio: f64) -> Self {
40        Self(Scalar::new(ratio))
41    }
42
43    /// Get the underlying number.
44    pub const fn get(self) -> f64 {
45        (self.0).get()
46    }
47
48    /// The absolute value of this fraction.
49    pub fn abs(self) -> Self {
50        Self::new(self.get().abs())
51    }
52
53    /// Determine this fraction's share in the remaining space.
54    pub fn share(self, total: Self, remaining: Abs) -> Abs {
55        let ratio = self / total;
56        if ratio.is_finite() && remaining.is_finite() {
57            (ratio * remaining).max(Abs::zero())
58        } else {
59            Abs::zero()
60        }
61    }
62}
63
64impl Numeric for Fr {
65    fn zero() -> Self {
66        Self::zero()
67    }
68
69    fn is_finite(self) -> bool {
70        self.0.is_finite()
71    }
72}
73
74impl Debug for Fr {
75    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
76        write!(f, "{:?}fr", self.get())
77    }
78}
79
80impl Repr for Fr {
81    fn repr(&self) -> EcoString {
82        repr::format_float_with_unit(self.get(), "fr")
83    }
84}
85
86impl Neg for Fr {
87    type Output = Self;
88
89    fn neg(self) -> Self {
90        Self(-self.0)
91    }
92}
93
94impl Add for Fr {
95    type Output = Self;
96
97    fn add(self, other: Self) -> Self {
98        Self(self.0 + other.0)
99    }
100}
101
102typst_utils::sub_impl!(Fr - Fr -> Fr);
103
104impl Mul<f64> for Fr {
105    type Output = Self;
106
107    fn mul(self, other: f64) -> Self {
108        Self(self.0 * other)
109    }
110}
111
112impl Mul<Fr> for f64 {
113    type Output = Fr;
114
115    fn mul(self, other: Fr) -> Fr {
116        other * self
117    }
118}
119
120impl Div for Fr {
121    type Output = f64;
122
123    fn div(self, other: Self) -> f64 {
124        self.get() / other.get()
125    }
126}
127
128impl Div<f64> for Fr {
129    type Output = Self;
130
131    fn div(self, other: f64) -> Self {
132        Self(self.0 / other)
133    }
134}
135
136typst_utils::assign_impl!(Fr += Fr);
137typst_utils::assign_impl!(Fr -= Fr);
138typst_utils::assign_impl!(Fr *= f64);
139typst_utils::assign_impl!(Fr /= f64);
140
141impl Sum for Fr {
142    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
143        Self(iter.map(|s| s.0).sum())
144    }
145}