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 8 : void begin() {} 27 : /// Refresh, called periodically. 28 20 : void update() {} 29 : /// Called when the setting changes. 30 18 : void update(setting_t oldSetting, setting_t newSetting) { 31 : (void)oldSetting, (void)newSetting; 32 18 : } 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 20 : SelectorBase() = default; 44 : 45 : public: 46 : /// Get the current selection/setting. 47 61 : 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 20 : setting_t setting = 0; 54 : }; 55 : 56 : template <setting_t N, class Callback = EmptySelectorCallback> 57 20 : 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 20 : GenericSelector(Selectable<N> &selectable, const Callback &callback) 71 60 : : selectable(selectable), callback(callback) {} 72 : 73 8 : void begin() override { 74 8 : callback.begin(); 75 8 : reset(); 76 8 : } 77 : 78 20 : void update() override { callback.update(); } 79 : 80 : /// Reset the selection to the initial selection. 81 9 : void reset() { set(selectable.getInitialSelection()); } 82 : 83 : /** 84 : * @brief Select the given selection 85 : * 86 : * @param newSetting 87 : * The new setting to select [0, N-1]. 88 : */ 89 26 : void set(setting_t newSetting) { 90 26 : newSetting = selectable.validateSetting(newSetting); 91 26 : selectable.select(newSetting); 92 26 : if (get() != newSetting) { 93 18 : callback.update(get(), newSetting); 94 18 : setting = newSetting; 95 18 : } 96 26 : } 97 : 98 : /** 99 : * @brief Add one to the setting, wrap around or clamp, depending on the 100 : * parameter, if the new setting would be out of range. 101 : * 102 : * @param wrap 103 : * Wrap or clamp if the new setting would be out of range. 104 : */ 105 11 : void increment(Wrap wrap) { 106 11 : setting_t setting = this->get(); 107 11 : setting++; 108 11 : if (setting == N) { 109 4 : if (wrap == Wrap::Wrap) 110 2 : setting = 0; 111 : else 112 2 : return; 113 2 : } 114 9 : this->set(setting); 115 11 : } 116 : 117 : /** 118 : * @brief Subtract one from the setting, wrap around or clamp, depending 119 : * on the parameter, if the new setting would be out of range. 120 : * 121 : * @param wrap 122 : * Wrap or clamp if the new setting would be out of range. 123 : */ 124 5 : void decrement(Wrap wrap) { 125 5 : setting_t setting = this->get(); 126 5 : if (setting == 0) { 127 2 : if (wrap == Wrap::Wrap) 128 1 : setting = N; 129 : else 130 1 : return; 131 1 : } 132 4 : setting--; 133 4 : this->set(setting); 134 5 : } 135 : 136 : private: 137 : Selectable<N> &selectable; 138 : 139 : public: 140 : Callback callback; 141 : }; 142 : 143 : /** 144 : * @brief A Selector with an empty callback. 145 : * 146 : * @tparam N 147 : * The number of possible settings. 148 : */ 149 : template <setting_t N> 150 : class Selector : public GenericSelector<N> { 151 : public: 152 : /// Constructor 153 : Selector(Selectable<N> &selectable) : GenericSelector<N>{selectable, {}} {} 154 : }; 155 : 156 : END_CS_NAMESPACE