Null safety for menu items, remove initialized flag from part_menu

This commit is contained in:
Jan-Henrik 2020-02-23 17:14:07 +01:00
parent 51da3fc17e
commit 8f4cb4f4f3
7 changed files with 122 additions and 107 deletions

View file

@ -21,6 +21,7 @@ class MenuItem : public AbstractMenuItem {
char stringRepresentation[24]; char stringRepresentation[24];
protected: protected:
MenuItem() {};
MenuItem(const char* _label, T* _value) MenuItem(const char* _label, T* _value)
: label(_label) : label(_label)
, value(_value) {}; , value(_value) {};
@ -29,6 +30,8 @@ class MenuItem : public AbstractMenuItem {
void set_value(T value) void set_value(T value)
{ {
if (!this->value)
return;
*this->value = value; *this->value = value;
this->to_string(stringRepresentation); this->to_string(stringRepresentation);
}; };
@ -46,7 +49,9 @@ class MenuItem : public AbstractMenuItem {
char* get_string_representation() char* get_string_representation()
{ {
if (!stringRepresentation[0]) if (!this->value) {
sprintf(stringRepresentation, "ERROR");
} else if (!stringRepresentation[0])
this->to_string(stringRepresentation); this->to_string(stringRepresentation);
return stringRepresentation; return stringRepresentation;
} }
@ -60,6 +65,7 @@ class NumberMenuItem : public MenuItem<T> {
T step; T step;
protected: protected:
NumberMenuItem() {};
NumberMenuItem(const char* _label, T* _value, T _minimumValue, T _maximumValue, T _step) NumberMenuItem(const char* _label, T* _value, T _minimumValue, T _maximumValue, T _step)
: MenuItem<T>(_label, _value) : MenuItem<T>(_label, _value)
, minimumValue(_minimumValue) , minimumValue(_minimumValue)
@ -76,14 +82,16 @@ class NumberMenuItem : public MenuItem<T> {
public: public:
void increase() void increase()
{ {
if (*this->get_value_ptr() + step <= maximumValue && *this->get_value_ptr() + step >= minimumValue) if (this->get_value_ptr())
this->set_value(*this->get_value_ptr() + step); if (*this->get_value_ptr() + step <= maximumValue && *this->get_value_ptr() + step >= minimumValue)
this->set_value(*this->get_value_ptr() + step);
}; };
void decrease() void decrease()
{ {
if (*this->get_value_ptr() - step >= minimumValue && *this->get_value_ptr() - step <= maximumValue) if (this->get_value_ptr())
this->set_value(*this->get_value_ptr() - step); if (*this->get_value_ptr() - step >= minimumValue && *this->get_value_ptr() - step <= maximumValue)
this->set_value(*this->get_value_ptr() - step);
}; };
}; };
@ -98,6 +106,7 @@ class UInt32MenuItem : public NumberMenuItem<uint32_t> {
public: public:
UInt32MenuItem(const char* _label, uint32_t* _value, uint32_t _minimumValue, uint32_t _maximumValue, uint32_t _step) UInt32MenuItem(const char* _label, uint32_t* _value, uint32_t _minimumValue, uint32_t _maximumValue, uint32_t _step)
: NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {};
UInt32MenuItem() {};
}; };
class Int32MenuItem : public NumberMenuItem<int32_t> { class Int32MenuItem : public NumberMenuItem<int32_t> {
@ -111,6 +120,7 @@ class Int32MenuItem : public NumberMenuItem<int32_t> {
public: public:
Int32MenuItem(const char* _label, int32_t* _value, int32_t _minimumValue, int32_t _maximumValue, int32_t _step) Int32MenuItem(const char* _label, int32_t* _value, int32_t _minimumValue, int32_t _maximumValue, int32_t _step)
: NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {};
Int32MenuItem();
}; };
class FloatMenuItem : public NumberMenuItem<float> { class FloatMenuItem : public NumberMenuItem<float> {
@ -124,6 +134,7 @@ class FloatMenuItem : public NumberMenuItem<float> {
public: public:
FloatMenuItem(const char* _label, float* _value, float _minimumValue, float _maximumValue, float _step) FloatMenuItem(const char* _label, float* _value, float _minimumValue, float _maximumValue, float _step)
: NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {}; : NumberMenuItem(_label, _value, _minimumValue, _maximumValue, _step) {};
FloatMenuItem() {};
}; };
class BoolMenuItem : public NumberMenuItem<bool> { class BoolMenuItem : public NumberMenuItem<bool> {
@ -135,8 +146,9 @@ class BoolMenuItem : public NumberMenuItem<bool> {
protected: protected:
const char* get_format_string() const char* get_format_string()
{ {
bool value = *this->get_value_ptr(); bool value = false;
if (this->get_value_ptr())
value = *this->get_value_ptr();
if (value) if (value)
return this->on_string; return this->on_string;
else else
@ -148,6 +160,7 @@ class BoolMenuItem : public NumberMenuItem<bool> {
: NumberMenuItem(_label, _value, 0, 1, 1) : NumberMenuItem(_label, _value, 0, 1, 1)
, on_string(_on_string) , on_string(_on_string)
, off_string(_off_string) {}; , off_string(_off_string) {};
BoolMenuItem() {};
}; };
class StringListMenuItem : public NumberMenuItem<uint8_t> { class StringListMenuItem : public NumberMenuItem<uint8_t> {
@ -157,13 +170,17 @@ class StringListMenuItem : public NumberMenuItem<uint8_t> {
protected: protected:
const char* get_format_string() const char* get_format_string()
{ {
return this->string_labels[*this->get_value_ptr()]; size_t index = 0;
if (this->get_value_ptr())
index = *this->get_value_ptr();
return this->string_labels[index];
} }
public: public:
StringListMenuItem(const char* _label, uint8_t* _value, const char** _stringLabels, size_t _itemCount) StringListMenuItem(const char* _label, uint8_t* _value, const char** _stringLabels, size_t _itemCount)
: NumberMenuItem(_label, _value, 0, _itemCount - 1, 1) : NumberMenuItem(_label, _value, 0, _itemCount - 1, 1)
, string_labels(_stringLabels) {}; , string_labels(_stringLabels) {};
StringListMenuItem() {};
}; };
class MidiNoteMenuItem : public NumberMenuItem<uint8_t> { class MidiNoteMenuItem : public NumberMenuItem<uint8_t> {
@ -177,7 +194,9 @@ class MidiNoteMenuItem : public NumberMenuItem<uint8_t> {
public: public:
char* get_string_representation() char* get_string_representation()
{ {
uint8_t currentNote = *this->get_value_ptr(); uint8_t currentNote = 0;
if (this->get_value_ptr())
currentNote = *this->get_value_ptr();
int note = currentNote % 12; int note = currentNote % 12;
int octave = (currentNote / 12) - 1; int octave = (currentNote / 12) - 1;
@ -202,4 +221,5 @@ class MidiNoteMenuItem : public NumberMenuItem<uint8_t> {
note_strings[10] = "A#"; note_strings[10] = "A#";
note_strings[11] = "B"; note_strings[11] = "B";
}; };
MidiNoteMenuItem() {};
}; };

View file

@ -1,5 +1,6 @@
#include <stm32f37x_conf.h> #include <stm32f37x_conf.h>
#include "config.h"
#include "drivers/display.h" #include "drivers/display.h"
#include "drivers/encoder.h" #include "drivers/encoder.h"
#include "drivers/gpio.h" #include "drivers/gpio.h"
@ -8,7 +9,6 @@
#include "part.h" #include "part.h"
#include "stmlib/system/system_clock.h" #include "stmlib/system/system_clock.h"
#include "ui.h" #include "ui.h"
#include "config.h"
using namespace stmlib; using namespace stmlib;
@ -16,8 +16,9 @@ GPIO gpio;
Display display; Display display;
Encoder encoder; Encoder encoder;
UI ui; Part parts[PART_COUNT];
Part part[PART_COUNT]; Part* part_pointers[PART_COUNT] = { &parts[0], &parts[1], &parts[2], &parts[3] };
UI ui(part_pointers);
// Default interrupt handlers. // Default interrupt handlers.
extern "C" { extern "C" {

View file

@ -7,17 +7,12 @@
#include "part.h" #include "part.h"
#include "stmlib/utils/random.h" #include "stmlib/utils/random.h"
#include <u8g2.h> #include <u8g2.h>
#include <array>
using namespace stmlib; using namespace stmlib;
const uint32_t kEncoderLongPressTime = 600; const uint32_t kEncoderLongPressTime = 600;
// TODO: This is kind of ugly, can we improve this somehow? UI::UI(Part** part_pointers) : main_menu(part_pointers)
Part parts[4];
Part* part_pointers[4] = {&parts[0], &parts[1], &parts[2], &parts[3]};
MainMenu mainMenu(part_pointers);
UI::UI()
{ {
this->input_queue.Init(); this->input_queue.Init();
} }
@ -53,7 +48,7 @@ void UI::Draw()
{ {
display.u8g2()->clearBuffer(); display.u8g2()->clearBuffer();
mainMenu.render(display.u8g2(), 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); main_menu.render(display.u8g2(), 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
display.Swap(); display.Swap();
} }
@ -90,18 +85,18 @@ void UI::DoEvents()
void UI::OnClick() void UI::OnClick()
{ {
mainMenu.enter(); main_menu.enter();
} }
void UI::OnLongClick() void UI::OnLongClick()
{ {
mainMenu.back(); main_menu.back();
} }
void UI::OnIncrement(Event& e) void UI::OnIncrement(Event& e)
{ {
if (e.data > 0) if (e.data > 0)
mainMenu.down(); main_menu.down();
else else
mainMenu.up(); main_menu.up();
} }

View file

@ -4,9 +4,13 @@
#include "stmlib/stmlib.h" #include "stmlib/stmlib.h"
#include "stmlib/ui/event_queue.h" #include "stmlib/ui/event_queue.h"
#include "config.h"
#include "ui/main_menu.h"
#include <array>
class UI { class UI {
public: public:
UI(); UI(Part** part_pointers);
~UI() {} ~UI() {}
void Poll(); void Poll();
@ -21,6 +25,8 @@ class UI {
bool encoder_long_press_event_sent_; bool encoder_long_press_event_sent_;
uint32_t encoder_press_time_; uint32_t encoder_press_time_;
MainMenu<PART_COUNT> main_menu;
void Draw(); void Draw();
void OnClick(); void OnClick();

View file

@ -1,72 +0,0 @@
#include "main_menu.h"
#include "stmlib/stmlib.h"
#include <u8g2.h>
const int kHeaderHeight = 13;
const char* kPartNames[] = { "A", "B", "C", "D" };
void MainMenu::back()
{
if (this->activePartMenu >= 0) {
if (this->partMenus[this->activePartMenu].back())
this->activePartMenu = -1;
}
}
void MainMenu::enter()
{
if (this->activePartMenu >= 0) {
if (this->partMenus[this->activePartMenu].enter())
this->activePartMenu = -1;
} else {
this->activePartMenu = this->selectedPart;
}
}
void MainMenu::up()
{
if (this->activePartMenu >= 0) {
this->partMenus[this->activePartMenu].up();
} else {
this->selectedPart--;
CONSTRAIN(this->selectedPart, 0, PART_COUNT - 1);
}
}
void MainMenu::down()
{
if (this->activePartMenu >= 0) {
this->partMenus[this->activePartMenu].down();
} else {
this->selectedPart++;
CONSTRAIN(this->selectedPart, 0, PART_COUNT - 1);
}
}
void MainMenu::render(U8G2* u8g2, int x, int y, int width, int height)
{
u8g2->setFont(/*u8g2_font_5x8_tf*/ u8g2_font_pcsenior_8u);
for (int i = 0; i < PART_COUNT; i++) {
u8g2->setFontMode(1);
u8g2->setDrawColor(1);
if (this->selectedPart == i) {
if (this->activePartMenu == i) {
u8g2->drawBox(x + 1 + i * (width / PART_COUNT), y + 1, (width / PART_COUNT) - 3, kHeaderHeight - 2);
} else {
u8g2->drawFrame(x + 1 + i * (width / PART_COUNT), y + 1, (width / PART_COUNT) - 3, kHeaderHeight - 2);
u8g2->drawFrame(x + i * (width / PART_COUNT), y, (width / PART_COUNT) - 1, kHeaderHeight);
}
} else {
u8g2->drawFrame(x + 1 + i * (width / PART_COUNT), y + 1, (width / PART_COUNT) - 3, kHeaderHeight - 2);
}
u8g2->setDrawColor(2);
u8g2->drawStr(x + i * (width / PART_COUNT) + 5, y + 9, kPartNames[i]);
u8g2->setDrawColor(1);
}
u8g2->drawHLine(0, kHeaderHeight + 1, width);
this->partMenus[this->selectedPart].render(u8g2, x, y + kHeaderHeight + 3, width, height - kHeaderHeight - 3);
}

View file

@ -1,25 +1,90 @@
#pragma once #pragma once
#include "../config.h"
#include "part_menu.h" #include "part_menu.h"
#include "stmlib/stmlib.h"
#include <U8g2lib.h> #include <U8g2lib.h>
static const int kHeaderHeight = 13;
static const char* kPartNames[] = { "A", "B", "C", "D" };
template <size_t part_count>
class MainMenu { class MainMenu {
public: public:
void back(); void back()
void enter(); {
void up(); if (this->activePartMenu >= 0) {
void down(); if (this->partMenus[this->activePartMenu].back())
this->activePartMenu = -1;
}
};
void enter()
{
if (this->activePartMenu >= 0) {
if (this->partMenus[this->activePartMenu].enter())
this->activePartMenu = -1;
} else {
this->activePartMenu = this->selectedPart;
}
};
void up()
{
if (this->activePartMenu >= 0) {
this->partMenus[this->activePartMenu].up();
} else {
this->selectedPart--;
CONSTRAIN(this->selectedPart, 0, (int)part_count - 1);
}
};
void down()
{
if (this->activePartMenu >= 0) {
this->partMenus[this->activePartMenu].down();
} else {
this->selectedPart++;
CONSTRAIN(this->selectedPart, 0, (int)part_count - 1);
}
};
void render(U8G2* u8g2, int x, int y, int width, int height); void render(U8G2* u8g2, int x, int y, int width, int height)
{
u8g2->setFont(/*u8g2_font_5x8_tf*/ u8g2_font_pcsenior_8u);
for (size_t i = 0; i < part_count; i++) {
u8g2->setFontMode(1);
u8g2->setDrawColor(1);
if (this->selectedPart == (int)i) {
if (this->activePartMenu == (int)i) {
u8g2->drawBox(x + 1 + i * (width / part_count), y + 1, (width / part_count) - 3, kHeaderHeight - 2);
} else {
u8g2->drawFrame(x + 1 + i * (width / part_count), y + 1, (width / part_count) - 3, kHeaderHeight - 2);
u8g2->drawFrame(x + i * (width / part_count), y, (width / part_count) - 1, kHeaderHeight);
}
} else {
u8g2->drawFrame(x + 1 + i * (width / part_count), y + 1, (width / part_count) - 3, kHeaderHeight - 2);
}
u8g2->setDrawColor(2);
u8g2->drawStr(x + i * (width / part_count) + 5, y + 9, kPartNames[i]);
u8g2->setDrawColor(1);
}
u8g2->drawHLine(0, kHeaderHeight + 1, width);
this->partMenus[this->selectedPart].render(u8g2, x, y + kHeaderHeight + 3, width, height - kHeaderHeight - 3);
}
MainMenu(Part** parts) MainMenu(Part** parts)
: partMenus({ parts[0], parts[1], parts[2], parts[3] }) : partMenus()
, activePartMenu(0) , activePartMenu(0)
, selectedPart(0) {}; , selectedPart(0)
{
for (size_t i = 0; i < part_count; i++) { // This is quite hacky, but it works.. we call the constructor again, this time with our parameters (Part*), as we cant do so directly from the default constructor for the array (thanks c++...)
new (&partMenus[i]) PartMenu(parts[i]);
}
};
private: private:
PartMenu partMenus[PART_COUNT]; PartMenu partMenus[part_count];
int activePartMenu; int activePartMenu;
int selectedPart; int selectedPart;

View file

@ -8,7 +8,7 @@
class PartMenu { class PartMenu {
public: public:
PartMenu(Part* _part); PartMenu(Part* _part);
PartMenu() {}
bool enter(); bool enter();
bool back(); bool back();
void up(); void up();