use std::time::{Duration, Instant};
use tracing::{debug, trace};
#[derive(Debug, Clone, PartialEq)]
pub enum RenderReason {
Initial,
UserInput,
SearchUpdate,
NavigationChange,
DataChange,
WindowResize,
PeriodicRefresh,
DebouncedAction,
}
pub struct RenderState {
dirty: bool,
dirty_reason: Option<RenderReason>,
last_render: Instant,
min_render_interval: Duration,
force_render: bool,
high_frequency_mode: bool,
}
impl Default for RenderState {
fn default() -> Self {
Self::new()
}
}
impl RenderState {
#[must_use]
pub fn new() -> Self {
Self {
dirty: true, dirty_reason: Some(RenderReason::Initial),
last_render: Instant::now(),
min_render_interval: Duration::from_millis(16), force_render: false,
high_frequency_mode: false,
}
}
pub fn mark_dirty(&mut self, reason: RenderReason) {
if !self.dirty {
debug!("Marking render state dirty: {:?}", reason);
}
self.dirty = true;
self.dirty_reason = Some(reason);
}
pub fn needs_render(&self) -> bool {
if self.force_render {
return true;
}
if !self.dirty {
return false;
}
let elapsed = self.last_render.elapsed();
if elapsed < self.min_render_interval && !self.high_frequency_mode {
trace!("Skipping render, only {:?} elapsed", elapsed);
return false;
}
true
}
pub fn rendered(&mut self) {
trace!("Render completed, reason was: {:?}", self.dirty_reason);
self.dirty = false;
self.dirty_reason = None;
self.last_render = Instant::now();
self.force_render = false;
}
pub fn force_render(&mut self) {
debug!("Forcing render on next check");
self.force_render = true;
self.dirty = true;
}
pub fn set_high_frequency_mode(&mut self, enabled: bool) {
if self.high_frequency_mode != enabled {
debug!("High-frequency render mode: {}", enabled);
self.high_frequency_mode = enabled;
if enabled {
self.min_render_interval = Duration::from_millis(8); } else {
self.min_render_interval = Duration::from_millis(16); }
}
}
#[must_use]
pub fn dirty_reason(&self) -> Option<&RenderReason> {
self.dirty_reason.as_ref()
}
#[must_use]
pub fn is_dirty(&self) -> bool {
self.dirty
}
}
impl RenderState {
pub fn on_navigation_change(&mut self) {
self.mark_dirty(RenderReason::NavigationChange);
}
pub fn on_search_update(&mut self) {
self.mark_dirty(RenderReason::SearchUpdate);
self.force_render = true;
}
pub fn on_data_change(&mut self) {
self.mark_dirty(RenderReason::DataChange);
}
pub fn on_user_input(&mut self) {
self.mark_dirty(RenderReason::UserInput);
}
pub fn on_window_resize(&mut self) {
self.mark_dirty(RenderReason::WindowResize);
self.force_render = true;
}
pub fn on_debounced_action(&mut self) {
self.mark_dirty(RenderReason::DebouncedAction);
self.force_render = true;
}
}