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_mut(|element| {
136 self.tree
137 .borrow_mut()
138 .borrow_mut()
139 .as_mut()
140 .unwrap()
141 .diff_children(std::slice::from_mut(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 diff(&mut self, tree: &mut Tree) {
261 let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
262 *self.tree.borrow_mut() = tree.clone();
263 self.rebuild_element_if_necessary();
264 }
265
266 fn size(&self) -> Size<Length> {
267 self.with_element(|element| element.as_widget().size())
268 }
269
270 fn layout(
271 &mut self,
272 tree: &mut Tree,
273 renderer: &Renderer,
274 limits: &layout::Limits,
275 ) -> layout::Node {
276 let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
277
278 self.with_element_mut(|element| {
279 element.as_widget_mut().layout(
280 &mut t.borrow_mut().as_mut().unwrap().children[0],
281 renderer,
282 limits,
283 )
284 })
285 }
286
287 fn update(
288 &mut self,
289 tree: &mut Tree,
290 event: &core::Event,
291 layout: Layout<'_>,
292 cursor: mouse::Cursor,
293 renderer: &Renderer,
294 shell: &mut Shell<'_, Message>,
295 viewport: &Rectangle,
296 ) {
297 let mut local_messages = Vec::new();
298 let mut local_shell = shell.local(&mut local_messages);
299
300 let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
301 self.with_element_mut(|element| {
302 element.as_widget_mut().update(
303 &mut t.borrow_mut().as_mut().unwrap().children[0],
304 event,
305 layout,
306 cursor,
307 renderer,
308 &mut local_shell,
309 viewport,
310 );
311 });
312
313 if local_shell.is_event_captured() {
314 shell.capture_event();
315 }
316
317 local_shell.revalidate_layout(|diff| shell.invalidate_layout_with(diff));
318 shell.request_redraw_at(local_shell.redraw_request());
319 shell.request_input_method(local_shell.input_method());
320 shell.clipboard_mut().merge(local_shell.clipboard_mut());
321
322 if !local_messages.is_empty() {
323 let mut heads = self.state.take().unwrap().into_heads();
324
325 for message in local_messages.into_iter().filter_map(|message| {
326 heads.component.update(
327 t.borrow_mut().as_mut().unwrap().state.downcast_mut(),
328 message,
329 )
330 }) {
331 shell.publish(message);
332 }
333
334 self.state = RefCell::new(Some(
335 StateBuilder {
336 component: heads.component,
337 message: PhantomData,
338 state: PhantomData,
339 element_builder: |_| None,
340 }
341 .build(),
342 ));
343
344 shell.invalidate_layout();
345 shell.request_redraw();
346 }
347 }
348
349 fn operate(
350 &mut self,
351 tree: &mut Tree,
352 layout: Layout<'_>,
353 renderer: &Renderer,
354 operation: &mut dyn widget::Operation,
355 ) {
356 self.rebuild_element_with_operation(layout, operation);
357
358 let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
359 self.with_element_mut(|element| {
360 element.as_widget_mut().operate(
361 &mut tree.borrow_mut().as_mut().unwrap().children[0],
362 layout,
363 renderer,
364 operation,
365 );
366 });
367 }
368
369 fn draw(
370 &self,
371 tree: &Tree,
372 renderer: &mut Renderer,
373 theme: &Theme,
374 style: &renderer::Style,
375 layout: Layout<'_>,
376 cursor: mouse::Cursor,
377 viewport: &Rectangle,
378 ) {
379 let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
380 self.with_element(|element| {
381 element.as_widget().draw(
382 &tree.borrow().as_ref().unwrap().children[0],
383 renderer,
384 theme,
385 style,
386 layout,
387 cursor,
388 viewport,
389 );
390 });
391 }
392
393 fn mouse_interaction(
394 &self,
395 tree: &Tree,
396 layout: Layout<'_>,
397 cursor: mouse::Cursor,
398 viewport: &Rectangle,
399 renderer: &Renderer,
400 ) -> mouse::Interaction {
401 let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
402 self.with_element(|element| {
403 element.as_widget().mouse_interaction(
404 &tree.borrow().as_ref().unwrap().children[0],
405 layout,
406 cursor,
407 viewport,
408 renderer,
409 )
410 })
411 }
412
413 fn overlay<'b>(
414 &'b mut self,
415 tree: &'b mut Tree,
416 layout: Layout<'b>,
417 renderer: &Renderer,
418 viewport: &Rectangle,
419 translation: Vector,
420 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
421 self.rebuild_element_if_necessary();
422
423 let state = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
424 let tree = state.borrow_mut().take().unwrap();
425
426 let overlay = InnerBuilder {
427 instance: self,
428 tree,
429 types: PhantomData,
430 overlay_builder: |instance, tree| {
431 instance
432 .state
433 .get_mut()
434 .as_mut()
435 .unwrap()
436 .with_element_mut(move |element| {
437 element
438 .as_mut()
439 .unwrap()
440 .as_widget_mut()
441 .overlay(
442 &mut tree.children[0],
443 layout,
444 renderer,
445 viewport,
446 translation,
447 )
448 .map(|overlay| RefCell::new(overlay::Nested::new(overlay)))
449 })
450 },
451 }
452 .build();
453
454 #[allow(clippy::redundant_closure_for_method_calls)]
455 if overlay.with_overlay(|overlay| overlay.is_some()) {
456 Some(overlay::Element::new(Box::new(OverlayInstance {
457 overlay: Some(Overlay(Some(overlay))), })))
459 } else {
460 let heads = overlay.into_heads();
461
462 *state.borrow_mut() = Some(heads.tree);
466
467 None
468 }
469 }
470}
471
472struct Overlay<'a, 'b, Message, Theme, Renderer, Event, S>(
473 Option<Inner<'a, 'b, Message, Theme, Renderer, Event, S>>,
474);
475
476impl<Message, Theme, Renderer, Event, S> Drop
477 for Overlay<'_, '_, Message, Theme, Renderer, Event, S>
478{
479 fn drop(&mut self) {
480 if let Some(heads) = self.0.take().map(Inner::into_heads) {
481 *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree);
482 }
483 }
484}
485
486#[self_referencing]
487struct Inner<'a, 'b, Message, Theme, Renderer, Event, S> {
488 instance: &'a mut Instance<'b, Message, Theme, Renderer, Event, S>,
489 tree: Tree,
490 types: PhantomData<(Message, Event, S)>,
491
492 #[borrows(mut instance, mut tree)]
493 #[not_covariant]
494 overlay: Option<RefCell<overlay::Nested<'this, Event, Theme, Renderer>>>,
495}
496
497struct OverlayInstance<'a, 'b, Message, Theme, Renderer, Event, S> {
498 overlay: Option<Overlay<'a, 'b, Message, Theme, Renderer, Event, S>>,
499}
500
501impl<Message, Theme, Renderer, Event, S>
502 OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
503{
504 fn with_overlay_maybe<T>(
505 &self,
506 f: impl FnOnce(&mut overlay::Nested<'_, Event, Theme, Renderer>) -> T,
507 ) -> Option<T> {
508 self.overlay
509 .as_ref()
510 .unwrap()
511 .0
512 .as_ref()
513 .unwrap()
514 .with_overlay(|overlay| overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())))
515 }
516
517 fn with_overlay_mut_maybe<T>(
518 &mut self,
519 f: impl FnOnce(&mut overlay::Nested<'_, Event, Theme, Renderer>) -> T,
520 ) -> Option<T> {
521 self.overlay
522 .as_mut()
523 .unwrap()
524 .0
525 .as_mut()
526 .unwrap()
527 .with_overlay_mut(|overlay| overlay.as_mut().map(|nested| (f)(nested.get_mut())))
528 }
529}
530
531impl<Message, Theme, Renderer, Event, S> overlay::Overlay<Message, Theme, Renderer>
532 for OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
533where
534 Renderer: core::Renderer,
535 S: 'static + Default,
536{
537 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
538 self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
539 .unwrap_or_default()
540 }
541
542 fn draw(
543 &self,
544 renderer: &mut Renderer,
545 theme: &Theme,
546 style: &renderer::Style,
547 layout: Layout<'_>,
548 cursor: mouse::Cursor,
549 ) {
550 let _ = self.with_overlay_maybe(|overlay| {
551 overlay.draw(renderer, theme, style, layout, cursor);
552 });
553 }
554
555 fn mouse_interaction(
556 &self,
557 layout: Layout<'_>,
558 cursor: mouse::Cursor,
559 renderer: &Renderer,
560 ) -> mouse::Interaction {
561 self.with_overlay_maybe(|overlay| overlay.mouse_interaction(layout, cursor, renderer))
562 .unwrap_or_default()
563 }
564
565 fn update(
566 &mut self,
567 event: &core::Event,
568 layout: Layout<'_>,
569 cursor: mouse::Cursor,
570 renderer: &Renderer,
571 shell: &mut Shell<'_, Message>,
572 ) {
573 let mut local_messages = Vec::new();
574 let mut local_shell = shell.local(&mut local_messages);
575
576 let _ = self.with_overlay_mut_maybe(|overlay| {
577 overlay.update(event, layout, cursor, renderer, &mut local_shell);
578 });
579
580 if local_shell.is_event_captured() {
581 shell.capture_event();
582 }
583
584 local_shell.revalidate_layout(|diff| shell.invalidate_layout_with(diff));
585 shell.request_redraw_at(local_shell.redraw_request());
586 shell.request_input_method(local_shell.input_method());
587 shell.clipboard_mut().merge(local_shell.clipboard_mut());
588
589 if !local_messages.is_empty() {
590 let mut inner = self.overlay.take().unwrap().0.take().unwrap().into_heads();
591 let mut heads = inner.instance.state.take().unwrap().into_heads();
592
593 for message in local_messages.into_iter().filter_map(|message| {
594 heads
595 .component
596 .update(inner.tree.state.downcast_mut(), message)
597 }) {
598 shell.publish(message);
599 }
600
601 *inner.instance.state.borrow_mut() = Some(
602 StateBuilder {
603 component: heads.component,
604 message: PhantomData,
605 state: PhantomData,
606 element_builder: |_| None,
607 }
608 .build(),
609 );
610
611 self.overlay = Some(Overlay(Some(
612 InnerBuilder {
613 instance: inner.instance,
614 tree: inner.tree,
615 types: PhantomData,
616 overlay_builder: |_, _| None,
617 }
618 .build(),
619 )));
620
621 shell.invalidate_layout();
622 }
623 }
624}