Skip to main content

cosmic/widget/
aspect_ratio.rs

1//! A container which constraints itself to a specific aspect ratio.
2
3use iced::Size;
4use iced::widget::Container;
5use iced_core::event::Event;
6use iced_core::widget::Tree;
7use iced_core::{
8    Alignment, Clipboard, Element, Layout, Length, Padding, Rectangle, Shell, Vector, Widget,
9    layout, mouse, overlay, renderer,
10};
11
12pub use iced_widget::container::{Catalog, Style};
13
14pub fn aspect_ratio_container<'a, Message: 'static, T>(
15    content: T,
16    ratio: f32,
17) -> AspectRatio<'a, Message, crate::Renderer>
18where
19    T: Into<Element<'a, Message, crate::Theme, crate::Renderer>>,
20{
21    AspectRatio::new(content, ratio)
22}
23
24/// A container which constraints itself to a specific aspect ratio.
25#[allow(missing_debug_implementations)]
26pub struct AspectRatio<'a, Message, Renderer>
27where
28    Renderer: iced_core::Renderer,
29{
30    ratio: f32,
31    container: Container<'a, Message, crate::Theme, Renderer>,
32}
33
34impl<Message, Renderer> AspectRatio<'_, Message, Renderer>
35where
36    Renderer: iced_core::Renderer,
37{
38    fn constrain_limits(&self, size: Size) -> Size {
39        let Size {
40            mut width,
41            mut height,
42        } = size;
43        if size.width / size.height > self.ratio {
44            width = self.ratio * height;
45        } else {
46            height = width / self.ratio;
47        }
48        Size { width, height }
49    }
50}
51
52impl<'a, Message, Renderer> AspectRatio<'a, Message, Renderer>
53where
54    Renderer: iced_core::Renderer,
55{
56    /// Creates an empty [`Container`].
57    pub(crate) fn new<T>(content: T, ratio: f32) -> Self
58    where
59        T: Into<Element<'a, Message, crate::Theme, Renderer>>,
60    {
61        AspectRatio {
62            ratio,
63            container: Container::new(content),
64        }
65    }
66
67    /// Sets the [`Padding`] of the [`Container`].
68    #[must_use]
69    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
70        self.container = self.container.padding(padding);
71        self
72    }
73
74    /// Sets the width of the [`self.`].
75    #[must_use]
76    #[inline]
77    pub fn width(mut self, width: Length) -> Self {
78        self.container = self.container.width(width);
79        self
80    }
81
82    /// Sets the height of the [`Container`].
83    #[must_use]
84    #[inline]
85    pub fn height(mut self, height: Length) -> Self {
86        self.container = self.container.height(height);
87        self
88    }
89
90    /// Sets the maximum width of the [`Container`].
91    #[must_use]
92    #[inline]
93    pub fn max_width(mut self, max_width: f32) -> Self {
94        self.container = self.container.max_width(max_width);
95        self
96    }
97
98    /// Sets the maximum height of the [`Container`] in pixels.
99    #[must_use]
100    #[inline]
101    pub fn max_height(mut self, max_height: f32) -> Self {
102        self.container = self.container.max_height(max_height);
103        self
104    }
105
106    /// Sets the content alignment for the horizontal axis of the [`Container`].
107    #[must_use]
108    #[inline]
109    pub fn align_x(mut self, alignment: Alignment) -> Self {
110        self.container = self.container.align_x(alignment);
111        self
112    }
113
114    /// Sets the content alignment for the vertical axis of the [`Container`].
115    #[must_use]
116    #[inline]
117    pub fn align_y(mut self, alignment: Alignment) -> Self {
118        self.container = self.container.align_y(alignment);
119        self
120    }
121
122    /// Centers the contents in the horizontal axis of the [`Container`].
123    #[must_use]
124    #[inline]
125    pub fn center_x(mut self, width: Length) -> Self {
126        self.container = self.container.center_x(width);
127        self
128    }
129
130    /// Centers the contents in the vertical axis of the [`Container`].
131    #[must_use]
132    #[inline]
133    pub fn center_y(mut self, height: Length) -> Self {
134        self.container = self.container.center_y(height);
135        self
136    }
137
138    /// Centers the contents in the horizontal and vertical axis of the [`Container`].
139    #[must_use]
140    #[inline]
141    pub fn center(mut self, length: Length) -> Self {
142        self.container = self.container.center(length);
143        self
144    }
145
146    /// Sets the style of the [`Container`].
147    #[must_use]
148    pub fn class(mut self, style: impl Into<crate::style::Container<'a>>) -> Self {
149        self.container = self.container.class(style);
150        self
151    }
152}
153
154impl<Message, Renderer> Widget<Message, crate::Theme, Renderer>
155    for AspectRatio<'_, Message, Renderer>
156where
157    Renderer: iced_core::Renderer,
158{
159    fn children(&self) -> Vec<Tree> {
160        self.container.children()
161    }
162
163    fn diff(&mut self, tree: &mut Tree) {
164        self.container.diff(tree);
165    }
166
167    fn size(&self) -> Size<Length> {
168        self.container.size()
169    }
170
171    fn layout(
172        &mut self,
173        tree: &mut Tree,
174        renderer: &Renderer,
175        limits: &layout::Limits,
176    ) -> layout::Node {
177        let custom_limits = layout::Limits::new(
178            self.constrain_limits(limits.min()),
179            self.constrain_limits(limits.max()),
180        );
181        self.container
182            .layout(&mut tree.children[0], renderer, &custom_limits)
183    }
184
185    fn operate(
186        &mut self,
187        tree: &mut Tree,
188        layout: Layout<'_>,
189        renderer: &Renderer,
190        operation: &mut dyn iced_core::widget::Operation<()>,
191    ) {
192        self.container.operate(tree, layout, renderer, operation);
193    }
194
195    fn update(
196        &mut self,
197        tree: &mut Tree,
198        event: &Event,
199        layout: Layout<'_>,
200        cursor_position: mouse::Cursor,
201        renderer: &Renderer,
202        clipboard: &mut dyn Clipboard,
203        shell: &mut Shell<'_, Message>,
204        viewport: &Rectangle,
205    ) {
206        self.container.update(
207            tree,
208            event,
209            layout,
210            cursor_position,
211            renderer,
212            clipboard,
213            shell,
214            viewport,
215        )
216    }
217
218    fn mouse_interaction(
219        &self,
220        tree: &Tree,
221        layout: Layout<'_>,
222        cursor_position: mouse::Cursor,
223        viewport: &Rectangle,
224        renderer: &Renderer,
225    ) -> mouse::Interaction {
226        self.container
227            .mouse_interaction(tree, layout, cursor_position, viewport, renderer)
228    }
229
230    fn draw(
231        &self,
232        tree: &Tree,
233        renderer: &mut Renderer,
234        theme: &crate::Theme,
235        renderer_style: &renderer::Style,
236        layout: Layout<'_>,
237        cursor_position: mouse::Cursor,
238        viewport: &Rectangle,
239    ) {
240        self.container.draw(
241            tree,
242            renderer,
243            theme,
244            renderer_style,
245            layout,
246            cursor_position,
247            viewport,
248        );
249    }
250
251    fn overlay<'b>(
252        &'b mut self,
253        tree: &'b mut Tree,
254        layout: Layout<'b>,
255        renderer: &Renderer,
256        viewport: &Rectangle,
257        translation: Vector,
258    ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
259        self.container
260            .overlay(tree, layout, renderer, viewport, translation)
261    }
262
263    #[cfg(feature = "a11y")]
264    /// get the a11y nodes for the widget
265    fn a11y_nodes(
266        &self,
267        layout: Layout<'_>,
268        state: &Tree,
269        p: mouse::Cursor,
270    ) -> iced_accessibility::A11yTree {
271        self.container.a11y_nodes(layout, state, p)
272    }
273}
274
275impl<'a, Message, Renderer> From<AspectRatio<'a, Message, Renderer>>
276    for Element<'a, Message, crate::Theme, Renderer>
277where
278    Message: 'a,
279    Renderer: 'a + iced_core::Renderer,
280{
281    fn from(
282        column: AspectRatio<'a, Message, Renderer>,
283    ) -> Element<'a, Message, crate::Theme, Renderer> {
284        Element::new(column)
285    }
286}