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