Skip to content
Closed
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,14 @@ features = [
'Node',
'PointerEvent',
'Window',
'WheelEvent'
'WheelEvent',
]

[target.'cfg(target_family = "wasm")'.dev-dependencies.web_sys]
package = "web-sys"
version = "0.3.22"
features = ['HtmlScriptElement']

[target.'cfg(target_family = "wasm")'.dependencies]
js-sys = "0.3"
wasm-bindgen = "0.2.45"
Expand Down
123 changes: 123 additions & 0 deletions examples/wasm_custom_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/// This example will show how to call a Rust function from JavaScript
/// You should see the number 42 appear in browser console output when you click the button

#[cfg(not(wasm_platform))]
pub fn main() {
panic!("This example is only meant to be compiled for wasm target")
}

#[cfg(wasm_platform)]
pub fn main() {
panic!("Please run `cargo run-wasm --example wasm_custom_event`")
}

#[cfg(wasm_platform)]
mod wasm {
use once_cell::unsync::OnceCell;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::HtmlScriptElement;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{EventLoop, EventLoopBuilder, EventLoopProxy};
use winit::window::{Window, WindowBuilder};


thread_local! {
pub static EVENT_LOOP_PROXY: OnceCell<EventLoopProxy<CustomEvent>> = OnceCell::new();
}

// Function to be called from JS
fn wasm_call() -> u32 {
42
}

#[derive(Debug, Clone, Copy)]
pub enum CustomEvent {
WasmCall,
}
Comment on lines +29 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make a slightly more useful example. Instead of just printing 42 we could e.g. use Window::set_inner_size(), which is a valid use-case and would also show how to pass parameters. This would require two input fields for width and height and a button to send.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awaiting if the cargo-wasm-runner will go through first. rukai/cargo-run-wasm#36


#[wasm_bindgen(start)]
pub fn run() {
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");

let event_loop: EventLoop<CustomEvent> =
EventLoopBuilder::<CustomEvent>::with_user_event().build();

let event_loop_proxy = event_loop.create_proxy();

// Initialize the thread_local EVENT_LOOP_PROXY value
EVENT_LOOP_PROXY.with(|cell| {
cell.set(event_loop_proxy).unwrap();
});

let window = WindowBuilder::new().build(&event_loop).unwrap();

insert_canvas(&window);

event_loop.run(move |event, _, control_flow| {
control_flow.set_wait();

match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => control_flow.set_exit(),
Comment on lines +61 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can skip that here, it's not a thing in Wasm.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip this match arm?

Event::WindowEvent {
    event: WindowEvent::CloseRequested,
    window_id,
} if window_id == window.id() => control_flow.set_exit(),

Event::MainEventsCleared => {
window.request_redraw();
}
// Handle custom events here
Event::UserEvent(CustomEvent::WasmCall) => {
// Send the result back to JS as proof that the custom event was handled
log::info!("{:?}", wasm_call());
}
_ => (),
}
});
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = "handleWasmCall"))]
pub fn handle_wasm_call() {
EVENT_LOOP_PROXY.with(|cell| {
cell.get().unwrap().send_event(CustomEvent::WasmCall).ok();
});
}

pub fn insert_canvas(window: &Window) {
use winit::platform::web::WindowExtWebSys;

let canvas = window.canvas();

let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();

// Set a background color for the canvas to make it easier to tell where the canvas is for debugging purposes.
canvas.style().set_property("background-color", "crimson").unwrap();
body.append_child(&canvas).unwrap();

// Create script element
let script: HtmlScriptElement = document
.create_element("script")
.unwrap()
.dyn_into()
.unwrap();
script.set_type("module");

// Your JavaScript code here, including creating the button and attaching the event handler
script.set_inner_text(
r#"
import { handleWasmCall } from "./wasm_custom_event.js";
let button = document.createElement("button");
button.innerHTML = "Favourite Number?";
button.onclick = handleWasmCall;
document.body.appendChild(button);
"#,
);

let first_child = body.first_child();
match first_child {
Some(node) => body.insert_before(&script, Some(&node)).unwrap(),
None => body.append_child(&script).unwrap(),
};
}
}
12 changes: 11 additions & 1 deletion run-wasm/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
fn main() {
cargo_run_wasm::run_wasm_with_css("body { margin: 0px; }");
cargo_run_wasm::run_wasm_with_css(
r#"
body {
margin: 0px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
"#,
);
}