Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement repaint_after for eframe web #1760

Merged
merged 3 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions eframe/src/web/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use super::{glow_wrapping::WrappedGlowPainter, *};

use crate::epi;

use egui::mutex::{Mutex, MutexGuard};
use egui::TexturesDelta;

pub use egui::{pos2, Color32};

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -34,21 +36,34 @@ impl WebInput {

use std::sync::atomic::Ordering::SeqCst;

pub struct NeedRepaint(std::sync::atomic::AtomicBool);
/// Stores when to do the next repaint.
pub struct NeedRepaint(Mutex<f64>);

impl Default for NeedRepaint {
fn default() -> Self {
Self(true.into())
Self(Mutex::new(f64::NEG_INFINITY)) // start with a repaint
}
}

impl NeedRepaint {
pub fn fetch_and_clear(&self) -> bool {
self.0.swap(false, SeqCst)
/// Returns the time (in [`now_sec`] scale) when
/// we should next repaint.
pub fn when_to_repaint(&self) -> f64 {
*self.0.lock()
}

/// Unschedule repainting.
pub fn clear(&self) {
*self.0.lock() = f64::INFINITY;
}

pub fn repaint_after(&self, num_seconds: f64) {
let mut repaint_time = self.0.lock();
*repaint_time = repaint_time.min(now_sec() + num_seconds);
}

pub fn set_true(&self) {
self.0.store(true, SeqCst);
pub fn repaint_asap(&self) {
*self.0.lock() = f64::NEG_INFINITY;
}
}

Expand Down Expand Up @@ -194,7 +209,7 @@ impl AppRunner {
{
let needs_repaint = needs_repaint.clone();
egui_ctx.set_request_repaint_callback(move || {
needs_repaint.0.store(true, SeqCst);
needs_repaint.repaint_asap();
});
}

Expand Down Expand Up @@ -420,7 +435,6 @@ fn start_runner(app_runner: AppRunner) -> Result<AppRunnerRef, JsValue> {
super::events::install_canvas_events(&runner_container)?;
super::events::install_document_events(&runner_container)?;
text_agent::install_text_agent(&runner_container)?;
super::events::repaint_every_ms(&runner_container, 1000)?; // just in case. TODO(emilk): make it a parameter

super::events::paint_and_schedule(&runner_container.runner, runner_container.panicked.clone())?;

Expand Down
78 changes: 23 additions & 55 deletions eframe/src/web/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ pub fn paint_and_schedule(
) -> Result<(), JsValue> {
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let mut runner_lock = runner_ref.lock();
if runner_lock.needs_repaint.fetch_and_clear() {
if runner_lock.needs_repaint.when_to_repaint() <= now_sec() {
runner_lock.needs_repaint.clear();
runner_lock.clear_color_buffer();
let (repaint_after, clipped_primitives) = runner_lock.logic()?;
runner_lock.paint(&clipped_primitives)?;
if repaint_after.is_zero() {
runner_lock.needs_repaint.set_true();
}
// TODO: schedule a repaint after `repaint_after` when it is not zero
runner_lock
.needs_repaint
.repaint_after(repaint_after.as_secs_f64());
runner_lock.auto_save();
}

Expand Down Expand Up @@ -75,7 +75,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
{
runner_lock.input.raw.events.push(egui::Event::Text(key));
}
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();

let egui_wants_keyboard = runner_lock.egui_ctx().wants_keyboard_input();

Expand Down Expand Up @@ -123,7 +123,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
modifiers,
});
}
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
},
)?;

Expand All @@ -137,7 +137,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
let text = text.replace("\r\n", "\n");
if !text.is_empty() {
runner_lock.input.raw.events.push(egui::Event::Paste(text));
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
event.stop_propagation();
event.prevent_default();
Expand All @@ -152,7 +152,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
"cut",
|_: web_sys::ClipboardEvent, mut runner_lock| {
runner_lock.input.raw.events.push(egui::Event::Cut);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
},
)?;

Expand All @@ -162,7 +162,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
"copy",
|_: web_sys::ClipboardEvent, mut runner_lock| {
runner_lock.input.raw.events.push(egui::Event::Copy);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
},
)?;

Expand All @@ -171,7 +171,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
&window,
event_name,
|_: web_sys::Event, runner_lock| {
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
},
)?;
}
Expand All @@ -190,38 +190,6 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
Ok(())
}

/// Repaint at least every `ms` milliseconds.
pub fn repaint_every_ms(
runner_container: &AppRunnerContainer,
milliseconds: i32,
) -> Result<(), JsValue> {
assert!(milliseconds >= 0);

use wasm_bindgen::JsCast;

let window = web_sys::window().unwrap();

let closure = Closure::wrap(Box::new({
let runner = runner_container.runner.clone();
let panicked = runner_container.panicked.clone();

move || {
// Do not lock the runner if the code has panicked
if !panicked.load(Ordering::SeqCst) {
runner.lock().needs_repaint.set_true();
}
}
}) as Box<dyn FnMut()>);

window.set_interval_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref(),
milliseconds,
)?;

closure.forget();
Ok(())
}

pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<(), JsValue> {
use wasm_bindgen::JsCast;
let canvas = canvas_element(runner_container.runner.lock().canvas_id()).unwrap();
Expand Down Expand Up @@ -254,7 +222,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
pressed: true,
modifiers,
});
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
event.stop_propagation();
// Note: prevent_default breaks VSCode tab focusing, hence why we don't call it here.
Expand All @@ -271,7 +239,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
.raw
.events
.push(egui::Event::PointerMoved(pos));
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
},
Expand All @@ -294,7 +262,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
pressed: false,
modifiers,
});
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();

text_agent::update_text_agent(runner_lock);
}
Expand All @@ -308,7 +276,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
"mouseleave",
|event: web_sys::MouseEvent, mut runner_lock| {
runner_lock.input.raw.events.push(egui::Event::PointerGone);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
},
Expand Down Expand Up @@ -336,7 +304,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
});

push_touches(&mut *runner_lock, egui::TouchPhase::Start, &event);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
},
Expand All @@ -358,7 +326,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
.push(egui::Event::PointerMoved(pos));

push_touches(&mut *runner_lock, egui::TouchPhase::Move, &event);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
},
Expand All @@ -385,7 +353,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
runner_lock.input.raw.events.push(egui::Event::PointerGone);

push_touches(&mut *runner_lock, egui::TouchPhase::End, &event);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
}
Expand Down Expand Up @@ -444,7 +412,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
.push(egui::Event::Scroll(delta));
}

runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
},
Expand All @@ -464,7 +432,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
});
}
}
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
}
Expand All @@ -476,7 +444,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
"dragleave",
|event: web_sys::DragEvent, mut runner_lock| {
runner_lock.input.raw.hovered_files.clear();
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();
},
Expand All @@ -488,7 +456,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
move |event: web_sys::DragEvent, mut runner_lock| {
if let Some(data_transfer) = event.data_transfer() {
runner_lock.input.raw.hovered_files.clear();
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
// Unlock the runner so it can be locked after a future await point
drop(runner_lock);

Expand Down Expand Up @@ -524,7 +492,7 @@ pub fn install_canvas_events(runner_container: &AppRunnerContainer) -> Result<()
..Default::default()
},
);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
Err(err) => {
tracing::error!("Failed to read file: {:?}", err);
Expand Down
5 changes: 3 additions & 2 deletions eframe/src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use std::sync::{
Arc,
};

use egui::mutex::{Mutex, MutexGuard};
use wasm_bindgen::prelude::*;
use web_sys::EventTarget;

Expand All @@ -30,7 +29,9 @@ use crate::Theme;

// ----------------------------------------------------------------------------

/// Current time in seconds (since undefined point in time)
/// Current time in seconds (since undefined point in time).
///
/// Monotonically increasing.
pub fn now_sec() -> f64 {
web_sys::window()
.expect("should have a Window")
Expand Down
8 changes: 4 additions & 4 deletions eframe/src/web/text_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn install_text_agent(runner_container: &AppRunnerContainer) -> Result<(), J
if !text.is_empty() && !is_composing.get() {
input_clone.set_value("");
runner_lock.input.raw.events.push(egui::Event::Text(text));
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
}
})?;
Expand All @@ -75,7 +75,7 @@ pub fn install_text_agent(runner_container: &AppRunnerContainer) -> Result<(), J
.raw
.events
.push(egui::Event::CompositionStart);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
})?;

Expand All @@ -85,7 +85,7 @@ pub fn install_text_agent(runner_container: &AppRunnerContainer) -> Result<(), J
move |event: web_sys::CompositionEvent, mut runner_lock: MutexGuard<'_, AppRunner>| {
if let Some(event) = event.data().map(egui::Event::CompositionUpdate) {
runner_lock.input.raw.events.push(event);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
},
)?;
Expand All @@ -99,7 +99,7 @@ pub fn install_text_agent(runner_container: &AppRunnerContainer) -> Result<(), J

if let Some(event) = event.data().map(egui::Event::CompositionEnd) {
runner_lock.input.raw.events.push(event);
runner_lock.needs_repaint.set_true();
runner_lock.needs_repaint.repaint_asap();
}
}
})?;
Expand Down
Loading