mirror of
				https://github.com/jhbruhn/eurorack.git
				synced 2025-10-28 18:06:05 +00:00 
			
		
		
		
	Implement invisible menu items but also destroy scrolling
This commit is contained in:
		
							parent
							
								
									4ae5d4a9b5
								
							
						
					
					
						commit
						600b139b8e
					
				
					 6 changed files with 145 additions and 35 deletions
				
			
		|  | @ -15,5 +15,5 @@ class BaseDisplay { | |||
|   void Init(); | ||||
| 
 | ||||
|   protected: | ||||
|   virtual void InitGLib(); | ||||
|   virtual void InitGLib() = 0; | ||||
| }; | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| #include "menu.h" | ||||
| #include <algorithm> | ||||
| #include <u8g2.h> | ||||
| 
 | ||||
| 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->items[this->selectedItem + 1]->visible()) { | ||||
|         if (this->selectedItem - this->currentScrollStart == maxVisibleItems - 2 && this->itemCount - this->currentScrollStart > maxVisibleItems) { | ||||
|           this->currentScrollStart++; | ||||
|         } | ||||
| 
 | ||||
|         this->selectedItem++; | ||||
|       } else { | ||||
|         for (size_t i = this->selectedItem + 1; i < this->itemCount; i++) { | ||||
|           if (this->items[i]->visible()) { | ||||
|             this->selectedItem = i; | ||||
|             break; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
| }; | ||||
|  |  | |||
|  | @ -1,15 +1,29 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "stmlib/stmlib.h" | ||||
| #include <functional> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| static auto always_visible = [] { return true; }; | ||||
| 
 | ||||
| // MenuItem without template so we can easily store pointers
 | ||||
| class AbstractMenuItem { | ||||
|   private: | ||||
|   std::function<bool()> visibility_indicator; | ||||
| 
 | ||||
|   public: | ||||
|   virtual const char* get_label(); | ||||
|   virtual char* get_string_representation(); | ||||
|   virtual void increase(); | ||||
|   virtual void decrease(); | ||||
|   AbstractMenuItem(std::function<bool()> 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 T> | ||||
|  | @ -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<bool()> 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<T> { | |||
| 
 | ||||
|   protected: | ||||
|   NumberMenuItem() {}; | ||||
|   NumberMenuItem(const char* _label, T* _value, T _minimumValue, T _maximumValue, T _step) | ||||
|       : MenuItem<T>(_label, _value) | ||||
|   NumberMenuItem(const char* _label, T* _value, T _minimumValue, T _maximumValue, T _step, std::function<bool()> visibility_indicator = always_visible) | ||||
|       : MenuItem<T>(_label, _value, visibility_indicator) | ||||
|       , minimumValue(_minimumValue) | ||||
|       , maximumValue(_maximumValue) | ||||
|       , step(_step) {}; | ||||
|  | @ -104,8 +127,8 @@ class UInt32MenuItem : public NumberMenuItem<uint32_t> { | |||
|   } | ||||
| 
 | ||||
|   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<bool()> visibility_indicator = always_visible) | ||||
|       : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; | ||||
|   UInt32MenuItem() {}; | ||||
| }; | ||||
| 
 | ||||
|  | @ -118,8 +141,8 @@ class UInt8MenuItem : public NumberMenuItem<uint8_t> { | |||
|   } | ||||
| 
 | ||||
|   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<bool()> visibility_indicator = always_visible) | ||||
|       : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; | ||||
|   UInt8MenuItem() {}; | ||||
| }; | ||||
| 
 | ||||
|  | @ -132,8 +155,8 @@ class Int32MenuItem : public NumberMenuItem<int32_t> { | |||
|   } | ||||
| 
 | ||||
|   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<bool()> visibility_indicator = always_visible) | ||||
|       : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; | ||||
|   Int32MenuItem(); | ||||
| }; | ||||
| 
 | ||||
|  | @ -146,8 +169,8 @@ class FloatMenuItem : public NumberMenuItem<float> { | |||
|   } | ||||
| 
 | ||||
|   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<bool()> visibility_indicator = always_visible) | ||||
|       : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step, visibility_indicator) {}; | ||||
|   FloatMenuItem() {}; | ||||
| }; | ||||
| 
 | ||||
|  | @ -170,8 +193,8 @@ class BoolMenuItem : public NumberMenuItem<bool> { | |||
|   } | ||||
| 
 | ||||
|   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<bool()> 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<uint8_t> { | |||
|   } | ||||
| 
 | ||||
|   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<bool()> 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<uint8_t> { | |||
|     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<bool()> visibility_indicator = always_visible) | ||||
|       : NumberMenuItem(_label, _value, 0, 127, 1, visibility_indicator) | ||||
|   { | ||||
|     note_strings[0] = "C"; | ||||
|     note_strings[1] = "C#"; | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue