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