use ferroui_base::{BoxedValue, FerroPropertyChangedEventArgs, ObjectType, Ref, Upcast, WeakRef};
use ferroui_controls::Control;
use ferroui_reactiveui::{typed_view_for, IActivatableView, IViewFor, IViewForOf, SettableProperty};
use crate::{register_view_control, value_of_optional_view_model, view_model_of_value, IReactiveViewClass, IViewControl};
use ferroui_system::component_model::{INotifyPropertyChanged, PropertyPath};
use ferroui_system::events::Event;
use ferroui_system::IDisposable;
use std::any::Any;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
const VIEW_MODEL_PROPERTY: &str = "ViewModel";
pub struct BindableView<C: IReactiveViewClass, VM: PartialEq + 'static> {
control: WeakRef<C>,
property_changed: Event<str>,
subscription: RefCell<Option<Rc<dyn IDisposable>>>,
_view_model: PhantomData<fn() -> VM>,
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> BindableView<C, VM> {
pub fn new(control: &Ref<C>) -> Rc<Self> {
register_view_control::<Self>();
let view = Rc::new(Self {
control: control.downgrade(),
property_changed: Event::new(),
subscription: RefCell::new(None),
_view_model: PhantomData,
});
let weak = Rc::downgrade(&view);
let element: &Control = Upcast::<Control>::upcast(&**control);
let subscription = element.property_changed(move |args: &FerroPropertyChangedEventArgs<'_>| {
if args.property() != C::view_model_property().as_property() {
return;
}
if let Some(view) = weak.upgrade() {
view.property_changed.raise(VIEW_MODEL_PROPERTY);
}
});
*view.subscription.borrow_mut() = Some(subscription);
view
}
pub fn control(&self) -> Option<Ref<C>> {
self.control.upgrade()
}
pub fn view_model(&self) -> Option<Rc<VM>> {
let control = self.control.upgrade()?;
let element: &Control = Upcast::<Control>::upcast(&*control);
let value = element.get_value(C::view_model_property())?;
view_model_of_value(&value).downcast::<VM>().ok()
}
pub fn set_view_model(&self, view_model: Option<Rc<VM>>) {
if let Some(control) = self.control.upgrade() {
let element: &Control = Upcast::<Control>::upcast(&*control);
element.set_value(C::view_model_property(), view_model.map(|view_model| view_model as BoxedValue));
}
}
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> Drop for BindableView<C, VM> {
fn drop(&mut self) {
if let Some(subscription) = self.subscription.borrow_mut().take() {
subscription.dispose();
}
}
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> INotifyPropertyChanged for BindableView<C, VM> {
fn property_changed(&self) -> &Event<str> {
&self.property_changed
}
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> IViewControl for BindableView<C, VM> {
fn view_control(&self) -> Option<Ref<Control>> {
self.control.upgrade().map(|control| control.upcast())
}
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> IActivatableView for BindableView<C, VM> {
fn as_view_for(&self) -> Option<&dyn IViewFor> {
Some(self)
}
fn as_notify_property_changed(self: Rc<Self>) -> Option<Rc<dyn INotifyPropertyChanged>> {
Some(self)
}
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> IViewFor for BindableView<C, VM> {
fn view_model(&self) -> Option<Rc<dyn Any>> {
let control = self.control.upgrade()?;
let element: &Control = Upcast::<Control>::upcast(&*control);
element.get_value(C::view_model_property()).as_ref().map(view_model_of_value)
}
fn set_view_model(&self, view_model: Option<Rc<dyn Any>>) {
if let Some(control) = self.control.upgrade() {
let element: &Control = Upcast::<Control>::upcast(&*control);
element.set_value(C::view_model_property(), value_of_optional_view_model(view_model));
}
}
fn as_view_for_of(self: Rc<Self>) -> Option<Box<dyn Any>> {
typed_view_for::<VM, Self>(self)
}
}
impl<C: IReactiveViewClass, VM: PartialEq + 'static> IViewForOf<VM> for BindableView<C, VM> {
fn view_model(&self) -> Option<Rc<VM>> {
BindableView::view_model(self)
}
fn set_view_model(&self, view_model: Option<Rc<VM>>) {
BindableView::set_view_model(self, view_model);
}
}
pub fn element_property<V: 'static, T: ObjectType, TV: 'static>(
name: &str,
element: &Ref<T>,
property: PropertyPath<Ref<T>, TV>,
set: impl Fn(&Ref<T>, TV) + 'static,
) -> SettableProperty<Rc<V>, TV> {
let (read, write) = (element.clone(), element.clone());
let path = PropertyPath::<Rc<V>, Ref<T>>::value(name, move |_: &Rc<V>| read.clone()).then(property);
SettableProperty::new(path, move |_: &Rc<V>, value: TV| set(&write, value))
}
pub fn owned_element_property<V: 'static, T: ObjectType, TV: 'static>(
name: &str,
element: &Ref<T>,
property: PropertyPath<Ref<T>, TV>,
set: impl Fn(&Ref<T>, TV) + 'static,
) -> SettableProperty<Rc<V>, TV> {
let (read, write) = (element.clone(), element.clone());
let path = PropertyPath::<Rc<V>, Ref<T>>::object_property(name, move |_: &Rc<V>| read.clone()).then(property);
SettableProperty::new(path, move |_: &Rc<V>, value: TV| set(&write, value))
}
pub fn element_control<V: 'static, T: ObjectType>(name: &str, element: &Ref<T>) -> PropertyPath<Rc<V>, Rc<Ref<T>>> {
let element = element.clone();
PropertyPath::<Rc<V>, Rc<Ref<T>>>::value(name, move |_: &Rc<V>| Rc::new(element.clone()))
}