diff --git a/src/lib/fcitx-utils/macros.h b/src/lib/fcitx-utils/macros.h index 0d7a285e4..f2863a686 100644 --- a/src/lib/fcitx-utils/macros.h +++ b/src/lib/fcitx-utils/macros.h @@ -152,15 +152,15 @@ } #define FCITX_DECLARE_READ_ONLY_PROPERTY(TYPE, GETTER) \ - std::conditional_t::value, const TYPE &, TYPE> \ - GETTER() const; + std::conditional_t, const TYPE &, TYPE> GETTER() \ + const; #define FCITX_DECLARE_PROPERTY(TYPE, GETTER, SETTER) \ FCITX_DECLARE_READ_ONLY_PROPERTY(TYPE, GETTER) \ void SETTER(TYPE); #define FCITX_DEFINE_READ_ONLY_PROPERTY_PRIVATE(THIS, TYPE, GETTER) \ - std::conditional_t::value, const TYPE &, TYPE> \ + std::conditional_t, const TYPE &, TYPE> \ THIS::GETTER() const { \ FCITX_TYPED_D(const THIS##Private); \ return d->GETTER##_; \ diff --git a/src/lib/fcitx/CMakeLists.txt b/src/lib/fcitx/CMakeLists.txt index 9e5522afd..df15dd57b 100644 --- a/src/lib/fcitx/CMakeLists.txt +++ b/src/lib/fcitx/CMakeLists.txt @@ -23,6 +23,7 @@ set(FCITX_CORE_SOURCES statusarea.cpp inputpanel.cpp candidatelist.cpp + candidateaction.cpp icontheme.cpp inputmethodengine.cpp ) @@ -38,6 +39,7 @@ set(FCITX_CORE_HEADERS inputcontextproperty.h inputpanel.h candidatelist.h + candidateaction.h focusgroup.h surroundingtext.h addonloader.h diff --git a/src/lib/fcitx/candidateaction.cpp b/src/lib/fcitx/candidateaction.cpp new file mode 100644 index 000000000..b8effa63b --- /dev/null +++ b/src/lib/fcitx/candidateaction.cpp @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2024-2024 CSSlayer + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ +#include "candidateaction.h" +#include "fcitx-utils/macros.h" + +namespace fcitx { + +class CandidateActionPrivate { +public: + CandidateActionPrivate() = default; + FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE_WITHOUT_SPEC( + CandidateActionPrivate); + + int id_ = 0; + std::string text_; + bool isSeparator_ = false; + std::string icon_; + bool isCheckable_ = false; + bool isChecked_ = false; +}; + +CandidateAction::CandidateAction() + : d_ptr(std::make_unique()) {} + +FCITX_DEFINE_DPTR_COPY_AND_DEFAULT_DTOR_AND_MOVE(CandidateAction); + +FCITX_DEFINE_PROPERTY_PRIVATE(CandidateAction, int, id, setId); +FCITX_DEFINE_PROPERTY_PRIVATE(CandidateAction, std::string, text, setText); +FCITX_DEFINE_PROPERTY_PRIVATE(CandidateAction, bool, isSeparator, setSeparator); +FCITX_DEFINE_PROPERTY_PRIVATE(CandidateAction, std::string, icon, setIcon); +FCITX_DEFINE_PROPERTY_PRIVATE(CandidateAction, bool, isCheckable, setCheckable); +FCITX_DEFINE_PROPERTY_PRIVATE(CandidateAction, bool, isChecked, setChecked); + +} // namespace fcitx diff --git a/src/lib/fcitx/candidateaction.h b/src/lib/fcitx/candidateaction.h new file mode 100644 index 000000000..a5b9dff74 --- /dev/null +++ b/src/lib/fcitx/candidateaction.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2024-2024 CSSlayer + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ +#ifndef _FCITX_CANDIDATEACTION_H_ +#define _FCITX_CANDIDATEACTION_H_ + +#include +#include +#include +#include "fcitxcore_export.h" + +namespace fcitx { + +class CandidateActionPrivate; + +class FCITXCORE_EXPORT CandidateAction { +public: + CandidateAction(); + virtual ~CandidateAction(); + FCITX_DECLARE_COPY_AND_MOVE(CandidateAction); + + FCITX_DECLARE_PROPERTY(int, id, setId); + FCITX_DECLARE_PROPERTY(std::string, text, setText); + FCITX_DECLARE_PROPERTY(bool, isSeparator, setSeparator); + FCITX_DECLARE_PROPERTY(std::string, icon, setIcon); + FCITX_DECLARE_PROPERTY(bool, isCheckable, setCheckable); + FCITX_DECLARE_PROPERTY(bool, isChecked, setChecked); + +private: + FCITX_DECLARE_PRIVATE(CandidateAction); + std::unique_ptr d_ptr; +}; + +} // namespace fcitx + +#endif // _FCITX_CANDIDATEACTION_H_ diff --git a/src/lib/fcitx/candidatelist.cpp b/src/lib/fcitx/candidatelist.cpp index 05d6acbd4..4d1950ed4 100644 --- a/src/lib/fcitx/candidatelist.cpp +++ b/src/lib/fcitx/candidatelist.cpp @@ -77,6 +77,8 @@ class CursorModifiableAdaptorForCommonCandidateList } // namespace +ActionableCandidateList::~ActionableCandidateList() = default; + class CandidateListPrivate { public: BulkCandidateList *bulk_ = nullptr; @@ -85,6 +87,7 @@ class CandidateListPrivate { CursorMovableCandidateList *cursorMovable_ = nullptr; BulkCursorCandidateList *bulkCursor_ = nullptr; CursorModifiableCandidateList *cursorModifiable_ = nullptr; + ActionableCandidateList *actionable_ = nullptr; }; CandidateList::CandidateList() @@ -124,6 +127,11 @@ BulkCursorCandidateList *CandidateList::toBulkCursor() const { return d->bulkCursor_; } +ActionableCandidateList *CandidateList::toActionable() const { + FCITX_D(); + return d->actionable_; +} + void CandidateList::setBulk(BulkCandidateList *list) { FCITX_D(); d->bulk_ = list; @@ -154,6 +162,11 @@ void CandidateList::setBulkCursor(BulkCursorCandidateList *list) { d->bulkCursor_ = list; } +void CandidateList::setActionable(ActionableCandidateList *list) { + FCITX_D(); + d->actionable_ = list; +} + class CandidateWordPrivate { public: CandidateWordPrivate(Text &&text) : text_(std::move(text)) {} @@ -330,6 +343,7 @@ class CommonCandidateListPrivate { bool cursorKeepInSamePage_ = false; CursorPositionAfterPaging cursorPositionAfterPaging_ = CursorPositionAfterPaging::DonotChange; + std::unique_ptr actionable_; int size() const { auto start = currentPage_ * pageSize_; @@ -711,4 +725,12 @@ void CommonCandidateList::fixAfterUpdate() { } } } + +void CommonCandidateList::setActionableImpl( + std::unique_ptr actionable) { + FCITX_D(); + d->actionable_ = std::move(actionable); + setActionable(d->actionable_.get()); +} + } // namespace fcitx diff --git a/src/lib/fcitx/candidatelist.h b/src/lib/fcitx/candidatelist.h index 424209c58..8013e4fd9 100644 --- a/src/lib/fcitx/candidatelist.h +++ b/src/lib/fcitx/candidatelist.h @@ -8,7 +8,10 @@ #define _FCITX_CANDIDATELIST_H_ #include +#include +#include #include +#include "fcitxcore_export.h" namespace fcitx { @@ -19,6 +22,7 @@ class ModifiableCandidateList; class CursorMovableCandidateList; class CursorModifiableCandidateList; class BulkCursorCandidateList; +class ActionableCandidateList; class CandidateListPrivate; @@ -96,6 +100,7 @@ class FCITXCORE_EXPORT CandidateList { CursorMovableCandidateList *toCursorMovable() const; CursorModifiableCandidateList *toCursorModifiable() const; BulkCursorCandidateList *toBulkCursor() const; + ActionableCandidateList *toActionable() const; protected: void setPageable(PageableCandidateList *list); @@ -104,6 +109,7 @@ class FCITXCORE_EXPORT CandidateList { void setCursorMovable(CursorMovableCandidateList *list); void setCursorModifiable(CursorModifiableCandidateList *list); void setBulkCursor(BulkCursorCandidateList *list); + void setActionable(ActionableCandidateList *list); private: std::unique_ptr d_ptr; @@ -187,6 +193,35 @@ class FCITXCORE_EXPORT BulkCursorCandidateList { virtual void setGlobalCursorIndex(int index) = 0; }; +/** + * Interface for trigger actions on candidates. + * + * @since 5.1.10 + */ +class FCITXCORE_EXPORT ActionableCandidateList { +public: + virtual ~ActionableCandidateList(); + + /** + * Check whether this candidate has action. + * + * This function should be fast and guarantee that candidateActions return a + * not empty vector. + */ + virtual bool hasAction(const CandidateWord &candidate) const = 0; + + /** + * Return a list of actions. + */ + virtual std::vector + candidateActions(const CandidateWord &candidate) const = 0; + + /** + * Trigger the action based on the index returned from candidateActions. + */ + virtual void triggerAction(const CandidateWord &candidate, int id) = 0; +}; + class DisplayOnlyCandidateListPrivate; class FCITXCORE_EXPORT DisplayOnlyCandidateList : public CandidateList { @@ -308,6 +343,13 @@ class FCITXCORE_EXPORT CommonCandidateList : public CandidateList, void setCursorKeepInSamePage(bool); void setCursorPositionAfterPaging(CursorPositionAfterPaging afterPaging); + /** + * Set an optional implemenation of actionable candidate list + * + * @since 5.1.10 + */ + void setActionableImpl(std::unique_ptr actionable); + private: void fixAfterUpdate(); void moveCursor(bool prev); diff --git a/test/testcandidatelist.cpp b/test/testcandidatelist.cpp index 5eca5348f..a19e35fa5 100644 --- a/test/testcandidatelist.cpp +++ b/test/testcandidatelist.cpp @@ -7,6 +7,7 @@ #include #include "fcitx-utils/log.h" +#include "fcitx/candidateaction.h" #include "fcitx/candidatelist.h" using namespace fcitx; @@ -327,11 +328,43 @@ void test_cursor() { FCITX_ASSERT(candidatelist.toBulkCursor()->globalCursorIndex(), 5); } +void test_candidateaction() { + CandidateAction action; + action.setText("Test"); + action.setId(1); + action.setCheckable(true); + action.setChecked(false); + action.setSeparator(false); + action.setIcon("Icon"); + + CandidateAction action2; + action2 = std::move(action); + + FCITX_ASSERT(action2.text() == "Test"); + FCITX_ASSERT(action2.id() == 1); + FCITX_ASSERT(action2.isCheckable()); + FCITX_ASSERT(!action2.isChecked()); + FCITX_ASSERT(!action2.isSeparator()); + FCITX_ASSERT(action2.icon() == "Icon"); + + CandidateAction action3(action2); + + for (const auto &action : {action2, action3}) { + FCITX_ASSERT(action.text() == "Test"); + FCITX_ASSERT(action.id() == 1); + FCITX_ASSERT(action.isCheckable()); + FCITX_ASSERT(!action.isChecked()); + FCITX_ASSERT(!action.isSeparator()); + FCITX_ASSERT(action.icon() == "Icon"); + } +} + int main() { test_basic(); test_faulty_placeholder(); test_label(); test_comment(); test_cursor(); + test_candidateaction(); return 0; }