Skip to main content

graphix_package_gui/widgets/
scrollable.rs

1use 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 = compile_callable!(gx, on_scroll, "scrollable on_scroll");
35        Ok(Box::new(Self {
36            gx: gx.clone(),
37            child_ref,
38            child: compiled_child,
39            direction: TRef::new(direction).context("scrollable tref direction")?,
40            width: TRef::new(width).context("scrollable tref width")?,
41            height: TRef::new(height).context("scrollable tref height")?,
42            on_scroll,
43            on_scroll_callable,
44        }))
45    }
46}
47
48impl<X: GXExt> GuiWidget<X> for ScrollableW<X> {
49    fn handle_update(
50        &mut self,
51        rt: &tokio::runtime::Handle,
52        id: ExprId,
53        v: &Value,
54    ) -> Result<bool> {
55        let mut changed = false;
56        changed |= self
57            .direction
58            .update(id, v)
59            .context("scrollable update direction")?
60            .is_some();
61        changed |= self.width.update(id, v).context("scrollable update width")?.is_some();
62        changed |=
63            self.height.update(id, v).context("scrollable update height")?.is_some();
64        update_child!(
65            self,
66            rt,
67            id,
68            v,
69            changed,
70            child_ref,
71            child,
72            "scrollable child recompile"
73        );
74        update_callable!(
75            self,
76            rt,
77            id,
78            v,
79            on_scroll,
80            on_scroll_callable,
81            "scrollable on_scroll recompile"
82        );
83        Ok(changed)
84    }
85
86    fn editor_action(
87        &mut self,
88        id: ExprId,
89        action: &iced_widget::text_editor::Action,
90    ) -> Option<(CallableId, Value)> {
91        self.child.editor_action(id, action)
92    }
93
94    fn view(&self) -> IcedElement<'_> {
95        let mut sc = widget::Scrollable::new(self.child.view());
96        if let Some(dir) = self.direction.t.as_ref() {
97            sc = sc.direction(dir.0);
98        }
99        if let Some(w) = self.width.t.as_ref() {
100            sc = sc.width(w.0);
101        }
102        if let Some(h) = self.height.t.as_ref() {
103            sc = sc.height(h.0);
104        }
105        if let Some(c) = &self.on_scroll_callable {
106            let id = c.id();
107            sc = sc.on_scroll(move |viewport| {
108                let off = viewport.absolute_offset();
109                let offset_val: Value = [
110                    (arcstr::literal!("x"), Value::F64(off.x as f64)),
111                    (arcstr::literal!("y"), Value::F64(off.y as f64)),
112                ]
113                .into();
114                Message::Call(id, ValArray::from_iter([offset_val]))
115            });
116        }
117        sc.into()
118    }
119}