Fcitx
candidatelist.cpp
1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "candidatelist.h"
9 #include <algorithm>
10 #include <cstddef>
11 #include <memory>
12 #include <stdexcept>
13 #include <string>
14 #include <unordered_set>
15 #include <utility>
16 #include <vector>
17 #include <fcitx-utils/macros.h>
18 #include <fcitx-utils/utf8.h>
19 #include "fcitx-utils/key.h"
20 #include "fcitx-utils/keysym.h"
21 #include "fcitx-utils/misc.h"
22 #include "text.h"
23 
24 namespace fcitx {
25 
26 namespace {
27 
28 constexpr size_t regularLabelSize = 10;
29 
30 template <typename Container, typename Transformer>
31 void fillLabels(std::vector<Text> &labels, const Container &container,
32  const Transformer &trans) {
33  labels.clear();
34  labels.reserve(std::max(std::size(container), regularLabelSize));
35  for (const auto &item : container) {
36  labels.emplace_back(trans(item));
37  }
38  while (labels.size() < regularLabelSize) {
39  labels.emplace_back();
40  }
41 }
42 
43 template <typename CandidateListType, typename InterfaceType>
44 class CandidateListInterfaceAdapter : public QPtrHolder<CandidateListType>,
45  public InterfaceType {
46 public:
47  CandidateListInterfaceAdapter(CandidateListType *q)
48  : QPtrHolder<CandidateListType>(q) {}
49 };
50 
51 #define FCITX_COMMA_IF_2 ,
52 #define FCITX_COMMA_IF_1
53 #define FCITX_COMMA_IF(X) FCITX_EXPAND(FCITX_CONCATENATE(FCITX_COMMA_IF_, X))
54 
55 #define FCITX_FORWARD_METHOD_ARG(N, ARG) ARG arg##N FCITX_COMMA_IF(N)
56 
57 #define FCITX_FORWARD_METHOD_ARG_NAME(N, ARG) arg##N FCITX_COMMA_IF(N)
58 
59 #define FCITX_FORWARD_METHOD(RETURN_TYPE, METHOD_NAME, ARGS, ...) \
60  RETURN_TYPE METHOD_NAME(FCITX_FOR_EACH_IDX(FCITX_FORWARD_METHOD_ARG, \
61  FCITX_REMOVE_PARENS(ARGS))) \
62  __VA_ARGS__ override { \
63  FCITX_Q(); \
64  return q->METHOD_NAME(FCITX_FOR_EACH_IDX( \
65  FCITX_FORWARD_METHOD_ARG_NAME, FCITX_REMOVE_PARENS(ARGS))); \
66  }
67 
68 class BulkCursorAdaptorForCommonCandidateList
69  : public CandidateListInterfaceAdapter<CommonCandidateList,
70  BulkCursorCandidateList> {
71 public:
72  using CandidateListInterfaceAdapter::CandidateListInterfaceAdapter;
73 
74  FCITX_FORWARD_METHOD(void, setGlobalCursorIndex, (int));
75  FCITX_FORWARD_METHOD(int, globalCursorIndex, (), const);
76 };
77 
78 class CursorModifiableAdaptorForCommonCandidateList
79  : public CandidateListInterfaceAdapter<CommonCandidateList,
80  CursorModifiableCandidateList> {
81 public:
82  using CandidateListInterfaceAdapter::CandidateListInterfaceAdapter;
83 
84  FCITX_FORWARD_METHOD(void, setCursorIndex, (int));
85 };
86 
87 } // namespace
88 
89 ActionableCandidateList::~ActionableCandidateList() = default;
90 
92 public:
93  BulkCandidateList *bulk_ = nullptr;
94  ModifiableCandidateList *modifiable_ = nullptr;
95  PageableCandidateList *pageable_ = nullptr;
96  CursorMovableCandidateList *cursorMovable_ = nullptr;
97  BulkCursorCandidateList *bulkCursor_ = nullptr;
98  CursorModifiableCandidateList *cursorModifiable_ = nullptr;
99  ActionableCandidateList *actionable_ = nullptr;
100 };
101 
102 CandidateList::CandidateList()
103  : d_ptr(std::make_unique<CandidateListPrivate>()) {}
104 
105 CandidateList::~CandidateList() {}
106 
107 bool CandidateList::empty() const { return size() == 0; }
108 
109 BulkCandidateList *CandidateList::toBulk() const {
110  FCITX_D();
111  return d->bulk_;
112 }
113 
114 ModifiableCandidateList *CandidateList::toModifiable() const {
115  FCITX_D();
116  return d->modifiable_;
117 }
118 
119 PageableCandidateList *CandidateList::toPageable() const {
120  FCITX_D();
121  return d->pageable_;
122 }
123 
124 CursorMovableCandidateList *CandidateList::toCursorMovable() const {
125  FCITX_D();
126  return d->cursorMovable_;
127 }
128 
129 CursorModifiableCandidateList *CandidateList::toCursorModifiable() const {
130  FCITX_D();
131  return d->cursorModifiable_;
132 }
133 
134 BulkCursorCandidateList *CandidateList::toBulkCursor() const {
135  FCITX_D();
136  return d->bulkCursor_;
137 }
138 
139 ActionableCandidateList *CandidateList::toActionable() const {
140  FCITX_D();
141  return d->actionable_;
142 }
143 
144 void CandidateList::setBulk(BulkCandidateList *list) {
145  FCITX_D();
146  d->bulk_ = list;
147 }
148 
149 void CandidateList::setModifiable(ModifiableCandidateList *list) {
150  FCITX_D();
151  d->modifiable_ = list;
152 }
153 
154 void CandidateList::setPageable(PageableCandidateList *list) {
155  FCITX_D();
156  d->pageable_ = list;
157 }
158 
159 void CandidateList::setCursorMovable(CursorMovableCandidateList *list) {
160  FCITX_D();
161  d->cursorMovable_ = list;
162 }
163 
164 void CandidateList::setCursorModifiable(CursorModifiableCandidateList *list) {
165  FCITX_D();
166  d->cursorModifiable_ = list;
167 }
168 
169 void CandidateList::setBulkCursor(BulkCursorCandidateList *list) {
170  FCITX_D();
171  d->bulkCursor_ = list;
172 }
173 
174 void CandidateList::setActionable(ActionableCandidateList *list) {
175  FCITX_D();
176  d->actionable_ = list;
177 }
178 
180 public:
181  CandidateWordPrivate(Text &&text) : text_(std::move(text)) {}
182  Text text_;
183  bool isPlaceHolder_ = false;
184  Text customLabel_;
185  bool hasCustomLabel_ = false;
186  Text comment_;
187 };
188 
189 CandidateWord::CandidateWord(Text text)
190  : d_ptr(std::make_unique<CandidateWordPrivate>(std::move(text))) {}
191 
192 CandidateWord::~CandidateWord() {}
193 
194 const Text &CandidateWord::text() const {
195  FCITX_D();
196  return d->text_;
197 }
198 
199 void CandidateWord::setText(Text text) {
200  FCITX_D();
201  d->text_ = std::move(text);
202 }
203 
204 const Text &CandidateWord::comment() const {
205  FCITX_D();
206  return d->comment_;
207 }
208 
209 void CandidateWord::setComment(Text comment) {
210  FCITX_D();
211  d->comment_ = std::move(comment);
212 }
213 
214 Text CandidateWord::textWithComment(std::string separator) const {
215  FCITX_D();
216  auto text = d->text_;
217  if (!d->comment_.empty()) {
218  text.append(std::move(separator));
219  text.append(d->comment_);
220  }
221  return text;
222 }
223 
225  FCITX_D();
226  return d->isPlaceHolder_;
227 }
228 
229 bool CandidateWord::hasCustomLabel() const {
230  FCITX_D();
231  return d->hasCustomLabel_;
232 }
233 
234 const Text &CandidateWord::customLabel() const {
235  FCITX_D();
236  return d->customLabel_;
237 }
238 
239 void CandidateWord::setPlaceHolder(bool placeHolder) {
240  FCITX_D();
241  d->isPlaceHolder_ = placeHolder;
242 }
243 
244 void CandidateWord::resetCustomLabel() {
245  FCITX_D();
246  d->customLabel_ = Text();
247  d->hasCustomLabel_ = false;
248 }
249 
250 void CandidateWord::setCustomLabel(Text text) {
251  FCITX_D();
252  d->customLabel_ = std::move(text);
253  d->hasCustomLabel_ = true;
254 }
255 
257 public:
258  Text emptyText_;
259  int cursorIndex_ = -1;
260  CandidateLayoutHint layoutHint_ = CandidateLayoutHint::Vertical;
261  std::vector<std::shared_ptr<CandidateWord>> candidateWords_;
262 
263  void checkIndex(int idx) const {
264  if (idx < 0 || static_cast<size_t>(idx) >= candidateWords_.size()) {
265  throw std::invalid_argument(
266  "DisplayOnlyCandidateList: invalid index");
267  }
268  }
269 };
270 
271 DisplayOnlyCandidateList::DisplayOnlyCandidateList()
272  : d_ptr(std::make_unique<DisplayOnlyCandidateListPrivate>()) {}
273 
274 DisplayOnlyCandidateList::~DisplayOnlyCandidateList() = default;
275 
276 void DisplayOnlyCandidateList::setContent(
277  const std::vector<std::string> &content) {
278  std::vector<Text> text_content;
279  for (const auto &str : content) {
280  text_content.emplace_back();
281  text_content.back().append(str);
282  }
283  setContent(std::move(text_content));
284 }
285 
286 void DisplayOnlyCandidateList::setContent(std::vector<Text> content) {
287  FCITX_D();
288  for (auto &text : content) {
289  d->candidateWords_.emplace_back(
290  std::make_shared<DisplayOnlyCandidateWord>(std::move(text)));
291  }
292 }
293 
294 void DisplayOnlyCandidateList::setLayoutHint(CandidateLayoutHint hint) {
295  FCITX_D();
296  d->layoutHint_ = hint;
297 }
298 
299 void DisplayOnlyCandidateList::setCursorIndex(int index) {
300  FCITX_D();
301  if (index < 0) {
302  d->cursorIndex_ = -1;
303  } else {
304  d->checkIndex(index);
305  d->cursorIndex_ = index;
306  }
307 }
308 
309 const Text &DisplayOnlyCandidateList::label(int idx) const {
310  FCITX_D();
311  d->checkIndex(idx);
312  return d->emptyText_;
313 }
314 
315 const CandidateWord &DisplayOnlyCandidateList::candidate(int idx) const {
316  FCITX_D();
317  d->checkIndex(idx);
318  return *d->candidateWords_[idx];
319 }
320 
321 int DisplayOnlyCandidateList::cursorIndex() const {
322  FCITX_D();
323  return d->cursorIndex_;
324 }
325 
326 int DisplayOnlyCandidateList::size() const {
327  FCITX_D();
328  return d->candidateWords_.size();
329 }
330 
331 CandidateLayoutHint DisplayOnlyCandidateList::layoutHint() const {
332  FCITX_D();
333  return d->layoutHint_;
334 }
335 
337 public:
339  : bulkCursor_(q), cursorModifiable_(q) {}
340 
341  BulkCursorAdaptorForCommonCandidateList bulkCursor_;
342  CursorModifiableAdaptorForCommonCandidateList cursorModifiable_;
343  bool usedNextBefore_ = false;
344  int cursorIndex_ = -1;
345  int currentPage_ = 0;
346  int pageSize_ = 5;
347  std::vector<Text> labels_;
348  // use shared_ptr for type erasure
349  std::vector<std::unique_ptr<CandidateWord>> candidateWord_;
350  CandidateLayoutHint layoutHint_ = CandidateLayoutHint::NotSet;
351  bool cursorIncludeUnselected_ = false;
352  bool cursorKeepInSamePage_ = false;
353  CursorPositionAfterPaging cursorPositionAfterPaging_ =
354  CursorPositionAfterPaging::DonotChange;
355  std::unique_ptr<ActionableCandidateList> actionable_;
356 
357  int size() const {
358  auto start = currentPage_ * pageSize_;
359  auto remain = static_cast<int>(candidateWord_.size()) - start;
360  if (remain > pageSize_) {
361  return pageSize_;
362  }
363  return remain;
364  }
365 
366  int toGlobalIndex(int idx) const {
367  return idx + (currentPage_ * pageSize_);
368  }
369 
370  void checkIndex(int idx) const {
371  if (idx < 0 || idx >= size()) {
372  throw std::invalid_argument("CommonCandidateList: invalid index");
373  }
374  }
375 
376  void checkGlobalIndex(int idx) const {
377  if (idx < 0 || static_cast<size_t>(idx) >= candidateWord_.size()) {
378  throw std::invalid_argument(
379  "CommonCandidateList: invalid global index");
380  }
381  }
382 
383  void fixCursorAfterPaging(int oldIndex) {
384  if (oldIndex < 0) {
385  return;
386  }
387 
388  switch (cursorPositionAfterPaging_) {
389  case CursorPositionAfterPaging::DonotChange:
390  break;
391  case CursorPositionAfterPaging::ResetToFirst:
392  cursorIndex_ = currentPage_ * pageSize_;
393  break;
394  case CursorPositionAfterPaging::SameAsLast: {
395  auto currentPageSize = size();
396  if (oldIndex >= currentPageSize) {
397  cursorIndex_ = currentPage_ * pageSize_ + size() - 1;
398  } else {
399  cursorIndex_ = currentPage_ * pageSize_ + oldIndex;
400  }
401  break;
402  }
403  }
404  }
405 };
406 
407 CommonCandidateList::CommonCandidateList()
408  : d_ptr(std::make_unique<CommonCandidateListPrivate>(this)) {
409  FCITX_D();
410  setPageable(this);
411  setModifiable(this);
412  setBulk(this);
413  setCursorMovable(this);
414  setBulkCursor(&d->bulkCursor_);
415  setCursorModifiable(&d->cursorModifiable_);
416 
417  setLabels();
418 }
419 
420 CommonCandidateList::~CommonCandidateList() {}
421 
422 std::string keyToLabel(const Key &key) {
423  std::string result;
424  if (key.sym() == FcitxKey_None) {
425  return result;
426  }
427 
428 #define _APPEND_MODIFIER_STRING(STR, VALUE) \
429  if (key.states() & KeyState::VALUE) { \
430  result += (STR); \
431  }
432  if (isApple()) {
433  _APPEND_MODIFIER_STRING("⌃", Ctrl)
434  _APPEND_MODIFIER_STRING("⌥", Alt)
435  _APPEND_MODIFIER_STRING("⇧", Shift)
436  _APPEND_MODIFIER_STRING("⌘", Super)
437  } else {
438  _APPEND_MODIFIER_STRING("C-", Ctrl)
439  _APPEND_MODIFIER_STRING("A-", Alt)
440  _APPEND_MODIFIER_STRING("S-", Shift)
441  _APPEND_MODIFIER_STRING("M-", Super)
442  }
443 
444 #undef _APPEND_MODIFIER_STRING
445 
446  auto chr = Key::keySymToUnicode(key.sym());
447  if (chr) {
448  result += utf8::UCS4ToUTF8(chr);
449  } else {
450  result = Key::keySymToString(key.sym(), KeyStringFormat::Localized);
451  }
452  if (!isApple()) {
453  // add a dot as separator
454  result += ". ";
455  }
456 
457  return result;
458 }
459 
460 void CommonCandidateList::setLabels(const std::vector<std::string> &labels) {
461  FCITX_D();
462  fillLabels(d->labels_, labels, [](const std::string &str) { return str; });
463 }
464 
465 void CommonCandidateList::setSelectionKey(const KeyList &keyList) {
466  FCITX_D();
467  fillLabels(d->labels_, keyList,
468  [](const Key &str) -> std::string { return keyToLabel(str); });
469 }
470 
471 void CommonCandidateList::clear() {
472  FCITX_D();
473  d->candidateWord_.clear();
474 }
475 
476 int CommonCandidateList::currentPage() const {
477  FCITX_D();
478  return d->currentPage_;
479 }
480 
481 int CommonCandidateList::cursorIndex() const {
482  FCITX_D();
483  int cursorPage = d->cursorIndex_ / d->pageSize_;
484  if (d->cursorIndex_ >= 0 && cursorPage == d->currentPage_) {
485  return d->cursorIndex_ % d->pageSize_;
486  }
487  return -1;
488 }
489 
490 bool CommonCandidateList::hasNext() const {
491  // page size = 5
492  // total size = 5 -> 1 page
493  // total size = 6 -> 2 page
494  FCITX_D();
495  return d->currentPage_ + 1 < totalPages();
496 }
497 
498 bool CommonCandidateList::hasPrev() const {
499  FCITX_D();
500  return d->currentPage_ > 0;
501 }
502 
503 void CommonCandidateList::prev() {
504  FCITX_D();
505  if (!hasPrev()) {
506  return;
507  }
508  setPage(d->currentPage_ - 1);
509 }
510 
511 void CommonCandidateList::next() {
512  FCITX_D();
513  if (!hasNext()) {
514  return;
515  }
516  setPage(d->currentPage_ + 1);
517  d->usedNextBefore_ = true;
518 }
519 
520 bool CommonCandidateList::usedNextBefore() const {
521  FCITX_D();
522  return d->usedNextBefore_;
523 }
524 
525 void CommonCandidateList::setPageSize(int size) {
526  FCITX_D();
527  if (size < 1) {
528  throw std::invalid_argument("CommonCandidateList: invalid page size");
529  }
530  d->pageSize_ = size;
531  d->currentPage_ = 0;
532 }
533 
534 int CommonCandidateList::pageSize() const {
535  FCITX_D();
536  return d->pageSize_;
537 }
538 
539 int CommonCandidateList::size() const {
540  FCITX_D();
541  return d->size();
542 }
543 
545  FCITX_D();
546  return d->candidateWord_.size();
547 }
548 
549 const CandidateWord &CommonCandidateList::candidate(int idx) const {
550  FCITX_D();
551  d->checkIndex(idx);
552  auto globalIndex = d->toGlobalIndex(idx);
553  return *d->candidateWord_[globalIndex];
554 }
555 
556 const Text &CommonCandidateList::label(int idx) const {
557  FCITX_D();
558  d->checkIndex(idx);
559  if (idx < 0 || idx >= size() ||
560  static_cast<size_t>(idx) >= d->labels_.size()) {
561  throw std::invalid_argument("CommonCandidateList: invalid label idx");
562  }
563 
564  return d->labels_[idx];
565 }
566 
567 void CommonCandidateList::insert(int idx, std::unique_ptr<CandidateWord> word) {
568  FCITX_D();
569  // it's ok to insert at tail
570  if (idx != static_cast<int>(d->candidateWord_.size())) {
571  d->checkGlobalIndex(idx);
572  }
573  d->candidateWord_.insert(d->candidateWord_.begin() + idx, std::move(word));
574 }
575 
576 void CommonCandidateList::remove(int idx) {
577  FCITX_D();
578  d->checkGlobalIndex(idx);
579  d->candidateWord_.erase(d->candidateWord_.begin() + idx);
580  fixAfterUpdate();
581 }
582 
583 int CommonCandidateList::totalPages() const {
584  FCITX_D();
585  return (totalSize() + d->pageSize_ - 1) / d->pageSize_;
586 }
587 
588 void CommonCandidateList::setLayoutHint(CandidateLayoutHint hint) {
589  FCITX_D();
590  d->layoutHint_ = hint;
591 }
592 
593 void CommonCandidateList::setGlobalCursorIndex(int index) {
594  FCITX_D();
595  if (index < 0) {
596  d->cursorIndex_ = -1;
597  } else {
598  d->checkGlobalIndex(index);
599  d->cursorIndex_ = index;
600  }
601 }
602 
604  FCITX_D();
605  d->checkIndex(index);
606  auto globalIndex = d->toGlobalIndex(index);
607  setGlobalCursorIndex(globalIndex);
608 }
609 
611  FCITX_D();
612  return d->cursorIndex_;
613 }
614 
615 CandidateLayoutHint CommonCandidateList::layoutHint() const {
616  FCITX_D();
617  return d->layoutHint_;
618 }
619 
621  FCITX_D();
622  d->checkGlobalIndex(idx);
623  return *d->candidateWord_[idx];
624 }
625 
626 void CommonCandidateList::move(int from, int to) {
627  FCITX_D();
628  d->checkGlobalIndex(from);
629  d->checkGlobalIndex(to);
630  if (from < to) {
631  // 1 2 3 4 5
632  // from 2 to 5
633  // 1 3 4 5 2
634  std::rotate(d->candidateWord_.begin() + from,
635  d->candidateWord_.begin() + from + 1,
636  d->candidateWord_.begin() + to + 1);
637  } else if (from > to) {
638  // 1 2 3 4 5
639  // from 5 to 2
640  // 1 5 2 3 4
641  std::rotate(d->candidateWord_.begin() + to,
642  d->candidateWord_.begin() + from,
643  d->candidateWord_.begin() + from + 1);
644  }
645 }
646 
647 void CommonCandidateList::moveCursor(bool prev) {
648  FCITX_D();
649  if (totalSize() <= 0 || size() <= 0) {
650  return;
651  }
652 
653  int startCursor = d->cursorIndex_;
654  int startPage = d->currentPage_;
655  std::unordered_set<int> deadloopDetect;
656  do {
657  deadloopDetect.insert(d->cursorIndex_);
658  auto pageBegin = d->pageSize_ * d->currentPage_;
659  if (cursorIndex() < 0) {
660  setGlobalCursorIndex(pageBegin + (prev ? size() - 1 : 0));
661  } else {
662  int rotationBase;
663  int rotationSize;
664  if (d->cursorKeepInSamePage_) {
665  rotationBase = pageBegin;
666  rotationSize = size();
667  } else {
668  rotationBase = 0;
669  rotationSize = totalSize();
670  }
671  auto newGlobalIndex = d->cursorIndex_ + (prev ? -1 : 1);
672  if (newGlobalIndex < rotationBase ||
673  newGlobalIndex >= rotationBase + rotationSize) {
674  if (d->cursorIncludeUnselected_) {
675  d->cursorIndex_ = -1;
676  } else {
677  d->cursorIndex_ =
678  prev ? (rotationBase + rotationSize - 1) : rotationBase;
679  }
680  } else {
681  d->cursorIndex_ = newGlobalIndex;
682  }
683  if (!d->cursorKeepInSamePage_ && d->cursorIndex_ >= 0) {
684  setPage(d->cursorIndex_ / d->pageSize_);
685  }
686  }
687  } while (!deadloopDetect.contains(d->cursorIndex_) &&
688  d->cursorIndex_ >= 0 &&
689  candidateFromAll(d->cursorIndex_).isPlaceHolder());
690  if (deadloopDetect.contains(d->cursorIndex_)) {
691  d->cursorIndex_ = startCursor;
692  d->currentPage_ = startPage;
693  }
694 }
695 
696 void CommonCandidateList::prevCandidate() { moveCursor(true); }
697 
698 void CommonCandidateList::nextCandidate() { moveCursor(false); }
699 
700 void CommonCandidateList::setCursorIncludeUnselected(bool v) {
701  FCITX_D();
702  d->cursorIncludeUnselected_ = v;
703 }
704 
705 void CommonCandidateList::setCursorKeepInSamePage(bool v) {
706  FCITX_D();
707  d->cursorKeepInSamePage_ = v;
708 }
709 
710 void CommonCandidateList::setCursorPositionAfterPaging(
711  CursorPositionAfterPaging afterPaging) {
712  FCITX_D();
713  d->cursorPositionAfterPaging_ = afterPaging;
714 }
715 
716 void CommonCandidateList::setPage(int page) {
717  FCITX_D();
718  auto totalPage = totalPages();
719  if (page >= 0 && page < totalPage) {
720  if (d->currentPage_ != page) {
721  auto oldIndex = cursorIndex();
722  d->currentPage_ = page;
723  d->fixCursorAfterPaging(oldIndex);
724  }
725  } else {
726  throw std::invalid_argument("invalid page");
727  }
728 }
729 
730 void CommonCandidateList::replace(int idx,
731  std::unique_ptr<CandidateWord> word) {
732  FCITX_D();
733  d->candidateWord_[idx] = std::move(word);
734 }
735 
736 void CommonCandidateList::fixAfterUpdate() {
737  FCITX_D();
738  if (d->currentPage_ >= totalPages() && d->currentPage_ > 0) {
739  d->currentPage_ = totalPages() - 1;
740  }
741  if (d->cursorIndex_ >= 0) {
742  if (d->cursorIndex_ >= totalSize()) {
743  d->cursorIndex_ = 0;
744  }
745  }
746 }
747 
749  std::unique_ptr<ActionableCandidateList> actionable) {
750  FCITX_D();
751  d->actionable_ = std::move(actionable);
752  setActionable(d->actionable_.get());
753 }
754 
755 } // namespace fcitx
Describe a Key in fcitx.
Definition: key.h:41
bool isPlaceHolder() const
Whether the candidate is only a place holder.
std::string UCS4ToUTF8(uint32_t code)
Convert UCS4 to UTF8 string.
Definition: utf8.cpp:19
Formatted string commonly used in user interface.
static uint32_t keySymToUnicode(KeySym sym)
Convert keysym to a unicode.
Definition: key.cpp:740
const CandidateWord & candidateFromAll(int idx) const override
If idx is out of range, it may raise exception.
Definition: action.cpp:17
int globalCursorIndex() const
Return Global cursor index.
C++ Utility functions for handling utf8 strings.
A class represents a formatted string.
Definition: text.h:27
void setActionableImpl(std::unique_ptr< ActionableCandidateList > actionable)
Set an optional implemenation of actionable candidate list.
void setLabels(const std::vector< std::string > &labels={})
Set the label of candidate list.
void setCursorIndex(int index)
Set cursor index on current page.
void setSelectionKey(const KeyList &keyList)
Set the label of candidate list by key.
Text textWithComment(std::string separator=" ") const
Return text with comment.
Interface for trigger actions on candidates.
const Text & comment() const
Return comment corresponding to the candidate.
Return the human readable string in localized format.
static std::string keySymToString(KeySym sym, KeyStringFormat format=KeyStringFormat::Portable)
Convert keysym to a string.
Definition: key.cpp:662
Base class of candidate word.
Definition: candidatelist.h:38
Key sym related types.
A common simple candidate list that serves most of the purpose.
int totalSize() const override
It&#39;s possible for this function to return -1 if the implement has no clear number how many candidates...
Class to represent a key.