1use crate::container;
2use crate::core::event::{self, Event};
3use crate::core::layout;
4use crate::core::mouse;
5use crate::core::overlay;
6use crate::core::renderer;
7use crate::core::widget::{self, Tree};
8use crate::core::{
9 self, Clipboard, Element, Layout, Point, Rectangle, Shell, Size, 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<'a, Message, Theme, Renderer> Content<'a, 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(&mut self, tree: &mut Tree) {
95 if tree.children.len() == 2 {
96 if let Some(title_bar) = self.title_bar.as_mut() {
97 title_bar.diff(&mut tree.children[1]);
98 }
99
100 tree.children[0].diff(&mut 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 on_event(
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 ) -> event::Status {
254 let mut event_status = event::Status::Ignored;
255
256 let body_layout = if let Some(title_bar) = &mut self.title_bar {
257 let mut children = layout.children();
258
259 event_status = title_bar.on_event(
260 &mut tree.children[1],
261 event.clone(),
262 children.next().unwrap(),
263 cursor,
264 renderer,
265 clipboard,
266 shell,
267 viewport,
268 );
269
270 children.next().unwrap()
271 } else {
272 layout
273 };
274
275 let body_status = if is_picked {
276 event::Status::Ignored
277 } else {
278 self.body.as_widget_mut().on_event(
279 &mut tree.children[0],
280 event,
281 body_layout,
282 cursor,
283 renderer,
284 clipboard,
285 shell,
286 viewport,
287 )
288 };
289
290 event_status.merge(body_status)
291 }
292
293 pub(crate) fn mouse_interaction(
294 &self,
295 tree: &Tree,
296 layout: Layout<'_>,
297 cursor: mouse::Cursor,
298 viewport: &Rectangle,
299 renderer: &Renderer,
300 drag_enabled: bool,
301 ) -> mouse::Interaction {
302 let (body_layout, title_bar_interaction) = if let Some(title_bar) =
303 &self.title_bar
304 {
305 let mut children = layout.children();
306 let title_bar_layout = children.next().unwrap();
307
308 let is_over_pick_area = cursor
309 .position()
310 .map(|cursor_position| {
311 title_bar
312 .is_over_pick_area(title_bar_layout, cursor_position)
313 })
314 .unwrap_or_default();
315
316 if is_over_pick_area && drag_enabled {
317 return mouse::Interaction::Grab;
318 }
319
320 let mouse_interaction = title_bar.mouse_interaction(
321 &tree.children[1],
322 title_bar_layout,
323 cursor,
324 viewport,
325 renderer,
326 );
327
328 (children.next().unwrap(), mouse_interaction)
329 } else {
330 (layout, mouse::Interaction::default())
331 };
332
333 self.body
334 .as_widget()
335 .mouse_interaction(
336 &tree.children[0],
337 body_layout,
338 cursor,
339 viewport,
340 renderer,
341 )
342 .max(title_bar_interaction)
343 }
344
345 pub(crate) fn overlay<'b>(
346 &'b mut self,
347 tree: &'b mut Tree,
348 layout: Layout<'_>,
349 renderer: &Renderer,
350 translation: Vector,
351 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
352 if let Some(title_bar) = self.title_bar.as_mut() {
353 let mut children = layout.children();
354 let title_bar_layout = children.next()?;
355
356 let mut states = tree.children.iter_mut();
357 let body_state = states.next().unwrap();
358 let title_bar_state = states.next().unwrap();
359
360 match title_bar.overlay(
361 title_bar_state,
362 title_bar_layout,
363 renderer,
364 translation,
365 ) {
366 Some(overlay) => Some(overlay),
367 None => self.body.as_widget_mut().overlay(
368 body_state,
369 children.next()?,
370 renderer,
371 translation,
372 ),
373 }
374 } else {
375 self.body.as_widget_mut().overlay(
376 &mut tree.children[0],
377 layout,
378 renderer,
379 translation,
380 )
381 }
382 }
383}
384
385impl<'a, Message, Theme, Renderer> Draggable
386 for &Content<'a, Message, Theme, Renderer>
387where
388 Theme: container::Catalog,
389 Renderer: core::Renderer,
390{
391 fn can_be_dragged_at(
392 &self,
393 layout: Layout<'_>,
394 cursor_position: Point,
395 ) -> bool {
396 if let Some(title_bar) = &self.title_bar {
397 let mut children = layout.children();
398 let title_bar_layout = children.next().unwrap();
399
400 title_bar.is_over_pick_area(title_bar_layout, cursor_position)
401 } else {
402 false
403 }
404 }
405}
406
407impl<'a, T, Message, Theme, Renderer> From<T>
408 for Content<'a, Message, Theme, Renderer>
409where
410 T: Into<Element<'a, Message, Theme, Renderer>>,
411 Theme: container::Catalog + 'a,
412 Renderer: core::Renderer,
413{
414 fn from(element: T) -> Self {
415 Self::new(element)
416 }
417}