use crate::constants::window::*; use crate::window::{ derive::{PopupLocation, PopupSize, TargetLocation}, util::get_monitor_for_window, WindowLabel, }; use tauri::{AppHandle, Manager, Runtime, WebviewUrl, WebviewWindowBuilder}; /// 创建弹出框窗口(内部实现) pub async fn create_popup_window_impl(app: &AppHandle, target: &TargetLocation, placement: &str) -> Result<(), String> { // 获取屏幕尺寸,优先为main窗口所在屏幕,主窗口没有打开时,才找主屏幕 let monitor: tauri::Monitor = get_monitor_for_window(app)?; // 使用动态计算的弹出窗口尺寸 let popup_size = calculate_popup_size_sync(); let page_size = monitor.size().to_logical(monitor.scale_factor()); // 计算弹出框位置和大小 let popup_location = calculate_placement(target, &popup_size, placement, &page_size).await?; let label = WindowLabel::POPUP.as_ref(); // 检查是否已经存在名为"popup"的窗口 if let Some(popup_window) = app.get_webview_window(label) { // 如果窗口已存在,直接关闭它 let _ = popup_window.close(); } let url = format!("popup-window.html?placement={}", popup_location.placement); // 创建弹出框窗口 WebviewWindowBuilder::new(app, label, WebviewUrl::App(url.into())) .title("弹出框") .inner_size(popup_location.width as f64, popup_location.height as f64) .position(popup_location.x as f64, popup_location.y as f64) .resizable(false) .transparent(true) .decorations(false) .shadow(false) .focusable(true) .accept_first_mouse(true) .skip_taskbar(true) .always_on_top(true) .visible(true) .build() .map_err(|e| format!("创建弹出框窗口失败: {}", e))?; Ok(()) } /// 根据用户登录状态和课程选择情况动态计算弹出窗口高度 pub fn calculate_popup_size_sync() -> PopupSize { // 默认基础项目数 let item_num = 3; // 根据项目数计算高度,每个项目40像素,加上边距2像素 let popup_height = 40 * item_num + 2; let popup_width = 180; // 返回计算得到的弹出窗口尺寸 PopupSize { width: popup_width, height: popup_height, } } /// 计算弹出框位置 pub async fn calculate_placement( target: &TargetLocation, popup_size: &PopupSize, placement: &str, page_size: &tauri::LogicalSize, ) -> Result { let margin = 12; // 箭头距离触发元素的距离 let mar = 12; // 距离屏幕边缘的最小距离 let page_width = page_size.width; let page_height = page_size.height; // 点击dom宽高 let target_width = target.width; let target_height = target.height; // 点击dom右上角的坐标 let target_x = target.x; let target_y = target.y; // 点击dom中心点坐标 let center_x = target_x + (target_width / 2) as i32; let center_y = target_y + (target_height / 2) as i32; // 弹出框的宽高 let bubble_width = popup_size.width; let bubble_height = popup_size.height; // 如果placement为"center",则直接返回屏幕中央位置 if placement == PLACEMENT_CENTER { return Ok(calculate_center_position(popup_size, page_size)); } // 上面足够 let top_enough = bubble_height as i32 + margin + mar < target_y; // 下面足够 let bottom_enough = (target_y + target_height as i32 + bubble_height as i32 + margin + mar) < page_height; // 左面足够 let left_enough = target_x - bubble_width as i32 - margin - mar >= 0; // 右面足够 let right_enough = target_x + target_width as i32 + bubble_width as i32 + margin + mar < page_width; // 屏幕宽度足够 let width_enough = page_width > bubble_width as i32 + mar * 2; // 屏幕高度足够 let height_enough = page_height > bubble_height as i32 + mar * 2; if (placement == PLACEMENT_AUTO || placement == PLACEMENT_TOP) && top_enough && width_enough { get_top_bottom_placement(PLACEMENT_TOP, target, popup_size, margin, mar, center_x, page_width) } else if (placement == PLACEMENT_AUTO || placement == PLACEMENT_BOTTOM) && bottom_enough && width_enough { get_top_bottom_placement(PLACEMENT_BOTTOM, target, popup_size, margin, mar, center_x, page_width) } else if (placement == PLACEMENT_AUTO || placement == PLACEMENT_LEFT) && left_enough && height_enough { get_left_right_placement(PLACEMENT_LEFT, target, popup_size, margin, mar, center_y, page_height) } else if (placement == PLACEMENT_AUTO || placement == PLACEMENT_RIGHT) && right_enough && height_enough { get_left_right_placement(PLACEMENT_RIGHT, target, popup_size, margin, mar, center_y, page_height) } else { let popup_y = center_y + (target_height as i32) / 2 + margin; Ok(PopupLocation { enough_show: false, placement: PLACEMENT_CENTER.to_string(), x: 0, y: popup_y, width: popup_size.width, height: popup_size.height, }) } } /// 计算屏幕中央位置 pub fn calculate_center_position(popup_size: &PopupSize, page_size: &tauri::LogicalSize) -> PopupLocation { let page_width = page_size.width; let page_height = page_size.height; let popup_x = (page_width - popup_size.width as i32) / 2; let popup_y = (page_height - popup_size.height as i32) / 2; PopupLocation { enough_show: true, placement: PLACEMENT_CENTER.to_string(), x: popup_x, y: popup_y, width: popup_size.width, height: popup_size.height, } } /// 上或者下足够并且宽度也足够时的位置计算 fn get_top_bottom_placement( placement: &str, target: &TargetLocation, popup_size: &PopupSize, margin: i32, mar: i32, center_x: i32, page_width: i32, ) -> Result { let popup_y = if placement == "bottom" { target.y + target.height as i32 + margin } else { target.y - popup_size.height as i32 - margin }; let right_x = center_x - (popup_size.width as i32) / 2; let left_x = center_x + (popup_size.width as i32) / 2; let mut popup_x = mar; if right_x >= mar && page_width - left_x >= mar { popup_x = right_x; } else if page_width - left_x >= mar { popup_x = mar; } else if right_x >= mar { popup_x = page_width - popup_size.width as i32 - mar; } Ok(PopupLocation { enough_show: true, placement: placement.to_string(), y: popup_y, x: popup_x, width: popup_size.width, height: popup_size.height, }) } /// 左或者右足够并且高度也足够时的位置计算 fn get_left_right_placement( placement: &str, target: &TargetLocation, popup_size: &PopupSize, margin: i32, mar: i32, center_y: i32, page_height: i32, ) -> Result { let popup_x = if placement == "left" { target.x - popup_size.width as i32 - margin } else { target.x + target.width as i32 + margin }; let top_y = center_y - (popup_size.height as i32) / 2; let bottom_y = center_y + (popup_size.height as i32) / 2; let popup_y = if top_y >= mar && page_height - bottom_y >= mar { // 水平居中 top_y } else if page_height - bottom_y >= mar { mar } else { page_height - popup_size.height as i32 - mar }; Ok(PopupLocation { enough_show: true, placement: placement.to_string(), y: popup_y, x: popup_x, width: popup_size.width, height: popup_size.height, }) }