add native window/webview builtins to el runtime
Adds five new builtins backed by wry 0.47 + tao 0.30: - window_open(title, width, height) — configure window - webview_load_url(url) — set URL to navigate to - webview_load_html(html) — set HTML to display - webview_eval(js) — queue JS to eval after load - window_run() — open native OS window and run event loop (blocking, exits on close) WebViewState is stored in a thread_local. window_run() calls tao's event_loop.run() which never returns; process exits when the window is closed via std::process::exit(0).
This commit is contained in:
Generated
+2081
-61
File diff suppressed because it is too large
Load Diff
@@ -34,3 +34,5 @@ hmac = { workspace = true }
|
|||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
|
wry = "0.47"
|
||||||
|
tao = "0.30"
|
||||||
|
|||||||
@@ -56,6 +56,29 @@ thread_local! {
|
|||||||
std::cell::RefCell::new(None);
|
std::cell::RefCell::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Native window / WebView state ────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct WebViewState {
|
||||||
|
title: String,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
url: Option<String>,
|
||||||
|
html: Option<String>,
|
||||||
|
pending_js: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static WV_STATE: std::cell::RefCell<WebViewState> = std::cell::RefCell::new(WebViewState {
|
||||||
|
title: "Neuron".to_string(),
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
url: None,
|
||||||
|
html: None,
|
||||||
|
pending_js: Vec::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ── CLI definition ────────────────────────────────────────────────────────────
|
// ── CLI definition ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@@ -4163,6 +4186,82 @@ fn dispatch_builtin(
|
|||||||
BuiltinResult::Handled
|
BuiltinResult::Handled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Native window / WebView builtins ──────────────────────────────────
|
||||||
|
|
||||||
|
"window_open" => {
|
||||||
|
// window_open(title: String, width: Int, height: Int) -> Void
|
||||||
|
let height = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as u32, _ => 800 };
|
||||||
|
let width = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as u32, _ => 1200 };
|
||||||
|
let title = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => "Neuron".to_string() };
|
||||||
|
WV_STATE.with(|st| {
|
||||||
|
let mut s = st.borrow_mut();
|
||||||
|
s.title = title;
|
||||||
|
s.width = width;
|
||||||
|
s.height = height;
|
||||||
|
});
|
||||||
|
BuiltinResult::Handled
|
||||||
|
}
|
||||||
|
|
||||||
|
"webview_load_url" => {
|
||||||
|
// webview_load_url(url: String) -> Void
|
||||||
|
let url = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { return BuiltinResult::Handled; } };
|
||||||
|
WV_STATE.with(|st| st.borrow_mut().url = Some(url));
|
||||||
|
BuiltinResult::Handled
|
||||||
|
}
|
||||||
|
|
||||||
|
"webview_load_html" => {
|
||||||
|
// webview_load_html(html: String) -> Void
|
||||||
|
let html = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { return BuiltinResult::Handled; } };
|
||||||
|
WV_STATE.with(|st| st.borrow_mut().html = Some(html));
|
||||||
|
BuiltinResult::Handled
|
||||||
|
}
|
||||||
|
|
||||||
|
"webview_eval" => {
|
||||||
|
// webview_eval(js: String) -> Void
|
||||||
|
let js = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { return BuiltinResult::Handled; } };
|
||||||
|
WV_STATE.with(|st| st.borrow_mut().pending_js.push(js));
|
||||||
|
BuiltinResult::Handled
|
||||||
|
}
|
||||||
|
|
||||||
|
"window_run" => {
|
||||||
|
// window_run() -> Void (never returns; exits process on window close)
|
||||||
|
use tao::event::{Event, WindowEvent};
|
||||||
|
use tao::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use tao::window::WindowBuilder;
|
||||||
|
use wry::WebViewBuilder;
|
||||||
|
|
||||||
|
let (title, width, height, url, html) = WV_STATE.with(|st| {
|
||||||
|
let s = st.borrow();
|
||||||
|
(s.title.clone(), s.width, s.height, s.url.clone(), s.html.clone())
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_title(title)
|
||||||
|
.with_inner_size(tao::dpi::LogicalSize::new(width, height))
|
||||||
|
.build(&event_loop)
|
||||||
|
.expect("failed to create window");
|
||||||
|
|
||||||
|
let mut builder = WebViewBuilder::new();
|
||||||
|
if let Some(u) = url {
|
||||||
|
builder = builder.with_url(u);
|
||||||
|
} else if let Some(h) = html {
|
||||||
|
builder = builder.with_html(h);
|
||||||
|
} else {
|
||||||
|
builder = builder.with_html("<h1>Neuron</h1>");
|
||||||
|
}
|
||||||
|
|
||||||
|
let _webview = builder.build(&window).expect("failed to create webview");
|
||||||
|
|
||||||
|
// event_loop.run() never returns (diverging fn `-> !`)
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::Wait;
|
||||||
|
if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = event {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_ => BuiltinResult::NotBuiltin,
|
_ => BuiltinResult::NotBuiltin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,13 @@ impl TypeEnv {
|
|||||||
env.functions.insert("cursor_col".into(), str_fn(vec![i.clone()], Type::Void));
|
env.functions.insert("cursor_col".into(), str_fn(vec![i.clone()], Type::Void));
|
||||||
env.functions.insert("http_sse_post".into(), str_fn(vec![s.clone(), s.clone(), s.clone()], s.clone()));
|
env.functions.insert("http_sse_post".into(), str_fn(vec![s.clone(), s.clone(), s.clone()], s.clone()));
|
||||||
|
|
||||||
|
// Native window / WebView builtins
|
||||||
|
env.functions.insert("window_open".into(), str_fn(vec![s.clone(), i.clone(), i.clone()], Type::Void));
|
||||||
|
env.functions.insert("webview_load_url".into(), str_fn(vec![s.clone()], Type::Void));
|
||||||
|
env.functions.insert("webview_load_html".into(), str_fn(vec![s.clone()], Type::Void));
|
||||||
|
env.functions.insert("webview_eval".into(), str_fn(vec![s.clone()], Type::Void));
|
||||||
|
env.functions.insert("window_run".into(), str_fn(vec![], Type::Void));
|
||||||
|
|
||||||
// Math
|
// Math
|
||||||
for name in &["math_abs","math_floor","math_ceil","math_round","math_sqrt"] {
|
for name in &["math_abs","math_floor","math_ceil","math_round","math_sqrt"] {
|
||||||
env.functions.insert(name.to_string(), str_fn(vec![u.clone()], u.clone()));
|
env.functions.insert(name.to_string(), str_fn(vec![u.clone()], u.clone()));
|
||||||
|
|||||||
Reference in New Issue
Block a user