mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 22:51:25 +08:00
283 lines
7.9 KiB
JavaScript
283 lines
7.9 KiB
JavaScript
const { ipcRenderer } = require("electron");
|
||
const os = require("os");
|
||
const { createBrowserWindow } = utools;
|
||
/**
|
||
* 创建对话框窗口
|
||
* @param {object} config - 对话框配置
|
||
* @param {object} [customDialogOptions] - 自定义窗口选项
|
||
* @returns {Promise} 返回对话框结果
|
||
*/
|
||
const createDialog = (config, customDialogOptions = {}) => {
|
||
return new Promise((resolve) => {
|
||
const dialogPath = "lib/dialog/view.html";
|
||
const preloadPath = "lib/dialog/controller.js";
|
||
|
||
const platform = os.platform();
|
||
|
||
const dialogWidth =
|
||
platform === "win32" && config.type !== "textarea" ? 370 : 470;
|
||
|
||
const dialogOptions = {
|
||
title: config.title || "对话框",
|
||
width: dialogWidth,
|
||
height: 80,
|
||
resizable: false,
|
||
minimizable: false,
|
||
maximizable: false,
|
||
fullscreenable: false,
|
||
skipTaskbar: true,
|
||
alwaysOnTop: true,
|
||
frame: false,
|
||
opacity: 0,
|
||
webPreferences: {
|
||
preload: preloadPath,
|
||
devTools: utools.isDev(),
|
||
},
|
||
...customDialogOptions, // 合并自定义选项
|
||
};
|
||
|
||
// 创建窗口
|
||
const UBrowser = createBrowserWindow(dialogPath, dialogOptions, () => {
|
||
const dialogResultHandler = (event, result) => {
|
||
resolve(result);
|
||
// 移除监听器
|
||
ipcRenderer.removeListener("dialog-result", dialogResultHandler);
|
||
UBrowser.destroy();
|
||
};
|
||
|
||
const dialogReadyHandler = (event, height) => {
|
||
// 获取当前窗口位置
|
||
const bounds = UBrowser.getBounds();
|
||
// 调整y坐标,保持窗口中心点不变
|
||
const y = Math.round(bounds.y - (height - bounds.height) / 2);
|
||
// 确保坐标和尺寸都是有效的整数
|
||
const newBounds = {
|
||
x: Math.round(bounds.x),
|
||
y: Math.max(0, y), // 确保不会超出屏幕顶部
|
||
width: dialogWidth,
|
||
height: Math.round(height),
|
||
};
|
||
// 设置新的位置和大小
|
||
UBrowser.setBounds(newBounds);
|
||
UBrowser.setOpacity(1);
|
||
ipcRenderer.removeListener("dialog-ready", dialogReadyHandler);
|
||
};
|
||
|
||
// 监听子窗口返回的计算高度, 等待按钮有自己的计算逻辑
|
||
config.type !== "wait-button" &&
|
||
ipcRenderer.on("dialog-ready", dialogReadyHandler);
|
||
|
||
// 监听子窗口返回的返回值
|
||
ipcRenderer.on("dialog-result", dialogResultHandler);
|
||
|
||
// 发送配置到子窗口
|
||
ipcRenderer.sendTo(UBrowser.webContents.id, "dialog-config", {
|
||
...config,
|
||
isDark: utools.isDarkColors(),
|
||
platform,
|
||
});
|
||
});
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级消息框
|
||
* @param {string} content - 消息内容
|
||
* @param {string} [title] - 标题,默认为空
|
||
* @returns {Promise<void>} Promise
|
||
*/
|
||
const showSystemMessageBox = async (content, title = "") => {
|
||
await createDialog({
|
||
type: "message",
|
||
title,
|
||
content,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级输入框组对话框
|
||
* @param {string[]|{label:string,value:string,hint:string}[]} options - 输入框配置,可以是标签数组或者带属性的对象数组
|
||
* @param {string} [title] - 标题,默认为空
|
||
* @returns {Promise<string[]>} 输入的内容数组
|
||
*/
|
||
const showSystemInputBox = async (options, title = "") => {
|
||
// 确保 options 是数组
|
||
const optionsArray = Array.isArray(options) ? options : [options];
|
||
|
||
// 转换每个选项为正确的格式
|
||
const inputOptions = optionsArray.map((opt) => {
|
||
if (typeof opt === "string") {
|
||
return opt;
|
||
}
|
||
if (typeof opt === "object" && opt.label) {
|
||
return {
|
||
label: opt.label,
|
||
value: opt.value || "",
|
||
hint: opt.hint || "",
|
||
};
|
||
}
|
||
throw new Error("输入框配置格式错误");
|
||
});
|
||
|
||
return await createDialog({
|
||
type: "input",
|
||
title,
|
||
inputOptions,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级确认框,返回是否点击了确认
|
||
* @param {string} content - 确认内容
|
||
* @param {string} [title] - 标题,默认为空
|
||
* @returns {Promise<boolean>} 是否确认
|
||
*/
|
||
const showSystemConfirmBox = async (content, title = "") => {
|
||
const result = await createDialog({
|
||
type: "confirm",
|
||
title,
|
||
content,
|
||
});
|
||
return !!result;
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级按钮组对话框,返回点击的按钮的索引和文本
|
||
* @param {string[]} buttons - 按钮文本数组
|
||
* @param {string} [title] - 标题,默认为空
|
||
* @returns {Promise<{id: number, text: string}>} 选择的按钮信息
|
||
*/
|
||
const showSystemButtonBox = async (buttons, title = "") => {
|
||
return await createDialog({
|
||
type: "buttons",
|
||
title,
|
||
buttons: Array.isArray(buttons) ? buttons : [buttons],
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级多行文本输入框
|
||
* @param {string} [placeholder] - 输入框的提示文本
|
||
* @param {string} [defaultText] - 输入框的默认文本,默认为空
|
||
* @returns {Promise<string>} 编辑后的文本
|
||
*/
|
||
const showSystemTextArea = async (placeholder = "请输入", defaultText = "") => {
|
||
return await createDialog({
|
||
type: "textarea",
|
||
placeholder,
|
||
defaultText,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级选择列表对话框
|
||
* @param {Array<string|{title: string, description?: string, icon?: string}>} items - 选项列表
|
||
* @param {object} [options] - 配置选项
|
||
* @param {string} [options.title] - 对话框标题
|
||
* @param {string} [options.placeholder] - 输入框占位符
|
||
* @param {boolean} [options.enableSearch] - 是否启用搜索
|
||
* @returns {Promise<{id: number, text: string, data: any}>} 选择的结果
|
||
*/
|
||
const showSystemSelectList = async (items, options = {}) => {
|
||
const {
|
||
title = "请选择",
|
||
placeholder = "",
|
||
enableSearch = true,
|
||
optionType = "plaintext",
|
||
} = options;
|
||
|
||
return await createDialog({
|
||
type: "select",
|
||
title,
|
||
placeholder,
|
||
enableSearch,
|
||
items,
|
||
optionType,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 显示一个系统级等待按钮
|
||
* @param {object} options - 配置选项
|
||
* @param {string} [options.text="等待操作"] - 按钮文本
|
||
* @param {string} [options.position="bottom-right"] - 按钮位置,可选值:top-left, top-right, bottom-left, bottom-right
|
||
* @param {boolean} [options.showCancel=false] - 是否显示取消按钮
|
||
* @returns {Promise<boolean>} 点击确定返回true,点击取消返回false
|
||
*/
|
||
const showSystemWaitButton = async (options = {}) => {
|
||
const {
|
||
text = "等待操作",
|
||
position = "bottom-right",
|
||
showCancel = true,
|
||
} = options;
|
||
|
||
// 创建临时span计算文本宽度
|
||
const span = document.createElement("span");
|
||
span.style.font =
|
||
'14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
||
span.style.visibility = "hidden";
|
||
span.style.position = "absolute";
|
||
span.textContent = text;
|
||
document.body.appendChild(span);
|
||
|
||
// 计算窗口尺寸
|
||
const textWidth = span.offsetWidth;
|
||
document.body.removeChild(span);
|
||
|
||
const dialogOptions = {
|
||
width: Math.max(textWidth + 32 + (showCancel ? 25 : 0), 80), // 文本宽度 + padding + 取消按钮宽度(如果有)
|
||
height: 36,
|
||
opacity: 1,
|
||
};
|
||
|
||
// 获取主屏幕尺寸
|
||
const primaryDisplay = utools.getPrimaryDisplay();
|
||
const { width, height } = primaryDisplay.workAreaSize;
|
||
|
||
// 根据position计算窗口位置
|
||
const padding = 20;
|
||
let x, y;
|
||
|
||
switch (position) {
|
||
case "top-left":
|
||
x = padding;
|
||
y = padding;
|
||
break;
|
||
case "top-right":
|
||
x = width - dialogOptions.width - padding;
|
||
y = padding;
|
||
break;
|
||
case "bottom-left":
|
||
x = padding;
|
||
y = height - dialogOptions.height - padding;
|
||
break;
|
||
case "bottom-right":
|
||
default:
|
||
x = width - dialogOptions.width - padding;
|
||
y = height - dialogOptions.height - padding;
|
||
break;
|
||
}
|
||
|
||
dialogOptions.x = x;
|
||
dialogOptions.y = y;
|
||
|
||
return await createDialog(
|
||
{
|
||
type: "wait-button",
|
||
text,
|
||
showCancel,
|
||
},
|
||
dialogOptions
|
||
);
|
||
};
|
||
|
||
module.exports = {
|
||
showSystemMessageBox,
|
||
showSystemInputBox,
|
||
showSystemConfirmBox,
|
||
showSystemButtonBox,
|
||
showSystemTextArea,
|
||
showSystemSelectList,
|
||
showSystemWaitButton,
|
||
};
|