#pragma once #include "stmlib/stmlib.h" #include #include static auto always_visible = [] { return true; }; // MenuItem without template so we can easily store pointers class AbstractMenuItem { private: std::function visibility_indicator; public: AbstractMenuItem(std::function visibility_indicator = always_visible) : visibility_indicator(visibility_indicator) {}; bool visible() { if (visibility_indicator) return visibility_indicator(); return true; } virtual const char* get_label() = 0; virtual char* get_string_representation() = 0; virtual void increase() = 0; virtual void decrease() = 0; }; template class MenuItem : public AbstractMenuItem { private: const char* label; T* value; T step; char stringRepresentation[24]; protected: MenuItem() : AbstractMenuItem(always_visible) {}; MenuItem(const char* _label, T* _value, std::function visibility_indicator = always_visible) : AbstractMenuItem(visibility_indicator) , label(_label) , value(_value) {}; virtual void to_string(char* buf) = 0; void set_value(T value) { if (!this->value) return; *this->value = value; this->to_string(stringRepresentation); }; public: bool visible() { if (this->visibility_indicator) return this->visibility_indicator(); return true; } T* get_value_ptr() { return value; } const char* get_label() { return label; } char* get_string_representation() { if (!this->value) { sprintf(stringRepresentation, "ERROR"); } else if (!stringRepresentation[0]) this->to_string(stringRepresentation); return stringRepresentation; } }; template class NumberMenuItem : public MenuItem { private: T minimumValue; T maximumValue; T step; protected: NumberMenuItem() {}; NumberMenuItem(const char* _label, T* _value, T _minimumValue, T _maximumValue, T _step, std::function visibility_indicator = always_visible) : MenuItem(_label, _value, visibility_indicator) , minimumValue(_minimumValue) , maximumValue(_maximumValue) , step(_step) {}; virtual const char* get_format_string() = 0; void to_string(char* buf) { sprintf(buf, this->get_format_string(), *this->get_value_ptr()); } public: void increase() { if (this->get_value_ptr()) if (*this->get_value_ptr() + step <= maximumValue && *this->get_value_ptr() + step >= minimumValue) this->set_value(*this->get_value_ptr() + step); }; void decrease() { if (this->get_value_ptr()) if (*this->get_value_ptr() - step >= minimumValue && *this->get_value_ptr() - step <= maximumValue) this->set_value(*this->get_value_ptr() - step); }; }; class UInt32MenuItem : public NumberMenuItem { private: protected: const char* get_format_string() { return "%u"; } public: UInt32MenuItem(const char* _label, uint32_t* _value, uint32_t _minimumValue, uint32_t _maximumValue, uint32_t _step, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; UInt32MenuItem() {}; }; class UInt8MenuItem : public NumberMenuItem { private: protected: const char* get_format_string() { return "%u"; } public: UInt8MenuItem(const char* _label, uint8_t* _value, uint8_t _minimumValue, uint8_t _maximumValue, uint8_t _step, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; UInt8MenuItem() {}; }; class Int32MenuItem : public NumberMenuItem { private: protected: const char* get_format_string() { return "%d"; } public: Int32MenuItem(const char* _label, int32_t* _value, int32_t _minimumValue, int32_t _maximumValue, int32_t _step, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; Int32MenuItem(); }; class FloatMenuItem : public NumberMenuItem { private: protected: const char* get_format_string() { return "%.2f"; } public: FloatMenuItem(const char* _label, float* _value, float _minimumValue, float _maximumValue, float _step, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; FloatMenuItem() {}; }; class BoolMenuItem : public NumberMenuItem { private: const char* on_string; const char* off_string; protected: const char* get_format_string() { bool value = false; if (this->get_value_ptr()) value = *this->get_value_ptr(); if (value) return this->on_string; else return this->off_string; } public: BoolMenuItem(const char* _label, bool* _value, const char* _on_string, const char* _off_string, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, 0, 1, 1, visibility_indicator) , on_string(_on_string) , off_string(_off_string) {}; BoolMenuItem() {}; }; class StringListMenuItem : public NumberMenuItem { private: const char** string_labels; protected: const char* get_format_string() { size_t index = 0; if (this->get_value_ptr()) index = *this->get_value_ptr(); return this->string_labels[index]; } public: StringListMenuItem(const char* _label, uint8_t* _value, const char** _stringLabels, size_t _itemCount, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, 0, _itemCount - 1, 1, visibility_indicator) , string_labels(_stringLabels) {}; StringListMenuItem() {}; }; class MidiNoteMenuItem : public NumberMenuItem { private: char string_buffer[4]; const char* note_strings[12]; protected: const char* get_format_string() { return ""; } public: char* get_string_representation() { uint8_t currentNote = 0; if (this->get_value_ptr()) currentNote = *this->get_value_ptr(); int note = currentNote % 12; int octave = (currentNote / 12) - 1; sprintf(this->string_buffer, "%s%d", note_strings[note], octave); return this->string_buffer; } MidiNoteMenuItem(const char* _label, uint8_t* _value, std::function visibility_indicator = always_visible) : NumberMenuItem(_label, _value, 0, 127, 1, visibility_indicator) { note_strings[0] = "C"; note_strings[1] = "C#"; note_strings[2] = "D"; note_strings[3] = "D#"; note_strings[4] = "E"; note_strings[5] = "F"; note_strings[6] = "F#"; note_strings[7] = "G"; note_strings[8] = "G#"; note_strings[9] = "A"; note_strings[10] = "A#"; note_strings[11] = "B"; }; MidiNoteMenuItem() {}; };