1#![allow(deprecated)]
3use crate::core::layout::{self, Layout};
4use crate::core::mouse;
5use crate::core::overlay;
6use crate::core::renderer;
7use crate::core::widget;
8use crate::core::widget::tree::{self, Tree};
9use crate::core::{
10 self, Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
11};
12use crate::runtime::overlay::Nested;
13
14use ouroboros::self_referencing;
15use std::cell::RefCell;
16use std::marker::PhantomData;
17use std::rc::Rc;
18
19#[cfg(feature = "lazy")]
46#[deprecated(
47 since = "0.13.0",
48 note = "components introduce encapsulated state and hamper the use of a single source of truth. \
49 Instead, leverage the Elm Architecture directly, or implement a custom widget"
50)]
51pub trait Component<Message, Theme = crate::Theme, Renderer = crate::Renderer> {
52 type State: Default;
54
55 type Event;
57
58 fn update(
62 &mut self,
63 state: &mut Self::State,
64 event: Self::Event,
65 ) -> Option<Message>;
66
67 fn view(
70 &self,
71 state: &Self::State,
72 ) -> Element<'_, Self::Event, Theme, Renderer>;
73
74 fn operate(
78 &self,
79 _bounds: Rectangle,
80 _state: &mut Self::State,
81 _operation: &mut dyn widget::Operation,
82 ) {
83 }
84
85 fn size_hint(&self) -> Size<Length> {
90 Size {
91 width: Length::Shrink,
92 height: Length::Shrink,
93 }
94 }
95}
96
97struct Tag<T>(T);
98
99pub fn view<'a, C, Message, Theme, Renderer>(
102 component: C,
103) -> Element<'a, Message, Theme, Renderer>
104where
105 C: Component<Message, Theme, Renderer> + 'a,
106 C::State: 'static,
107 Message: 'a,
108 Theme: 'a,
109 Renderer: core::Renderer + 'a,
110{
111 Element::new(Instance {
112 state: RefCell::new(Some(
113 StateBuilder {
114 component: Box::new(component),
115 message: PhantomData,
116 state: PhantomData,
117 element_builder: |_| None,
118 }
119 .build(),
120 )),
121 tree: RefCell::new(Rc::new(RefCell::new(None))),
122 })
123}
124
125struct Instance<'a, Message, Theme, Renderer, Event, S> {
126 state: RefCell<Option<State<'a, Message, Theme, Renderer, Event, S>>>,
127 tree: RefCell<Rc<RefCell<Option<Tree>>>>,
128}
129
130#[self_referencing]
131struct State<'a, Message: 'a, Theme: 'a, Renderer: 'a, Event: 'a, S: 'a> {
132 component: Box<
133 dyn Component<Message, Theme, Renderer, Event = Event, State = S> + 'a,
134 >,
135 message: PhantomData<Message>,
136 state: PhantomData<S>,
137
138 #[borrows(component)]
139 #[covariant]
140 element: Option<Element<'this, Event, Theme, Renderer>>,
141}
142
143impl<Message, Theme, Renderer, Event, S>
144 Instance<'_, Message, Theme, Renderer, Event, S>
145where
146 S: Default + 'static,
147 Renderer: renderer::Renderer,
148{
149 fn diff_self(&self) {
150 self.with_element(|element| {
151 self.tree
152 .borrow_mut()
153 .borrow_mut()
154 .as_mut()
155 .unwrap()
156 .diff_children(std::slice::from_ref(&element));
157 });
158 }
159
160 fn rebuild_element_if_necessary(&self) {
161 let inner = self.state.borrow_mut().take().unwrap();
162 if inner.borrow_element().is_none() {
163 let heads = inner.into_heads();
164
165 *self.state.borrow_mut() = Some(
166 StateBuilder {
167 component: heads.component,
168 message: PhantomData,
169 state: PhantomData,
170 element_builder: |component| {
171 Some(
172 component.view(
173 self.tree
174 .borrow()
175 .borrow()
176 .as_ref()
177 .unwrap()
178 .state
179 .downcast_ref::<S>(),
180 ),
181 )
182 },
183 }
184 .build(),
185 );
186 self.diff_self();
187 } else {
188 *self.state.borrow_mut() = Some(inner);
189 }
190 }
191
192 fn rebuild_element_with_operation(
193 &self,
194 layout: Layout<'_>,
195 operation: &mut dyn widget::Operation,
196 ) {
197 let heads = self.state.borrow_mut().take().unwrap().into_heads();
198
199 heads.component.operate(
200 layout.bounds(),
201 self.tree
202 .borrow_mut()
203 .borrow_mut()
204 .as_mut()
205 .unwrap()
206 .state
207 .downcast_mut(),
208 operation,
209 );
210
211 *self.state.borrow_mut() = Some(
212 StateBuilder {
213 component: heads.component,
214 message: PhantomData,
215 state: PhantomData,
216 element_builder: |component| {
217 Some(
218 component.view(
219 self.tree
220 .borrow()
221 .borrow()
222 .as_ref()
223 .unwrap()
224 .state
225 .downcast_ref(),
226 ),
227 )
228 },
229 }
230 .build(),
231 );
232 self.diff_self();
233 }
234
235 fn with_element<T>(
236 &self,
237 f: impl FnOnce(&Element<'_, Event, Theme, Renderer>) -> T,
238 ) -> T {
239 self.with_element_mut(|element| f(element))
240 }
241
242 fn with_element_mut<T>(
243 &self,
244 f: impl FnOnce(&mut Element<'_, Event, Theme, Renderer>) -> T,
245 ) -> T {
246 self.rebuild_element_if_necessary();
247 self.state
248 .borrow_mut()
249 .as_mut()
250 .unwrap()
251 .with_element_mut(|element| f(element.as_mut().unwrap()))
252 }
253}
254
255impl<Message, Theme, Renderer, Event, S> Widget<Message, Theme, Renderer>
256 for Instance<'_, Message, Theme, Renderer, Event, S>
257where
258 S: 'static + Default,
259 Renderer: core::Renderer,
260{
261 fn tag(&self) -> tree::Tag {
262 tree::Tag::of::<Tag<S>>()
263 }
264
265 fn state(&self) -> tree::State {
266 let state = Rc::new(RefCell::new(Some(Tree {
267 tag: tree::Tag::of::<Tag<S>>(),
268 state: tree::State::new(S::default()),
269 children: vec![Tree::empty()],
270 })));
271
272 *self.tree.borrow_mut() = state.clone();
273 self.diff_self();
274
275 tree::State::new(state)
276 }
277
278 fn children(&self) -> Vec<Tree> {
279 vec![]
280 }
281
282 fn diff(&self, tree: &mut Tree) {
283 let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
284 *self.tree.borrow_mut() = tree.clone();
285 self.rebuild_element_if_necessary();
286 }
287
288 fn size(&self) -> Size<Length> {
289 self.with_element(|element| element.as_widget().size())
290 }
291
292 fn size_hint(&self) -> Size<Length> {
293 self.state
294 .borrow()
295 .as_ref()
296 .expect("Borrow instance state")
297 .borrow_component()
298 .size_hint()
299 }
300
301 fn layout(
302 &self,
303 tree: &mut Tree,
304 renderer: &Renderer,
305 limits: &layout::Limits,
306 ) -> layout::Node {
307 let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
308
309 self.with_element(|element| {
310 element.as_widget().layout(
311 &mut t.borrow_mut().as_mut().unwrap().children[0],
312 renderer,
313 limits,
314 )
315 })
316 }
317
318 fn update(
319 &mut self,
320 tree: &mut Tree,
321 event: &core::Event,
322 layout: Layout<'_>,
323 cursor: mouse::Cursor,
324 renderer: &Renderer,
325 clipboard: &mut dyn Clipboard,
326 shell: &mut Shell<'_, Message>,
327 viewport: &Rectangle,
328 ) {
329 let mut local_messages = Vec::new();
330 let mut local_shell = Shell::new(&mut local_messages);
331
332 let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
333 self.with_element_mut(|element| {
334 element.as_widget_mut().update(
335 &mut t.borrow_mut().as_mut().unwrap().children[0],
336 event,
337 layout,
338 cursor,
339 renderer,
340 clipboard,
341 &mut local_shell,
342 viewport,
343 );
344 });
345
346 if local_shell.is_event_captured() {
347 shell.capture_event();
348 }
349
350 local_shell.revalidate_layout(|| shell.invalidate_layout());
351 shell.request_redraw_at(local_shell.redraw_request());
352 shell.request_input_method(local_shell.input_method());
353
354 if !local_messages.is_empty() {
355 let mut heads = self.state.take().unwrap().into_heads();
356
357 for message in local_messages.into_iter().filter_map(|message| {
358 heads.component.update(
359 t.borrow_mut().as_mut().unwrap().state.downcast_mut(),
360 message,
361 )
362 }) {
363 shell.publish(message);
364 }
365
366 self.state = RefCell::new(Some(
367 StateBuilder {
368 component: heads.component,
369 message: PhantomData,
370 state: PhantomData,
371 element_builder: |_| None,
372 }
373 .build(),
374 ));
375
376 shell.invalidate_layout();
377 }
378 }
379
380 fn operate(
381 &self,
382 tree: &mut Tree,
383 layout: Layout<'_>,
384 renderer: &Renderer,
385 operation: &mut dyn widget::Operation,
386 ) {
387 self.rebuild_element_with_operation(layout, operation);
388
389 let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
390 self.with_element(|element| {
391 element.as_widget().operate(
392 &mut tree.borrow_mut().as_mut().unwrap().children[0],
393 layout,
394 renderer,
395 operation,
396 );
397 });
398 }
399
400 fn draw(
401 &self,
402 tree: &Tree,
403 renderer: &mut Renderer,
404 theme: &Theme,
405 style: &renderer::Style,
406 layout: Layout<'_>,
407 cursor: mouse::Cursor,
408 viewport: &Rectangle,
409 ) {
410 let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
411 self.with_element(|element| {
412 element.as_widget().draw(
413 &tree.borrow().as_ref().unwrap().children[0],
414 renderer,
415 theme,
416 style,
417 layout,
418 cursor,
419 viewport,
420 );
421 });
422 }
423
424 fn mouse_interaction(
425 &self,
426 tree: &Tree,
427 layout: Layout<'_>,
428 cursor: mouse::Cursor,
429 viewport: &Rectangle,
430 renderer: &Renderer,
431 ) -> mouse::Interaction {
432 let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
433 self.with_element(|element| {
434 element.as_widget().mouse_interaction(
435 &tree.borrow().as_ref().unwrap().children[0],
436 layout,
437 cursor,
438 viewport,
439 renderer,
440 )
441 })
442 }
443
444 fn overlay<'b>(
445 &'b mut self,
446 tree: &'b mut Tree,
447 layout: Layout<'b>,
448 renderer: &Renderer,
449 viewport: &Rectangle,
450 translation: Vector,
451 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
452 self.rebuild_element_if_necessary();
453
454 let state = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
455 let tree = state.borrow_mut().take().unwrap();
456
457 let overlay = InnerBuilder {
458 instance: self,
459 tree,
460 types: PhantomData,
461 overlay_builder: |instance, tree| {
462 instance.state.get_mut().as_mut().unwrap().with_element_mut(
463 move |element| {
464 element
465 .as_mut()
466 .unwrap()
467 .as_widget_mut()
468 .overlay(
469 &mut tree.children[0],
470 layout,
471 renderer,
472 viewport,
473 translation,
474 )
475 .map(|overlay| RefCell::new(Nested::new(overlay)))
476 },
477 )
478 },
479 }
480 .build();
481
482 #[allow(clippy::redundant_closure_for_method_calls)]
483 if overlay.with_overlay(|overlay| overlay.is_some()) {
484 Some(overlay::Element::new(Box::new(OverlayInstance {
485 overlay: Some(Overlay(Some(overlay))), })))
487 } else {
488 let heads = overlay.into_heads();
489
490 *state.borrow_mut() = Some(heads.tree);
494
495 None
496 }
497 }
498}
499
500struct Overlay<'a, 'b, Message, Theme, Renderer, Event, S>(
501 Option<Inner<'a, 'b, Message, Theme, Renderer, Event, S>>,
502);
503
504impl<Message, Theme, Renderer, Event, S> Drop
505 for Overlay<'_, '_, Message, Theme, Renderer, Event, S>
506{
507 fn drop(&mut self) {
508 if let Some(heads) = self.0.take().map(Inner::into_heads) {
509 *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree);
510 }
511 }
512}
513
514#[self_referencing]
515struct Inner<'a, 'b, Message, Theme, Renderer, Event, S> {
516 instance: &'a mut Instance<'b, Message, Theme, Renderer, Event, S>,
517 tree: Tree,
518 types: PhantomData<(Message, Event, S)>,
519
520 #[borrows(mut instance, mut tree)]
521 #[not_covariant]
522 overlay: Option<RefCell<Nested<'this, Event, Theme, Renderer>>>,
523}
524
525struct OverlayInstance<'a, 'b, Message, Theme, Renderer, Event, S> {
526 overlay: Option<Overlay<'a, 'b, Message, Theme, Renderer, Event, S>>,
527}
528
529impl<Message, Theme, Renderer, Event, S>
530 OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
531{
532 fn with_overlay_maybe<T>(
533 &self,
534 f: impl FnOnce(&mut Nested<'_, Event, Theme, Renderer>) -> T,
535 ) -> Option<T> {
536 self.overlay
537 .as_ref()
538 .unwrap()
539 .0
540 .as_ref()
541 .unwrap()
542 .with_overlay(|overlay| {
543 overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut()))
544 })
545 }
546
547 fn with_overlay_mut_maybe<T>(
548 &mut self,
549 f: impl FnOnce(&mut Nested<'_, Event, Theme, Renderer>) -> T,
550 ) -> Option<T> {
551 self.overlay
552 .as_mut()
553 .unwrap()
554 .0
555 .as_mut()
556 .unwrap()
557 .with_overlay_mut(|overlay| {
558 overlay.as_mut().map(|nested| (f)(nested.get_mut()))
559 })
560 }
561}
562
563impl<Message, Theme, Renderer, Event, S>
564 overlay::Overlay<Message, Theme, Renderer>
565 for OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
566where
567 Renderer: core::Renderer,
568 S: 'static + Default,
569{
570 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
571 self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
572 .unwrap_or_default()
573 }
574
575 fn draw(
576 &self,
577 renderer: &mut Renderer,
578 theme: &Theme,
579 style: &renderer::Style,
580 layout: Layout<'_>,
581 cursor: mouse::Cursor,
582 ) {
583 let _ = self.with_overlay_maybe(|overlay| {
584 overlay.draw(renderer, theme, style, layout, cursor);
585 });
586 }
587
588 fn mouse_interaction(
589 &self,
590 layout: Layout<'_>,
591 cursor: mouse::Cursor,
592 renderer: &Renderer,
593 ) -> mouse::Interaction {
594 self.with_overlay_maybe(|overlay| {
595 overlay.mouse_interaction(layout, cursor, renderer)
596 })
597 .unwrap_or_default()
598 }
599
600 fn update(
601 &mut self,
602 event: &core::Event,
603 layout: Layout<'_>,
604 cursor: mouse::Cursor,
605 renderer: &Renderer,
606 clipboard: &mut dyn Clipboard,
607 shell: &mut Shell<'_, Message>,
608 ) {
609 let mut local_messages = Vec::new();
610 let mut local_shell = Shell::new(&mut local_messages);
611
612 let _ = self.with_overlay_mut_maybe(|overlay| {
613 overlay.update(
614 event,
615 layout,
616 cursor,
617 renderer,
618 clipboard,
619 &mut local_shell,
620 );
621 });
622
623 if local_shell.is_event_captured() {
624 shell.capture_event();
625 }
626
627 local_shell.revalidate_layout(|| shell.invalidate_layout());
628 shell.request_redraw_at(local_shell.redraw_request());
629 shell.request_input_method(local_shell.input_method());
630
631 if !local_messages.is_empty() {
632 let mut inner =
633 self.overlay.take().unwrap().0.take().unwrap().into_heads();
634 let mut heads = inner.instance.state.take().unwrap().into_heads();
635
636 for message in local_messages.into_iter().filter_map(|message| {
637 heads
638 .component
639 .update(inner.tree.state.downcast_mut(), message)
640 }) {
641 shell.publish(message);
642 }
643
644 *inner.instance.state.borrow_mut() = Some(
645 StateBuilder {
646 component: heads.component,
647 message: PhantomData,
648 state: PhantomData,
649 element_builder: |_| None,
650 }
651 .build(),
652 );
653
654 self.overlay = Some(Overlay(Some(
655 InnerBuilder {
656 instance: inner.instance,
657 tree: inner.tree,
658 types: PhantomData,
659 overlay_builder: |_, _| None,
660 }
661 .build(),
662 )));
663
664 shell.invalidate_layout();
665 }
666 }
667}