1
\$\begingroup\$

I am currently learning how to use PDcurses in VC++ 2015 Community, and I noticed that menu.h is not implemented so I decided to extend the library for my own use (feel free to just copy the code if it's what you were looking for).

The only question I have is: was my use of namespaces (both named and unnamed) correct? And is this a good approach as I do not see a reason to make a singleton or something involving classes?

NOTE: I did research the web about dividing functions into namespaces like I did in the code, but I can't find if it's the correct approach for this specific task.

menu.h

#ifndef MENU_H
#define MENU_H

#include "curses.h"

namespace menu
{
    int renderMenu(WINDOW* menu_win, const char* title, const char* items[], int n_items);
    WINDOW* menuWin(const char* title, const char* items[], int n_items, int y, int x);
    void initMenu();
}

#endif  

menu.cpp

#include "menu.h"
#include <string>

namespace
{
    void renderItems(WINDOW* win, int selected, const char* items[], int n_items)
    {
        for (int index{}; index < n_items; ++index) {
            if (index == selected) {
                wattron(win, A_BOLD);
                mvwprintw(win, 3 + index, 2, items[index]);
                wattroff(win, A_BOLD);
            }
            else 
                mvwprintw(win, 3 + index, 2, items[index]);
        }

        wrefresh(win);
    }

    int navigateMenu(WINDOW* win, const char* items[], int n_items)
    {
        int selected{};
        renderItems(win, selected, items, n_items);

        keypad(win, TRUE);
        while (true) {
            switch (wgetch(win)) {
              case KEY_DOWN:
                    if (selected < n_items - 1)
                        ++selected;
                    else
                        selected = 0;
                  break;
              case KEY_UP:
                    if (selected > 0)
                        --selected;
                    else
                        selected = n_items - 1;
                  break;
                case 10: /*enter key*/
                    return selected;
                    break;
            }

            renderItems(win, selected, items, n_items);
        }
    }
}

int menu::renderMenu(WINDOW* menu_win, const char* title, const char* items[], int n_items)
{
    std::string separator{ '+' + std::string(getmaxx(menu_win) - 2, '-') + '+' };
    mvwprintw(menu_win, 1, (getmaxx(menu_win) - strlen(title)) / 2, title); 
    wborder(menu_win, '|', '|', '-', '-', '+', '+', '+', '+');
    mvwprintw(menu_win, 2, 0, separator.c_str());
    wrefresh(menu_win);

    return navigateMenu(menu_win, items, n_items);
}

WINDOW * menu::menuWin(const char* title, const char* items[], int n_items, int y, int x)
{
    int width{};
    for (int index{}; index < n_items; ++index) 
        if (width < static_cast<int>(strlen(items[index])))
            width = strlen(items[index]);
    if (width < static_cast<int>(strlen(title)))
        width = strlen(title);
    width += 4;

    int height{ 4 + n_items };

    return newwin(height, width, y, x);
}

void menu::initMenu()
{
    curs_set(FALSE);
    noecho();
    cbreak();
    refresh();
}

source.cpp <--example of use

#include "menu.h"
#include <string>

int main()
{
    const char* items[]{
        {"option a"},
        {"option b"},
        {"option c"}
    };

    const char* title{ "Main Menu" };

    initscr();
    menu::initMenu();

    WINDOW* menu = menu::menuWin(title, items, 3, 2, 2);
    int choice = menu::renderMenu(menu, title, items, 3);
    /*use choice to preform an action*/

    mvprintw(0, 0, "%s was chosen.", items[choice]);
    refresh();

    getch();
    endwin();
    return 0;
}
\$\endgroup\$
1
\$\begingroup\$

An if inside the loop is usually counterproductive. Consider highlighting the choice afterwards:

    for (int index{}; index < n_items; ++index) {
        mvwprintw(win, 3 + index, 2, items[index]);
    }
    wattron(win, A_BOLD);
    mvwprintw(win, 3 + selected, 2, items[selected]);
    wattroff(win, A_BOLD);

It is very unclear what the 3 and 2 offsets stand for. Apparently they have something to do with the visual layout. Consider giving them some meaningful names.


Since you are writing in , you'd be in much better shape operating on vector<char *> rather than a raw array.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the advice! Iv'e already implemented the changes. Sorry for the long response time, my internet connection was severed for the past few days. Only one question - what about the namespaces approach ? is it the correct circumstance to use it ? \$\endgroup\$ – globalturist Oct 11 '17 at 21:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.