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