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