68 lines
2.9 KiB
Rust
68 lines
2.9 KiB
Rust
|
|
//! Глобальные горячие клавиши (docs/client-tauri.md §5).
|
||
|
|
//!
|
||
|
|
//! Push-to-talk работает при неактивном окне: системный перехват ловит нажатие
|
||
|
|
//! и отпускание выбранной комбинации и передаёт состояние в веб-клиент, а тот
|
||
|
|
//! включает микрофон (в самом клиенте должен быть выбран режим «Push-to-talk»).
|
||
|
|
//! Дополнительно доступны переключение микрофона и «не слышу».
|
||
|
|
|
||
|
|
use tauri::{AppHandle, Manager, Runtime};
|
||
|
|
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState};
|
||
|
|
|
||
|
|
use crate::bridge::{self, WebMessage};
|
||
|
|
use crate::settings::Settings;
|
||
|
|
use crate::state::AppState;
|
||
|
|
|
||
|
|
/// Обработчик нажатий: плагин вызывает его для любой зарегистрированной комбинации.
|
||
|
|
pub fn on_shortcut<R: Runtime>(app: &AppHandle<R>, shortcut: &Shortcut, event: tauri_plugin_global_shortcut::ShortcutEvent) {
|
||
|
|
let settings = app.state::<AppState>().settings();
|
||
|
|
let pressed = event.state == ShortcutState::Pressed;
|
||
|
|
|
||
|
|
let ptt = parse(&settings.ptt_shortcut);
|
||
|
|
if ptt.as_ref() == Some(shortcut) {
|
||
|
|
bridge::send(app, &WebMessage::Ptt { active: pressed });
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Остальные шорткаты срабатывают по нажатию, чтобы не дублировать действие.
|
||
|
|
if !pressed {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if parse(&settings.mute_shortcut).as_ref() == Some(shortcut) {
|
||
|
|
bridge::send(app, &WebMessage::ToggleMic);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if parse(&settings.deafen_shortcut).as_ref() == Some(shortcut) {
|
||
|
|
bridge::send(app, &WebMessage::ToggleDeafen);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Перерегистрирует комбинации после изменения настроек.
|
||
|
|
pub fn apply<R: Runtime>(app: &AppHandle<R>, settings: &Settings) -> Result<(), String> {
|
||
|
|
let shortcuts = app.global_shortcut();
|
||
|
|
shortcuts
|
||
|
|
.unregister_all()
|
||
|
|
.map_err(|error| format!("не удалось снять старые шорткаты: {error}"))?;
|
||
|
|
|
||
|
|
for spec in [
|
||
|
|
&settings.ptt_shortcut,
|
||
|
|
&settings.mute_shortcut,
|
||
|
|
&settings.deafen_shortcut,
|
||
|
|
] {
|
||
|
|
if spec.trim().is_empty() {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
shortcuts
|
||
|
|
.register(spec.as_str())
|
||
|
|
.map_err(|error| format!("комбинация «{spec}» не зарегистрирована: {error}"))?;
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Разбирает строку комбинации; пустая строка и мусор дают `None`.
|
||
|
|
fn parse(spec: &str) -> Option<Shortcut> {
|
||
|
|
let trimmed = spec.trim();
|
||
|
|
if trimmed.is_empty() {
|
||
|
|
return None;
|
||
|
|
}
|
||
|
|
trimmed.parse::<Shortcut>().ok()
|
||
|
|
}
|