[Update/TMP] tmp

This commit is contained in:
Sakamoto Shiina
2025-01-12 04:46:55 +09:00
parent 9e5af73ccb
commit ff57454073
16 changed files with 308 additions and 35 deletions

View File

@@ -94,6 +94,7 @@ config_page:
transcription: Transcription
vr: VR
others: Others
hotkeys: Hotkeys
advanced_settings: Advanced Settings
supporters: Supporters
about_vrct: About VRCT

View File

@@ -1,35 +1,76 @@
import { appWindow } from "@tauri-apps/api/window";
import { register, unregisterAll, isRegistered } from "@tauri-apps/api/globalShortcut";
import { useEffect, useRef } from "react";
import { store } from "@store";
import { appWindow } from "@tauri-apps/api/window";
import { register, unregisterAll, isRegistered } from "@tauri-apps/api/globalShortcut";
import { useEffect } from "react";
import { store } from "@store";
import { useHotkeys } from "@logics_configs";
export const GlobalHotKeyController = () => {
const is_initialized = useRef(false);
useEffect(() => {
if (is_initialized.current) return;
const registerShortcuts = async () => {
const shortcut = "Alt+Y";
const is_already_registered = await isRegistered(shortcut);
if (is_already_registered) return;
await register(shortcut, async () => {
console.log("Shortcut triggered, setFocus");
appWindow.unminimize();
await appWindow.setFocus();
store.text_area_ref.current?.focus();
});
};
registerShortcuts();
is_initialized.current = true;
return () => {
unregisterAll().catch((error) => {
console.error("Failed to unregister shortcuts:", error);
});
};
}, []);
return null;
// 修飾キーのパースを行う関数
const parseHotkey = (hotkeyString) => {
const keyMap = {
Ctrl: "Control",
Alt: "Alt",
Shift: "Shift",
Meta: "Super",
};
// 入力文字列を分解して対応するキーを再結合
return hotkeyString
.map((key) => keyMap[key] || key) // 修飾キーをマップし、その他はそのまま
.join("+");
};
export const GlobalHotKeyController = () => {
const { currentHotkeys } = useHotkeys();
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;
}
// 入力文字列をTauriで使える形式に変換
const shortcut = parseHotkey(shortcut_raw);
// const shortcut = "F9";
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}`);
}
} catch (error) {
console.error("Failed to register global shortcut:", error);
}
};
registerShortcuts();
// クリーンアップ関数でショートカットを解除
return () => {
unregisterAll().catch((error) => {
console.error("Failed to unregister shortcuts:", error);
});
};
}, [currentHotkeys.data.toggle_active_vrct]); // 監視対象を明確に指定
return null;
};

View File

@@ -8,6 +8,7 @@ import {
Others,
AdvancedSettings,
Vr,
Hotkeys,
Supporters,
AboutVrct,
} from "@setting_box";
@@ -27,6 +28,8 @@ export const SettingBox = () => {
return <Others />;
case "vr":
return <Vr />;
case "hotkeys":
return <Hotkeys />;
case "advanced_settings":
return <AdvancedSettings />;
case "supporters":

View File

@@ -8,16 +8,22 @@ const _Entry = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
blur: () => {
inputRef.current.blur();
}
}));
const input_class_names = clsx(styles.entry_input_area, {
[styles.is_disabled]: props.is_disabled
[styles.is_disabled]: props.is_disabled,
});
const input_wrapper_class_names = clsx(styles.entry_wrapper, {
[styles.is_activated]: props.is_activated,
});
return (
<div className={styles.entry_container}>
<div
className={styles.entry_wrapper}
className={input_wrapper_class_names}
style={{width: props.width }}
>
<input
@@ -25,6 +31,7 @@ const _Entry = forwardRef((props, ref) => {
className={input_class_names}
value={props.ui_variable === null ? "" : props.ui_variable}
onChange={(e) => props.onChange(e)}
{...props}
/>
</div>
</div>

View File

@@ -9,6 +9,9 @@
background-color: var(--dark_875_color);
border: 0.1rem solid var(--dark_750_color);
border-radius: 0.4rem;
&.is_activated {
border: 0.1rem solid var(--primary_400_color);
}
}
.entry_input_area {

View File

@@ -0,0 +1,134 @@
import styles from "./HotkeysEntry.module.scss";
import { _Entry } from "../_atoms/_entry/_Entry";
import { useState, useRef } from "react";
export const HotkeysEntry = (props) => {
const [is_accepting_input, setIsAcceptingInput] = useState(false); // キー入力受付中かどうか
const lastKeyRef = useRef(null); // 直前のキーを保持
const [displayValue, setDisplayValue] = useState(props.value[props.hotkey_id]); // 表示用の値
const isModifierOnlyRef = useRef(false); // 修飾キー単体かどうかのフラグ
const entryRef = useRef(null);
const pressedKeys = useRef(new Set()); // 押されているキーを追跡
const keysRef = useRef([]); // 最新のkeysを保存
const setHotkeys = (keys) => {
entryRef.current.blur();
props.setHotkeys({ [props.hotkey_id]: keys });
};
const handleKeyInput = (event) => {
const keys = [];
const nonModifierKeys = []; // 修飾キー以外を追跡する配列
// 修飾キーを判定して追加(重複防止)
if (event.ctrlKey && !keys.includes("Ctrl")) keys.push("Ctrl");
if (event.shiftKey && !keys.includes("Shift")) keys.push("Shift");
if (event.altKey && !keys.includes("Alt")) keys.push("Alt");
if (event.metaKey && !keys.includes("Super")) keys.push("Super");
let register_key = event.key === "Meta" ? "Super" : event.key;
// アルファベットの場合は大文字に変換
if (/^[a-zA-Z]$/.test(register_key)) {
register_key = register_key.toUpperCase();
}
// 修飾キー以外を追加
if (!["Control", "Shift", "Alt", "Meta"].includes(event.key)) {
keys.push(register_key);
nonModifierKeys.push(register_key); // 修飾キー以外のキーを追跡
}
// キーが既に追跡されていない場合のみ追加
if (!pressedKeys.current.has(register_key)) {
pressedKeys.current.add(register_key);
}
// 最新のキー構成を保存
keysRef.current = [...keys];
// 表示用の値を更新
setDisplayValue(keys.join(" + "));
// 修飾キー単体かどうかを更新
isModifierOnlyRef.current = nonModifierKeys.length === 0;
// 修飾キーのみの場合は登録処理をスキップ
if (isModifierOnlyRef.current) return;
};
const handleKeyDown = (event) => {
event.preventDefault(); // デフォルト動作を防ぐ
// 直前のキーと同じならスキップ
const currentKey = event.key;
if (lastKeyRef.current === currentKey) {
return;
}
lastKeyRef.current = currentKey; // 今回のキーを記録
handleKeyInput(event);
};
const handleKeyUp = (event) => {
lastKeyRef.current = null; // キーが離されたらリセット
// 修飾キーのみの場合でも表示は維持
if (isModifierOnlyRef.current) {
setDisplayValue(""); // 非修飾キーが含まれていた場合リセット
}
let register_key = event.key === "Meta" ? "Super" : event.key;
// アルファベットの場合は大文字に変換
if (/^[a-zA-Z]$/.test(register_key)) {
register_key = register_key.toUpperCase();
}
// 押されているキーから削除
pressedKeys.current.delete(register_key);
// 全てのキーが放された場合
if (pressedKeys.current.size === 0) {
// 修飾キーのみの場合はスキップ
const hasNonModifierKeys = keysRef.current.some(
(key) => !["Ctrl", "Shift", "Alt", "Super"].includes(key)
);
if (!hasNonModifierKeys) {
return;
}
// 保存されたキー構成を使用して登録
setHotkeys(keysRef.current);
}
};
const handleBlur = () => {
setIsAcceptingInput(false);
pressedKeys.current.clear();
};
return (
<div className={styles.container}>
<_Entry
ref={entryRef}
type="text"
placeholder="Press hotkeys keys"
onFocus={() => [setIsAcceptingInput(true)]}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
value={displayValue} // 表示用の値を設定
width="20rem"
is_activated={is_accepting_input}
readOnly
/>
<button
className={styles.delete_button}
onClick={() => [setHotkeys(null), setDisplayValue("")]}
>
Delete
</button>
</div>
);
};

View File

@@ -0,0 +1,13 @@
.container {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
}
.delete_button {
padding: 0.8rem;
font-size: 1.4rem;
background-color: var(--dark_800_color);
flex-shrink: 0;
}

View File

@@ -3,6 +3,7 @@ export { ComputeDevice } from "./compute_device/ComputeDevice";
export { DeeplAuthKey, OpenWebpage_DeeplAuthKey } from "./deepl_auth_key/DeeplAuthKey";
export { DropdownMenu } from "./dropdown_menu/DropdownMenu";
export { Entry } from "./entry/Entry";
export { HotkeysEntry } from "./hotkeys_entry/HotkeysEntry";
export { LabelComponent } from "./label_component/LabelComponent";
export { RadioButton } from "./radio_button/RadioButton";
export { SectionLabelComponent } from "./section_label_component/SectionLabelComponent";

View File

@@ -8,6 +8,7 @@ import {
Slider,
SwitchBox,
Entry,
HotkeysEntry,
RadioButton,
OpenWebpage_DeeplAuthKey,
DeeplAuthKey,
@@ -75,6 +76,10 @@ export const EntryContainer = (props) => (
<CommonContainer Component={Entry} {...props} add_break_point={false} />
);
export const HotkeysEntryContainer = (props) => (
<CommonContainer Component={HotkeysEntry} {...props} add_break_point={false} />
);
export const RadioButtonContainer = (props) => (
<CommonContainer Component={RadioButton} {...props} />
);

View File

@@ -0,0 +1,26 @@
import { useHotkeys } from "@logics_configs";
import styles from "./Hotkeys.module.scss";
import { HotkeysEntryContainer } from "../_templates/Templates";
export const Hotkeys = () => {
return (
<div className={styles.container}>
<HotkeysBoxContainer />
</div>
);
};
const HotkeysBoxContainer = () => {
const { currentHotkeys, setHotkeys } = useHotkeys();
return (
<div className={styles.container}>
<HotkeysEntryContainer
// label={t("config_page.appearance.send_message_button_type.label")}
label="Toggle active input box"
hotkey_id="toggle_active_vrct"
value={currentHotkeys.data}
setHotkeys={setHotkeys}
/>
</div>
);
};

View File

@@ -0,0 +1,5 @@
.container {
display: flex;
gap: 6.4rem;
flex-direction: column;
}

View File

@@ -5,5 +5,6 @@ export { Transcription } from "./transcription/Transcription";
export { Others, VrcMicMuteSyncContainer } from "./others/Others";
export { AdvancedSettings } from "./advanced_settings/AdvancedSettings";
export { Vr } from "./vr/Vr";
export { Hotkeys } from "./hotkeys/Hotkeys";
export { AboutVrct } from "./about_vrct/AboutVrct";
export { Supporters } from "./supporters/Supporters";

View File

@@ -10,6 +10,7 @@ export const SidebarSection = () => {
<Tab tab_id="transcription" />
<Tab tab_id="vr" />
<Tab tab_id="others" />
<Tab tab_id="hotkeys" />
<Tab tab_id="advanced_settings" />
</div>
<div className={styles.separated_tabs_wrapper}>

View File

@@ -0,0 +1,25 @@
import { useStore_Hotkeys } from "@store";
// import { useStdoutToPython } from "@logics/useStdoutToPython";
export const useHotkeys = () => {
// const { asyncStdoutToPython } = useStdoutToPython();
const { currentHotkeys, updateHotkeys, pendingHotkeys } = useStore_Hotkeys();
const getHotkeys = () => {
// pendingHotkeys();
// asyncStdoutToPython("/get/data/osc_ip_address");
};
const setHotkeys = (hotkeys) => {
updateHotkeys(hotkeys);
// pendingHotkeys();
// asyncStdoutToPython("/set/data/osc_ip_address", osc_ip_address);
};
return {
currentHotkeys,
// getHotkeys,
updateHotkeys,
setHotkeys,
};
};

View File

@@ -51,6 +51,8 @@ export { useOverlayShowOnlyTranslatedMessages } from "./vr/useOverlayShowOnlyTra
export { useOverlayLargeLogSettings } from "./vr/useOverlayLargeLogSettings";
export { useSendTextToOverlay } from "./vr/useSendTextToOverlay";
export { useHotkeys } from "./hotkeys/useHotkeys";
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
export { useOscPort } from "./advanced_settings/useOscPort";

View File

@@ -264,6 +264,11 @@ export const { atomInstance: Atom_EnableVrcMicMuteSync, useHook: useStore_Enable
export const { atomInstance: Atom_EnableSendMessageToVrc, useHook: useStore_EnableSendMessageToVrc } = createAtomWithHook(true, "EnableSendMessageToVrc");
export const { atomInstance: Atom_EnableSendReceivedMessageToVrc, useHook: useStore_EnableSendReceivedMessageToVrc } = createAtomWithHook(false, "EnableSendReceivedMessageToVrc");
// Hotkeys
export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createAtomWithHook({
toggle_active_vrct: null,
}, "Hotkeys");
// Advanced Settings
export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress");
export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createAtomWithHook("9000", "OscPort");