Как распечатать/отобразить следующий пункт меню с помощью menubackend.h?
Я пытаюсь сделать навигационное меню с подменю для отображения на OLED. С этим все в порядке, однако я хотел бы отображать следующий элемент на том же уровне, чтобы поощрять прокрутку, но я не могу с этим разобраться.
Я хочу, чтобы отображался текущий пункт меню, а также следующий пункт, до которого можно добраться, прокрутив вниз. Я знаю, что можно легко отобразить предыдущие и текущие пункты с помощью
Serial.print(fromFlash(changed.from.getName()));
и
Serial.println(fromFlash(changed.to.getName()));
соответственно - но я хотел бы отобразить текущий и следующий (ниже, но на том же уровне) элемент. Как мне это сделать? Я игрался с этим большую часть сегодняшнего дня, но я в полном тупике.
Я упростил приведенный ниже пример. Ниже я включил menubackend.h
Работает на чипе ATMEGA328P.
#include "MenuBackend.h"
/*
This is the structure of the modelled menu
Settings
Pin
Debug
Options
Delay (D)
100 ms
200 ms
300 ms
400 ms
*/
#define TEST_FROMFLASH
#undef TEST_GETITEMNAMEFROMFLASH
const char itemNameSettings[] PROGMEM = "Settings";
const char itemNamePin[] PROGMEM = "Pin";
const char itemNameDebug[] PROGMEM = "Debug";
const char itemNameOptions[] PROGMEM = "Options";
const char itemNameDelay[] PROGMEM = "Delay";
const char itemName100ms[] PROGMEM = "100 ms";
const char itemName200ms[] PROGMEM = "200 ms";
const char itemName300ms[] PROGMEM = "300 ms";
const char itemName400ms[] PROGMEM = "400 ms";
const char itemNameshutdown[] PROGMEM = "shutdown";
static void menuChangeEvent(MenuChangeEvent changed);
static void menuUseEvent(MenuUseEvent used);
//this controls the menu backend and the event generation
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
//beneath is list of menu items needed to build the menu
MenuItem settings = MenuItem(itemNameSettings);
MenuItem pin = MenuItem(itemNamePin);
MenuItem debug = MenuItem(itemNameDebug);
MenuItem options = MenuItem(itemNameOptions);
MenuItem setDelay = MenuItem(itemNameDelay,'D');
MenuItem d100 = MenuItem(itemName100ms);
MenuItem d200 = MenuItem(itemName200ms);
MenuItem d300 = MenuItem(itemName300ms);
MenuItem d400 = MenuItem(itemName400ms);
MenuItem shutdown1 = MenuItem(itemNameshutdown);
//this function builds the menu and connects the correct items together
void menuSetup()
{
Serial.println(F("Setting up menu..."));
//add the file menu to the menu root
menu.getRoot().add(settings);
//setup the settings menu item
settings.addRight(pin);
//we want looping both up and down
pin.addBefore(debug);
pin.addAfter(debug);
debug.addAfter(pin);
//we want a left movement to pint to settings from anywhere
debug.addLeft(shutdown1);
pin.addLeft(settings);
settings.addAfter(options);
shutdown1.addAfter(settings);
options.addAfter(shutdown1);
options.addRight(setDelay);
setDelay.addLeft(options);
setDelay.addRight(d100);
d100.addBefore(d100); //loop to d400
d100.addAfter(d200);
d200.addAfter(d300);
d300.addAfter(d400);
d400.addAfter(d100); //loop back to d100
//we want left to always be bak to delay
d100.addLeft(setDelay);
d200.addLeft(setDelay);
d300.addLeft(setDelay);
d400.addLeft(setDelay);
}
#ifdef TEST_FROMFLASH
inline const __FlashStringHelper * fromFlash(const char * pStr)
{
return reinterpret_cast<const __FlashStringHelper *>(pStr);
}
#endif
#ifdef TEST_GETITEMNAMEFROMFLASH
inline char * getItemNameFromFlash(const MenuItem & item, char * copyToBuffer, size_t bufferSize)
{
strncpy_P(copyToBuffer, item.getName(), bufferSize);
copyToBuffer[bufferSize - 1] = '\0'; // Make sure the buffer is null-terminated.
return copyToBuffer;
}
#endif
/*
This is an important function
Here all use events are handled
This is where you define a behaviour for a menu item
*/
void menuUseEvent(MenuUseEvent used)
{
Serial.print(F("Menu use "));
#ifdef TEST_FROMFLASH
Serial.println(fromFlash(used.item.getName()));
#endif
#ifdef TEST_GETITEMNAMEFROMFLASH
char itemNameBuffer[31]; // 30 chars + '\0';
Serial.println(getItemNameFromFlash(used.item, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
if (used.item == setDelay) //comparison against a known item
{
Serial.println(F("menuUseEvent found Delay (D)"));
}
}
/*
This is an important function
Here we get a notification whenever the user changes the menu
That is, when the menu is navigated
*/
void menuChangeEvent(MenuChangeEvent changed)
{
Serial.print("Menu change ");
#ifdef TEST_FROMFLASH
Serial.println(fromFlash(changed.to.getName()));
#endif
#ifdef TEST_GETITEMNAMEFROMFLASH
char itemNameBuffer[31]; // 30 chars + '\0';
Serial.print(getItemNameFromFlash(changed.from, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.print(" char is ");
Serial.println(getItemNameFromFlash(changed.to, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
}
void setup()
{
Serial.begin(9600);
Serial.println(F("Revision 1.0."));
menuSetup();
Serial.println(F("Starting navigation:\r\n Down: s Left: a Right: d Use: e"));
}
void showAllNames()
{
#ifdef TEST_FROMFLASH
Serial.println(fromFlash(settings.getName()));
Serial.println(fromFlash(pin.getName()));
Serial.println(fromFlash(debug.getName()));
Serial.println(fromFlash(options.getName()));
Serial.println(fromFlash(setDelay.getName()));
Serial.println(fromFlash(d100.getName()));
Serial.println(fromFlash(d200.getName()));
Serial.println(fromFlash(d300.getName()));
Serial.println(fromFlash(d400.getName()));
#endif
#ifdef TEST_GETITEMNAMEFROMFLASH
char itemNameBuffer[31]; // 30 chars + '\0';
Serial.println(getItemNameFromFlash(settings, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(pin, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(debug, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(options, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(setDelay, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(d100, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(d200, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(d300, itemNameBuffer, sizeof(itemNameBuffer)));
Serial.println(getItemNameFromFlash(d400, itemNameBuffer, sizeof(itemNameBuffer)));
#endif
}
void loop()
{
if (Serial.available()) {
byte read = Serial.read();
switch (read) {
case 's':
menu.moveDown();
break;
case 'd':
menu.moveRight();
break;
case 'a':
menu.moveLeft();
break;
case 'e':
menu.use();
break;
case 'z':
showAllNames();
break;
}
}
}
MenuBackend.h
/*
||
|| @file MenuBackend.h
|| @версия 1.4
|| @автор Александр Бревиг
|| @contact [email protected]
|| @contribution Адриан Бжезинский [email protected], http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?action=viewprofile;username=vzhang
||
|| @description
|| | Предоставить простой способ создания меню
|| #
||
|| @лицензия
|| | Эта библиотека является свободным программным обеспечением; вы можете распространять ее и/или
|| | изменить его в соответствии с условиями GNU Lesser General Public
|| | Лицензия, опубликованная Free Software Foundation; версия
|| | 2.1 Лицензии.
|| |
|| | Эта библиотека распространяется в надежде, что она будет полезна,
|| | но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемой гарантии
|| | ТОВАРНАЯ ПРИГОДНОСТЬ или ПРИГОДНОСТЬ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ. См. GNU
|| | Более подробная информация в Стандартной общественной лицензии ограниченного использования.
|| |
|| | Вы должны были получить копию GNU Lesser General Public
|| | Лицензия вместе с этой библиотекой; если нет, напишите в Free Software
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, США
|| #
||
*/
#ifndef MenuBackend_h
#define MenuBackend_h
#include <avr/pgmspace.h>
/*
Пункт меню будет контейнером для пункта, который является частью меню.
Каждый такой элемент имеет логическое положение в иерархии, а также текст и, возможно, мнемоническую комбинацию клавиш.
*/
class MenuItem {
public:
MenuItem(const char * itemName, char shortKey='\0' ) : name(itemName), shortkey(shortKey) {
before = right = after = left = 0;
}
//void use(){} //update some internal data / statistics
inline const char * getName() const { return name; }
inline const char getShortkey() const { return shortkey; }
inline const bool hasShortkey() const { return (shortkey!='\0'); }
inline void setBack(MenuItem *b) { back = b; }
inline MenuItem* getBack() const { return back; }
inline MenuItem* getBefore() const { return before; }
inline MenuItem* getRight() const { return right; }
inline MenuItem* getAfter() const { return after; }
inline MenuItem* getLeft() const { return left; }
MenuItem *moveBack() { return back; }
MenuItem *moveUp() {
if (before) { before->back = this; }
return before;
}
MenuItem *moveDown() {
if (after) { after->back = this; }
return after;
}
MenuItem *moveLeft() {
if (left) { left->back = this; }
return left;
}
MenuItem *moveRight() {
if (right) { right->back = this; }
return right;
}
//default vertical menu
MenuItem &add(MenuItem &mi) { return addAfter(mi); }
MenuItem &addBefore(MenuItem &mi) {
mi.after = this;
before = &mi;
if ( !mi.back ) mi.back = back;
return mi;
}
MenuItem &addRight(MenuItem &mi) {
mi.left = this;
right = &mi;
if ( !mi.back ) mi.back = back;
return mi;
}
MenuItem &addAfter(MenuItem &mi) {
mi.before = this;
after = &mi;
if ( !mi.back ) mi.back = back;
return mi;
}
MenuItem &addLeft(MenuItem &mi) {
mi.right = this;
left = &mi;
if ( !mi.back ) mi.back = back;
return mi;
}
protected:
const char * name;
const char shortkey;
MenuItem *before;
MenuItem *right;
MenuItem *after;
MenuItem *left;
MenuItem *back;
};
//no dependant inclusion of string or cstring
bool menuTestStrings1(const char *a, const char *b) { // a is in flash, b is in SRAM.
for (uint8_t da = pgm_read_byte_near(a); da; da = pgm_read_byte_near(++a), b++)
{
if (da != *b)
return false;
}
return (*b == '\0'); // We reached the end of a. Strings are equal only if we reached the end of b too.
}
bool menuTestStrings2(const char *a, const char *b) { // a and b are both in flash.
uint8_t da = pgm_read_byte_near(a); // Dereferenced a.
uint8_t db = pgm_read_byte_near(b); // Dereferenced b.
for ( ; da; da = pgm_read_byte_near(++a), db = pgm_read_byte_near(++b))
{
if (da != db)
return false;
}
return (db == '\0'); // We reached the end of a. Strings are equal only if we reached the end of b too.
}
bool operator==(MenuItem &lhs, char* test) {
return menuTestStrings1(lhs.getName(),test);
}
bool operator==(const MenuItem &lhs, char* test) {
return menuTestStrings1(lhs.getName(),test);
}
bool operator==(MenuItem &lhs, MenuItem &rhs) {
return menuTestStrings2(lhs.getName(),rhs.getName());
}
bool operator==(const MenuItem &lhs, MenuItem &rhs) {
return menuTestStrings2(lhs.getName(),rhs.getName());
}
struct MenuChangeEvent {
const MenuItem &from;
const MenuItem &to;
};
struct MenuUseEvent {
const MenuItem &item;
};
typedef void (*cb_change)(MenuChangeEvent);
typedef void (*cb_use)(MenuUseEvent);
const char itemNameMenuRoot[] PROGMEM = "MenuRoot";
class MenuBackend {
public:
MenuBackend(cb_use menuUse, cb_change menuChange = 0) : root(itemNameMenuRoot) {
current = &root;
cb_menuChange = menuChange;
cb_menuUse = menuUse;
}
MenuItem &getRoot() {
return root;
}
MenuItem &getCurrent() {
return *current;
}
void moveBack() {
setCurrent(current->getBack());
}
void moveUp() {
setCurrent(current->moveUp());
}
void moveDown() {
setCurrent(current->moveDown());
}
void moveLeft() {
setCurrent(current->moveLeft());
}
void moveRight() {
setCurrent(current->moveRight());
}
void use(char shortkey)
{
recursiveSearch(shortkey,&root);
use();
}
void use() {
//current->use();
if (cb_menuUse) {
MenuUseEvent mue = { *current };
cb_menuUse(mue);
}
}
private:
void setCurrent( MenuItem *next ) {
if (next) {
if (cb_menuChange) {
MenuChangeEvent mce = { *current, *next };
(*cb_menuChange)(mce);
}
current = next;
}
}
void foundShortkeyItem(MenuItem *mi) {
mi->setBack(current);
current = mi;
}
char canSearch(const char shortkey, MenuItem *m) {
if (m==0) { return 0; }
else {
if (m->getShortkey()==shortkey) {
foundShortkeyItem(m);
return 1;
}
return -1;
}
}
void rSAfter(const char shortkey, MenuItem *m) {
if (canSearch(shortkey,m)!=1) {
rSAfter(shortkey, m->getAfter());
rSRight(shortkey, m->getRight());
rSLeft(shortkey, m->getLeft());
}
}
void rSRight(const char shortkey, MenuItem *m) {
if (canSearch(shortkey,m)!=1) {
rSAfter(shortkey, m->getAfter());
rSRight(shortkey, m->getRight());
rSBefore(shortkey, m->getBefore());
}
}
void rSLeft(const char shortkey, MenuItem *m) {
if (canSearch(shortkey,m)!=1) {
rSAfter(shortkey, m->getAfter());
rSLeft(shortkey, m->getLeft());
rSBefore(shortkey, m->getBefore());
}
}
void rSBefore(const char shortkey, MenuItem *m) {
if (canSearch(shortkey,m)!=1) {
rSRight(shortkey, m->getRight());
rSLeft(shortkey, m->getLeft());
rSBefore(shortkey, m->getBefore());
}
}
void recursiveSearch(const char shortkey, MenuItem *m) {
if (canSearch(shortkey,m)!=1) {
rSAfter(shortkey, m->getAfter());
rSRight(shortkey, m->getRight());
rSLeft(shortkey, m->getLeft());
rSBefore(shortkey, m->getBefore());
}
}
MenuItem root;
MenuItem *current;
cb_change cb_menuChange;
cb_use cb_menuUse;
};
#endif
@Calum Nicoll, 👍0
1 ответ
Лучший ответ:
У вас есть глобальная переменная menu
, которая имеет тип MenuBackend
, который содержит структуру вашей системы меню. Одна из функций-членов MenuBackend
— это MenuItem getCurrent()
, которая возвращает ссылку на текущее активное меню.
Если вы посмотрите на определение класса MenuItem
, то увидите, что среди открытых функций-членов есть:
inline MenuItem* getBack() const { return back; }
inline MenuItem* getBefore() const { return before; }
inline MenuItem* getRight() const { return right; }
inline MenuItem* getAfter() const { return after; }
inline MenuItem* getLeft() const { return left; }
Поскольку menu
является глобальным, в любом месте вашей программы вы должны иметь возможность использовать:
menu.getCurrent().getAfter();
для доступа к MenuItem
после текущего. Аналогично с помощью просто menu.getCurrent()
.
РЕДАКТИРОВАТЬ:
Я добавляю следующие строки в вашу функцию showAllNames()
, просто для проверки:
Serial.println(fromFlash(menu.getCurrent().getName()));
Serial.println(fromFlash(menu.getCurrent().getAfter()->getName()));
И, кажется, все работало нормально.
- Почему я не могу распечатать значения логических переменных (0 и 1) с помощью функции println?
- Печать массива с помощью функции печати и последовательной записи в Arduino Uno
- Arduino Serial.ReadString() проблема
- Int64_t, он же long long, действует как int32_t и переполняется на 2^31.
- Печать доступна с помощью U8GLib
- Serial.print печатает, а lcd.print - нет.
- Создание строк с символами UTF-8 из данных
- Как прочитать и Serial.print содержимое (текущие значения) 8-битного регистра?
Отлично! Я попробовал то, что вы предложили, но понял, что в моем приложении отображается тот, что следует за тем, который я только что изменил. Поэтому я использовал changed.to.getAfter()->getName()), и это работает блестяще! Большое спасибо за вашу помощь, вы определенно сделали мой день!, @Calum Nicoll