Files
rclone-ui/src-tauri/common/window.rs
T
2026-05-12 19:31:34 +03:00

366 lines
11 KiB
Rust

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::<f64>(scale);
let pos = monitor.position().to_logical::<f64>(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<bool>,
) -> 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::<f64>(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<f64>,
height: Option<f64>,
) -> 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<f64>,
height: Option<f64>,
) -> 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<Vec<String>>) -> 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<Vec<String>>) -> 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(())
}