diff --git a/CMakeLists.txt b/CMakeLists.txt index 51da2ff..1268dd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.6.0) -project(fcitx5-chinese-addons VERSION 5.1.5) +project(fcitx5-chinese-addons VERSION 5.1.6) find_package(ECM REQUIRED 1.0.0) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) diff --git a/im/table/candidate.cpp b/im/table/candidate.cpp index 889e2d9..de8f2cd 100644 --- a/im/table/candidate.cpp +++ b/im/table/candidate.cpp @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include namespace fcitx { @@ -92,4 +94,34 @@ void TablePredictCandidateWord::select(InputContext *inputContext) const { state_->resetAndPredict(); } +TableActionableCandidateList::TableActionableCandidateList(TableState *state) + : state_(state) {} + +bool TableActionableCandidateList::hasAction( + const CandidateWord &candidate) const { + return dynamic_cast(&candidate); +} +std::vector TableActionableCandidateList::candidateActions( + const CandidateWord &candidate) const { + if (!hasAction(candidate)) { + return {}; + } + std::vector actions; + CandidateAction action; + action.setId(0); + action.setText(_("Forget word")); + actions.push_back(std::move(action)); + return actions; +} + +void TableActionableCandidateList::triggerAction(const CandidateWord &candidate, + int id) { + if (id != 0) { + return; + } + if (const auto *tableCandidate = + dynamic_cast(&candidate)) { + state_->forgetCandidateWord(tableCandidate->idx_); + } +} } // namespace fcitx diff --git a/im/table/candidate.h b/im/table/candidate.h index b96c424..99d2cb2 100644 --- a/im/table/candidate.h +++ b/im/table/candidate.h @@ -9,10 +9,12 @@ #include "engine.h" #include +#include #include #include #include #include +#include namespace fcitx { @@ -62,6 +64,19 @@ class TablePredictCandidateWord : public CandidateWord { std::string word_; }; +class TableActionableCandidateList : public ActionableCandidateList { +public: + TableActionableCandidateList(TableState *state); + + bool hasAction(const CandidateWord &candidate) const override; + std::vector + candidateActions(const CandidateWord &candidate) const override; + void triggerAction(const CandidateWord &candidate, int id) override; + +private: + TableState *state_; +}; + } // namespace fcitx #endif // _TABLE_CANDIDATE_H_ diff --git a/im/table/state.cpp b/im/table/state.cpp index 0a8bfc5..cf189b7 100644 --- a/im/table/state.cpp +++ b/im/table/state.cpp @@ -1332,6 +1332,8 @@ void TableState::updateUI(bool keepOldCursor, bool maybePredict) { } candidateList->setGlobalCursorIndex(cursor); } + candidateList->setActionableImpl( + std::make_unique(this)); inputPanel.setCandidateList(std::move(candidateList)); } const bool useClientPreedit = diff --git a/org.fcitx.Fcitx5.Addon.ChineseAddons.metainfo.xml.in b/org.fcitx.Fcitx5.Addon.ChineseAddons.metainfo.xml.in index add9e0c..74a2404 100644 --- a/org.fcitx.Fcitx5.Addon.ChineseAddons.metainfo.xml.in +++ b/org.fcitx.Fcitx5.Addon.ChineseAddons.metainfo.xml.in @@ -14,6 +14,7 @@ https://github.com/fcitx/fcitx5-chinese-addons Fcitx + diff --git a/test/testtable.cpp b/test/testtable.cpp index 5a42f8a..3141a11 100644 --- a/test/testtable.cpp +++ b/test/testtable.cpp @@ -6,25 +6,43 @@ */ #include "testdir.h" #include "testfrontend_public.h" +#include #include +#include +#include #include +#include #include #include #include #include +#include #include #include #include -#include +#include +#include using namespace fcitx; -void scheduleEvent(EventDispatcher *dispatcher, Instance *instance) { - dispatcher->schedule([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 scheduleEvent(Instance *instance) { + instance->eventDispatcher().schedule([instance]() { auto *table = instance->addonManager().addon("table", true); FCITX_ASSERT(table); }); - dispatcher->schedule([instance]() { + instance->eventDispatcher().schedule([instance]() { auto defaultGroup = instance->inputMethodManager().currentGroup(); defaultGroup.inputMethodList().clear(); defaultGroup.inputMethodList().push_back( @@ -83,7 +101,7 @@ void scheduleEvent(EventDispatcher *dispatcher, Instance *instance) { testfrontend->call(uuid, Key("A"), false); testfrontend->call(uuid, Key("Return"), false); }); - dispatcher->schedule([dispatcher, instance]() { + instance->eventDispatcher().schedule([instance]() { auto defaultGroup = instance->inputMethodManager().currentGroup(); defaultGroup.inputMethodList().clear(); defaultGroup.inputMethodList().push_back( @@ -134,15 +152,37 @@ void scheduleEvent(EventDispatcher *dispatcher, Instance *instance) { testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("a"), false); - // This comma trigger only match commit. testfrontend->call(uuid, Key("a"), false); testfrontend->call(uuid, Key("5"), false); - dispatcher->schedule([dispatcher, instance]() { - dispatcher->detach(); - instance->exit(); - }); + testfrontend->call(uuid, Key("a"), false); + testfrontend->call(uuid, Key("a"), false); + testfrontend->call(uuid, Key("a"), false); + testfrontend->call(uuid, Key("a"), false); + + auto *candidateList = ic->inputPanel().candidateList().get(); + FCITX_ASSERT(candidateList); + auto idx = findCandidateOrDie(ic, "工工工工"); + auto *actionable = ic->inputPanel().candidateList()->toActionable(); + FCITX_ASSERT(actionable); + FCITX_ASSERT(actionable->hasAction(candidateList->candidate(idx))); + FCITX_ASSERT( + actionable->candidateActions(candidateList->candidate(idx))[0] + .id() == 0); + actionable->triggerAction(candidateList->candidate(idx), 0); + + // Check if 工工工工 is deleted. + candidateList = ic->inputPanel().candidateList().get(); + FCITX_ASSERT(candidateList); + for (int i = 0; i < candidateList->size(); i++) { + FCITX_ASSERT(candidateList->candidate(i).text().toString() != + "工工工工") + << "Candidate " << i << " is not deleted."; + } + ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel, + true); }); + instance->eventDispatcher().schedule([instance]() { instance->exit(); }); } int main() { @@ -156,14 +196,13 @@ int main() { fcitx::Log::setLogRule("default=5,table=5,libime-table=5"); char arg0[] = "testtable"; char arg1[] = "--disable=all"; - char arg2[] = "--enable=testim,testfrontend,table,quickphrase,punctuation," - "pinyinhelper"; + char arg2[] = + "--enable=testui,testim,testfrontend,table,quickphrase,punctuation," + "pinyinhelper"; char *argv[] = {arg0, arg1, arg2}; Instance instance(FCITX_ARRAY_SIZE(argv), argv); instance.addonManager().registerDefaultLoader(nullptr); - EventDispatcher dispatcher; - dispatcher.attach(&instance.eventLoop()); - scheduleEvent(&dispatcher, &instance); + scheduleEvent(&instance); instance.exec(); return 0;