use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindow, WebviewWindowBuilder}; #[cfg(target_os = "macos")] pub fn make_transparent(window: &WebviewWindow) -> Result<(), tauri::Error> { use cocoa::{ appkit::NSColor, base::{id, nil}, foundation::NSString, }; use objc::{class, msg_send, runtime::Object, sel, sel_impl}; window.with_webview(|webview| unsafe { let webview_obj = webview.inner() as *mut Object; let no_value: id = msg_send![class!(NSNumber), numberWithBool:0]; let key = NSString::alloc(nil).init_str("drawsBackground"); let _: id = msg_send![webview_obj, setValue:no_value forKey:key]; let _: () = msg_send![key, release]; let ns_window: id = msg_send![webview_obj, window]; let bg_color = NSColor::colorWithSRGBRed_green_blue_alpha_(nil, 0.0, 0.0, 0.0, 0.0); let _: id = msg_send![ns_window, setBackgroundColor: bg_color]; // let _: () = msg_send![ns_window, setIgnoresMouseEvents:true]; })?; Ok(()) } #[cfg(not(target_os = "macos"))] pub fn make_transparent(_window: &WebviewWindow) -> Result<(), tauri::Error> { Ok(()) } #[cfg(target_os = "linux")] fn centered_logical_position( app_handle: &AppHandle, win_width: f64, win_height: f64, ) -> Option<(f64, f64)> { let monitor = app_handle .primary_monitor() .ok() .flatten() .or_else(|| app_handle.available_monitors().ok()?.into_iter().next())?; let scale = monitor.scale_factor(); let size = monitor.size().to_logical::(scale); let pos = monitor.position().to_logical::(scale); let x = pos.x + ((size.width - win_width) / 2.0).max(0.0); let y = pos.y + ((size.height - win_height) / 2.0).max(0.0); Some((x, y)) } #[cfg(target_os = "linux")] pub(crate) fn focus_window_linux(window: &WebviewWindow) { use gtk::prelude::*; if let Ok(gtk_win) = window.gtk_window() { gtk_win.present_with_time(gtk::current_event_time()); } } #[tauri::command] #[allow(unused_variables)] pub async fn open_full_window( app_handle: AppHandle, name: String, url: String, hide_title_bar: Option, ) -> Result<(), String> { if let Some(existing) = app_handle.get_webview_window(&name) { existing.set_focus().map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] focus_window_linux(&existing); return Ok(()); } let os = std::env::consts::OS; let primary_monitor = app_handle .primary_monitor() .map_err(|e| e.to_string())? .or_else(|| app_handle.available_monitors().ok()?.into_iter().next()); let monitor = primary_monitor.ok_or("No monitor found")?; let physical_size = monitor.size(); let scale_factor = monitor.scale_factor(); let logical_size = physical_size.to_logical::(scale_factor); let width = logical_size.width; let mut height = logical_size.height; if os == "windows" { height -= 100.0; } let mut builder = WebviewWindowBuilder::new(&app_handle, &name, WebviewUrl::App(url.into())) .title(&name) .inner_size(width, height) .resizable(true) .visible(false) .focused(false) .decorations(true) .zoom_hotkeys_enabled(false); #[cfg(target_os = "linux")] if let Some((x, y)) = centered_logical_position(&app_handle, width, height) { builder = builder.position(x, y); } #[cfg(target_os = "macos")] if hide_title_bar.unwrap_or(false) { builder = builder.title_bar_style(tauri::TitleBarStyle::Overlay); } let window = builder.build().map_err(|e| e.to_string())?; std::thread::sleep(std::time::Duration::from_millis(750)); #[cfg(not(target_os = "linux"))] window.center().map_err(|e| e.to_string())?; window.set_zoom(1.0).map_err(|e| e.to_string())?; window.show().map_err(|e| e.to_string())?; window.set_focus().map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] focus_window_linux(&window); #[cfg(target_os = "linux")] if let Some((x, y)) = centered_logical_position(&app_handle, width, height) { let _ = window.set_position(tauri::Position::Logical(tauri::LogicalPosition::new(x, y))); } if os != "windows" && os != "macos" { window.set_resizable(false).map_err(|e| e.to_string())?; window.set_resizable(true).map_err(|e| e.to_string())?; } Ok(()) } #[cfg(not(target_os = "linux"))] #[tauri::command] pub async fn open_window( app_handle: AppHandle, name: String, url: String, width: Option, height: Option, ) -> Result<(), String> { if let Some(existing) = app_handle.get_webview_window(&name) { existing.set_focus().map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] focus_window_linux(&existing); return Ok(()); } let os = std::env::consts::OS; let default_height = if os == "windows" { 755.0 } else { 725.0 }; let width = width.unwrap_or(840.0); let height = height.unwrap_or(default_height); let mut builder = WebviewWindowBuilder::new(&app_handle, &name, WebviewUrl::App(url.into())) .title(&name) .inner_size(width, height) .min_inner_size(700.0, 700.0) .max_inner_size(1000.0, 1000.0) .resizable(true) .visible(false) .focused(false) .decorations(true) .zoom_hotkeys_enabled(false); #[cfg(target_os = "macos")] { builder = builder.title_bar_style(tauri::TitleBarStyle::Overlay); } let window = builder.build().map_err(|e| e.to_string())?; std::thread::sleep(std::time::Duration::from_millis(750)); let monitor = window .current_monitor() .map_err(|e| e.to_string())? .or_else(|| app_handle.primary_monitor().ok().flatten()) .ok_or("Monitor not found")?; let screen_size = monitor.size(); let monitor_pos = monitor.position(); let window_size = window.outer_size().map_err(|e| e.to_string())?; let x = monitor_pos.x + (screen_size.width as i32 - window_size.width as i32) / 2; let y = monitor_pos.y + (screen_size.height as i32 - window_size.height as i32) / 2; let count = app_handle.webview_windows().len() as i32; let offset = count * 20; let sign = if count % 2 != 0 { -1 } else { 1 }; let final_offset = offset * sign; window .set_position(tauri::Position::Physical(tauri::PhysicalPosition { x: x + final_offset, y: y + final_offset, })) .map_err(|e| e.to_string())?; window.set_zoom(1.0).map_err(|e| e.to_string())?; window.show().map_err(|e| e.to_string())?; window.set_focus().map_err(|e| e.to_string())?; Ok(()) } #[cfg(target_os = "linux")] #[tauri::command] pub async fn open_window( app_handle: AppHandle, name: String, url: String, width: Option, height: Option, ) -> Result<(), String> { if let Some(existing) = app_handle.get_webview_window(&name) { existing.set_focus().map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] focus_window_linux(&existing); return Ok(()); } let width = width.unwrap_or(840.0); let height = height.unwrap_or(725.0); let mut builder = WebviewWindowBuilder::new(&app_handle, &name, WebviewUrl::App(url.into())) .title(&name) .inner_size(width, height) .min_inner_size(700.0, 700.0) .max_inner_size(1000.0, 1000.0) .resizable(true) .visible(false) .focused(false) .decorations(true) .zoom_hotkeys_enabled(false); if let Some((x, y)) = centered_logical_position(&app_handle, width, height) { builder = builder.position(x, y); } let window = builder.build().map_err(|e| e.to_string())?; window.set_zoom(1.0).map_err(|e| e.to_string())?; window.show().map_err(|e| e.to_string())?; window.set_focus().map_err(|e| e.to_string())?; focus_window_linux(&window); if let Some((x, y)) = centered_logical_position(&app_handle, width, height) { let _ = window.set_position(tauri::Position::Logical(tauri::LogicalPosition::new(x, y))); } window.set_resizable(false).map_err(|e| e.to_string())?; window.set_resizable(true).map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub async fn open_small_window( app_handle: AppHandle, name: String, url: String, ) -> Result<(), String> { if let Some(existing) = app_handle.get_webview_window(&name) { existing.set_focus().map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] focus_window_linux(&existing); return Ok(()); } let os = std::env::consts::OS; let mut builder = WebviewWindowBuilder::new(&app_handle, &name, WebviewUrl::App(url.into())) .title(&name) .inner_size(800.0, 500.0) .resizable(false) .visible(false) .focused(false) .decorations(false) .closable(false) .shadow(false) .zoom_hotkeys_enabled(false); #[cfg(target_os = "linux")] if let Some((x, y)) = centered_logical_position(&app_handle, 800.0, 500.0) { builder = builder.position(x, y); } #[cfg(target_os = "linux")] { builder = builder.transparent(true); } let window = builder.build().map_err(|e| e.to_string())?; #[cfg(not(target_os = "linux"))] window.center().map_err(|e| e.to_string())?; window.set_zoom(1.0).map_err(|e| e.to_string())?; #[cfg(target_os = "macos")] if let Err(err) = make_transparent(&window) { log::warn!("failed to make small window transparent: {}", err); } std::thread::sleep(std::time::Duration::from_millis(750)); window.set_decorations(false).map_err(|e| e.to_string())?; window.show().map_err(|e| e.to_string())?; window.set_focus().map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] focus_window_linux(&window); window.set_always_on_top(true).map_err(|e| e.to_string())?; #[cfg(target_os = "linux")] if let Some((x, y)) = centered_logical_position(&app_handle, 800.0, 500.0) { let _ = window.set_position(tauri::Position::Logical(tauri::LogicalPosition::new(x, y))); } if os != "windows" && os != "macos" { window.set_resizable(true).map_err(|e| e.to_string())?; window.set_resizable(false).map_err(|e| e.to_string())?; } Ok(()) } #[tauri::command] pub async fn lock_windows(app_handle: AppHandle, ids: Option>) -> Result<(), String> { let windows = app_handle.webview_windows(); for (label, window) in windows.iter() { let should_lock = match &ids { Some(id_list) => id_list.contains(label), None => true, }; if should_lock { window.set_closable(false).map_err(|e| e.to_string())?; } } Ok(()) } #[tauri::command] pub async fn unlock_windows(app_handle: AppHandle, ids: Option>) -> Result<(), String> { let windows = app_handle.webview_windows(); for (label, window) in windows.iter() { let should_unlock = match &ids { Some(id_list) => id_list.contains(label), None => true, }; if should_unlock { window.set_closable(true).map_err(|e| e.to_string())?; } } Ok(()) }