Skip to main content

iced_widget/pane_grid/
content.rs

1use crate::container;
2use crate::core::layout;
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::widget::{self, Tree};
7use crate::core::{self, Element, Event, Layout, Point, Rectangle, Shell, Size, Vector};
8use crate::pane_grid::{Draggable, TitleBar};
9
10/// The content of a [`Pane`].
11///
12/// [`Pane`]: super::Pane
13pub struct Content<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
14where
15    Theme: container::Catalog,
16    Renderer: core::Renderer,
17{
18    title_bar: Option<TitleBar<'a, Message, Theme, Renderer>>,
19    body: Element<'a, Message, Theme, Renderer>,
20    class: Theme::Class<'a>,
21}
22
23impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer>
24where
25    Theme: container::Catalog,
26    Renderer: core::Renderer,
27{
28    /// Creates a new [`Content`] with the provided body.
29    pub fn new(body: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self {
30        Self {
31            title_bar: None,
32            body: body.into(),
33            class: Theme::default(),
34        }
35    }
36
37    /// Sets the [`TitleBar`] of the [`Content`].
38    pub fn title_bar(mut self, title_bar: TitleBar<'a, Message, Theme, Renderer>) -> Self {
39        self.title_bar = Some(title_bar);
40        self
41    }
42
43    /// Sets the style of the [`Content`].
44    #[must_use]
45    pub fn style(mut self, style: impl Fn(&Theme) -> container::Style + 'a) -> Self
46    where
47        Theme::Class<'a>: From<container::StyleFn<'a, Theme>>,
48    {
49        self.class = (Box::new(style) as container::StyleFn<'a, Theme>).into();
50        self
51    }
52
53    /// Sets the style class of the [`Content`].
54    #[cfg(feature = "advanced")]
55    #[must_use]
56    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
57        self.class = class.into();
58        self
59    }
60}
61
62impl<Message, Theme, Renderer> Content<'_, Message, Theme, Renderer>
63where
64    Theme: container::Catalog,
65    Renderer: core::Renderer,
66{
67    pub(super) fn state(&self) -> Tree {
68        let children = if let Some(title_bar) = self.title_bar.as_ref() {
69            vec![Tree::new(&self.body), title_bar.state()]
70        } else {
71            vec![Tree::new(&self.body), Tree::empty()]
72        };
73
74        Tree {
75            children,
76            ..Tree::empty()
77        }
78    }
79
80    pub(super) fn diff(&self, tree: &mut Tree) {
81        if tree.children.len() == 2 {
82            if let Some(title_bar) = self.title_bar.as_ref() {
83                title_bar.diff(&mut tree.children[1]);
84            }
85
86            tree.children[0].diff(&self.body);
87        } else {
88            *tree = self.state();
89        }
90    }
91
92    /// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`].
93    ///
94    /// [`Renderer`]: core::Renderer
95    pub fn draw(
96        &self,
97        tree: &Tree,
98        renderer: &mut Renderer,
99        theme: &Theme,
100        style: &renderer::Style,
101        layout: Layout<'_>,
102        cursor: mouse::Cursor,
103        viewport: &Rectangle,
104    ) {
105        let bounds = layout.bounds();
106
107        {
108            let style = theme.style(&self.class);
109
110            container::draw_background(renderer, &style, bounds);
111        }
112
113        if let Some(title_bar) = &self.title_bar {
114            let mut children = layout.children();
115            let title_bar_layout = children.next().unwrap();
116            let body_layout = children.next().unwrap();
117
118            let show_controls = cursor.is_over(bounds);
119
120            self.body.as_widget().draw(
121                &tree.children[0],
122                renderer,
123                theme,
124                style,
125                body_layout,
126                cursor,
127                viewport,
128            );
129
130            title_bar.draw(
131                &tree.children[1],
132                renderer,
133                theme,
134                style,
135                title_bar_layout,
136                cursor,
137                viewport,
138                show_controls,
139            );
140        } else {
141            self.body.as_widget().draw(
142                &tree.children[0],
143                renderer,
144                theme,
145                style,
146                layout,
147                cursor,
148                viewport,
149            );
150        }
151    }
152
153    pub(crate) fn layout(
154        &mut self,
155        tree: &mut Tree,
156        renderer: &Renderer,
157        limits: &layout::Limits,
158    ) -> layout::Node {
159        if let Some(title_bar) = &mut self.title_bar {
160            let max_size = limits.max();
161
162            let title_bar_layout = title_bar.layout(
163                &mut tree.children[1],
164                renderer,
165                &layout::Limits::new(Size::ZERO, max_size),
166            );
167
168            let title_bar_size = title_bar_layout.size();
169
170            let body_layout = self.body.as_widget_mut().layout(
171                &mut tree.children[0],
172                renderer,
173                &layout::Limits::new(
174                    Size::ZERO,
175                    Size::new(max_size.width, max_size.height - title_bar_size.height),
176                ),
177            );
178
179            layout::Node::with_children(
180                max_size,
181                vec![
182                    title_bar_layout,
183                    body_layout.move_to(Point::new(0.0, title_bar_size.height)),
184                ],
185            )
186        } else {
187            self.body
188                .as_widget_mut()
189                .layout(&mut tree.children[0], renderer, limits)
190        }
191    }
192
193    pub(crate) fn operate(
194        &mut self,
195        tree: &mut Tree,
196        layout: Layout<'_>,
197        renderer: &Renderer,
198        operation: &mut dyn widget::Operation,
199    ) {
200        let body_layout = if let Some(title_bar) = &mut self.title_bar {
201            let mut children = layout.children();
202
203            title_bar.operate(
204                &mut tree.children[1],
205                children.next().unwrap(),
206                renderer,
207                operation,
208            );
209
210            children.next().unwrap()
211        } else {
212            layout
213        };
214
215        self.body
216            .as_widget_mut()
217            .operate(&mut tree.children[0], body_layout, renderer, operation);
218    }
219
220    pub(crate) fn update(
221        &mut self,
222        tree: &mut Tree,
223        event: &Event,
224        layout: Layout<'_>,
225        cursor: mouse::Cursor,
226        renderer: &Renderer,
227        shell: &mut Shell<'_, Message>,
228        viewport: &Rectangle,
229        is_picked: bool,
230    ) {
231        let body_layout = if let Some(title_bar) = &mut self.title_bar {
232            let mut children = layout.children();
233
234            title_bar.update(
235                &mut tree.children[1],
236                event,
237                children.next().unwrap(),
238                cursor,
239                renderer,
240                shell,
241                viewport,
242            );
243
244            children.next().unwrap()
245        } else {
246            layout
247        };
248
249        if !is_picked {
250            self.body.as_widget_mut().update(
251                &mut tree.children[0],
252                event,
253                body_layout,
254                cursor,
255                renderer,
256                shell,
257                viewport,
258            );
259        }
260    }
261
262    pub(crate) fn grid_interaction(
263        &self,
264        layout: Layout<'_>,
265        cursor: mouse::Cursor,
266        drag_enabled: bool,
267    ) -> Option<mouse::Interaction> {
268        let title_bar = self.title_bar.as_ref()?;
269
270        let mut children = layout.children();
271        let title_bar_layout = children.next().unwrap();
272
273        let is_over_pick_area = cursor
274            .position()
275            .map(|cursor_position| title_bar.is_over_pick_area(title_bar_layout, cursor_position))
276            .unwrap_or_default();
277
278        if is_over_pick_area && drag_enabled {
279            return Some(mouse::Interaction::Grab);
280        }
281
282        None
283    }
284
285    pub(crate) fn mouse_interaction(
286        &self,
287        tree: &Tree,
288        layout: Layout<'_>,
289        cursor: mouse::Cursor,
290        viewport: &Rectangle,
291        renderer: &Renderer,
292        drag_enabled: bool,
293    ) -> mouse::Interaction {
294        let (body_layout, title_bar_interaction) = if let Some(title_bar) = &self.title_bar {
295            let mut children = layout.children();
296            let title_bar_layout = children.next().unwrap();
297
298            let is_over_pick_area = cursor
299                .position()
300                .map(|cursor_position| {
301                    title_bar.is_over_pick_area(title_bar_layout, cursor_position)
302                })
303                .unwrap_or_default();
304
305            if is_over_pick_area && drag_enabled {
306                return mouse::Interaction::Grab;
307            }
308
309            let mouse_interaction = title_bar.mouse_interaction(
310                &tree.children[1],
311                title_bar_layout,
312                cursor,
313                viewport,
314                renderer,
315            );
316
317            (children.next().unwrap(), mouse_interaction)
318        } else {
319            (layout, mouse::Interaction::default())
320        };
321
322        self.body
323            .as_widget()
324            .mouse_interaction(&tree.children[0], body_layout, cursor, viewport, renderer)
325            .max(title_bar_interaction)
326    }
327
328    pub(crate) fn overlay<'b>(
329        &'b mut self,
330        tree: &'b mut Tree,
331        layout: Layout<'b>,
332        renderer: &Renderer,
333        viewport: &Rectangle,
334        translation: Vector,
335    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
336        if let Some(title_bar) = self.title_bar.as_mut() {
337            let mut children = layout.children();
338            let title_bar_layout = children.next()?;
339
340            let mut states = tree.children.iter_mut();
341            let body_state = states.next().unwrap();
342            let title_bar_state = states.next().unwrap();
343
344            match title_bar.overlay(
345                title_bar_state,
346                title_bar_layout,
347                renderer,
348                viewport,
349                translation,
350            ) {
351                Some(overlay) => Some(overlay),
352                None => self.body.as_widget_mut().overlay(
353                    body_state,
354                    children.next()?,
355                    renderer,
356                    viewport,
357                    translation,
358                ),
359            }
360        } else {
361            self.body.as_widget_mut().overlay(
362                &mut tree.children[0],
363                layout,
364                renderer,
365                viewport,
366                translation,
367            )
368        }
369    }
370}
371
372impl<Message, Theme, Renderer> Draggable for &Content<'_, Message, Theme, Renderer>
373where
374    Theme: container::Catalog,
375    Renderer: core::Renderer,
376{
377    fn can_be_dragged_at(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
378        if let Some(title_bar) = &self.title_bar {
379            let mut children = layout.children();
380            let title_bar_layout = children.next().unwrap();
381
382            title_bar.is_over_pick_area(title_bar_layout, cursor_position)
383        } else {
384            false
385        }
386    }
387}
388
389impl<'a, T, Message, Theme, Renderer> From<T> for Content<'a, Message, Theme, Renderer>
390where
391    T: Into<Element<'a, Message, Theme, Renderer>>,
392    Theme: container::Catalog + 'a,
393    Renderer: core::Renderer,
394{
395    fn from(element: T) -> Self {
396        Self::new(element)
397    }
398}