Skip to main content

graphix_package_gui/widgets/
markdown.rs

1use super::{GuiW, IcedElement, Message};
2use crate::types::LengthV;
3use anyhow::{Context, Result};
4use arcstr::ArcStr;
5use graphix_compiler::expr::ExprId;
6use graphix_rt::{Callable, GXExt, GXHandle, Ref, TRef};
7use iced_widget as widget;
8use netidx::{protocol::valarray::ValArray, publisher::Value};
9use tokio::try_join;
10
11pub(crate) struct MarkdownW<X: GXExt> {
12    gx: GXHandle<X>,
13    content: TRef<X, String>,
14    on_link: Ref<X>,
15    on_link_callable: Option<Callable<X>>,
16    spacing: TRef<X, Option<f64>>,
17    text_size: TRef<X, Option<f64>>,
18    width: TRef<X, LengthV>,
19    items: Vec<widget::markdown::Item>,
20}
21
22impl<X: GXExt> MarkdownW<X> {
23    pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
24        let [(_, content), (_, on_link), (_, spacing), (_, text_size), (_, width)] =
25            source.cast_to::<[(ArcStr, u64); 5]>().context("markdown flds")?;
26        let (content_ref, on_link, spacing, text_size, width) = try_join! {
27            gx.compile_ref(content),
28            gx.compile_ref(on_link),
29            gx.compile_ref(spacing),
30            gx.compile_ref(text_size),
31            gx.compile_ref(width),
32        }?;
33        let callable = compile_callable!(gx, on_link, "markdown on_link");
34        let content = TRef::new(content_ref).context("markdown tref content")?;
35        let items = match content.t.as_deref() {
36            Some(s) => widget::markdown::parse(s).collect(),
37            None => vec![],
38        };
39        Ok(Box::new(Self {
40            gx: gx.clone(),
41            content,
42            on_link,
43            on_link_callable: callable,
44            spacing: TRef::new(spacing).context("markdown tref spacing")?,
45            text_size: TRef::new(text_size).context("markdown tref text_size")?,
46            width: TRef::new(width).context("markdown tref width")?,
47            items,
48        }))
49    }
50}
51
52impl<X: GXExt> super::GuiWidget<X> for MarkdownW<X> {
53    fn handle_update(
54        &mut self,
55        rt: &tokio::runtime::Handle,
56        id: ExprId,
57        v: &Value,
58    ) -> Result<bool> {
59        let mut changed = false;
60        if let Some(_) = self.content.update(id, v).context("markdown update content")? {
61            self.items = match self.content.t.as_deref() {
62                Some(s) => widget::markdown::parse(s).collect(),
63                None => vec![],
64            };
65            changed = true;
66        }
67        changed |=
68            self.spacing.update(id, v).context("markdown update spacing")?.is_some();
69        changed |= self
70            .text_size
71            .update(id, v)
72            .context("markdown update text_size")?
73            .is_some();
74        changed |= self.width.update(id, v).context("markdown update width")?.is_some();
75        update_callable!(
76            self, rt, id, v, on_link, on_link_callable, "markdown on_link recompile"
77        );
78        Ok(changed)
79    }
80
81    fn view(&self) -> IcedElement<'_> {
82        let text_size = self.text_size.t.flatten().unwrap_or(16.0) as f32;
83        let settings =
84            widget::markdown::Settings::with_text_size(text_size, iced_core::Theme::Dark);
85        let on_link_id = self.on_link_callable.as_ref().map(|c| c.id());
86        let md: iced_core::Element<'_, widget::markdown::Uri, _, _> =
87            widget::markdown::view(&self.items, settings);
88        let element = md.map(move |uri| match on_link_id {
89            Some(id) => {
90                Message::Call(id, ValArray::from_iter([Value::String(uri.into())]))
91            }
92            None => Message::Nop,
93        });
94        let mut container = iced_widget::Container::new(element);
95        if let Some(w) = self.width.t.as_ref() {
96            container = container.width(w.0);
97        }
98        container.into()
99    }
100}