Browse Source

Add `example_web` app

pull/47/head
Emil Ernerfeldt 4 years ago
parent
commit
0cb3bb791b
  1. 12
      Cargo.lock
  2. 3
      Cargo.toml
  3. 30
      build_example_web.sh
  4. 4
      build_web.sh
  5. 3
      check.sh
  6. 1
      demo_glium/src/main.rs
  7. 7
      demo_web/src/lib.rs
  8. 67
      docs/example.html
  9. 5
      docs/index.html
  10. 55
      example_glium/src/example_app.rs
  11. 63
      example_glium/src/main.rs
  12. 17
      example_web/Cargo.toml
  13. 49
      example_web/src/example_app.rs
  14. 20
      example_web/src/lib.rs

12
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"

3
Cargo.toml

@ -1,11 +1,12 @@
[workspace]
members = [
"demo_glium",
"demo_web",
"egui_glium",
"egui_web",
"egui",
"example_glium",
"demo_web",
"example_web",
]

30
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

4
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

3
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

1
demo_glium/src/main.rs

@ -1,3 +1,4 @@
#![forbid(unsafe_code)]
#![deny(warnings)]
#![warn(clippy::all)]

7
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(())
}

67
docs/example.html

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- Disable zooming: -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<head>
<title>Egui – An experimental immediate mode GUI written in Rust</title>
<style>
html {
/* Remove touch delay: */
touch-action: manipulation;
}
body {
background: #101010;
}
/* Allow canvas to fill entire web page: */
html,
body {
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
}
</style>
</head>
<body>
<!--
THis is where the app will show up.
The WASM code will resize this to cover the entire screen.
-->
<canvas id="the_canvas_id"></canvas>
<script>
// The `--no-modules`-generated JS from `wasm-bindgen` attempts to use
// `WebAssembly.instantiateStreaming` to instantiate the wasm module,
// but this doesn't work with `file://` urls. This example is frequently
// viewed by simply opening `index.html` in a browser (with a `file://`
// url), so it would fail if we were to call this function!
//
// Work around this for now by deleting the function to ensure that the
// `no_modules.js` script doesn't have access to it. You won't need this
// hack when deploying over HTTP.
delete WebAssembly.instantiateStreaming;
</script>
<!-- this is the JS generated by the `wasm-bindgen` CLI tool -->
<script src="example_web.js"></script>
<script>
// We'll defer our execution until the wasm is ready to go.
// Here we tell bindgen the path to the wasm file so it can start
// initialization and return to us a promise when it's done.
wasm_bindgen("./example_web_bg.wasm")
.then(on_wasm_loaded)["catch"](console.error);
function on_wasm_loaded() {
// This call installs a bunch of callbacks and then return
wasm_bindgen.start("the_canvas_id");
}
</script>
</body>
</html>

5
docs/index.html

@ -29,7 +29,7 @@
<body>
<!-- The WASM code will resize this to cover the entire screen -->
<canvas id="canvas"></canvas>
<canvas id="the_canvas_id"></canvas>
<script>
// The `--no-modules`-generated JS from `wasm-bindgen` attempts to use
@ -55,7 +55,8 @@
.then(on_wasm_loaded)["catch"](console.error);
function on_wasm_loaded() {
wasm_bindgen.start("canvas");
// This call installs a bunch of callbacks and then return
wasm_bindgen.start("the_canvas_id");
}
</script>
</body>

55
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<egui::Context>,
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);
}
}

63
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<egui::Context>,
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);
}

17
example_web/Cargo.toml

@ -0,0 +1,17 @@
[package]
name = "example_web"
version = "0.1.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
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"

49
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<egui::Context>,
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;
}
});
}
}

20
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(())
}
Loading…
Cancel
Save