From 2949d970b84b45c779cb532597260aafcd0ecc93 Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Fri, 27 Jun 2025 17:02:40 -0700 Subject: [PATCH] Use upper case "V" for shuangpin's quickphrase triggering. Fix #225 --- im/pinyin/pinyin.cpp | 20 ++++--- test/testpinyin.cpp | 129 ++++++++++++++++++++++++++----------------- 2 files changed, 92 insertions(+), 57 deletions(-) diff --git a/im/pinyin/pinyin.cpp b/im/pinyin/pinyin.cpp index 78fc52c..97ab616 100644 --- a/im/pinyin/pinyin.cpp +++ b/im/pinyin/pinyin.cpp @@ -2004,14 +2004,20 @@ void PinyinEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &event) { (event.key().check(FcitxKey_apostrophe) && !state->context_.empty()) || checkSp(event, state)) { // first v, use it to trigger quickphrase - if (!state->context_.useShuangpin() && quickphrase() && - *config_.useVAsQuickphrase && event.key().check(FcitxKey_v) && + if (*config_.useVAsQuickphrase && quickphrase() && state->context_.empty()) { - - quickphrase()->call( - inputContext, "", "v", "", "", Key(FcitxKey_None)); - event.filterAndAccept(); - return; + const bool isSp = state->context_.useShuangpin(); + if ((event.key().check(FcitxKey_v) && !isSp) || + (event.key().check(FcitxKey_V) && isSp)) { + const std::string leadingString = + Key::keySymToUTF8(event.key().sym()); + + quickphrase()->call( + inputContext, "", leadingString, "", "", + Key(FcitxKey_None)); + event.filterAndAccept(); + return; + } } event.filterAndAccept(); if (!state->context_.type(keyStr.get())) { diff --git a/test/testpinyin.cpp b/test/testpinyin.cpp index 279de65..3004387 100644 --- a/test/testpinyin.cpp +++ b/test/testpinyin.cpp @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -54,7 +56,21 @@ void findAndSelectCandidate(InputContext *ic, std::string_view word) { candList->candidate(findCandidateOrDie(ic, word)).select(ic); } -void testBasic(Instance *instance) { +void sendControlSpace(AddonInstance *testfrontend, InputContext *ic) { + for (int i = 0; i < 2; i++) { + testfrontend->call(ic->uuid(), + Key("Control_L"), false); + testfrontend->call( + ic->uuid(), Key("Control+space"), false); + testfrontend->call(ic->uuid(), + Key("Control+space"), true); + testfrontend->call( + ic->uuid(), Key("Control+Control_L"), true); + ic->reset(); + } +} + +void setup(Instance *instance) { instance->eventDispatcher().schedule([instance]() { auto *pinyin = instance->addonManager().addon("pinyin", true); FCITX_ASSERT(pinyin); @@ -64,8 +80,16 @@ void testBasic(Instance *instance) { InputMethodGroupItem("keyboard-us")); defaultGroup.inputMethodList().push_back( InputMethodGroupItem("pinyin")); + defaultGroup.inputMethodList().push_back( + InputMethodGroupItem("shuangpin")); defaultGroup.setDefaultInputMethod(""); instance->inputMethodManager().setGroup(std::move(defaultGroup)); + }); +} + +void testBasic(Instance *instance) { + instance->eventDispatcher().schedule([instance]() { + auto *pinyin = instance->addonManager().addon("pinyin"); auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); @@ -73,8 +97,10 @@ void testBasic(Instance *instance) { testfrontend->call("ni"); testfrontend->call("ni"); testfrontend->call("你hao"); - testfrontend->call(uuid, Key("Control+space"), - false); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); + testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("`"), false); testfrontend->call(uuid, Key("n"), false); @@ -98,8 +124,6 @@ void testBasic(Instance *instance) { testfrontend->call(uuid, Key("h"), false); testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("o"), false); - auto *ic = instance->inputContextManager().findByUUID(uuid); - FCITX_ASSERT(ic); // Make a partial selection, we do search because the data might change. findAndSelectCandidate(ic, "你"); testfrontend->call(uuid, Key("Return"), false); @@ -111,10 +135,7 @@ void testBasic(Instance *instance) { testfrontend->call(uuid, Key("h"), false); testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("o"), false); - testfrontend->call(uuid, Key("Control+space"), - false); - testfrontend->call(uuid, Key("Control+space"), - false); + sendControlSpace(testfrontend, ic); testfrontend->call("你hao"); testfrontend->call(uuid, Key("n"), false); @@ -124,10 +145,7 @@ void testBasic(Instance *instance) { testfrontend->call(uuid, Key("o"), false); // Make a partial selection, we do search because the data might change. findAndSelectCandidate(ic, "你"); - testfrontend->call(uuid, Key("Control+space"), - false); - testfrontend->call(uuid, Key("Control+space"), - false); + sendControlSpace(testfrontend, ic); RawConfig config; config.setValueByPath("SwitchInputMethodBehavior", @@ -141,10 +159,7 @@ void testBasic(Instance *instance) { auto sentence = ic->inputPanel().candidateList()->candidate(0).text().toString(); testfrontend->call(sentence); - testfrontend->call(uuid, Key("Control+space"), - false); - testfrontend->call(uuid, Key("Control+space"), - false); + sendControlSpace(testfrontend, ic); config.setValueByPath("SwitchInputMethodBehavior", "Clear"); pinyin->setConfig(config); @@ -153,10 +168,7 @@ void testBasic(Instance *instance) { testfrontend->call(uuid, Key("h"), false); testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("o"), false); - testfrontend->call(uuid, Key("Control+space"), - false); - testfrontend->call(uuid, Key("Control+space"), - false); + sendControlSpace(testfrontend, ic); }); } @@ -165,9 +177,9 @@ void testSelectByChar(Instance *instance) { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); - - testfrontend->call(uuid, Key("Control+space"), - false); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); testfrontend->call(uuid, Key("n"), false); testfrontend->call(uuid, Key("i"), false); @@ -187,8 +199,6 @@ void testSelectByChar(Instance *instance) { testfrontend->call(uuid, Key("g"), false); testfrontend->call("你好主病"); - auto *ic = instance->inputContextManager().findByUUID(uuid); - FCITX_ASSERT(ic); findAndSelectCandidate(ic, "你好"); auto candidateIdx = findCandidateOrDie(ic, "公主"); ic->inputPanel().candidateList()->toBulkCursor()->setGlobalCursorIndex( @@ -204,9 +214,9 @@ void testUppercase(Instance *instance) { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); - - testfrontend->call(uuid, Key("Control+space"), - false); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); testfrontend->call("Apple"); @@ -234,17 +244,15 @@ void testForget(Instance *instance) { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); - - testfrontend->call(uuid, Key("Control+space"), - false); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); testfrontend->call(uuid, Key("n"), false); testfrontend->call(uuid, Key("i"), false); testfrontend->call(uuid, Key("h"), false); testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("o"), false); - auto *ic = instance->inputContextManager().findByUUID(uuid); - FCITX_ASSERT(ic); auto *candidateList = ic->inputPanel().candidateList().get(); const auto &cand = candidateList->candidate(0); auto *actionable = candidateList->toActionable(); @@ -265,9 +273,8 @@ void testActionInStrokeFilter(Instance *instance) { testfrontend->call("testapp"); auto *ic = instance->inputContextManager().findByUUID(uuid); FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); - testfrontend->call(uuid, Key("Control+space"), - false); // Target ppp for 彡 testfrontend->call(uuid, Key("p"), false); testfrontend->call(uuid, Key("p"), false); @@ -298,9 +305,9 @@ void testPin(Instance *instance) { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); - - testfrontend->call(uuid, Key("Control+space"), - false); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); testfrontend->call(uuid, Key("t"), false); testfrontend->call(uuid, Key("o"), false); @@ -309,8 +316,6 @@ void testPin(Instance *instance) { testfrontend->call(uuid, Key("y"), false); testfrontend->call(uuid, Key("i"), false); testfrontend->call(uuid, Key("n"), false); - auto *ic = instance->inputContextManager().findByUUID(uuid); - FCITX_ASSERT(ic); auto index1 = findCandidateOrDie(ic, "同音"); auto index2 = findCandidateOrDie(ic, "痛饮"); const auto oldIndex1 = index1; @@ -374,16 +379,14 @@ void testQuickPhraseTrigger(Instance *instance) { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); - - testfrontend->call(uuid, Key("Control+space"), - false); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); testfrontend->call(uuid, Key("w"), false); testfrontend->call(uuid, Key("w"), false); testfrontend->call(uuid, Key("w"), false); testfrontend->call(uuid, Key("."), false); - auto *ic = instance->inputContextManager().findByUUID(uuid); - FCITX_ASSERT(ic); FCITX_ASSERT(ic->inputPanel().preedit().toString() == "www."); ic->reset(); @@ -428,14 +431,37 @@ void testQuickPhraseTrigger(Instance *instance) { }); } +void testVQuickPhraseTrigger(Instance *instance) { + instance->eventDispatcher().schedule([instance]() { + auto *testfrontend = instance->addonManager().addon("testfrontend"); + auto uuid = + testfrontend->call("testapp"); + + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); + + testfrontend->call(uuid, Key("v"), false); + testfrontend->call(uuid, Key("."), false); + FCITX_ASSERT(ic->inputPanel().preedit().toString() == "v."); + + instance->setCurrentInputMethod(ic, "shuangpin", true); + ic->reset(); + testfrontend->call(uuid, Key("V"), false); + testfrontend->call(uuid, Key("."), false); + FCITX_ASSERT(ic->inputPanel().preedit().toString() == "V."); + }); +} + void testPunctuation(Instance *instance) { instance->eventDispatcher().schedule([instance]() { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); - testfrontend->call(uuid, Key("Control+space"), - false); testfrontend->call("。"); FCITX_ASSERT(testfrontend->call( uuid, Key("."), false)); @@ -468,9 +494,10 @@ void testPunctuationPart2(Instance *instance) { auto *testfrontend = instance->addonManager().addon("testfrontend"); auto uuid = testfrontend->call("testapp"); + auto *ic = instance->inputContextManager().findByUUID(uuid); + FCITX_ASSERT(ic); + instance->setCurrentInputMethod(ic, "pinyin", true); - testfrontend->call(uuid, Key("Control+space"), - false); FCITX_ASSERT(!testfrontend->call( uuid, Key("1"), false)); FCITX_ASSERT(!testfrontend->call( @@ -505,6 +532,7 @@ int main() { fcitx::Log::setLogRule("default=5,pinyin=5"); Instance instance(FCITX_ARRAY_SIZE(argv), argv); instance.addonManager().registerDefaultLoader(nullptr); + setup(&instance); testBasic(&instance); testSelectByChar(&instance); testUppercase(&instance); @@ -512,6 +540,7 @@ int main() { testActionInStrokeFilter(&instance); testPin(&instance); testQuickPhraseTrigger(&instance); + testVQuickPhraseTrigger(&instance); testPunctuation(&instance); instance.exec(); endTestEvent.reset();