#include #include #include "fcitx-utils/log.h" #include "fcitx/candidatelist.h" #include "../src/candidatemenu.h" using namespace fcitx; using namespace fcitx::m3ui; namespace { class EngineActions : public ActionableCandidateList { public: explicit EngineActions(bool available) : available_(available) {} bool hasAction(const CandidateWord &) const override { return available_; } std::vector candidateActions(const CandidateWord &) const override { CandidateAction action; action.setId(-42); // IDs must not be interpreted by the UI. action.setText("Engine action"); CandidateAction separator; separator.setSeparator(true); return {separator, action}; } void triggerAction(const CandidateWord &, int) override {} private: bool available_; }; } // namespace int main() { CommonCandidateList list; list.setPageSize(5); list.append(Text("候选词")); list.append(Text("保留的词")); // Never invent a blocking action when ranking operations are unavailable. auto menu = candidateMenu(list, list.candidate(0)); FCITX_ASSERT(menu.actions.empty()); // An engine can have an action interface but no actions for this word. list.setActionableImpl(std::make_unique(false)); menu = candidateMenu(list, list.candidate(0)); FCITX_ASSERT(menu.actions.empty()); // Keep engine actions and IDs intact; omit separators, never append blocking. list.setActionableImpl(std::make_unique(true)); menu = candidateMenu(list, list.candidate(0)); FCITX_ASSERT(menu.actions.size() == 1); FCITX_ASSERT(menu.actions[0].id() == -42); FCITX_ASSERT(menu.actions[0].text() == "Engine action"); FCITX_ASSERT(list.size() == 2); FCITX_ASSERT(list.candidate(0).text().toString() == "候选词"); // Do not offer an ineffective block action for a read-only list. DisplayOnlyCandidateList readOnly; readOnly.setContent(std::vector{"只读"}); menu = candidateMenu(readOnly, readOnly.candidate(0)); FCITX_ASSERT(menu.actions.empty()); }