Add proper button function

This commit is contained in:
Emil Ernerfeldt 2018-12-26 10:46:23 +01:00
parent 6755a90734
commit db30e552d2
8 changed files with 269 additions and 155 deletions

Binary file not shown.

View file

@ -76,11 +76,7 @@ function js_gui(input) {
}); });
return commands; return commands;
} }
function paint_gui(canvas, mouse_pos) { function paint_gui(canvas, input) {
var input = {
mouse_pos: mouse_pos,
screen_size: { x: canvas.width, y: canvas.height }
};
var commands = rust_gui(input); var commands = rust_gui(input);
for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) { for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) {
var cmd = commands_1[_i]; var cmd = commands_1[_i];
@ -88,6 +84,15 @@ function paint_gui(canvas, mouse_pos) {
} }
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
var g_mouse_pos = { x: -1000.0, y: -1000.0 };
var g_mouse_down = false;
function get_input(canvas) {
return {
mouse_down: g_mouse_down,
mouse_pos: g_mouse_pos,
screen_size: { x: canvas.width, y: canvas.height }
};
}
function mouse_pos_from_event(canvas, evt) { function mouse_pos_from_event(canvas, evt) {
var rect = canvas.getBoundingClientRect(); var rect = canvas.getBoundingClientRect();
return { return {
@ -98,12 +103,22 @@ function mouse_pos_from_event(canvas, evt) {
function initialize() { function initialize() {
var canvas = document.getElementById("canvas"); var canvas = document.getElementById("canvas");
canvas.addEventListener("mousemove", function (evt) { canvas.addEventListener("mousemove", function (evt) {
var mouse_pos = mouse_pos_from_event(canvas, evt); g_mouse_pos = mouse_pos_from_event(canvas, evt);
paint_gui(canvas, mouse_pos); paint_gui(canvas, get_input(canvas));
}, false);
canvas.addEventListener("mouseleave", function (evt) {
g_mouse_pos = { x: -1000.0, y: -1000.0 };
paint_gui(canvas, get_input(canvas));
}, false); }, false);
canvas.addEventListener("mousedown", function (evt) { canvas.addEventListener("mousedown", function (evt) {
var mouse_pos = mouse_pos_from_event(canvas, evt); g_mouse_pos = mouse_pos_from_event(canvas, evt);
paint_gui(canvas, mouse_pos); g_mouse_down = true;
paint_gui(canvas, get_input(canvas));
}, false); }, false);
paint_gui(canvas, { x: 0, y: 0 }); canvas.addEventListener("mouseup", function (evt) {
g_mouse_pos = mouse_pos_from_event(canvas, evt);
g_mouse_down = false;
paint_gui(canvas, get_input(canvas));
}, false);
paint_gui(canvas, get_input(canvas));
} }

View file

@ -104,10 +104,16 @@ function paintCommand(canvas, cmd: PaintCmd) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
interface Input { /// What the integration gives to the gui.
interface RawInput {
/// Is the button currently down?
mouse_down: boolean;
/// Current position of the mouse in points.
mouse_pos: Vec2; mouse_pos: Vec2;
/// Size of the screen in points.
screen_size: Vec2; screen_size: Vec2;
// TODO: mouse down etc
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -127,13 +133,13 @@ wasm_bindgen("./emgui_bg.wasm")
.then(wasm_loaded) .then(wasm_loaded)
.catch(console.error); .catch(console.error);
function rust_gui(input: Input): PaintCmd[] { function rust_gui(input: RawInput): PaintCmd[] {
return JSON.parse(wasm_bindgen.show_gui(JSON.stringify(input))); return JSON.parse(wasm_bindgen.show_gui(JSON.stringify(input)));
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
function js_gui(input: Input): PaintCmd[] { function js_gui(input: RawInput): PaintCmd[] {
const commands = []; const commands = [];
commands.push({ commands.push({
@ -152,11 +158,7 @@ function js_gui(input: Input): PaintCmd[] {
return commands; return commands;
} }
function paint_gui(canvas, mouse_pos) { function paint_gui(canvas, input: RawInput) {
const input = {
mouse_pos,
screen_size: { x: canvas.width, y: canvas.height },
};
const commands = rust_gui(input); const commands = rust_gui(input);
for (const cmd of commands) { for (const cmd of commands) {
@ -166,6 +168,17 @@ function paint_gui(canvas, mouse_pos) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
let g_mouse_pos = { x: -1000.0, y: -1000.0 };
let g_mouse_down = false;
function get_input(canvas): RawInput {
return {
mouse_down: g_mouse_down,
mouse_pos: g_mouse_pos,
screen_size: { x: canvas.width, y: canvas.height },
};
}
function mouse_pos_from_event(canvas, evt): Vec2 { function mouse_pos_from_event(canvas, evt): Vec2 {
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
return { return {
@ -180,8 +193,17 @@ function initialize() {
canvas.addEventListener( canvas.addEventListener(
"mousemove", "mousemove",
(evt) => { (evt) => {
const mouse_pos = mouse_pos_from_event(canvas, evt); g_mouse_pos = mouse_pos_from_event(canvas, evt);
paint_gui(canvas, mouse_pos); paint_gui(canvas, get_input(canvas));
},
false,
);
canvas.addEventListener(
"mouseleave",
(evt) => {
g_mouse_pos = { x: -1000.0, y: -1000.0 };
paint_gui(canvas, get_input(canvas));
}, },
false, false,
); );
@ -189,11 +211,22 @@ function initialize() {
canvas.addEventListener( canvas.addEventListener(
"mousedown", "mousedown",
(evt) => { (evt) => {
const mouse_pos = mouse_pos_from_event(canvas, evt); g_mouse_pos = mouse_pos_from_event(canvas, evt);
paint_gui(canvas, mouse_pos); g_mouse_down = true;
paint_gui(canvas, get_input(canvas));
}, },
false, false,
); );
paint_gui(canvas, { x: 0, y: 0 }); canvas.addEventListener(
"mouseup",
(evt) => {
g_mouse_pos = mouse_pos_from_event(canvas, evt);
g_mouse_down = false;
paint_gui(canvas, get_input(canvas));
},
false,
);
paint_gui(canvas, get_input(canvas));
} }

View file

@ -41,4 +41,3 @@
<canvas id="canvas" width="1024" height="768"></canvas> <canvas id="canvas" width="1024" height="768"></canvas>
</body> </body>
</html> </html>

28
src/app.rs Normal file
View file

@ -0,0 +1,28 @@
use gui::Gui;
pub struct App {
count: i32,
}
impl App {
pub fn new() -> Self {
App { count: 0 }
}
pub fn show_gui(&mut self, gui: &mut Gui) {
if gui.button("Click me").clicked {
self.count += 1;
}
if gui.button("Or click me instead!").clicked {
self.count += 1;
}
gui.label(format!(
"The buttons have been clicked {} times",
self.count
));
let commands_json = serde_json::to_string_pretty(&gui.paint_commands()).unwrap();
gui.label(format!("All paint commands:\n{}", commands_json));
}
}

100
src/gui.rs Normal file
View file

@ -0,0 +1,100 @@
use types::*;
#[derive(Default)]
pub struct InteractInfo {
pub hovering: bool,
pub clicked: bool,
}
// TODO: implement Gui on this so we can add children to a widget
// pub struct Widget {}
pub struct Gui {
commands: Vec<PaintCmd>,
input: GuiInput,
cursor: Vec2,
}
impl Gui {
pub fn new(input: GuiInput) -> Self {
Gui {
commands: vec![PaintCmd::Clear {
fill_style: "#44444400".to_string(),
}],
input,
cursor: Vec2 { x: 32.0, y: 32.0 },
}
}
pub fn input(&self) -> &GuiInput {
&self.input
}
pub fn into_commands(self) -> Vec<PaintCmd> {
self.commands
}
pub fn paint_commands(&self) -> &[PaintCmd] {
&self.commands
}
fn rect(&mut self, rect: Rect, fill_style: String) -> InteractInfo {
let hovering = rect.contains(self.input.mouse_pos);
let clicked = hovering && self.input.mouse_clicked;
let ii = InteractInfo { hovering, clicked };
self.commands.push(PaintCmd::RoundedRect {
fill_style,
pos: rect.pos,
corner_radius: 5.0,
size: rect.size,
});
ii
}
fn text<S: Into<String>>(&mut self, pos: Vec2, text: S) {
self.commands.push(PaintCmd::Text {
fill_style: "#ffffffbb".to_string(),
font: "14px Palatino".to_string(),
pos,
text: text.into(),
text_align: TextAlign::Start,
});
}
// ------------------------------------------------------------------------
pub fn button<S: Into<String>>(&mut self, text: S) -> InteractInfo {
let rect = Rect {
pos: self.cursor,
size: Vec2 { x: 200.0, y: 32.0 }, // TODO: get from some settings
};
let hovering = rect.contains(self.input.mouse_pos);
let fill_style = if hovering {
"#444444ff".to_string()
} else {
"#222222ff".to_string()
};
let ii = self.rect(rect, fill_style);
self.text(
Vec2 {
x: rect.pos.x + 4.0,
y: rect.center().y + 14.0 / 2.0,
},
text,
);
self.cursor.y += rect.size.y + 16.0;
ii
}
pub fn label<S: Into<String>>(&mut self, text: S) {
for line in text.into().split("\n") {
let pos = self.cursor;
self.text(pos, line);
self.cursor.y += 16.0;
}
self.cursor.y += 16.0; // Padding
}
// ------------------------------------------------------------------------
}

View file

@ -11,10 +11,12 @@ use std::sync::Mutex;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
mod types;
use types::*; use types::*;
pub mod app;
pub mod gui;
pub mod types;
/* /*
// Fast compilation, slow code: // Fast compilation, slow code:
@ -30,133 +32,21 @@ fn foo<T: Trait>(x: &dyn T);
fn foo(x: &Trait); fn foo(x: &Trait);
*/ */
#[derive(Default)]
pub struct InteractInfo {
pub is_hovering: bool,
}
// TODO: implement Gui on this so we can add children to a widget
// pub struct Widget {}
pub struct Gui {
commands: Vec<PaintCmd>,
input: Input,
}
impl Gui {
pub fn new(input: Input) -> Self {
Gui {
commands: vec![PaintCmd::Clear {
fill_style: "#44444400".to_string(),
}],
input,
}
}
pub fn input(&self) -> &Input {
&self.input
}
pub fn into_commands(self) -> Vec<PaintCmd> {
self.commands
}
pub fn rect(&mut self, rect: Rect) -> InteractInfo {
let ii = InteractInfo {
is_hovering: rect.contains(&self.input.mouse_pos),
};
self.commands.push(PaintCmd::RoundedRect {
fill_style: "#ffffff10".to_string(),
pos: rect.pos,
corner_radius: 40.0,
size: rect.size,
});
ii
}
pub fn text(&mut self, pos: Vec2, text: String) {
self.commands.push(PaintCmd::Text {
fill_style: "#11ff00".to_string(),
font: "14px Palatino".to_string(),
pos,
text,
text_align: TextAlign::Start,
});
}
}
struct App {
count: i32,
}
impl App {
fn new() -> Self {
App { count: 0 }
}
fn show_gui(&mut self, gui: &mut Gui, input: &Input) {
gui.rect(Rect {
pos: Vec2 { x: 0.0, y: 0.0 },
size: input.screen_size,
});
gui.rect(Rect {
pos: Vec2 { x: 50.0, y: 50.0 },
size: Vec2 {
x: (input.screen_size.x - 100.0) / 3.0,
y: (input.screen_size.y - 100.0),
},
});
let is_hovering = gui
.rect(Rect {
pos: Vec2 { x: 100.0, y: 100.0 },
size: Vec2 { x: 200.0, y: 200.0 },
}).is_hovering;
if is_hovering {
self.count += 1;
}
gui.text(
Vec2 { x: 100.0, y: 350.0 },
format!(
"Mouse pos: {} {}, is_hovering: {}",
input.mouse_pos.x, input.mouse_pos.y, is_hovering
),
);
let m = input.mouse_pos;
let hw = 32.0;
gui.rect(Rect {
pos: Vec2 {
x: m.x - hw,
y: m.y - hw,
},
size: Vec2 {
x: 2.0 * hw,
y: 2.0 * hw,
},
});
gui.text(
Vec2 { x: 100.0, y: 400.0 },
format!("Count: {}", self.count),
);
}
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn show_gui(input_json: &str) -> String { pub fn show_gui(raw_input_json: &str) -> String {
// TODO: faster interface than JSON
let raw_input: RawInput = serde_json::from_str(raw_input_json).unwrap();
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref APP: Mutex<App> = Mutex::new(App::new()); static ref APP: Mutex<app::App> = Mutex::new(app::App::new());
static ref LAST_INPUT: Mutex<RawInput> = Default::default();
} }
// TODO: faster interface than JSON let gui_input = GuiInput::from_last_and_new(&LAST_INPUT.lock().unwrap(), &raw_input);
let input: Input = serde_json::from_str(input_json).unwrap(); *LAST_INPUT.lock().unwrap() = raw_input;
let mut gui = Gui::new(input); let mut gui = gui::Gui::new(gui_input);
APP.lock().unwrap().show_gui(&mut gui, &input); APP.lock().unwrap().show_gui(&mut gui);
let commands = gui.into_commands(); let commands = gui.into_commands();
serde_json::to_string(&commands).unwrap() serde_json::to_string(&commands).unwrap()
} }

View file

@ -1,30 +1,79 @@
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
pub struct Vec2 { pub struct Vec2 {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
} }
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
pub struct Rect { pub struct Rect {
pub pos: Vec2, pub pos: Vec2,
pub size: Vec2, pub size: Vec2,
} }
impl Rect { impl Rect {
pub fn contains(&self, p: &Vec2) -> bool { pub fn contains(&self, p: Vec2) -> bool {
self.pos.x <= p.x self.pos.x <= p.x
&& p.x <= self.pos.x + self.size.x && p.x <= self.pos.x + self.size.x
&& self.pos.y <= p.y && self.pos.y <= p.y
&& p.y <= self.pos.y + self.size.y && p.y <= self.pos.y + self.size.y
} }
pub fn center(&self) -> Vec2 {
Vec2 {
x: self.pos.x + self.size.x / 2.0,
y: self.pos.y + self.size.y / 2.0,
}
}
} }
#[derive(Clone, Copy, Debug, Deserialize)] // ----------------------------------------------------------------------------
pub struct Input {
pub screen_size: Vec2, /// What the integration gives to the gui.
#[derive(Clone, Copy, Debug, Default, Deserialize)]
pub struct RawInput {
/// Is the button currently down?
pub mouse_down: bool,
/// Current position of the mouse in points.
pub mouse_pos: Vec2, pub mouse_pos: Vec2,
/// Size of the screen in points.
pub screen_size: Vec2,
} }
/// What the gui maintains
#[derive(Clone, Copy, Debug)]
pub struct GuiInput {
/// Is the button currently down?
pub mouse_down: bool,
/// The mouse went from !down to down
pub mouse_clicked: bool,
/// The mouse went from down to !down
pub mouse_released: bool,
/// Current position of the mouse in points.
pub mouse_pos: Vec2,
/// Size of the screen in points.
pub screen_size: Vec2,
}
impl GuiInput {
pub fn from_last_and_new(last: &RawInput, new: &RawInput) -> GuiInput {
GuiInput {
mouse_down: new.mouse_down,
mouse_clicked: !last.mouse_down && new.mouse_down,
mouse_released: last.mouse_down && !new.mouse_down,
mouse_pos: new.mouse_pos,
screen_size: new.screen_size,
}
}
}
// ----------------------------------------------------------------------------
#[derive(Clone, Copy, Debug, Serialize)] #[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum TextAlign { pub enum TextAlign {