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