I was just wondering.. What would be the best code method for creating - say - a number of menus that you can navigate around? Thanks for any info on this subject in advance. :)
Printable View
I was just wondering.. What would be the best code method for creating - say - a number of menus that you can navigate around? Thanks for any info on this subject in advance. :)
GUI or console?
GUI, please. ^^
Can you be more specific on what type of libraries you're using to create this?
Ah, I purposely didn't specify. I'm just talking about theoretically, if you're making any sort of program that has menu's in it, how would you go about doing it in the most efficient way? I hope I clearified that enough. >_<
I'm not really looking for "how would you do it with _____ library, etc... " I can try and figure that part out myself. ;)
Wellll, if you really don't want to code anything much but have a GUI fast, use the Win32 API, its GUI heaven..
Other than that, I don't know much about GUI work.
Hmmm, it's a good thought, but I wasn't really intending to use "Windows API." :P
Never intended for it to be made fast. XD
I thought this would be an opertune time to demonstrate how a menu system can get out of control. The concept I had going here was very solid, but through coding I ended up adding a lot of functionality "hacks" that made the system kinda combursom.
EVENTS: drive everything , including the UI so, heres the class header
UI: Header fileCode:////////////////////////////////////
// event.h
// game processes class file
// all entities that require some sort of
// processing or handling inherit the event object
#ifndef PR_EVENT
#define PR_EVENT
#include "global.h"
// Objects that need to be managed will
// inherit this as base class
class CEventNode {
friend class CEventList;
public:
CEventNode();
~CEventNode();
void setThread(CThread *thread);
virtual bool process(CEventList *owner) = 0; // if a process returns true it was handled and should be removed
// else it remains in teh que for the next game tick
protected:
__int64 spawn; // onlything important relavent to all processes is when it was spawned
// anything else is defined in subclasses
static long int instanceCount;
long int id; // event id - I have no idea of the potential ammount of events in a single game long should be fine
CEventNode * next; // points to next event to be processed
CEventNode * prev; // points to prev event (easier removal of nodes)
CThread *thread; // thread spawning
};
class ListCleanUp : public CEventNode { // a node that on process tells the managing list it's empty
public:
ListCleanUp(bool fullclean){ this->fullclean = fullclean; };
~ListCleanUp() {};
bool process(CEventList *owner);
private:
bool fullclean;
};
// Special CEventNode() that handles setting or removing a CThread
// depreciated ?
class CEventNodeThreader : public CEventNode {
public:
CEventNodeThreader();
~CEventNodeThreader();
bool process(CEventList *owner);
private:
CThread *thread;
};
// event/object managers will
// inherit this as base class
class CEventList {
public:
CEventList();
~CEventList();
public:
void add( CEventNode *e );
void empty(bool really);
void ignore();
void remove( long int id );
void setOwner(CThread *owner);
void spawnThread(CThread *thread);
int size();
bool handle();
private:
CThread *owner;
CEventNode *head;
CEventNode *tail;
bool ignoreEvents;
};
// not the traditional thread in multithreading
// but more along teh lines of a guide line strand.... right.
class CThread {
public:
CThread();
~CThread();
bool Run();
void setProcedure(CThread *thread); // sets a procedure
void removeProcedure();
protected:
CThread * pActiveProcedure;
CEventList list;
public:
char * name;
};
#else
#endif
[code]
///////////////////////////
// UI.h
// user interface header file
#ifndef PR_UI
#define PR_UI
#include "global.h"
#include "font.h"
// The functionality of the User Interface is such that
// at runtime the main windows thread spawns our top level
// menu. This top level menu is a UIMain object, that spawns it's own thread
// at this point the main window loop is waiting for this thread to return(finish)
// The menu is a running thread with a list of events that need to be processed before
// it can return to the main window loop and thus exit the application.
// the list contains UIMenuItem objects that inherit EventNode, the list is cycled through
// and the menu item performs as defined, some items might actually call for a new UIMenu
// to spawn create a sub menu - at this point the winmain thread is waiting for the Main menu,
// witch is now waiting on the submenu, the submenu then has it's own event list that needs to
// be processed before it returns. Generally an Exit menu item will be the trigger to end the
// event list and collapse back into teh calling thread. A UIMenuItem will also be capable of
// spawning a GameThreadEvent witch up can return to the calling menu several methods - manual
// escape, winning or losing - etc.
class UIMenuItem;
class UIMain : public CThread {
public:
UIMain();
~UIMain();
void addItem(UIMenuItem *item);
// thread process info
private:
// uhhhhhhh stuff
};
// UIMenuItem is an abstract interface for menu items
// that range from strings, to media items
// menu items actually decide if they are being
// interacted with by the user rather then the menu
// manager class doing so.
class UIMenuItem : public CEventNode {
public:
UIMenuItem();
~UIMenuItem();
// rather then obtain this global information manualy in each function,
// a interface method is being implimented in case at some future point
// i want this info obtain through strict process by authorized class managers
// this saves time for redoing the process in teh long run.
sKey getKey(int key); // checks global key structure for key activity
sMouse getMouse(); // grab mouse info from global structure
void setColor(float r, float g, float b);
void setPos(int x, int y);
void setSpawnEvent(CEventNode *e);
bool hotSpot(int textx, int testy);
protected:
// uhhh other stuff
CEventNode * spawn; // an event that spawns as a result of this thing being handled
int x, y; // screen position;
int width, height;
sRGB color;
};
// UIItemStaticText
// simple text lables
class UIItemStaticText : public UIMenuItem {
public:
UIItemStaticText();
~UIItemStaticText();
bool process(CEventList *owner);
public:
void setFont(char *name, int size);
void setString(char *string);
protected:
char *string;
GLFONT *pFont;
};
// UIItemTextOption
// text item that interacts with user
class UIItemTextOption : public UIItemStaticText {
public:
UIItemTextOption();
~UIItemTextOption();
bool process(CEventList *owner);
void setInteractColors(sRGB base, sRGB hightlight, sRGB down);
private:
sRGB highlightColor;
sRGB downColor;
};
#else
#endif
[/quote]
UI: function definitions
And actually using the UI in the Initialize()Code:#include "global.h"
UIMain::UIMain() {};
UIMain::~UIMain() {};
void UIMain::addItem(UIMenuItem *item) {
list.add(item);
};
// thread process info
UIMenuItem::UIMenuItem() {
this->spawn=0; // nothing by default
};
void UIMenuItem::setSpawnEvent(CEventNode *e) {
this->spawn = e; // set an event to be triggered.
};
UIMenuItem::~UIMenuItem() { };
sKey UIMenuItem::getKey(int key) {
// this needs protection data entry in place
// actually at this point its only read data
// should be protected when writing
return keys[key];
};
sMouse UIMenuItem::getMouse() {
// read data
return mouse; // this needs protection data entry in place
};
// set render position top left of an item
void UIMenuItem::setPos(int x, int y) {
this->x = x;
this->y = y;
};
// set the color option
void UIMenuItem::setColor(float r, float g, float b) {
this->color.r = r;
this->color.g = g;
this->color.b = b;
};
// test a pixel position against rectangle hotspot
bool UIMenuItem::hotSpot(int testx, int testy) {
if(testx > x && testx < x+width)
if(testy > y && testy < y+height)
return true;
return false;
};
// constructor for static text items
// defaults to arial, size 14, "Menu Item" color black
UIItemStaticText::UIItemStaticText() {
string = "Menu Item";
this->pFont = new GLFONT("Arial", 14);
this->height = 14;
this->x = 20;
this->y = 20;
sRGB c = { 0, 0, 0 };
this->color = c;
this->width = pFont->stringWidth(string); // calculate size of default
};
UIItemStaticText::~UIItemStaticText() {
};
// Static text is kind of a "permanent" event.
// such that it is never handled and removed from the list
// for this reason in order to exit from any thread that
// contains a permanent event, a special exit event must
// be spawned and processed in order to tell the thread to
// forcefully empty it's event list and return true;
bool UIItemStaticText::process(CEventList *owner) {
glColor3f(color.r, color.g, color.b);
pFont->display(x, y, string);
return false;
};
// set the font and size of static text item
void UIItemStaticText::setFont(char *name, int size) {
this->pFont = new GLFONT(name, size);
this->height = size;
this->width = pFont->stringWidth(string); // calculate size of new font
};
// set teh string, of text.
void UIItemStaticText::setString(char* string) {
this->string = string;
this->width = pFont->stringWidth(string); // calculate size of new string
};
// set default highlight and down colors
// initialize default string and font
UIItemTextOption::UIItemTextOption() {
sRGB h = { 1.0f, 0.0f, 0.0f }; // default highlight color is red
this->highlightColor = h;
sRGB d = { 1.0f, 1.0f, 0.0f };
this->downColor = d;
};
UIItemTextOption::~UIItemTextOption() {
};
// set colors for the text options, unfocused, focused and down
void UIItemTextOption::setInteractColors(sRGB base, sRGB highlight, sRGB down) {
this->highlightColor = highlight;
this->downColor = down;
this->color = base;
};
bool UIItemTextOption::process(CEventList *owner) {
sMouse mpos = getMouse();
font->display(this->x - 30, this->y, "width: %i", width);
font->display(this->x + width, this->y, "|<- end|");
font->display(10.0f, 30.0f, "MB: %i, Mx: %i, My: %i, RECT(%i, %i, %i, %i)", mpos.button, mpos.x, mpos.y, this->x, this->y, this->x+this->width, this->y+this->height);
if(this->hotSpot(mpos.x, mpos.y+this->height)) { // option have focus?
glColor3f(this->highlightColor.r, this->highlightColor.g, this->highlightColor.b);
if(!this->hotSpot(mpos.prevx, mpos.prevy)) // fist time having focus?
{
//ADDME: play mouse over sound
}
if(mpos.button == 1) {
glColor3f(this->downColor.r, this->downColor.g, this->downColor.b);
//ADDME: play mouse over sound
//ADDME: spawn some event
if(this->thread != 0) // if there is a thread to spawn
owner->spawnThread(this->thread); // tell the list to spawn this thread
if(this->spawn != 0) // if there is an event to spawn
owner->add(this->spawn); // tell the list to spawn this event
pFont->display(x, y, string);
return false;
}
pFont->display(x, y, string);
} else { // option does not have focus
glColor3f(color.r, color.g, color.b);
pFont->display(x, y, string);
}
return false;
};
Code:// menu / process testing
MainMenu = new UIMain();
MainMenu->name = "Main Menu";
// make a menu item
// set properties
UIItemStaticText *item = new UIItemStaticText();
item->setString("Hyperspace 2005");
item->setFont("Times New Roman", 20);
item->setColor(0.25f, 0.50f, 0.75f);
item->setPos(g_winWidth/2, g_winHeight/2);
// add it to the menu
MainMenu->addItem(item);
UIItemTextOption *item2 = new UIItemTextOption();
item2->setString("Start A New Game");
//item2->setInteractColors();
item2->setFont("Verdana", 20);
item2->setPos(g_winWidth/2, (g_winHeight/2)+22);
// add it to teh menu
MainMenu->addItem(item2);
item2 = new UIItemTextOption();
item2->setString("Another menu item.");
item2->setFont("Arial", 20);
item2->setPos(g_winWidth/2, (g_winHeight/2)+44);
MainMenu->addItem(item2);
UIItemTextOption *exit = new UIItemTextOption();
exit->setString("Exit");
exit->setFont("Arial", 18);
exit->setColor(0.01f, 0.01f, 0.8f);
exit->setPos(g_winWidth/2, (g_winHeight/2)+66);
exit->setSpawnEvent(new ListCleanUp(1));
MainMenu->addItem(exit);
// Sub menu test
UIMain *subMenu = new UIMain();
subMenu->name = "Sub Menu";
UIItemStaticText *subitem = new UIItemStaticText();
subitem->setString("This is a submenu!");
subitem->setFont("Times New Roman", 18);
subitem->setPos(g_winWidth/2, (g_winHeight/2));
subMenu->addItem(subitem);
UIItemTextOption *subitem2 = new UIItemTextOption();
subitem2->setString("Exit");
subitem2->setColor(0.3f, 0.9f, 0.4f);
subitem2->setPos(g_winWidth/2, (g_winHeight/2)+22);
subitem2->setFont("Sans Sarif", 20);
subitem2->setSpawnEvent(new ListCleanUp(0));
subMenu->addItem(subitem2);
// assign sub menu, to a button in main menu
item2->setThread(subMenu);
// officially associate the main menu as the primary game thread
GameThread = MainMenu; // set the game thread pointer to teh MAIN Menu
OH FYI: This UI is used in the program in this thread: http://cboard.cprogramming.com/game-programming/70368-space-pong-debug-release.html
Well why didn't you specify this in the original post?Quote:
Originally Posted by SSJMetroid
My bad. ^^;;;
[Edit:]
Btw: Thanks for that UI example, Jeremy. That's sorta the kind of example I needed. You rock, dude. :)
Windows is GUI hell, not GUI heaven.