mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
Fix renderer overlay loading and modularize renderer
This commit is contained in:
238
src/renderer/handlers/keyboard.ts
Normal file
238
src/renderer/handlers/keyboard.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
import type { Keybinding } from "../../types";
|
||||
import type { RendererContext } from "../context";
|
||||
|
||||
export function createKeyboardHandlers(
|
||||
ctx: RendererContext,
|
||||
options: {
|
||||
handleRuntimeOptionsKeydown: (e: KeyboardEvent) => boolean;
|
||||
handleSubsyncKeydown: (e: KeyboardEvent) => boolean;
|
||||
handleKikuKeydown: (e: KeyboardEvent) => boolean;
|
||||
handleJimakuKeydown: (e: KeyboardEvent) => boolean;
|
||||
saveInvisiblePositionEdit: () => void;
|
||||
cancelInvisiblePositionEdit: () => void;
|
||||
setInvisiblePositionEditMode: (enabled: boolean) => void;
|
||||
applyInvisibleSubtitleOffsetPosition: () => void;
|
||||
updateInvisiblePositionEditHud: () => void;
|
||||
},
|
||||
) {
|
||||
const CHORD_MAP = new Map<string, { type: "mpv" | "electron"; command?: string[]; action?: () => void }>([
|
||||
["KeyS", { type: "mpv", command: ["script-message", "subminer-start"] }],
|
||||
["Shift+KeyS", { type: "mpv", command: ["script-message", "subminer-stop"] }],
|
||||
["KeyT", { type: "mpv", command: ["script-message", "subminer-toggle"] }],
|
||||
["KeyI", { type: "mpv", command: ["script-message", "subminer-toggle-invisible"] }],
|
||||
["Shift+KeyI", { type: "mpv", command: ["script-message", "subminer-show-invisible"] }],
|
||||
["KeyU", { type: "mpv", command: ["script-message", "subminer-hide-invisible"] }],
|
||||
["KeyO", { type: "mpv", command: ["script-message", "subminer-options"] }],
|
||||
["KeyR", { type: "mpv", command: ["script-message", "subminer-restart"] }],
|
||||
["KeyC", { type: "mpv", command: ["script-message", "subminer-status"] }],
|
||||
["KeyY", { type: "mpv", command: ["script-message", "subminer-menu"] }],
|
||||
[
|
||||
"KeyD",
|
||||
{ type: "electron", action: () => window.electronAPI.toggleDevTools() },
|
||||
],
|
||||
]);
|
||||
|
||||
function isInteractiveTarget(target: EventTarget | null): boolean {
|
||||
if (!(target instanceof Element)) return false;
|
||||
if (target.closest(".modal")) return true;
|
||||
if (ctx.dom.subtitleContainer.contains(target)) return true;
|
||||
if (target.tagName === "IFRAME" && target.id?.startsWith("yomitan-popup")) {
|
||||
return true;
|
||||
}
|
||||
if (target.closest && target.closest('iframe[id^="yomitan-popup"]')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function keyEventToString(e: KeyboardEvent): string {
|
||||
const parts: string[] = [];
|
||||
if (e.ctrlKey) parts.push("Ctrl");
|
||||
if (e.altKey) parts.push("Alt");
|
||||
if (e.shiftKey) parts.push("Shift");
|
||||
if (e.metaKey) parts.push("Meta");
|
||||
parts.push(e.code);
|
||||
return parts.join("+");
|
||||
}
|
||||
|
||||
function isInvisiblePositionToggleShortcut(e: KeyboardEvent): boolean {
|
||||
return (
|
||||
e.code === ctx.platform.invisiblePositionEditToggleCode &&
|
||||
!e.altKey &&
|
||||
e.shiftKey &&
|
||||
(e.ctrlKey || e.metaKey)
|
||||
);
|
||||
}
|
||||
|
||||
function handleInvisiblePositionEditKeydown(e: KeyboardEvent): boolean {
|
||||
if (!ctx.platform.isInvisibleLayer) return false;
|
||||
|
||||
if (isInvisiblePositionToggleShortcut(e)) {
|
||||
e.preventDefault();
|
||||
if (ctx.state.invisiblePositionEditMode) {
|
||||
options.cancelInvisiblePositionEdit();
|
||||
} else {
|
||||
options.setInvisiblePositionEditMode(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ctx.state.invisiblePositionEditMode) return false;
|
||||
|
||||
const step = e.shiftKey
|
||||
? ctx.platform.invisiblePositionStepFastPx
|
||||
: ctx.platform.invisiblePositionStepPx;
|
||||
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
options.cancelInvisiblePositionEdit();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.key === "Enter" || ((e.ctrlKey || e.metaKey) && e.code === "KeyS")) {
|
||||
e.preventDefault();
|
||||
options.saveInvisiblePositionEdit();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
e.key === "ArrowUp" ||
|
||||
e.key === "ArrowDown" ||
|
||||
e.key === "ArrowLeft" ||
|
||||
e.key === "ArrowRight" ||
|
||||
e.key === "h" ||
|
||||
e.key === "j" ||
|
||||
e.key === "k" ||
|
||||
e.key === "l" ||
|
||||
e.key === "H" ||
|
||||
e.key === "J" ||
|
||||
e.key === "K" ||
|
||||
e.key === "L"
|
||||
) {
|
||||
e.preventDefault();
|
||||
if (e.key === "ArrowUp" || e.key === "k" || e.key === "K") {
|
||||
ctx.state.invisibleSubtitleOffsetYPx += step;
|
||||
} else if (e.key === "ArrowDown" || e.key === "j" || e.key === "J") {
|
||||
ctx.state.invisibleSubtitleOffsetYPx -= step;
|
||||
} else if (e.key === "ArrowLeft" || e.key === "h" || e.key === "H") {
|
||||
ctx.state.invisibleSubtitleOffsetXPx -= step;
|
||||
} else if (e.key === "ArrowRight" || e.key === "l" || e.key === "L") {
|
||||
ctx.state.invisibleSubtitleOffsetXPx += step;
|
||||
}
|
||||
options.applyInvisibleSubtitleOffsetPosition();
|
||||
options.updateInvisiblePositionEditHud();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function resetChord(): void {
|
||||
ctx.state.chordPending = false;
|
||||
if (ctx.state.chordTimeout !== null) {
|
||||
clearTimeout(ctx.state.chordTimeout);
|
||||
ctx.state.chordTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function setupMpvInputForwarding(): Promise<void> {
|
||||
const keybindings: Keybinding[] = await window.electronAPI.getKeybindings();
|
||||
ctx.state.keybindingsMap = new Map();
|
||||
for (const binding of keybindings) {
|
||||
if (binding.command) {
|
||||
ctx.state.keybindingsMap.set(binding.key, binding.command);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e: KeyboardEvent) => {
|
||||
const yomitanPopup = document.querySelector('iframe[id^="yomitan-popup"]');
|
||||
if (yomitanPopup) return;
|
||||
if (handleInvisiblePositionEditKeydown(e)) return;
|
||||
|
||||
if (ctx.state.runtimeOptionsModalOpen) {
|
||||
options.handleRuntimeOptionsKeydown(e);
|
||||
return;
|
||||
}
|
||||
if (ctx.state.subsyncModalOpen) {
|
||||
options.handleSubsyncKeydown(e);
|
||||
return;
|
||||
}
|
||||
if (ctx.state.kikuModalOpen) {
|
||||
options.handleKikuKeydown(e);
|
||||
return;
|
||||
}
|
||||
if (ctx.state.jimakuModalOpen) {
|
||||
options.handleJimakuKeydown(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.state.chordPending) {
|
||||
const modifierKeys = [
|
||||
"ShiftLeft",
|
||||
"ShiftRight",
|
||||
"ControlLeft",
|
||||
"ControlRight",
|
||||
"AltLeft",
|
||||
"AltRight",
|
||||
"MetaLeft",
|
||||
"MetaRight",
|
||||
];
|
||||
if (modifierKeys.includes(e.code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
const secondKey = keyEventToString(e);
|
||||
const action = CHORD_MAP.get(secondKey);
|
||||
resetChord();
|
||||
if (action) {
|
||||
if (action.type === "mpv" && action.command) {
|
||||
window.electronAPI.sendMpvCommand(action.command);
|
||||
} else if (action.type === "electron" && action.action) {
|
||||
action.action();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
e.code === "KeyY" &&
|
||||
!e.ctrlKey &&
|
||||
!e.altKey &&
|
||||
!e.shiftKey &&
|
||||
!e.metaKey &&
|
||||
!e.repeat
|
||||
) {
|
||||
e.preventDefault();
|
||||
ctx.state.chordPending = true;
|
||||
ctx.state.chordTimeout = setTimeout(() => {
|
||||
resetChord();
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
const keyString = keyEventToString(e);
|
||||
const command = ctx.state.keybindingsMap.get(keyString);
|
||||
|
||||
if (command) {
|
||||
e.preventDefault();
|
||||
window.electronAPI.sendMpvCommand(command);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("mousedown", (e: MouseEvent) => {
|
||||
if (e.button === 2 && !isInteractiveTarget(e.target)) {
|
||||
e.preventDefault();
|
||||
window.electronAPI.sendMpvCommand(["cycle", "pause"]);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("contextmenu", (e: Event) => {
|
||||
if (!isInteractiveTarget(e.target)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
setupMpvInputForwarding,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user