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",
|
||||
"private": true,
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -3866,7 +3866,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vrclipboard-ime-gui"
|
||||
version = "1.4.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"calc",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vrclipboard-ime-gui"
|
||||
version = "1.4.0"
|
||||
version = "1.6.0"
|
||||
description = "VRClipboard IME"
|
||||
authors = ["mii"]
|
||||
edition = "2021"
|
||||
|
@ -21,7 +21,9 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub on_copy_mode: OnCopyMode,
|
||||
#[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 {
|
||||
@ -31,8 +33,9 @@ impl Default for Config {
|
||||
split: "/".to_string(),
|
||||
command: ";".to_string(),
|
||||
ignore_prefix: true,
|
||||
on_copy_mode: OnCopyMode::ReturnToChatbox ,
|
||||
skip_url: true
|
||||
on_copy_mode: OnCopyMode::ReturnToChatbox,
|
||||
skip_url: true,
|
||||
use_tsf_reconvert: false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,6 +46,8 @@ fn slash() -> String { String::from("/") }
|
||||
fn semicolon() -> String { String::from(";") }
|
||||
#[inline]
|
||||
fn bool_true() -> bool { true }
|
||||
#[inline]
|
||||
fn bool_false() -> bool { false }
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum OnCopyMode {
|
||||
|
@ -6,12 +6,13 @@ use clipboard_master::{ClipboardHandler, CallbackResult};
|
||||
use regex::Regex;
|
||||
use rosc::{encoder, OscMessage, OscPacket, OscType};
|
||||
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;
|
||||
|
||||
pub struct ConversionHandler {
|
||||
app_handle: AppHandle,
|
||||
conversion: Conversion,
|
||||
tsf_conversion: Option<TsfConversion>,
|
||||
clipboard_ctx: ClipboardContext,
|
||||
last_text: String,
|
||||
}
|
||||
@ -19,9 +20,10 @@ pub struct ConversionHandler {
|
||||
impl ConversionHandler {
|
||||
pub fn new(app_handle: AppHandle) -> Result<Self> {
|
||||
let conversion = Conversion::new();
|
||||
let tsf_conversion = None;
|
||||
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 {
|
||||
@ -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 {
|
||||
fn on_clipboard_change(&mut self) -> CallbackResult {
|
||||
let config = self.get_config();
|
||||
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.starts_with(&config.prefix) || config.ignore_prefix {
|
||||
|
||||
|
@ -9,6 +9,7 @@ mod config;
|
||||
mod converter;
|
||||
mod transform_rule;
|
||||
mod tsf;
|
||||
mod tsf_conversion;
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
use anyhow::Result;
|
||||
use windows::{
|
||||
core::Interface,
|
||||
Win32::{
|
||||
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}},
|
||||
},
|
||||
Win32::UI::TextServices::{ITfFnSearchCandidateProvider, ITfFunctionProvider},
|
||||
};
|
||||
|
||||
use super::search_candidate_provider::SearchCandidateProvider;
|
||||
|
||||
pub struct FunctionProvider {
|
||||
function_provider: ITfFunctionProvider,
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ use anyhow::Result;
|
||||
use windows::Win32::UI::WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS};
|
||||
|
||||
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<()> {
|
||||
let mut result = thread_local_input_settings;
|
||||
|
@ -1,11 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use windows::{
|
||||
core::Interface,
|
||||
Win32::{
|
||||
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 windows::Win32::UI::TextServices::{ITfFnSearchCandidateProvider, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT};
|
||||
|
||||
use super::{function_provider::FunctionProvider, input_processor_profile_mgr::InputProcessorProfileMgr, thread_mgr::ThreadMgr};
|
||||
|
||||
pub struct SearchCandidateProvider {
|
||||
search_candidate_provider: ITfFnSearchCandidateProvider,
|
||||
|
@ -1,11 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use windows::{
|
||||
core::Interface,
|
||||
Win32::{
|
||||
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 windows::Win32::{
|
||||
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
|
||||
UI::TextServices::{CLSID_TF_ThreadMgr, ITfFunctionProvider, ITfThreadMgr2},
|
||||
};
|
||||
|
||||
pub struct ThreadMgr {
|
||||
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": {
|
||||
"productName": "vrclipboard-ime-gui",
|
||||
"version": "1.5.0"
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"tauri": {
|
||||
"updater": {
|
||||
|
@ -9,6 +9,7 @@ interface Config {
|
||||
ignore_prefix: boolean;
|
||||
on_copy_mode: OnCopyMode;
|
||||
skip_url: boolean;
|
||||
use_tsf_reconvert: boolean;
|
||||
}
|
||||
|
||||
enum OnCopyMode {
|
||||
@ -24,7 +25,8 @@ const SettingsComponent = () => {
|
||||
command: ';',
|
||||
ignore_prefix: false,
|
||||
on_copy_mode: OnCopyMode.ReturnToChatbox,
|
||||
skip_url: true
|
||||
skip_url: true,
|
||||
use_tsf_reconvert: false
|
||||
});
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
@ -179,6 +181,19 @@ const SettingsComponent = () => {
|
||||
</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
|
||||
onClick={saveSettings}
|
||||
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