mirror of https://github.com/emilk/egui.git
Emil Ernerfeldt
6 years ago
commit
856bbf4dae
14 changed files with 701 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||
|
*.sublime* |
||||
|
/target |
@ -0,0 +1,22 @@ |
|||||
|
[package] |
||||
|
name = "emgui" |
||||
|
version = "0.1.0" |
||||
|
authors = ["Emil Ernerfeldt <emilernerfeldt@gmail.com>"] |
||||
|
|
||||
|
[lib] |
||||
|
crate-type = ["cdylib", "rlib"] |
||||
|
|
||||
|
[dependencies] |
||||
|
# rand = { version="0.6", features = ['wasm-bindgen'] } |
||||
|
serde = "1" |
||||
|
serde_derive = "1" |
||||
|
serde_json = "1" |
||||
|
wasm-bindgen = "0.2" |
||||
|
web-sys = { version = "0.3.5", features = ['console', 'Performance', 'Window'] } |
||||
|
|
||||
|
# Optimize for small code size: |
||||
|
[profile.dev] |
||||
|
opt-level = "s" |
||||
|
|
||||
|
[profile.release] |
||||
|
opt-level = "s" |
@ -0,0 +1,21 @@ |
|||||
|
# Emgui – An Experimental, Modularized immediate mode Graphical User Interface |
||||
|
|
||||
|
Here are the steps, in chronological order of execution: |
||||
|
|
||||
|
CODE: Input bindings, i.e. gathering GuiInput data from the system (web browser, Mac Window, iPhone App, ...) |
||||
|
DATA: GuiInput: mouse and keyboard state + window size |
||||
|
DATA: GuiSizes: this is a configuration of the ImLayout system, sets sizes of e.g. a slider. |
||||
|
CODE: ImLayout: Immediate mode layout Gui elements. THIS IS WHAT YOUR APP CODE CALLS! |
||||
|
DATA: GuiPaint: High-level commands to render e.g. a checked box with a hover-effect at a certain position. |
||||
|
DATA: GuiStyle: The colors/shading of the gui. |
||||
|
CODE: GuiPainter: Renders GuiPaint + GuiStyle into DrawCommands |
||||
|
DATA: PaintCommands: low-level commands (e.g. "Draw a rectangle with this color here") |
||||
|
CODE: Painter: paints the the PaintCommands to the screen (HTML canvas, OpenGL, ...) |
||||
|
|
||||
|
This is similar to Dear ImGui but separates the layout from the rendering, and adds another step to the rendering. |
||||
|
|
||||
|
# Implementation |
||||
|
|
||||
|
Input is gathered in TypeScript. |
||||
|
PaintCommands rendered to a HTML canvas. |
||||
|
Everything else is written in Rust, compiled to WASM. |
@ -0,0 +1,33 @@ |
|||||
|
#!/bin/bash |
||||
|
set -eu |
||||
|
|
||||
|
# Pre-requisites: |
||||
|
rustup target add wasm32-unknown-unknown |
||||
|
if ! [[ $(wasm-bindgen --version) ]]; then |
||||
|
cargo install wasm-bindgen-cli |
||||
|
fi |
||||
|
|
||||
|
BUILD=debug |
||||
|
# BUILD=release |
||||
|
|
||||
|
# Clear output from old stuff: |
||||
|
rm -rf docs/*.d.ts |
||||
|
rm -rf docs/*.js |
||||
|
rm -rf docs/*.wasm |
||||
|
|
||||
|
echo "Build rust:" |
||||
|
cargo build --target wasm32-unknown-unknown |
||||
|
|
||||
|
echo "Lint and clean up typescript:" |
||||
|
tslint --fix docs/*.ts |
||||
|
|
||||
|
echo "Compile typescript:" |
||||
|
tsc |
||||
|
|
||||
|
echo "Generate JS bindings for wasm:" |
||||
|
|
||||
|
FOLDER_NAME=${PWD##*/} |
||||
|
TARGET_NAME="$FOLDER_NAME.wasm" |
||||
|
wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \ |
||||
|
--out-dir docs --no-modules |
||||
|
# --no-modules-global hoboho |
@ -0,0 +1,6 @@ |
|||||
|
#!/bin/bash |
||||
|
set -eu |
||||
|
|
||||
|
./build.sh |
||||
|
|
||||
|
open "docs/index.html" |
@ -0,0 +1,11 @@ |
|||||
|
/* tslint:disable */ |
||||
|
export function show_gui(arg0: string): string; |
||||
|
|
||||
|
export class Input { |
||||
|
free(): void; |
||||
|
screen_width: number |
||||
|
screen_height: number |
||||
|
mouse_x: number |
||||
|
mouse_y: number |
||||
|
|
||||
|
} |
@ -0,0 +1,151 @@ |
|||||
|
(function() { |
||||
|
var wasm; |
||||
|
const __exports = {}; |
||||
|
|
||||
|
|
||||
|
let cachedTextEncoder = new TextEncoder('utf-8'); |
||||
|
|
||||
|
let cachegetUint8Memory = null; |
||||
|
function getUint8Memory() { |
||||
|
if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { |
||||
|
cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); |
||||
|
} |
||||
|
return cachegetUint8Memory; |
||||
|
} |
||||
|
|
||||
|
function passStringToWasm(arg) { |
||||
|
|
||||
|
const buf = cachedTextEncoder.encode(arg); |
||||
|
const ptr = wasm.__wbindgen_malloc(buf.length); |
||||
|
getUint8Memory().set(buf, ptr); |
||||
|
return [ptr, buf.length]; |
||||
|
} |
||||
|
|
||||
|
let cachedTextDecoder = new TextDecoder('utf-8'); |
||||
|
|
||||
|
function getStringFromWasm(ptr, len) { |
||||
|
return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); |
||||
|
} |
||||
|
|
||||
|
let cachedGlobalArgumentPtr = null; |
||||
|
function globalArgumentPtr() { |
||||
|
if (cachedGlobalArgumentPtr === null) { |
||||
|
cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr(); |
||||
|
} |
||||
|
return cachedGlobalArgumentPtr; |
||||
|
} |
||||
|
|
||||
|
let cachegetUint32Memory = null; |
||||
|
function getUint32Memory() { |
||||
|
if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { |
||||
|
cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); |
||||
|
} |
||||
|
return cachegetUint32Memory; |
||||
|
} |
||||
|
/** |
||||
|
* @param {string} arg0 |
||||
|
* @returns {string} |
||||
|
*/ |
||||
|
__exports.show_gui = function(arg0) { |
||||
|
const [ptr0, len0] = passStringToWasm(arg0); |
||||
|
const retptr = globalArgumentPtr(); |
||||
|
try { |
||||
|
wasm.show_gui(retptr, ptr0, len0); |
||||
|
const mem = getUint32Memory(); |
||||
|
const rustptr = mem[retptr / 4]; |
||||
|
const rustlen = mem[retptr / 4 + 1]; |
||||
|
|
||||
|
const realRet = getStringFromWasm(rustptr, rustlen).slice(); |
||||
|
wasm.__wbindgen_free(rustptr, rustlen * 1); |
||||
|
return realRet; |
||||
|
|
||||
|
|
||||
|
} finally { |
||||
|
wasm.__wbindgen_free(ptr0, len0 * 1); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
function freeInput(ptr) { |
||||
|
|
||||
|
wasm.__wbg_input_free(ptr); |
||||
|
} |
||||
|
/** |
||||
|
*/ |
||||
|
class Input { |
||||
|
|
||||
|
free() { |
||||
|
const ptr = this.ptr; |
||||
|
this.ptr = 0; |
||||
|
freeInput(ptr); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @returns {number} |
||||
|
*/ |
||||
|
get screen_width() { |
||||
|
return wasm.__wbg_get_input_screen_width(this.ptr); |
||||
|
} |
||||
|
set screen_width(arg0) { |
||||
|
return wasm.__wbg_set_input_screen_width(this.ptr, arg0); |
||||
|
} |
||||
|
/** |
||||
|
* @returns {number} |
||||
|
*/ |
||||
|
get screen_height() { |
||||
|
return wasm.__wbg_get_input_screen_height(this.ptr); |
||||
|
} |
||||
|
set screen_height(arg0) { |
||||
|
return wasm.__wbg_set_input_screen_height(this.ptr, arg0); |
||||
|
} |
||||
|
/** |
||||
|
* @returns {number} |
||||
|
*/ |
||||
|
get mouse_x() { |
||||
|
return wasm.__wbg_get_input_mouse_x(this.ptr); |
||||
|
} |
||||
|
set mouse_x(arg0) { |
||||
|
return wasm.__wbg_set_input_mouse_x(this.ptr, arg0); |
||||
|
} |
||||
|
/** |
||||
|
* @returns {number} |
||||
|
*/ |
||||
|
get mouse_y() { |
||||
|
return wasm.__wbg_get_input_mouse_y(this.ptr); |
||||
|
} |
||||
|
set mouse_y(arg0) { |
||||
|
return wasm.__wbg_set_input_mouse_y(this.ptr, arg0); |
||||
|
} |
||||
|
} |
||||
|
__exports.Input = Input; |
||||
|
|
||||
|
__exports.__wbindgen_throw = function(ptr, len) { |
||||
|
throw new Error(getStringFromWasm(ptr, len)); |
||||
|
}; |
||||
|
|
||||
|
function init(path_or_module) { |
||||
|
let instantiation; |
||||
|
const imports = { './emgui': __exports }; |
||||
|
if (path_or_module instanceof WebAssembly.Module) { |
||||
|
instantiation = WebAssembly.instantiate(path_or_module, imports) |
||||
|
.then(instance => { |
||||
|
return { instance, module: module_or_path } |
||||
|
}); |
||||
|
} else { |
||||
|
const data = fetch(path_or_module); |
||||
|
if (typeof WebAssembly.instantiateStreaming === 'function') { |
||||
|
instantiation = WebAssembly.instantiateStreaming(data, imports); |
||||
|
} else { |
||||
|
instantiation = data |
||||
|
.then(response => response.arrayBuffer()) |
||||
|
.then(buffer => WebAssembly.instantiate(buffer, imports)); |
||||
|
} |
||||
|
} |
||||
|
return instantiation.then(({instance}) => { |
||||
|
wasm = init.wasm = instance.exports; |
||||
|
return; |
||||
|
}); |
||||
|
}; |
||||
|
self.wasm_bindgen = Object.assign(init, __exports); |
||||
|
})(); |
Binary file not shown.
@ -0,0 +1,112 @@ |
|||||
|
// ----------------------------------------------------------------------------
|
||||
|
// Paint module:
|
||||
|
function paintCommand(canvas, cmd) { |
||||
|
var ctx = canvas.getContext("2d"); |
||||
|
switch (cmd.kind) { |
||||
|
case "clear": |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
|
return; |
||||
|
case "line": |
||||
|
ctx.beginPath(); |
||||
|
ctx.lineWidth = cmd.line_width; |
||||
|
ctx.strokeStyle = cmd.stroke_style; |
||||
|
ctx.moveTo(cmd.from[0], cmd.from[1]); |
||||
|
ctx.lineTo(cmd.to[0], cmd.to[1]); |
||||
|
ctx.stroke(); |
||||
|
return; |
||||
|
case "circle": |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
ctx.beginPath(); |
||||
|
ctx.arc(cmd.center[0], cmd.center[1], cmd.radius, 0, 2 * Math.PI, false); |
||||
|
ctx.fill(); |
||||
|
return; |
||||
|
case "rounded_rect": |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
var x = cmd.pos[0]; |
||||
|
var y = cmd.pos[1]; |
||||
|
var width = cmd.size[0]; |
||||
|
var height = cmd.size[1]; |
||||
|
var radius = cmd.radius; |
||||
|
ctx.beginPath(); |
||||
|
ctx.moveTo(x + radius, y); |
||||
|
ctx.lineTo(x + width - radius, y); |
||||
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius); |
||||
|
ctx.lineTo(x + width, y + height - radius); |
||||
|
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); |
||||
|
ctx.lineTo(x + radius, y + height); |
||||
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius); |
||||
|
ctx.lineTo(x, y + radius); |
||||
|
ctx.quadraticCurveTo(x, y, x + radius, y); |
||||
|
ctx.closePath(); |
||||
|
ctx.fill(); |
||||
|
return; |
||||
|
case "text": |
||||
|
ctx.font = cmd.font; |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
ctx.textAlign = cmd.text_align; |
||||
|
ctx.fillText(cmd.text, cmd.pos[0], cmd.pos[1]); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
// we'll defer our execution until the wasm is ready to go
|
||||
|
function wasm_loaded() { |
||||
|
console.log("wasm loaded"); |
||||
|
initialize(); |
||||
|
} |
||||
|
// 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("./emgui_bg.wasm") |
||||
|
.then(wasm_loaded)["catch"](console.error); |
||||
|
function rust_gui(input) { |
||||
|
return JSON.parse(wasm_bindgen.show_gui(JSON.stringify(input))); |
||||
|
} |
||||
|
// ----------------------------------------------------------------------------
|
||||
|
function js_gui(input) { |
||||
|
var commands = []; |
||||
|
commands.push({ |
||||
|
fillStyle: "#111111", |
||||
|
kind: "clear" |
||||
|
}); |
||||
|
commands.push({ |
||||
|
fillStyle: "#ff1111", |
||||
|
kind: "rounded_rect", |
||||
|
pos: [100, 100], |
||||
|
radius: 20, |
||||
|
size: [200, 100] |
||||
|
}); |
||||
|
return commands; |
||||
|
} |
||||
|
function paint_gui(canvas, mouse_pos) { |
||||
|
var input = { |
||||
|
mouse_x: mouse_pos.x, |
||||
|
mouse_y: mouse_pos.y, |
||||
|
screen_height: canvas.height, |
||||
|
screen_width: canvas.width |
||||
|
}; |
||||
|
var commands = rust_gui(input); |
||||
|
for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) { |
||||
|
var cmd = commands_1[_i]; |
||||
|
paintCommand(canvas, cmd); |
||||
|
} |
||||
|
} |
||||
|
// ----------------------------------------------------------------------------
|
||||
|
function mouse_pos_from_event(canvas, evt) { |
||||
|
var rect = canvas.getBoundingClientRect(); |
||||
|
return { |
||||
|
x: evt.clientX - rect.left, |
||||
|
y: evt.clientY - rect.top |
||||
|
}; |
||||
|
} |
||||
|
function initialize() { |
||||
|
var canvas = document.getElementById("canvas"); |
||||
|
canvas.addEventListener("mousemove", function (evt) { |
||||
|
var mouse_pos = mouse_pos_from_event(canvas, evt); |
||||
|
paint_gui(canvas, mouse_pos); |
||||
|
}, false); |
||||
|
canvas.addEventListener("mousedown", function (evt) { |
||||
|
var mouse_pos = mouse_pos_from_event(canvas, evt); |
||||
|
paint_gui(canvas, mouse_pos); |
||||
|
}, false); |
||||
|
paint_gui(canvas, { x: 0, y: 0 }); |
||||
|
} |
@ -0,0 +1,206 @@ |
|||||
|
// ----------------------------------------------------------------------------
|
||||
|
// Paint module:
|
||||
|
|
||||
|
interface Clear { |
||||
|
kind: "clear"; |
||||
|
fill_style: string; |
||||
|
} |
||||
|
|
||||
|
interface Line { |
||||
|
kind: "line"; |
||||
|
from: [number, number]; |
||||
|
line_width: number; |
||||
|
stroke_style: string; |
||||
|
to: [number, number]; |
||||
|
} |
||||
|
|
||||
|
interface Circle { |
||||
|
kind: "circle"; |
||||
|
center: [number, number]; |
||||
|
fill_style: string; |
||||
|
radius: number; |
||||
|
} |
||||
|
|
||||
|
interface RoundedRect { |
||||
|
kind: "rounded_rect"; |
||||
|
fill_style: string; |
||||
|
pos: [number, number]; |
||||
|
radius: number; |
||||
|
size: [number, number]; |
||||
|
} |
||||
|
|
||||
|
interface Text { |
||||
|
kind: "text"; |
||||
|
fill_style: string; |
||||
|
font: string; |
||||
|
pos: [number, number]; |
||||
|
text: string; |
||||
|
text_align: "start" | "center" | "end"; |
||||
|
} |
||||
|
|
||||
|
type PaintCmd = Clear | Line | Circle | RoundedRect | Text; |
||||
|
|
||||
|
function paintCommand(canvas, cmd: PaintCmd) { |
||||
|
const ctx = canvas.getContext("2d"); |
||||
|
|
||||
|
switch (cmd.kind) { |
||||
|
case "clear": |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
|
return; |
||||
|
|
||||
|
case "line": |
||||
|
ctx.beginPath(); |
||||
|
ctx.lineWidth = cmd.line_width; |
||||
|
ctx.strokeStyle = cmd.stroke_style; |
||||
|
ctx.moveTo(cmd.from[0], cmd.from[1]); |
||||
|
ctx.lineTo(cmd.to[0], cmd.to[1]); |
||||
|
ctx.stroke(); |
||||
|
return; |
||||
|
|
||||
|
case "circle": |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
ctx.beginPath(); |
||||
|
ctx.arc(cmd.center[0], cmd.center[1], cmd.radius, 0, 2 * Math.PI, false); |
||||
|
ctx.fill(); |
||||
|
return; |
||||
|
|
||||
|
case "rounded_rect": |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
const x = cmd.pos[0]; |
||||
|
const y = cmd.pos[1]; |
||||
|
const width = cmd.size[0]; |
||||
|
const height = cmd.size[1]; |
||||
|
const radius = cmd.radius; |
||||
|
ctx.beginPath(); |
||||
|
ctx.moveTo(x + radius, y); |
||||
|
ctx.lineTo(x + width - radius, y); |
||||
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius); |
||||
|
ctx.lineTo(x + width, y + height - radius); |
||||
|
ctx.quadraticCurveTo( |
||||
|
x + width, |
||||
|
y + height, |
||||
|
x + width - radius, |
||||
|
y + height, |
||||
|
); |
||||
|
ctx.lineTo(x + radius, y + height); |
||||
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius); |
||||
|
ctx.lineTo(x, y + radius); |
||||
|
ctx.quadraticCurveTo(x, y, x + radius, y); |
||||
|
ctx.closePath(); |
||||
|
ctx.fill(); |
||||
|
return; |
||||
|
|
||||
|
case "text": |
||||
|
ctx.font = cmd.font; |
||||
|
ctx.fillStyle = cmd.fill_style; |
||||
|
ctx.textAlign = cmd.text_align; |
||||
|
ctx.fillText(cmd.text, cmd.pos[0], cmd.pos[1]); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ----------------------------------------------------------------------------
|
||||
|
|
||||
|
interface Coord { |
||||
|
x: number; |
||||
|
y: number; |
||||
|
} |
||||
|
|
||||
|
interface Input { |
||||
|
mouse_x: number; |
||||
|
mouse_y: number; |
||||
|
screen_height: number; |
||||
|
screen_width: number; |
||||
|
// TODO: mouse down etc
|
||||
|
} |
||||
|
|
||||
|
// ----------------------------------------------------------------------------
|
||||
|
|
||||
|
// the `wasm_bindgen` global is set to the exports of the Rust module. Override with wasm-bindgen --no-modules-global
|
||||
|
declare var wasm_bindgen: any; |
||||
|
|
||||
|
// we'll defer our execution until the wasm is ready to go
|
||||
|
function wasm_loaded() { |
||||
|
console.log(`wasm loaded`); |
||||
|
initialize(); |
||||
|
} |
||||
|
|
||||
|
// 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("./emgui_bg.wasm") |
||||
|
.then(wasm_loaded) |
||||
|
.catch(console.error); |
||||
|
|
||||
|
function rust_gui(input: Input): PaintCmd[] { |
||||
|
return JSON.parse(wasm_bindgen.show_gui(JSON.stringify(input))); |
||||
|
} |
||||
|
|
||||
|
// ----------------------------------------------------------------------------
|
||||
|
|
||||
|
function js_gui(input: Input): PaintCmd[] { |
||||
|
const commands = []; |
||||
|
|
||||
|
commands.push({ |
||||
|
fillStyle: "#111111", |
||||
|
kind: "clear", |
||||
|
}); |
||||
|
|
||||
|
commands.push({ |
||||
|
fillStyle: "#ff1111", |
||||
|
kind: "rounded_rect", |
||||
|
pos: [100, 100], |
||||
|
radius: 20, |
||||
|
size: [200, 100], |
||||
|
}); |
||||
|
|
||||
|
return commands; |
||||
|
} |
||||
|
|
||||
|
function paint_gui(canvas, mouse_pos) { |
||||
|
const input = { |
||||
|
mouse_x: mouse_pos.x, |
||||
|
mouse_y: mouse_pos.y, |
||||
|
screen_height: canvas.height, |
||||
|
screen_width: canvas.width, |
||||
|
}; |
||||
|
const commands = rust_gui(input); |
||||
|
|
||||
|
for (const cmd of commands) { |
||||
|
paintCommand(canvas, cmd); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ----------------------------------------------------------------------------
|
||||
|
|
||||
|
function mouse_pos_from_event(canvas, evt): Coord { |
||||
|
const rect = canvas.getBoundingClientRect(); |
||||
|
return { |
||||
|
x: evt.clientX - rect.left, |
||||
|
y: evt.clientY - rect.top, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
function initialize() { |
||||
|
const canvas = document.getElementById("canvas"); |
||||
|
|
||||
|
canvas.addEventListener( |
||||
|
"mousemove", |
||||
|
(evt) => { |
||||
|
const mouse_pos = mouse_pos_from_event(canvas, evt); |
||||
|
paint_gui(canvas, mouse_pos); |
||||
|
}, |
||||
|
false, |
||||
|
); |
||||
|
|
||||
|
canvas.addEventListener( |
||||
|
"mousedown", |
||||
|
(evt) => { |
||||
|
const mouse_pos = mouse_pos_from_event(canvas, evt); |
||||
|
paint_gui(canvas, mouse_pos); |
||||
|
}, |
||||
|
false, |
||||
|
); |
||||
|
|
||||
|
paint_gui(canvas, { x: 0, y: 0 }); |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
||||
|
<head> |
||||
|
<title>Gui Experiment</title> |
||||
|
|
||||
|
<style> |
||||
|
html { |
||||
|
/* Remove touch delay: */ |
||||
|
touch-action: manipulation; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
background: #111111; |
||||
|
color: #bbbbbb; |
||||
|
max-width: 480px; |
||||
|
margin: auto; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<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="emgui.js"></script> |
||||
|
|
||||
|
<script src="frontend.js" type="module"></script> |
||||
|
|
||||
|
<!-- TODO: make this cover the entire screen, with resize and all --> |
||||
|
<canvas id="canvas" width="480" height="800"></canvas> |
||||
|
</body> |
||||
|
</html> |
||||
|
|
@ -0,0 +1,70 @@ |
|||||
|
extern crate serde; |
||||
|
extern crate serde_json; |
||||
|
extern crate wasm_bindgen; |
||||
|
extern crate web_sys; |
||||
|
#[macro_use] |
||||
|
extern crate serde_derive; |
||||
|
|
||||
|
use wasm_bindgen::prelude::*; |
||||
|
|
||||
|
#[wasm_bindgen] |
||||
|
#[derive(Deserialize)] |
||||
|
pub struct Input { |
||||
|
pub screen_width: f32, |
||||
|
pub screen_height: f32, |
||||
|
pub mouse_x: f32, |
||||
|
pub mouse_y: f32, |
||||
|
} |
||||
|
|
||||
|
#[derive(Serialize)] |
||||
|
#[serde(rename_all = "snake_case")] |
||||
|
enum TextAlign { |
||||
|
Start, |
||||
|
Center, |
||||
|
End, |
||||
|
} |
||||
|
|
||||
|
#[derive(Serialize)] |
||||
|
#[serde(rename_all = "snake_case", tag = "kind")] |
||||
|
enum PaintCmd { |
||||
|
Clear { |
||||
|
fill_style: String, |
||||
|
}, |
||||
|
RoundedRect { |
||||
|
fill_style: String, |
||||
|
pos: [f32; 2], |
||||
|
size: [f32; 2], |
||||
|
radius: f32, |
||||
|
}, |
||||
|
Text { |
||||
|
fill_style: String, |
||||
|
font: String, |
||||
|
pos: [f32; 2], |
||||
|
text: String, |
||||
|
text_align: TextAlign, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
#[wasm_bindgen] |
||||
|
pub fn show_gui(input_json: &str) -> String { |
||||
|
let input: Input = serde_json::from_str(input_json).unwrap(); |
||||
|
let commands = [ |
||||
|
PaintCmd::Clear { |
||||
|
fill_style: "#44444400".to_string(), |
||||
|
}, |
||||
|
PaintCmd::RoundedRect { |
||||
|
fill_style: "#1111ff".to_string(), |
||||
|
pos: [100.0, 100.0], |
||||
|
radius: 40.0, |
||||
|
size: [200.0, 200.0], |
||||
|
}, |
||||
|
PaintCmd::Text { |
||||
|
fill_style: "#11ff00".to_string(), |
||||
|
font: "14px Palatino".to_string(), |
||||
|
pos: [200.0, 32.0], |
||||
|
text: format!("Mouse pos: {} {}", input.mouse_x, input.mouse_y), |
||||
|
text_align: TextAlign::Center, |
||||
|
}, |
||||
|
]; |
||||
|
serde_json::to_string(&commands).unwrap() |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"module": "es2015", |
||||
|
}, |
||||
|
"include": [ |
||||
|
"docs/**/*" |
||||
|
] |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"defaultSeverity": "error", |
||||
|
"extends": [ |
||||
|
"tslint:recommended" |
||||
|
], |
||||
|
"jsRules": {}, |
||||
|
"rules": { |
||||
|
"interface-name": [true, "never-prefix"], |
||||
|
"max-classes-per-file": [false], |
||||
|
"no-bitwise": false, |
||||
|
"no-console": false, |
||||
|
"variable-name": false |
||||
|
}, |
||||
|
"rulesDirectory": [] |
||||
|
} |
Loading…
Reference in new issue