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