1 #pragma once
2 
3 #include <wayfire/config/option-types.hpp>
4 #include <functional>
5 #include <limits>
6 
7 #include <memory>
8 
9 namespace wf
10 {
11 namespace config
12 {
13 /**
14  * A base class for all option types.
15  */
16 class option_base_t
17 {
18   public:
19     virtual ~option_base_t();
20     option_base_t(const option_base_t& other) = delete;
21     option_base_t& operator =(const option_base_t& other) = delete;
22 
23     /** @return The name of the option */
24     std::string get_name() const;
25 
26     /** @return A copy of the option */
27     virtual std::shared_ptr<option_base_t> clone_option() const = 0;
28 
29     /**
30      * Set the option value from the given string.
31      * Invalid values are ignored.
32      *
33      * @return true if the option value was updated.
34      */
35     virtual bool set_value_str(const std::string& value) = 0;
36 
37     /** Reset the option to its default value.  */
38     virtual void reset_to_default() = 0;
39 
40     /**
41      * Change the default value of an option. Note that this will not change the
42      * option value, only its default value.
43      *
44      * If the new default value is invalid, the request will be ignored.
45      * @return true if the default value was updated.
46      */
47     virtual bool set_default_value_str(const std::string& default_value) = 0;
48 
49     /** Get the option value in string format */
50     virtual std::string get_value_str() const = 0;
51 
52     /** Get the default option value in string format */
53     virtual std::string get_default_value_str() const = 0;
54 
55     /**
56      * A function to be executed when the option value changes.
57      */
58     using updated_callback_t = std::function<void ()>;
59 
60     /**
61      * Register a new callback to execute when the option value changes.
62      */
63     void add_updated_handler(updated_callback_t *callback);
64 
65     /**
66      * Unregister a callback to execute when the option value changes.
67      * If the same callback has been registered multiple times, this unregister
68      * all registered instances.
69      */
70     void rem_updated_handler(updated_callback_t *callback);
71 
72     /**
73      * Set the lock status of an option, this is reference-counted.
74      *
75      * An option is unlocked by default. When an option is locked, the option
76      * should not be modified by any config backend (for ex. when reading from
77      * a file).
78      *
79      * Note that changing the value of the option manually still works.
80      */
81     void set_locked(bool locked = true);
82 
83     /**
84      * Get the current locked status.
85      */
86     bool is_locked() const;
87 
88     struct impl;
89     std::unique_ptr<impl> priv;
90 
91   protected:
92     /** Construct a new option with the given name. */
93     option_base_t(const std::string& name);
94 
95     /** Notify all watchers */
96     void notify_updated() const;
97 
98     /** Initialize a cloned version of this option. */
99     void init_clone(option_base_t& clone) const;
100 };
101 
102 /**
103  * A base class for options which can have minimum and maximum.
104  * By default, no bounding checks are enabled.
105  */
106 template<class Type, bool enable_bounds>
107 class bounded_option_base_t
108 {
109   protected:
closest_valid_value(const Type & value) const110     Type closest_valid_value(const Type& value) const
111     {
112         return value;
113     }
114 };
115 
116 /**
117  * Specialization for option types which do support bounded values.
118  */
119 template<class Type>
120 class bounded_option_base_t<Type, true>
121 {
122   public:
123     /** @return The minimal permissible value for this option, if it is set. */
get_minimum() const124     stdx::optional<Type> get_minimum() const
125     {
126         return minimum;
127     }
128 
129     /** @return The maximal permissible value for this option, if it is set. */
get_maximum() const130     stdx::optional<Type> get_maximum() const
131     {
132         return maximum;
133     }
134 
135   protected:
136     stdx::optional<Type> minimum;
137     stdx::optional<Type> maximum;
138 
139     /**
140      * @return The closest possible value
141      */
closest_valid_value(const Type & value) const142     Type closest_valid_value(const Type& value) const
143     {
144         auto real_minimum =
145             minimum.value_or(std::numeric_limits<Type>::lowest());
146         auto real_maximum =
147             maximum.value_or(std::numeric_limits<Type>::max());
148 
149         if (value < real_minimum)
150         {
151             return real_minimum;
152         }
153 
154         if (value > real_maximum)
155         {
156             return real_maximum;
157         }
158 
159         return value;
160     }
161 };
162 
163 namespace detail
164 {
165 template<class Type, class Result> using boundable_type_only =
166     std::enable_if_t<std::is_arithmetic<Type>::value, Result>;
167 }
168 
169 /**
170  * Represents an option of the given type.
171  */
172 template<class Type>
173 class option_t : public option_base_t,
174     public bounded_option_base_t<Type, std::is_arithmetic<Type>::value>
175 {
176   public:
177     /**
178      * Create a new option with the given name and default value.
179      */
option_t(const std::string & name,Type def_value)180     option_t(const std::string& name, Type def_value) :
181         option_base_t(name), default_value(def_value), value(default_value)
182     {}
183 
184     /**
185      * Create a copy of the option.
186      */
clone_option() const187     virtual std::shared_ptr<option_base_t> clone_option() const override
188     {
189         auto result = std::make_shared<option_t>(get_name(), get_default_value());
190         result->set_value(get_value());
191         if constexpr (std::is_arithmetic<Type>::value)
192         {
193             result->minimum = this->minimum;
194             result->maximum = this->maximum;
195         }
196 
197         init_clone(*result);
198         return result;
199     }
200 
201     /**
202      * Set the value of the option from the given string.
203      * The value will be auto-clamped to the defined bounds, if they exist.
204      * If the value actually changes, the updated handlers will be called.
205      */
set_value_str(const std::string & new_value_str)206     virtual bool set_value_str(const std::string& new_value_str) override
207     {
208         auto new_value = option_type::from_string<Type>(new_value_str);
209         if (new_value)
210         {
211             set_value(new_value.value());
212             return true;
213         }
214 
215         return false;
216     }
217 
218     /**
219      * Reset the option to its default value.
220      */
reset_to_default()221     virtual void reset_to_default() override
222     {
223         set_value(default_value);
224     }
225 
226     /**
227      * Change the default value of the function, if possible.
228      */
set_default_value_str(const std::string & defvalue)229     virtual bool set_default_value_str(const std::string& defvalue) override
230     {
231         auto parsed = option_type::from_string<Type>(defvalue);
232         if (parsed)
233         {
234             this->default_value = parsed.value();
235             return true;
236         }
237 
238         return false;
239     }
240 
241     /**
242      * Set the value of the option.
243      * The value will be auto-clamped to the defined bounds, if they exist.
244      * If the value actually changes, the updated handlers will be called.
245      */
set_value(const Type & new_value)246     void set_value(const Type& new_value)
247     {
248         auto real_value = this->closest_valid_value(new_value);
249         if (!(this->value == real_value))
250         {
251             this->value = real_value;
252             this->notify_updated();
253         }
254     }
255 
get_value() const256     Type get_value() const
257     {
258         return value;
259     }
260 
get_default_value() const261     Type get_default_value() const
262     {
263         return default_value;
264     }
265 
get_value_str() const266     virtual std::string get_value_str() const override
267     {
268         return option_type::to_string<Type>(get_value());
269     }
270 
get_default_value_str() const271     virtual std::string get_default_value_str() const override
272     {
273         return option_type::to_string<Type>(get_default_value());
274     }
275 
276   public:
277     /**
278      * Set the minimum permissible value for arithmetic type options.
279      * An attempt to set the value to a value below the minimum will set the
280      * value of the option to the minimum.
281      */
282     template<class U = void>
set_minimum(Type min)283     detail::boundable_type_only<Type, U> set_minimum(Type min)
284     {
285         this->minimum = {min};
286         this->value   = this->closest_valid_value(this->value);
287     }
288 
289     /**
290      * Set the maximum permissible value for arithmetic type options.
291      * An attempt to set the value to a value above the maximum will set the
292      * value of the option to the maximum.
293      */
294     template<class U = void>
set_maximum(Type max)295     detail::boundable_type_only<Type, U> set_maximum(Type max)
296     {
297         this->maximum = {max};
298         this->value   = this->closest_valid_value(this->value);
299     }
300 
301   protected:
302     Type default_value; /* default value */
303     Type value; /* current value */
304 };
305 }
306 }
307