mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-29 00:24:46 +01:00
feat: allow to launch coco across multi monitors (#135)
* feat: allow to launch coco across multi monitors * chore: add missing commits * fix: fix build and default style
This commit is contained in:
@@ -18,7 +18,7 @@ use reqwest::Client;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::ActivationPolicy;
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow, WindowEvent};
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, PhysicalPosition, Runtime, WebviewWindow, Window, WindowEvent};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tokio::runtime::Runtime as RT;
|
||||
@@ -106,8 +106,7 @@ pub fn run() {
|
||||
dbg!("Async initialization tasks completed");
|
||||
});
|
||||
|
||||
|
||||
shortcut::enable_shortcut(app);
|
||||
shortcut::enable_shortcut(&app);
|
||||
enable_tray(app);
|
||||
enable_autostart(app);
|
||||
|
||||
@@ -181,18 +180,30 @@ pub async fn init<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
registry.register_source(source).await;
|
||||
}
|
||||
|
||||
// Clone app_handle to move it into the background task
|
||||
let app_handle_clone = app_handle.clone();
|
||||
|
||||
let dir = vec![
|
||||
dirs::home_dir().map(|home| home.join("Applications")), // Resolve `~/Applications`
|
||||
Some(PathBuf::from("/Applications")),
|
||||
Some(PathBuf::from("/System/Applications")),
|
||||
Some(PathBuf::from("/System/Applications/Utilities")),
|
||||
];
|
||||
// Run the slow application directory search in the background
|
||||
tokio::spawn(async move {
|
||||
dbg!("Initializing application search source in background");
|
||||
let dir = vec![
|
||||
dirs::home_dir().map(|home| home.join("Applications")), // Resolve `~/Applications`
|
||||
Some(PathBuf::from("/Applications")),
|
||||
Some(PathBuf::from("/System/Applications")),
|
||||
Some(PathBuf::from("/System/Applications/Utilities")),
|
||||
];
|
||||
|
||||
// Remove any `None` values if `home_dir()` fails
|
||||
let app_dirs: Vec<PathBuf> = dir.into_iter().flatten().collect();
|
||||
let application_search = local::application::ApplicationSearchSource::new(1000f64, app_dirs);
|
||||
registry.register_source(application_search).await;
|
||||
// Remove any `None` values if `home_dir()` fails
|
||||
let app_dirs: Vec<PathBuf> = dir.into_iter().flatten().collect();
|
||||
|
||||
let application_search = local::application::ApplicationSearchSource::new(1000f64, app_dirs);
|
||||
|
||||
// Register the application search source
|
||||
let registry = app_handle_clone.state::<SearchSourceRegistry>();
|
||||
registry.register_source(application_search).await;
|
||||
|
||||
dbg!("Application search source initialized in background");
|
||||
});
|
||||
|
||||
dbg!("Initialization completed");
|
||||
}
|
||||
@@ -226,6 +237,8 @@ fn handle_open_coco(app: &AppHandle) {
|
||||
println!("Open Coco menu clicked!");
|
||||
|
||||
if let Some(window) = app.get_window(MAIN_WINDOW_LABEL) {
|
||||
move_window_to_active_monitor(&window);
|
||||
|
||||
window.show().unwrap();
|
||||
window.set_visible_on_all_workspaces(true).unwrap();
|
||||
window.set_always_on_top(true).unwrap();
|
||||
@@ -235,6 +248,81 @@ fn handle_open_coco(app: &AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
fn move_window_to_active_monitor<R: Runtime>(window: &Window<R>) {
|
||||
dbg!("Moving window to active monitor");
|
||||
// Try to get the available monitors, handle failure gracefully
|
||||
let available_monitors = match window.available_monitors() {
|
||||
Ok(monitors) => monitors,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to get monitors: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Attempt to get the cursor position, handle failure gracefully
|
||||
let cursor_position = match window.cursor_position() {
|
||||
Ok(pos) => Some(pos),
|
||||
Err(e) => {
|
||||
eprintln!("Failed to get cursor position: {}", e);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// Find the monitor that contains the cursor or default to the primary monitor
|
||||
let target_monitor = if let Some(cursor_position) = cursor_position {
|
||||
// Convert cursor position to integers
|
||||
let cursor_x = cursor_position.x.round() as i32;
|
||||
let cursor_y = cursor_position.y.round() as i32;
|
||||
|
||||
// Find the monitor that contains the cursor
|
||||
available_monitors.into_iter().find(|monitor| {
|
||||
let monitor_position = monitor.position();
|
||||
let monitor_size = monitor.size();
|
||||
|
||||
cursor_x >= monitor_position.x
|
||||
&& cursor_x <= monitor_position.x + monitor_size.width as i32
|
||||
&& cursor_y >= monitor_position.y
|
||||
&& cursor_y <= monitor_position.y + monitor_size.height as i32
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Use the target monitor or default to the primary monitor
|
||||
let monitor = match target_monitor.or_else(|| window.primary_monitor().ok().flatten()) {
|
||||
Some(monitor) => monitor,
|
||||
None => {
|
||||
eprintln!("No monitor found!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let monitor_position = monitor.position();
|
||||
let monitor_size = monitor.size();
|
||||
|
||||
// Get the current size of the window
|
||||
let window_size = match window.inner_size() {
|
||||
Ok(size) => size,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to get window size: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let window_width = window_size.width as i32;
|
||||
let window_height = window_size.height as i32;
|
||||
|
||||
// Calculate the new position to center the window on the monitor
|
||||
let window_x = monitor_position.x + (monitor_size.width as i32 - window_width) / 2;
|
||||
let window_y = monitor_position.y + (monitor_size.height as i32 - window_height) / 2;
|
||||
|
||||
// Move the window to the new position
|
||||
if let Err(e) = window.set_position(PhysicalPosition::new(window_x, window_y)) {
|
||||
eprintln!("Failed to move window: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn handle_hide_coco(app: &AppHandle) {
|
||||
// println!("Hide Coco menu clicked!");
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
coco_lib::run();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::COCO_TAURI_STORE;
|
||||
use crate::{move_window_to_active_monitor, COCO_TAURI_STORE};
|
||||
use tauri::App;
|
||||
use tauri::AppHandle;
|
||||
use tauri::Manager;
|
||||
@@ -99,18 +99,22 @@ pub fn change_shortcut<R: Runtime>(
|
||||
|
||||
/// Helper function to register a shortcut, used for shortcut updates.
|
||||
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| {
|
||||
.on_shortcut(shortcut, move |app, scut, event| {
|
||||
if scut == &shortcut {
|
||||
dbg!("shortcut pressed");
|
||||
let main_window = app.get_window(MAIN_WINDOW_LABEL).unwrap();
|
||||
if let ShortcutState::Pressed = event.state() {
|
||||
if main_window.is_visible().unwrap() {
|
||||
dbg!("hiding window");
|
||||
main_window.hide().unwrap();
|
||||
} else {
|
||||
main_window.show().unwrap();
|
||||
dbg!("showing window");
|
||||
move_window_to_active_monitor(&main_window);
|
||||
main_window.set_visible_on_all_workspaces(true).unwrap();
|
||||
main_window.set_always_on_top(true).unwrap();
|
||||
main_window.set_focus().unwrap();
|
||||
main_window.show().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,21 +127,23 @@ use crate::common::MAIN_WINDOW_LABEL;
|
||||
|
||||
/// Helper function to register a shortcut, used to set up the shortcut up App's first start.
|
||||
fn _register_shortcut_upon_start(app: &App, shortcut: Shortcut) {
|
||||
let window = app.get_webview_window(MAIN_WINDOW_LABEL).unwrap();
|
||||
app.handle()
|
||||
let handler = app.app_handle();
|
||||
handler
|
||||
.plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(move |app, scut, event| {
|
||||
if scut == &shortcut {
|
||||
let window = app.get_window(MAIN_WINDOW_LABEL).unwrap();
|
||||
if let ShortcutState::Pressed = event.state() {
|
||||
if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
} else {
|
||||
dbg!("showing window");
|
||||
window.show().unwrap();
|
||||
move_window_to_active_monitor(&window);
|
||||
window.set_visible_on_all_workspaces(true).unwrap();
|
||||
window.set_always_on_top(true).unwrap();
|
||||
window.set_focus().unwrap();
|
||||
window.show().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,14 +71,7 @@
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": [
|
||||
"nsis",
|
||||
"dmg",
|
||||
"app",
|
||||
"appimage",
|
||||
"deb",
|
||||
"rpm"
|
||||
],
|
||||
"targets": "all",
|
||||
"shortDescription": "Coco AI",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
@@ -103,11 +96,11 @@
|
||||
"dmg": {
|
||||
"appPosition": {
|
||||
"x": 180,
|
||||
"y": 140
|
||||
"y": 180
|
||||
},
|
||||
"applicationFolderPosition": {
|
||||
"x": 480,
|
||||
"y": 140
|
||||
"y": 180
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user