diff --git a/midi2cv/drivers/base_display.h b/midi2cv/drivers/base_display.h index 1be2dc0..03b97b3 100644 --- a/midi2cv/drivers/base_display.h +++ b/midi2cv/drivers/base_display.h @@ -15,5 +15,5 @@ class BaseDisplay { void Init(); protected: - virtual void InitGLib(); + virtual void InitGLib() = 0; }; diff --git a/midi2cv/menu/menu.cc b/midi2cv/menu/menu.cc index 62b14e1..0361f91 100644 --- a/midi2cv/menu/menu.cc +++ b/midi2cv/menu/menu.cc @@ -1,7 +1,6 @@ #include "menu.h" #include #include - const int kMenuItemHeight = 12; void Menu::render(U8G2* u8g2_, uint8_t xStart, uint8_t yStart, uint8_t width, uint8_t height) @@ -13,12 +12,25 @@ void Menu::render(U8G2* u8g2_, uint8_t xStart, uint8_t yStart, uint8_t width, ui uint8_t itemsToRender = std::min(maxVisibleItems, uint8_t(this->itemCount - currentScrollStart)); u8g2_->setFont(u8g2_font_6x10_tf); + + uint8_t itemIndex = this->currentScrollStart; + for (uint8_t i = 0; i < itemsToRender; i++) { - bool selected = this->selectedItem == (i + this->currentScrollStart); - bool editing = this->currentEditingItem == (i + this->currentScrollStart); uint8_t yPosition = yStart + i * kMenuItemHeight; - AbstractMenuItem* item = this->items[i + this->currentScrollStart]; + AbstractMenuItem* item; + + while (!(item = this->items[itemIndex])->visible()) { + itemIndex++; + if (itemIndex >= this->itemCount) + break; + } + + if (itemIndex >= this->itemCount) + break; + + bool selected = this->selectedItem == itemIndex; + bool editing = this->currentEditingItem == itemIndex; u8g2_->setDrawColor(selected ? 1 : 0); @@ -33,19 +45,39 @@ void Menu::render(U8G2* u8g2_, uint8_t xStart, uint8_t yStart, uint8_t width, ui uint8_t valueStringWidth = u8g2_->getStrWidth(item->get_string_representation()); u8g2_->drawStr(xStart + width - valueStringWidth - 2, yPosition + kMenuItemHeight - 3, item->get_string_representation()); + + itemIndex++; } } void Menu::up() { if (this->currentEditingItem >= 0) { + // we store the amount of visible items before the counter before + // changing and then compare afterwards to move the cursor back to + // its intended position if necessary + uint8_t visible_count_before = this->visible_item_count_before_selection(); + this->items[this->selectedItem]->decrease(); + + uint8_t visible_count_after = this->visible_item_count_before_selection(); + + this->selectedItem += visible_count_after - visible_count_before; } else if (this->selectedItem > 0) { - if (this->selectedItem - this->currentScrollStart == 1) { // keep scroll start one up + uint8_t newItem = this->selectedItem; + int8_t i; + for (i = this->selectedItem - 1; i >= 0; i--) { + if (this->items[i]->visible()) { + newItem = i; + break; + } + } + + if (newItem - this->currentScrollStart == i) { // keep scroll start one up this->currentScrollStart--; } - this->selectedItem--; + this->selectedItem = newItem; if (this->selectedItem == 0) { this->currentScrollStart = 0; @@ -56,15 +88,30 @@ void Menu::up() void Menu::down() { if (this->currentEditingItem >= 0) { + uint8_t visible_count_before = this->visible_item_count_before_selection(); + this->items[this->selectedItem]->increase(); + + uint8_t visible_count_after = this->visible_item_count_before_selection(); + + this->selectedItem += visible_count_after - visible_count_before; } else { uint8_t maxVisibleItems = height / kMenuItemHeight; if (this->selectedItem < this->itemCount - 1) { - if (this->selectedItem - this->currentScrollStart == maxVisibleItems - 2 && this->itemCount - this->currentScrollStart > maxVisibleItems) { - this->currentScrollStart++; - } + if (this->items[this->selectedItem + 1]->visible()) { + if (this->selectedItem - this->currentScrollStart == maxVisibleItems - 2 && this->itemCount - this->currentScrollStart > maxVisibleItems) { + this->currentScrollStart++; + } - this->selectedItem++; + this->selectedItem++; + } else { + for (size_t i = this->selectedItem + 1; i < this->itemCount; i++) { + if (this->items[i]->visible()) { + this->selectedItem = i; + break; + } + } + } } } } diff --git a/midi2cv/menu/menu.h b/midi2cv/menu/menu.h index fd18be6..0eefd05 100644 --- a/midi2cv/menu/menu.h +++ b/midi2cv/menu/menu.h @@ -35,5 +35,29 @@ class Menu { } } + uint8_t visible_item_count() + { + uint8_t count = 0; + + for (size_t i = 0; i < itemCount; i++) { + if (items[i]->visible()) + count++; + } + + return count; + } + + uint8_t visible_item_count_before_selection() + { + uint8_t count = 0; + + for (size_t i = 0; i <= selectedItem; i++) { + if (items[i]->visible()) + count++; + } + + return count; + } + void render(U8G2* u8g2_, uint8_t x, uint8_t y, uint8_t width, uint8_t height); }; diff --git a/midi2cv/menu/menu_items.h b/midi2cv/menu/menu_items.h index 060086f..f4b2e4e 100644 --- a/midi2cv/menu/menu_items.h +++ b/midi2cv/menu/menu_items.h @@ -1,15 +1,29 @@ #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: - virtual const char* get_label(); - virtual char* get_string_representation(); - virtual void increase(); - virtual void decrease(); + 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 @@ -21,9 +35,11 @@ class MenuItem : public AbstractMenuItem { char stringRepresentation[24]; protected: - MenuItem() {}; - MenuItem(const char* _label, T* _value) - : label(_label) + 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; @@ -37,6 +53,13 @@ class MenuItem : public AbstractMenuItem { }; public: + bool visible() + { + if (this->visibility_indicator) + return this->visibility_indicator(); + return true; + } + T* get_value_ptr() { return value; @@ -66,8 +89,8 @@ class NumberMenuItem : public MenuItem { protected: NumberMenuItem() {}; - NumberMenuItem(const char* _label, T* _value, T _minimumValue, T _maximumValue, T _step) - : MenuItem(_label, _value) + 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) {}; @@ -104,8 +127,8 @@ class UInt32MenuItem : public NumberMenuItem { } public: - UInt32MenuItem(const char* _label, uint32_t* _value, uint32_t _minimumValue, uint32_t _maximumValue, uint32_t _step) - : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; + 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() {}; }; @@ -118,8 +141,8 @@ class UInt8MenuItem : public NumberMenuItem { } public: - UInt8MenuItem(const char* _label, uint8_t* _value, uint8_t _minimumValue, uint8_t _maximumValue, uint8_t _step) - : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; + 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() {}; }; @@ -132,8 +155,8 @@ class Int32MenuItem : public NumberMenuItem { } public: - Int32MenuItem(const char* _label, int32_t* _value, int32_t _minimumValue, int32_t _maximumValue, int32_t _step) - : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; + 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(); }; @@ -146,8 +169,8 @@ class FloatMenuItem : public NumberMenuItem { } public: - FloatMenuItem(const char* _label, float* _value, float _minimumValue, float _maximumValue, float _step) - : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; + 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() {}; }; @@ -170,8 +193,8 @@ class BoolMenuItem : public NumberMenuItem { } public: - BoolMenuItem(const char* _label, bool* _value, const char* _on_string, const char* _off_string) - : NumberMenuItem(_label, _value, 0, 1, 1) + 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() {}; @@ -191,8 +214,8 @@ class StringListMenuItem : public NumberMenuItem { } public: - StringListMenuItem(const char* _label, uint8_t* _value, const char** _stringLabels, size_t _itemCount) - : NumberMenuItem(_label, _value, 0, _itemCount - 1, 1) + 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() {}; }; @@ -219,8 +242,8 @@ class MidiNoteMenuItem : public NumberMenuItem { return this->string_buffer; } - MidiNoteMenuItem(const char* _label, uint8_t* _value) - : NumberMenuItem(_label, _value, 0, 127, 1) + 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#"; diff --git a/midi2cv/midi2cv.cc b/midi2cv/midi2cv.cc index dc0cb66..12ea50d 100644 --- a/midi2cv/midi2cv.cc +++ b/midi2cv/midi2cv.cc @@ -11,6 +11,20 @@ #include "stmlib/system/system_clock.h" #include "ui.h" +namespace std { +void __throw_bad_function_call() +{ + while (1) + ; +}; +} + +extern "C" void __cxa_pure_virtual() +{ + while (1) + ; +} + using namespace stmlib; GPIO gpio; diff --git a/midi2cv/ui/part_menu.cc b/midi2cv/ui/part_menu.cc index cebb2e9..dbef5f0 100644 --- a/midi2cv/ui/part_menu.cc +++ b/midi2cv/ui/part_menu.cc @@ -15,7 +15,9 @@ PartMenu::PartMenu(Part* _part) , item_voice_count("voice count", (uint8_t*)&_part->data.part_voice_count, 1, 4, 1) , item_voice_detail("voice detail", (uint8_t*)&_part->data.part_voice_detail, kVoiceDetailStrings, 4) , item_midi_filter_enabled("MIDI filter", &_part->data.midi_filter_channel_enabled, "on", "off") - , item_midi_channel("MIDI channel", (uint8_t*)&_part->data.midi_filter_channel, kMidiChannelStrings, 17) + , item_midi_channel("MIDI channel", (uint8_t*)&_part->data.midi_filter_channel, kMidiChannelStrings, 17, [_part] { + return _part->data.midi_filter_channel_enabled; + }) , item_midi_input("MIDI input", (uint8_t*)&_part->data.midi_filter_input, kMidiInputStrings, 3) , item_midi_lowest_note("MIDI lowest", (uint8_t*)&_part->data.midi_filter_lowest_note) , item_midi_highest_note("MIDI highest", (uint8_t*)&_part->data.midi_filter_highest_note)