iced_core/
content_fit.rs

1//! Control the fit of some content (like an image) within a space.
2use crate::Size;
3
4use std::fmt;
5
6/// The strategy used to fit the contents of a widget to its bounding box.
7///
8/// Each variant of this enum is a strategy that can be applied for resolving
9/// differences in aspect ratio and size between the image being displayed and
10/// the space its being displayed in.
11///
12/// For an interactive demonstration of these properties as they are implemented
13/// in CSS, see [Mozilla's docs][1], or run the `tour` example
14///
15/// [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
16#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, Default)]
17pub enum ContentFit {
18    /// Scale as big as it can be without needing to crop or hide parts.
19    ///
20    /// The image will be scaled (preserving aspect ratio) so that it just fits
21    /// within the window.  This won't distort the image or crop/hide any edges,
22    /// but if the image doesn't fit perfectly, there may be whitespace on the
23    /// top/bottom or left/right.
24    ///
25    /// This is a great fit for when you need to display an image without losing
26    /// any part of it, particularly when the image itself is the focus of the
27    /// screen.
28    #[default]
29    Contain,
30
31    /// Scale the image to cover all of the bounding box, cropping if needed.
32    ///
33    /// This doesn't distort the image, and it ensures that the widget's area is
34    /// completely covered, but it might crop off a bit of the edges of the
35    /// widget, particularly when there is a big difference between the aspect
36    /// ratio of the widget and the aspect ratio of the image.
37    ///
38    /// This is best for when you're using an image as a background, or to fill
39    /// space, and any details of the image around the edge aren't too
40    /// important.
41    Cover,
42
43    /// Distort the image so the widget is 100% covered without cropping.
44    ///
45    /// This stretches the image to fit the widget, without any whitespace or
46    /// cropping. However, because of the stretch, the image may look distorted
47    /// or elongated, particularly when there's a mismatch of aspect ratios.
48    Fill,
49
50    /// Don't resize or scale the image at all.
51    ///
52    /// This will not apply any transformations to the provided image, but also
53    /// means that unless you do the math yourself, the widget's area will not
54    /// be completely covered, or the image might be cropped.
55    ///
56    /// This is best for when you've sized the image yourself.
57    None,
58
59    /// Scale the image down if it's too big for the space, but never scale it
60    /// up.
61    ///
62    /// This works much like [`Contain`](Self::Contain), except that if the
63    /// image would have been scaled up, it keeps its original resolution to
64    /// avoid the bluring that accompanies upscaling images.
65    ScaleDown,
66}
67
68impl ContentFit {
69    /// Attempt to apply the given fit for a content size within some bounds.
70    ///
71    /// The returned value is the recommended scaled size of the content.
72    pub fn fit(&self, content: Size, bounds: Size) -> Size {
73        let content_ar = content.width / content.height;
74        let bounds_ar = bounds.width / bounds.height;
75
76        match self {
77            Self::Contain => {
78                if bounds_ar > content_ar {
79                    Size {
80                        width: content.width * bounds.height / content.height,
81                        ..bounds
82                    }
83                } else {
84                    Size {
85                        height: content.height * bounds.width / content.width,
86                        ..bounds
87                    }
88                }
89            }
90            Self::Cover => {
91                if bounds_ar < content_ar {
92                    Size {
93                        width: content.width * bounds.height / content.height,
94                        ..bounds
95                    }
96                } else {
97                    Size {
98                        height: content.height * bounds.width / content.width,
99                        ..bounds
100                    }
101                }
102            }
103            Self::Fill => bounds,
104            Self::None => content,
105            Self::ScaleDown => {
106                if bounds_ar > content_ar && bounds.height < content.height {
107                    Size {
108                        width: content.width * bounds.height / content.height,
109                        ..bounds
110                    }
111                } else if bounds.width < content.width {
112                    Size {
113                        height: content.height * bounds.width / content.width,
114                        ..bounds
115                    }
116                } else {
117                    content
118                }
119            }
120        }
121    }
122}
123
124impl fmt::Display for ContentFit {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        f.write_str(match self {
127            ContentFit::Contain => "Contain",
128            ContentFit::Cover => "Cover",
129            ContentFit::Fill => "Fill",
130            ContentFit::None => "None",
131            ContentFit::ScaleDown => "Scale Down",
132        })
133    }
134}