[Update/TMP] Hotkey Alt+Y will active VRCT when VRChat is the active window.
This commit is contained in:
1114
src-tauri/Cargo.lock
generated
1114
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -11,11 +11,13 @@ edition = "2021"
|
|||||||
tauri-build = { version = "1", features = [] }
|
tauri-build = { version = "1", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "1", features = [ "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] }
|
tauri = { version = "1", features = [ "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
font-kit = "0.14.2"
|
font-kit = "0.14.2"
|
||||||
window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" }
|
window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" }
|
||||||
|
windows = { version = "0.38", features = ["Win32_Foundation", "Win32_UI_WindowsAndMessaging", "Win32_System_Threading"] }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
||||||
|
|
||||||
// use tauri::command;
|
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
use window_shadows::set_shadow;
|
use window_shadows::set_shadow;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowTextW};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
@@ -13,18 +12,16 @@ fn main() {
|
|||||||
{ main_window.open_devtools(); }
|
{ main_window.open_devtools(); }
|
||||||
|
|
||||||
#[cfg(any(windows, target_os = "macos"))]
|
#[cfg(any(windows, target_os = "macos"))]
|
||||||
set_shadow(main_window, true).unwrap();
|
set_shadow(main_window, true).unwrap()
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![get_font_list])
|
.invoke_handler(tauri::generate_handler![get_font_list, get_active_window_title])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
use font_kit::{source::SystemSource};
|
use font_kit::{source::SystemSource};
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn get_font_list() -> Vec<String> {
|
async fn get_font_list() -> Vec<String> {
|
||||||
@@ -41,3 +38,27 @@ async fn get_font_list() -> Vec<String> {
|
|||||||
|
|
||||||
font_families.into_iter().collect()
|
font_families.into_iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use windows::Win32::Foundation::HWND;
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_active_window_title() -> String {
|
||||||
|
unsafe {
|
||||||
|
// Get the handle of the foreground (active) window
|
||||||
|
let hwnd = GetForegroundWindow();
|
||||||
|
if hwnd == HWND(0) { // `HWND(0)` は null ハンドルを表します
|
||||||
|
return "No active window".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a buffer to hold the window title
|
||||||
|
let mut buffer: [u16; 512] = [0; 512];
|
||||||
|
|
||||||
|
// Get the window title
|
||||||
|
let length = GetWindowTextW(hwnd, &mut buffer);
|
||||||
|
|
||||||
|
if length > 0 {
|
||||||
|
// Convert the result to a Rust string
|
||||||
|
return String::from_utf16_lossy(&buffer[..length as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"Failed to retrieve window title".to_string()
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
"window": {
|
"window": {
|
||||||
"all": false,
|
"all": false,
|
||||||
"setAlwaysOnTop": true,
|
"setAlwaysOnTop": true,
|
||||||
|
"setFocus": true,
|
||||||
"setDecorations": true,
|
"setDecorations": true,
|
||||||
"close": true,
|
"close": true,
|
||||||
|
"hide": true,
|
||||||
"setPosition": true,
|
"setPosition": true,
|
||||||
"setSize": true,
|
"setSize": true,
|
||||||
"maximize": true,
|
"maximize": true,
|
||||||
@@ -24,7 +26,10 @@
|
|||||||
"unmaximize": true,
|
"unmaximize": true,
|
||||||
"unminimize": true,
|
"unminimize": true,
|
||||||
"startDragging": true
|
"startDragging": true
|
||||||
},
|
},
|
||||||
|
"globalShortcut": {
|
||||||
|
"all": true
|
||||||
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"all": false,
|
"all": false,
|
||||||
"open": true,
|
"open": true,
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import {
|
|||||||
useWindow,
|
useWindow,
|
||||||
} from "@logics_common";
|
} from "@logics_common";
|
||||||
|
|
||||||
// import React from "react";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
KeyEventController,
|
KeyEventController,
|
||||||
|
GlobalHotKeyController,
|
||||||
StartPythonController,
|
StartPythonController,
|
||||||
UiLanguageController,
|
UiLanguageController,
|
||||||
ConfigPageCloseTriggerController,
|
ConfigPageCloseTriggerController,
|
||||||
@@ -34,6 +33,7 @@ export const App = () => {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<KeyEventController />
|
<KeyEventController />
|
||||||
|
<GlobalHotKeyController />
|
||||||
<StartPythonController />
|
<StartPythonController />
|
||||||
<UiLanguageController />
|
<UiLanguageController />
|
||||||
<ConfigPageCloseTriggerController />
|
<ConfigPageCloseTriggerController />
|
||||||
|
|||||||
43
src-ui/app/_app_controllers/GlobalHotKeyController.jsx
Normal file
43
src-ui/app/_app_controllers/GlobalHotKeyController.jsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { appWindow } from "@tauri-apps/api/window";
|
||||||
|
import { register, unregisterAll, isRegistered } from "@tauri-apps/api/globalShortcut";
|
||||||
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { store } from "@store";
|
||||||
|
|
||||||
|
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 () => {
|
||||||
|
const activeWindowTitle = await invoke("get_active_window_title");
|
||||||
|
|
||||||
|
if (activeWindowTitle.includes("VRChat")) {
|
||||||
|
console.log("Shortcut triggered, setFocus");
|
||||||
|
appWindow.unminimize();
|
||||||
|
await appWindow.setFocus();
|
||||||
|
store.text_area_ref.current?.focus();
|
||||||
|
} else {
|
||||||
|
console.log("Active window is not VRChat.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
registerShortcuts();
|
||||||
|
is_initialized.current = true;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unregisterAll().catch((error) => {
|
||||||
|
console.error("Failed to unregister shortcuts:", error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -2,24 +2,27 @@ import { useEffect } from "react";
|
|||||||
|
|
||||||
export const KeyEventController = () => {
|
export const KeyEventController = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeydown = (event) => {
|
const handleKeydown = (event) => {
|
||||||
if (event.key === "F5" || (event.ctrlKey && event.key === "r") ||
|
if (
|
||||||
(event.metaKey && event.key === "r")) {
|
event.key === "F5" ||
|
||||||
|
(event.ctrlKey && event.key === "r") ||
|
||||||
|
(event.metaKey && event.key === "r")
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleContextmenu = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const handleContextmenu = (event) => {
|
document.addEventListener("keydown", handleKeydown);
|
||||||
event.preventDefault();
|
document.addEventListener("contextmenu", handleContextmenu);
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("keydown", handleKeydown);
|
return () => {
|
||||||
document.addEventListener("contextmenu", handleContextmenu);
|
document.removeEventListener("keydown", handleKeydown);
|
||||||
|
document.removeEventListener("contextmenu", handleContextmenu);
|
||||||
return () => {
|
};
|
||||||
document.removeEventListener("keydown", handleKeydown);
|
|
||||||
document.removeEventListener("contextmenu", handleContextmenu);
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export { KeyEventController } from "./KeyEventController";
|
export { KeyEventController } from "./KeyEventController";
|
||||||
|
export { GlobalHotKeyController } from "./GlobalHotKeyController";
|
||||||
export { StartPythonController } from "./StartPythonController";
|
export { StartPythonController } from "./StartPythonController";
|
||||||
export { UiLanguageController } from "./UiLanguageController";
|
export { UiLanguageController } from "./UiLanguageController";
|
||||||
export { ConfigPageCloseTriggerController } from "./ConfigPageCloseTriggerController";
|
export { ConfigPageCloseTriggerController } from "./ConfigPageCloseTriggerController";
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useLayoutEffect, useRef } from "react";
|
||||||
import styles from "./MessageInputBox.module.scss";
|
import styles from "./MessageInputBox.module.scss";
|
||||||
import SendMessageSvg from "@images/send_message.svg?react";
|
import SendMessageSvg from "@images/send_message.svg?react";
|
||||||
import { useMessage } from "@logics_common";
|
import { useMessage } from "@logics_common";
|
||||||
import { useSendMessageButtonType, useEnableAutoClearMessageInputBox } from "@logics_configs";
|
import { useSendMessageButtonType, useEnableAutoClearMessageInputBox } from "@logics_configs";
|
||||||
import { useMessageLogScroll } from "@logics_main";
|
import { useMessageLogScroll } from "@logics_main";
|
||||||
|
import { store } from "@store";
|
||||||
|
import { appWindow } from "@tauri-apps/api/window";
|
||||||
|
|
||||||
export const MessageInputBox = () => {
|
export const MessageInputBox = () => {
|
||||||
const [message_history, setMessageHistory] = useState([]);
|
const [message_history, setMessageHistory] = useState([]);
|
||||||
@@ -22,6 +24,12 @@ export const MessageInputBox = () => {
|
|||||||
|
|
||||||
const { scrollToBottom } = useMessageLogScroll();
|
const { scrollToBottom } = useMessageLogScroll();
|
||||||
|
|
||||||
|
const log_box_ref = useRef(null);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
store.text_area_ref = log_box_ref;
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentMessageLogs.data) {
|
if (currentMessageLogs.data) {
|
||||||
const sentMessages = currentMessageLogs.data
|
const sentMessages = currentMessageLogs.data
|
||||||
@@ -33,6 +41,7 @@ export const MessageInputBox = () => {
|
|||||||
|
|
||||||
const onSubmitFunction = (e) => {
|
const onSubmitFunction = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
appWindow.minimize();
|
||||||
|
|
||||||
if (!currentMessageInputValue.data.trim()) return updateMessageInputValue("");
|
if (!currentMessageInputValue.data.trim()) return updateMessageInputValue("");
|
||||||
|
|
||||||
@@ -90,6 +99,7 @@ export const MessageInputBox = () => {
|
|||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.message_box_wrapper}>
|
<div className={styles.message_box_wrapper}>
|
||||||
<textarea
|
<textarea
|
||||||
|
ref={log_box_ref}
|
||||||
className={styles.message_box_input_area}
|
className={styles.message_box_input_area}
|
||||||
onChange={onChangeFunction}
|
onChange={onChangeFunction}
|
||||||
onBlur={stopTyping}
|
onBlur={stopTyping}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export const store = {
|
|||||||
config_page: null,
|
config_page: null,
|
||||||
setting_box_scroll_container: null,
|
setting_box_scroll_container: null,
|
||||||
log_box_ref: null,
|
log_box_ref: null,
|
||||||
|
text_area_ref: null,
|
||||||
is_applied_init_message_box_height: false,
|
is_applied_init_message_box_height: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user