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