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