graphix-package-gui 0.8.0

A dataflow language for UIs and network programming, GUI package
Documentation
use super::{compile, GuiW, GuiWidget, IcedElement, Message};
use crate::types::{LengthV, PaddingV};
use anyhow::{Context, Result};
use arcstr::ArcStr;
use graphix_compiler::expr::ExprId;
use graphix_rt::{Callable, GXExt, GXHandle, Ref, TRef};
use iced_widget as widget;
use netidx::{protocol::valarray::ValArray, publisher::Value};
use tokio::try_join;

pub(crate) struct ButtonW<X: GXExt> {
    gx: GXHandle<X>,
    disabled: TRef<X, bool>,
    width: TRef<X, LengthV>,
    height: TRef<X, LengthV>,
    padding: TRef<X, PaddingV>,
    on_press: Ref<X>,
    on_press_callable: Option<Callable<X>>,
    child_ref: Ref<X>,
    child: GuiW<X>,
}

impl<X: GXExt> ButtonW<X> {
    pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
        let [(_, child), (_, disabled), (_, height), (_, on_press), (_, padding), (_, width)] =
            source.cast_to::<[(ArcStr, u64); 6]>().context("button flds")?;
        let (child_ref, disabled, height, on_press, padding, width) = try_join! {
            gx.compile_ref(child),
            gx.compile_ref(disabled),
            gx.compile_ref(height),
            gx.compile_ref(on_press),
            gx.compile_ref(padding),
            gx.compile_ref(width),
        }?;
        let compiled_child = compile_child!(gx, child_ref, "button child");
        let callable = compile_callable!(gx, on_press, "button on_press");
        Ok(Box::new(Self {
            gx: gx.clone(),
            disabled: TRef::new(disabled).context("button tref disabled")?,
            width: TRef::new(width).context("button tref width")?,
            height: TRef::new(height).context("button tref height")?,
            padding: TRef::new(padding).context("button tref padding")?,
            on_press,
            on_press_callable: callable,
            child_ref,
            child: compiled_child,
        }))
    }
}

impl<X: GXExt> GuiWidget<X> for ButtonW<X> {
    fn children_mut(&mut self) -> &mut [GuiW<X>] {
        std::slice::from_mut(&mut self.child)
    }

    fn children(&self) -> &[GuiW<X>] {
        std::slice::from_ref(&self.child)
    }

    fn handle_update(
        &mut self,
        rt: &tokio::runtime::Handle,
        id: ExprId,
        v: &Value,
    ) -> Result<bool> {
        let mut changed = false;
        changed |=
            self.disabled.update(id, v).context("button update disabled")?.is_some();
        changed |= self.width.update(id, v).context("button update width")?.is_some();
        changed |= self.height.update(id, v).context("button update height")?.is_some();
        changed |= self.padding.update(id, v).context("button update padding")?.is_some();
        update_callable!(
            self,
            rt,
            id,
            v,
            on_press,
            on_press_callable,
            "button on_press recompile"
        );
        update_child!(
            self,
            rt,
            id,
            v,
            changed,
            child_ref,
            child,
            "button child recompile"
        );
        Ok(changed)
    }


    fn view(&self) -> IcedElement<'_> {
        let mut btn = widget::Button::new(self.child.view());
        if !self.disabled.t.unwrap_or(false) {
            if let Some(callable) = &self.on_press_callable {
                btn = btn.on_press(Message::Call(
                    callable.id(),
                    ValArray::from_iter([Value::Null]),
                ));
            }
        }
        if let Some(w) = self.width.t.as_ref() {
            btn = btn.width(w.0);
        }
        if let Some(h) = self.height.t.as_ref() {
            btn = btn.height(h.0);
        }
        if let Some(p) = self.padding.t.as_ref() {
            btn = btn.padding(p.0);
        }
        btn.into()
    }
}