graphix_package_gui/
lib.rs1#![doc(
2 html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
3 html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
4)]
5use async_trait::async_trait;
6use graphix_compiler::{
7 env::Env,
8 expr::{ExprId, ModPath},
9 typ::Type,
10};
11use graphix_package::CustomDisplay;
12use graphix_rt::{CompExp, GXExt, GXHandle};
13use log::error;
14use netidx::publisher::Value;
15use std::{marker::PhantomData, sync::LazyLock};
16use tokio::sync::oneshot;
17use triomphe::Arc;
18use types::SizeV;
19use winit::{event_loop::EventLoopProxy, window::WindowId};
20
21mod clipboard;
22mod convert;
23mod event_loop;
24mod render;
25pub(crate) mod theme;
26mod types;
27pub(crate) mod widgets;
28mod window;
29
30#[cfg(test)]
31mod test;
32
33pub(crate) enum ToGui {
34 Update(ExprId, Value),
35 ResizeTimer(WindowId, SizeV),
36 Stop(oneshot::Sender<()>),
37}
38
39struct Gui<X: GXExt> {
40 proxy: EventLoopProxy<ToGui>,
41 ph: PhantomData<X>,
42}
43
44impl<X: GXExt> Gui<X> {
45 async fn start(
46 gx: &GXHandle<X>,
47 _env: Env,
48 root: CompExp<X>,
49 stop: oneshot::Sender<()>,
50 run_on_main: graphix_package::MainThreadHandle,
51 ) -> Self {
52 let gx = gx.clone();
53 let (proxy_tx, proxy_rx) = oneshot::channel();
54 let rt_handle = tokio::runtime::Handle::current();
55 run_on_main
56 .run(Box::new(move || {
57 event_loop::run(gx, root, proxy_tx, stop, rt_handle);
58 }))
59 .expect("main thread receiver dropped");
60 let proxy = proxy_rx.await.expect("event loop failed to send proxy");
61 Self { proxy, ph: PhantomData }
62 }
63
64 fn update(&self, id: ExprId, v: Value) {
65 if self.proxy.send_event(ToGui::Update(id, v)).is_err() {
66 error!("could not send update because gui event loop closed")
67 }
68 }
69}
70
71static GUITYP: LazyLock<Type> = LazyLock::new(|| {
72 Type::Array(Arc::new(Type::ByRef(Arc::new(Type::Ref {
73 scope: ModPath::root(),
74 name: ModPath::from(["gui", "Window"]),
75 params: Arc::from_iter([]),
76 }))))
77});
78
79#[async_trait]
80impl<X: GXExt> CustomDisplay<X> for Gui<X> {
81 async fn clear(&mut self) {
82 let (tx, rx) = oneshot::channel::<()>();
83 let _ = self.proxy.send_event(ToGui::Stop(tx));
84 let _ = rx.await;
85 }
86
87 async fn process_update(&mut self, _env: &Env, id: ExprId, v: Value) {
88 self.update(id, v);
89 }
90}
91
92graphix_derive::defpackage! {
93 builtins => [
94 clipboard::ReadText,
95 clipboard::WriteText,
96 clipboard::ReadImage,
97 clipboard::WriteImage,
98 clipboard::ReadHtml,
99 clipboard::WriteHtml,
100 clipboard::ReadFiles,
101 clipboard::WriteFiles,
102 clipboard::Clear,
103 ],
104 is_custom => |gx, env, e| {
105 if let Some(typ) = e.typ.with_deref(|t| t.cloned())
106 && typ != Type::Bottom
107 && typ != Type::Any
108 {
109 GUITYP.contains(env, &typ).unwrap_or(false)
110 } else {
111 false
112 }
113 },
114 init_custom => |gx, env, stop, e, run_on_main| {
115 Ok(Box::new(Gui::<X>::start(gx, env.clone(), e, stop, run_on_main).await))
116 },
117}