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 entireGUI 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 automaticallycall 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…
/****************************************************************************
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?
My answer was “the GUI doesn’t own the window pointers; the game itself is responsible for adding them.” This is consistent with the C++ rule of thumb that says “those who new things also delete them.”
The alternative to the method I chose was to say “the parent window is responsible for the pointers of all his child windows.” That would mean that to prevent memory leaks, each window must, in it’s (virtual) destructor (remember, there’s derived classes), loop through its m_subwindows array and delete all of the windows contained within it.
If you decide to implement a GUI-owns-pointer system, be aware of an important trade-off - all of your windows must be dynamically allocated (newed). A quick way to crash a system like that is to pass in the address of a variable on the stack, i.e. say something like “addwindow(&mywindow)”, where mywindow is declared as a local variable on the stack. Things will work until mywindow goes out of scope, or until the destructor for the parent window is called, whereupon it’ll try to delete that address and all hell will break loose. The lesson is “be extra careful with pointers.”
Читать дальше