Fcitx
option.h
1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_CONFIG_OPTION_H_
8 #define _FCITX_CONFIG_OPTION_H_
9 
10 #include <limits>
11 #include <memory>
12 #include <optional>
13 #include <stdexcept>
14 #include <string>
15 #include <type_traits>
16 #include <utility>
17 #include <vector>
18 #include <fcitx-config/fcitxconfig_export.h>
19 #include <fcitx-config/marshallfunction.h>
20 #include <fcitx-config/option_details.h> // IWYU pragma: export
21 #include <fcitx-config/optiontypename.h>
22 #include <fcitx-config/rawconfig.h>
23 #include <fcitx-utils/flags.h>
24 #include <fcitx-utils/key.h>
25 
26 namespace fcitx {
27 
28 /// An option that launches external tool.
29 class FCITXCONFIG_EXPORT ExternalOption : public OptionBase {
30 public:
31  ExternalOption(Configuration *parent, std::string path,
32  std::string description, std::string uri);
33 
34  std::string typeString() const override;
35  void reset() override;
36  bool isDefault() const override;
37 
38  void marshall(RawConfig &config) const override;
39  bool unmarshall(const RawConfig &config, bool partial) override;
40  std::unique_ptr<Configuration> subConfigSkeleton() const override;
41 
42  bool equalTo(const OptionBase &other) const override;
43  void copyFrom(const OptionBase &other) override;
44 
45  bool skipDescription() const override;
46  bool skipSave() const override;
47  void dumpDescription(RawConfig &config) const override;
48 
49 private:
50  std::string externalUri_;
51 };
52 
53 /// An option that launches external tool.
54 class FCITXCONFIG_EXPORT SubConfigOption : public ExternalOption {
55 public:
56  using ExternalOption::ExternalOption;
57  void dumpDescription(RawConfig &config) const override;
58 };
59 
60 /// Default Constrain with no actual constrain.
61 template <typename T>
62 struct NoConstrain {
63  using Type = T;
64  bool check(const T & /*unused*/) const { return true; }
65  void dumpDescription(RawConfig & /*unused*/) const {}
66 };
67 
68 /// Default Annotation with no options.
69 struct NoAnnotation {
70  bool skipDescription() { return false; }
71  bool skipSave() { return false; }
72  void dumpDescription(RawConfig & /*unused*/) const {}
73 };
74 
75 /// Annotation to display a tooltip in configtool.
77  ToolTipAnnotation(std::string tooltip) : tooltip_(std::move(tooltip)) {}
78 
79  bool skipDescription() { return false; }
80  bool skipSave() { return false; }
81  void dumpDescription(RawConfig &config) const {
82  config.setValueByPath("Tooltip", tooltip_);
83  }
84 
85 private:
86  std::string tooltip_;
87 };
88 
89 /// For a list of sub config, the field that should be used for display.
91  ListDisplayOptionAnnotation(std::string option)
92  : option_(std::move(option)) {}
93 
94  bool skipDescription() { return false; }
95  bool skipSave() { return false; }
96  void dumpDescription(RawConfig &config) const {
97  config.setValueByPath("ListDisplayOption", option_);
98  }
99 
100 private:
101  std::string option_;
102 };
103 
104 /**
105  * Annotation to be used against String type to indicate this is a Font.
106  */
108  bool skipDescription() { return false; }
109  bool skipSave() { return false; }
110  void dumpDescription(RawConfig &config) {
111  config.setValueByPath("Font", "True");
112  }
113 };
114 
115 /**
116  * Annotation to be used against String type, for those type of string
117  * that should shown as a combobox, but the value is run time based.
118  * User of this annotation should take a sub class of it and set
119  * Enum/n, and EnumI18n/n correspondingly.
120  */
122  bool skipDescription() { return false; }
123  bool skipSave() { return false; }
124  void dumpDescription(RawConfig &config) const {
125  config.setValueByPath("IsEnum", "True");
126  }
127 };
128 
129 /**
130  * Option that will not shown in UI.
131  *
132  * You may want to use HiddenOption instead.
133  *
134  * @see HiddenOption
135  */
137  bool skipDescription() { return true; }
138  bool skipSave() { return false; }
139  void dumpDescription(RawConfig & /*unused*/) const {}
140 };
141 
142 template <typename Annotation>
143 struct HideInDescriptionAnnotation : public Annotation {
144  using Annotation::Annotation;
145  bool skipDescription() { return true; }
146  using Annotation::dumpDescription;
147  using Annotation::skipSave;
148 };
149 
150 /**
151  * List Constrain that applies the constrain to all element.
152  */
153 template <typename SubConstrain>
155  ListConstrain(SubConstrain sub = SubConstrain()) : sub_(std::move(sub)) {}
156 
157  using ElementType = typename SubConstrain::Type;
158  using Type = std::vector<ElementType>;
159  bool check(const Type &value) {
160  return std::all_of(
161  value.begin(), value.end(),
162  [this](const ElementType &ele) { return sub_.check(ele); });
163  }
164 
165  void dumpDescription(RawConfig &config) const {
166  sub_.dumpDescription(*config.get("ListConstrain", true));
167  }
168 
169 private:
170  SubConstrain sub_;
171 };
172 
173 /**
174  * Optional Constrain that applies the constrain only when the value is not
175  * std::nullopt.
176  */
177 template <typename SubConstrain>
179  OptionalConstrain(SubConstrain sub = SubConstrain())
180  : sub_(std::move(sub)) {}
181 
182  using ElementType = typename SubConstrain::Type;
183  using Type = std::optional<ElementType>;
184  bool check(const Type &value) {
185  if (!value) {
186  return true;
187  }
188  return sub_.check(*value);
189  }
190  void dumpDescription(RawConfig &config) const {
191  sub_.dumpDescription(*config.get("OptionalConstrain", true));
192  }
193 
194 private:
195  SubConstrain sub_;
196 };
197 
198 /// Integer type constrain with a lower and a upper bound.
200 public:
201  using Type = int;
202  IntConstrain(int min = std::numeric_limits<int>::min(),
203  int max = std::numeric_limits<int>::max())
204  : min_(min), max_(max) {}
205  bool check(int value) const { return value >= min_ && value <= max_; }
206  void dumpDescription(RawConfig &config) const {
207  if (min_ != std::numeric_limits<int>::min()) {
208  marshallOption(config["IntMin"], min_);
209  }
210  if (max_ != std::numeric_limits<int>::max()) {
211  marshallOption(config["IntMax"], max_);
212  }
213  }
214 
215 private:
216  int min_;
217  int max_;
218 };
219 
220 /// Key option constrain flag.
221 enum class KeyConstrainFlag {
222  /// The key can be modifier only, like Control_L.
223  AllowModifierOnly = (1 << 0),
224  /// The key can be modifier less (Key that usually produce character).
225  AllowModifierLess = (1 << 1),
226 };
227 
229 
230 /// Key option constrain.
232 public:
233  using Type = Key;
234  KeyConstrain(KeyConstrainFlags flags) : flags_(flags) {}
235 
236  bool check(const Key &key) const {
237  if (!flags_.test(KeyConstrainFlag::AllowModifierLess) &&
238  key.states() == 0) {
239  return false;
240  }
241 
242  if (!flags_.test(KeyConstrainFlag::AllowModifierOnly) &&
243  key.isModifier()) {
244  return false;
245  }
246 
247  return true;
248  }
249 
250  void dumpDescription(RawConfig &config) const {
251  if (flags_.test(KeyConstrainFlag::AllowModifierLess)) {
252  config["AllowModifierLess"] = "True";
253  }
254  if (flags_.test(KeyConstrainFlag::AllowModifierOnly)) {
255  config["AllowModifierOnly"] = "True";
256  }
257  }
258 
259 private:
260  KeyConstrainFlags flags_;
261 };
262 
263 /// Default marshaller that write the config RawConfig.
264 template <typename T>
266  DefaultMarshaller() = default;
267 
268  void marshall(RawConfig &config, const T &value) const {
269  return marshallOption(config, value);
270  }
271  bool unmarshall(T &value, const RawConfig &config, bool partial) const {
272  return unmarshallOption(value, config, partial);
273  }
274 };
275 
276 /// A helper class provide writing ability to option value.
277 template <typename OptionType>
279 public:
280  using value_type = typename OptionType::value_type;
281 
282  MutableOption(OptionType *option = nullptr)
283  : option_(option), value_(option ? option->value() : value_type()) {}
284 
285  ~MutableOption() {
286  if (option_) {
287  option_->setValue(std::move(value_));
288  }
289  }
290 
291  MutableOption(MutableOption &&other) noexcept
292  : option_(other.option_), value_(std::move(other.value_)) {
293  other.option_ = nullptr;
294  }
295 
296  MutableOption &operator=(MutableOption &&other) noexcept {
297  option_ = other.option_;
298  value_ = std::move(other.value_);
299  other.option_ = nullptr;
300  }
301 
302  value_type &operator*() { return value_; }
303  value_type *operator->() { return &value_; }
304 
305 private:
306  OptionType *option_;
307  value_type value_;
308 };
309 
310 template <typename Constrain, typename Marshaller, typename Annotation,
311  typename T>
313  Configuration *parent;
314  std::string path;
315  std::string description;
316  T defaultValue;
317  Constrain constrain{};
318  Marshaller marshaller{};
319  Annotation annotation{};
320 };
321 
322 /**
323  * Represent a Configuration option.
324  *
325  */
326 template <typename T, typename Constrain = NoConstrain<T>,
327  typename Marshaller = DefaultMarshaller<T>,
328  typename Annotation = NoAnnotation>
329 class Option : public OptionBaseV3 {
330 public:
331  using value_type = T;
332  using constrain_type = Constrain;
333  using OptionParametersType =
335 
336  Option(Configuration *parent, std::string path, std::string description,
337  const T &defaultValue = T(), Constrain constrain = Constrain(),
338  Marshaller marshaller = Marshaller(),
339  Annotation annotation = Annotation())
340  : OptionBaseV3(parent, std::move(path), std::move(description)),
341  defaultValue_(defaultValue), value_(defaultValue),
342  marshaller_(std::move(marshaller)), constrain_(std::move(constrain)),
343  annotation_(std::move(annotation)) {
344  if (!constrain_.check(defaultValue_)) {
345  throw std::invalid_argument(
346  "defaultValue doesn't satisfy constrain");
347  }
348  }
349 
351  : Option(params.parent, std::move(params.path),
352  std::move(params.description), std::move(params.defaultValue),
353  std::move(params.constrain), std::move(params.marshaller),
354  std::move(params.annotation)) {}
355 
356  std::string typeString() const override { return OptionTypeName<T>::get(); }
357 
358  void dumpDescription(RawConfig &config) const override {
359  OptionBase::dumpDescription(config);
360  if constexpr (not std::is_base_of_v<Configuration, T>) {
361  marshaller_.marshall(config["DefaultValue"], defaultValue_);
362  }
363  constrain_.dumpDescription(config);
364  annotation_.dumpDescription(config);
365  using ::fcitx::dumpDescriptionHelper;
366  dumpDescriptionHelper(
367  config, static_cast<typename RemoveVector<T>::type *>(nullptr));
368  }
369 
370  std::unique_ptr<Configuration> subConfigSkeleton() const override {
371  if constexpr (std::is_base_of_v<Configuration, T>) {
372  auto skeleton = std::make_unique<T>(defaultValue_);
373  skeleton->syncDefaultValueToCurrent();
374  return skeleton;
375  }
376  if constexpr (std::is_base_of_v<Configuration,
377  typename RemoveVector<T>::type>) {
378  return std::make_unique<typename RemoveVector<T>::type>();
379  }
380 
381  return nullptr;
382  }
383 
384  bool isDefault() const override { return defaultValue_ == value_; }
385  void reset() override { value_ = defaultValue_; }
386 
387  const T &value() const { return value_; }
388 
389  const T &defaultValue() const { return defaultValue_; }
390 
391  const T &operator*() const { return value(); }
392  const T *operator->() const { return &value_; }
393 
394  template <typename U>
395  bool setValue(U &&value) {
396  if (!constrain_.check(value)) {
397  return false;
398  }
399  value_ = std::forward<U>(value);
400  return true;
401  }
402 
403  template <typename Dummy = int,
404  std::enable_if_t<!std::is_same<Constrain, NoConstrain<T>>::value,
405  Dummy> = 0>
406  MutableOption<Option> mutableValue() {
407  return {this};
408  }
409 
410  template <typename Dummy = int,
411  std::enable_if_t<std::is_same<Constrain, NoConstrain<T>>::value,
412  Dummy> = 0>
413  T *mutableValue() {
414  return &value_;
415  }
416 
417  void marshall(RawConfig &config) const override {
418  return marshaller_.marshall(config, value_);
419  }
420  bool unmarshall(const RawConfig &config, bool partial) override {
421  T tempValue{};
422  if (partial) {
423  tempValue = value_;
424  }
425  if (!marshaller_.unmarshall(tempValue, config, partial)) {
426  return false;
427  }
428  return setValue(tempValue);
429  }
430 
431  bool equalTo(const OptionBase &other) const override {
432  auto otherP = static_cast<const Option *>(&other);
433  return value_ == otherP->value_;
434  }
435 
436  void copyFrom(const OptionBase &other) override {
437  auto otherP = static_cast<const Option *>(&other);
438  value_ = otherP->value_;
439  }
440 
441  bool skipDescription() const override {
442  return annotation_.skipDescription();
443  }
444 
445  bool skipSave() const override { return annotation_.skipSave(); }
446 
447  void syncDefaultValueToCurrent() override {
448  defaultValue_ = value_;
449  if constexpr (std::is_base_of_v<Configuration, T>) {
450  value_.syncDefaultValueToCurrent();
451  defaultValue_.syncDefaultValueToCurrent();
452  }
453  }
454 
455  auto &annotation() const { return annotation_; }
456 
457 private:
458  T defaultValue_;
459  T value_;
460  Marshaller marshaller_;
461  Constrain constrain_;
462  mutable Annotation annotation_;
463 };
464 
465 /// Shorthand if you want a option type with only custom annotation.
466 template <typename T, typename Annotation>
467 using OptionWithAnnotation =
469 
470 /// Shorthand if you want a option type with only custom annotation.
471 template <typename Annotation>
474  Annotation>;
475 
476 /// Shorthand for KeyList option with constrain.
478 
479 /// Shorthand for create a key list constrain.
480 static inline ListConstrain<KeyConstrain>
481 KeyListConstrain(KeyConstrainFlags flags = KeyConstrainFlags()) {
482  return {KeyConstrain(flags)};
483 }
484 
485 /// Shorthand for option that will not show in UI.
486 template <typename T, typename Constrain = NoConstrain<T>,
487  typename Marshaller = DefaultMarshaller<T>,
488  typename Annotation = NoAnnotation>
489 using HiddenOption =
491 
492 template <bool hidden, typename T>
494 
495 template <typename T, typename Constrain, typename Marshaller,
496  typename Annotation>
498  Option<T, Constrain, Marshaller, Annotation>> {
500 };
501 
502 template <typename T, typename Constrain, typename Marshaller,
503  typename Annotation>
505  Option<T, Constrain, Marshaller, Annotation>> {
507 };
508 
509 template <>
511  using OptionType = SubConfigOption;
512 };
513 
514 template <>
516  class HiddenSubConfigOption : public SubConfigOption {
517  public:
518  using SubConfigOption::SubConfigOption;
519  bool skipDescription() const override { return true; }
520  };
521  using OptionType = HiddenSubConfigOption;
522 };
523 
524 template <bool hidden, typename T>
525 using ConditionalHidden =
527 
528 } // namespace fcitx
529 
530 #endif // _FCITX_CONFIG_OPTION_H_
Optional Constrain that applies the constrain only when the value is not std::nullopt.
Definition: option.h:178
A helper class provide writing ability to option value.
Definition: option.h:278
Describe a Key in fcitx.
Definition: key.h:41
Option that will not shown in UI.
Definition: option.h:136
Default Constrain with no actual constrain.
Definition: option.h:62
Annotation to be used against String type to indicate this is a Font.
Definition: option.h:107
List Constrain that applies the constrain to all element.
Definition: option.h:154
Definition: action.cpp:17
For a list of sub config, the field that should be used for display.
Definition: option.h:90
Key option constrain.
Definition: option.h:231
bool isModifier() const
Check if the key is a modifier press.
Definition: key.cpp:460
An option that launches external tool.
Definition: option.h:54
An option that launches external tool.
Definition: option.h:29
Annotation to be used against String type, for those type of string that should shown as a combobox...
Definition: option.h:121
Helper template class to make easier to use type safe enum flags.
Default marshaller that write the config RawConfig.
Definition: option.h:265
Class to represent a key.
Annotation to display a tooltip in configtool.
Definition: option.h:76
Represent a Configuration option.
Definition: option.h:329
Default Annotation with no options.
Definition: option.h:69
Integer type constrain with a lower and a upper bound.
Definition: option.h:199