From 0cb3bb791bdf196c260dd0c3a7527e8864b1e22a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 17 Nov 2020 23:24:14 +0100 Subject: [PATCH] Add `example_web` app --- Cargo.lock | 12 ++++++ Cargo.toml | 3 +- build_example_web.sh | 30 ++++++++++++++ build_web.sh | 4 +- check.sh | 3 ++ demo_glium/src/main.rs | 1 + demo_web/src/lib.rs | 7 +++- docs/example.html | 67 ++++++++++++++++++++++++++++++++ docs/index.html | 5 ++- example_glium/src/example_app.rs | 55 ++++++++++++++++++++++++++ example_glium/src/main.rs | 63 +++--------------------------- example_web/Cargo.toml | 17 ++++++++ example_web/src/example_app.rs | 49 +++++++++++++++++++++++ example_web/src/lib.rs | 20 ++++++++++ 14 files changed, 272 insertions(+), 64 deletions(-) create mode 100755 build_example_web.sh create mode 100644 docs/example.html create mode 100644 example_glium/src/example_app.rs create mode 100644 example_web/Cargo.toml create mode 100644 example_web/src/example_app.rs create mode 100644 example_web/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6854f607e..a96fce1a6 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 94c392594..7fc2f7677 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 000000000..ba5b1e5ce --- /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 4f5917be7..881ef6e63 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 1058d44cb..8191ba2dc 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 a523bcafb..721c8e9a2 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 0248398a0..3f634a7bb 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 000000000..7164f26c0 --- /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 d44c8e6ab..b65c5c337 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 000000000..9fb2a705c --- /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 5db844be5..a3a00d10a 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 000000000..87e9eb865 --- /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 000000000..80b97015e --- /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 000000000..787187b48 --- /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(()) +}