diff --git a/Cargo.lock b/Cargo.lock
index 6854f607..a96fce1a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -580,6 +580,18 @@ dependencies = [
"serde",
]
+[[package]]
+name = "example_web"
+version = "0.1.0"
+dependencies = [
+ "egui",
+ "egui_web",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+]
+
[[package]]
name = "fnv"
version = "1.0.7"
diff --git a/Cargo.toml b/Cargo.toml
index 94c39259..7fc2f767 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,11 +1,12 @@
[workspace]
members = [
"demo_glium",
+ "demo_web",
"egui_glium",
"egui_web",
"egui",
"example_glium",
- "demo_web",
+ "example_web",
]
diff --git a/build_example_web.sh b/build_example_web.sh
new file mode 100755
index 00000000..ba5b1e5c
--- /dev/null
+++ b/build_example_web.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -eu
+
+# Pre-requisites:
+rustup target add wasm32-unknown-unknown
+if ! wasm-bindgen --version; then
+ cargo clean
+ cargo install -f wasm-bindgen-cli
+ cargo update
+fi
+
+# BUILD=debug
+BUILD=release
+
+export RUSTFLAGS=--cfg=web_sys_unstable_apis # required for the clipboard API
+
+# Clear output from old stuff:
+rm -rf docs/example_web.wasm
+
+echo "Build rust:"
+# cargo build -p example_web --target wasm32-unknown-unknown
+cargo build --release -p example_web --target wasm32-unknown-unknown
+
+echo "Generate JS bindings for wasm:"
+FOLDER_NAME=${PWD##*/}
+TARGET_NAME="example_web.wasm"
+wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \
+ --out-dir docs --no-modules --no-typescript
+
+open http://localhost:8888/example.html
diff --git a/build_web.sh b/build_web.sh
index 4f5917be..881ef6e6 100755
--- a/build_web.sh
+++ b/build_web.sh
@@ -15,7 +15,7 @@ BUILD=release
export RUSTFLAGS=--cfg=web_sys_unstable_apis # required for the clipboard API
# Clear output from old stuff:
-rm -rf docs/*.wasm
+rm -rf docs/demo_web.wasm
echo "Build rust:"
# cargo build -p demo_web --target wasm32-unknown-unknown
@@ -27,4 +27,4 @@ TARGET_NAME="demo_web.wasm"
wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \
--out-dir docs --no-modules --no-typescript
-open http://localhost:8888
+open http://localhost:8888/index.html
diff --git a/check.sh b/check.sh
index 1058d44c..8191ba2d 100755
--- a/check.sh
+++ b/check.sh
@@ -9,7 +9,10 @@ CARGO_INCREMENTAL=0 cargo clippy --workspace --all-targets --all-features -- -D
cargo test --workspace --all-targets --all-features
cargo test --workspace --doc
+cargo check -p egui --target wasm32-unknown-unknown
+cargo check -p egui_web --target wasm32-unknown-unknown
cargo check -p demo_web --target wasm32-unknown-unknown
+cargo check -p example_web --target wasm32-unknown-unknown
# For finding bloat:
# cargo bloat --release --bin demo_glium -n 200 | rg egui
diff --git a/demo_glium/src/main.rs b/demo_glium/src/main.rs
index a523bcaf..721c8e9a 100644
--- a/demo_glium/src/main.rs
+++ b/demo_glium/src/main.rs
@@ -1,3 +1,4 @@
+#![forbid(unsafe_code)]
#![deny(warnings)]
#![warn(clippy::all)]
diff --git a/demo_web/src/lib.rs b/demo_web/src/lib.rs
index 0248398a..3f634a7b 100644
--- a/demo_web/src/lib.rs
+++ b/demo_web/src/lib.rs
@@ -5,11 +5,14 @@
use wasm_bindgen::prelude::*;
/// This is the entry-point for all the web-assembly.
+/// This is called once from the HTML.
+/// It loads the app, installs some callbacks, then returns.
+/// You can add more callbacks like this if you want to call in to your code.
#[wasm_bindgen]
pub fn start(canvas_id: &str) -> Result<(), wasm_bindgen::JsValue> {
+ let app = egui::DemoApp::default();
let backend = egui_web::WebBackend::new(canvas_id)?;
- let app = Box::new(egui::DemoApp::default());
- let runner = egui_web::AppRunner::new(backend, app)?;
+ let runner = egui_web::AppRunner::new(backend, Box::new(app))?;
egui_web::start(runner)?;
Ok(())
}
diff --git a/docs/example.html b/docs/example.html
new file mode 100644
index 00000000..7164f26c
--- /dev/null
+++ b/docs/example.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+ Egui – An experimental immediate mode GUI written in Rust
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/index.html b/docs/index.html
index d44c8e6a..b65c5c33 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -29,7 +29,7 @@
-
+
diff --git a/example_glium/src/example_app.rs b/example_glium/src/example_app.rs
new file mode 100644
index 00000000..9fb2a705
--- /dev/null
+++ b/example_glium/src/example_app.rs
@@ -0,0 +1,55 @@
+/// We derive Deserialize/Serialize so we can persist app state on shutdown.
+#[derive(serde::Deserialize, serde::Serialize)]
+pub struct ExampleApp {
+ name: String,
+ age: u32,
+}
+
+impl Default for ExampleApp {
+ fn default() -> Self {
+ Self {
+ name: "Arthur".to_owned(),
+ age: 42,
+ }
+ }
+}
+
+impl egui::app::App for ExampleApp {
+ /// Called each time the UI needs repainting, which may be many times per second.
+ /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
+ fn ui(
+ &mut self,
+ ctx: &std::sync::Arc,
+ integration_context: &mut egui::app::IntegrationContext,
+ ) {
+ let ExampleApp { name, age } = self;
+
+ // Example used in `README.md`.
+ egui::CentralPanel::default().show(ctx, |ui| {
+ ui.heading("My Egui Application");
+
+ ui.horizontal(|ui| {
+ ui.label("Your name: ");
+ ui.text_edit_singleline(name);
+ });
+
+ ui.add(egui::Slider::u32(age, 0..=120).text("age"));
+ if ui.button("Click each year").clicked {
+ *age += 1;
+ }
+
+ ui.label(format!("Hello '{}', age {}", name, age));
+
+ ui.advance_cursor(16.0);
+ if ui.button("Quit").clicked {
+ integration_context.output.quit = true;
+ }
+ });
+
+ integration_context.output.window_size = Some(ctx.used_size()); // resize the window to be just the size we need it to be
+ }
+
+ fn on_exit(&mut self, storage: &mut dyn egui::app::Storage) {
+ egui::app::set_value(storage, egui::app::APP_KEY, self);
+ }
+}
diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs
index 5db844be..a3a00d10 100644
--- a/example_glium/src/main.rs
+++ b/example_glium/src/main.rs
@@ -1,63 +1,10 @@
//! Example of how to use Egui
-
+#![forbid(unsafe_code)]
#![deny(warnings)]
#![warn(clippy::all)]
-/// We derive Deserialize/Serialize so we can persist app state on shutdown.
-#[derive(serde::Deserialize, serde::Serialize)]
-struct MyApp {
- name: String,
- age: u32,
-}
-
-impl Default for MyApp {
- fn default() -> Self {
- Self {
- name: "Arthur".to_owned(),
- age: 42,
- }
- }
-}
-
-impl egui::app::App for MyApp {
- /// Called each time the UI needs repainting, which may be many times per second.
- /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
- fn ui(
- &mut self,
- ctx: &std::sync::Arc,
- integration_context: &mut egui::app::IntegrationContext,
- ) {
- let MyApp { name, age } = self;
-
- // Example used in `README.md`.
- egui::CentralPanel::default().show(ctx, |ui| {
- ui.heading("My Egui Application");
-
- ui.horizontal(|ui| {
- ui.label("Your name: ");
- ui.text_edit_singleline(name);
- });
-
- ui.add(egui::Slider::u32(age, 0..=120).text("age"));
- if ui.button("Click each year").clicked {
- *age += 1;
- }
-
- ui.label(format!("Hello '{}', age {}", name, age));
-
- ui.advance_cursor(16.0);
- if ui.button("Quit").clicked {
- integration_context.output.quit = true;
- }
- });
-
- integration_context.output.window_size = Some(ctx.used_size()); // resize the window to be just the size we need it to be
- }
-
- fn on_exit(&mut self, storage: &mut dyn egui::app::Storage) {
- egui::app::set_value(storage, egui::app::APP_KEY, self);
- }
-}
+mod example_app;
+use example_app::ExampleApp;
fn main() {
let title = "My Egui Window";
@@ -68,6 +15,8 @@ fn main() {
// Alternative: store nowhere
// let storage = egui::app::DummyStorage::default();
- let app: MyApp = egui::app::get_value(&storage, egui::app::APP_KEY).unwrap_or_default(); // Restore `MyApp` from file, or create new `MyApp`.
+ // Restore `example_app` from file, or create new `ExampleApp`:
+ let app: ExampleApp = egui::app::get_value(&storage, egui::app::APP_KEY).unwrap_or_default();
+
egui_glium::run(title, Box::new(storage), app);
}
diff --git a/example_web/Cargo.toml b/example_web/Cargo.toml
new file mode 100644
index 00000000..87e9eb86
--- /dev/null
+++ b/example_web/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "example_web"
+version = "0.1.0"
+authors = ["Emil Ernerfeldt "]
+license = "MIT OR Apache-2.0"
+edition = "2018"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+egui = { path = "../egui", features = ["serde"] }
+egui_web = { path = "../egui_web" }
+js-sys = "0.3"
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+wasm-bindgen = "0.2"
diff --git a/example_web/src/example_app.rs b/example_web/src/example_app.rs
new file mode 100644
index 00000000..80b97015
--- /dev/null
+++ b/example_web/src/example_app.rs
@@ -0,0 +1,49 @@
+/// We derive Deserialize/Serialize so we can persist app state on shutdown.
+#[derive(serde::Deserialize, serde::Serialize)]
+pub struct ExampleApp {
+ name: String,
+ age: u32,
+}
+
+impl Default for ExampleApp {
+ fn default() -> Self {
+ Self {
+ name: "Arthur".to_owned(),
+ age: 42,
+ }
+ }
+}
+
+impl egui::app::App for ExampleApp {
+ /// Called each time the UI needs repainting, which may be many times per second.
+ /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
+ fn ui(
+ &mut self,
+ ctx: &std::sync::Arc,
+ integration_context: &mut egui::app::IntegrationContext,
+ ) {
+ let ExampleApp { name, age } = self;
+
+ // Example used in `README.md`.
+ egui::CentralPanel::default().show(ctx, |ui| {
+ ui.heading("My Egui Application");
+
+ ui.horizontal(|ui| {
+ ui.label("Your name: ");
+ ui.text_edit_singleline(name);
+ });
+
+ ui.add(egui::Slider::u32(age, 0..=120).text("age"));
+ if ui.button("Click each year").clicked {
+ *age += 1;
+ }
+
+ ui.label(format!("Hello '{}', age {}", name, age));
+
+ ui.advance_cursor(16.0);
+ if ui.button("Quit").clicked {
+ integration_context.output.quit = true;
+ }
+ });
+ }
+}
diff --git a/example_web/src/lib.rs b/example_web/src/lib.rs
new file mode 100644
index 00000000..787187b4
--- /dev/null
+++ b/example_web/src/lib.rs
@@ -0,0 +1,20 @@
+#![forbid(unsafe_code)]
+#![deny(warnings)]
+#![warn(clippy::all)]
+
+mod example_app;
+
+use wasm_bindgen::prelude::*;
+
+/// This is the entry-point for all the web-assembly.
+/// This is called once from the HTML.
+/// It loads the app, installs some callbacks, then returns.
+/// You can add more callbacks like this if you want to call in to your code.
+#[wasm_bindgen]
+pub fn start(canvas_id: &str) -> Result<(), wasm_bindgen::JsValue> {
+ let app = example_app::ExampleApp::default();
+ let backend = egui_web::WebBackend::new(canvas_id)?;
+ let runner = egui_web::AppRunner::new(backend, Box::new(app))?;
+ egui_web::start(runner)?;
+ Ok(())
+}