From 56da7f40e89731b413eb7eee2102fc87dbe433e2 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 6 Jan 2019 16:34:01 +0100 Subject: [PATCH] Major refactor of layout code --- emgui/src/emgui.rs | 34 ++++- emgui/src/layout.rs | 317 +++++++++++++++++++++------------------- emgui/src/lib.rs | 2 +- emgui_wasm/Cargo.toml | 2 +- emgui_wasm/src/app.rs | 12 +- emgui_wasm/src/lib.rs | 96 +++++++----- emgui_wasm/src/webgl.rs | 3 +- 7 files changed, 267 insertions(+), 199 deletions(-) diff --git a/emgui/src/emgui.rs b/emgui/src/emgui.rs index aa71b94c4..fc064d0a5 100644 --- a/emgui/src/emgui.rs +++ b/emgui/src/emgui.rs @@ -1,29 +1,51 @@ -use crate::{font::Font, layout, style, types::*}; +use crate::{font::Font, layout, style, types::GuiInput, Frame, Painter, RawInput}; /// Encapsulates input, layout and painting for ease of use. #[derive(Clone)] pub struct Emgui { pub last_input: RawInput, - pub layout: layout::Layout, + pub data: layout::Data, pub style: style::Style, + pub painter: Painter, } impl Emgui { pub fn new(font: Font) -> Emgui { Emgui { last_input: Default::default(), - layout: layout::Layout::new(font), + data: layout::Data::new(font.clone()), style: Default::default(), + painter: Painter::new(font), } } + pub fn texture(&self) -> (u16, u16, &[u8]) { + self.painter.texture() + } + pub fn new_frame(&mut self, new_input: RawInput) { let gui_input = GuiInput::from_last_and_new(&self.last_input, &new_input); self.last_input = new_input; - self.layout.new_frame(gui_input); + self.data.new_frame(gui_input); + } + + pub fn whole_screen_region(&mut self) -> layout::Region { + layout::Region { + data: &mut self.data, + id: Default::default(), + dir: layout::Direction::Vertical, + cursor: Default::default(), + size: Default::default(), + } + } + + pub fn set_options(&mut self, options: layout::LayoutOptions) { + self.data.options = options; } - pub fn paint(&mut self) -> Vec { - style::into_paint_commands(self.layout.gui_commands(), &self.style) + pub fn paint(&mut self) -> Frame { + let gui_commands = self.data.gui_commands(); + let paint_commands = style::into_paint_commands(gui_commands, &self.style); + self.painter.paint(&paint_commands) } } diff --git a/emgui/src/layout.rs b/emgui/src/layout.rs index 8db34d7f9..06ea53c30 100644 --- a/emgui/src/layout.rs +++ b/emgui/src/layout.rs @@ -52,24 +52,25 @@ pub struct GuiResponse<'a> { /// The mouse is interacting with this thing (e.g. dragging it) pub active: bool, - layout: &'a mut Layout, + /// Used for showing a popup (if any) + data: &'a mut Data, } impl<'a> GuiResponse<'a> { /// Show some stuff if the item was hovered - pub fn tooltip(self, add_contents: F) -> Self + pub fn tooltip(&mut self, add_contents: F) -> &mut Self where - F: FnOnce(&mut Layout), + F: FnOnce(&mut Region), { if self.hovered { - let window_pos = self.layout.input.mouse_pos + vec2(16.0, 16.0); - self.layout.show_popup(window_pos, add_contents); + let window_pos = self.data.input().mouse_pos + vec2(16.0, 16.0); + self.data.show_popup(window_pos, add_contents); } self } /// Show this text if the item was hovered - pub fn tooltip_text>(self, text: S) -> Self { + pub fn tooltip_text>(&mut self, text: S) -> &mut Self { self.tooltip(|popup| { popup.label(text); }) @@ -79,7 +80,7 @@ impl<'a> GuiResponse<'a> { // ---------------------------------------------------------------------------- #[derive(Clone, Debug, Default)] -struct Memory { +pub struct Memory { /// The widget being interacted with (e.g. dragged, in case of a slider). active_id: Option, @@ -102,7 +103,7 @@ type TextFragments = Vec; // ---------------------------------------------------------------------------- #[derive(Clone, Copy, Debug, PartialEq)] -enum Direction { +pub enum Direction { Horizontal, Vertical, } @@ -115,59 +116,27 @@ impl Default for Direction { // ---------------------------------------------------------------------------- -// TODO: give this a better name -#[derive(Clone, Debug, Default)] -struct Layouter { - /// Doesn't change. - dir: Direction, - - /// Changes only along self.dir - cursor: Vec2, - - /// We keep track of our max-size along the orthogonal to self.dir - size: Vec2, -} - -impl Layouter { - /// Reserve this much space and move the cursor. - fn reserve_space(&mut self, size: Vec2) { - if self.dir == Direction::Horizontal { - self.cursor.x += size.x; - self.size.x += size.x; - self.size.y = self.size.y.max(size.y); - } else { - self.cursor.y += size.y; - self.size.y += size.y; - self.size.x = self.size.x.max(size.x); - } - } -} - -// ---------------------------------------------------------------------------- - type Id = u64; +// TODO: give a better name. +/// Contains the input, options and output of all GUI commands. #[derive(Clone)] -pub struct Layout { - options: LayoutOptions, - font: Font, // TODO: Arc? - input: GuiInput, - memory: Memory, - id: Id, - layouter: Layouter, - graphics: Vec, - hovering_graphics: Vec, +pub struct Data { + pub(crate) options: LayoutOptions, + pub(crate) font: Font, // TODO: Arc?. TODO: move to options. + pub(crate) input: GuiInput, + pub(crate) memory: Memory, + pub(crate) graphics: Vec, + pub(crate) hovering_graphics: Vec, } -impl Layout { - pub fn new(font: Font) -> Layout { - Layout { +impl Data { + pub fn new(font: Font) -> Data { + Data { options: Default::default(), font, input: Default::default(), memory: Default::default(), - id: Default::default(), - layouter: Default::default(), graphics: Default::default(), hovering_graphics: Default::default(), } @@ -193,23 +162,93 @@ impl Layout { pub fn new_frame(&mut self, gui_input: GuiInput) { self.graphics.clear(); self.hovering_graphics.clear(); - self.layouter = Default::default(); self.input = gui_input; if !gui_input.mouse_down { self.memory.active_id = None; } } - // ------------------------------------------------------------------------ + /// Show a pop-over window + pub fn show_popup(&mut self, window_pos: Vec2, add_contents: F) + where + F: FnOnce(&mut Region), + { + // TODO: nicer way to do layering! + let num_graphics_before = self.graphics.len(); + + let window_padding = self.options.window_padding; + + let mut popup_region = Region { + data: self, + id: Default::default(), + dir: Direction::Vertical, + cursor: window_pos + window_padding, + size: vec2(0.0, 0.0), + }; + + add_contents(&mut popup_region); + + // TODO: handle the last item_spacing in a nicer way + let inner_size = popup_region.size - self.options.item_spacing; + let outer_size = inner_size + 2.0 * window_padding; + + let rect = Rect::from_min_size(window_pos, outer_size); + + let popup_graphics = self.graphics.split_off(num_graphics_before); + self.hovering_graphics.push(GuiCmd::Window { rect }); + self.hovering_graphics.extend(popup_graphics); + } +} + +// ---------------------------------------------------------------------------- + +/// Represents a region of the screen +/// with a type of layout (horizontal or vertical). +pub struct Region<'a> { + pub(crate) data: &'a mut Data, + + // TODO: add min_size and max_size + /// Unique ID of this region. + pub(crate) id: Id, + + /// Doesn't change. + pub(crate) dir: Direction, + + /// Changes only along self.dir + pub(crate) cursor: Vec2, + + /// We keep track of our max-size along the orthogonal to self.dir + pub(crate) size: Vec2, +} + +impl<'a> Region<'a> { + /// It is up to the caller to make sure there is room for this. + /// Can be used for free painting. + /// NOTE: all coordinates are screen coordinates! + pub fn add_graphic(&mut self, gui_cmd: GuiCmd) { + self.data.graphics.push(gui_cmd) + } + + pub fn options(&self) -> &LayoutOptions { + self.data.options() + } + + pub fn set_options(&mut self, options: LayoutOptions) { + self.data.set_options(options) + } + + pub fn input(&self) -> &GuiInput { + self.data.input() + } pub fn button>(&mut self, text: S) -> GuiResponse { let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&text); - let text_cursor = self.layouter.cursor + self.options.button_padding; + let text_cursor = self.cursor + self.options().button_padding; let (rect, interact) = - self.reserve_space(text_size + 2.0 * self.options.button_padding, Some(id)); - self.graphics.push(GuiCmd::Button { interact, rect }); + self.reserve_space(text_size + 2.0 * self.options().button_padding, Some(id)); + self.add_graphic(GuiCmd::Button { interact, rect }); self.add_text(text_cursor, text); self.response(interact) } @@ -218,20 +257,20 @@ impl Layout { let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&text); - let text_cursor = self.layouter.cursor - + self.options.button_padding - + vec2(self.options.start_icon_width, 0.0); + let text_cursor = self.cursor + + self.options().button_padding + + vec2(self.options().start_icon_width, 0.0); let (rect, interact) = self.reserve_space( - self.options.button_padding - + vec2(self.options.start_icon_width, 0.0) + self.options().button_padding + + vec2(self.options().start_icon_width, 0.0) + text_size - + self.options.button_padding, + + self.options().button_padding, Some(id), ); if interact.clicked { *checked = !*checked; } - self.graphics.push(GuiCmd::Checkbox { + self.add_graphic(GuiCmd::Checkbox { checked: *checked, interact, rect, @@ -243,7 +282,7 @@ impl Layout { pub fn label>(&mut self, text: S) -> GuiResponse { let text: String = text.into(); let (text, text_size) = self.layout_text(&text); - self.add_text(self.layouter.cursor, text); + self.add_text(self.cursor, text); let (_, interact) = self.reserve_space(text_size, None); self.response(interact) } @@ -253,17 +292,17 @@ impl Layout { let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&text); - let text_cursor = self.layouter.cursor - + self.options.button_padding - + vec2(self.options.start_icon_width, 0.0); + let text_cursor = self.cursor + + self.options().button_padding + + vec2(self.options().start_icon_width, 0.0); let (rect, interact) = self.reserve_space( - self.options.button_padding - + vec2(self.options.start_icon_width, 0.0) + self.options().button_padding + + vec2(self.options().start_icon_width, 0.0) + text_size - + self.options.button_padding, + + self.options().button_padding, Some(id), ); - self.graphics.push(GuiCmd::RadioButton { + self.add_graphic(GuiCmd::RadioButton { checked, interact, rect, @@ -283,19 +322,19 @@ impl Layout { let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&format!("{}: {:.3}", text, value)); - self.add_text(self.layouter.cursor, text); - self.layouter.reserve_space(text_size); + self.add_text(self.cursor, text); + self.reserve_space_inner(text_size); let (slider_rect, interact) = self.reserve_space( Vec2 { - x: self.options.width, - y: self.font.line_spacing(), + x: self.options().width, + y: self.data.font.line_spacing(), }, Some(id), ); if interact.active { *value = remap_clamp( - self.input.mouse_pos.x, + self.input().mouse_pos.x, slider_rect.min().x, slider_rect.max().x, min, @@ -303,7 +342,7 @@ impl Layout { ); } - self.graphics.push(GuiCmd::Slider { + self.add_graphic(GuiCmd::Slider { interact, max, min, @@ -320,47 +359,51 @@ impl Layout { pub fn foldable(&mut self, text: S, add_contents: F) -> GuiResponse where S: Into, - F: FnOnce(&mut Layout), + F: FnOnce(&mut Region), { assert!( - self.layouter.dir == Direction::Vertical, + self.dir == Direction::Vertical, "Horizontal foldable is unimplemented" ); let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&text); - let text_cursor = self.layouter.cursor + self.options.button_padding; + let text_cursor = self.cursor + self.options().button_padding; let (rect, interact) = self.reserve_space( vec2( - self.options.width, - text_size.y + 2.0 * self.options.button_padding.y, + self.options().width, + text_size.y + 2.0 * self.options().button_padding.y, ), Some(id), ); if interact.clicked { - if self.memory.open_foldables.contains(&id) { - self.memory.open_foldables.remove(&id); + if self.data.memory.open_foldables.contains(&id) { + self.data.memory.open_foldables.remove(&id); } else { - self.memory.open_foldables.insert(id); + self.data.memory.open_foldables.insert(id); } } - let open = self.memory.open_foldables.contains(&id); + let open = self.data.memory.open_foldables.contains(&id); - self.graphics.push(GuiCmd::FoldableHeader { + self.add_graphic(GuiCmd::FoldableHeader { interact, rect, open, }); - self.add_text(text_cursor + vec2(self.options.start_icon_width, 0.0), text); + self.add_text( + text_cursor + vec2(self.options().start_icon_width, 0.0), + text, + ); if open { + // TODO: new region let old_id = self.id; self.id = id; - let old_x = self.layouter.cursor.x; - self.layouter.cursor.x += self.options.indent; + let old_x = self.cursor.x; + self.cursor.x += self.options().indent; add_contents(self); - self.layouter.cursor.x = old_x; + self.cursor.x = old_x; self.id = old_id; } @@ -370,73 +413,35 @@ impl Layout { /// Start a region with horizontal layout pub fn horizontal(&mut self, add_contents: F) where - F: FnOnce(&mut Layout), - { - let horizontal_layouter = Layouter { - dir: Direction::Horizontal, - cursor: self.layouter.cursor, - ..Default::default() - }; - let old_layouter = std::mem::replace(&mut self.layouter, horizontal_layouter); - add_contents(self); - let horizontal_layouter = std::mem::replace(&mut self.layouter, old_layouter); - self.layouter.reserve_space(horizontal_layouter.size); - } - - // ------------------------------------------------------------------------ - // Free painting. It is up to the caller to make sure there is room for these. - pub fn add_paint_command(&mut self, cmd: GuiCmd) { - self.graphics.push(cmd); - } - - // ------------------------------------------------------------------------ - - /// Show a pop-over window - pub fn show_popup(&mut self, window_pos: Vec2, add_contents: F) - where - F: FnOnce(&mut Layout), + F: FnOnce(&mut Region), { - // TODO: less copying - let mut popup_layout = Layout { - options: self.options, - input: self.input, - font: self.font.clone(), - memory: self.memory.clone(), // TODO: Arc + let mut horizontal_region = Region { + data: self.data, id: self.id, - layouter: Default::default(), - graphics: vec![], - hovering_graphics: vec![], + dir: Direction::Horizontal, + cursor: self.cursor, + size: vec2(0.0, 0.0), }; - popup_layout.layouter.cursor = window_pos + self.options.window_padding; - - add_contents(&mut popup_layout); - - // TODO: handle the last item_spacing in a nicer way - let inner_size = popup_layout.layouter.size - self.options.item_spacing; - let outer_size = inner_size + 2.0 * self.options.window_padding; - - let rect = Rect::from_min_size(window_pos, outer_size); - self.hovering_graphics.push(GuiCmd::Window { rect }); - self.hovering_graphics - .extend(popup_layout.gui_commands().cloned()); + add_contents(&mut horizontal_region); + let size = horizontal_region.size; + self.reserve_space_inner(size); } // ------------------------------------------------------------------------ fn reserve_space(&mut self, size: Vec2, interaction_id: Option) -> (Rect, InteractInfo) { let rect = Rect { - pos: self.layouter.cursor, + pos: self.cursor, size, }; - self.layouter - .reserve_space(size + self.options.item_spacing); - let hovered = rect.contains(self.input.mouse_pos); - let clicked = hovered && self.input.mouse_clicked; + self.reserve_space_inner(size + self.options().item_spacing); + let hovered = rect.contains(self.input().mouse_pos); + let clicked = hovered && self.input().mouse_clicked; let active = if interaction_id.is_some() { if clicked { - self.memory.active_id = interaction_id; + self.data.memory.active_id = interaction_id; } - self.memory.active_id == interaction_id + self.data.memory.active_id == interaction_id } else { false }; @@ -449,6 +454,19 @@ impl Layout { (rect, interact) } + /// Reserve this much space and move the cursor. + fn reserve_space_inner(&mut self, size: Vec2) { + if self.dir == Direction::Horizontal { + self.cursor.x += size.x; + self.size.x += size.x; + self.size.y = self.size.y.max(size.y); + } else { + self.cursor.y += size.y; + self.size.y += size.y; + self.size.x = self.size.x.max(size.x); + } + } + fn get_id(&self, id_str: &str) -> Id { use std::hash::Hasher; let mut hasher = std::collections::hash_map::DefaultHasher::new(); @@ -457,13 +475,14 @@ impl Layout { hasher.finish() } + // TODO: move this function fn layout_text(&self, text: &str) -> (TextFragments, Vec2) { - let line_spacing = self.font.line_spacing(); + let line_spacing = self.data.font.line_spacing(); let mut cursor_y = 0.0; let mut max_width = 0.0; let mut text_fragments = Vec::new(); for line in text.split('\n') { - let x_offsets = self.font.layout_single_line(&line); + let x_offsets = self.data.font.layout_single_line(&line); let line_width = *x_offsets.last().unwrap(); text_fragments.push(TextFragment { x_offsets, @@ -480,7 +499,7 @@ impl Layout { fn add_text(&mut self, pos: Vec2, text: Vec) { for fragment in text { - self.graphics.push(GuiCmd::Text { + self.add_graphic(GuiCmd::Text { pos: pos + vec2(0.0, fragment.y_offset), style: TextStyle::Label, text: fragment.text, @@ -494,7 +513,7 @@ impl Layout { hovered: interact.hovered, clicked: interact.clicked, active: interact.active, - layout: self, + data: self.data, } } } diff --git a/emgui/src/lib.rs b/emgui/src/lib.rs index d3d5d87a1..71b882e33 100644 --- a/emgui/src/lib.rs +++ b/emgui/src/lib.rs @@ -17,8 +17,8 @@ pub mod types; pub use crate::{ emgui::Emgui, font::Font, - layout::Layout, layout::LayoutOptions, + layout::Region, painter::{Frame, Painter, Vertex}, style::Style, types::RawInput, diff --git a/emgui_wasm/Cargo.toml b/emgui_wasm/Cargo.toml index 59bfb1ddf..8597e0312 100644 --- a/emgui_wasm/Cargo.toml +++ b/emgui_wasm/Cargo.toml @@ -9,7 +9,6 @@ crate-type = ["cdylib", "rlib"] [dependencies] js-sys = "0.3" -lazy_static = "1" serde = "1" serde_json = "1" wasm-bindgen = "0.2" @@ -22,6 +21,7 @@ features = [ 'Document', 'Element', 'HtmlCanvasElement', + 'Performance', 'WebGlBuffer', 'WebGlProgram', 'WebGlRenderingContext', diff --git a/emgui_wasm/src/app.rs b/emgui_wasm/src/app.rs index e6bb89bf9..3d9a62e9a 100644 --- a/emgui_wasm/src/app.rs +++ b/emgui_wasm/src/app.rs @@ -1,7 +1,7 @@ -use emgui::{math::*, types::*, Layout}; +use emgui::{math::*, types::*, Region}; pub trait GuiSettings { - fn show_gui(&mut self, gui: &mut Layout); + fn show_gui(&mut self, gui: &mut Region); } pub struct App { @@ -30,7 +30,7 @@ impl Default for App { } impl GuiSettings for App { - fn show_gui(&mut self, gui: &mut Layout) { + fn show_gui(&mut self, gui: &mut Region) { gui.label(format!( "Screen size: {} x {}", gui.input().screen_size.x, @@ -72,7 +72,7 @@ impl GuiSettings for App { gui.slider_f32("stroke_width", &mut self.stroke_width, 0.0, 10.0); }); - gui.add_paint_command(GuiCmd::PaintCommands(vec![PaintCmd::Rect { + gui.add_graphic(GuiCmd::PaintCommands(vec![PaintCmd::Rect { corner_radius: self.corner_radius, fill_color: Some(srgba(136, 136, 136, 255)), pos: vec2(300.0, 100.0), @@ -92,7 +92,7 @@ impl GuiSettings for App { } impl GuiSettings for emgui::LayoutOptions { - fn show_gui(&mut self, gui: &mut Layout) { + fn show_gui(&mut self, gui: &mut Region) { if gui.button("Reset LayoutOptions").clicked { *self = Default::default(); } @@ -109,7 +109,7 @@ impl GuiSettings for emgui::LayoutOptions { } impl GuiSettings for emgui::Style { - fn show_gui(&mut self, gui: &mut Layout) { + fn show_gui(&mut self, gui: &mut Region) { if gui.button("Reset Style").clicked { *self = Default::default(); } diff --git a/emgui_wasm/src/lib.rs b/emgui_wasm/src/lib.rs index 71ab334de..d87ea6381 100644 --- a/emgui_wasm/src/lib.rs +++ b/emgui_wasm/src/lib.rs @@ -1,75 +1,103 @@ #![deny(warnings)] -extern crate lazy_static; extern crate serde_json; extern crate wasm_bindgen; extern crate emgui; -use std::sync::Mutex; - -use emgui::{Emgui, Font, Frame, RawInput}; +use emgui::{Emgui, Font, RawInput}; use wasm_bindgen::prelude::*; mod app; mod webgl; -fn font() -> Font { - Font::new(20) // TODO: don't create this multiple times +#[derive(Clone, Copy, Default)] +struct Stats { + num_vertices: usize, + num_triangles: usize, + everything_ms: f64, + webgl_ms: f64, } -#[wasm_bindgen] -pub fn new_webgl_painter(canvas_id: &str) -> Result { - let emgui_painter = emgui::Painter::new(font()); // TODO: don't create this twice - webgl::Painter::new(canvas_id, emgui_painter.texture()) +fn now_ms() -> f64 { + web_sys::window() + .expect("should have a Window") + .performance() + .expect("should have a Performance") + .now() } -struct State { +#[wasm_bindgen] +pub struct State { app: app::App, emgui: Emgui, - emgui_painter: emgui::Painter, + webgl_painter: webgl::Painter, + stats: Stats, } impl State { - fn new() -> State { - State { + fn new(canvas_id: &str) -> Result { + let font = Font::new(20); // TODO: Arc to avoid cloning + let emgui = Emgui::new(font); + let webgl_painter = webgl::Painter::new(canvas_id, emgui.texture())?; + Ok(State { app: Default::default(), - emgui: Emgui::new(font()), - emgui_painter: emgui::Painter::new(font()), - } + emgui, + webgl_painter, + stats: Default::default(), + }) } - fn frame(&mut self, raw_input: RawInput) -> Frame { + fn run(&mut self, raw_input: RawInput) -> Result<(), JsValue> { + let everything_start = now_ms(); + self.emgui.new_frame(raw_input); use crate::app::GuiSettings; - self.app.show_gui(&mut self.emgui.layout); let mut style = self.emgui.style.clone(); - self.emgui.layout.foldable("Style", |gui| { + let mut region = self.emgui.whole_screen_region(); + self.app.show_gui(&mut region); + + region.foldable("Style", |gui| { style.show_gui(gui); }); + + let stats = self.stats; // TODO: avoid + region.foldable("Stats", |gui| { + gui.label(format!("num_vertices: {}", stats.num_vertices)); + gui.label(format!("num_triangles: {}", stats.num_triangles)); + + gui.label("Timings:"); + gui.label(format!("Everything: {:.1} ms", stats.everything_ms)); + gui.label(format!("WebGL: {:.1} ms", stats.webgl_ms)); + }); + self.emgui.style = style; + let frame = self.emgui.paint(); + + self.stats.num_vertices = frame.vertices.len(); + self.stats.num_triangles = frame.indices.len() / 3; - let commands = self.emgui.paint(); - self.emgui_painter.paint(&commands) + let webgl_start = now_ms(); + let result = self.webgl_painter.paint(&frame); + self.stats.webgl_ms = now_ms() - webgl_start; + + self.stats.everything_ms = now_ms() - everything_start; + + result } } #[wasm_bindgen] -pub fn paint_webgl(webgl_painter: &webgl::Painter, raw_input_json: &str) -> Result<(), JsValue> { +pub fn new_webgl_gui(canvas_id: &str) -> Result { + State::new(canvas_id) +} + +#[wasm_bindgen] +pub fn run_gui(state: &mut State, raw_input_json: &str) -> Result<(), JsValue> { // TODO: nicer interface than JSON let raw_input: RawInput = serde_json::from_str(raw_input_json).unwrap(); - - lazy_static::lazy_static! { - static ref STATE: Mutex> = Default::default(); - } - - let mut state = STATE.lock().unwrap(); - if state.is_none() { - *state = Some(State::new()); - } - let frame = state.as_mut().unwrap().frame(raw_input); - webgl_painter.paint(&frame) + state.run(raw_input) } diff --git a/emgui_wasm/src/webgl.rs b/emgui_wasm/src/webgl.rs index 589716444..0bdc8db40 100644 --- a/emgui_wasm/src/webgl.rs +++ b/emgui_wasm/src/webgl.rs @@ -10,7 +10,6 @@ use emgui::Frame; type Gl = WebGlRenderingContext; -#[wasm_bindgen] pub struct Painter { canvas: web_sys::HtmlCanvasElement, gl: WebGlRenderingContext, @@ -101,7 +100,7 @@ impl Painter { varying vec4 v_color; void main() { gl_FragColor = v_color; - gl_FragColor *= texture2D(u_sampler, v_tc).a; + gl_FragColor.a *= texture2D(u_sampler, v_tc).a; } "#, )?;