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