From bac7bb15d365d62a76d7f8fbf4f862b96ec9fdba Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 17 Jan 2025 02:41:27 +0900 Subject: [PATCH] [Update] Add hotkeys main functions --- locales/en.yml | 10 +++ locales/ja.yml | 4 + src-python/config.py | 5 +- .../GlobalHotKeyController.jsx | 80 ++++++++++++------- .../hotkeys_entry/HotkeysEntry.jsx | 10 ++- .../setting_box/hotkeys/Hotkeys.jsx | 30 ++++++- .../setting_box/hotkeys/Hotkeys.module.scss | 2 +- .../message_input_box/MessageInputBox.jsx | 2 +- src-ui/logics/configs/hotkeys/useHotkeys.js | 8 +- src-ui/logics/main/useMainFunction.js | 20 ++++- src-ui/store.js | 31 +++---- 11 files changed, 145 insertions(+), 57 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 438af71e..3ef81b30 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -258,6 +258,16 @@ config_page: label: Send Received Message To VRChat desc: Send the message you received from the speaker's sound to VRChat's chatbox. + hotkeys: + toggle_vrct_visibility: + label: Toggle VRCT Visibility + toggle_translation: + label: Toggle Translation + toggle_transcription_send: + label: Toggle Voice2Chatbox + toggle_transcription_receive: + label: Toggle Speaker2Log + advanced_settings: osc_ip_address: label: OSC IP Address diff --git a/locales/ja.yml b/locales/ja.yml index 97784185..94abb3d1 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -255,6 +255,10 @@ config_page: label: 受信したメッセージをVRChatに送信する desc: スピーカーから聞き取り、文字起こしされたメッセージをVRChatに送信します。 + hotkeys: + toggle_vrct_visibility: + label: VRCTの最小化/アクティブ化の切り替え + advanced_settings: osc_ip_address: label: OSC IP Address diff --git a/src-python/config.py b/src-python/config.py index 54fe37fd..15d0aacb 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -1040,7 +1040,10 @@ class Config: self._MIC_MAX_PHRASES = 10 self._MIC_WORD_FILTER = [] self._HOTKEYS = { - "toggle_active_vrct": None, + "toggle_vrct_visibility": None, + "toggle_translation": None, + "toggle_transcription_send": None, + "toggle_transcription_receive": None, } self._MIC_AVG_LOGPROB = -0.8 self._MIC_NO_SPEECH_PROB = 0.6 diff --git a/src-ui/app/_app_controllers/GlobalHotKeyController.jsx b/src-ui/app/_app_controllers/GlobalHotKeyController.jsx index e3b8474a..f21a2cee 100644 --- a/src-ui/app/_app_controllers/GlobalHotKeyController.jsx +++ b/src-ui/app/_app_controllers/GlobalHotKeyController.jsx @@ -3,6 +3,7 @@ import { register, unregisterAll, isRegistered } from "@tauri-apps/api/globalSho import { useEffect } from "react"; import { store } from "@store"; import { useHotkeys } from "@logics_configs"; +import { useMainFunction } from "@logics_main"; // 修飾キーのパースを行う関数 const parseHotkey = (hotkeyString) => { @@ -22,40 +23,65 @@ const parseHotkey = (hotkeyString) => { export const GlobalHotKeyController = () => { const { currentHotkeys } = useHotkeys(); + const { + toggleTranslation, + toggleTranscriptionSend, + toggleTranscriptionReceive, + } = useMainFunction(); + useEffect(() => { const registerShortcuts = async () => { - const shortcut_raw = currentHotkeys.data.toggle_active_vrct; - console.log(shortcut_raw); - - if (!shortcut_raw) { - console.warn("No hotkey defined."); - return; - } - - const shortcut = parseHotkey(shortcut_raw); - try { // 既存のショートカットをすべて解除 await unregisterAll(); - // 新しいショートカットを登録 - const isAlreadyRegistered = await isRegistered(shortcut); - if (!isAlreadyRegistered) { - await register(shortcut, async () => { - console.log(`Shortcut "${shortcut}" triggered, setting focus.`); - const minimized = await appWindow.isMinimized(); - if (minimized === true) { - appWindow.unminimize(); - await appWindow.setFocus(); - store.text_area_ref.current?.focus(); - } else { - appWindow.minimize(); - } - }); - console.log(`Registered global shortcut: ${shortcut}`); + const hotkeyEntries = Object.entries(currentHotkeys.data); + + for (const [actionKey, hotkeyRaw] of hotkeyEntries) { + if (!hotkeyRaw) continue; + + const shortcut = parseHotkey(hotkeyRaw); + const isAlreadyRegistered = await isRegistered(shortcut); + + if (!isAlreadyRegistered) { + await register(shortcut, async () => { + console.log(`Shortcut for "${actionKey}" triggered.`); + + switch (actionKey) { + case "toggle_vrct_visibility": { + const minimized = await appWindow.isMinimized(); + if (minimized) { + appWindow.unminimize(); + await appWindow.setFocus(); + store.text_area_ref.current?.focus(); + } else { + appWindow.minimize(); + } + break; + } + case "toggle_translation": { + toggleTranslation(); + break; + } + case "toggle_transcription_send": { + toggleTranscriptionSend(); + break; + } + case "toggle_transcription_receive": { + toggleTranscriptionReceive(); + break; + } + default: { + console.warn(`No handler defined for action: ${actionKey}`); + break; + } + } + }); + console.log(`Registered global shortcut: ${shortcut} for action: ${actionKey}`); + } } } catch (error) { - console.error("Failed to register global shortcut:", error); + console.error("Failed to register global shortcuts:", error); } }; @@ -67,7 +93,7 @@ export const GlobalHotKeyController = () => { console.error("Failed to unregister shortcuts:", error); }); }; - }, [currentHotkeys.data.toggle_active_vrct]); // 監視対象を明確に指定 + }, [currentHotkeys.data]); // 監視対象を全体に変更 return null; }; diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx index 21a874a0..1e71a069 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx @@ -17,8 +17,6 @@ export const HotkeysEntry = (props) => { const init_display_value = props.value[props.hotkey_id] ? props.value[props.hotkey_id].join(" + ") : ""; setDisplayValue(init_display_value); }, []); - console.log(props.value[props.hotkey_id]); - const updateHotkeys = (keys) => { entryRef.current.blur(); @@ -35,9 +33,10 @@ export const HotkeysEntry = (props) => { const keys = []; const nonModifierKeys = []; - ["Ctrl", "Shift", "Alt", "Super"].forEach((modKey) => { + ["Ctrl", "Shift", "Alt", "Meta"].forEach((modKey) => { if (event[`${modKey.toLowerCase()}Key`] && !keys.includes(modKey)) { - keys.push(modKey); + let register_mod_key = (modKey === "Meta") ? "Super" : modKey; + keys.push(register_mod_key); } }); @@ -80,6 +79,9 @@ export const HotkeysEntry = (props) => { ); if (hasNonModifierKeys) { updateHotkeys(keysRef.current); + } else { + const display_value = props.value[props.hotkey_id] ? props.value[props.hotkey_id].join(" + ") : ""; + setDisplayValue(display_value); } } }; diff --git a/src-ui/app/config_page/setting_section/setting_box/hotkeys/Hotkeys.jsx b/src-ui/app/config_page/setting_section/setting_box/hotkeys/Hotkeys.jsx index a0359b5a..2fe0116d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/hotkeys/Hotkeys.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/hotkeys/Hotkeys.jsx @@ -1,7 +1,7 @@ import { useHotkeys } from "@logics_configs"; import styles from "./Hotkeys.module.scss"; import { HotkeysEntryContainer } from "../_templates/Templates"; - +import { useTranslation } from "react-i18next"; export const Hotkeys = () => { return (
@@ -11,13 +11,35 @@ export const Hotkeys = () => { }; const HotkeysBoxContainer = () => { + const { t } = useTranslation(); const { currentHotkeys, setHotkeys } = useHotkeys(); + return (
+ + + { const onSubmitFunction = (e) => { e.preventDefault(); - appWindow.minimize(); + // appWindow.minimize(); if (!currentMessageInputValue.data.trim()) return updateMessageInputValue(""); diff --git a/src-ui/logics/configs/hotkeys/useHotkeys.js b/src-ui/logics/configs/hotkeys/useHotkeys.js index 1da14fb6..65ea3448 100644 --- a/src-ui/logics/configs/hotkeys/useHotkeys.js +++ b/src-ui/logics/configs/hotkeys/useHotkeys.js @@ -12,7 +12,13 @@ export const useHotkeys = () => { const setHotkeys = (hotkeys) => { pendingHotkeys(); - asyncStdoutToPython("/set/data/hotkeys", hotkeys); + const send_obj = { + ...currentHotkeys.data, + ...hotkeys, + }; + asyncStdoutToPython("/set/data/hotkeys", send_obj); + + }; return { diff --git a/src-ui/logics/main/useMainFunction.js b/src-ui/logics/main/useMainFunction.js index 14b1e923..9345f090 100644 --- a/src-ui/logics/main/useMainFunction.js +++ b/src-ui/logics/main/useMainFunction.js @@ -6,7 +6,7 @@ import { useStore_TranscriptionReceiveStatus, useStore_ForegroundStatus, } from "@store"; - +import { useCallback } from "react"; import { useStdoutToPython } from "@logics/useStdoutToPython"; export const useMainFunction = () => { @@ -40,7 +40,11 @@ export const useMainFunction = () => { asyncStdoutToPython("/set/disable/translation"); } }; - const toggleTranslation = () => setTranslation(!currentTranslationStatus.data); + const toggleTranslation = () => { + updateTranslationStatus(prev_state => { + if (prev_state.state === "ok") setTranslation(!prev_state.data); + }, { set_state: "pending" }); + }; const setTranscriptionSend = (to_enable) => { pendingTranscriptionSendStatus(); @@ -50,7 +54,11 @@ export const useMainFunction = () => { asyncStdoutToPython("/set/disable/transcription_send"); } }; - const toggleTranscriptionSend = () => setTranscriptionSend(!currentTranscriptionSendStatus.data); + const toggleTranscriptionSend = () => { + updateTranscriptionSendStatus(prev_state => { + if (prev_state.state === "ok") setTranscriptionSend(!prev_state.data); + }, { set_state: "pending" }); + }; const setTranscriptionReceive = (to_enable) => { pendingTranscriptionReceiveStatus(); @@ -60,7 +68,11 @@ export const useMainFunction = () => { asyncStdoutToPython("/set/disable/transcription_receive"); } }; - const toggleTranscriptionReceive = () => setTranscriptionReceive(!currentTranscriptionReceiveStatus.data); + const toggleTranscriptionReceive = () => { + updateTranscriptionReceiveStatus(prev_state => { + if (prev_state.state === "ok") setTranscriptionReceive(!prev_state.data); + }, { set_state: "pending" }); + }; const toggleForeground = () => { diff --git a/src-ui/store.js b/src-ui/store.js index 53d30ed5..24043576 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -55,20 +55,20 @@ const createAtomWithHook = (initialValue, base_name, options) => { }); }; - const updateAtom = (payload) => { + const updateAtom = (payload, options = {}) => { + const { remain_state = false, set_state } = options; + setAtom((currentValue) => { - if (typeof payload === "function") { - const updated_data = payload(currentValue); - return { - state: "ok", - data: updated_data, - }; - } else { - return { - state: "ok", - data: payload, - }; - } + const new_state = set_state ?? (remain_state ? currentValue.state : "ok"); + + const updated_data = typeof payload === "function" + ? payload(currentValue) + : payload; + + return { + state: new_state, + data: updated_data, + }; }); }; @@ -266,7 +266,10 @@ export const { atomInstance: Atom_EnableSendReceivedMessageToVrc, useHook: useSt // Hotkeys export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createAtomWithHook({ - toggle_active_vrct: null, + toggle_vrct_visibility: null, + toggle_translation: null, + toggle_transcription_send: null, + toggle_transcription_receive: null, }, "Hotkeys"); // Advanced Settings