gui_window(); // boring
virtual ~gui_window(); // boring
virtual void init(void); // boring
gui_window *getparent(void) { return(m_pParent); }
/////////////
// section I: window management controls
/////////////
int addwindow(gui_window *w);
int removewindow(gui_window *w);
void show(void) { m_bIsShown = true; }
void hide(void) { m_bIsShown = false; }
bool isshown(void) { return(m_bIsShown); }
void bringtotop(void);
bool isactive(void);
/////////////
// Section II: coordinates
/////////////
void setpos(coord x1, coord y1); // boring
void setsize(coord width, coord height); // boring
void screentoclient(coord& x, coord& y);
int virtxtopixels(coord virtx); // convert GUI units to actual pixels
int virtytopixels(coord virty); // ditto
virtual gui_window *findchildatcoord(coord x, coord y, int flags = 0);
/////////////
// Section III: Drawing Code
/////////////
// renders this window + all children recursively
int renderall(coord x, coord y, int drawme = 1);
gui_wincolor& getcurrentcolorset(void) { return(isactive() ? m_activecolors : m_inactivecolors); }
/////////////
// Messaging stuff to be discussed in later Parts
/////////////
int calcall(void);
virtual int wm_paint(coord x, coord y);
virtual int wm_rendermouse(coord x, coord y);
virtual int wm_lbuttondown(coord x, coord y);
virtual int wm_lbuttonup(coord x, coord y);
virtual int wm_ldrag(coord x, coord y);
virtual int wm_lclick(coord x, coord y);
virtual int wm_keydown(int key);
virtual int wm_command(gui_window *win, int cmd, int param) { return(0); };
virtual int wm_cansize(coord x, coord y);
virtual int wm_size(coord x, coord y, int cansize);
virtual int wm_sizechanged(void) { return(0); }
virtual int wm_update(int msdelta) { return(0); }
protected:
virtual void copy(gui_window& r); // deep copies one window to another
gui_window *m_pParent;
uti_pointerarray m_subwins;
uti_rectangle m_position;
// active and inactive colorsets
gui_wincolor m_activecolor;
gui_wincolor m_inactivecolor;
// window caption
uti_string m_caption;
};
First of all, notice the virtual destructor on the window class. This may not seem like it’s needed just yet, but we’ll eventually be deriving controls from this class, so it’s important that it have a virtual destructor.
As you peruse the functions we’ll be talking about, keep in mind that recursion is everywhere. For example, our game will be drawing the entire GUI system by making a call to the renderall() method of the root window, which will in turn call the renderall() methods of its subwindows, which will call renderall() for their subwindows, and so on. Most of the functions follow this recursive pattern.
The whole GUI system will be contained within one global static variable - the root window. To be on the safe side, I encapsulated this variable within a global GetDesktop() function.
Also, notice that the class definition is rife with virtual keywords. This is where C++’s polymorphism is working for us. Need to change how certain types of windows (or controls - say, buttons) deal with a “left mouse button has just been pushed down” event? Simple, derive a class from the base window and override its wm_lbuttondown() method. The system will automatically call the derived class’s method where appropriate; behold the power of C++.
Now that we’ve got the header, let’s start filling in some functions, starting with the Window Management code…
Window Management Code
/****************************************************************************
addwindow: adds a window to this window's subwin array
****************************************************************************/
int gui_window::addwindow(gui_window *w) {
if (!w) return(-1);
// only add it if it isn't already in our window list.
if (m_subwins.find(w) == -1) m_subwins.add(w);
w-›setparent(this);
return(0);
}
/****************************************************************************
removewindow: removes a window from this window's subwin array
****************************************************************************/
int gui_window::removewindow(gui_window *w) {
w-›setparent(NULL);
return (m_subwins.findandremove(w));
}
/****************************************************************************
bringtotop: bring this window to the top of the z-order. the top of the
z-order is the HIGHEST index in the subwin array.
****************************************************************************/
void gui_window::bringtotop(void) {
if (m_parent) {
// we gotta save the old parent so we know who to add back to
gui_window *p = m_parent;
p-›removewindow(this);
p-›addwindow(this);
}
}
/****************************************************************************
isactive: returns true if this window is the active one (the one with input focus).
****************************************************************************/
bool gui_window::isactive(void) {
if (!m_parent) return(1);
if (!m_parent-›isactive()) return(0);
return(this == m_parent-›m_subwins.getat(m_parent-›m_subwins.getsize()-1));
}
This set of functions deals with what I call window management; adding windows, deleting them, showing/hiding them, and changing their z-order. All of these are really just array operations; this is where your array class gets a workout.
The only thing interesting in the add / remove window functions is the question, “who is responsible for the window pointer?” This is always a good question to ask yourself in C++. Addwindow and removewindow both take pointers to a window class. This means that to create a new window, your code news it, then passes the pointer to the parent (desktop) window through addwindow(). So who’s responsible for deleting the pointer you newed?