Merge pull request #642 from Ehtasham-Yasin/feat/shuangpin-shift-enter

feat(server): submit converted Shuangpin with Shift+Enter
This commit is contained in:
Neptrue_Lin authored and GitHub committed 2026-10-07 16:53:13 +08:00
commit d627ca1ad5
6 files changed
+71 -6

No files matched your search

+17 -1
View File
@@ -905,7 +905,13 @@ void HandleImeKey(uint64_t client_id, uint64_t activation_epoch, uint64_t reques
const bool shift_only = (Global::ModifiersDown & 0b00000111u) == 0b00000001u;
const bool chinese_scheme = g_inputSession && (g_inputSession->current_scheme_type() == SchemeType::Quanpin ||
g_inputSession->current_scheme_type() == SchemeType::Shuangpin);
if (Global::Keycode == VK_RETURN && !input_before_key.empty() && GetConfiguredEnterLearnsEnglishWord())
const bool pinyin_commit =
FanyImeIpc::IsPinyinCommitKey(Global::Keycode, Global::ModifiersDown) && !input_before_key.empty();
const bool convert_shuangpin = pinyin_commit && g_inputSession->current_scheme_type() == SchemeType::Shuangpin &&
!g_english_input_mode && !g_r_mode_triggered &&
!IsSpecialModeCompositionActive(input_before_key);
if (Global::Keycode == VK_RETURN && !input_before_key.empty() && !convert_shuangpin &&
GetConfiguredEnterLearnsEnglishWord())
{
std::string english_word;
// V 模式没有触发标记,按输入串认:回车上屏的 V123 不是要学的英文词。
@@ -916,6 +922,16 @@ void HandleImeKey(uint64_t client_id, uint64_t activation_epoch, uint64_t reques
english_word = g_r_mode_triggered ? "R" + input_before_key : input_before_key;
EnqueueLearnEnteredEnglishWordTask(english_word);
}
if (pinyin_commit)
{
const std::string raw = g_r_mode_triggered ? "R" + input_before_key : input_before_key;
const std::string text = FanyImeIpc::EnteredPinyinText(
raw, convert_shuangpin ? g_inputSession->get_quanpin() : std::string{}, convert_shuangpin);
Global::candidate_ui.selected_text = string_to_wstring(GlobalIme::composition.creating_word.word + text);
Global::MsgTypeToTsf = Global::DataFromServerMsgType::CommitExactText;
SendCurrentDataToClient(client_id, activation_epoch, request_id);
return;
}
if (chinese_scheme && !g_english_input_mode && GetConfiguredQuickPhraseEnabled() && input_before_key.empty() &&
Global::Keycode == 'K' && Global::Wch == L'K' && shift_only)
g_quick_phrase_triggered = true;
+13
View File
@@ -30,6 +30,19 @@ constexpr bool IsEnglishModeToggleKey(uint32_t keycode, uint32_t modifiers_down)
return keycode == static_cast<uint32_t>('E') && (modifiers_down & kKeyModifierMask) == kEnglishModeToggleModifiers;
}
constexpr bool IsPinyinCommitKey(uint32_t keycode, uint32_t modifiers_down)
{
return keycode == 0x0D && (modifiers_down & kKeyModifierMask) == kModifierShift;
}
inline std::string EnteredPinyinText(const std::string &raw, std::string quanpin, bool convert_shuangpin)
{
if (!convert_shuangpin || quanpin.empty())
return raw;
quanpin.erase(std::remove(quanpin.begin(), quanpin.end(), '\''), quanpin.end());
return quanpin;
}
// Return -1 / +1 for the first / last Han character, or zero for an ordinary key.
constexpr int WordToCharacterDirection(uint32_t keycode, uint32_t character, uint32_t modifiers, bool enabled,
bool minus_equal)
@@ -1,6 +1,27 @@
#include "ipc/input_key_policy.h"
#include "tests/includes/test_framework.h"
TEST_CASE(pinyin_commit_requires_shift_enter)
{
REQUIRE(FanyImeIpc::IsPinyinCommitKey(0x0D, 1));
REQUIRE(FanyImeIpc::IsPinyinCommitKey(0x0D, 1 | FanyImeIpc::kModifierUiLess));
for (unsigned modifiers = 0; modifiers < 8; ++modifiers)
REQUIRE(FanyImeIpc::IsPinyinCommitKey(0x0D, modifiers) == (modifiers == 1));
REQUIRE(!FanyImeIpc::IsPinyinCommitKey('A', 1));
}
TEST_CASE(pinyin_commit_converts_shuangpin_and_preserves_raw_fallback)
{
using FanyImeIpc::EnteredPinyinText;
REQUIRE_EQ(EnteredPinyinText("nihc", "ni'hao", true), std::string("nihao"));
REQUIRE_EQ(EnteredPinyinText("ni'hc", "ni'hao'", true), std::string("nihao"));
REQUIRE_EQ(EnteredPinyinText("vgh", "zheng'h", true), std::string("zhengh"));
REQUIRE_EQ(EnteredPinyinText("nihc", "ni'hao", false), std::string("nihc"));
REQUIRE_EQ(EnteredPinyinText("hello", "", true), std::string("hello"));
REQUIRE_EQ(EnteredPinyinText("U4E00", "", false), std::string("U4E00"));
REQUIRE_EQ(EnteredPinyinText("", "", true), std::string(""));
}
TEST_CASE(word_to_character_uses_only_the_selected_unmodified_key_pair)
{
using FanyImeIpc::WordToCharacterDirection;
+12 -3
View File
@@ -141,6 +141,12 @@ bool IsTranslationCommitShortcut(UINT code, UINT modifiers)
(GetAsyncKeyState(VK_RWIN) & 0x8000) == 0;
}
bool IsPinyinCommitShortcut(UINT code, UINT modifiers)
{
return code == VK_RETURN && (modifiers & 0b00000111u) == 0b00000001u && (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 &&
(GetAsyncKeyState(VK_RWIN) & 0x8000) == 0;
}
bool IsCharacterSetInputModeToggle(UINT code, UINT modifiers)
{
return FanyImeProtocol::IsCharacterSetShortcut(code, modifiers) && (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 &&
@@ -333,9 +339,12 @@ BOOL CMetasequoiaIME::_IsKeyEaten( //
return TRUE;
}
// 候选框开着的时候 Ctrl+Enter 上屏高亮候选的译文;没有候选框时它仍然属于应用。
if (!freshCompositionState && _candidateMode != CANDIDATE_NONE &&
IsTranslationCommitShortcut(*pCodeOut, shortcutModifiers))
// Ctrl+Enter commits a candidate translation. Shift+Enter commits the
// Server's converted spelling, including compositions without candidates.
if (!freshCompositionState &&
((_candidateMode != CANDIDATE_NONE && IsTranslationCommitShortcut(*pCodeOut, shortcutModifiers)) ||
(!_serverUnavailableFallbackActive && pCompositionProcessorEngine->GetVirtualKeyLength() > 0 &&
IsPinyinCommitShortcut(*pCodeOut, shortcutModifiers))))
{
if (pKeyState)
{
+1
View File
@@ -17,6 +17,7 @@ KEYSTROKE_FUNCTION SegmentEditFunction(UINT code, UINT modifiers);
UINT CaptureIpcModifiers();
bool IsEnglishInputModeToggle(UINT code, UINT modifiers);
bool IsTranslationCommitShortcut(UINT code, UINT modifiers);
bool IsPinyinCommitShortcut(UINT code, UINT modifiers);
bool IsCharacterSetInputModeToggle(UINT code, UINT modifiers);
void PostOwnerMessageWithSyncFallback(HWND window, UINT message, WPARAM wParam = 0, LPARAM lParam = 0);
bool IsShiftVk(UINT code);
@@ -418,8 +418,13 @@ bool CMetasequoiaIME::_ClassifyDeferredKeyDown(_In_ ITfContext *pContext, WPARAM
// 同上:译文上屏也要在这条 Ctrl/Alt 拦截之前分类,否则这颗键会被交还给应用。
const bool projectedCandidateActive =
_deferredKeyProjectionValid ? _deferredProjectedCandidateActive : (_candidateMode == CANDIDATE_ORIGINAL);
if (projectedImeOpen && !_IsKeyboardDisabled() && projectedCandidateActive &&
IsTranslationCommitShortcut(*classifiedCode, capturedModifiers))
const bool projectedInputActive = _deferredKeyProjectionValid
? _deferredProjectedInputLength > 0
: _pCompositionProcessorEngine->GetVirtualKeyLength() > 0;
if (projectedImeOpen && !_IsKeyboardDisabled() &&
((projectedCandidateActive && IsTranslationCommitShortcut(*classifiedCode, capturedModifiers)) ||
(!_serverUnavailableFallbackActive && projectedInputActive &&
IsPinyinCommitShortcut(*classifiedCode, capturedModifiers))))
{
keyState->Category = CATEGORY_CANDIDATE;
keyState->Function = FUNCTION_SERVER_CANDIDATE_KEY;