Skip to main content

graphix_package_gui/widgets/
context_menu.rs

1use super::context_menu_widget::OwnedContextMenu;
2use super::menu_bar::{compile_menu_items, menu_item_desc, MenuItemKind};
3use super::{compile, GuiW, GuiWidget, IcedElement};
4use anyhow::{Context, Result};
5use arcstr::ArcStr;
6use graphix_compiler::expr::ExprId;
7use graphix_rt::{CallableId, GXExt, GXHandle, Ref};
8use netidx::publisher::Value;
9use tokio::try_join;
10
11pub(crate) struct ContextMenuW<X: GXExt> {
12    gx: GXHandle<X>,
13    child_ref: Ref<X>,
14    child: GuiW<X>,
15    items_ref: Ref<X>,
16    items: Vec<MenuItemKind<X>>,
17}
18
19impl<X: GXExt> ContextMenuW<X> {
20    pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
21        let [(_, child), (_, items)] =
22            source.cast_to::<[(ArcStr, u64); 2]>().context("context_menu flds")?;
23        let (child_ref, items_ref) = try_join! {
24            gx.compile_ref(child),
25            gx.compile_ref(items),
26        }?;
27        let compiled_child = compile_child!(gx, child_ref, "context_menu child");
28        let compiled_items = match items_ref.last.as_ref() {
29            Some(v) => {
30                compile_menu_items(&gx, v.clone()).await.context("context_menu items")?
31            }
32            None => vec![],
33        };
34        Ok(Box::new(Self {
35            gx: gx.clone(),
36            child_ref,
37            child: compiled_child,
38            items_ref,
39            items: compiled_items,
40        }))
41    }
42}
43
44impl<X: GXExt> GuiWidget<X> for ContextMenuW<X> {
45    fn handle_update(
46        &mut self,
47        rt: &tokio::runtime::Handle,
48        id: ExprId,
49        v: &Value,
50    ) -> Result<bool> {
51        let mut changed = false;
52        update_child!(
53            self, rt, id, v, changed, child_ref, child,
54            "context_menu child recompile"
55        );
56        if id == self.items_ref.id {
57            self.items_ref.last = Some(v.clone());
58            self.items = rt
59                .block_on(compile_menu_items(&self.gx, v.clone()))
60                .context("context_menu items recompile")?;
61            changed = true;
62        }
63        for item in &mut self.items {
64            match item {
65                MenuItemKind::Action {
66                    label,
67                    shortcut,
68                    on_click,
69                    on_click_callable,
70                    disabled,
71                } => {
72                    changed |= label
73                        .update(id, v)
74                        .context("context_menu item update label")?
75                        .is_some();
76                    changed |= shortcut
77                        .update(id, v)
78                        .context("context_menu item update shortcut")?
79                        .is_some();
80                    changed |= disabled
81                        .update(id, v)
82                        .context("context_menu item update disabled")?
83                        .is_some();
84                    if id == on_click.id {
85                        on_click.last = Some(v.clone());
86                        *on_click_callable = Some(
87                            rt.block_on(self.gx.compile_callable(v.clone()))
88                                .context("context_menu item on_click recompile")?,
89                        );
90                    }
91                }
92                MenuItemKind::Divider => {}
93            }
94        }
95        Ok(changed)
96    }
97
98    fn editor_action(
99        &mut self,
100        id: ExprId,
101        action: &iced_widget::text_editor::Action,
102    ) -> Option<(CallableId, Value)> {
103        self.child.editor_action(id, action)
104    }
105
106    fn view(&self) -> IcedElement<'_> {
107        let items = self.items.iter().map(menu_item_desc).collect();
108        OwnedContextMenu::new(self.child.view(), items).into()
109    }
110}