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