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