Skip to main content

graphix_package_gui/widgets/
image.rs

1use super::{GuiW, GuiWidget, IcedElement};
2use crate::types::{ContentFitV, ImageSourceV, LengthV};
3use anyhow::{Context, Result};
4use arcstr::ArcStr;
5use graphix_compiler::expr::ExprId;
6use graphix_rt::{GXExt, GXHandle, TRef};
7use iced_widget as widget;
8use netidx::publisher::Value;
9use tokio::try_join;
10
11fn make_handle(source: &ImageSourceV) -> ImageHandle {
12    if source.is_svg() {
13        ImageHandle::Svg(source.to_svg_handle())
14    } else {
15        ImageHandle::Raster(source.to_handle())
16    }
17}
18
19enum ImageHandle {
20    Raster(iced_core::image::Handle),
21    Svg(iced_core::svg::Handle),
22}
23
24pub(crate) struct ImageW<X: GXExt> {
25    source: TRef<X, ImageSourceV>,
26    handle: Option<ImageHandle>,
27    width: TRef<X, LengthV>,
28    height: TRef<X, LengthV>,
29    content_fit: TRef<X, ContentFitV>,
30}
31
32impl<X: GXExt> ImageW<X> {
33    pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
34        let [(_, content_fit), (_, height), (_, src), (_, width)] =
35            source.cast_to::<[(ArcStr, u64); 4]>().context("image flds")?;
36        let (content_fit, height, src, width) = try_join! {
37            gx.compile_ref(content_fit),
38            gx.compile_ref(height),
39            gx.compile_ref(src),
40            gx.compile_ref(width),
41        }?;
42        let source = TRef::new(src).context("image tref source")?;
43        let handle = source.t.as_ref().map(make_handle);
44        Ok(Box::new(Self {
45            source,
46            handle,
47            width: TRef::new(width).context("image tref width")?,
48            height: TRef::new(height).context("image tref height")?,
49            content_fit: TRef::new(content_fit).context("image tref content_fit")?,
50        }))
51    }
52}
53
54impl<X: GXExt> GuiWidget<X> for ImageW<X> {
55    fn handle_update(
56        &mut self,
57        _rt: &tokio::runtime::Handle,
58        id: ExprId,
59        v: &Value,
60    ) -> Result<bool> {
61        let mut changed = false;
62        if self.source.update(id, v).context("image update source")?.is_some() {
63            self.handle = self.source.t.as_ref().map(make_handle);
64            changed = true;
65        }
66        changed |= self.width.update(id, v).context("image update width")?.is_some();
67        changed |= self.height.update(id, v).context("image update height")?.is_some();
68        changed |=
69            self.content_fit.update(id, v).context("image update content_fit")?.is_some();
70        Ok(changed)
71    }
72
73    fn view(&self) -> IcedElement<'_> {
74        let width = self.width.t.as_ref().map(|w| w.0);
75        let height = self.height.t.as_ref().map(|h| h.0);
76        let content_fit = self.content_fit.t.as_ref().map(|cf| cf.0);
77        match &self.handle {
78            Some(ImageHandle::Svg(h)) => {
79                let mut s = widget::Svg::new(h.clone());
80                if let Some(w) = width {
81                    s = s.width(w);
82                }
83                if let Some(h) = height {
84                    s = s.height(h);
85                }
86                if let Some(cf) = content_fit {
87                    s = s.content_fit(cf);
88                }
89                s.into()
90            }
91            handle => {
92                let h = match handle {
93                    Some(ImageHandle::Raster(h)) => h.clone(),
94                    _ => iced_core::image::Handle::from_path(""),
95                };
96                let mut img = widget::Image::new(h);
97                if let Some(w) = width {
98                    img = img.width(w);
99                }
100                if let Some(h) = height {
101                    img = img.height(h);
102                }
103                if let Some(cf) = content_fit {
104                    img = img.content_fit(cf);
105                }
106                img.into()
107            }
108        }
109    }
110}