use-viewport 0.1.0

Viewport and display context primitives for RustUse UI
Documentation
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]

/// Viewport orientation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ViewportOrientation {
    Portrait,
    Landscape,
}

/// Density preference for a display context.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum Density {
    Compact,
    Comfortable,
    Spacious,
}

/// Coarse viewport class.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ViewportClass {
    Xs,
    Sm,
    Md,
    Lg,
    Xl,
    Xxl,
}

/// A display scale expressed in thousandths.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct DisplayScale(u32);

impl DisplayScale {
    pub fn from_milli(value: u32) -> Self {
        Self(value)
    }

    pub fn one() -> Self {
        Self(1_000)
    }

    pub fn milli(self) -> u32 {
        self.0
    }
}

/// Viewport dimensions in abstract display units.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ViewportSize {
    width: u32,
    height: u32,
}

impl ViewportSize {
    pub fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }

    pub fn width(self) -> u32 {
        self.width
    }

    pub fn height(self) -> u32 {
        self.height
    }

    pub fn orientation(self) -> ViewportOrientation {
        if self.width >= self.height {
            ViewportOrientation::Landscape
        } else {
            ViewportOrientation::Portrait
        }
    }

    pub fn class(self) -> ViewportClass {
        match self.width {
            0..=479 => ViewportClass::Xs,
            480..=767 => ViewportClass::Sm,
            768..=1023 => ViewportClass::Md,
            1024..=1279 => ViewportClass::Lg,
            1280..=1535 => ViewportClass::Xl,
            _ => ViewportClass::Xxl,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Density, DisplayScale, ViewportClass, ViewportOrientation, ViewportSize};

    #[test]
    fn classifies_viewport_size() {
        let phone = ViewportSize::new(390, 844);
        let desktop = ViewportSize::new(1440, 900);

        assert_eq!(phone.orientation(), ViewportOrientation::Portrait);
        assert_eq!(phone.class(), ViewportClass::Xs);
        assert_eq!(desktop.orientation(), ViewportOrientation::Landscape);
        assert_eq!(desktop.class(), ViewportClass::Xl);
    }

    #[test]
    fn stores_density_and_scale() {
        let scale = DisplayScale::from_milli(1_250);

        assert_eq!(Density::Comfortable, Density::Comfortable);
        assert_eq!(DisplayScale::one().milli(), 1_000);
        assert_eq!(scale.milli(), 1_250);
    }
}