mirror of
https://github.com/jhbruhn/eurorack.git
synced 2025-03-15 02:55:49 +00:00
Null safety for menu items, remove initialized flag from part_menu
This commit is contained in:
parent
51da3fc17e
commit
8f4cb4f4f3
7 changed files with 122 additions and 107 deletions
|
@ -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,12 +82,14 @@ class NumberMenuItem : public MenuItem<T> {
|
||||||
public:
|
public:
|
||||||
void increase()
|
void increase()
|
||||||
{
|
{
|
||||||
|
if (this->get_value_ptr())
|
||||||
if (*this->get_value_ptr() + step <= maximumValue && *this->get_value_ptr() + step >= minimumValue)
|
if (*this->get_value_ptr() + step <= maximumValue && *this->get_value_ptr() + step >= minimumValue)
|
||||||
this->set_value(*this->get_value_ptr() + step);
|
this->set_value(*this->get_value_ptr() + step);
|
||||||
};
|
};
|
||||||
|
|
||||||
void decrease()
|
void decrease()
|
||||||
{
|
{
|
||||||
|
if (this->get_value_ptr())
|
||||||
if (*this->get_value_ptr() - step >= minimumValue && *this->get_value_ptr() - step <= maximumValue)
|
if (*this->get_value_ptr() - step >= minimumValue && *this->get_value_ptr() - step <= maximumValue)
|
||||||
this->set_value(*this->get_value_ptr() - step);
|
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() {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue