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