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