feat(rust): register plugins, global shortcut, commands
This commit is contained in:
@@ -34,12 +34,14 @@ pub async fn apply_state<R: Runtime>(
|
||||
.set_ignore_cursor_events(new_state)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Opacity dip half of the visual pulse. The JS click-through-toggled
|
||||
// Opacity dip — visual pulse to signal the state change.
|
||||
// Tauri 2.11 does not expose set_opacity on WebviewWindow, so we apply
|
||||
// the dip by evaluating JS on the webview. The JS click-through-toggled
|
||||
// handler is responsible for restoring opacity to the user's actual
|
||||
// target — Rust deliberately does NOT call set_opacity again, so there
|
||||
// is no possibility of the global-shortcut path "snapping back" to a
|
||||
// wrong value (we don't have a current-opacity getter).
|
||||
let _ = overlay.set_opacity(0.4);
|
||||
// target — Rust deliberately does NOT set opacity back, so there is no
|
||||
// possibility of the global-shortcut path "snapping back" to a wrong
|
||||
// value (we don't have a current-opacity getter).
|
||||
let _ = overlay.eval("document.documentElement.style.opacity='0.4'");
|
||||
tokio::time::sleep(Duration::from_millis(180)).await;
|
||||
|
||||
app.emit(
|
||||
@@ -102,8 +104,9 @@ pub fn get_hotkey_status(app: AppHandle) -> Result<crate::HotkeyStatus, String>
|
||||
Ok(guard.clone())
|
||||
}
|
||||
|
||||
/// Set window opacity. Tauri 2's JS WebviewWindow does NOT expose setOpacity
|
||||
/// (only the Rust side does), so the JS layer routes through this command.
|
||||
/// Set window opacity. Tauri 2's JS WebviewWindow does NOT expose setOpacity,
|
||||
/// so the JS layer routes through this command. Tauri 2.11 also doesn't expose
|
||||
/// set_opacity on the Rust WebviewWindow, so we apply the change via JS eval.
|
||||
#[tauri::command]
|
||||
pub fn set_window_opacity<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
@@ -113,5 +116,9 @@ pub fn set_window_opacity<R: Runtime>(
|
||||
let Some(window) = app.get_webview_window(&label) else {
|
||||
return Err(format!("window not found: {label}"));
|
||||
};
|
||||
window.set_opacity(opacity).map_err(|e| e.to_string())
|
||||
window
|
||||
.eval(&format!(
|
||||
"document.documentElement.style.opacity='{opacity}'"
|
||||
))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -1,14 +1,95 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
mod commands;
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use serde::Serialize;
|
||||
use tauri::Manager;
|
||||
|
||||
#[cfg(desktop)]
|
||||
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState};
|
||||
|
||||
pub struct AppState {
|
||||
pub click_through: Mutex<bool>,
|
||||
/// Hotkey registration outcome, set during setup. JS pulls this via
|
||||
/// `get_hotkey_status` after the listener-not-yet-registered window is
|
||||
/// safely past, so the result is never lost to a race.
|
||||
pub hotkey_status: Mutex<HotkeyStatus>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Default)]
|
||||
#[serde(tag = "kind", rename_all = "lowercase")]
|
||||
pub enum HotkeyStatus {
|
||||
#[default]
|
||||
Pending,
|
||||
Ok,
|
||||
Failed { error: String },
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.manage(AppState {
|
||||
click_through: Mutex::new(false),
|
||||
hotkey_status: Mutex::new(HotkeyStatus::Pending),
|
||||
})
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
.setup(|app| {
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
app.handle()
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())?;
|
||||
|
||||
let shortcut = Shortcut::new(
|
||||
Some(Modifiers::CONTROL | Modifiers::ALT),
|
||||
Code::Space,
|
||||
);
|
||||
let shortcut_for_handler = shortcut;
|
||||
|
||||
app.handle().plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(move |app, sc, event| {
|
||||
if sc != &shortcut_for_handler {
|
||||
return;
|
||||
}
|
||||
if event.state() != ShortcutState::Pressed {
|
||||
return;
|
||||
}
|
||||
let app_handle = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if let Err(err) =
|
||||
commands::overlay::toggle_via_global_shortcut(&app_handle).await
|
||||
{
|
||||
eprintln!("global shortcut toggle failed: {err}");
|
||||
}
|
||||
});
|
||||
})
|
||||
.build(),
|
||||
)?;
|
||||
|
||||
let registration_result = app.global_shortcut().register(shortcut);
|
||||
let status = match ®istration_result {
|
||||
Ok(()) => HotkeyStatus::Ok,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to register Ctrl+Alt+Space: {e}");
|
||||
HotkeyStatus::Failed {
|
||||
error: "Hotkey unavailable — click-through escape disabled".to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
{
|
||||
let state = app.state::<AppState>();
|
||||
*state.hotkey_status.lock().expect("poisoned") = status;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::overlay::toggle_click_through,
|
||||
commands::overlay::sync_click_through_cache,
|
||||
commands::overlay::get_hotkey_status,
|
||||
commands::overlay::set_window_opacity,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user