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