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  bool spaceBetweenComment_ = true;
188 };
189 
190 CandidateWord::CandidateWord(Text text)
191  : d_ptr(std::make_unique<CandidateWordPrivate>(std::move(text))) {}
192 
193 CandidateWord::~CandidateWord() {}
194 
195 const Text &CandidateWord::text() const {
196  FCITX_D();
197  return d->text_;
198 }
199 
200 void CandidateWord::setText(Text text) {
201  FCITX_D();
202  d->text_ = std::move(text);
203 }
204 
205 const Text &CandidateWord::comment() const {
206  FCITX_D();
207  return d->comment_;
208 }
209 
210 void CandidateWord::setComment(Text comment) {
211  FCITX_D();
212  d->comment_ = std::move(comment);
213 }
214 
215 Text CandidateWord::textWithComment(std::string separator) const {
216  FCITX_D();
217  auto text = d->text_;
218  if (!d->comment_.empty()) {
219  text.append(std::move(separator));
220  text.append(d->comment_);
221  }
222  return text;
223 }
224 
226  FCITX_D();
227  return d->isPlaceHolder_;
228 }
229 
230 bool CandidateWord::hasCustomLabel() const {
231  FCITX_D();
232  return d->hasCustomLabel_;
233 }
234 
235 const Text &CandidateWord::customLabel() const {
236  FCITX_D();
237  return d->customLabel_;
238 }
239 
240 void CandidateWord::setPlaceHolder(bool placeHolder) {
241  FCITX_D();
242  d->isPlaceHolder_ = placeHolder;
243 }
244 
245 void CandidateWord::resetCustomLabel() {
246  FCITX_D();
247  d->customLabel_ = Text();
248  d->hasCustomLabel_ = false;
249 }
250 
251 void CandidateWord::setCustomLabel(Text text) {
252  FCITX_D();
253  d->customLabel_ = std::move(text);
254  d->hasCustomLabel_ = true;
255 }
256 
258  FCITX_D();
259  return d->spaceBetweenComment_;
260 }
261 
262 void CandidateWord::setSpaceBetweenComment(bool space) {
263  FCITX_D();
264  d->spaceBetweenComment_ = space;
265 }
266 
268 public:
269  Text emptyText_;
270  int cursorIndex_ = -1;
271  CandidateLayoutHint layoutHint_ = CandidateLayoutHint::Vertical;
272  std::vector<std::shared_ptr<CandidateWord>> candidateWords_;
273 
274  void checkIndex(int idx) const {
275  if (idx < 0 || static_cast<size_t>(idx) >= candidateWords_.size()) {
276  throw std::invalid_argument(
277  "DisplayOnlyCandidateList: invalid index");
278  }
279  }
280 };
281 
282 DisplayOnlyCandidateList::DisplayOnlyCandidateList()
283  : d_ptr(std::make_unique<DisplayOnlyCandidateListPrivate>()) {}
284 
285 DisplayOnlyCandidateList::~DisplayOnlyCandidateList() = default;
286 
287 void DisplayOnlyCandidateList::setContent(
288  const std::vector<std::string> &content) {
289  std::vector<Text> text_content;
290  for (const auto &str : content) {
291  text_content.emplace_back();
292  text_content.back().append(str);
293  }
294  setContent(std::move(text_content));
295 }
296 
297 void DisplayOnlyCandidateList::setContent(std::vector<Text> content) {
298  FCITX_D();
299  for (auto &text : content) {
300  d->candidateWords_.emplace_back(
301  std::make_shared<DisplayOnlyCandidateWord>(std::move(text)));
302  }
303 }
304 
305 void DisplayOnlyCandidateList::setLayoutHint(CandidateLayoutHint hint) {
306  FCITX_D();
307  d->layoutHint_ = hint;
308 }
309 
310 void DisplayOnlyCandidateList::setCursorIndex(int index) {
311  FCITX_D();
312  if (index < 0) {
313  d->cursorIndex_ = -1;
314  } else {
315  d->checkIndex(index);
316  d->cursorIndex_ = index;
317  }
318 }
319 
320 const Text &DisplayOnlyCandidateList::label(int idx) const {
321  FCITX_D();
322  d->checkIndex(idx);
323  return d->emptyText_;
324 }
325 
326 const CandidateWord &DisplayOnlyCandidateList::candidate(int idx) const {
327  FCITX_D();
328  d->checkIndex(idx);
329  return *d->candidateWords_[idx];
330 }
331 
332 int DisplayOnlyCandidateList::cursorIndex() const {
333  FCITX_D();
334  return d->cursorIndex_;
335 }
336 
337 int DisplayOnlyCandidateList::size() const {
338  FCITX_D();
339  return d->candidateWords_.size();
340 }
341 
342 CandidateLayoutHint DisplayOnlyCandidateList::layoutHint() const {
343  FCITX_D();
344  return d->layoutHint_;
345 }
346 
348 public:
350  : bulkCursor_(q), cursorModifiable_(q) {}
351 
352  BulkCursorAdaptorForCommonCandidateList bulkCursor_;
353  CursorModifiableAdaptorForCommonCandidateList cursorModifiable_;
354  bool usedNextBefore_ = false;
355  int cursorIndex_ = -1;
356  int currentPage_ = 0;
357  int pageSize_ = 5;
358  std::vector<Text> labels_;
359  // use shared_ptr for type erasure
360  std::vector<std::unique_ptr<CandidateWord>> candidateWord_;
361  CandidateLayoutHint layoutHint_ = CandidateLayoutHint::NotSet;
362  bool cursorIncludeUnselected_ = false;
363  bool cursorKeepInSamePage_ = false;
364  CursorPositionAfterPaging cursorPositionAfterPaging_ =
365  CursorPositionAfterPaging::DonotChange;
366  std::unique_ptr<ActionableCandidateList> actionable_;
367 
368  int size() const {
369  auto start = currentPage_ * pageSize_;
370  auto remain = static_cast<int>(candidateWord_.size()) - start;
371  if (remain > pageSize_) {
372  return pageSize_;
373  }
374  return remain;
375  }
376 
377  int toGlobalIndex(int idx) const {
378  return idx + (currentPage_ * pageSize_);
379  }
380 
381  void checkIndex(int idx) const {
382  if (idx < 0 || idx >= size()) {
383  throw std::invalid_argument("CommonCandidateList: invalid index");
384  }
385  }
386 
387  void checkGlobalIndex(int idx) const {
388  if (idx < 0 || static_cast<size_t>(idx) >= candidateWord_.size()) {
389  throw std::invalid_argument(
390  "CommonCandidateList: invalid global index");
391  }
392  }
393 
394  void fixCursorAfterPaging(int oldIndex) {
395  if (oldIndex < 0) {
396  return;
397  }
398 
399  switch (cursorPositionAfterPaging_) {
400  case CursorPositionAfterPaging::DonotChange:
401  break;
402  case CursorPositionAfterPaging::ResetToFirst:
403  cursorIndex_ = currentPage_ * pageSize_;
404  break;
405  case CursorPositionAfterPaging::SameAsLast: {
406  auto currentPageSize = size();
407  if (oldIndex >= currentPageSize) {
408  cursorIndex_ = currentPage_ * pageSize_ + size() - 1;
409  } else {
410  cursorIndex_ = currentPage_ * pageSize_ + oldIndex;
411  }
412  break;
413  }
414  }
415  }
416 };
417 
418 CommonCandidateList::CommonCandidateList()
419  : d_ptr(std::make_unique<CommonCandidateListPrivate>(this)) {
420  FCITX_D();
421  setPageable(this);
422  setModifiable(this);
423  setBulk(this);
424  setCursorMovable(this);
425  setBulkCursor(&d->bulkCursor_);
426  setCursorModifiable(&d->cursorModifiable_);
427 
428  setLabels();
429 }
430 
431 CommonCandidateList::~CommonCandidateList() {}
432 
433 std::string keyToLabel(const Key &key) {
434  std::string result;
435  if (key.sym() == FcitxKey_None) {
436  return result;
437  }
438 
439 #define _APPEND_MODIFIER_STRING(STR, VALUE) \
440  if (key.states() & KeyState::VALUE) { \
441  result += (STR); \
442  }
443  if (isApple()) {
444  _APPEND_MODIFIER_STRING("⌃", Ctrl)
445  _APPEND_MODIFIER_STRING("⌥", Alt)
446  _APPEND_MODIFIER_STRING("⇧", Shift)
447  _APPEND_MODIFIER_STRING("⌘", Super)
448  } else {
449  _APPEND_MODIFIER_STRING("C-", Ctrl)
450  _APPEND_MODIFIER_STRING("A-", Alt)
451  _APPEND_MODIFIER_STRING("S-", Shift)
452  _APPEND_MODIFIER_STRING("M-", Super)
453  }
454 
455 #undef _APPEND_MODIFIER_STRING
456 
457  auto chr = Key::keySymToUnicode(key.sym());
458  if (chr) {
459  result += utf8::UCS4ToUTF8(chr);
460  } else {
461  result = Key::keySymToString(key.sym(), KeyStringFormat::Localized);
462  }
463  if (!isApple()) {
464  // add a dot as separator
465  result += ". ";
466  }
467 
468  return result;
469 }
470 
471 void CommonCandidateList::setLabels(const std::vector<std::string> &labels) {
472  FCITX_D();
473  fillLabels(d->labels_, labels, [](const std::string &str) { return str; });
474 }
475 
476 void CommonCandidateList::setSelectionKey(const KeyList &keyList) {
477  FCITX_D();
478  fillLabels(d->labels_, keyList,
479  [](const Key &str) -> std::string { return keyToLabel(str); });
480 }
481 
482 void CommonCandidateList::clear() {
483  FCITX_D();
484  d->candidateWord_.clear();
485 }
486 
487 int CommonCandidateList::currentPage() const {
488  FCITX_D();
489  return d->currentPage_;
490 }
491 
492 int CommonCandidateList::cursorIndex() const {
493  FCITX_D();
494  int cursorPage = d->cursorIndex_ / d->pageSize_;
495  if (d->cursorIndex_ >= 0 && cursorPage == d->currentPage_) {
496  return d->cursorIndex_ % d->pageSize_;
497  }
498  return -1;
499 }
500 
501 bool CommonCandidateList::hasNext() const {
502  // page size = 5
503  // total size = 5 -> 1 page
504  // total size = 6 -> 2 page
505  FCITX_D();
506  return d->currentPage_ + 1 < totalPages();
507 }
508 
509 bool CommonCandidateList::hasPrev() const {
510  FCITX_D();
511  return d->currentPage_ > 0;
512 }
513 
514 void CommonCandidateList::prev() {
515  FCITX_D();
516  if (!hasPrev()) {
517  return;
518  }
519  setPage(d->currentPage_ - 1);
520 }
521 
522 void CommonCandidateList::next() {
523  FCITX_D();
524  if (!hasNext()) {
525  return;
526  }
527  setPage(d->currentPage_ + 1);
528  d->usedNextBefore_ = true;
529 }
530 
531 bool CommonCandidateList::usedNextBefore() const {
532  FCITX_D();
533  return d->usedNextBefore_;
534 }
535 
536 void CommonCandidateList::setPageSize(int size) {
537  FCITX_D();
538  if (size < 1) {
539  throw std::invalid_argument("CommonCandidateList: invalid page size");
540  }
541  d->pageSize_ = size;
542  d->currentPage_ = 0;
543 }
544 
545 int CommonCandidateList::pageSize() const {
546  FCITX_D();
547  return d->pageSize_;
548 }
549 
550 int CommonCandidateList::size() const {
551  FCITX_D();
552  return d->size();
553 }
554 
556  FCITX_D();
557  return d->candidateWord_.size();
558 }
559 
560 const CandidateWord &CommonCandidateList::candidate(int idx) const {
561  FCITX_D();
562  d->checkIndex(idx);
563  auto globalIndex = d->toGlobalIndex(idx);
564  return *d->candidateWord_[globalIndex];
565 }
566 
567 const Text &CommonCandidateList::label(int idx) const {
568  FCITX_D();
569  d->checkIndex(idx);
570  if (idx < 0 || idx >= size() ||
571  static_cast<size_t>(idx) >= d->labels_.size()) {
572  throw std::invalid_argument("CommonCandidateList: invalid label idx");
573  }
574 
575  return d->labels_[idx];
576 }
577 
578 void CommonCandidateList::insert(int idx, std::unique_ptr<CandidateWord> word) {
579  FCITX_D();
580  // it's ok to insert at tail
581  if (idx != static_cast<int>(d->candidateWord_.size())) {
582  d->checkGlobalIndex(idx);
583  }
584  d->candidateWord_.insert(d->candidateWord_.begin() + idx, std::move(word));
585 }
586 
587 void CommonCandidateList::remove(int idx) {
588  FCITX_D();
589  d->checkGlobalIndex(idx);
590  d->candidateWord_.erase(d->candidateWord_.begin() + idx);
591  fixAfterUpdate();
592 }
593 
594 int CommonCandidateList::totalPages() const {
595  FCITX_D();
596  return (totalSize() + d->pageSize_ - 1) / d->pageSize_;
597 }
598 
599 void CommonCandidateList::setLayoutHint(CandidateLayoutHint hint) {
600  FCITX_D();
601  d->layoutHint_ = hint;
602 }
603 
604 void CommonCandidateList::setGlobalCursorIndex(int index) {
605  FCITX_D();
606  if (index < 0) {
607  d->cursorIndex_ = -1;
608  } else {
609  d->checkGlobalIndex(index);
610  d->cursorIndex_ = index;
611  }
612 }
613 
615  FCITX_D();
616  d->checkIndex(index);
617  auto globalIndex = d->toGlobalIndex(index);
618  setGlobalCursorIndex(globalIndex);
619 }
620 
622  FCITX_D();
623  return d->cursorIndex_;
624 }
625 
626 CandidateLayoutHint CommonCandidateList::layoutHint() const {
627  FCITX_D();
628  return d->layoutHint_;
629 }
630 
632  FCITX_D();
633  d->checkGlobalIndex(idx);
634  return *d->candidateWord_[idx];
635 }
636 
637 void CommonCandidateList::move(int from, int to) {
638  FCITX_D();
639  d->checkGlobalIndex(from);
640  d->checkGlobalIndex(to);
641  if (from < to) {
642  // 1 2 3 4 5
643  // from 2 to 5
644  // 1 3 4 5 2
645  std::rotate(d->candidateWord_.begin() + from,
646  d->candidateWord_.begin() + from + 1,
647  d->candidateWord_.begin() + to + 1);
648  } else if (from > to) {
649  // 1 2 3 4 5
650  // from 5 to 2
651  // 1 5 2 3 4
652  std::rotate(d->candidateWord_.begin() + to,
653  d->candidateWord_.begin() + from,
654  d->candidateWord_.begin() + from + 1);
655  }
656 }
657 
658 void CommonCandidateList::moveCursor(bool prev) {
659  FCITX_D();
660  if (totalSize() <= 0 || size() <= 0) {
661  return;
662  }
663 
664  int startCursor = d->cursorIndex_;
665  int startPage = d->currentPage_;
666  std::unordered_set<int> deadloopDetect;
667  do {
668  deadloopDetect.insert(d->cursorIndex_);
669  auto pageBegin = d->pageSize_ * d->currentPage_;
670  if (cursorIndex() < 0) {
671  setGlobalCursorIndex(pageBegin + (prev ? size() - 1 : 0));
672  } else {
673  int rotationBase;
674  int rotationSize;
675  if (d->cursorKeepInSamePage_) {
676  rotationBase = pageBegin;
677  rotationSize = size();
678  } else {
679  rotationBase = 0;
680  rotationSize = totalSize();
681  }
682  auto newGlobalIndex = d->cursorIndex_ + (prev ? -1 : 1);
683  if (newGlobalIndex < rotationBase ||
684  newGlobalIndex >= rotationBase + rotationSize) {
685  if (d->cursorIncludeUnselected_) {
686  d->cursorIndex_ = -1;
687  } else {
688  d->cursorIndex_ =
689  prev ? (rotationBase + rotationSize - 1) : rotationBase;
690  }
691  } else {
692  d->cursorIndex_ = newGlobalIndex;
693  }
694  if (!d->cursorKeepInSamePage_ && d->cursorIndex_ >= 0) {
695  setPage(d->cursorIndex_ / d->pageSize_);
696  }
697  }
698  } while (!deadloopDetect.contains(d->cursorIndex_) &&
699  d->cursorIndex_ >= 0 &&
700  candidateFromAll(d->cursorIndex_).isPlaceHolder());
701  if (deadloopDetect.contains(d->cursorIndex_)) {
702  d->cursorIndex_ = startCursor;
703  d->currentPage_ = startPage;
704  }
705 }
706 
707 void CommonCandidateList::prevCandidate() { moveCursor(true); }
708 
709 void CommonCandidateList::nextCandidate() { moveCursor(false); }
710 
711 void CommonCandidateList::setCursorIncludeUnselected(bool v) {
712  FCITX_D();
713  d->cursorIncludeUnselected_ = v;
714 }
715 
716 void CommonCandidateList::setCursorKeepInSamePage(bool v) {
717  FCITX_D();
718  d->cursorKeepInSamePage_ = v;
719 }
720 
721 void CommonCandidateList::setCursorPositionAfterPaging(
722  CursorPositionAfterPaging afterPaging) {
723  FCITX_D();
724  d->cursorPositionAfterPaging_ = afterPaging;
725 }
726 
727 void CommonCandidateList::setPage(int page) {
728  FCITX_D();
729  auto totalPage = totalPages();
730  if (page >= 0 && page < totalPage) {
731  if (d->currentPage_ != page) {
732  auto oldIndex = cursorIndex();
733  d->currentPage_ = page;
734  d->fixCursorAfterPaging(oldIndex);
735  }
736  } else {
737  throw std::invalid_argument("invalid page");
738  }
739 }
740 
741 void CommonCandidateList::replace(int idx,
742  std::unique_ptr<CandidateWord> word) {
743  FCITX_D();
744  d->candidateWord_[idx] = std::move(word);
745 }
746 
747 void CommonCandidateList::fixAfterUpdate() {
748  FCITX_D();
749  if (d->currentPage_ >= totalPages() && d->currentPage_ > 0) {
750  d->currentPage_ = totalPages() - 1;
751  }
752  if (d->cursorIndex_ >= 0) {
753  if (d->cursorIndex_ >= totalSize()) {
754  d->cursorIndex_ = 0;
755  }
756  }
757 }
758 
760  std::unique_ptr<ActionableCandidateList> actionable) {
761  FCITX_D();
762  d->actionable_ = std::move(actionable);
763  setActionable(d->actionable_.get());
764 }
765 
766 } // 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:21
Formatted string commonly used in user interface.
static uint32_t keySymToUnicode(KeySym sym)
Convert keysym to a unicode.
Definition: key.cpp:738
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
bool spaceBetweenComment() const
Whether there should be no space between text and comment.
void setActionableImpl(std::unique_ptr< ActionableCandidateList > actionable)
Set an optional implementation 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:660
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.