Rendering a menu in arduino-menusystem version 2.1.1 isn't dictated by the API. For example, in the current_menu.ino example the menu is rendered in loop():

void loop()
{
  Serial.println("");
  
  // Display the menu
  Menu const* cp_menu = ms.get_current_menu();
  MenuComponent const* cp_menu_sel = cp_menu->get_selected();
  for (int i = 0; i < cp_menu->get_num_menu_components(); ++i)
  {
    MenuComponent const* cp_m_comp = cp_menu->get_menu_component(i);
    Serial.print(cp_m_comp->get_name());
    
    if (cp_menu_sel == cp_m_comp)
      Serial.print("<<< ");
    
    Serial.println("");
  }
  
  // Code ommitted
}

There are two problems with this approach:

  1. Complex decision blocks would be needed to render each MenuComponent type differently, resulting in code that's difficult to read and maintain;
  2. It's not obvious how the menu system should be rendered; the API provides no guideance.

At first glance a reasonable solution would be to add a pure virtual render method to the MenuComponent and override it in each subclass. This provides a clear interface to rendering and removes complex decision blocks for each type.

class MenuItem : public MenuComponent {
public:
    void render() const {
        Serial.println(_name);
    }
};

void loop() {
  Menu const* cp_menu = ms.get_current_menu();
  MenuComponent const* cp_menu_sel = cp_menu->get_selected();
  for (int i = 0; i < cp_menu->get_num_menu_components(); ++i)
  {
    MenuComponent const* cp_m_comp = cp_menu->get_menu_component(i);
    cp_m_comp->render();
    
    if (cp_menu_sel == cp_m_comp)
      Serial.print("<<< ");
    
    Serial.println("");
  }
}

It doesn't take long to see the problems with this approach:

  1. The MenuComponent is tightly coupled to the display hardware because it can't render without it (Serial in the case above);
  2. The MenuComponent violates the single responsibility principle: it's responsible for describing the menu system structure and for rendering that structure.

A better approach is to factor out the responsibility of rendering into its own class. In version 3.0.0 this is achieved using the visitor design pattern.

The visitor design pattern is a way of separating an algorithm from an object structure on which it operates.

The MenuComponentRenderer abstract base class decouples the rendering from the menu structure. The interface is composed of render methods for each concrete MenuComponent type.

The client provides a custom renderer which is passed as the only argument to the MenuSystem constructor. When MenuSystem::display() is called, the render method for the current Menu is called.

Depending on what the client wants to display, the render method for the Menu component should call the render method on one or more concrete MenuComponent instances in the menu, passing it a reference to the renderer. The render method on each concrete MenuComponent calls the appropriate render method in the renderer, passing a reference to itself.

The current_item.ino is a succinct example of a renderer:

#include <MenuSystem.h>

// Renderer

class MyRenderer : public MenuComponentRenderer {
public:
    void render(Menu const& menu) const {
        menu.get_current_component()->render(*this);
    }

    void render_menu_item(MenuItem const& menu_item) const {
        Serial.println(menu_item.get_name());
    }

    void render_back_menu_item(BackMenuItem const& menu_item) const {
        Serial.println(menu_item.get_name());
    }

    void render_numeric_menu_item(NumericMenuItem const& menu_item) const {
        Serial.println(menu_item.get_name());
    }

    void render_menu(Menu const& menu) const {
        Serial.println(menu.get_name());
    }
};

Each concrete MenuItem type is rendered in the same way: by printing its name to the Serial console; a Menu is rendered by rendering its current component.

Using the visitor pattern makes it much easier to reason about how the menu system is displayed and results in code that's much easier to understand and maintain.