216 lines
7.7 KiB
Rust
216 lines
7.7 KiB
Rust
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,
|
||
})
|
||
}
|