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