mirror of
https://github.com/mii443/vrclipboard-ime-gui.git
synced 2025-08-22 16:15:32 +00:00
add tsf conversion
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vrclipboard-ime-gui",
|
"name": "vrclipboard-ime-gui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.5.0",
|
"version": "1.6.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -3866,7 +3866,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vrclipboard-ime-gui"
|
name = "vrclipboard-ime-gui"
|
||||||
version = "1.4.0"
|
version = "1.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"calc",
|
"calc",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vrclipboard-ime-gui"
|
name = "vrclipboard-ime-gui"
|
||||||
version = "1.4.0"
|
version = "1.6.0"
|
||||||
description = "VRClipboard IME"
|
description = "VRClipboard IME"
|
||||||
authors = ["mii"]
|
authors = ["mii"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -21,7 +21,9 @@ pub struct Config {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub on_copy_mode: OnCopyMode,
|
pub on_copy_mode: OnCopyMode,
|
||||||
#[serde(default = "bool_true")]
|
#[serde(default = "bool_true")]
|
||||||
pub skip_url: bool
|
pub skip_url: bool,
|
||||||
|
#[serde(default = "bool_false")]
|
||||||
|
pub use_tsf_reconvert: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -31,8 +33,9 @@ impl Default for Config {
|
|||||||
split: "/".to_string(),
|
split: "/".to_string(),
|
||||||
command: ";".to_string(),
|
command: ";".to_string(),
|
||||||
ignore_prefix: true,
|
ignore_prefix: true,
|
||||||
on_copy_mode: OnCopyMode::ReturnToChatbox ,
|
on_copy_mode: OnCopyMode::ReturnToChatbox,
|
||||||
skip_url: true
|
skip_url: true,
|
||||||
|
use_tsf_reconvert: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,6 +46,8 @@ fn slash() -> String { String::from("/") }
|
|||||||
fn semicolon() -> String { String::from(";") }
|
fn semicolon() -> String { String::from(";") }
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bool_true() -> bool { true }
|
fn bool_true() -> bool { true }
|
||||||
|
#[inline]
|
||||||
|
fn bool_false() -> bool { false }
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub enum OnCopyMode {
|
pub enum OnCopyMode {
|
||||||
|
@ -6,12 +6,13 @@ use clipboard_master::{ClipboardHandler, CallbackResult};
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rosc::{encoder, OscMessage, OscPacket, OscType};
|
use rosc::{encoder, OscMessage, OscPacket, OscType};
|
||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use crate::{config::{Config, OnCopyMode}, conversion::Conversion, Log, STATE};
|
use crate::{config::{Config, OnCopyMode}, conversion::Conversion, tsf_conversion::TsfConversion, Log, STATE};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
pub struct ConversionHandler {
|
pub struct ConversionHandler {
|
||||||
app_handle: AppHandle,
|
app_handle: AppHandle,
|
||||||
conversion: Conversion,
|
conversion: Conversion,
|
||||||
|
tsf_conversion: Option<TsfConversion>,
|
||||||
clipboard_ctx: ClipboardContext,
|
clipboard_ctx: ClipboardContext,
|
||||||
last_text: String,
|
last_text: String,
|
||||||
}
|
}
|
||||||
@ -19,9 +20,10 @@ pub struct ConversionHandler {
|
|||||||
impl ConversionHandler {
|
impl ConversionHandler {
|
||||||
pub fn new(app_handle: AppHandle) -> Result<Self> {
|
pub fn new(app_handle: AppHandle) -> Result<Self> {
|
||||||
let conversion = Conversion::new();
|
let conversion = Conversion::new();
|
||||||
|
let tsf_conversion = None;
|
||||||
let clipboard_ctx = ClipboardProvider::new().unwrap();
|
let clipboard_ctx = ClipboardProvider::new().unwrap();
|
||||||
|
|
||||||
Ok(Self { app_handle, conversion, clipboard_ctx, last_text: String::new() })
|
Ok(Self { app_handle, conversion, tsf_conversion, clipboard_ctx, last_text: String::new() })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config(&self) -> Config {
|
pub fn get_config(&self) -> Config {
|
||||||
@ -29,10 +31,28 @@ impl ConversionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConversionHandler {
|
||||||
|
fn tsf_conversion(&mut self, contents: &str, config: &Config) -> CallbackResult {
|
||||||
|
if self.tsf_conversion.is_none() {
|
||||||
|
self.tsf_conversion = Some(TsfConversion::new());
|
||||||
|
|
||||||
|
println!("TSF conversion created.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let tsf_conversion = self.tsf_conversion.as_mut().unwrap();
|
||||||
|
|
||||||
|
CallbackResult::Next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ClipboardHandler for ConversionHandler {
|
impl ClipboardHandler for ConversionHandler {
|
||||||
fn on_clipboard_change(&mut self) -> CallbackResult {
|
fn on_clipboard_change(&mut self) -> CallbackResult {
|
||||||
let config = self.get_config();
|
let config = self.get_config();
|
||||||
if let Ok(mut contents) = self.clipboard_ctx.get_contents() {
|
if let Ok(mut contents) = self.clipboard_ctx.get_contents() {
|
||||||
|
if config.use_tsf_reconvert {
|
||||||
|
return self.tsf_conversion(&contents, &config);
|
||||||
|
}
|
||||||
|
|
||||||
if contents != self.last_text {
|
if contents != self.last_text {
|
||||||
if contents.starts_with(&config.prefix) || config.ignore_prefix {
|
if contents.starts_with(&config.prefix) || config.ignore_prefix {
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ mod config;
|
|||||||
mod converter;
|
mod converter;
|
||||||
mod transform_rule;
|
mod transform_rule;
|
||||||
mod tsf;
|
mod tsf;
|
||||||
|
mod tsf_conversion;
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use windows::{
|
use windows::{
|
||||||
core::Interface,
|
core::Interface,
|
||||||
Win32::{
|
Win32::UI::TextServices::{ITfFnSearchCandidateProvider, ITfFunctionProvider},
|
||||||
System::Com::{CoCreateInstance, CoInitialize, CoUninitialize, CLSCTX_INPROC_SERVER},
|
|
||||||
UI::{Input::KeyboardAndMouse::HKL, TextServices::{CLSID_TF_InputProcessorProfiles, CLSID_TF_ThreadMgr, ITfFnSearchCandidateProvider, ITfFunctionProvider, ITfInputProcessorProfileMgr, ITfThreadMgr2, GUID_TFCAT_TIP_KEYBOARD, TF_INPUTPROCESSORPROFILE, TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE, TF_PROFILETYPE_INPUTPROCESSOR, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT}, WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS}},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::search_candidate_provider::SearchCandidateProvider;
|
||||||
|
|
||||||
pub struct FunctionProvider {
|
pub struct FunctionProvider {
|
||||||
function_provider: ITfFunctionProvider,
|
function_provider: ITfFunctionProvider,
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@ use anyhow::Result;
|
|||||||
use windows::Win32::UI::WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS};
|
use windows::Win32::UI::WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS};
|
||||||
|
|
||||||
pub mod input_processor_profile_mgr;
|
pub mod input_processor_profile_mgr;
|
||||||
|
pub mod function_provider;
|
||||||
|
pub mod search_candidate_provider;
|
||||||
|
pub mod thread_mgr;
|
||||||
|
|
||||||
pub fn set_thread_local_input_settings(thread_local_input_settings: bool) -> Result<()> {
|
pub fn set_thread_local_input_settings(thread_local_input_settings: bool) -> Result<()> {
|
||||||
let mut result = thread_local_input_settings;
|
let mut result = thread_local_input_settings;
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use windows::{
|
use windows::Win32::UI::TextServices::{ITfFnSearchCandidateProvider, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT};
|
||||||
core::Interface,
|
|
||||||
Win32::{
|
use super::{function_provider::FunctionProvider, input_processor_profile_mgr::InputProcessorProfileMgr, thread_mgr::ThreadMgr};
|
||||||
System::Com::{CoCreateInstance, CoInitialize, CoUninitialize, CLSCTX_INPROC_SERVER},
|
|
||||||
UI::{Input::KeyboardAndMouse::HKL, TextServices::{CLSID_TF_InputProcessorProfiles, CLSID_TF_ThreadMgr, ITfFnSearchCandidateProvider, ITfFunctionProvider, ITfInputProcessorProfileMgr, ITfThreadMgr2, GUID_TFCAT_TIP_KEYBOARD, TF_INPUTPROCESSORPROFILE, TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE, TF_PROFILETYPE_INPUTPROCESSOR, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT}, WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS}},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct SearchCandidateProvider {
|
pub struct SearchCandidateProvider {
|
||||||
search_candidate_provider: ITfFnSearchCandidateProvider,
|
search_candidate_provider: ITfFnSearchCandidateProvider,
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use windows::{
|
use windows::Win32::{
|
||||||
core::Interface,
|
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
|
||||||
Win32::{
|
UI::TextServices::{CLSID_TF_ThreadMgr, ITfFunctionProvider, ITfThreadMgr2},
|
||||||
System::Com::{CoCreateInstance, CoInitialize, CoUninitialize, CLSCTX_INPROC_SERVER},
|
};
|
||||||
UI::{Input::KeyboardAndMouse::HKL, TextServices::{CLSID_TF_InputProcessorProfiles, CLSID_TF_ThreadMgr, ITfFnSearchCandidateProvider, ITfFunctionProvider, ITfInputProcessorProfileMgr, ITfThreadMgr2, GUID_TFCAT_TIP_KEYBOARD, TF_INPUTPROCESSORPROFILE, TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE, TF_PROFILETYPE_INPUTPROCESSOR, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT}, WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS}},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ThreadMgr {
|
pub struct ThreadMgr {
|
||||||
thread_mgr: ITfThreadMgr2,
|
thread_mgr: ITfThreadMgr2,
|
||||||
|
28
src-tauri/src/tsf_conversion.rs
Normal file
28
src-tauri/src/tsf_conversion.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use crate::tsf::{search_candidate_provider::SearchCandidateProvider, set_thread_local_input_settings};
|
||||||
|
|
||||||
|
pub struct TsfConversion {
|
||||||
|
pub conversion_history: Vec<String>,
|
||||||
|
pub clipboard_history: Vec<String>,
|
||||||
|
pub now_reconvertion: bool,
|
||||||
|
pub target_text: String,
|
||||||
|
pub search_candidate_provider: SearchCandidateProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TsfConversion {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
set_thread_local_input_settings(true).unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
conversion_history: Vec::new(),
|
||||||
|
clipboard_history: Vec::new(),
|
||||||
|
now_reconvertion: false,
|
||||||
|
target_text: String::new(),
|
||||||
|
search_candidate_provider: SearchCandidateProvider::create().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert(&self, text: &str) -> Result<String> {
|
||||||
|
Ok(self.search_candidate_provider.get_candidates(text, 10)?[0].clone())
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "vrclipboard-ime-gui",
|
"productName": "vrclipboard-ime-gui",
|
||||||
"version": "1.5.0"
|
"version": "1.6.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"updater": {
|
"updater": {
|
||||||
|
@ -9,6 +9,7 @@ interface Config {
|
|||||||
ignore_prefix: boolean;
|
ignore_prefix: boolean;
|
||||||
on_copy_mode: OnCopyMode;
|
on_copy_mode: OnCopyMode;
|
||||||
skip_url: boolean;
|
skip_url: boolean;
|
||||||
|
use_tsf_reconvert: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum OnCopyMode {
|
enum OnCopyMode {
|
||||||
@ -24,7 +25,8 @@ const SettingsComponent = () => {
|
|||||||
command: ';',
|
command: ';',
|
||||||
ignore_prefix: false,
|
ignore_prefix: false,
|
||||||
on_copy_mode: OnCopyMode.ReturnToChatbox,
|
on_copy_mode: OnCopyMode.ReturnToChatbox,
|
||||||
skip_url: true
|
skip_url: true,
|
||||||
|
use_tsf_reconvert: false
|
||||||
});
|
});
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
@ -179,6 +181,19 @@ const SettingsComponent = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="use_tsf_reconvert"
|
||||||
|
name="use_tsf_reconvert"
|
||||||
|
checked={settings.use_tsf_reconvert}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
<label htmlFor="use_tsf_reconvert" className="text-sm font-medium text-gray-700">
|
||||||
|
ベータ機能: Text Services Framework 再変換を使用(区切り、モード変更、開始文字が無効化されます)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={saveSettings}
|
onClick={saveSettings}
|
||||||
className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition-colors"
|
className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition-colors"
|
||||||
|
Reference in New Issue
Block a user