/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Data binding types are used to implement dynamic behaviors in UIs. [`Event`] is the primitive //! type underlying most others. [Properties](Property) are what should usually be used in UI //! models, since they have `From` impls allowing different binding behaviors to be set. use std::cell::RefCell; use std::rc::Rc; /// An event which can have multiple subscribers. /// /// The type parameter is the payload of the event. pub struct Event { subscribers: Rc>>>, } impl Clone for Event { fn clone(&self) -> Self { Event { subscribers: self.subscribers.clone(), } } } impl Default for Event { fn default() -> Self { Event { subscribers: Default::default(), } } } impl std::fmt::Debug for Event { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{} {{ {} subscribers }}", std::any::type_name::(), self.subscribers.borrow().len() ) } } impl Event { /// Add a callback for when the event is fired. pub fn subscribe(&self, f: F) where F: Fn(&T) + 'static, { self.subscribers.borrow_mut().push(Box::new(f)); } /// Fire the event with the given payload. pub fn fire(&self, payload: &T) { for f in self.subscribers.borrow().iter() { f(payload); } } } /// A synchronized runtime value. /// /// Consumers can subscribe to change events on the value. Change events are fired when /// `borrow_mut()` references are dropped. #[derive(Default)] pub struct Synchronized { inner: Rc>, } impl Clone for Synchronized { fn clone(&self) -> Self { Synchronized { inner: self.inner.clone(), } } } impl std::fmt::Debug for Synchronized { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct(std::any::type_name::()) .field("current", &*self.inner.current.borrow()) .field("change", &self.inner.change) .finish() } } #[derive(Default)] struct SynchronizedInner { current: RefCell, change: Event, } impl Synchronized { /// Create a new value with the given inner data. pub fn new(initial: T) -> Self { Synchronized { inner: Rc::new(SynchronizedInner { current: RefCell::new(initial), change: Default::default(), }), } } /// Borrow a value's data. pub fn borrow(&self) -> std::cell::Ref { self.inner.current.borrow() } /// Mutably borrow a value's data. /// /// When the mutable reference is dropped, a change event is fired. pub fn borrow_mut(&self) -> ValueRefMut<'_, T> { ValueRefMut { value: std::mem::ManuallyDrop::new(self.inner.current.borrow_mut()), inner: &self.inner, } } /// Subscribe to change events in the value. pub fn on_change(&self, f: F) { self.inner.change.subscribe(f); } /// Update another synchronized value when this one changes. pub fn update_on_change U + 'static>( &self, other: &Synchronized, f: F, ) { let other = other.clone(); self.on_change(move |val| { *other.borrow_mut() = f(val); }); } /// Immediately call the closure on the current value and call it whenever the value changes. pub fn map_with(&self, f: F) { // Hold the borrow to guarantee the value doesn't change until we are subscribed (just as a // measure of extra sanity; this should never occur). let borrow = self.borrow(); f(&*borrow); self.on_change(f); } /// Create a new synchronized value which will update when this one changes. pub fn mapped U + 'static>(&self, f: F) -> Synchronized { let s = Synchronized::new(f(&*self.borrow())); self.update_on_change(&s, f); s } pub fn join T + Clone + 'static>( a: &Synchronized, b: &Synchronized, f: F, ) -> Self where T: 'static, { let s = Synchronized::new(f(&*a.borrow(), &*b.borrow())); let update = cc! { (a,b,s) move || { *s.borrow_mut() = f(&*a.borrow(), &*b.borrow()); }}; a.on_change(cc! { (update) move |_| update()}); b.on_change(move |_| update()); s } } /// A runtime value that can be fetched on-demand (read-only). /// /// Consumers call [`read`] or [`get`] to retrieve the value, while producers call [`register`] to /// set the function which is called to retrieve the value. This is of most use for things like /// editable text strings, where it would be unnecessarily expensive to e.g. update a /// `Synchronized` property as the text string is changed (debouncing could be used, but if change /// notification isn't needed then it's still unnecessary). pub struct OnDemand { get: Rc>>>, } impl Default for OnDemand { fn default() -> Self { OnDemand { get: Default::default(), } } } impl Clone for OnDemand { fn clone(&self) -> Self { OnDemand { get: self.get.clone(), } } } impl std::fmt::Debug for OnDemand { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{} {{ {} }}", std::any::type_name::(), if self.get.borrow().is_none() { "not registered" } else { "registered" } ) } } impl OnDemand { /// Reads the current value. pub fn read(&self, value: &mut T) { match &*self.get.borrow() { None => { // The test UI doesn't always register OnDemand getters (only on a per-test basis), // so don't panic otherwise the tests will fail unnecessarily. #[cfg(not(test))] panic!("OnDemand not registered by renderer") } Some(f) => f(value), } } /// Get a copy of the current value. pub fn get(&self) -> T where T: Default, { let mut r = T::default(); self.read(&mut r); r } /// Register the function to use when getting the value. pub fn register(&self, f: impl Fn(&mut T) + 'static) { *self.get.borrow_mut() = Some(Box::new(f)); } } /// A UI element property. /// /// Properties support static and dynamic value bindings. /// * `T` can be converted to static bindings. /// * `Synchronized` can be converted to dynamic bindings which will be updated /// bidirectionally. /// * `OnDemand` can be converted to dynamic bindings which can be queried on an as-needed /// basis. #[derive(Clone, Debug)] pub enum Property { Static(T), Binding(Synchronized), ReadOnly(OnDemand), } #[cfg(test)] impl Property { pub fn set(&self, value: T) { match self { Property::Static(_) => panic!("cannot set static property"), Property::Binding(s) => *s.borrow_mut() = value, Property::ReadOnly(o) => o.register(move |v| *v = value.clone()), } } pub fn get(&self) -> T { match self { Property::Static(v) => v.clone(), Property::Binding(s) => s.borrow().clone(), Property::ReadOnly(o) => o.get(), } } } impl Default for Property { fn default() -> Self { Property::Static(Default::default()) } } impl From for Property { fn from(value: T) -> Self { Property::Static(value) } } impl From<&Synchronized> for Property { fn from(value: &Synchronized) -> Self { Property::Binding(value.clone()) } } impl From<&OnDemand> for Property { fn from(value: &OnDemand) -> Self { Property::ReadOnly(value.clone()) } } /// A mutable Value reference. /// /// When dropped, the Value's change event will fire (_after_ demoting the RefMut to a Ref). pub struct ValueRefMut<'a, T> { value: std::mem::ManuallyDrop>, inner: &'a SynchronizedInner, } impl std::ops::Deref for ValueRefMut<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { &*self.value } } impl std::ops::DerefMut for ValueRefMut<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.value } } impl Drop for ValueRefMut<'_, T> { fn drop(&mut self) { unsafe { std::mem::ManuallyDrop::drop(&mut self.value) }; self.inner.change.fire(&*self.inner.current.borrow()); } } #[cfg(test)] mod test { use super::*; use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; #[derive(Default, Clone)] struct Trace { count: Rc, } impl Trace { fn inc(&self) { self.count.fetch_add(1, Relaxed); } fn set(&self, v: usize) { self.count.store(v, Relaxed); } fn count(&self) -> usize { self.count.load(Relaxed) } } #[test] fn event() { let t1 = Trace::default(); let t2 = Trace::default(); let evt = Event::default(); evt.subscribe(cc! { (t1) move |x| { assert!(x == &42); t1.inc() }}); evt.fire(&42); assert_eq!(t1.count(), 1); evt.subscribe(cc! { (t2) move |_| t2.inc() }); evt.fire(&42); assert_eq!(t1.count(), 2); assert_eq!(t2.count(), 1); } #[test] fn synchronized() { let t1 = Trace::default(); let s = Synchronized::::default(); assert_eq!(*s.borrow(), 0); s.on_change(cc! { (t1) move |v| t1.set(*v) }); { let mut s_ref = s.borrow_mut(); *s_ref = 41; // Changes should only occur when the ref is dropped assert_eq!(t1.count(), 0); *s_ref = 42; } assert_eq!(t1.count(), 42); assert_eq!(*s.borrow(), 42); } #[test] fn ondemand() { let t1 = Trace::default(); let d = OnDemand::::default(); d.register(|v| *v = 42); { let mut v = 0; d.read(&mut v); assert_eq!(v, 42); } d.register(|v| *v = 10); assert_eq!(d.get(), 10); t1.inc(); d.register(cc! { (t1) move |v| *v = t1.count() }); assert_eq!(d.get(), 1); t1.set(42); assert_eq!(d.get(), 42); } }