Skip to main content

use_viewport/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4/// Viewport orientation.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
6pub enum ViewportOrientation {
7    Portrait,
8    Landscape,
9}
10
11/// Density preference for a display context.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
13pub enum Density {
14    Compact,
15    Comfortable,
16    Spacious,
17}
18
19/// Coarse viewport class.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
21pub enum ViewportClass {
22    Xs,
23    Sm,
24    Md,
25    Lg,
26    Xl,
27    Xxl,
28}
29
30/// A display scale expressed in thousandths.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
32pub struct DisplayScale(u32);
33
34impl DisplayScale {
35    pub fn from_milli(value: u32) -> Self {
36        Self(value)
37    }
38
39    pub fn one() -> Self {
40        Self(1_000)
41    }
42
43    pub fn milli(self) -> u32 {
44        self.0
45    }
46}
47
48/// Viewport dimensions in abstract display units.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
50pub struct ViewportSize {
51    width: u32,
52    height: u32,
53}
54
55impl ViewportSize {
56    pub fn new(width: u32, height: u32) -> Self {
57        Self { width, height }
58    }
59
60    pub fn width(self) -> u32 {
61        self.width
62    }
63
64    pub fn height(self) -> u32 {
65        self.height
66    }
67
68    pub fn orientation(self) -> ViewportOrientation {
69        if self.width >= self.height {
70            ViewportOrientation::Landscape
71        } else {
72            ViewportOrientation::Portrait
73        }
74    }
75
76    pub fn class(self) -> ViewportClass {
77        match self.width {
78            0..=479 => ViewportClass::Xs,
79            480..=767 => ViewportClass::Sm,
80            768..=1023 => ViewportClass::Md,
81            1024..=1279 => ViewportClass::Lg,
82            1280..=1535 => ViewportClass::Xl,
83            _ => ViewportClass::Xxl,
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::{Density, DisplayScale, ViewportClass, ViewportOrientation, ViewportSize};
91
92    #[test]
93    fn classifies_viewport_size() {
94        let phone = ViewportSize::new(390, 844);
95        let desktop = ViewportSize::new(1440, 900);
96
97        assert_eq!(phone.orientation(), ViewportOrientation::Portrait);
98        assert_eq!(phone.class(), ViewportClass::Xs);
99        assert_eq!(desktop.orientation(), ViewportOrientation::Landscape);
100        assert_eq!(desktop.class(), ViewportClass::Xl);
101    }
102
103    #[test]
104    fn stores_density_and_scale() {
105        let scale = DisplayScale::from_milli(1_250);
106
107        assert_eq!(Density::Comfortable, Density::Comfortable);
108        assert_eq!(DisplayScale::one().milli(), 1_000);
109        assert_eq!(scale.milli(), 1_250);
110    }
111}