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
13pub 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 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 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 pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
61 self.padding = padding.into();
62 self
63 }
64
65 pub fn always_show_controls(mut self) -> Self {
74 self.always_show_controls = true;
75 self
76 }
77
78 #[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 #[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 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 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}