sql_cli/ui/rendering/
render_state.rs1use std::time::{Duration, Instant};
7use tracing::{debug, trace};
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum RenderReason {
12 Initial,
14 UserInput,
16 SearchUpdate,
18 NavigationChange,
20 DataChange,
22 WindowResize,
24 PeriodicRefresh,
26 DebouncedAction,
28}
29
30pub struct RenderState {
32 dirty: bool,
34 dirty_reason: Option<RenderReason>,
36 last_render: Instant,
38 min_render_interval: Duration,
40 force_render: bool,
42 high_frequency_mode: bool,
44}
45
46impl RenderState {
47 pub fn new() -> Self {
49 Self {
50 dirty: true, dirty_reason: Some(RenderReason::Initial),
52 last_render: Instant::now(),
53 min_render_interval: Duration::from_millis(16), force_render: false,
55 high_frequency_mode: false,
56 }
57 }
58
59 pub fn mark_dirty(&mut self, reason: RenderReason) {
61 if !self.dirty {
62 debug!("Marking render state dirty: {:?}", reason);
63 }
64 self.dirty = true;
65 self.dirty_reason = Some(reason);
66 }
67
68 pub fn needs_render(&self) -> bool {
70 if self.force_render {
71 return true;
72 }
73
74 if !self.dirty {
75 return false;
76 }
77
78 let elapsed = self.last_render.elapsed();
80 if elapsed < self.min_render_interval && !self.high_frequency_mode {
81 trace!("Skipping render, only {:?} elapsed", elapsed);
82 return false;
83 }
84
85 true
86 }
87
88 pub fn rendered(&mut self) {
90 trace!("Render completed, reason was: {:?}", self.dirty_reason);
91 self.dirty = false;
92 self.dirty_reason = None;
93 self.last_render = Instant::now();
94 self.force_render = false;
95 }
96
97 pub fn force_render(&mut self) {
99 debug!("Forcing render on next check");
100 self.force_render = true;
101 self.dirty = true;
102 }
103
104 pub fn set_high_frequency_mode(&mut self, enabled: bool) {
106 if self.high_frequency_mode != enabled {
107 debug!("High-frequency render mode: {}", enabled);
108 self.high_frequency_mode = enabled;
109 if enabled {
110 self.min_render_interval = Duration::from_millis(8); } else {
113 self.min_render_interval = Duration::from_millis(16); }
115 }
116 }
117
118 pub fn dirty_reason(&self) -> Option<&RenderReason> {
120 self.dirty_reason.as_ref()
121 }
122
123 pub fn is_dirty(&self) -> bool {
125 self.dirty
126 }
127}
128
129impl RenderState {
131 pub fn on_navigation_change(&mut self) {
133 self.mark_dirty(RenderReason::NavigationChange);
134 }
135
136 pub fn on_search_update(&mut self) {
138 self.mark_dirty(RenderReason::SearchUpdate);
139 self.force_render = true;
141 }
142
143 pub fn on_data_change(&mut self) {
145 self.mark_dirty(RenderReason::DataChange);
146 }
147
148 pub fn on_user_input(&mut self) {
150 self.mark_dirty(RenderReason::UserInput);
151 }
152
153 pub fn on_window_resize(&mut self) {
155 self.mark_dirty(RenderReason::WindowResize);
156 self.force_render = true;
157 }
158
159 pub fn on_debounced_action(&mut self) {
161 self.mark_dirty(RenderReason::DebouncedAction);
162 self.force_render = true;
164 }
165}