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