deft 0.15.0

Cross platform ui framework
use crate as deft;
use crate::element::container::Container;
use crate::element::label::Label;
use crate::element::{Element, ElementBackend, ElementType};
use crate::event::ClickEventListener;
use crate::js_value;
use crate::mrc::Mrc;
use deft_macros::{js_methods, mrc_object};
use log::error;
use quick_js::JsValue;

#[mrc_object]
pub struct Menu {
    items: Vec<MenuItem>,
}

js_value!(Menu);

#[js_methods]
impl Menu {
    #[js_func]
    pub fn new() -> Self {
        MenuData { items: Vec::new() }.to_ref()
    }
    pub fn add_item(&mut self, item: MenuItem) {
        self.items.push(item);
    }

    #[js_func]
    pub fn add_standard_item(&mut self, standard_item: StandardMenuItem) {
        self.items.push(MenuItem::Standard(standard_item));
    }

    #[js_func]
    pub fn add_separator(&mut self) {
        self.items.push(MenuItem::Separator);
    }
}

#[mrc_object]
pub struct StandardMenuItem {
    pub disabled: bool,
    pub label: String,
    pub onclick: Mrc<Option<Box<dyn FnMut()>>>,
}

js_value!(StandardMenuItem);

#[js_methods]
impl StandardMenuItem {
    pub fn new<F: FnMut() + 'static>(label: &str, callback: F) -> Self {
        StandardMenuItemData {
            disabled: false,
            label: label.to_string(),
            onclick: Mrc::new(Some(Box::new(callback))),
        }
        .to_ref()
    }

    #[js_func]
    pub fn js_new(label: String, callback: JsValue) -> Self {
        StandardMenuItemData {
            disabled: false,
            label,
            onclick: Mrc::new(Some(Box::new(move || {
                if let Err(e) = callback.clone().call_as_function(vec![]) {
                    error!("Error calling callback: {}", e);
                }
            }))),
        }
        .to_ref()
    }

    #[js_func]
    pub fn set_disabled(&mut self, disabled: bool) {
        self.disabled = disabled;
    }

    #[js_func]
    pub fn get_disabled(&self) -> bool {
        self.disabled
    }
}

#[derive(Clone)]
pub enum MenuItem {
    Separator,
    Standard(StandardMenuItem),
}

pub fn build_menu_elements(menu: Menu) -> Element {
    let mut root = Element::create(Container::create);
    root.tag = "menu".to_string();
    root.set_element_type(ElementType::Widget);
    for it in menu.items.clone() {
        match it {
            MenuItem::Separator => {
                let mut e = Element::create(Container::create);
                e.tag = "menu-item-separator".to_string();
                e.set_element_type(ElementType::Widget);
                root.add_child(e, -1).unwrap();
            }
            MenuItem::Standard(s) => {
                let mut e = Element::create(Label::create);
                e.is_form_element = true;
                e.tag = "menu-item-standard".to_string();
                e.set_element_type(ElementType::Widget);
                e.set_disabled(s.disabled);
                let label = e.get_backend_mut_as::<Label>();
                label.set_text(s.label.to_string());
                let mut onclick = s.onclick.clone();
                e.register_event_listener(ClickEventListener::new(move |_, _| {
                    if let Some(onclick) = &mut *onclick {
                        onclick();
                    }
                }));
                root.add_child(e, -1).unwrap();
            }
        }
    }
    root
}