Skip to main content

iced_widget/pane_grid/
title_bar.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, Padding, Point, Rectangle, Shell, Size, Vector};
8use crate::pane_grid::controls::Controls;
9
10/// The title bar of a [`Pane`].
11///
12/// [`Pane`]: super::Pane
13pub struct TitleBar<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
14where
15    Theme: container::Catalog,
16    Renderer: core::Renderer,
17{
18    content: Element<'a, Message, Theme, Renderer>,
19    controls: Option<Controls<'a, Message, Theme, Renderer>>,
20    padding: Padding,
21    always_show_controls: bool,
22    class: Theme::Class<'a>,
23}
24
25impl<'a, Message, Theme, Renderer> TitleBar<'a, Message, Theme, Renderer>
26where
27    Theme: container::Catalog,
28    Renderer: core::Renderer,
29{
30    /// Creates a new [`TitleBar`] with the given content.
31    pub fn new(content: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self {
32        Self {
33            content: content.into(),
34            controls: None,
35            padding: Padding::ZERO,
36            always_show_controls: false,
37            class: Theme::default(),
38        }
39    }
40
41    /// Sets the controls of the [`TitleBar`].
42    pub fn controls(mut self, controls: impl Into<Controls<'a, Message, Theme, Renderer>>) -> Self {
43        self.controls = Some(controls.into());
44        self
45    }
46
47    /// Sets the [`Padding`] of the [`TitleBar`].
48    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
49        self.padding = padding.into();
50        self
51    }
52
53    /// Sets whether or not the [`controls`] attached to this [`TitleBar`] are
54    /// always visible.
55    ///
56    /// By default, the controls are only visible when the [`Pane`] of this
57    /// [`TitleBar`] is hovered.
58    ///
59    /// [`controls`]: Self::controls
60    /// [`Pane`]: super::Pane
61    pub fn always_show_controls(mut self) -> Self {
62        self.always_show_controls = true;
63        self
64    }
65
66    /// Sets the style of the [`TitleBar`].
67    #[must_use]
68    pub fn style(mut self, style: impl Fn(&Theme) -> container::Style + 'a) -> Self
69    where
70        Theme::Class<'a>: From<container::StyleFn<'a, Theme>>,
71    {
72        self.class = (Box::new(style) as container::StyleFn<'a, Theme>).into();
73        self
74    }
75
76    /// Sets the style class of the [`TitleBar`].
77    #[cfg(feature = "advanced")]
78    #[must_use]
79    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
80        self.class = class.into();
81        self
82    }
83}
84
85impl<Message, Theme, Renderer> TitleBar<'_, Message, Theme, Renderer>
86where
87    Theme: container::Catalog,
88    Renderer: core::Renderer,
89{
90    pub(super) fn state(&self) -> Tree {
91        let children = match self.controls.as_ref() {
92            Some(controls) => match controls.compact.as_ref() {
93                Some(compact) => vec![
94                    Tree::new(&self.content),
95                    Tree::new(&controls.full),
96                    Tree::new(compact),
97                ],
98                None => vec![
99                    Tree::new(&self.content),
100                    Tree::new(&controls.full),
101                    Tree::empty(),
102                ],
103            },
104            None => {
105                vec![Tree::new(&self.content), Tree::empty(), Tree::empty()]
106            }
107        };
108
109        Tree {
110            children,
111            ..Tree::empty()
112        }
113    }
114
115    pub(super) fn diff(&self, tree: &mut Tree) {
116        if tree.children.len() == 3 {
117            if let Some(controls) = self.controls.as_ref() {
118                if let Some(compact) = controls.compact.as_ref() {
119                    tree.children[2].diff(compact);
120                }
121
122                tree.children[1].diff(&controls.full);
123            }
124
125            tree.children[0].diff(&self.content);
126        } else {
127            *tree = self.state();
128        }
129    }
130
131    /// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`].
132    ///
133    /// [`Renderer`]: core::Renderer
134    pub fn draw(
135        &self,
136        tree: &Tree,
137        renderer: &mut Renderer,
138        theme: &Theme,
139        inherited_style: &renderer::Style,
140        layout: Layout<'_>,
141        cursor: mouse::Cursor,
142        viewport: &Rectangle,
143        show_controls: bool,
144    ) {
145        let bounds = layout.bounds();
146        let style = theme.style(&self.class);
147
148        let inherited_style = renderer::Style {
149            text_color: style.text_color.unwrap_or(inherited_style.text_color),
150        };
151
152        container::draw_background(renderer, &style, bounds);
153
154        let mut children = layout.children();
155        let padded = children.next().unwrap();
156
157        let mut children = padded.children();
158        let title_layout = children.next().unwrap();
159        let mut show_title = true;
160
161        if let Some(controls) = &self.controls
162            && (show_controls || self.always_show_controls)
163        {
164            let controls_layout = children.next().unwrap();
165            if title_layout.bounds().width + controls_layout.bounds().width > padded.bounds().width
166            {
167                if let Some(compact) = controls.compact.as_ref() {
168                    let compact_layout = children.next().unwrap();
169
170                    compact.as_widget().draw(
171                        &tree.children[2],
172                        renderer,
173                        theme,
174                        &inherited_style,
175                        compact_layout,
176                        cursor,
177                        viewport,
178                    );
179                } else {
180                    show_title = false;
181
182                    controls.full.as_widget().draw(
183                        &tree.children[1],
184                        renderer,
185                        theme,
186                        &inherited_style,
187                        controls_layout,
188                        cursor,
189                        viewport,
190                    );
191                }
192            } else {
193                controls.full.as_widget().draw(
194                    &tree.children[1],
195                    renderer,
196                    theme,
197                    &inherited_style,
198                    controls_layout,
199                    cursor,
200                    viewport,
201                );
202            }
203        }
204
205        if show_title {
206            self.content.as_widget().draw(
207                &tree.children[0],
208                renderer,
209                theme,
210                &inherited_style,
211                title_layout,
212                cursor,
213                viewport,
214            );
215        }
216    }
217
218    /// Returns whether the mouse cursor is over the pick area of the
219    /// [`TitleBar`] or not.
220    ///
221    /// The whole [`TitleBar`] is a pick area, except its controls.
222    pub fn is_over_pick_area(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
223        if layout.bounds().contains(cursor_position) {
224            let mut children = layout.children();
225            let padded = children.next().unwrap();
226            let mut children = padded.children();
227            let title_layout = children.next().unwrap();
228
229            if let Some(controls) = self.controls.as_ref() {
230                let controls_layout = children.next().unwrap();
231
232                if title_layout.bounds().width + controls_layout.bounds().width
233                    > padded.bounds().width
234                {
235                    if controls.compact.is_some() {
236                        let compact_layout = children.next().unwrap();
237
238                        !compact_layout.bounds().contains(cursor_position)
239                            && !title_layout.bounds().contains(cursor_position)
240                    } else {
241                        !controls_layout.bounds().contains(cursor_position)
242                    }
243                } else {
244                    !controls_layout.bounds().contains(cursor_position)
245                        && !title_layout.bounds().contains(cursor_position)
246                }
247            } else {
248                !title_layout.bounds().contains(cursor_position)
249            }
250        } else {
251            false
252        }
253    }
254
255    pub(crate) fn layout(
256        &mut self,
257        tree: &mut Tree,
258        renderer: &Renderer,
259        limits: &layout::Limits,
260    ) -> layout::Node {
261        let limits = limits.shrink(self.padding);
262        let max_size = limits.max();
263
264        let title_layout = self.content.as_widget_mut().layout(
265            &mut tree.children[0],
266            renderer,
267            &layout::Limits::new(Size::ZERO, max_size),
268        );
269
270        let title_size = title_layout.size();
271
272        let node = if let Some(controls) = &mut self.controls {
273            let controls_layout = controls.full.as_widget_mut().layout(
274                &mut tree.children[1],
275                renderer,
276                &layout::Limits::new(Size::ZERO, max_size),
277            );
278
279            if title_layout.bounds().width + controls_layout.bounds().width > max_size.width {
280                if let Some(compact) = controls.compact.as_mut() {
281                    let compact_layout = compact.as_widget_mut().layout(
282                        &mut tree.children[2],
283                        renderer,
284                        &layout::Limits::new(Size::ZERO, max_size),
285                    );
286
287                    let compact_size = compact_layout.size();
288                    let space_before_controls = max_size.width - compact_size.width;
289
290                    let height = title_size.height.max(compact_size.height);
291
292                    layout::Node::with_children(
293                        Size::new(max_size.width, height),
294                        vec![
295                            title_layout,
296                            controls_layout,
297                            compact_layout.move_to(Point::new(space_before_controls, 0.0)),
298                        ],
299                    )
300                } else {
301                    let controls_size = controls_layout.size();
302                    let space_before_controls = max_size.width - controls_size.width;
303
304                    let height = title_size.height.max(controls_size.height);
305
306                    layout::Node::with_children(
307                        Size::new(max_size.width, height),
308                        vec![
309                            title_layout,
310                            controls_layout.move_to(Point::new(space_before_controls, 0.0)),
311                        ],
312                    )
313                }
314            } else {
315                let controls_size = controls_layout.size();
316                let space_before_controls = max_size.width - controls_size.width;
317
318                let height = title_size.height.max(controls_size.height);
319
320                layout::Node::with_children(
321                    Size::new(max_size.width, height),
322                    vec![
323                        title_layout,
324                        controls_layout.move_to(Point::new(space_before_controls, 0.0)),
325                    ],
326                )
327            }
328        } else {
329            layout::Node::with_children(
330                Size::new(max_size.width, title_size.height),
331                vec![title_layout],
332            )
333        };
334
335        layout::Node::container(node, self.padding)
336    }
337
338    pub(crate) fn operate(
339        &mut self,
340        tree: &mut Tree,
341        layout: Layout<'_>,
342        renderer: &Renderer,
343        operation: &mut dyn widget::Operation,
344    ) {
345        let mut children = layout.children();
346        let padded = children.next().unwrap();
347
348        let mut children = padded.children();
349        let title_layout = children.next().unwrap();
350        let mut show_title = true;
351
352        if let Some(controls) = &mut self.controls {
353            let controls_layout = children.next().unwrap();
354
355            if title_layout.bounds().width + controls_layout.bounds().width > padded.bounds().width
356            {
357                if let Some(compact) = controls.compact.as_mut() {
358                    let compact_layout = children.next().unwrap();
359
360                    compact.as_widget_mut().operate(
361                        &mut tree.children[2],
362                        compact_layout,
363                        renderer,
364                        operation,
365                    );
366                } else {
367                    show_title = false;
368
369                    controls.full.as_widget_mut().operate(
370                        &mut tree.children[1],
371                        controls_layout,
372                        renderer,
373                        operation,
374                    );
375                }
376            } else {
377                controls.full.as_widget_mut().operate(
378                    &mut tree.children[1],
379                    controls_layout,
380                    renderer,
381                    operation,
382                );
383            }
384        };
385
386        if show_title {
387            self.content.as_widget_mut().operate(
388                &mut tree.children[0],
389                title_layout,
390                renderer,
391                operation,
392            );
393        }
394    }
395
396    pub(crate) fn update(
397        &mut self,
398        tree: &mut Tree,
399        event: &Event,
400        layout: Layout<'_>,
401        cursor: mouse::Cursor,
402        renderer: &Renderer,
403        shell: &mut Shell<'_, Message>,
404        viewport: &Rectangle,
405    ) {
406        let mut children = layout.children();
407        let padded = children.next().unwrap();
408
409        let mut children = padded.children();
410        let title_layout = children.next().unwrap();
411        let mut show_title = true;
412
413        if let Some(controls) = &mut self.controls {
414            let controls_layout = children.next().unwrap();
415
416            if title_layout.bounds().width + controls_layout.bounds().width > padded.bounds().width
417            {
418                if let Some(compact) = controls.compact.as_mut() {
419                    let compact_layout = children.next().unwrap();
420
421                    compact.as_widget_mut().update(
422                        &mut tree.children[2],
423                        event,
424                        compact_layout,
425                        cursor,
426                        renderer,
427                        shell,
428                        viewport,
429                    );
430                } else {
431                    show_title = false;
432
433                    controls.full.as_widget_mut().update(
434                        &mut tree.children[1],
435                        event,
436                        controls_layout,
437                        cursor,
438                        renderer,
439                        shell,
440                        viewport,
441                    );
442                }
443            } else {
444                controls.full.as_widget_mut().update(
445                    &mut tree.children[1],
446                    event,
447                    controls_layout,
448                    cursor,
449                    renderer,
450                    shell,
451                    viewport,
452                );
453            }
454        }
455
456        if show_title {
457            self.content.as_widget_mut().update(
458                &mut tree.children[0],
459                event,
460                title_layout,
461                cursor,
462                renderer,
463                shell,
464                viewport,
465            );
466        }
467    }
468
469    pub(crate) fn mouse_interaction(
470        &self,
471        tree: &Tree,
472        layout: Layout<'_>,
473        cursor: mouse::Cursor,
474        viewport: &Rectangle,
475        renderer: &Renderer,
476    ) -> mouse::Interaction {
477        let mut children = layout.children();
478        let padded = children.next().unwrap();
479
480        let mut children = padded.children();
481        let title_layout = children.next().unwrap();
482
483        let title_interaction = self.content.as_widget().mouse_interaction(
484            &tree.children[0],
485            title_layout,
486            cursor,
487            viewport,
488            renderer,
489        );
490
491        if let Some(controls) = &self.controls {
492            let controls_layout = children.next().unwrap();
493            let controls_interaction = controls.full.as_widget().mouse_interaction(
494                &tree.children[1],
495                controls_layout,
496                cursor,
497                viewport,
498                renderer,
499            );
500
501            if title_layout.bounds().width + controls_layout.bounds().width > padded.bounds().width
502            {
503                if let Some(compact) = controls.compact.as_ref() {
504                    let compact_layout = children.next().unwrap();
505                    let compact_interaction = compact.as_widget().mouse_interaction(
506                        &tree.children[2],
507                        compact_layout,
508                        cursor,
509                        viewport,
510                        renderer,
511                    );
512
513                    compact_interaction.max(title_interaction)
514                } else {
515                    controls_interaction
516                }
517            } else {
518                controls_interaction.max(title_interaction)
519            }
520        } else {
521            title_interaction
522        }
523    }
524
525    pub(crate) fn overlay<'b>(
526        &'b mut self,
527        tree: &'b mut Tree,
528        layout: Layout<'b>,
529        renderer: &Renderer,
530        viewport: &Rectangle,
531        translation: Vector,
532    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
533        let mut children = layout.children();
534        let padded = children.next()?;
535
536        let mut children = padded.children();
537        let title_layout = children.next()?;
538
539        let Self {
540            content, controls, ..
541        } = self;
542
543        let mut states = tree.children.iter_mut();
544        let title_state = states.next().unwrap();
545        let controls_state = states.next().unwrap();
546
547        content
548            .as_widget_mut()
549            .overlay(title_state, title_layout, renderer, viewport, translation)
550            .or_else(move || {
551                controls.as_mut().and_then(|controls| {
552                    let controls_layout = children.next()?;
553
554                    if title_layout.bounds().width + controls_layout.bounds().width
555                        > padded.bounds().width
556                    {
557                        if let Some(compact) = controls.compact.as_mut() {
558                            let compact_state = states.next().unwrap();
559                            let compact_layout = children.next()?;
560
561                            compact.as_widget_mut().overlay(
562                                compact_state,
563                                compact_layout,
564                                renderer,
565                                viewport,
566                                translation,
567                            )
568                        } else {
569                            controls.full.as_widget_mut().overlay(
570                                controls_state,
571                                controls_layout,
572                                renderer,
573                                viewport,
574                                translation,
575                            )
576                        }
577                    } else {
578                        controls.full.as_widget_mut().overlay(
579                            controls_state,
580                            controls_layout,
581                            renderer,
582                            viewport,
583                            translation,
584                        )
585                    }
586                })
587            })
588    }
589}