soma_tauri_shell/blur.rs
1//! Blur-dismiss state and generic window-lifecycle event helpers.
2//!
3//! A launcher-style window commonly wants two behaviors: hide (rather than
4//! quit) when the user clicks the close button, and optionally hide when it
5//! loses focus ("click away to dismiss"). This module owns the runtime gate
6//! and decision logic; the app wires it into `on_window_event`.
7
8use std::sync::atomic::{AtomicBool, Ordering};
9
10use tauri::{Manager, Window};
11
12/// Runtime gate for blur-dismiss, toggled at runtime (e.g. while a result or
13/// settings view is open, the app may want to suppress hide-on-blur so
14/// resizing or copying from another window doesn't make it vanish).
15pub struct BlurDismissState(AtomicBool);
16
17impl BlurDismissState {
18 #[must_use]
19 pub fn new(initial: bool) -> Self {
20 Self(AtomicBool::new(initial))
21 }
22
23 pub fn set(&self, enabled: bool) {
24 self.0.store(enabled, Ordering::Relaxed);
25 }
26
27 pub fn enabled(&self) -> bool {
28 self.0.load(Ordering::Relaxed)
29 }
30}
31
32impl Default for BlurDismissState {
33 fn default() -> Self {
34 Self::new(true)
35 }
36}
37
38/// Decide whether a focus-lost event should hide the window: both the
39/// runtime gate (`blur_dismiss_enabled`, from [`BlurDismissState::enabled`])
40/// and the user's persisted preference (`hide_on_blur_pref`, product-owned
41/// settings) must allow it.
42#[must_use]
43pub fn should_hide_on_blur(blur_dismiss_enabled: bool, hide_on_blur_pref: bool) -> bool {
44 blur_dismiss_enabled && hide_on_blur_pref
45}
46
47/// `WindowEvent::CloseRequested` handler: prevent the actual close and hide
48/// the window instead, so the app keeps running in the tray.
49pub fn handle_close_requested(window: &Window, api: &tauri::CloseRequestApi) {
50 api.prevent_close();
51 if let Err(err) = window.hide() {
52 tracing::warn!("failed to hide window on close: {err}");
53 }
54}
55
56/// `WindowEvent::Focused(false)` handler: hide `window` when
57/// [`should_hide_on_blur`] allows it. `hide_on_blur_pref` is the caller's
58/// product-owned settings value (not this crate's concern).
59pub fn handle_focus_lost(window: &Window, state: &BlurDismissState, hide_on_blur_pref: bool) {
60 if should_hide_on_blur(state.enabled(), hide_on_blur_pref) {
61 if let Err(err) = window.hide() {
62 tracing::warn!("failed to hide window on focus loss: {err}");
63 }
64 }
65}
66
67/// Convenience: fetch `state`'s [`BlurDismissState`] from `window`'s
68/// `AppHandle` and call [`handle_focus_lost`]. Requires `state` to have been
69/// registered via `app.manage(BlurDismissState::default())` (or similar).
70pub fn handle_focus_lost_from_managed_state(window: &Window, hide_on_blur_pref: bool) {
71 let app = window.app_handle();
72 let state = app.state::<BlurDismissState>();
73 handle_focus_lost(window, &state, hide_on_blur_pref);
74}
75
76#[cfg(test)]
77#[path = "blur_tests.rs"]
78mod tests;