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