graphix_package_gui/widgets/
markdown.rs1use 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}