Add the feature that repeated smart punctuation input switches to Chinese punctuation.
This commit is contained in:
1 parent
8a84cdc178
commit
04eda22a40
6 files changed
+186
-18
No files matched your search
@@ -142,13 +142,64 @@ WCHAR CMetasequoiaIME::_GetPrecedingCharForSmartPunctuation(TfEditCookie ec, _In
|
||||
return _GetPrecedingDocumentChar(ec, pContext);
|
||||
}
|
||||
|
||||
void CMetasequoiaIME::_ResetSmartPunctuationHistory()
|
||||
{
|
||||
void CMetasequoiaIME::_ResetSmartPunctuationHistory()
|
||||
{
|
||||
_smartPunctuationKey = 0;
|
||||
_smartPunctuationPrecedingChar = 0;
|
||||
_smartPunctuationCommittedAscii = false;
|
||||
_smartPunctuationAsciiRejected = false;
|
||||
}
|
||||
_smartPunctuationCommittedAscii = false;
|
||||
_smartPunctuationAsciiRejected = false;
|
||||
_smartPunctuationCommitTick = 0;
|
||||
_smartPunctuationFocusToken = 0;
|
||||
_smartPunctuationForegroundWindow = nullptr;
|
||||
}
|
||||
|
||||
bool CMetasequoiaIME::_QueueRepeatedSmartPunctuationReplacement(WCHAR wch)
|
||||
{
|
||||
if (!_smartPunctuationCommittedAscii || _smartPunctuationKey != wch ||
|
||||
_smartPunctuationCommitTick == 0 || _msgWndHandle == nullptr ||
|
||||
_pCompositionProcessorEngine == nullptr || _IsComposing() ||
|
||||
_candidateMode != CANDIDATE_NONE ||
|
||||
!Global::SmartPunctuationEnabled.load(std::memory_order_relaxed) ||
|
||||
!Global::SmartPunctuationRepeatToChineseEnabled.load(std::memory_order_relaxed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
if (now - _smartPunctuationCommitTick > SMART_PUNCTUATION_REPEAT_INTERVAL_MS ||
|
||||
!_IsFocusSessionCurrent(_smartPunctuationFocusToken) ||
|
||||
GetForegroundWindow() != _smartPunctuationForegroundWindow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const WCHAR *chinese = _pCompositionProcessorEngine->GetPunctuation(wch);
|
||||
if (chinese == nullptr || chinese[0] == L'\0' || chinese[1] != L'\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_pendingSmartPunctuationReplacement = chinese[0];
|
||||
_pendingSmartPunctuationFocusToken = _smartPunctuationFocusToken;
|
||||
_pendingSmartPunctuationForegroundWindow = _smartPunctuationForegroundWindow;
|
||||
_pendingSmartPunctuationDeadline =
|
||||
_smartPunctuationCommitTick + SMART_PUNCTUATION_REPEAT_INTERVAL_MS;
|
||||
|
||||
const uint64_t focusToken = _pendingSmartPunctuationFocusToken;
|
||||
if (!PostMessage(_msgWndHandle, WM_ReplaceRepeatedSmartPunctuation,
|
||||
static_cast<WPARAM>(focusToken & 0xFFFFFFFFULL),
|
||||
static_cast<LPARAM>((focusToken >> 32) & 0xFFFFFFFFULL)))
|
||||
{
|
||||
_pendingSmartPunctuationReplacement = 0;
|
||||
_pendingSmartPunctuationFocusToken = 0;
|
||||
_pendingSmartPunctuationForegroundWindow = nullptr;
|
||||
_pendingSmartPunctuationDeadline = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
_ResetSmartPunctuationHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMetasequoiaIME::_InvalidateSmartPunctuationShadow()
|
||||
{
|
||||
@@ -211,9 +262,20 @@ void CMetasequoiaIME::_UpdateSmartPunctuationShadow(UINT code, WCHAR wch, bool i
|
||||
_smartPunctuationShadowValid = true;
|
||||
}
|
||||
|
||||
void CMetasequoiaIME::_NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten)
|
||||
{
|
||||
_UpdateSmartPunctuationShadow(code, wch, isEaten);
|
||||
void CMetasequoiaIME::_NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten)
|
||||
{
|
||||
// The replacement message normally runs before another input event. If it
|
||||
// does not, never let a later key leave the queued Backspace targeting an
|
||||
// unrelated character.
|
||||
if (_pendingSmartPunctuationReplacement != 0)
|
||||
{
|
||||
_pendingSmartPunctuationReplacement = 0;
|
||||
_pendingSmartPunctuationFocusToken = 0;
|
||||
_pendingSmartPunctuationForegroundWindow = nullptr;
|
||||
_pendingSmartPunctuationDeadline = 0;
|
||||
}
|
||||
|
||||
_UpdateSmartPunctuationShadow(code, wch, isEaten);
|
||||
|
||||
if (_smartPunctuationKey == 0)
|
||||
{
|
||||
@@ -296,8 +358,20 @@ std::wstring CMetasequoiaIME::_ResolveSmartPunctuation(WCHAR wch, WCHAR precedin
|
||||
|
||||
_smartPunctuationKey = wch;
|
||||
_smartPunctuationPrecedingChar = precedingChar;
|
||||
_smartPunctuationCommittedAscii = committedAscii;
|
||||
_smartPunctuationAsciiRejected = asciiRejected;
|
||||
_smartPunctuationCommittedAscii = committedAscii;
|
||||
_smartPunctuationAsciiRejected = asciiRejected;
|
||||
if (committedAscii)
|
||||
{
|
||||
_smartPunctuationCommitTick = GetTickCount64();
|
||||
_smartPunctuationFocusToken = _CaptureFocusSessionToken();
|
||||
_smartPunctuationForegroundWindow = GetForegroundWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
_smartPunctuationCommitTick = 0;
|
||||
_smartPunctuationFocusToken = 0;
|
||||
_smartPunctuationForegroundWindow = nullptr;
|
||||
}
|
||||
if (!resolved.empty())
|
||||
{
|
||||
_smartPunctuationShadowChar = resolved.back();
|
||||
|
||||
@@ -1671,6 +1671,8 @@ void CMetasequoiaIME::IpcWorkerThread(CMetasequoiaIME *pIME)
|
||||
}
|
||||
}
|
||||
if (validFrame && (buf.msg_type == Global::DataToTsfWorkerThreadMsgType::SmartPunctuationChanged ||
|
||||
buf.msg_type ==
|
||||
Global::DataToTsfWorkerThreadMsgType::SmartPunctuationRepeatToChineseChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::PairedPunctuationChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::MicrosoftShuangpinChanged))
|
||||
{
|
||||
@@ -1720,6 +1722,7 @@ void CMetasequoiaIME::IpcWorkerThread(CMetasequoiaIME *pIME)
|
||||
// Soft config/control frames: drop without killing the worker pipe.
|
||||
if (buf.msg_type == Global::DataToTsfWorkerThreadMsgType::PagingCommaPeriodChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::SmartPunctuationChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::SmartPunctuationRepeatToChineseChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::PairedPunctuationChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::MicrosoftShuangpinChanged ||
|
||||
buf.msg_type == Global::DataToTsfWorkerThreadMsgType::PipeReady ||
|
||||
@@ -1777,6 +1780,10 @@ void CMetasequoiaIME::IpcWorkerThread(CMetasequoiaIME *pIME)
|
||||
{
|
||||
Global::SmartPunctuationEnabled.store(buf.data[0] == L'1', std::memory_order_relaxed);
|
||||
}
|
||||
else if (buf.msg_type == Global::DataToTsfWorkerThreadMsgType::SmartPunctuationRepeatToChineseChanged)
|
||||
{
|
||||
Global::SmartPunctuationRepeatToChineseEnabled.store(buf.data[0] == L'1', std::memory_order_relaxed);
|
||||
}
|
||||
else if (buf.msg_type == Global::DataToTsfWorkerThreadMsgType::PairedPunctuationChanged)
|
||||
{
|
||||
Global::PairedPunctuationEnabled.store(buf.data[0] == L'1', std::memory_order_relaxed);
|
||||
@@ -2506,6 +2513,52 @@ LRESULT CALLBACK CMetasequoiaIME_WindowProc(HWND hWnd, UINT message, WPARAM wPar
|
||||
SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
|
||||
break;
|
||||
}
|
||||
case WM_ReplaceRepeatedSmartPunctuation: {
|
||||
const uint64_t focusToken = static_cast<uint64_t>(static_cast<uint32_t>(wParam)) |
|
||||
(static_cast<uint64_t>(static_cast<uint32_t>(lParam)) << 32);
|
||||
const WCHAR replacement = pIME->_pendingSmartPunctuationReplacement;
|
||||
const bool requestCurrent =
|
||||
replacement != 0 && focusToken != 0 &&
|
||||
focusToken == pIME->_pendingSmartPunctuationFocusToken &&
|
||||
Global::SmartPunctuationRepeatToChineseEnabled.load(std::memory_order_relaxed) &&
|
||||
pIME->_IsFocusSessionCurrent(focusToken) &&
|
||||
GetForegroundWindow() == pIME->_pendingSmartPunctuationForegroundWindow &&
|
||||
GetTickCount64() <= pIME->_pendingSmartPunctuationDeadline;
|
||||
|
||||
pIME->_pendingSmartPunctuationReplacement = 0;
|
||||
pIME->_pendingSmartPunctuationFocusToken = 0;
|
||||
pIME->_pendingSmartPunctuationForegroundWindow = nullptr;
|
||||
pIME->_pendingSmartPunctuationDeadline = 0;
|
||||
if (!requestCurrent)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
INPUT inputs[4] = {};
|
||||
inputs[0].type = INPUT_KEYBOARD;
|
||||
inputs[0].ki.wVk = VK_BACK;
|
||||
inputs[0].ki.dwExtraInfo = SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO;
|
||||
inputs[1] = inputs[0];
|
||||
inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
inputs[2].type = INPUT_KEYBOARD;
|
||||
inputs[2].ki.wScan = replacement;
|
||||
inputs[2].ki.dwFlags = KEYEVENTF_UNICODE;
|
||||
inputs[2].ki.dwExtraInfo = SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO;
|
||||
inputs[3] = inputs[2];
|
||||
inputs[3].ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
|
||||
if (SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)) == ARRAYSIZE(inputs))
|
||||
{
|
||||
pIME->_smartPunctuationShadowChar = replacement;
|
||||
pIME->_smartPunctuationShadowValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pIME->_InvalidateSmartPunctuationShadow();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SETTINGCHANGE: {
|
||||
// Registry may not be flushed yet when the broadcast arrives.
|
||||
if (lParam && _wcsicmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0)
|
||||
|
||||
@@ -38,6 +38,9 @@ const DWORD WM_DrainDeferredKeyDown = WM_USER + 18;
|
||||
const DWORD WM_InsertText = WM_USER + 19;
|
||||
const DWORD WM_RefreshLanguageBarTheme = WM_USER + 20;
|
||||
const DWORD WM_PairedPunctuationMoveLeft = WM_USER + 21;
|
||||
const DWORD WM_ReplaceRepeatedSmartPunctuation = WM_USER + 22;
|
||||
constexpr ULONG_PTR SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO = 0x4D535050u;
|
||||
constexpr ULONGLONG SMART_PUNCTUATION_REPEAT_INTERVAL_MS = 2000;
|
||||
constexpr UINT_PTR TIMER_CONNECT_ALL_NAMEDPIPE = 1;
|
||||
constexpr UINT_PTR TIMER_CONNECT_TO_TSF_NAMEDPIPE = 2;
|
||||
constexpr UINT_PTR TIMER_REFRESH_LANG_BAR_THEME = 3;
|
||||
@@ -165,10 +168,11 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
// Shadow first, document read as the fallback. See _smartPunctuationShadowChar.
|
||||
WCHAR _GetPrecedingCharForSmartPunctuation(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
|
||||
// Smart punctuation: backspacing the ASCII punctuation we just committed
|
||||
// means that form was unwanted, so the spot stays on Chinese punctuation.
|
||||
std::wstring _ResolveSmartPunctuation(WCHAR wch, WCHAR precedingChar);
|
||||
void _NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten);
|
||||
// Smart punctuation: backspacing the ASCII punctuation we just committed
|
||||
// means that form was unwanted, so the spot stays on Chinese punctuation.
|
||||
std::wstring _ResolveSmartPunctuation(WCHAR wch, WCHAR precedingChar);
|
||||
bool _QueueRepeatedSmartPunctuationReplacement(WCHAR wch);
|
||||
void _NoteKeyForSmartPunctuation(UINT code, WCHAR wch, bool isEaten);
|
||||
void _ResetSmartPunctuationHistory();
|
||||
void _UpdateSmartPunctuationShadow(UINT code, WCHAR wch, bool isEaten);
|
||||
void _InvalidateSmartPunctuationShadow();
|
||||
@@ -491,9 +495,16 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
|
||||
// Last smart-punctuation commit, used to detect a backspace rejection.
|
||||
WCHAR _smartPunctuationKey = 0;
|
||||
WCHAR _smartPunctuationPrecedingChar = 0;
|
||||
bool _smartPunctuationCommittedAscii = false;
|
||||
bool _smartPunctuationAsciiRejected = false;
|
||||
WCHAR _smartPunctuationPrecedingChar = 0;
|
||||
bool _smartPunctuationCommittedAscii = false;
|
||||
bool _smartPunctuationAsciiRejected = false;
|
||||
ULONGLONG _smartPunctuationCommitTick = 0;
|
||||
uint64_t _smartPunctuationFocusToken = 0;
|
||||
HWND _smartPunctuationForegroundWindow = nullptr;
|
||||
WCHAR _pendingSmartPunctuationReplacement = 0;
|
||||
uint64_t _pendingSmartPunctuationFocusToken = 0;
|
||||
HWND _pendingSmartPunctuationForegroundWindow = nullptr;
|
||||
ULONGLONG _pendingSmartPunctuationDeadline = 0;
|
||||
|
||||
// Last character known to have reached the application. Hosts such as the
|
||||
// VS Code terminal back the context with a proxy text store that only ever
|
||||
|
||||
+5
-1
@@ -322,15 +322,19 @@ constexpr UINT SmartPunctuationChanged = 11;
|
||||
constexpr UINT PairedPunctuationChanged = 12;
|
||||
// Whether ';' is an input key for the Microsoft shuangpin profile.
|
||||
constexpr UINT MicrosoftShuangpinChanged = 13;
|
||||
// Replace a repeated smart ASCII punctuation with its Chinese mapping.
|
||||
constexpr UINT SmartPunctuationRepeatToChineseChanged = 14;
|
||||
// Highest opcode this build understands. Unknown higher opcodes must be
|
||||
// ignored by the worker reader (never tear down the pipe).
|
||||
constexpr UINT MaxKnown = MicrosoftShuangpinChanged;
|
||||
constexpr UINT MaxKnown = SmartPunctuationRepeatToChineseChanged;
|
||||
} // namespace DataToTsfWorkerThreadMsgType
|
||||
|
||||
inline std::atomic_bool PagingCommaPeriodEnabled{false};
|
||||
// Default on: matches rime-ice digit_separators behavior until Server syncs.
|
||||
inline std::atomic_bool SmartPunctuationEnabled{true};
|
||||
// Default on until the Server sends the persisted setting.
|
||||
inline std::atomic_bool SmartPunctuationRepeatToChineseEnabled{true};
|
||||
// Default on until the Server sends the persisted setting.
|
||||
inline std::atomic_bool PairedPunctuationEnabled{true};
|
||||
inline std::atomic_bool MicrosoftShuangpinEnabled{false};
|
||||
inline thread_local bool g_connected = false;
|
||||
|
||||
@@ -1128,6 +1128,11 @@ STDAPI CMetasequoiaIME::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARA
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (static_cast<ULONG_PTR>(GetMessageExtraInfo()) == SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO)
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
PerfTimer onTestKeyDownTimer;
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
_TrackModifierHotkeyArming(wParam, lParam, false);
|
||||
@@ -1649,6 +1654,11 @@ STDAPI CMetasequoiaIME::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lP
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (static_cast<ULONG_PTR>(GetMessageExtraInfo()) == SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO)
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
PerfTimer onKeyDownTimer;
|
||||
const uint64_t focusGeneration = _deferredKeyFocusGeneration;
|
||||
(void)_DispatchKeyDown(pContext, wParam, lParam, pIsEaten, nullptr, nullptr,
|
||||
@@ -2016,6 +2026,11 @@ STDAPI CMetasequoiaIME::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (static_cast<ULONG_PTR>(GetMessageExtraInfo()) == SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO)
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
|
||||
@@ -2059,6 +2074,11 @@ STDAPI CMetasequoiaIME::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lPar
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (static_cast<ULONG_PTR>(GetMessageExtraInfo()) == SMART_PUNCTUATION_SENDINPUT_EXTRA_INFO)
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
|
||||
if (_HasDeferredKeyBarrier())
|
||||
|
||||
@@ -988,6 +988,12 @@ HRESULT CMetasequoiaIME::_HandleCompositionPunctuation(TfEditCookie ec, _In_ ITf
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
PerfTimer timer;
|
||||
|
||||
if (_QueueRepeatedSmartPunctuationReplacement(wch))
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Get punctuation char from composition processor engine
|
||||
//
|
||||
|
||||
Reference in new issue
Block a user