deft 1.0.0

Cross platform ui framework
use crate as deft;
use crate::element::container::Container;
use crate::element::image::Image;
use crate::element::label::Label;
use crate::element::{Element, Widget, ElementDelegate, ElementWeak};
use crate::event::{ClickEventListener};
use crate::ok_or_return;
use crate::style::length::LengthOrPercent;
use crate::style::{FixedStyleProp, ResolvedStyleProp, StylePropVal};
use deft_macros::{widget, event, js_methods, mrc_object};
use std::collections::HashMap;
use yoga::{Align, Display, FlexDirection};
use crate::js_module;
use crate::mrc::Mrc;

#[event]
pub struct ChangeEvent {}

struct CheckboxState {
    label: Label,
    delegate: CheckboxDelegate,
}

#[widget]
pub struct Checkbox {
    state: Mrc<CheckboxState>,
}

#[js_methods]
impl Checkbox {
    #[js_func]
    pub fn set_label(&mut self, label: String) {
        self.state.label.set_text(label);
    }

    #[js_func]
    pub fn get_label(&mut self) -> String {
        self.state.label.get_text()
    }

    #[js_func]
    pub fn is_checked(&self) -> bool {
        self.state.delegate.checked
    }

    #[js_func]
    pub fn set_checked(&mut self, checked: bool) {
        let mut el = ok_or_return!(self.state.delegate.element.upgrade());
        if checked {
            el.set_attribute("checked".to_string(), "".to_string());
        } else {
            el.remove_attribute("checked".to_string());
        }
    }

    #[js_func]
    pub fn create() -> Self {
        let mut element = Element::new("checkbox");
        element.is_form_element = true;
        element.register_js_event::<ChangeEvent>("change");
        let mut wrapper = Container::create();
        let mut box_container = Container::create();
        let label = Label::create();
        let mut img = Image::create();
        img.set_src_svg_raw(include_bytes!("./checked.svg"));
        img.set_style_props(vec![
            FixedStyleProp::Width(StylePropVal::Custom(LengthOrPercent::Percent(100.0))),
            FixedStyleProp::Height(StylePropVal::Custom(LengthOrPercent::Percent(100.0))),
        ]);
        box_container.add_child(&img, Some(0)).unwrap();

        wrapper.add_child(&box_container, Some(0)).unwrap();
        wrapper.add_child(&label, Some(1)).unwrap();

        element.add_child(&wrapper, Some(0)).unwrap();
        wrapper.set_style_props(vec![
            FixedStyleProp::AlignItems(StylePropVal::Custom(Align::Center)),
            FixedStyleProp::FlexDirection(StylePropVal::Custom(FlexDirection::Row)),
        ]);
        let delegate = CheckboxDelegateData {
            box_container,
            img,
            checked: false,
            element: element.as_weak(),
        }.to_ref();
        element.set_delegate(delegate.clone());
        {
            let mut delegate = delegate.clone();
            element.register_event_listener(ClickEventListener::new(move |_e, _ctx| {
                let checked = !delegate.checked;
                delegate.update_checked(checked);
            }));
        }
        let mut inst = Checkbox {
            el: element,
            state: Mrc::new(CheckboxState {
                label,
                delegate,
            })
        };
        inst.state.delegate.update_children();
        inst
    }
}

impl Widget for Checkbox {}

impl ElementDelegate for CheckboxDelegate {
    fn accept_pseudo_element_styles(&mut self, styles: HashMap<String, Vec<ResolvedStyleProp>>) {
        if let Some(styles) = styles.get("box") {
            let styles = styles.iter().map(|s| s.to_unresolved()).collect::<Vec<_>>();
            self.box_container.set_style_props(styles);
        }
    }

    fn on_attribute_changed(&mut self, key: &str, value: Option<&str>) {
        match key {
            "checked" => self.update_checked(value.is_some()),
            _ => {},
        }
    }
}

#[mrc_object]
struct CheckboxDelegate {
    element: ElementWeak,
    box_container: Container,
    checked: bool,
    img: Image,
}

impl CheckboxDelegate {
    fn update_checked(&mut self, checked: bool) {
        if self.checked != checked {
            self.checked = checked;
            self.update_children();
            self.element.emit(ChangeEvent {});
        }
    }

    fn update_children(&mut self) {
        let display = if self.checked {
            Display::Flex
        } else {
            Display::None
        };
        self.img.set_style_props(vec![FixedStyleProp::Display(StylePropVal::Custom(display))]);
    }
}

js_module!(Checkbox);