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