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, Clipboard, 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        clipboard: &mut dyn Clipboard,
228        shell: &mut Shell<'_, Message>,
229        viewport: &Rectangle,
230        is_picked: bool,
231    ) {
232        let body_layout = if let Some(title_bar) = &mut self.title_bar {
233            let mut children = layout.children();
234
235            title_bar.update(
236                &mut tree.children[1],
237                event,
238                children.next().unwrap(),
239                cursor,
240                renderer,
241                clipboard,
242                shell,
243                viewport,
244            );
245
246            children.next().unwrap()
247        } else {
248            layout
249        };
250
251        if !is_picked {
252            self.body.as_widget_mut().update(
253                &mut tree.children[0],
254                event,
255                body_layout,
256                cursor,
257                renderer,
258                clipboard,
259                shell,
260                viewport,
261            );
262        }
263    }
264
265    pub(crate) fn grid_interaction(
266        &self,
267        layout: Layout<'_>,
268        cursor: mouse::Cursor,
269        drag_enabled: bool,
270    ) -> Option<mouse::Interaction> {
271        let title_bar = self.title_bar.as_ref()?;
272
273        let mut children = layout.children();
274        let title_bar_layout = children.next().unwrap();
275
276        let is_over_pick_area = cursor
277            .position()
278            .map(|cursor_position| title_bar.is_over_pick_area(title_bar_layout, cursor_position))
279            .unwrap_or_default();
280
281        if is_over_pick_area && drag_enabled {
282            return Some(mouse::Interaction::Grab);
283        }
284
285        None
286    }
287
288    pub(crate) fn mouse_interaction(
289        &self,
290        tree: &Tree,
291        layout: Layout<'_>,
292        cursor: mouse::Cursor,
293        viewport: &Rectangle,
294        renderer: &Renderer,
295        drag_enabled: bool,
296    ) -> mouse::Interaction {
297        let (body_layout, title_bar_interaction) = if let Some(title_bar) = &self.title_bar {
298            let mut children = layout.children();
299            let title_bar_layout = children.next().unwrap();
300
301            let is_over_pick_area = cursor
302                .position()
303                .map(|cursor_position| {
304                    title_bar.is_over_pick_area(title_bar_layout, cursor_position)
305                })
306                .unwrap_or_default();
307
308            if is_over_pick_area && drag_enabled {
309                return mouse::Interaction::Grab;
310            }
311
312            let mouse_interaction = title_bar.mouse_interaction(
313                &tree.children[1],
314                title_bar_layout,
315                cursor,
316                viewport,
317                renderer,
318            );
319
320            (children.next().unwrap(), mouse_interaction)
321        } else {
322            (layout, mouse::Interaction::default())
323        };
324
325        self.body
326            .as_widget()
327            .mouse_interaction(&tree.children[0], body_layout, cursor, viewport, renderer)
328            .max(title_bar_interaction)
329    }
330
331    pub(crate) fn overlay<'b>(
332        &'b mut self,
333        tree: &'b mut Tree,
334        layout: Layout<'b>,
335        renderer: &Renderer,
336        viewport: &Rectangle,
337        translation: Vector,
338    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
339        if let Some(title_bar) = self.title_bar.as_mut() {
340            let mut children = layout.children();
341            let title_bar_layout = children.next()?;
342
343            let mut states = tree.children.iter_mut();
344            let body_state = states.next().unwrap();
345            let title_bar_state = states.next().unwrap();
346
347            match title_bar.overlay(
348                title_bar_state,
349                title_bar_layout,
350                renderer,
351                viewport,
352                translation,
353            ) {
354                Some(overlay) => Some(overlay),
355                None => self.body.as_widget_mut().overlay(
356                    body_state,
357                    children.next()?,
358                    renderer,
359                    viewport,
360                    translation,
361                ),
362            }
363        } else {
364            self.body.as_widget_mut().overlay(
365                &mut tree.children[0],
366                layout,
367                renderer,
368                viewport,
369                translation,
370            )
371        }
372    }
373}
374
375impl<Message, Theme, Renderer> Draggable for &Content<'_, Message, Theme, Renderer>
376where
377    Theme: container::Catalog,
378    Renderer: core::Renderer,
379{
380    fn can_be_dragged_at(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
381        if let Some(title_bar) = &self.title_bar {
382            let mut children = layout.children();
383            let title_bar_layout = children.next().unwrap();
384
385            title_bar.is_over_pick_area(title_bar_layout, cursor_position)
386        } else {
387            false
388        }
389    }
390}
391
392impl<'a, T, Message, Theme, Renderer> From<T> for Content<'a, Message, Theme, Renderer>
393where
394    T: Into<Element<'a, Message, Theme, Renderer>>,
395    Theme: container::Catalog + 'a,
396    Renderer: core::Renderer,
397{
398    fn from(element: T) -> Self {
399        Self::new(element)
400    }
401}