Implement invisible menu items but also destroy scrolling

This commit is contained in:
Jan-Henrik 2020-02-26 01:22:26 +01:00
parent 4ae5d4a9b5
commit 600b139b8e
6 changed files with 145 additions and 35 deletions

View file

@ -15,5 +15,5 @@ class BaseDisplay {
void Init();
protected:
virtual void InitGLib();
virtual void InitGLib() = 0;
};

View file

@ -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;
}
}
}
}
}
}

View file

@ -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);
};

View file

@ -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#";

View file

@ -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;

View file

@ -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)