From 6f991544b7bd71506dcf862bab04988b1ea0d0ad Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Thu, 11 Apr 2024 17:57:13 -0700 Subject: [PATCH] Use new selectCustom to handle partial selection Fix #164 --- im/pinyin/pinyin.cpp | 33 +++++++++++++------ test/testpinyin.cpp | 78 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 84 insertions(+), 27 deletions(-) diff --git a/im/pinyin/pinyin.cpp b/im/pinyin/pinyin.cpp index fd4f04b8..dc69b7fe 100644 --- a/im/pinyin/pinyin.cpp +++ b/im/pinyin/pinyin.cpp @@ -2221,24 +2221,37 @@ void PinyinEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &event) { } else if (int idx = event.key().keyListIndex(*config_.selectCharFromPhrase); idx >= 0) { - if (candidateList && candidateList->size() && + if (candidateList && !candidateList->empty() && candidateList->cursorIndex() >= 0) { - auto str = - candidateList->candidate(candidateList->cursorIndex()) - .text() - .toStringForCommit(); + const auto &candidate = + candidateList->candidate(candidateList->cursorIndex()); + auto str = candidate.text().toStringForCommit(); // Validate string and length. if (auto len = utf8::lengthValidated(str); len != utf8::INVALID_LENGTH && len > static_cast(idx)) { // Get idx-th char. - auto charRange = + const std::string_view chr = std::next(utf8::MakeUTF8CharRange(str).begin(), idx) - .charRange(); - std::string chr(charRange.first, charRange.second); - inputContext->commitString(chr); + .view(); + auto segmentLength = state->context_.size() - + state->context_.selectedLength(); + const auto *pyCandidate = + dynamic_cast(&candidate); + if (pyCandidate) { + const auto &contextCandidates = + state->context_.candidatesToCursor(); + if (pyCandidate->idx_ < contextCandidates.size()) { + const auto &sentence = + contextCandidates[pyCandidate->idx_].sentence(); + const auto candidateSegmentLength = + sentence.back()->to()->index(); + segmentLength = + std::min(segmentLength, candidateSegmentLength); + } + } + state->context_.selectCustom(segmentLength, chr); event.filterAndAccept(); - state->context_.clear(); } } } diff --git a/test/testpinyin.cpp b/test/testpinyin.cpp index 4f7f1d08..09f73454 100644 --- a/test/testpinyin.cpp +++ b/test/testpinyin.cpp @@ -17,12 +17,30 @@ #include #include #include +#include using namespace fcitx; std::unique_ptr endTestEvent; void testPunctuationPart2(EventDispatcher *dispatcher, Instance *instance); +int findCandidateOrDie(InputContext *ic, std::string_view word) { + auto candList = ic->inputPanel().candidateList(); + for (int i = 0; i < candList->toBulk()->totalSize(); i++) { + const auto &candidate = candList->toBulk()->candidateFromAll(i); + if (candidate.text().toString() == word) { + return i; + } + } + FCITX_ASSERT(false) << "Failed to find candidate: " << word; + return -1; +} + +void findAndSelectCandidate(InputContext *ic, std::string_view word) { + auto candList = ic->inputPanel().candidateList(); + candList->candidate(findCandidateOrDie(ic, word)).select(ic); +} + void testBasic(EventDispatcher *dispatcher, Instance *instance) { dispatcher->schedule([instance]() { auto *pinyin = instance->addonManager().addon("pinyin", true); @@ -67,17 +85,10 @@ void testBasic(EventDispatcher *dispatcher, 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); + auto *ic = instance->inputContextManager().findByUUID(uuid); FCITX_ASSERT(ic); // Make a partial selection, we do search because the data might change. - auto candList = ic->inputPanel().candidateList(); - for (int i = 0; i < candList->size(); i++) { - auto &candidate = candList->candidate(i); - if (candidate.text().toString() == "你") { - candidate.select(ic); - break; - } - } + findAndSelectCandidate(ic, "你"); testfrontend->call(uuid, Key("Return"), false); // Test switch input method. @@ -99,14 +110,7 @@ void testBasic(EventDispatcher *dispatcher, Instance *instance) { testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("o"), false); // Make a partial selection, we do search because the data might change. - candList = ic->inputPanel().candidateList(); - for (int i = 0; i < candList->size(); i++) { - auto &candidate = candList->candidate(i); - if (candidate.text().toString() == "你") { - candidate.select(ic); - break; - } - } + findAndSelectCandidate(ic, "你"); testfrontend->call(uuid, Key("Control+space"), false); testfrontend->call(uuid, Key("Control+space"), @@ -143,6 +147,45 @@ void testBasic(EventDispatcher *dispatcher, Instance *instance) { }); } +void testSelectByChar(EventDispatcher *dispatcher, Instance *instance) { + dispatcher->schedule([instance, dispatcher]() { + auto *testfrontend = instance->addonManager().addon("testfrontend"); + auto uuid = + testfrontend->call("testapp"); + + testfrontend->call(uuid, Key("Control+space"), + false); + + 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); + testfrontend->call(uuid, Key("g"), false); + testfrontend->call(uuid, Key("o"), false); + testfrontend->call(uuid, Key("n"), false); + testfrontend->call(uuid, Key("g"), false); + testfrontend->call(uuid, Key("z"), false); + testfrontend->call(uuid, Key("h"), false); + testfrontend->call(uuid, Key("u"), false); + testfrontend->call(uuid, Key("b"), false); + testfrontend->call(uuid, Key("i"), false); + testfrontend->call(uuid, Key("n"), false); + 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( + candidateIdx); + // With default config, this should select "主". + testfrontend->call(uuid, Key("]"), false); + findAndSelectCandidate(ic, "病"); + }); +} + void testPunctuation(EventDispatcher *dispatcher, Instance *instance) { dispatcher->schedule([instance, dispatcher]() { auto *testfrontend = instance->addonManager().addon("testfrontend"); @@ -226,6 +269,7 @@ int main() { EventDispatcher dispatcher; dispatcher.attach(&instance.eventLoop()); testBasic(&dispatcher, &instance); + testSelectByChar(&dispatcher, &instance); testPunctuation(&dispatcher, &instance); instance.exec(); endTestEvent.reset();