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