makeover_layout/picture.rs
1/// How a picture sits in the box it is given.
2///
3/// An intent rather than a value, so a renderer picks the expression it has:
4/// `object-fit` in a webview, a texture's UV rect in egui, and in a terminal a
5/// choice about how many cells the blit gets. Named because MNW already makes
6/// the distinction deliberately at 17 sites and makes it three different ways,
7/// which is a policy the app decided rather than one a shared crate would be
8/// picking by accident.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10#[non_exhaustive]
11pub enum Fit {
12 /// The picture's own proportions, and the box takes the height they imply.
13 ///
14 /// The default because it is the only one that shows the whole picture at
15 /// its own shape, so a renderer that ignores this enum entirely is still
16 /// right about the common case. A screenshot wants this; the shipped MNW
17 /// carousel sets no `object-fit` at all, which is this.
18 #[default]
19 Natural,
20 /// Fill the box and crop whatever does not fit.
21 ///
22 /// For a picture in a slot whose shape the layout fixed: a thumbnail, an
23 /// avatar, cover art. 15 of MNW's 17 sites.
24 Cover,
25 /// Fit inside the box whole, leaving space on two sides.
26 ///
27 /// The letterbox. For when the whole picture matters more than filling the
28 /// space, and the space is not the picture's shape.
29 Contain,
30}
31
32/// A picture's own pixel dimensions.
33///
34/// Deliberately not [`makeover_geometry`]'s business. Geometry answers *how
35/// much space a thing should get*, which is a scale question with the same
36/// answer on every screen. This is the intrinsic size of one asset, which is a
37/// fact about that asset and varies per picture.
38///
39/// [`makeover_geometry`]: https://docs.rs/makeover-geometry
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41#[non_exhaustive]
42pub struct Extent {
43 /// Width in the picture's own pixels.
44 pub width: u32,
45 /// Height in the picture's own pixels.
46 pub height: u32,
47}
48
49impl Extent {
50 /// A picture's dimensions.
51 #[must_use]
52 pub const fn new(width: u32, height: u32) -> Self {
53 Self { width, height }
54 }
55
56 /// Width over height, or `None` if either side is zero.
57 ///
58 /// The form a renderer actually reserves space with: a box that knows its
59 /// proportion holds the right height at any width, which is what a
60 /// responsive picture needs and what a fixed pixel height cannot give.
61 #[must_use]
62 pub fn ratio(self) -> Option<f32> {
63 (self.width > 0 && self.height > 0).then(|| self.width as f32 / self.height as f32)
64 }
65}