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