Как распечатать/отобразить следующий пункт меню с помощью menubackend.h?

print

Я пытаюсь сделать навигационное меню с подменю для отображения на 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

, 👍0


1 ответ


Лучший ответ:

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()));

И, кажется, все работало нормально.

,

Отлично! Я попробовал то, что вы предложили, но понял, что в моем приложении отображается тот, что следует за тем, который я только что изменил. Поэтому я использовал changed.to.getAfter()->getName()), и это работает блестяще! Большое спасибо за вашу помощь, вы определенно сделали мой день!, @Calum Nicoll