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