Files
tauri-meeting/src-tauri/src/window/create_popup_window.rs
O昵称重要吗O 745dc0e8fa feat: 新增浮动列表窗口和弹出窗口功能
添加浮动列表窗口,支持拖拽、吸附和折叠功能,包含课程互动和辅导工具菜单
新增弹出窗口,提供主窗口、画板和退出等快捷操作
优化窗口创建逻辑,添加日志记录和焦点设置
2026-03-13 11:32:05 +08:00

216 lines
7.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::constants::window::*;
use crate::window::{
WindowLabel,
derive::{PopupLocation, PopupSize, TargetLocation},
util::get_monitor_for_window,
};
use tauri::{AppHandle, Manager, Runtime, WebviewUrl, WebviewWindowBuilder};
/// 创建弹出框窗口(内部实现)
pub async fn create_popup_window_impl<R: Runtime>(app: &AppHandle<R>, 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();
}
log::info!("创建弹出框窗口: {:?}", popup_location);
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)
.focused(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<i32>) -> Result<PopupLocation, String> {
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<i32>) -> 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<PopupLocation, String> {
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<PopupLocation, String> {
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,
})
}