iced_widget/pane_grid/
content.rs

1use crate::container;
2use crate::core::event::{self, Event};
3use crate::core::layout;
4use crate::core::mouse;
5use crate::core::overlay;
6use crate::core::renderer;
7use crate::core::widget::{self, Tree};
8use crate::core::{
9    self, Clipboard, Element, Layout, Point, Rectangle, Shell, Size, 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<'a, Message, Theme, Renderer> Content<'a, 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(&mut self, tree: &mut Tree) {
95        if tree.children.len() == 2 {
96            if let Some(title_bar) = self.title_bar.as_mut() {
97                title_bar.diff(&mut tree.children[1]);
98            }
99
100            tree.children[0].diff(&mut 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 on_event(
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    ) -> event::Status {
254        let mut event_status = event::Status::Ignored;
255
256        let body_layout = if let Some(title_bar) = &mut self.title_bar {
257            let mut children = layout.children();
258
259            event_status = title_bar.on_event(
260                &mut tree.children[1],
261                event.clone(),
262                children.next().unwrap(),
263                cursor,
264                renderer,
265                clipboard,
266                shell,
267                viewport,
268            );
269
270            children.next().unwrap()
271        } else {
272            layout
273        };
274
275        let body_status = if is_picked {
276            event::Status::Ignored
277        } else {
278            self.body.as_widget_mut().on_event(
279                &mut tree.children[0],
280                event,
281                body_layout,
282                cursor,
283                renderer,
284                clipboard,
285                shell,
286                viewport,
287            )
288        };
289
290        event_status.merge(body_status)
291    }
292
293    pub(crate) fn mouse_interaction(
294        &self,
295        tree: &Tree,
296        layout: Layout<'_>,
297        cursor: mouse::Cursor,
298        viewport: &Rectangle,
299        renderer: &Renderer,
300        drag_enabled: bool,
301    ) -> mouse::Interaction {
302        let (body_layout, title_bar_interaction) = if let Some(title_bar) =
303            &self.title_bar
304        {
305            let mut children = layout.children();
306            let title_bar_layout = children.next().unwrap();
307
308            let is_over_pick_area = cursor
309                .position()
310                .map(|cursor_position| {
311                    title_bar
312                        .is_over_pick_area(title_bar_layout, cursor_position)
313                })
314                .unwrap_or_default();
315
316            if is_over_pick_area && drag_enabled {
317                return mouse::Interaction::Grab;
318            }
319
320            let mouse_interaction = title_bar.mouse_interaction(
321                &tree.children[1],
322                title_bar_layout,
323                cursor,
324                viewport,
325                renderer,
326            );
327
328            (children.next().unwrap(), mouse_interaction)
329        } else {
330            (layout, mouse::Interaction::default())
331        };
332
333        self.body
334            .as_widget()
335            .mouse_interaction(
336                &tree.children[0],
337                body_layout,
338                cursor,
339                viewport,
340                renderer,
341            )
342            .max(title_bar_interaction)
343    }
344
345    pub(crate) fn overlay<'b>(
346        &'b mut self,
347        tree: &'b mut Tree,
348        layout: Layout<'_>,
349        renderer: &Renderer,
350        translation: Vector,
351    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
352        if let Some(title_bar) = self.title_bar.as_mut() {
353            let mut children = layout.children();
354            let title_bar_layout = children.next()?;
355
356            let mut states = tree.children.iter_mut();
357            let body_state = states.next().unwrap();
358            let title_bar_state = states.next().unwrap();
359
360            match title_bar.overlay(
361                title_bar_state,
362                title_bar_layout,
363                renderer,
364                translation,
365            ) {
366                Some(overlay) => Some(overlay),
367                None => self.body.as_widget_mut().overlay(
368                    body_state,
369                    children.next()?,
370                    renderer,
371                    translation,
372                ),
373            }
374        } else {
375            self.body.as_widget_mut().overlay(
376                &mut tree.children[0],
377                layout,
378                renderer,
379                translation,
380            )
381        }
382    }
383}
384
385impl<'a, Message, Theme, Renderer> Draggable
386    for &Content<'a, Message, Theme, Renderer>
387where
388    Theme: container::Catalog,
389    Renderer: core::Renderer,
390{
391    fn can_be_dragged_at(
392        &self,
393        layout: Layout<'_>,
394        cursor_position: Point,
395    ) -> bool {
396        if let Some(title_bar) = &self.title_bar {
397            let mut children = layout.children();
398            let title_bar_layout = children.next().unwrap();
399
400            title_bar.is_over_pick_area(title_bar_layout, cursor_position)
401        } else {
402            false
403        }
404    }
405}
406
407impl<'a, T, Message, Theme, Renderer> From<T>
408    for Content<'a, Message, Theme, Renderer>
409where
410    T: Into<Element<'a, Message, Theme, Renderer>>,
411    Theme: container::Catalog + 'a,
412    Renderer: core::Renderer,
413{
414    fn from(element: T) -> Self {
415        Self::new(element)
416    }
417}