floem/views/
value_container.rs

1use std::any::Any;
2
3use floem_reactive::{Effect, RwSignal, SignalGet, SignalUpdate, UpdaterEffect};
4
5use crate::{
6    context::UpdateCx,
7    id::ViewId,
8    view::{IntoView, View},
9};
10
11/// A wrapper around another View that has value updates. See [`value_container`]
12pub struct ValueContainer<T> {
13    id: ViewId,
14    on_update: Option<Box<dyn Fn(T)>>,
15}
16
17/// A convenience function that creates two signals for use in a [`value_container`]
18/// - The outbound signal enables a widget's internal input event handlers
19///   to publish state changes via `ValueContainer::on_update`.
20/// - The inbound signal propagates value changes in the producer function
21///   into a widget's internals.
22pub fn create_value_container_signals<T>(
23    producer: impl Fn() -> T + 'static,
24) -> (RwSignal<T>, RwSignal<T>)
25where
26    T: Clone + 'static,
27{
28    let initial_value = producer();
29
30    let inbound_signal = RwSignal::new(initial_value.clone());
31    Effect::new(move |_| {
32        let checked = producer();
33        inbound_signal.set(checked);
34    });
35
36    let outbound_signal = RwSignal::new(initial_value.clone());
37    Effect::new(move |_| {
38        let checked = outbound_signal.get();
39        inbound_signal.set(checked);
40    });
41
42    (inbound_signal, outbound_signal)
43}
44
45/// A wrapper around another View that has value updates.
46///
47/// A [`ValueContainer`] is useful for wrapping another [View](crate::view::View).
48/// This is to provide the `on_update` method which can notify when the view's
49/// internal value was get changed
50pub fn value_container<T: 'static, V: IntoView + 'static>(
51    child: V,
52    value_update: impl Fn() -> T + 'static,
53) -> ValueContainer<T> {
54    let id = ViewId::new();
55    let child = child.into_view();
56    id.set_children([child]);
57    UpdaterEffect::new(value_update, move |new_value| id.update_state(new_value));
58    ValueContainer {
59        id,
60        on_update: None,
61    }
62}
63
64impl<T> ValueContainer<T> {
65    pub fn on_update(mut self, action: impl Fn(T) + 'static) -> Self {
66        self.on_update = Some(Box::new(action));
67        self
68    }
69}
70
71impl<T: 'static> View for ValueContainer<T> {
72    fn id(&self) -> ViewId {
73        self.id
74    }
75
76    fn update(&mut self, _cx: &mut UpdateCx, state: Box<dyn Any>) {
77        if let Ok(state) = state.downcast::<T>() {
78            if let Some(on_update) = self.on_update.as_ref() {
79                on_update(*state);
80            }
81        }
82    }
83}