[web] minor fixes for reactive mode

This commit is contained in:
Emil Ernerfeldt 2020-07-19 00:44:06 +02:00
parent 900a758903
commit 57ae03f52d
11 changed files with 62 additions and 45 deletions

View file

@ -207,24 +207,24 @@ function makeMutClosure(arg0, arg1, dtor, f) {
real.original = state;
return real;
}
function __wbg_adapter_26(arg0, arg1) {
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5402719cc6dde927(arg0, arg1);
function __wbg_adapter_26(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h22fd33d9f501a695(arg0, arg1, addHeapObject(arg2));
}
function __wbg_adapter_29(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h22fd33d9f501a695(arg0, arg1, addHeapObject(arg2));
function __wbg_adapter_29(arg0, arg1) {
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h84da5f062b972f09(arg0, arg1);
}
function __wbg_adapter_32(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h22fd33d9f501a695(arg0, arg1, addHeapObject(arg2));
}
function __wbg_adapter_35(arg0, arg1) {
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h84da5f062b972f09(arg0, arg1);
function __wbg_adapter_35(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h22fd33d9f501a695(arg0, arg1, addHeapObject(arg2));
}
function __wbg_adapter_38(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h22fd33d9f501a695(arg0, arg1, addHeapObject(arg2));
function __wbg_adapter_38(arg0, arg1) {
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5402719cc6dde927(arg0, arg1);
}
function __wbg_adapter_41(arg0, arg1, arg2) {
@ -725,28 +725,28 @@ async function init(input) {
var ret = wasm.memory;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper383 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 72, __wbg_adapter_41);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper385 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 71, __wbg_adapter_41);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper376 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 71, __wbg_adapter_29);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper379 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 71, __wbg_adapter_38);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper381 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 71, __wbg_adapter_35);
var ret = makeMutClosure(arg0, arg1, 72, __wbg_adapter_35);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper375 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 71, __wbg_adapter_26);
var ret = makeMutClosure(arg0, arg1, 72, __wbg_adapter_38);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper383 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 71, __wbg_adapter_32);
imports.wbg.__wbindgen_closure_wrapper376 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 72, __wbg_adapter_26);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper381 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 72, __wbg_adapter_29);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper379 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 72, __wbg_adapter_32);
return addHeapObject(ret);
};

Binary file not shown.

View file

@ -5,8 +5,6 @@
<!-- Disable zooming: -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<!-- TODO: read https://www.html5rocks.com/en/mobile/mobifying/#toc-meta-viewport -->
<head>
<title>Egui An experimental immediate mode GUI written in Rust</title>
<style>

View file

@ -51,6 +51,7 @@ This is the core library crate Egui. It is fully platform independent without an
* [x] Change to resize cursor on hover
* [ ] Make it a JS library for easily creating your own stuff
* [x] Read url fragment and redirect to a subpage (e.g. different examples apps)
* [ ] Read https://www.html5rocks.com/en/mobile/mobifying/#toc-meta-viewport
### Visuals
* [x] Simplify button style to make for nicer collapsible headers. Maybe weak outline? Or just subtle different text color?
@ -101,7 +102,7 @@ Add extremely quick animations for some things, maybe 2-3 frames. For instance:
* [x] Solve which parts of Context are behind a mutex
* [x] Rename Region to Ui
* [ ] Move Path and Triangles to own crate
* [ ] Maybe find a shorter name for the library like `egui`?
* [x] Maybe find a shorter name for the library like `egui`?
### Global widget search
Ability to do a search for any widget. The search works even for collapsed regions and closed windows and menus. This is implemented like this: while searching, all region are layed out and their add_content functions are run. If none of the contents matches the search, the layout is reverted and nothing is shown. So windows will get temporarily opened and run, but if the search is not a match in the window it is closed again. This means then when searching your whole GUI is being run, which may be a bit slower, but it would be a really awesome feature.

View file

@ -185,16 +185,18 @@ impl Prepared {
if move_interact.active {
state.pos += input.mouse.delta;
state.vel = input.mouse.velocity;
ctx.request_repaint();
} else {
let stop_speed = 20.0; // Pixels per second.
let friction_coeff = 1000.0; // Pixels per second squared.
let dt = input.unstable_dt;
let friction = friction_coeff * input.dt;
let friction = friction_coeff * dt;
if friction > state.vel.length() || state.vel.length() < stop_speed {
state.vel = Vec2::zero();
} else {
state.vel -= friction * state.vel.normalized();
state.pos += state.vel * input.dt;
state.pos += state.vel * dt;
ctx.request_repaint();
}
}

View file

@ -50,13 +50,14 @@ impl State {
pub fn toggle(&mut self, ui: &Ui) {
self.open = !self.open;
self.toggle_time = ui.input().time;
ui.ctx().request_repaint();
}
/// 0 for closed, 1 for open, with tweening
pub fn openness(&self, ui: &Ui) -> f32 {
let animation_time = ui.style().animation_time;
let time_since_toggle = (ui.input().time - self.toggle_time) as f32;
let time_since_toggle = time_since_toggle + ui.input().dt; // Instant feedback
let time_since_toggle = time_since_toggle + ui.input().predicted_dt; // Instant feedback
if time_since_toggle <= animation_time {
ui.ctx().request_repaint();
}
@ -103,6 +104,7 @@ impl State {
let openness = self.openness(ui);
let animate = 0.0 < openness && openness < 1.0;
if animate {
ui.ctx().request_repaint();
Some(ui.add_custom(|child_ui| {
let max_height = if self.open {
if let Some(full_height) = self.open_height {

View file

@ -193,7 +193,7 @@ impl Prepared {
if show_scroll_this_frame && current_scroll_bar_width <= 0.0 {
// Avoid frame delay; start shwoing scroll bar right away:
current_scroll_bar_width = remap_clamp(
ui.input().dt,
ui.input().predicted_dt,
0.0..=ui.style().animation_time,
0.0..=max_scroll_bar_width,
);
@ -291,6 +291,7 @@ impl Prepared {
if show_scroll_this_frame != state.show_scroll {
state.toggle_time = ui.input().time;
ui.ctx().request_repaint();
}
state.offset.y = state.offset.y.min(content_size.y - inner_rect.height());

View file

@ -58,7 +58,11 @@ pub struct InputState {
pub time: f64,
/// Time since last frame, in seconds.
pub dt: f32,
/// This can be very unstable in reactive mode (when we don't paint each frame).
pub unstable_dt: f32,
/// Can be used to fast-forward to next frame for instance feedback. hacky.
pub predicted_dt: f32,
/// Local time. Only used for the clock in the example app.
pub seconds_since_midnight: Option<f64>,
@ -177,14 +181,15 @@ impl InputState {
#[must_use]
pub fn begin_frame(self, new: RawInput) -> InputState {
let mouse = self.mouse.begin_frame(&new);
let dt = (new.time - self.raw.time) as f32;
let unstable_dt = (new.time - self.raw.time) as f32;
InputState {
mouse,
scroll_delta: new.scroll_delta,
screen_size: new.screen_size,
pixels_per_point: new.pixels_per_point.unwrap_or(1.0),
time: new.time,
dt,
unstable_dt,
predicted_dt: 1.0 / 60.0, // TODO: remove this hack
seconds_since_midnight: new.seconds_since_midnight,
events: new.events.clone(), // TODO: remove clone() and use raw.events
raw: new,
@ -312,7 +317,7 @@ impl InputState {
self.pixels_per_point
));
ui.add(label!("time: {:.3} s", self.time));
ui.add(label!("dt: {:.3} s", self.dt));
ui.add(label!("dt: {:.1} ms", 1e3 * self.unstable_dt));
ui.add(label!(
"seconds_since_midnight: {:?} s",
self.seconds_since_midnight

View file

@ -142,6 +142,7 @@ impl<'t> Widget for TextEdit<'t> {
));
}
}
ui.ctx().request_repaint(); // TODO: only when cursor blinks on or off
}
ui.add_galley(interact.rect.min, galley, text_style, text_color);

View file

@ -25,7 +25,7 @@ pub enum RunMode {
/// This is good for games and stuff where you want to run logic at e.g. 60 FPS.
Continuous,
/// Only repaint when there is new input (mouse movement, keyboard input etc).
/// Only repaint when there are animations or input (mouse movement, keyboard input etc).
Reactive,
}
@ -410,13 +410,13 @@ pub fn translate_key(key: &str) -> Option<egui::Key> {
pub struct AppRunnerRef(Arc<Mutex<AppRunner>>);
fn paint_and_schedule(runner_ref: AppRunnerRef) -> Result<(), JsValue> {
fn paint(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let mut runner_lock = runner_ref.0.lock();
if runner_lock.backend.run_mode() == RunMode::Continuous || runner_lock.needs_repaint {
runner_lock.needs_repaint = false;
let (output, paint_jobs) = runner_lock.logic()?;
runner_lock.paint(paint_jobs)?;
runner_lock.needs_repaint = output.needs_repaint;
runner_lock.needs_repaint |= output.needs_repaint;
}
Ok(())
}
@ -430,7 +430,7 @@ fn paint_and_schedule(runner_ref: AppRunnerRef) -> Result<(), JsValue> {
Ok(())
}
paint(&runner_ref)?;
paint_if_needed(&runner_ref)?;
request_animation_frame(runner_ref)
}
@ -501,6 +501,7 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
runner_lock.web_input.mouse_pos =
Some(pos_from_mouse_event(runner_lock.canvas_id(), &event));
runner_lock.web_input.mouse_down = true;
runner_lock.logic().unwrap(); // in case we get "mouseup" the same frame. TODO: handle via events instead
runner_lock.needs_repaint = true;
event.stop_propagation();
event.prevent_default();
@ -599,7 +600,7 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let mut runner_lock = runner_ref.0.lock();
runner_lock.web_input.is_touch = true;
runner_lock.web_input.mouse_down = false; // First release mouse to click...
runner_lock.logic().unwrap(); // ...do the clicking...
runner_lock.logic().unwrap(); // ...do the clicking... (TODO: handle via events instead)
runner_lock.web_input.mouse_pos = None; // ...remove hover effect
runner_lock.needs_repaint = true;
event.stop_propagation();

View file

@ -1,7 +1,7 @@
#![deny(warnings)]
#![warn(clippy::all)]
use egui::{label, widgets::Separator, Align, TextStyle, *};
use egui::{label, Align, Layout, TextStyle};
use wasm_bindgen::prelude::*;
@ -9,7 +9,7 @@ use wasm_bindgen::prelude::*;
/// This is the entry-point for all the web-assembly.
#[wasm_bindgen]
pub fn start(canvas_id: &str) -> Result<(), JsValue> {
pub fn start(canvas_id: &str) -> Result<(), wasm_bindgen::JsValue> {
let backend = egui_web::Backend::new(canvas_id, egui_web::RunMode::Reactive)?;
let app = Box::new(MyApp::default());
let runner = egui_web::AppRunner::new(backend, app)?;
@ -22,6 +22,7 @@ pub fn start(canvas_id: &str) -> Result<(), JsValue> {
#[derive(Default)]
pub struct MyApp {
example_app: egui::examples::ExampleApp,
frames_painted: u64,
}
impl egui_web::App for MyApp {
@ -41,7 +42,7 @@ impl egui_web::App for MyApp {
ui.label("Project home page:");
ui.hyperlink("https://github.com/emilk/emigui/");
});
ui.add(Separator::new());
ui.separator();
ui.label("WebGl painter info:");
ui.indent("webgl region id", |ui| {
@ -56,13 +57,15 @@ impl egui_web::App for MyApp {
.text_style(TextStyle::Monospace),
);
ui.separator();
ui.horizontal(|ui| {
let mut run_mode = backend.run_mode();
ui.label("Run mode:");
ui.radio_value("Continuous", &mut run_mode, egui_web::RunMode::Continuous)
.tooltip_text("Repaint everything each frame");
ui.radio_value("Reactive", &mut run_mode, egui_web::RunMode::Reactive)
.tooltip_text("Repaint when there is new input (e.g. mouse movement)");
.tooltip_text("Repaint when there are animations or input (e.g. mouse movement)");
backend.set_run_mode(run_mode);
});
@ -72,7 +75,10 @@ impl egui_web::App for MyApp {
.text_style(TextStyle::Monospace),
);
} else {
ui.label("Only running UI code when there is new input");
ui.label("Only running UI code when there are animations or input");
}
self.frames_painted += 1;
ui.label(format!("Total frames painted: {}", self.frames_painted));
}
}