mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-09 15:33:31 +00:00
123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
import type { BrowserWindow, MenuItemConstructorOptions } from "electron";
|
|
import type { nativeT } from "./nativeI18n";
|
|
|
|
export type ProjectMenuAction = "menu-load-project" | "menu-save-project" | "menu-save-project-as";
|
|
type EditAction = "undo" | "redo" | "cut" | "copy" | "paste" | "selectAll";
|
|
type Submenu = MenuItemConstructorOptions & { submenu: MenuItemConstructorOptions[] };
|
|
type Target = {
|
|
window: BrowserWindow | null;
|
|
isValid: (window: BrowserWindow) => boolean;
|
|
};
|
|
type Options = {
|
|
translate: typeof nativeT;
|
|
onProjectAction: (action: ProjectMenuAction, target?: BrowserWindow) => void;
|
|
target?: Target;
|
|
};
|
|
|
|
export function captureEditorMenuTarget(
|
|
window: BrowserWindow | null,
|
|
isOwnedEditor: (window: BrowserWindow) => boolean,
|
|
): Target {
|
|
const contents = window && isOwnedEditor(window) ? window.webContents : null;
|
|
const url = contents?.getURL();
|
|
return {
|
|
window: contents ? window : null,
|
|
isValid: (candidate) =>
|
|
candidate === window &&
|
|
isOwnedEditor(candidate) &&
|
|
candidate.webContents === contents &&
|
|
contents?.getURL() === url,
|
|
};
|
|
}
|
|
|
|
/** The tray captures one editor; never fall back to another window after it closes. */
|
|
function runOnTarget(target: Target, action: (window: BrowserWindow) => void) {
|
|
const window = target.window;
|
|
if (!window || !target.isValid(window)) return;
|
|
const deadline = Date.now() + 400;
|
|
if (window.isMinimized()) window.restore();
|
|
if (!target.isValid(window)) return;
|
|
window.show();
|
|
if (!target.isValid(window)) return;
|
|
window.focus();
|
|
let contentsFocusRequested = false;
|
|
const checkFocus = () => {
|
|
if (!target.isValid(window) || Date.now() > deadline) return;
|
|
if (!window.isFocused()) {
|
|
// Once acquired, losing focus cancels the command instead of reclaiming it.
|
|
if (contentsFocusRequested) return;
|
|
} else {
|
|
if (!contentsFocusRequested) {
|
|
contentsFocusRequested = true;
|
|
window.webContents.focus();
|
|
}
|
|
if (!target.isValid(window) || !window.isFocused()) return;
|
|
if (window.webContents.isFocused()) {
|
|
if (target.isValid(window) && Date.now() <= deadline) action(window);
|
|
return;
|
|
}
|
|
}
|
|
const remaining = deadline - Date.now();
|
|
if (remaining > 0) setTimeout(checkFocus, Math.min(20, remaining));
|
|
};
|
|
checkFocus();
|
|
}
|
|
|
|
/** Shared labels/actions; the ordinary application menu retains its native Edit roles. */
|
|
export function createEditorCommandMenus(options: Options): [Submenu, Submenu] {
|
|
const { translate: t, target } = options;
|
|
const enabled = !target || (!!target.window && target.isValid(target.window));
|
|
const projectItem = (
|
|
label: Parameters<typeof nativeT>[0],
|
|
accelerator: string,
|
|
action: ProjectMenuAction,
|
|
): MenuItemConstructorOptions => ({
|
|
label: t(label),
|
|
accelerator,
|
|
enabled,
|
|
click: () => {
|
|
if (target) runOnTarget(target, (window) => options.onProjectAction(action, window));
|
|
else options.onProjectAction(action);
|
|
},
|
|
});
|
|
const editItem = (action: EditAction): MenuItemConstructorOptions =>
|
|
target
|
|
? {
|
|
label: t(action),
|
|
enabled,
|
|
// Target WebContents directly: a tray click must never edit another app.
|
|
click: () => runOnTarget(target, (window) => window.webContents[action]()),
|
|
}
|
|
: { label: t(action), role: action };
|
|
return [
|
|
{
|
|
label: t("file"),
|
|
submenu: [
|
|
projectItem("openProjects", "CmdOrCtrl+O", "menu-load-project"),
|
|
projectItem("saveProject", "CmdOrCtrl+S", "menu-save-project"),
|
|
projectItem("saveProjectAs", "CmdOrCtrl+Shift+S", "menu-save-project-as"),
|
|
],
|
|
},
|
|
{
|
|
label: t("edit"),
|
|
submenu: [
|
|
editItem("undo"),
|
|
editItem("redo"),
|
|
{ type: "separator" },
|
|
editItem("cut"),
|
|
editItem("copy"),
|
|
editItem("paste"),
|
|
editItem("selectAll"),
|
|
],
|
|
},
|
|
];
|
|
}
|
|
|
|
export function withHiddenDockEditorMenus(
|
|
items: MenuItemConstructorOptions[],
|
|
options: Options & { target: Target; platform: NodeJS.Platform; dockVisible: boolean },
|
|
): MenuItemConstructorOptions[] {
|
|
if (options.platform !== "darwin" || options.dockVisible) return items;
|
|
return [...items, { type: "separator" }, ...createEditorCommandMenus(options)];
|
|
}
|