Line data Source code
1 : #pragma once
2 :
3 : #include "Selectable.hpp"
4 : #include <AH/Containers/Updatable.hpp>
5 : #include <AH/Debug/Debug.hpp>
6 : #include <Def/Def.hpp>
7 :
8 : BEGIN_CS_NAMESPACE
9 :
10 : /**
11 : * @brief An enumeration to set the behavior of selectors that are incremented
12 : * (decremented) beyond their maximum (minimum) setting.
13 : */
14 : enum class Wrap : bool {
15 : Clamp = false, ///< When the maximum (minimum) setting is reached,
16 : ///< clamp to the maximum (minimum) setting.
17 : Wrap = true, ///< When the maximum (minimum) setting is reached,
18 : ///< wrap around to the minimum (maximum) setting.
19 : NoWrap = false,
20 : };
21 :
22 : /// A callback for the GenericSelector class that does nothing.
23 : struct EmptySelectorCallback {
24 : /// Initialize.
25 7 : void begin() {}
26 : /// Refresh, called periodically.
27 26 : void update() {}
28 : /// Called when the setting changes.
29 23 : void update(setting_t oldSetting, setting_t newSetting) {
30 : (void)oldSetting, (void)newSetting;
31 23 : }
32 : };
33 :
34 : /// Base class for all Selectors exposing the `get` method, so it can be used
35 : /// by display elements etc, without having to provide the full generic type.
36 : ///
37 : /// A `set` method is not provided, because that would require either more
38 : /// virtual functions, or a rather large refactoring.
39 : class SelectorBase {
40 : protected:
41 : /// Constructor.
42 19 : SelectorBase() = default;
43 :
44 : public:
45 : /// Get the current selection/setting.
46 44 : setting_t get() const { return setting; }
47 :
48 : protected:
49 : /// The selection of the selector. It is saved in the selector as well as
50 : /// the selectable, because you need it in order to implement
51 : /// increment/decrement methods.
52 : setting_t setting = 0;
53 : };
54 :
55 : template <setting_t N, class Callback = EmptySelectorCallback>
56 : class GenericSelector : public SelectorBase, public AH::Updatable<> {
57 : public:
58 : /**
59 : * @brief Constructor.
60 : *
61 : * @param selectable
62 : * The selectable object to manage. When the value of the selector
63 : * changes, it changes the selection of this selectable.
64 : * @param callback
65 : * The callback to call when the value changes. Used for (visual)
66 : * feedback from the selector (e.g. LEDs or some different kind of
67 : * display).
68 : */
69 19 : GenericSelector(Selectable<N> &selectable, const Callback &callback)
70 19 : : selectable(selectable), callback(callback) {}
71 :
72 7 : void begin() override {
73 7 : callback.begin();
74 7 : reset();
75 7 : }
76 :
77 26 : void update() override { callback.update(); }
78 :
79 : /// Reset the selection to the initial selection.
80 8 : void reset() {
81 8 : setting_t initialSelection = selectable.getInitialSelection();
82 8 : selectable.select(initialSelection);
83 8 : callback.update(initialSelection, initialSelection);
84 8 : this->setting = initialSelection;
85 8 : }
86 :
87 : /**
88 : * @brief Select the given selection
89 : *
90 : * @param newSetting
91 : * The new setting to select [0, N-1].
92 : */
93 15 : void set(setting_t newSetting) {
94 15 : newSetting = selectable.validateSetting(newSetting);
95 15 : selectable.select(newSetting);
96 15 : if (get() != newSetting) {
97 15 : callback.update(get(), newSetting);
98 15 : this->setting = newSetting;
99 : }
100 15 : }
101 :
102 : /**
103 : * @brief Add one to the setting, wrap around or clamp, depending on the
104 : * parameter, if the new setting would be out of range.
105 : *
106 : * @param wrap
107 : * Wrap or clamp if the new setting would be out of range.
108 : */
109 8 : void increment(Wrap wrap) {
110 8 : setting_t setting = this->get();
111 8 : setting++;
112 8 : if (setting == N) {
113 3 : if (wrap == Wrap::Wrap)
114 2 : setting = 0;
115 : else
116 1 : return;
117 : }
118 7 : this->set(setting);
119 : }
120 :
121 : /**
122 : * @brief Subtract one from the setting, wrap around or clamp, depending
123 : * on the parameter, if the new setting would be out of range.
124 : *
125 : * @param wrap
126 : * Wrap or clamp if the new setting would be out of range.
127 : */
128 5 : void decrement(Wrap wrap) {
129 5 : setting_t setting = this->get();
130 5 : if (setting == 0) {
131 2 : if (wrap == Wrap::Wrap)
132 1 : setting = N;
133 : else
134 1 : return;
135 : }
136 4 : setting--;
137 4 : this->set(setting);
138 : }
139 :
140 : private:
141 : Selectable<N> &selectable;
142 :
143 : public:
144 : Callback callback;
145 : };
146 :
147 : /**
148 : * @brief A Selector with an empty callback.
149 : *
150 : * @tparam N
151 : * The number of possible settings.
152 : */
153 : template <setting_t N>
154 : class Selector : public GenericSelector<N> {
155 : public:
156 : /// Constructor
157 : Selector(Selectable<N> &selectable) : GenericSelector<N> {selectable, {}} {}
158 : };
159 :
160 : END_CS_NAMESPACE
|