graphix-package-gui 0.8.0

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

fn make_handle(source: &ImageSourceV) -> ImageHandle {
    if source.is_svg() {
        ImageHandle::Svg(source.to_svg_handle())
    } else {
        ImageHandle::Raster(source.to_handle())
    }
}

enum ImageHandle {
    Raster(iced_core::image::Handle),
    Svg(iced_core::svg::Handle),
}

pub(crate) struct ImageW<X: GXExt> {
    source: TRef<X, ImageSourceV>,
    handle: Option<ImageHandle>,
    width: TRef<X, LengthV>,
    height: TRef<X, LengthV>,
    content_fit: TRef<X, ContentFitV>,
}

impl<X: GXExt> ImageW<X> {
    pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
        let [(_, content_fit), (_, height), (_, src), (_, width)] =
            source.cast_to::<[(ArcStr, u64); 4]>().context("image flds")?;
        let (content_fit, height, src, width) = try_join! {
            gx.compile_ref(content_fit),
            gx.compile_ref(height),
            gx.compile_ref(src),
            gx.compile_ref(width),
        }?;
        let source = TRef::new(src).context("image tref source")?;
        let handle = source.t.as_ref().map(make_handle);
        Ok(Box::new(Self {
            source,
            handle,
            width: TRef::new(width).context("image tref width")?,
            height: TRef::new(height).context("image tref height")?,
            content_fit: TRef::new(content_fit).context("image tref content_fit")?,
        }))
    }
}

impl<X: GXExt> GuiWidget<X> for ImageW<X> {
    fn handle_update(
        &mut self,
        _rt: &tokio::runtime::Handle,
        id: ExprId,
        v: &Value,
    ) -> Result<bool> {
        let mut changed = false;
        if self.source.update(id, v).context("image update source")?.is_some() {
            self.handle = self.source.t.as_ref().map(make_handle);
            changed = true;
        }
        changed |= self.width.update(id, v).context("image update width")?.is_some();
        changed |= self.height.update(id, v).context("image update height")?.is_some();
        changed |=
            self.content_fit.update(id, v).context("image update content_fit")?.is_some();
        Ok(changed)
    }

    fn view(&self) -> IcedElement<'_> {
        let width = self.width.t.as_ref().map(|w| w.0);
        let height = self.height.t.as_ref().map(|h| h.0);
        let content_fit = self.content_fit.t.as_ref().map(|cf| cf.0);
        match &self.handle {
            Some(ImageHandle::Svg(h)) => {
                let mut s = widget::Svg::new(h.clone());
                if let Some(w) = width {
                    s = s.width(w);
                }
                if let Some(h) = height {
                    s = s.height(h);
                }
                if let Some(cf) = content_fit {
                    s = s.content_fit(cf);
                }
                s.into()
            }
            handle => {
                let h = match handle {
                    Some(ImageHandle::Raster(h)) => h.clone(),
                    _ => iced_core::image::Handle::from_path(""),
                };
                let mut img = widget::Image::new(h);
                if let Some(w) = width {
                    img = img.width(w);
                }
                if let Some(h) = height {
                    img = img.height(h);
                }
                if let Some(cf) = content_fit {
                    img = img.content_fit(cf);
                }
                img.into()
            }
        }
    }
}