graphix_package_gui/widgets/
scrollable.rs1use super::{compile, GuiW, GuiWidget, IcedElement, Message};
2use crate::types::{LengthV, ScrollDirectionV};
3use anyhow::{Context, Result};
4use arcstr::ArcStr;
5use graphix_compiler::expr::ExprId;
6use graphix_rt::{Callable, CallableId, GXExt, GXHandle, Ref, TRef};
7use iced_widget as widget;
8use netidx::{protocol::valarray::ValArray, publisher::Value};
9use tokio::try_join;
10
11pub(crate) struct ScrollableW<X: GXExt> {
12 gx: GXHandle<X>,
13 child_ref: Ref<X>,
14 child: GuiW<X>,
15 direction: TRef<X, ScrollDirectionV>,
16 width: TRef<X, LengthV>,
17 height: TRef<X, LengthV>,
18 on_scroll: Ref<X>,
19 on_scroll_callable: Option<Callable<X>>,
20}
21
22impl<X: GXExt> ScrollableW<X> {
23 pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
24 let [(_, child), (_, direction), (_, height), (_, on_scroll), (_, width)] =
25 source.cast_to::<[(ArcStr, u64); 5]>().context("scrollable flds")?;
26 let (child_ref, direction, height, on_scroll, width) = try_join! {
27 gx.compile_ref(child),
28 gx.compile_ref(direction),
29 gx.compile_ref(height),
30 gx.compile_ref(on_scroll),
31 gx.compile_ref(width),
32 }?;
33 let compiled_child = compile_child!(gx, child_ref, "scrollable child");
34 let on_scroll_callable =
35 compile_callable!(gx, on_scroll, "scrollable on_scroll");
36 Ok(Box::new(Self {
37 gx: gx.clone(),
38 child_ref,
39 child: compiled_child,
40 direction: TRef::new(direction).context("scrollable tref direction")?,
41 width: TRef::new(width).context("scrollable tref width")?,
42 height: TRef::new(height).context("scrollable tref height")?,
43 on_scroll,
44 on_scroll_callable,
45 }))
46 }
47}
48
49impl<X: GXExt> GuiWidget<X> for ScrollableW<X> {
50 fn handle_update(
51 &mut self,
52 rt: &tokio::runtime::Handle,
53 id: ExprId,
54 v: &Value,
55 ) -> Result<bool> {
56 let mut changed = false;
57 changed |= self
58 .direction
59 .update(id, v)
60 .context("scrollable update direction")?
61 .is_some();
62 changed |= self.width.update(id, v).context("scrollable update width")?.is_some();
63 changed |=
64 self.height.update(id, v).context("scrollable update height")?.is_some();
65 update_child!(self, rt, id, v, changed, child_ref, child, "scrollable child recompile");
66 update_callable!(self, rt, id, v, on_scroll, on_scroll_callable, "scrollable on_scroll recompile");
67 Ok(changed)
68 }
69
70 fn editor_action(
71 &mut self,
72 id: ExprId,
73 action: &iced_widget::text_editor::Action,
74 ) -> Option<(CallableId, Value)> {
75 self.child.editor_action(id, action)
76 }
77
78 fn view(&self) -> IcedElement<'_> {
79 let mut sc = widget::Scrollable::new(self.child.view());
80 if let Some(dir) = self.direction.t.as_ref() {
81 sc = sc.direction(dir.0);
82 }
83 if let Some(w) = self.width.t.as_ref() {
84 sc = sc.width(w.0);
85 }
86 if let Some(h) = self.height.t.as_ref() {
87 sc = sc.height(h.0);
88 }
89 if let Some(c) = &self.on_scroll_callable {
90 let id = c.id();
91 sc = sc.on_scroll(move |viewport| {
92 let off = viewport.absolute_offset();
93 let offset_val: Value = [
94 (arcstr::literal!("x"), Value::F64(off.x as f64)),
95 (arcstr::literal!("y"), Value::F64(off.y as f64)),
96 ]
97 .into();
98 Message::Call(id, ValArray::from_iter([offset_val]))
99 });
100 }
101 sc.into()
102 }
103}