Skip to content

Commit 4292e1f

Browse files
committed
perf: detect system theme off UI thread
1 parent 87ba5b7 commit 4292e1f

1 file changed

Lines changed: 63 additions & 14 deletions

File tree

src/app.rs

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,32 +70,78 @@ struct ThemeState {
7070
preference: ThemeMode,
7171
resolved: ThemeMode,
7272
last_applied: Option<ThemeMode>,
73-
last_checked: Instant,
73+
last_checked: Option<Instant>,
74+
detection_in_flight: bool,
75+
detection_generation: u64,
76+
detection_sender: mpsc::Sender<(u64, Option<ThemeMode>)>,
77+
detection_receiver: mpsc::Receiver<(u64, Option<ThemeMode>)>,
7478
}
7579

7680
impl ThemeState {
7781
fn new(preference: ThemeMode) -> Self {
82+
let (detection_sender, detection_receiver) = mpsc::channel();
7883
Self {
7984
resolved: crate::ui::theme::resolve_theme_mode(preference),
8085
last_applied: None,
81-
last_checked: Instant::now(),
86+
last_checked: Some(Instant::now()),
87+
detection_in_flight: false,
88+
detection_generation: 0,
89+
detection_sender,
90+
detection_receiver,
8291
preference,
8392
}
8493
}
8594

86-
/// Polls the OS for system theme changes (throttled to every 2s) and
87-
/// applies the theme if it changed. Returns `true` if the theme was applied.
95+
fn drain_detection_results(&mut self) {
96+
while let Ok((generation, detected)) = self.detection_receiver.try_recv() {
97+
if generation != self.detection_generation {
98+
continue;
99+
}
100+
101+
self.detection_in_flight = false;
102+
if self.preference != ThemeMode::System {
103+
continue;
104+
}
105+
if let Some(detected) = detected
106+
&& detected != self.resolved
107+
{
108+
self.resolved = detected;
109+
}
110+
}
111+
}
112+
113+
fn request_system_detection(&mut self, ctx: &egui::Context, now: Instant) {
114+
if self.detection_in_flight {
115+
return;
116+
}
117+
if self
118+
.last_checked
119+
.is_some_and(|last_checked| now.duration_since(last_checked) < Duration::from_secs(2))
120+
{
121+
return;
122+
}
123+
124+
self.last_checked = Some(now);
125+
self.detection_in_flight = true;
126+
let sender = self.detection_sender.clone();
127+
let generation = self.detection_generation;
128+
let ctx = ctx.clone();
129+
tokio::task::spawn_blocking(move || {
130+
let detected = crate::ui::theme::try_detect_system_theme();
131+
if sender.send((generation, detected)).is_err() {
132+
tracing::debug!("Dropping OS theme detection result because receiver closed");
133+
}
134+
ctx.request_repaint();
135+
});
136+
}
137+
138+
/// Polls asynchronously for OS theme changes (throttled to every 2s) and
139+
/// applies the latest detected theme if it changed. Returns `true` if the
140+
/// theme was applied.
88141
fn poll_and_apply(&mut self, ctx: &egui::Context) -> bool {
142+
self.drain_detection_results();
89143
if self.preference == ThemeMode::System {
90-
let now = Instant::now();
91-
if now.duration_since(self.last_checked) >= Duration::from_secs(2) {
92-
self.last_checked = now;
93-
if let Some(detected) = crate::ui::theme::try_detect_system_theme()
94-
&& detected != self.resolved
95-
{
96-
self.resolved = detected;
97-
}
98-
}
144+
self.request_system_detection(ctx, Instant::now());
99145
}
100146
if self.last_applied != Some(self.resolved) {
101147
crate::ui::theme::apply_theme(ctx, self.resolved);
@@ -108,6 +154,9 @@ impl ThemeState {
108154

109155
fn apply_new_preference(&mut self, ctx: &egui::Context, new_theme: ThemeMode) -> bool {
110156
self.preference = new_theme;
157+
self.detection_generation = self.detection_generation.wrapping_add(1);
158+
self.detection_in_flight = false;
159+
let now = Instant::now();
111160
let mut detection_failed = false;
112161
self.resolved = if new_theme == ThemeMode::System {
113162
match crate::ui::theme::try_detect_system_theme() {
@@ -120,7 +169,7 @@ impl ThemeState {
120169
} else {
121170
new_theme
122171
};
123-
self.last_checked = Instant::now();
172+
self.last_checked = Some(now);
124173
crate::ui::theme::apply_theme(ctx, self.resolved);
125174
self.last_applied = Some(self.resolved);
126175
detection_failed

0 commit comments

Comments
 (0)