iced_native/widget/pane_grid/
title_bar.rs1use crate::event::{self, Event};
2use crate::layout;
3use crate::mouse;
4use crate::overlay;
5use crate::renderer;
6use crate::widget::container;
7use crate::widget::{self, Tree};
8use crate::{
9 Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size,
10};
11
12#[allow(missing_debug_implementations)]
16pub struct TitleBar<'a, Message, Renderer>
17where
18 Renderer: crate::Renderer,
19 Renderer::Theme: container::StyleSheet,
20{
21 content: Element<'a, Message, Renderer>,
22 controls: Option<Element<'a, Message, Renderer>>,
23 padding: Padding,
24 always_show_controls: bool,
25 style: <Renderer::Theme as container::StyleSheet>::Style,
26}
27
28impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer>
29where
30 Renderer: crate::Renderer,
31 Renderer::Theme: container::StyleSheet,
32{
33 pub fn new<E>(content: E) -> Self
35 where
36 E: Into<Element<'a, Message, Renderer>>,
37 {
38 Self {
39 content: content.into(),
40 controls: None,
41 padding: Padding::ZERO,
42 always_show_controls: false,
43 style: Default::default(),
44 }
45 }
46
47 pub fn controls(
49 mut self,
50 controls: impl Into<Element<'a, Message, Renderer>>,
51 ) -> Self {
52 self.controls = Some(controls.into());
53 self
54 }
55
56 pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
58 self.padding = padding.into();
59 self
60 }
61
62 pub fn style(
64 mut self,
65 style: impl Into<<Renderer::Theme as container::StyleSheet>::Style>,
66 ) -> Self {
67 self.style = style.into();
68 self
69 }
70
71 pub fn always_show_controls(mut self) -> Self {
80 self.always_show_controls = true;
81 self
82 }
83}
84
85impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer>
86where
87 Renderer: crate::Renderer,
88 Renderer::Theme: container::StyleSheet,
89{
90 pub(super) fn state(&self) -> Tree {
91 let children = if let Some(controls) = self.controls.as_ref() {
92 vec![Tree::new(&self.content), Tree::new(controls)]
93 } else {
94 vec![Tree::new(&self.content), Tree::empty()]
95 };
96
97 Tree {
98 children,
99 ..Tree::empty()
100 }
101 }
102
103 pub(super) fn diff(&self, tree: &mut Tree) {
104 if tree.children.len() == 2 {
105 if let Some(controls) = self.controls.as_ref() {
106 tree.children[1].diff(controls);
107 }
108
109 tree.children[0].diff(&self.content);
110 } else {
111 *tree = self.state();
112 }
113 }
114
115 pub fn draw(
119 &self,
120 tree: &Tree,
121 renderer: &mut Renderer,
122 theme: &Renderer::Theme,
123 inherited_style: &renderer::Style,
124 layout: Layout<'_>,
125 cursor_position: Point,
126 viewport: &Rectangle,
127 show_controls: bool,
128 ) {
129 use container::StyleSheet;
130
131 let bounds = layout.bounds();
132 let style = theme.appearance(&self.style);
133 let inherited_style = renderer::Style {
134 text_color: style.text_color.unwrap_or(inherited_style.text_color),
135 };
136
137 container::draw_background(renderer, &style, bounds);
138
139 let mut children = layout.children();
140 let padded = children.next().unwrap();
141
142 let mut children = padded.children();
143 let title_layout = children.next().unwrap();
144 let mut show_title = true;
145
146 if let Some(controls) = &self.controls {
147 if show_controls || self.always_show_controls {
148 let controls_layout = children.next().unwrap();
149 if title_layout.bounds().width + controls_layout.bounds().width
150 > padded.bounds().width
151 {
152 show_title = false;
153 }
154
155 controls.as_widget().draw(
156 &tree.children[1],
157 renderer,
158 theme,
159 &inherited_style,
160 controls_layout,
161 cursor_position,
162 viewport,
163 );
164 }
165 }
166
167 if show_title {
168 self.content.as_widget().draw(
169 &tree.children[0],
170 renderer,
171 theme,
172 &inherited_style,
173 title_layout,
174 cursor_position,
175 viewport,
176 );
177 }
178 }
179
180 pub fn is_over_pick_area(
185 &self,
186 layout: Layout<'_>,
187 cursor_position: Point,
188 ) -> bool {
189 if layout.bounds().contains(cursor_position) {
190 let mut children = layout.children();
191 let padded = children.next().unwrap();
192 let mut children = padded.children();
193 let title_layout = children.next().unwrap();
194
195 if self.controls.is_some() {
196 let controls_layout = children.next().unwrap();
197
198 if title_layout.bounds().width + controls_layout.bounds().width
199 > padded.bounds().width
200 {
201 !controls_layout.bounds().contains(cursor_position)
202 } else {
203 !controls_layout.bounds().contains(cursor_position)
204 && !title_layout.bounds().contains(cursor_position)
205 }
206 } else {
207 !title_layout.bounds().contains(cursor_position)
208 }
209 } else {
210 false
211 }
212 }
213
214 pub(crate) fn layout(
215 &self,
216 renderer: &Renderer,
217 limits: &layout::Limits,
218 ) -> layout::Node {
219 let limits = limits.pad(self.padding);
220 let max_size = limits.max();
221
222 let title_layout = self
223 .content
224 .as_widget()
225 .layout(renderer, &layout::Limits::new(Size::ZERO, max_size));
226
227 let title_size = title_layout.size();
228
229 let mut node = if let Some(controls) = &self.controls {
230 let mut controls_layout = controls
231 .as_widget()
232 .layout(renderer, &layout::Limits::new(Size::ZERO, max_size));
233
234 let controls_size = controls_layout.size();
235 let space_before_controls = max_size.width - controls_size.width;
236
237 let height = title_size.height.max(controls_size.height);
238
239 controls_layout.move_to(Point::new(space_before_controls, 0.0));
240
241 layout::Node::with_children(
242 Size::new(max_size.width, height),
243 vec![title_layout, controls_layout],
244 )
245 } else {
246 layout::Node::with_children(
247 Size::new(max_size.width, title_size.height),
248 vec![title_layout],
249 )
250 };
251
252 node.move_to(Point::new(self.padding.left, self.padding.top));
253
254 layout::Node::with_children(node.size().pad(self.padding), vec![node])
255 }
256
257 pub(crate) fn operate(
258 &self,
259 tree: &mut Tree,
260 layout: Layout<'_>,
261 renderer: &Renderer,
262 operation: &mut dyn widget::Operation<Message>,
263 ) {
264 let mut children = layout.children();
265 let padded = children.next().unwrap();
266
267 let mut children = padded.children();
268 let title_layout = children.next().unwrap();
269 let mut show_title = true;
270
271 if let Some(controls) = &self.controls {
272 let controls_layout = children.next().unwrap();
273
274 if title_layout.bounds().width + controls_layout.bounds().width
275 > padded.bounds().width
276 {
277 show_title = false;
278 }
279
280 controls.as_widget().operate(
281 &mut tree.children[1],
282 controls_layout,
283 renderer,
284 operation,
285 )
286 };
287
288 if show_title {
289 self.content.as_widget().operate(
290 &mut tree.children[0],
291 title_layout,
292 renderer,
293 operation,
294 )
295 }
296 }
297
298 pub(crate) fn on_event(
299 &mut self,
300 tree: &mut Tree,
301 event: Event,
302 layout: Layout<'_>,
303 cursor_position: Point,
304 renderer: &Renderer,
305 clipboard: &mut dyn Clipboard,
306 shell: &mut Shell<'_, Message>,
307 ) -> event::Status {
308 let mut children = layout.children();
309 let padded = children.next().unwrap();
310
311 let mut children = padded.children();
312 let title_layout = children.next().unwrap();
313 let mut show_title = true;
314
315 let control_status = if let Some(controls) = &mut self.controls {
316 let controls_layout = children.next().unwrap();
317 if title_layout.bounds().width + controls_layout.bounds().width
318 > padded.bounds().width
319 {
320 show_title = false;
321 }
322
323 controls.as_widget_mut().on_event(
324 &mut tree.children[1],
325 event.clone(),
326 controls_layout,
327 cursor_position,
328 renderer,
329 clipboard,
330 shell,
331 )
332 } else {
333 event::Status::Ignored
334 };
335
336 let title_status = if show_title {
337 self.content.as_widget_mut().on_event(
338 &mut tree.children[0],
339 event,
340 title_layout,
341 cursor_position,
342 renderer,
343 clipboard,
344 shell,
345 )
346 } else {
347 event::Status::Ignored
348 };
349
350 control_status.merge(title_status)
351 }
352
353 pub(crate) fn mouse_interaction(
354 &self,
355 tree: &Tree,
356 layout: Layout<'_>,
357 cursor_position: Point,
358 viewport: &Rectangle,
359 renderer: &Renderer,
360 ) -> mouse::Interaction {
361 let mut children = layout.children();
362 let padded = children.next().unwrap();
363
364 let mut children = padded.children();
365 let title_layout = children.next().unwrap();
366
367 let title_interaction = self.content.as_widget().mouse_interaction(
368 &tree.children[0],
369 title_layout,
370 cursor_position,
371 viewport,
372 renderer,
373 );
374
375 if let Some(controls) = &self.controls {
376 let controls_layout = children.next().unwrap();
377 let controls_interaction = controls.as_widget().mouse_interaction(
378 &tree.children[1],
379 controls_layout,
380 cursor_position,
381 viewport,
382 renderer,
383 );
384
385 if title_layout.bounds().width + controls_layout.bounds().width
386 > padded.bounds().width
387 {
388 controls_interaction
389 } else {
390 controls_interaction.max(title_interaction)
391 }
392 } else {
393 title_interaction
394 }
395 }
396
397 pub(crate) fn overlay<'b>(
398 &'b mut self,
399 tree: &'b mut Tree,
400 layout: Layout<'_>,
401 renderer: &Renderer,
402 ) -> Option<overlay::Element<'b, Message, Renderer>> {
403 let mut children = layout.children();
404 let padded = children.next()?;
405
406 let mut children = padded.children();
407 let title_layout = children.next()?;
408
409 let Self {
410 content, controls, ..
411 } = self;
412
413 let mut states = tree.children.iter_mut();
414 let title_state = states.next().unwrap();
415 let controls_state = states.next().unwrap();
416
417 content
418 .as_widget_mut()
419 .overlay(title_state, title_layout, renderer)
420 .or_else(move || {
421 controls.as_mut().and_then(|controls| {
422 let controls_layout = children.next()?;
423
424 controls.as_widget_mut().overlay(
425 controls_state,
426 controls_layout,
427 renderer,
428 )
429 })
430 })
431 }
432}