zng_layout/unit/
grid.rs

1use std::{fmt, mem, ops};
2
3use zng_var::{animation::Transitionable, impl_from_and_into_var};
4
5use crate::unit::{LengthCompositeParser, ParseCompositeError};
6
7use super::{Factor, Factor2d, FactorPercent, Layout1d, LayoutMask, Length, Px, PxVector, impl_length_comp_conversions};
8
9/// Spacing in-between grid cells in [`Length`] units.
10#[derive(Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
11pub struct GridSpacing {
12    /// Spacing in-between columns, in length units.
13    pub column: Length,
14    /// Spacing in-between rows, in length units.
15    pub row: Length,
16}
17impl GridSpacing {
18    /// New column, row from any [`Length`] unit..
19    pub fn new<C: Into<Length>, R: Into<Length>>(column: C, row: R) -> Self {
20        GridSpacing {
21            column: column.into(),
22            row: row.into(),
23        }
24    }
25
26    /// Same spacing for both columns and rows.
27    pub fn new_all<S: Into<Length>>(same: S) -> Self {
28        let same = same.into();
29        GridSpacing {
30            column: same.clone(),
31            row: same,
32        }
33    }
34}
35impl super::Layout2d for GridSpacing {
36    type Px = PxGridSpacing;
37
38    fn layout_dft(&self, default: Self::Px) -> Self::Px {
39        PxGridSpacing {
40            column: self.column.layout_dft_x(default.column),
41            row: self.row.layout_dft_y(default.row),
42        }
43    }
44
45    fn affect_mask(&self) -> LayoutMask {
46        self.column.affect_mask() | self.row.affect_mask()
47    }
48}
49impl_length_comp_conversions! {
50    fn from(column: C, row: R) -> GridSpacing {
51        GridSpacing::new(column, row)
52    }
53}
54impl_from_and_into_var! {
55    /// Same spacing for both columns and rows.
56    fn from(all: Length) -> GridSpacing {
57        GridSpacing::new_all(all)
58    }
59
60    /// Column and row equal relative length.
61    fn from(percent: FactorPercent) -> GridSpacing {
62        GridSpacing::new_all(percent)
63    }
64    /// Column and row equal relative length.
65    fn from(norm: Factor) -> GridSpacing {
66        GridSpacing::new_all(norm)
67    }
68
69    /// Column and row equal exact length.
70    fn from(f: f32) -> GridSpacing {
71        GridSpacing::new_all(f)
72    }
73    /// Column and row equal exact length.
74    fn from(i: i32) -> GridSpacing {
75        GridSpacing::new_all(i)
76    }
77
78    /// Column and row in device pixel length.
79    fn from(spacing: PxGridSpacing) -> GridSpacing {
80        GridSpacing::new(spacing.column, spacing.row)
81    }
82}
83impl fmt::Debug for GridSpacing {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        if f.alternate() {
86            f.debug_struct("GridSpacing")
87                .field("column", &self.column)
88                .field("row", &self.row)
89                .finish()
90        } else if self.column == self.row {
91            write!(f, "{:.p$?}", self.column, p = f.precision().unwrap_or(0))
92        } else {
93            write!(f, "({:.p$?}, {:.p$?})", self.column, self.row, p = f.precision().unwrap_or(0))
94        }
95    }
96}
97impl fmt::Display for GridSpacing {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        if self.column == self.row {
100            write!(f, "{:.p$}", self.column, p = f.precision().unwrap_or(0))
101        } else {
102            write!(f, "({:.p$}, {:.p$})", self.column, self.row, p = f.precision().unwrap_or(0))
103        }
104    }
105}
106impl std::str::FromStr for GridSpacing {
107    type Err = ParseCompositeError;
108
109    fn from_str(s: &str) -> Result<Self, Self::Err> {
110        let mut parser = LengthCompositeParser::new(s)?;
111        let a = parser.next()?;
112        if parser.has_ended() {
113            return Ok(Self::new_all(a));
114        }
115        let b = parser.expect_last()?;
116        Ok(Self::new(a, b))
117    }
118}
119impl<S: Into<Factor2d>> ops::Mul<S> for GridSpacing {
120    type Output = Self;
121
122    fn mul(self, rhs: S) -> Self {
123        let fct = rhs.into();
124
125        GridSpacing {
126            column: self.column * fct.x,
127            row: self.row * fct.y,
128        }
129    }
130}
131impl<S: Into<Factor2d>> ops::Mul<S> for &GridSpacing {
132    type Output = GridSpacing;
133
134    fn mul(self, rhs: S) -> Self::Output {
135        self.clone() * rhs
136    }
137}
138impl<S: Into<Factor2d>> ops::MulAssign<S> for GridSpacing {
139    fn mul_assign(&mut self, rhs: S) {
140        let column = mem::take(&mut self.column);
141        let row = mem::take(&mut self.row);
142        let fct = rhs.into();
143
144        self.column = column * fct.x;
145        self.row = row * fct.y;
146    }
147}
148impl<S: Into<Factor2d>> ops::Div<S> for GridSpacing {
149    type Output = Self;
150
151    fn div(self, rhs: S) -> Self {
152        let fct = rhs.into();
153
154        GridSpacing {
155            column: self.column / fct.x,
156            row: self.row / fct.y,
157        }
158    }
159}
160impl<S: Into<Factor2d>> ops::Div<S> for &GridSpacing {
161    type Output = GridSpacing;
162
163    fn div(self, rhs: S) -> Self::Output {
164        self.clone() / rhs
165    }
166}
167impl<S: Into<Factor2d>> ops::DivAssign<S> for GridSpacing {
168    fn div_assign(&mut self, rhs: S) {
169        let column = mem::take(&mut self.column);
170        let row = mem::take(&mut self.row);
171        let fct = rhs.into();
172
173        self.column = column / fct.x;
174        self.row = row / fct.y;
175    }
176}
177
178/// Computed [`GridSpacing`].
179#[derive(Clone, Default, Copy, Debug, PartialEq, Eq, Hash)]
180pub struct PxGridSpacing {
181    /// Spacing in-between columns, in layout pixels.
182    pub column: Px,
183    /// Spacing in-between rows, in layout pixels.
184    pub row: Px,
185}
186impl PxGridSpacing {
187    /// New grid spacing
188    pub fn new(column: Px, row: Px) -> Self {
189        Self { column, row }
190    }
191    /// Zero spacing.
192    pub fn zero() -> Self {
193        PxGridSpacing { column: Px(0), row: Px(0) }
194    }
195
196    /// Convert to vector.
197    pub fn to_vector(self) -> PxVector {
198        PxVector::new(self.column, self.row)
199    }
200}
201impl ops::Add for GridSpacing {
202    type Output = Self;
203
204    fn add(mut self, rhs: Self) -> Self {
205        self += rhs;
206        self
207    }
208}
209impl ops::AddAssign for GridSpacing {
210    fn add_assign(&mut self, rhs: Self) {
211        self.column += rhs.column;
212        self.row += rhs.row;
213    }
214}
215impl ops::Sub for GridSpacing {
216    type Output = Self;
217
218    fn sub(mut self, rhs: Self) -> Self {
219        self -= rhs;
220        self
221    }
222}
223impl ops::SubAssign for GridSpacing {
224    fn sub_assign(&mut self, rhs: Self) {
225        self.column -= rhs.column;
226        self.row -= rhs.row;
227    }
228}
229impl From<PxGridSpacing> for PxVector {
230    fn from(s: PxGridSpacing) -> Self {
231        s.to_vector()
232    }
233}
234impl From<PxVector> for PxGridSpacing {
235    fn from(s: PxVector) -> Self {
236        PxGridSpacing { column: s.x, row: s.y }
237    }
238}