Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/units.rs
1use derive_more::{AddAssign, SubAssign};
2use ordered_float::OrderedFloat;
3use serde::{Deserialize, Serialize};
4use std::fmt::{Display, Formatter};
5use std::ops::Neg;
6 
7/// Newtype representing a position in line coordinates. See `to_pixel` to convert to pixel
8/// coordinates.
9#[derive(
10 Clone,
11 Copy,
12 Debug,
13 Default,
14 Eq,
15 PartialEq,
16 Ord,
17 PartialOrd,
18 AddAssign,
19 SubAssign,
20 num_derive::FromPrimitive,
21 num_derive::ToPrimitive,
22 num_derive::NumCast,
23 num_derive::NumOps,
24 num_derive::Zero,
25 num_derive::One,
26 num_derive::Num,
27 num_derive::Float,
28)]
29pub struct Lines(OrderedFloat<f64>);
30 
31impl Lines {
32 /// The epsilon value used for approximate equality checks.
33 const APPROX_EQ_EPSILON: f64 = 0.000001;
34 
35 pub const fn new(lines: f64) -> Self {
36 Lines(OrderedFloat(lines))
37 }
38 
39 pub fn to_pixels(self, line_height: impl Into<Pixels>) -> Pixels {
40 let line_height = line_height.into();
41 Pixels(self.as_f64() as f32 * line_height.0)
42 }
43 
44 pub const fn zero() -> Self {
45 Self::new(0.)
46 }
47 
48 pub fn fract(&self) -> Lines {
49 self.0.fract().into_lines()
50 }
51 
52 pub fn as_f64(&self) -> f64 {
53 self.0 .0
54 }
55 
56 pub fn max(self, other: Self) -> Self {
57 Self(self.0.max(other.0))
58 }
59 
60 pub fn min(self, other: Self) -> Self {
61 Self(self.0.min(other.0))
62 }
63}
64 
65impl Neg for Lines {
66 type Output = Self;
67 
68 fn neg(self) -> Self::Output {
69 Self(self.0.neg())
70 }
71}
72 
73#[cfg(any(test, feature = "test-util"))]
74impl std::ops::Add<f64> for Lines {
75 type Output = Self;
76 
77 fn add(self, rhs: f64) -> Self::Output {
78 Self(self.0 + rhs)
79 }
80}
81 
82#[cfg(any(test, feature = "test-util"))]
83impl std::ops::Sub<f64> for Lines {
84 type Output = Self;
85 
86 fn sub(self, rhs: f64) -> Self::Output {
87 Self(self.0 - rhs)
88 }
89}
90 
91impl Display for Lines {
92 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93 write!(f, "{}", self.0)
94 }
95}
96 
97impl float_cmp::ApproxEq for Lines {
98 type Margin = float_cmp::F64Margin;
99 
100 fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
101 let margin: Self::Margin = margin.into();
102 let margin = Self::Margin {
103 // Make sure we use an epsilon value that's at least APPROX_EQ_EPSILON.
104 epsilon: margin.epsilon.max(Self::APPROX_EQ_EPSILON),
105 ulps: margin.ulps,
106 };
107 self.as_f64().approx_eq(other.as_f64(), margin)
108 }
109}
110 
111/// Newtype representing a position in pixel coordinates. See `to_lines` to convert to line
112/// coordinates.
113#[derive(
114 Clone,
115 Copy,
116 Debug,
117 Default,
118 PartialOrd,
119 PartialEq,
120 AddAssign,
121 SubAssign,
122 Serialize,
123 Deserialize,
124 num_derive::FromPrimitive,
125 num_derive::ToPrimitive,
126 num_derive::NumCast,
127 num_derive::NumOps,
128 num_derive::Zero,
129 num_derive::One,
130 num_derive::Num,
131 num_derive::Float,
132)]
133#[cfg_attr(feature = "schema_gen", derive(schemars::JsonSchema))]
134#[cfg_attr(feature = "schema_gen", schemars(description = "A value in pixels."))]
135pub struct Pixels(f32);
136 
137impl Pixels {
138 pub const fn new(pixels: f32) -> Self {
139 Pixels(pixels)
140 }
141 
142 pub fn to_lines(self, line_height: Pixels) -> Lines {
143 Lines(OrderedFloat(self.0 as f64 / line_height.0 as f64))
144 }
145 
146 pub fn fract(&self) -> Pixels {
147 self.0.fract().into_pixels()
148 }
149 
150 pub fn zero() -> Self {
151 Pixels(0.)
152 }
153 
154 pub fn as_f32(&self) -> f32 {
155 self.0
156 }
157 
158 pub fn max(self, other: Self) -> Self {
159 Self(self.0.max(other.0))
160 }
161 
162 pub fn min(self, other: Self) -> Self {
163 Self(self.0.min(other.0))
164 }
165}
166 
167impl Neg for Pixels {
168 type Output = Self;
169 
170 fn neg(self) -> Self::Output {
171 Self(self.0.neg())
172 }
173}
174 
175impl Display for Pixels {
176 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
177 self.0.fmt(f)
178 }
179}
180 
181impl float_cmp::ApproxEq for Pixels {
182 type Margin = float_cmp::F32Margin;
183 
184 fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
185 self.as_f32().approx_eq(other.as_f32(), margin)
186 }
187}
188 
189/// Trait to convert an arbitrary type to `Pixels`.
190pub trait IntoPixels {
191 fn into_pixels(self) -> Pixels;
192}
193 
194/// Trait to convert an arbitrary type to `Lines`.
195pub trait IntoLines {
196 fn into_lines(self) -> Lines;
197}
198 
199impl IntoLines for Lines {
200 fn into_lines(self) -> Lines {
201 self
202 }
203}
204 
205macro_rules! impl_into_pixels {
206 ($($t:ident)*) => ($(impl IntoPixels for $t {
207 fn into_pixels(self) -> Pixels {
208 Pixels(self as f32)
209 }
210 })*)
211}
212 
213macro_rules! impl_into_lines {
214 ($($t:ident)*) => ($(impl IntoLines for $t {
215 fn into_lines(self) -> Lines {
216 Lines(OrderedFloat(self as f64))
217 }
218 })*)
219}
220 
221impl_into_pixels! { usize f32 f64 }
222impl_into_lines! { usize i32 u32 u64 f32 f64 }
223