mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 06:10:06 -04:00
Add Context::request_repaint_after (#1694)
This commit is contained in:
@@ -28,7 +28,6 @@ impl Default for WrappedTextureManager {
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Default)]
|
||||
struct ContextImpl {
|
||||
/// `None` until the start of the first frame.
|
||||
@@ -47,7 +46,9 @@ struct ContextImpl {
|
||||
output: PlatformOutput,
|
||||
|
||||
paint_stats: PaintStats,
|
||||
|
||||
/// the duration backend will poll for new events, before forcing another egui update
|
||||
/// even if there's no new events.
|
||||
repaint_after: std::time::Duration,
|
||||
/// While positive, keep requesting repaints. Decrement at the end of each frame.
|
||||
repaint_requests: u32,
|
||||
request_repaint_callbacks: Option<Box<dyn Fn() + Send + Sync>>,
|
||||
@@ -574,6 +575,39 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Request repaint after the specified duration elapses in the case of no new input
|
||||
/// events being received.
|
||||
///
|
||||
/// The function can be multiple times, but only the *smallest* duration will be considered.
|
||||
/// So, if the function is called two times with `1 second` and `2 seconds`, egui will repaint
|
||||
/// after `1 second`
|
||||
///
|
||||
/// This is primarily useful for applications who would like to save battery by avoiding wasted
|
||||
/// redraws when the app is not in focus. But sometimes the GUI of the app might become stale
|
||||
/// and outdated if it is not updated for too long.
|
||||
///
|
||||
/// Lets say, something like a stop watch widget that displays the time in seconds. You would waste
|
||||
/// resources repainting multiple times within the same second (when you have no input),
|
||||
/// just calculate the difference of duration between current time and next second change,
|
||||
/// and call this function, to make sure that you are displaying the latest updated time, but
|
||||
/// not wasting resources on needless repaints within the same second.
|
||||
///
|
||||
/// NOTE: only works if called before `Context::end_frame()`. to force egui to update,
|
||||
/// use `Context::request_repaint()` instead.
|
||||
///
|
||||
/// ### Quirk:
|
||||
/// Duration begins at the next frame. lets say for example that its a very inefficient app
|
||||
/// and takes 500 milliseconds per frame at 2 fps. The widget / user might want a repaint in
|
||||
/// next 500 milliseconds. Now, app takes 1000 ms per frame (1 fps) because the backend event
|
||||
/// timeout takes 500 milli seconds AFTER the vsync swap buffer.
|
||||
/// So, its not that we are requesting repaint within X duration. We are rather timing out
|
||||
/// during app idle time where we are not receiving any new input events.
|
||||
pub fn request_repaint_after(&self, duration: std::time::Duration) {
|
||||
// Maybe we can check if duration is ZERO, and call self.request_repaint()?
|
||||
let mut ctx = self.write();
|
||||
ctx.repaint_after = ctx.repaint_after.min(duration);
|
||||
}
|
||||
|
||||
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`].
|
||||
///
|
||||
/// This lets you wake up a sleeping UI thread.
|
||||
@@ -805,19 +839,26 @@ impl Context {
|
||||
|
||||
let platform_output: PlatformOutput = std::mem::take(&mut self.output());
|
||||
|
||||
let needs_repaint = if self.read().repaint_requests > 0 {
|
||||
// if repaint_requests is greater than zero. just set the duration to zero for immediate
|
||||
// repaint. if there's no repaint requests, then we can use the actual repaint_after instead.
|
||||
let repaint_after = if self.read().repaint_requests > 0 {
|
||||
self.write().repaint_requests -= 1;
|
||||
true
|
||||
std::time::Duration::ZERO
|
||||
} else {
|
||||
false
|
||||
self.read().repaint_after
|
||||
};
|
||||
self.write().requested_repaint_last_frame = needs_repaint;
|
||||
|
||||
self.write().requested_repaint_last_frame = repaint_after.is_zero();
|
||||
// make sure we reset the repaint_after duration.
|
||||
// otherwise, if repaint_after is low, then any widget setting repaint_after next frame,
|
||||
// will fail to overwrite the previous lower value. and thus, repaints will never
|
||||
// go back to higher values.
|
||||
self.write().repaint_after = std::time::Duration::MAX;
|
||||
let shapes = self.drain_paint_lists();
|
||||
|
||||
FullOutput {
|
||||
platform_output,
|
||||
needs_repaint,
|
||||
repaint_after,
|
||||
textures_delta,
|
||||
shapes,
|
||||
}
|
||||
|
||||
@@ -10,10 +10,15 @@ pub struct FullOutput {
|
||||
/// Non-rendering related output.
|
||||
pub platform_output: PlatformOutput,
|
||||
|
||||
/// If `true`, egui is requesting immediate repaint (i.e. on the next frame).
|
||||
/// If `Duration::is_zero()`, egui is requesting immediate repaint (i.e. on the next frame).
|
||||
///
|
||||
/// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`.
|
||||
pub needs_repaint: bool,
|
||||
///
|
||||
/// If `Duration` is greater than zero, egui wants to be repainted at or before the specified
|
||||
/// duration elapses. when in reactive mode, egui spends forever waiting for input and only then,
|
||||
/// will it repaint itself. this can be used to make sure that backend will only wait for a
|
||||
/// specified amount of time, and repaint egui without any new input.
|
||||
pub repaint_after: std::time::Duration,
|
||||
|
||||
/// Texture changes since last frame (including the font texture).
|
||||
///
|
||||
@@ -32,13 +37,13 @@ impl FullOutput {
|
||||
pub fn append(&mut self, newer: Self) {
|
||||
let Self {
|
||||
platform_output,
|
||||
needs_repaint,
|
||||
repaint_after,
|
||||
textures_delta,
|
||||
shapes,
|
||||
} = newer;
|
||||
|
||||
self.platform_output.append(platform_output);
|
||||
self.needs_repaint = needs_repaint; // if the last frame doesn't need a repaint, then we don't need to repaint
|
||||
self.repaint_after = repaint_after; // if the last frame doesn't need a repaint, then we don't need to repaint
|
||||
self.textures_delta.append(textures_delta);
|
||||
self.shapes = shapes; // Only paint the latest
|
||||
}
|
||||
@@ -49,7 +54,7 @@ impl FullOutput {
|
||||
/// You can access (and modify) this with [`crate::Context::output`].
|
||||
///
|
||||
/// The backend should use this.
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct PlatformOutput {
|
||||
/// Set the cursor to this icon.
|
||||
|
||||
Reference in New Issue
Block a user