304 lines
11 KiB
Rust
304 lines
11 KiB
Rust
use crate::screenshot::take_area_screenshot;
|
|
use tauri::App;
|
|
use tauri::AppHandle;
|
|
use tauri::Emitter;
|
|
use tauri::Manager;
|
|
use tauri::Runtime;
|
|
use tauri_plugin_global_shortcut::GlobalShortcutExt;
|
|
use tauri_plugin_global_shortcut::Shortcut;
|
|
use tauri_plugin_global_shortcut::ShortcutState;
|
|
use tauri_plugin_store::JsonValue;
|
|
use tauri_plugin_store::StoreExt;
|
|
|
|
/// Constants for Tauri store configuration
|
|
const HAYSTACK_TAURI_STORE: &str = "haystack_tauri_store"; // Name of the persistent store
|
|
const HAYSTACK_GLOBAL_SHORTCUT: &str = "haystack_global_shortcut"; // Key for storing the toggle window shortcut
|
|
const HAYSTACK_SCREENSHOT_SHORTCUT: &str = "haystack_screenshot_shortcut"; // Key for storing the screenshot shortcut
|
|
|
|
/// Platform-specific default shortcuts
|
|
#[cfg(target_os = "macos")]
|
|
const DEFAULT_SHORTCUT: &str = "command+shift+k"; // macOS uses Command key
|
|
#[cfg(target_os = "macos")]
|
|
const DEFAULT_SCREENSHOT_SHORTCUT: &str = "command+shift+p"; // macOS screenshot shortcut
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
const DEFAULT_SHORTCUT: &str = "ctrl+shift+k"; // Windows/Linux use Ctrl key
|
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
|
const DEFAULT_SCREENSHOT_SHORTCUT: &str = "ctrl+shift+p"; // Windows/Linux screenshot shortcut
|
|
|
|
/// Initializes the global shortcut during application startup.
|
|
/// This function:
|
|
/// 1. Checks if a shortcut is already stored
|
|
/// 2. Uses the stored shortcut if it exists
|
|
/// 3. Falls back to the platform-specific default if no shortcut is stored
|
|
/// 4. Registers the shortcut with the system
|
|
pub fn enable_shortcut(app: &App) {
|
|
// Get or create the persistent store
|
|
let store = app
|
|
.store(HAYSTACK_TAURI_STORE)
|
|
.expect("Creating the store should not fail");
|
|
|
|
// Initialize toggle window shortcut
|
|
let toggle_shortcut = if let Some(stored_shortcut) = store.get(HAYSTACK_GLOBAL_SHORTCUT) {
|
|
let stored_shortcut_str = match stored_shortcut {
|
|
JsonValue::String(str) => str,
|
|
unexpected_type => panic!(
|
|
"Haystack shortcuts should be stored as strings, found type: {} ",
|
|
unexpected_type
|
|
),
|
|
};
|
|
stored_shortcut_str
|
|
.parse::<Shortcut>()
|
|
.expect("Stored shortcut string should be valid")
|
|
} else {
|
|
store.set(
|
|
HAYSTACK_GLOBAL_SHORTCUT,
|
|
JsonValue::String(DEFAULT_SHORTCUT.to_string()),
|
|
);
|
|
DEFAULT_SHORTCUT
|
|
.parse::<Shortcut>()
|
|
.expect("Default shortcut should be valid")
|
|
};
|
|
|
|
// Initialize screenshot shortcut
|
|
let screenshot_shortcut = if let Some(stored_shortcut) = store.get(HAYSTACK_SCREENSHOT_SHORTCUT)
|
|
{
|
|
let stored_shortcut_str = match stored_shortcut {
|
|
JsonValue::String(str) => str,
|
|
unexpected_type => panic!(
|
|
"Haystack shortcuts should be stored as strings, found type: {} ",
|
|
unexpected_type
|
|
),
|
|
};
|
|
stored_shortcut_str
|
|
.parse::<Shortcut>()
|
|
.expect("Stored shortcut string should be valid")
|
|
} else {
|
|
store.set(
|
|
HAYSTACK_SCREENSHOT_SHORTCUT,
|
|
JsonValue::String(DEFAULT_SCREENSHOT_SHORTCUT.to_string()),
|
|
);
|
|
DEFAULT_SCREENSHOT_SHORTCUT
|
|
.parse::<Shortcut>()
|
|
.expect("Default screenshot shortcut should be valid")
|
|
};
|
|
|
|
// Register both shortcuts
|
|
register_shortcut_upon_start(app, toggle_shortcut, screenshot_shortcut);
|
|
}
|
|
|
|
/// Returns the currently configured shortcut as a string.
|
|
/// This is exposed as a Tauri command for the frontend to query.
|
|
#[tauri::command]
|
|
pub fn get_current_shortcut<R: Runtime>(app: AppHandle<R>) -> Result<String, String> {
|
|
Ok(get_shortcut_from_store(&app))
|
|
}
|
|
|
|
/// Unregisters the current global shortcut from the system.
|
|
/// This is exposed as a Tauri command for the frontend to trigger.
|
|
#[tauri::command]
|
|
pub fn unregister_shortcut<R: Runtime>(app: AppHandle<R>) {
|
|
let shortcut_str = get_shortcut_from_store(&app);
|
|
let shortcut = shortcut_str
|
|
.parse::<Shortcut>()
|
|
.expect("Stored shortcut string should be valid");
|
|
|
|
app.global_shortcut()
|
|
.unregister(shortcut)
|
|
.expect("Failed to unregister shortcut")
|
|
}
|
|
|
|
/// Changes the global shortcut to a new key combination.
|
|
/// This function:
|
|
/// 1. Validates the new shortcut
|
|
/// 2. Stores it in the persistent store
|
|
/// 3. Registers it with the system
|
|
#[tauri::command]
|
|
pub fn change_shortcut<R: Runtime>(
|
|
app: AppHandle<R>,
|
|
_window: tauri::Window<R>,
|
|
key: String,
|
|
) -> Result<(), String> {
|
|
println!("Key: {}", key);
|
|
let shortcut = match key.parse::<Shortcut>() {
|
|
Ok(shortcut) => shortcut,
|
|
Err(_) => return Err(format!("Invalid shortcut {}", key)),
|
|
};
|
|
|
|
// Store the new shortcut
|
|
let store = app
|
|
.get_store(HAYSTACK_TAURI_STORE)
|
|
.expect("Store should already be loaded or created");
|
|
store.set(HAYSTACK_GLOBAL_SHORTCUT, JsonValue::String(key));
|
|
|
|
// Register the new shortcut
|
|
register_shortcut(&app, shortcut);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Handles the window visibility toggle logic when the shortcut is pressed.
|
|
/// This function:
|
|
/// 1. Hides the window if it's visible
|
|
/// 2. Shows and focuses the window if it's hidden
|
|
/// 3. Emits a 'focus-search' event when showing the window
|
|
fn handle_window_visibility<R: Runtime>(app: &AppHandle<R>, window: &tauri::WebviewWindow<R>) {
|
|
if window.is_visible().unwrap() {
|
|
window.hide().unwrap();
|
|
} else {
|
|
window.show().unwrap();
|
|
window.set_focus().unwrap();
|
|
app.emit("focus-search", ()).unwrap();
|
|
}
|
|
}
|
|
|
|
/// Registers a new shortcut with the system.
|
|
/// This is used when changing shortcuts during runtime.
|
|
/// The function:
|
|
/// 1. Gets the main window
|
|
/// 2. Sets up the shortcut handler
|
|
/// 3. Registers the shortcut with the system
|
|
fn register_shortcut<R: Runtime>(app: &AppHandle<R>, shortcut: Shortcut) {
|
|
let main_window = app.get_webview_window("main").unwrap();
|
|
|
|
app.global_shortcut()
|
|
.on_shortcut(shortcut, move |app, scut, event| {
|
|
if scut == &shortcut {
|
|
if let ShortcutState::Pressed = event.state() {
|
|
handle_window_visibility(app, &main_window);
|
|
}
|
|
}
|
|
})
|
|
.map_err(|err| format!("Failed to register new shortcut '{}'", err))
|
|
.unwrap();
|
|
}
|
|
|
|
/// Registers a shortcut during application startup.
|
|
/// This is similar to register_shortcut but handles the initial plugin setup.
|
|
/// The function:
|
|
/// 1. Gets the main window
|
|
/// 2. Sets up the global shortcut plugin
|
|
/// 3. Registers the shortcut with the system
|
|
fn register_shortcut_upon_start(
|
|
app: &App,
|
|
toggle_shortcut: Shortcut,
|
|
screenshot_shortcut: Shortcut,
|
|
) {
|
|
let window = app
|
|
.get_webview_window("main")
|
|
.expect("webview to be defined");
|
|
|
|
app.handle()
|
|
.plugin(
|
|
tauri_plugin_global_shortcut::Builder::new()
|
|
.with_handler(move |app, scut, event| {
|
|
if scut == &toggle_shortcut {
|
|
if let ShortcutState::Pressed = event.state() {
|
|
handle_window_visibility(app, &window);
|
|
}
|
|
} else if scut == &screenshot_shortcut {
|
|
if let ShortcutState::Pressed = event.state() {
|
|
// TODO: Implement screenshot functionality
|
|
println!("Screenshot shortcut pressed");
|
|
}
|
|
if let Err(e) = take_area_screenshot(app) {
|
|
println!("Failed to take screenshot: {}", e);
|
|
}
|
|
}
|
|
})
|
|
.build(),
|
|
)
|
|
.unwrap();
|
|
app.global_shortcut().register(toggle_shortcut).unwrap();
|
|
app.global_shortcut().register(screenshot_shortcut).unwrap();
|
|
}
|
|
|
|
/// Retrieves the currently stored shortcut from the persistent store.
|
|
/// This is a helper function used by other functions to access the stored shortcut.
|
|
fn get_shortcut_from_store<R: Runtime>(app: &AppHandle<R>) -> String {
|
|
let store = app
|
|
.get_store(HAYSTACK_TAURI_STORE)
|
|
.expect("Store should already be loaded or created");
|
|
|
|
match store
|
|
.get(HAYSTACK_GLOBAL_SHORTCUT)
|
|
.expect("Shortcut should already be stored")
|
|
{
|
|
JsonValue::String(str) => str,
|
|
unexpected_type => panic!(
|
|
"Haystack shortcuts should be stored as strings, found type: {} ",
|
|
unexpected_type
|
|
),
|
|
}
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn get_current_screenshot_shortcut<R: Runtime>(app: AppHandle<R>) -> Result<String, String> {
|
|
Ok(get_screenshot_shortcut_from_store(&app))
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn unregister_screenshot_shortcut<R: Runtime>(app: AppHandle<R>) {
|
|
let shortcut_str = get_screenshot_shortcut_from_store(&app);
|
|
let shortcut = shortcut_str
|
|
.parse::<Shortcut>()
|
|
.expect("Stored shortcut string should be valid");
|
|
|
|
app.global_shortcut()
|
|
.unregister(shortcut)
|
|
.expect("Failed to unregister screenshot shortcut")
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn change_screenshot_shortcut<R: Runtime>(
|
|
app: AppHandle<R>,
|
|
_window: tauri::Window<R>,
|
|
key: String,
|
|
) -> Result<(), String> {
|
|
let shortcut = match key.parse::<Shortcut>() {
|
|
Ok(shortcut) => shortcut,
|
|
Err(_) => return Err(format!("Invalid screenshot shortcut {}", key)),
|
|
};
|
|
|
|
let store = app
|
|
.get_store(HAYSTACK_TAURI_STORE)
|
|
.expect("Store should already be loaded or created");
|
|
store.set(HAYSTACK_SCREENSHOT_SHORTCUT, JsonValue::String(key));
|
|
|
|
register_screenshot_shortcut(&app, shortcut);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn register_screenshot_shortcut<R: Runtime>(app: &AppHandle<R>, shortcut: Shortcut) {
|
|
app.global_shortcut()
|
|
.on_shortcut(shortcut, move |app, scut, event| {
|
|
if scut == &shortcut {
|
|
if let ShortcutState::Pressed = event.state() {
|
|
if let Err(e) = take_area_screenshot(app) {
|
|
println!("Failed to take screenshot: {}", e);
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.map_err(|err| format!("Failed to register new screenshot shortcut '{}'", err))
|
|
.unwrap();
|
|
}
|
|
|
|
fn get_screenshot_shortcut_from_store<R: Runtime>(app: &AppHandle<R>) -> String {
|
|
let store = app
|
|
.get_store(HAYSTACK_TAURI_STORE)
|
|
.expect("Store should already be loaded or created");
|
|
|
|
match store
|
|
.get(HAYSTACK_SCREENSHOT_SHORTCUT)
|
|
.expect("Screenshot shortcut should already be stored")
|
|
{
|
|
JsonValue::String(str) => str,
|
|
unexpected_type => panic!(
|
|
"Haystack shortcuts should be stored as strings, found type: {} ",
|
|
unexpected_type
|
|
),
|
|
}
|
|
}
|