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, 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 shell: &mut Shell<'_, Message>,
228 viewport: &Rectangle,
229 is_picked: bool,
230 ) {
231 let body_layout = if let Some(title_bar) = &mut self.title_bar {
232 let mut children = layout.children();
233
234 title_bar.update(
235 &mut tree.children[1],
236 event,
237 children.next().unwrap(),
238 cursor,
239 renderer,
240 shell,
241 viewport,
242 );
243
244 children.next().unwrap()
245 } else {
246 layout
247 };
248
249 if !is_picked {
250 self.body.as_widget_mut().update(
251 &mut tree.children[0],
252 event,
253 body_layout,
254 cursor,
255 renderer,
256 shell,
257 viewport,
258 );
259 }
260 }
261
262 pub(crate) fn grid_interaction(
263 &self,
264 layout: Layout<'_>,
265 cursor: mouse::Cursor,
266 drag_enabled: bool,
267 ) -> Option<mouse::Interaction> {
268 let title_bar = self.title_bar.as_ref()?;
269
270 let mut children = layout.children();
271 let title_bar_layout = children.next().unwrap();
272
273 let is_over_pick_area = cursor
274 .position()
275 .map(|cursor_position| title_bar.is_over_pick_area(title_bar_layout, cursor_position))
276 .unwrap_or_default();
277
278 if is_over_pick_area && drag_enabled {
279 return Some(mouse::Interaction::Grab);
280 }
281
282 None
283 }
284
285 pub(crate) fn mouse_interaction(
286 &self,
287 tree: &Tree,
288 layout: Layout<'_>,
289 cursor: mouse::Cursor,
290 viewport: &Rectangle,
291 renderer: &Renderer,
292 drag_enabled: bool,
293 ) -> mouse::Interaction {
294 let (body_layout, title_bar_interaction) = if let Some(title_bar) = &self.title_bar {
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 mouse::Interaction::Grab;
307 }
308
309 let mouse_interaction = title_bar.mouse_interaction(
310 &tree.children[1],
311 title_bar_layout,
312 cursor,
313 viewport,
314 renderer,
315 );
316
317 (children.next().unwrap(), mouse_interaction)
318 } else {
319 (layout, mouse::Interaction::default())
320 };
321
322 self.body
323 .as_widget()
324 .mouse_interaction(&tree.children[0], body_layout, cursor, viewport, renderer)
325 .max(title_bar_interaction)
326 }
327
328 pub(crate) fn overlay<'b>(
329 &'b mut self,
330 tree: &'b mut Tree,
331 layout: Layout<'b>,
332 renderer: &Renderer,
333 viewport: &Rectangle,
334 translation: Vector,
335 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
336 if let Some(title_bar) = self.title_bar.as_mut() {
337 let mut children = layout.children();
338 let title_bar_layout = children.next()?;
339
340 let mut states = tree.children.iter_mut();
341 let body_state = states.next().unwrap();
342 let title_bar_state = states.next().unwrap();
343
344 match title_bar.overlay(
345 title_bar_state,
346 title_bar_layout,
347 renderer,
348 viewport,
349 translation,
350 ) {
351 Some(overlay) => Some(overlay),
352 None => self.body.as_widget_mut().overlay(
353 body_state,
354 children.next()?,
355 renderer,
356 viewport,
357 translation,
358 ),
359 }
360 } else {
361 self.body.as_widget_mut().overlay(
362 &mut tree.children[0],
363 layout,
364 renderer,
365 viewport,
366 translation,
367 )
368 }
369 }
370}
371
372impl<Message, Theme, Renderer> Draggable for &Content<'_, Message, Theme, Renderer>
373where
374 Theme: container::Catalog,
375 Renderer: core::Renderer,
376{
377 fn can_be_dragged_at(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
378 if let Some(title_bar) = &self.title_bar {
379 let mut children = layout.children();
380 let title_bar_layout = children.next().unwrap();
381
382 title_bar.is_over_pick_area(title_bar_layout, cursor_position)
383 } else {
384 false
385 }
386 }
387}
388
389impl<'a, T, Message, Theme, Renderer> From<T> for Content<'a, Message, Theme, Renderer>
390where
391 T: Into<Element<'a, Message, Theme, Renderer>>,
392 Theme: container::Catalog + 'a,
393 Renderer: core::Renderer,
394{
395 fn from(element: T) -> Self {
396 Self::new(element)
397 }
398}