1use crate::core::layout::{self, Layout};
2use crate::core::mouse;
3use crate::core::overlay;
4use crate::core::renderer;
5use crate::core::widget;
6use crate::core::widget::tree::{self, Tree};
7use crate::core::{
8 self, Clipboard, Element, Event, Length, Point, Rectangle, Shell, Size,
9 Vector, Widget,
10};
11use crate::horizontal_space;
12use crate::runtime::overlay::Nested;
13
14use ouroboros::self_referencing;
15use std::cell::{RefCell, RefMut};
16use std::marker::PhantomData;
17use std::ops::Deref;
18
19#[cfg(feature = "lazy")]
24#[allow(missing_debug_implementations)]
25pub struct Responsive<
26 'a,
27 Message,
28 Theme = crate::Theme,
29 Renderer = crate::Renderer,
30> {
31 view: Box<dyn Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a>,
32 content: RefCell<Content<'a, Message, Theme, Renderer>>,
33}
34
35impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer>
36where
37 Renderer: core::Renderer,
38{
39 pub fn new(
46 view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a,
47 ) -> Self {
48 Self {
49 view: Box::new(view),
50 content: RefCell::new(Content {
51 size: Size::ZERO,
52 layout: None,
53 element: Element::new(horizontal_space().width(0)),
54 }),
55 }
56 }
57}
58
59struct Content<'a, Message, Theme, Renderer> {
60 size: Size,
61 layout: Option<layout::Node>,
62 element: Element<'a, Message, Theme, Renderer>,
63}
64
65impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer>
66where
67 Renderer: core::Renderer,
68{
69 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer) {
70 if self.layout.is_none() {
71 self.layout = Some(self.element.as_widget().layout(
72 tree,
73 renderer,
74 &layout::Limits::new(Size::ZERO, self.size),
75 ));
76 }
77 }
78
79 fn update(
80 &mut self,
81 tree: &mut Tree,
82 new_size: Size,
83 view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>,
84 ) {
85 if self.size != new_size {
86 self.element = view(new_size);
87 self.size = new_size;
88 self.layout = None;
89
90 tree.diff(&self.element);
91 } else {
92 let is_tree_empty =
93 tree.tag == tree::Tag::stateless() && tree.children.is_empty();
94
95 if is_tree_empty {
96 self.layout = None;
97 tree.diff(&self.element);
98 }
99 }
100 }
101
102 fn resolve<R, T>(
103 &mut self,
104 tree: &mut Tree,
105 renderer: R,
106 layout: Layout<'_>,
107 view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>,
108 f: impl FnOnce(
109 &mut Tree,
110 R,
111 Layout<'_>,
112 &mut Element<'a, Message, Theme, Renderer>,
113 ) -> T,
114 ) -> T
115 where
116 R: Deref<Target = Renderer>,
117 {
118 self.update(tree, layout.bounds().size(), view);
119 self.layout(tree, renderer.deref());
120
121 let content_layout = Layout::with_offset(
122 layout.position() - Point::ORIGIN,
123 self.layout.as_ref().unwrap(),
124 );
125
126 f(tree, renderer, content_layout, &mut self.element)
127 }
128}
129
130struct State {
131 tree: RefCell<Tree>,
132}
133
134impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
135 for Responsive<'_, Message, Theme, Renderer>
136where
137 Renderer: core::Renderer,
138{
139 fn tag(&self) -> tree::Tag {
140 tree::Tag::of::<State>()
141 }
142
143 fn state(&self) -> tree::State {
144 tree::State::new(State {
145 tree: RefCell::new(Tree::empty()),
146 })
147 }
148
149 fn size(&self) -> Size<Length> {
150 Size {
151 width: Length::Fill,
152 height: Length::Fill,
153 }
154 }
155
156 fn layout(
157 &self,
158 _tree: &mut Tree,
159 _renderer: &Renderer,
160 limits: &layout::Limits,
161 ) -> layout::Node {
162 layout::Node::new(limits.max())
163 }
164
165 fn operate(
166 &self,
167 tree: &mut Tree,
168 layout: Layout<'_>,
169 renderer: &Renderer,
170 operation: &mut dyn widget::Operation,
171 ) {
172 let state = tree.state.downcast_mut::<State>();
173 let mut content = self.content.borrow_mut();
174
175 content.resolve(
176 &mut state.tree.borrow_mut(),
177 renderer,
178 layout,
179 &self.view,
180 |tree, renderer, layout, element| {
181 element
182 .as_widget()
183 .operate(tree, layout, renderer, operation);
184 },
185 );
186 }
187
188 fn update(
189 &mut self,
190 tree: &mut Tree,
191 event: &Event,
192 layout: Layout<'_>,
193 cursor: mouse::Cursor,
194 renderer: &Renderer,
195 clipboard: &mut dyn Clipboard,
196 shell: &mut Shell<'_, Message>,
197 viewport: &Rectangle,
198 ) {
199 let state = tree.state.downcast_mut::<State>();
200 let mut content = self.content.borrow_mut();
201
202 let mut local_messages = vec![];
203 let mut local_shell = Shell::new(&mut local_messages);
204
205 content.resolve(
206 &mut state.tree.borrow_mut(),
207 renderer,
208 layout,
209 &self.view,
210 |tree, renderer, layout, element| {
211 element.as_widget_mut().update(
212 tree,
213 event,
214 layout,
215 cursor,
216 renderer,
217 clipboard,
218 &mut local_shell,
219 viewport,
220 );
221 },
222 );
223
224 if local_shell.is_layout_invalid() {
225 content.layout = None;
226 }
227
228 shell.merge(local_shell, std::convert::identity);
229 }
230
231 fn draw(
232 &self,
233 tree: &Tree,
234 renderer: &mut Renderer,
235 theme: &Theme,
236 style: &renderer::Style,
237 layout: Layout<'_>,
238 cursor: mouse::Cursor,
239 viewport: &Rectangle,
240 ) {
241 let state = tree.state.downcast_ref::<State>();
242 let mut content = self.content.borrow_mut();
243
244 content.resolve(
245 &mut state.tree.borrow_mut(),
246 renderer,
247 layout,
248 &self.view,
249 |tree, renderer, layout, element| {
250 element.as_widget().draw(
251 tree, renderer, theme, style, layout, cursor, viewport,
252 );
253 },
254 );
255 }
256
257 fn mouse_interaction(
258 &self,
259 tree: &Tree,
260 layout: Layout<'_>,
261 cursor: mouse::Cursor,
262 viewport: &Rectangle,
263 renderer: &Renderer,
264 ) -> mouse::Interaction {
265 let state = tree.state.downcast_ref::<State>();
266 let mut content = self.content.borrow_mut();
267
268 content.resolve(
269 &mut state.tree.borrow_mut(),
270 renderer,
271 layout,
272 &self.view,
273 |tree, renderer, layout, element| {
274 element
275 .as_widget()
276 .mouse_interaction(tree, layout, cursor, viewport, renderer)
277 },
278 )
279 }
280
281 fn overlay<'b>(
282 &'b mut self,
283 tree: &'b mut Tree,
284 layout: Layout<'_>,
285 renderer: &Renderer,
286 translation: Vector,
287 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
288 use std::ops::DerefMut;
289
290 let state = tree.state.downcast_ref::<State>();
291
292 let overlay = OverlayBuilder {
293 content: self.content.borrow_mut(),
294 tree: state.tree.borrow_mut(),
295 types: PhantomData,
296 overlay_builder: |content: &mut RefMut<
297 '_,
298 Content<'_, _, _, _>,
299 >,
300 tree| {
301 content.update(tree, layout.bounds().size(), &self.view);
302 content.layout(tree, renderer);
303
304 let Content {
305 element,
306 layout: content_layout_node,
307 ..
308 } = content.deref_mut();
309
310 let content_layout = Layout::with_offset(
311 layout.bounds().position() - Point::ORIGIN,
312 content_layout_node.as_ref().unwrap(),
313 );
314
315 (
316 element
317 .as_widget_mut()
318 .overlay(tree, content_layout, renderer, translation)
319 .map(|overlay| RefCell::new(Nested::new(overlay))),
320 content_layout_node,
321 )
322 },
323 }
324 .build();
325
326 if overlay.with_overlay(|(overlay, _layout)| overlay.is_some()) {
327 Some(overlay::Element::new(Box::new(overlay)))
328 } else {
329 None
330 }
331 }
332}
333
334impl<'a, Message, Theme, Renderer>
335 From<Responsive<'a, Message, Theme, Renderer>>
336 for Element<'a, Message, Theme, Renderer>
337where
338 Message: 'a,
339 Theme: 'a,
340 Renderer: core::Renderer + 'a,
341{
342 fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self {
343 Self::new(responsive)
344 }
345}
346
347#[self_referencing]
348struct Overlay<'a, 'b, Message, Theme, Renderer> {
349 content: RefMut<'a, Content<'b, Message, Theme, Renderer>>,
350 tree: RefMut<'a, Tree>,
351 types: PhantomData<Message>,
352
353 #[borrows(mut content, mut tree)]
354 #[not_covariant]
355 overlay: (
356 Option<RefCell<Nested<'this, Message, Theme, Renderer>>>,
357 &'this mut Option<layout::Node>,
358 ),
359}
360
361impl<Message, Theme, Renderer> Overlay<'_, '_, Message, Theme, Renderer> {
362 fn with_overlay_maybe<T>(
363 &self,
364 f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T,
365 ) -> Option<T> {
366 self.with_overlay(|(overlay, _layout)| {
367 overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut()))
368 })
369 }
370
371 fn with_overlay_mut_maybe<T>(
372 &mut self,
373 f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T,
374 ) -> Option<T> {
375 self.with_overlay_mut(|(overlay, _layout)| {
376 overlay.as_mut().map(|nested| (f)(nested.get_mut()))
377 })
378 }
379}
380
381impl<Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
382 for Overlay<'_, '_, Message, Theme, Renderer>
383where
384 Renderer: core::Renderer,
385{
386 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
387 self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
388 .unwrap_or_default()
389 }
390
391 fn draw(
392 &self,
393 renderer: &mut Renderer,
394 theme: &Theme,
395 style: &renderer::Style,
396 layout: Layout<'_>,
397 cursor: mouse::Cursor,
398 ) {
399 let _ = self.with_overlay_maybe(|overlay| {
400 overlay.draw(renderer, theme, style, layout, cursor);
401 });
402 }
403
404 fn mouse_interaction(
405 &self,
406 layout: Layout<'_>,
407 cursor: mouse::Cursor,
408 viewport: &Rectangle,
409 renderer: &Renderer,
410 ) -> mouse::Interaction {
411 self.with_overlay_maybe(|overlay| {
412 overlay.mouse_interaction(layout, cursor, viewport, renderer)
413 })
414 .unwrap_or_default()
415 }
416
417 fn update(
418 &mut self,
419 event: &Event,
420 layout: Layout<'_>,
421 cursor: mouse::Cursor,
422 renderer: &Renderer,
423 clipboard: &mut dyn Clipboard,
424 shell: &mut Shell<'_, Message>,
425 ) {
426 let mut is_layout_invalid = false;
427
428 let _ = self.with_overlay_mut_maybe(|overlay| {
429 overlay.update(event, layout, cursor, renderer, clipboard, shell);
430
431 is_layout_invalid = shell.is_layout_invalid();
432 });
433
434 if is_layout_invalid {
435 self.with_overlay_mut(|(_overlay, layout)| {
436 **layout = None;
437 });
438 }
439 }
440
441 fn is_over(
442 &self,
443 layout: Layout<'_>,
444 renderer: &Renderer,
445 cursor_position: Point,
446 ) -> bool {
447 self.with_overlay_maybe(|overlay| {
448 overlay.is_over(layout, renderer, cursor_position)
449 })
450 .unwrap_or_default()
451 }
452
453 fn operate(
454 &mut self,
455 layout: Layout<'_>,
456 renderer: &Renderer,
457 operation: &mut dyn widget::Operation,
458 ) {
459 let _ = self.with_overlay_mut_maybe(|overlay| {
460 overlay.operate(layout, renderer, operation);
461 });
462 }
463}