[web] minor fixes for reactive mode
This commit is contained in:
parent
900a758903
commit
57ae03f52d
11 changed files with 62 additions and 45 deletions
|
@ -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.
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue