use crate::properties_impl;
use std;
use std::cell::RefCell;
use std::convert::From;
use std::default::Default;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::{Rc, Weak};
pub trait PropertyBindingFn<T> {
fn run(&self) -> Option<T>;
fn description(&self) -> String {
String::default()
}
}
impl<F, T> PropertyBindingFn<T> for F
where
F: Fn() -> T,
{
fn run(&self) -> Option<T> {
Some((*self)())
}
}
impl<F, T> PropertyBindingFn<T> for Option<F>
where
F: Fn() -> Option<T>,
{
fn run(&self) -> Option<T> {
self.as_ref().and_then(|x| x())
}
}
impl<F, T> PropertyBindingFn<T> for (String, F)
where
F: Fn() -> Option<T>,
{
fn run(&self) -> Option<T> {
(self.1)()
}
fn description(&self) -> String {
(self.0).clone()
}
}
#[derive(Default, Clone)]
pub struct WeakProperty<'a, T> {
d: Weak<properties_impl::Property<T>>,
phantom: PhantomData<&'a ()>,
}
impl<'a, T: Default + Clone> WeakProperty<'a, T> {
pub fn get(&self) -> Option<T> {
self.d
.upgrade()
.map(|x| unsafe { Pin::new_unchecked(x) }.as_ref().get())
}
}
pub struct Property<'a, T: Default> {
d: Pin<Rc<properties_impl::Property<T>>>,
callbacks: RefCell<Vec<Pin<Box<properties_impl::ChangeEvent<dyn Fn() + 'a>>>>>,
}
impl<'a, T: Default> Default for Property<'a, T> {
fn default() -> Self {
Property {
d: Rc::pin(properties_impl::Property::default()),
callbacks: Default::default(),
}
}
}
impl<'a, T: Default + Clone> Property<'a, T> {
pub fn from_binding<F: PropertyBindingFn<T> + 'a>(f: F) -> Property<'a, T> {
let d = Rc::pin(properties_impl::Property::default());
d.as_ref().set_binding_owned(move || f.run().unwrap());
Property {
d,
callbacks: Default::default(),
}
}
pub fn set(&self, t: T) {
self.d.as_ref().set(t);
}
pub fn set_binding<F: PropertyBindingFn<T> + 'a>(&self, f: F) {
self.d.as_ref().set_binding_owned(move || f.run().unwrap());
}
pub fn value(&self) -> T {
self.get()
}
pub fn get(&self) -> T {
self.d.as_ref().get()
}
pub fn as_weak(&self) -> WeakProperty<'a, T> {
WeakProperty {
d: Rc::downgrade(unsafe {
std::mem::transmute::<
&std::pin::Pin<std::rc::Rc<properties_impl::Property<T>>>,
&std::rc::Rc<properties_impl::Property<T>>,
>(&self.d)
}),
phantom: PhantomData,
}
}
pub fn on_notify<F>(&self, callback: F)
where
F: Fn(&T) + 'a,
T: 'a,
{
let d = self.d.clone();
let e = Box::pin(properties_impl::ChangeEvent::new(move || {
callback(&d.as_ref().get())
}));
e.as_ref().listen(self.d.as_ref());
self.callbacks.borrow_mut().push(e);
}
}
impl<'a, T: Default + Clone> From<T> for Property<'a, T> {
fn from(t: T) -> Self {
let p = Property::default();
p.set(t);
p
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
#[derive(Default)]
struct Rectangle<'a> {
width: Property<'a, u32>,
height: Property<'a, u32>,
area: Property<'a, u32>,
}
#[test]
fn it_works() {
let rec = Rc::new(RefCell::new(Rectangle::default()));
rec.borrow_mut().width = Property::from(2);
let wr = Rc::downgrade(&rec);
rec.borrow_mut().area = Property::from_binding(move || {
wr.upgrade()
.map(|wr| wr.borrow().width.value() * wr.borrow().height.value())
.unwrap()
});
rec.borrow().height.set(4);
assert_eq!(rec.borrow().area.value(), 4 * 2);
}
#[test]
fn test_notify() {
let x = Cell::new(0);
let bar = Property::from(2);
let foo = Property::from(2);
foo.on_notify(|_| x.set(x.get() + 1));
foo.set(3);
assert_eq!(x.get(), 1);
foo.set(45);
assert_eq!(x.get(), 2);
foo.set_binding(|| bar.value());
assert_eq!(x.get(), 3);
bar.set(8);
assert_eq!(x.get(), 4);
}
}
#[derive(Default)]
pub struct Signal<'a> {
callbacks: RefCell<Vec<Box<dyn PropertyBindingFn<()> + 'a>>>,
}
impl<'a> Signal<'a> {
pub fn set_binding<F: PropertyBindingFn<()> + 'a>(&self, f: F) {
self.callbacks.borrow_mut().push(Box::new(f));
}
pub fn emit(&self) {
for cb in self.callbacks.borrow_mut().iter_mut() {
cb.run();
}
}
}