Skip to main content

cosmic/widget/
autosize.rs

1//! Autosize Container, which will resize the window to its contents.
2
3use iced_core::event::{self, Event};
4use iced_core::widget::{Id, Operation, Tree};
5use iced_core::{
6    Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse, overlay,
7    renderer,
8};
9pub use iced_widget::container::{Catalog, Style};
10
11pub fn autosize<'a, Message: 'static, Theme, E>(
12    content: E,
13    id: Id,
14) -> Autosize<'a, Message, Theme, crate::Renderer>
15where
16    E: Into<Element<'a, Message, Theme, crate::Renderer>>,
17    Theme: iced_widget::container::Catalog,
18    <Theme as iced_widget::container::Catalog>::Class<'a>: From<crate::theme::Container<'a>>,
19{
20    Autosize::new(content, id)
21}
22
23/// An element decorating some content.
24///
25/// It is normally used for alignment purposes.
26#[allow(missing_debug_implementations)]
27pub struct Autosize<'a, Message, Theme, Renderer>
28where
29    Renderer: iced_core::Renderer,
30{
31    content: Element<'a, Message, Theme, Renderer>,
32    id: Id,
33    limits: layout::Limits,
34    auto_width: bool,
35    auto_height: bool,
36}
37
38impl<'a, Message, Theme, Renderer> Autosize<'a, Message, Theme, Renderer>
39where
40    Renderer: iced_core::Renderer,
41{
42    /// Creates an empty [`IdContainer`].
43    pub(crate) fn new<T>(content: T, id: Id) -> Self
44    where
45        T: Into<Element<'a, Message, Theme, Renderer>>,
46    {
47        Autosize {
48            content: content.into(),
49            id,
50            limits: layout::Limits::NONE,
51            auto_width: true,
52            auto_height: true,
53        }
54    }
55
56    #[inline]
57    pub fn limits(mut self, limits: layout::Limits) -> Self {
58        self.limits = limits;
59        self
60    }
61
62    #[inline]
63    pub fn auto_width(mut self, auto_width: bool) -> Self {
64        self.auto_width = auto_width;
65        self
66    }
67
68    #[inline]
69    pub fn auto_height(mut self, auto_height: bool) -> Self {
70        self.auto_height = auto_height;
71        self
72    }
73
74    #[inline]
75    pub fn max_width(mut self, v: f32) -> Self {
76        self.limits = self.limits.max_width(v);
77        self
78    }
79
80    #[inline]
81    pub fn max_height(mut self, v: f32) -> Self {
82        self.limits = self.limits.max_height(v);
83        self
84    }
85
86    #[inline]
87    pub fn min_width(mut self, v: f32) -> Self {
88        self.limits = self.limits.min_width(v);
89        self
90    }
91
92    #[inline]
93    pub fn min_height(mut self, v: f32) -> Self {
94        self.limits = self.limits.min_height(v);
95        self
96    }
97}
98
99impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
100    for Autosize<'_, Message, Theme, Renderer>
101where
102    Renderer: iced_core::Renderer,
103{
104    fn children(&self) -> Vec<Tree> {
105        vec![Tree::new(&self.content)]
106    }
107
108    fn diff(&mut self, tree: &mut Tree) {
109        tree.diff_children(std::slice::from_mut(&mut self.content));
110    }
111
112    fn size(&self) -> iced_core::Size<Length> {
113        self.content.as_widget().size()
114    }
115
116    fn layout(
117        &mut self,
118        tree: &mut Tree,
119        renderer: &Renderer,
120        limits: &layout::Limits,
121    ) -> layout::Node {
122        let mut my_limits = self.limits;
123        let min = limits.min();
124        let max = limits.max();
125        if !self.auto_width {
126            my_limits = limits.min_width(min.width).max_width(max.width);
127        }
128        if !self.auto_height {
129            my_limits = limits.min_height(min.height).max_height(max.height);
130        }
131        let node = self
132            .content
133            .as_widget_mut()
134            .layout(&mut tree.children[0], renderer, &my_limits);
135        let size = node.size();
136        layout::Node::with_children(size, vec![node])
137    }
138
139    fn operate(
140        &mut self,
141        tree: &mut Tree,
142        layout: Layout<'_>,
143        renderer: &Renderer,
144        operation: &mut dyn Operation,
145    ) {
146        operation.container(Some(&self.id), layout.bounds());
147        operation.traverse(&mut |operation| {
148            self.content.as_widget_mut().operate(
149                &mut tree.children[0],
150                layout
151                    .children()
152                    .next()
153                    .unwrap()
154                    .with_virtual_offset(layout.virtual_offset()),
155                renderer,
156                operation,
157            );
158        });
159    }
160
161    fn update(
162        &mut self,
163        tree: &mut Tree,
164        event: &Event,
165        layout: Layout<'_>,
166        cursor_position: mouse::Cursor,
167        renderer: &Renderer,
168        clipboard: &mut dyn Clipboard,
169        shell: &mut Shell<'_, Message>,
170        viewport: &Rectangle,
171    ) {
172        #[cfg(all(feature = "wayland", target_os = "linux"))]
173        if matches!(
174            event,
175            Event::PlatformSpecific(event::PlatformSpecific::Wayland(
176                event::wayland::Event::RequestResize
177            ))
178        ) {
179            let bounds = layout.bounds().size();
180            clipboard.request_logical_window_size(bounds.width.max(1.), bounds.height.max(1.));
181        }
182        self.content.as_widget_mut().update(
183            &mut tree.children[0],
184            event,
185            layout
186                .children()
187                .next()
188                .unwrap()
189                .with_virtual_offset(layout.virtual_offset()),
190            cursor_position,
191            renderer,
192            clipboard,
193            shell,
194            viewport,
195        );
196    }
197
198    fn mouse_interaction(
199        &self,
200        tree: &Tree,
201        layout: Layout<'_>,
202        cursor_position: mouse::Cursor,
203        viewport: &Rectangle,
204        renderer: &Renderer,
205    ) -> mouse::Interaction {
206        let content_layout = layout.children().next().unwrap();
207        self.content.as_widget().mouse_interaction(
208            &tree.children[0],
209            content_layout.with_virtual_offset(layout.virtual_offset()),
210            cursor_position,
211            viewport,
212            renderer,
213        )
214    }
215
216    fn draw(
217        &self,
218        tree: &Tree,
219        renderer: &mut Renderer,
220        theme: &Theme,
221        renderer_style: &renderer::Style,
222        layout: Layout<'_>,
223        cursor_position: mouse::Cursor,
224        viewport: &Rectangle,
225    ) {
226        let content_layout = layout.children().next().unwrap();
227        self.content.as_widget().draw(
228            &tree.children[0],
229            renderer,
230            theme,
231            renderer_style,
232            content_layout.with_virtual_offset(layout.virtual_offset()),
233            cursor_position,
234            viewport,
235        );
236    }
237
238    fn overlay<'b>(
239        &'b mut self,
240        tree: &'b mut Tree,
241        layout: Layout<'b>,
242        renderer: &Renderer,
243        viewport: &Rectangle,
244        translation: Vector,
245    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
246        self.content.as_widget_mut().overlay(
247            &mut tree.children[0],
248            layout
249                .children()
250                .next()
251                .unwrap()
252                .with_virtual_offset(layout.virtual_offset()),
253            renderer,
254            viewport,
255            translation,
256        )
257    }
258
259    fn drag_destinations(
260        &self,
261        state: &Tree,
262        layout: Layout<'_>,
263        renderer: &Renderer,
264        dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
265    ) {
266        let content_layout = layout.children().next().unwrap();
267        self.content.as_widget().drag_destinations(
268            &state.children[0],
269            content_layout.with_virtual_offset(layout.virtual_offset()),
270            renderer,
271            dnd_rectangles,
272        );
273    }
274
275    fn id(&self) -> Option<crate::widget::Id> {
276        Some(self.id.clone())
277    }
278
279    fn set_id(&mut self, id: crate::widget::Id) {
280        self.id = id;
281    }
282
283    #[cfg(feature = "a11y")]
284    /// get the a11y nodes for the widget
285    fn a11y_nodes(
286        &self,
287        layout: Layout<'_>,
288        state: &Tree,
289        p: mouse::Cursor,
290    ) -> iced_accessibility::A11yTree {
291        let c_layout = layout.children().next().unwrap();
292        let c_state = &state.children[0];
293        self.content.as_widget().a11y_nodes(
294            c_layout.with_virtual_offset(layout.virtual_offset()),
295            c_state,
296            p,
297        )
298    }
299}
300
301impl<'a, Message, Theme, Renderer> From<Autosize<'a, Message, Theme, Renderer>>
302    for Element<'a, Message, Theme, Renderer>
303where
304    Message: 'a,
305    Renderer: 'a + iced_core::Renderer,
306    Theme: 'a,
307{
308    fn from(c: Autosize<'a, Message, Theme, Renderer>) -> Element<'a, Message, Theme, Renderer> {
309        Element::new(c)
310    }
311}