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