#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
#include <Windows.h>
// Console window's dimensions.
#define CONSOLE_WINDOW_HEIGHT 50
#define CONSOLE_WINDOW_WIDTH 80
const short MAX = 7;
// Properties of each menu string.
// id = identifier for menu string we are referring to.
// coord = screen coordinates where the menu string is drawn.
// color = simulates a highlighted menu string by changing the
// foreground over background color of menu string.
// clickable = define this menu string as "selectable".
// selected = Optional. is this menu string currently selected?
typedef struct
{
short id;
COORD coord;
WORD color;
std::string str;
bool clickable;
bool selected;
} TEXTMENU;
// Vector that will hold each menu string.
typedef std::vector<TEXTMENU> VectorMenu;
// Constant iterator.
typedef std::vector<TEXTMENU>::const_iterator VectorMenuConstIter;
// Windows routine for std in/out.
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
// Stores keyboard/mouse events.
INPUT_RECORD irEvent;
// Total number of input events read.
DWORD dwNumEventsRead;
bool mKeyPressed;
// Ascii code of the pressed key.
char wKeyCode;
// This variables is the replacement for TEXTMENU's selected.
// It is used to keep track which menu string is currently
// selected/highlighted.
short item = -1;
VectorMenu * menu = new VectorMenu();
VectorMenuConstIter cit;
// Colors we want for the menu strings.
WORD WHITE_ON_BLACK = 0x0F;
WORD BLACK_ON_GRAY = 0x70;
// Windows specific console initialization routine.
// This will just initialize the console the way we want it.
void init()
{
if(hout == INVALID_HANDLE_VALUE || hin == INVALID_HANDLE_VALUE)
exit(EXIT_FAILURE);
COORD screen = { CONSOLE_WINDOW_WIDTH, CONSOLE_WINDOW_HEIGHT };
if(!SetConsoleScreenBufferSize(hout, screen))
exit(EXIT_FAILURE);
SMALL_RECT rect = { 0, 0, CONSOLE_WINDOW_WIDTH - 1, CONSOLE_WINDOW_HEIGHT - 1 };
if(!SetConsoleWindowInfo(hout, TRUE, &rect))
exit(EXIT_FAILURE);
CONSOLE_CURSOR_INFO info = { 1, false };
if(!SetConsoleCursorInfo(hout, &info))
exit(EXIT_FAILURE);
RECT r;
HWND consolewin = GetConsoleWindow();
if(consolewin != NULL)
{
GetWindowRect(consolewin, &r);
int winx = r.right - r.left;
int winy = r.bottom - r.top;
int screenx = GetSystemMetrics(SM_CXSCREEN);
int screeny = GetSystemMetrics(SM_CYSCREEN);
int posx = (screenx / 2) - (winx / 2);
int posy = (screeny / 2) - (winy / 2);
if(!SetWindowPos(consolewin, NULL, posx, posy, screenx, screeny, SWP_NOSIZE))
exit(EXIT_FAILURE);
}
}
// Polls the keyboard for key press.
void pollkeyboard()
{
WaitForSingleObject(hin, INFINITE);
/* ReadConsoleInput blocks, so peek for input character first. */
if (PeekConsoleInput(hin, &irEvent, 1, &dwNumEventsRead))
{
/* Listen for keyboard events. */
if((irEvent.EventType == KEY_EVENT) && (irEvent.Event.KeyEvent.bKeyDown))
{
if(!ReadConsoleInput(hin, &irEvent, 1, &dwNumEventsRead))
{
std::cout << "Error reading input.\n";
}
mKeyPressed = true;
wKeyCode = irEvent.Event.KeyEvent.wVirtualKeyCode;
}
}
FlushConsoleInputBuffer(hin);
}
bool iskeypressed()
{
if(mKeyPressed)
{
mKeyPressed = false;
return true;
}
return false;
}
char getkeypressed()
{
return wKeyCode;
}
// Alternative to system("cls");
void clearconsole()
{
COORD coordScreen = { 0, 0 }; // home for the cursor
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
if(!GetConsoleScreenBufferInfo(hout, &csbi))
{
return;
}
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
// Fill the entire screen with blanks.
if(!FillConsoleOutputCharacter(hout, // Handle to console screen buffer
(TCHAR) ' ', // Character to write to the buffer
dwConSize, // Number of cells to write
coordScreen, // Coordinates of first cell
&cCharsWritten)) // Receive number of characters written
{
return;
}
// Get the current text attribute.
if(!GetConsoleScreenBufferInfo(hout, &csbi))
{
return;
}
// Set the buffer's attributes accordingly.
if(!FillConsoleOutputAttribute( hout, // Handle to console screen buffer
csbi.wAttributes, // Character attributes to use
dwConSize, // Number of cells to set attribute
coordScreen, // Coordinates of first cell
&cCharsWritten)) // Receive number of characters written
{
return;
}
// Put the cursor at its home coordinates.
SetConsoleCursorPosition(hout, coordScreen);
}
// Draws the menu strings from the vector.
void draw()
{
for(cit = menu->begin(); cit != menu->end(); ++cit)
{
SetConsoleTextAttribute(hout, cit->color);
SetConsoleCursorPosition(hout, cit->coord);
std::cout << cit->str;
}
}
// Overloaded draw().
// Draws a single string to the center of the console screen.
void draw(std::string s)
{
SetConsoleTextAttribute(hout, WHITE_ON_BLACK);
COORD c = { (CONSOLE_WINDOW_WIDTH / 2) - ((s.length()+12) / 2),
CONSOLE_WINDOW_HEIGHT / 2 };
SetConsoleCursorPosition(hout, c);
std::cout << s << " selected...";
}
// Find/Manipulate which string to highlight depending on the key pressed.
void selectmenu(std::string s)
{
// Initially, item == -1, meaning no menu string is currently highlighted.
if(item < 0 && s == "VK_UP")
return;
// Select the first menu string to highlight as default.
else if(item < 0 && s == "VK_DOWN")
{
menu->at(1).color = BLACK_ON_GRAY;
item = 1;
}
else if(s == "VK_UP")
{
if(item <= 1)
return;
else
{
menu->at(item).color = WHITE_ON_BLACK;
menu->at(--item).color = BLACK_ON_GRAY;
}
}
else if(s == "VK_DOWN")
{
if(item >= MAX - 1)
return;
else
{
menu->at(item).color = WHITE_ON_BLACK;
menu->at(++item).color = BLACK_ON_GRAY;
}
}
// Enter key will transition the console window to the next screen.
// eg. clear the screen then draw which menu string was selected.
else if(s == "VK_RETURN")
{
if(item < 0)
return;
if(item == MAX - 1)
exit(EXIT_SUCCESS);
if(item > 1 || item < MAX - 1)
{
clearconsole();
draw(menu->at(item).str);
return;
}
}
draw();
}
int main()
{
SHORT midx = CONSOLE_WINDOW_WIDTH / 2;
SHORT midy = CONSOLE_WINDOW_HEIGHT / 2;
SHORT locyarr[MAX] = { midy - 8, midy - 4, midy - 2, midy, midy + 2, midy + 4, midy + 6 };
std::string strarr[MAX] = { "Select Menu:", " Menu 1 ", " Menu 2 ", " Menu 3 ",
" Menu 4 ", " Menu 5 ", " Exit " };
// Create and initialize a number of menu strings.
// Then push it into the vector.
// Make the first menu string not selectable.
TEXTMENU textmenu;
for(int i = 0; i < MAX; ++i)
{
textmenu.id = i;
textmenu.coord.X = midx - strarr[i].length() / 2; textmenu.coord.Y = locyarr[i];
textmenu.color = WHITE_ON_BLACK;
textmenu.str = strarr[i];
(i == 0) ? textmenu.clickable = false : textmenu.clickable = true;
textmenu.selected = false;
menu->push_back(textmenu);
}
init();
draw(); // First time to call draw().
// Loop until Escape key is hit or "Exit" menu string is selected.
while(true)
{
pollkeyboard();
if(iskeypressed())
{
switch(getkeypressed())
{
case VK_UP : selectmenu("VK_UP"); break;
case VK_DOWN : selectmenu("VK_DOWN"); break;
case VK_RETURN : selectmenu("VK_RETURN"); break;
case VK_ESCAPE : return 0;
default : break;
}
}
}
return 0;
}