use super::Fixed;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Dimension {
Px(Fixed),
Percent(Fixed),
#[default]
Auto,
Content,
}
impl Dimension {
#[inline(always)]
pub fn resolve(self, parent_size: Fixed) -> Option<Fixed> {
match self {
Self::Px(v) => Some(v),
Self::Percent(pct) => Some(parent_size * pct / Fixed::from_int(100)),
Self::Auto | Self::Content => None,
}
}
#[inline]
pub fn resolve_or(self, parent_size: Fixed, default: Fixed) -> Fixed {
self.resolve(parent_size).unwrap_or(default)
}
#[inline]
pub const fn px(v: i32) -> Self {
Self::Px(Fixed::from_int(v))
}
#[inline]
pub const fn percent(v: i32) -> Self {
Self::Percent(Fixed::from_int(v))
}
}
impl From<i32> for Dimension {
#[inline]
fn from(v: i32) -> Self {
Self::Px(Fixed::from_int(v))
}
}
impl From<u16> for Dimension {
#[inline]
fn from(v: u16) -> Self {
Self::Px(v.into())
}
}
impl From<Fixed> for Dimension {
#[inline]
fn from(v: Fixed) -> Self {
Self::Px(v)
}
}
impl core::ops::Add for Dimension {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Px(a), Self::Px(b)) => Self::Px(a + b),
(Self::Percent(a), Self::Percent(b)) => Self::Percent(a + b),
_ => self,
}
}
}
impl core::ops::Sub for Dimension {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
match (self, rhs) {
(Self::Px(a), Self::Px(b)) => Self::Px(a - b),
(Self::Percent(a), Self::Percent(b)) => Self::Percent(a - b),
_ => self,
}
}
}
impl core::ops::Mul<Fixed> for Dimension {
type Output = Self;
#[inline]
fn mul(self, rhs: Fixed) -> Self {
match self {
Self::Px(v) => Self::Px(v * rhs),
Self::Percent(v) => Self::Percent(v * rhs),
other => other,
}
}
}
impl core::ops::Div<Fixed> for Dimension {
type Output = Self;
#[inline]
fn div(self, rhs: Fixed) -> Self {
match self {
Self::Px(v) => Self::Px(v / rhs),
Self::Percent(v) => Self::Percent(v / rhs),
other => other,
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct DimPoint {
pub x: Dimension,
pub y: Dimension,
}
impl From<crate::types::Point> for DimPoint {
fn from(p: crate::types::Point) -> Self {
Self {
x: Dimension::Px(p.x),
y: Dimension::Px(p.y),
}
}
}
impl<X: Into<Dimension>, Y: Into<Dimension>> From<(X, Y)> for DimPoint {
fn from((x, y): (X, Y)) -> Self {
Self {
x: x.into(),
y: y.into(),
}
}
}
impl DimPoint {
pub const ZERO: Self = Self {
x: Dimension::Px(Fixed::ZERO),
y: Dimension::Px(Fixed::ZERO),
};
pub const CENTER: Self = Self {
x: Dimension::Percent(Fixed::from_int(50)),
y: Dimension::Percent(Fixed::from_int(50)),
};
pub const fn px(x: i32, y: i32) -> Self {
Self {
x: Dimension::px(x),
y: Dimension::px(y),
}
}
pub const fn percent(x: i32, y: i32) -> Self {
Self {
x: Dimension::percent(x),
y: Dimension::percent(y),
}
}
pub fn resolve(self, parent_w: Fixed, parent_h: Fixed) -> (Fixed, Fixed) {
(
self.x.resolve_or(parent_w, Fixed::ZERO),
self.y.resolve_or(parent_h, Fixed::ZERO),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dim_point_centre_resolves_to_half_rect() {
let (x, y) = DimPoint::CENTER.resolve(Fixed::from_int(100), Fixed::from_int(40));
assert_eq!(x.to_int(), 50);
assert_eq!(y.to_int(), 20);
}
#[test]
fn dim_point_px_passthrough() {
let (x, y) = DimPoint::px(30, 10).resolve(Fixed::from_int(100), Fixed::from_int(100));
assert_eq!(x.to_int(), 30);
assert_eq!(y.to_int(), 10);
}
#[test]
fn resolve_px() {
let d = Dimension::Px(Fixed::from_int(50));
assert_eq!(d.resolve(Fixed::from_int(200)), Some(Fixed::from_int(50)));
}
#[test]
fn resolve_percent() {
let d = Dimension::Percent(Fixed::from_int(50));
let result = d.resolve(Fixed::from_int(200)).unwrap();
assert_eq!(result.to_int(), 100);
}
#[test]
fn resolve_percent_large_parent() {
for parent in [640, 1280, 1920, 4096, 8192] {
let got = Dimension::percent(100)
.resolve(Fixed::from_int(parent))
.unwrap();
assert_eq!(got.to_int(), parent, "Percent(100) on parent={parent}");
}
}
#[test]
fn resolve_percent_fraction_large_parent() {
let d = Dimension::percent(33);
let got = d.resolve(Fixed::from_int(1200)).unwrap();
assert_eq!(got.to_int(), 396);
}
#[test]
fn resolve_auto() {
assert_eq!(Dimension::Auto.resolve(Fixed::from_int(200)), None);
}
#[test]
fn from_i32() {
let d: Dimension = 100.into();
assert_eq!(d, Dimension::Px(Fixed::from_int(100)));
}
#[test]
fn add_px() {
let a = Dimension::Px(Fixed::from_int(10));
let b = Dimension::Px(Fixed::from_int(20));
assert_eq!((a + b), Dimension::Px(Fixed::from_int(30)));
}
#[test]
fn mul_fixed() {
let d = Dimension::Px(Fixed::from_int(10));
let result = d * Fixed::from_f32(1.5);
assert_eq!(result, Dimension::Px(Fixed::from_f32(15.0)));
}
}