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, Point, Rectangle, Shell, Size,
9 Vector,
10};
11use crate::pane_grid::{Draggable, TitleBar};
12
13pub struct Content<
17 'a,
18 Message,
19 Theme = crate::Theme,
20 Renderer = crate::Renderer,
21> where
22 Theme: container::Catalog,
23 Renderer: core::Renderer,
24{
25 title_bar: Option<TitleBar<'a, Message, Theme, Renderer>>,
26 body: Element<'a, Message, Theme, Renderer>,
27 class: Theme::Class<'a>,
28}
29
30impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer>
31where
32 Theme: container::Catalog,
33 Renderer: core::Renderer,
34{
35 pub fn new(body: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self {
37 Self {
38 title_bar: None,
39 body: body.into(),
40 class: Theme::default(),
41 }
42 }
43
44 pub fn title_bar(
46 mut self,
47 title_bar: TitleBar<'a, Message, Theme, Renderer>,
48 ) -> Self {
49 self.title_bar = Some(title_bar);
50 self
51 }
52
53 #[must_use]
55 pub fn style(
56 mut self,
57 style: impl Fn(&Theme) -> container::Style + 'a,
58 ) -> Self
59 where
60 Theme::Class<'a>: From<container::StyleFn<'a, Theme>>,
61 {
62 self.class = (Box::new(style) as container::StyleFn<'a, Theme>).into();
63 self
64 }
65
66 #[cfg(feature = "advanced")]
68 #[must_use]
69 pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
70 self.class = class.into();
71 self
72 }
73}
74
75impl<Message, Theme, Renderer> Content<'_, Message, Theme, Renderer>
76where
77 Theme: container::Catalog,
78 Renderer: core::Renderer,
79{
80 pub(super) fn state(&self) -> Tree {
81 let children = if let Some(title_bar) = self.title_bar.as_ref() {
82 vec![Tree::new(&self.body), title_bar.state()]
83 } else {
84 vec![Tree::new(&self.body), Tree::empty()]
85 };
86
87 Tree {
88 children,
89 ..Tree::empty()
90 }
91 }
92
93 pub(super) fn diff(&self, tree: &mut Tree) {
94 if tree.children.len() == 2 {
95 if let Some(title_bar) = self.title_bar.as_ref() {
96 title_bar.diff(&mut tree.children[1]);
97 }
98
99 tree.children[0].diff(&self.body);
100 } else {
101 *tree = self.state();
102 }
103 }
104
105 pub fn draw(
109 &self,
110 tree: &Tree,
111 renderer: &mut Renderer,
112 theme: &Theme,
113 style: &renderer::Style,
114 layout: Layout<'_>,
115 cursor: mouse::Cursor,
116 viewport: &Rectangle,
117 ) {
118 let bounds = layout.bounds();
119
120 {
121 let style = theme.style(&self.class);
122
123 container::draw_background(renderer, &style, bounds);
124 }
125
126 if let Some(title_bar) = &self.title_bar {
127 let mut children = layout.children();
128 let title_bar_layout = children.next().unwrap();
129 let body_layout = children.next().unwrap();
130
131 let show_controls = cursor.is_over(bounds);
132
133 self.body.as_widget().draw(
134 &tree.children[0],
135 renderer,
136 theme,
137 style,
138 body_layout,
139 cursor,
140 viewport,
141 );
142
143 title_bar.draw(
144 &tree.children[1],
145 renderer,
146 theme,
147 style,
148 title_bar_layout,
149 cursor,
150 viewport,
151 show_controls,
152 );
153 } else {
154 self.body.as_widget().draw(
155 &tree.children[0],
156 renderer,
157 theme,
158 style,
159 layout,
160 cursor,
161 viewport,
162 );
163 }
164 }
165
166 pub(crate) fn layout(
167 &mut self,
168 tree: &mut Tree,
169 renderer: &Renderer,
170 limits: &layout::Limits,
171 ) -> layout::Node {
172 if let Some(title_bar) = &mut self.title_bar {
173 let max_size = limits.max();
174
175 let title_bar_layout = title_bar.layout(
176 &mut tree.children[1],
177 renderer,
178 &layout::Limits::new(Size::ZERO, max_size),
179 );
180
181 let title_bar_size = title_bar_layout.size();
182
183 let body_layout = self.body.as_widget_mut().layout(
184 &mut tree.children[0],
185 renderer,
186 &layout::Limits::new(
187 Size::ZERO,
188 Size::new(
189 max_size.width,
190 max_size.height - title_bar_size.height,
191 ),
192 ),
193 );
194
195 layout::Node::with_children(
196 max_size,
197 vec![
198 title_bar_layout,
199 body_layout.move_to(Point::new(0.0, title_bar_size.height)),
200 ],
201 )
202 } else {
203 self.body.as_widget_mut().layout(
204 &mut tree.children[0],
205 renderer,
206 limits,
207 )
208 }
209 }
210
211 pub(crate) fn operate(
212 &mut self,
213 tree: &mut Tree,
214 layout: Layout<'_>,
215 renderer: &Renderer,
216 operation: &mut dyn widget::Operation,
217 ) {
218 let body_layout = if let Some(title_bar) = &mut self.title_bar {
219 let mut children = layout.children();
220
221 title_bar.operate(
222 &mut tree.children[1],
223 children.next().unwrap(),
224 renderer,
225 operation,
226 );
227
228 children.next().unwrap()
229 } else {
230 layout
231 };
232
233 self.body.as_widget_mut().operate(
234 &mut tree.children[0],
235 body_layout,
236 renderer,
237 operation,
238 );
239 }
240
241 pub(crate) fn update(
242 &mut self,
243 tree: &mut Tree,
244 event: &Event,
245 layout: Layout<'_>,
246 cursor: mouse::Cursor,
247 renderer: &Renderer,
248 clipboard: &mut dyn Clipboard,
249 shell: &mut Shell<'_, Message>,
250 viewport: &Rectangle,
251 is_picked: bool,
252 ) {
253 let body_layout = if let Some(title_bar) = &mut self.title_bar {
254 let mut children = layout.children();
255
256 title_bar.update(
257 &mut tree.children[1],
258 event,
259 children.next().unwrap(),
260 cursor,
261 renderer,
262 clipboard,
263 shell,
264 viewport,
265 );
266
267 children.next().unwrap()
268 } else {
269 layout
270 };
271
272 if !is_picked {
273 self.body.as_widget_mut().update(
274 &mut tree.children[0],
275 event,
276 body_layout,
277 cursor,
278 renderer,
279 clipboard,
280 shell,
281 viewport,
282 );
283 }
284 }
285
286 pub(crate) fn grid_interaction(
287 &self,
288 layout: Layout<'_>,
289 cursor: mouse::Cursor,
290 drag_enabled: bool,
291 ) -> Option<mouse::Interaction> {
292 let title_bar = self.title_bar.as_ref()?;
293
294 let mut children = layout.children();
295 let title_bar_layout = children.next().unwrap();
296
297 let is_over_pick_area = cursor
298 .position()
299 .map(|cursor_position| {
300 title_bar.is_over_pick_area(title_bar_layout, cursor_position)
301 })
302 .unwrap_or_default();
303
304 if is_over_pick_area && drag_enabled {
305 return Some(mouse::Interaction::Grab);
306 }
307
308 None
309 }
310
311 pub(crate) fn mouse_interaction(
312 &self,
313 tree: &Tree,
314 layout: Layout<'_>,
315 cursor: mouse::Cursor,
316 viewport: &Rectangle,
317 renderer: &Renderer,
318 drag_enabled: bool,
319 ) -> mouse::Interaction {
320 let (body_layout, title_bar_interaction) = if let Some(title_bar) =
321 &self.title_bar
322 {
323 let mut children = layout.children();
324 let title_bar_layout = children.next().unwrap();
325
326 let is_over_pick_area = cursor
327 .position()
328 .map(|cursor_position| {
329 title_bar
330 .is_over_pick_area(title_bar_layout, cursor_position)
331 })
332 .unwrap_or_default();
333
334 if is_over_pick_area && drag_enabled {
335 return mouse::Interaction::Grab;
336 }
337
338 let mouse_interaction = title_bar.mouse_interaction(
339 &tree.children[1],
340 title_bar_layout,
341 cursor,
342 viewport,
343 renderer,
344 );
345
346 (children.next().unwrap(), mouse_interaction)
347 } else {
348 (layout, mouse::Interaction::default())
349 };
350
351 self.body
352 .as_widget()
353 .mouse_interaction(
354 &tree.children[0],
355 body_layout,
356 cursor,
357 viewport,
358 renderer,
359 )
360 .max(title_bar_interaction)
361 }
362
363 pub(crate) fn overlay<'b>(
364 &'b mut self,
365 tree: &'b mut Tree,
366 layout: Layout<'b>,
367 renderer: &Renderer,
368 viewport: &Rectangle,
369 translation: Vector,
370 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
371 if let Some(title_bar) = self.title_bar.as_mut() {
372 let mut children = layout.children();
373 let title_bar_layout = children.next()?;
374
375 let mut states = tree.children.iter_mut();
376 let body_state = states.next().unwrap();
377 let title_bar_state = states.next().unwrap();
378
379 match title_bar.overlay(
380 title_bar_state,
381 title_bar_layout,
382 renderer,
383 viewport,
384 translation,
385 ) {
386 Some(overlay) => Some(overlay),
387 None => self.body.as_widget_mut().overlay(
388 body_state,
389 children.next()?,
390 renderer,
391 viewport,
392 translation,
393 ),
394 }
395 } else {
396 self.body.as_widget_mut().overlay(
397 &mut tree.children[0],
398 layout,
399 renderer,
400 viewport,
401 translation,
402 )
403 }
404 }
405}
406
407impl<Message, Theme, Renderer> Draggable
408 for &Content<'_, Message, Theme, Renderer>
409where
410 Theme: container::Catalog,
411 Renderer: core::Renderer,
412{
413 fn can_be_dragged_at(
414 &self,
415 layout: Layout<'_>,
416 cursor_position: Point,
417 ) -> bool {
418 if let Some(title_bar) = &self.title_bar {
419 let mut children = layout.children();
420 let title_bar_layout = children.next().unwrap();
421
422 title_bar.is_over_pick_area(title_bar_layout, cursor_position)
423 } else {
424 false
425 }
426 }
427}
428
429impl<'a, T, Message, Theme, Renderer> From<T>
430 for Content<'a, Message, Theme, Renderer>
431where
432 T: Into<Element<'a, Message, Theme, Renderer>>,
433 Theme: container::Catalog + 'a,
434 Renderer: core::Renderer,
435{
436 fn from(element: T) -> Self {
437 Self::new(element)
438 }
439}