Выбрать главу

Progress bars are very similar to scroll bars; in fact, it might make sense to implement your progess bars in terms of your scroll bars. I made my progress bar separate from my scroll bars simply because I wanted the two to have drastically different appearances - your needs may be different.

Sliders and Scrollbars

Drawing a slider or a scrollbar is similar to drawing a progress bar, in that you need to express the slider’s current position as a percentage of its client rectangle, giving you the position at which to draw the “pointer” (or, for a scrollbar, the elevator). You’ll have to make some slight modifications for horizontal vs. vertical controls - I got around these by implementing a base class, gui_slider, which contained all of the common code, and all of the member variables, and then implementing two specific derivatives, gui_slider_horz and gui_slider_vert, which handled the differences in drawing and clicking logic.

As for processing mouse clicks, I opted for the easy way when I created my sliders. If a mouse click occurs in the client area of a scrollbar, I automatically scroll directly to that position. In my sliders, you can’t click in the “shaft” and move the position by a page at a time - you jump directly to where you clicked. This was a decision I made primarily because it was easy, but also because I dislike the default Windows behavior of paging.

As for the scrollbar / slider logic, you’ve got the same basic setup as a progress bar - min, max, and current positions. Unlike a progress bar, however, the user can change the current position by clicking in the control.

Now, scrollbars. I decided, for my GUI, that scrollbars are just sliders with two buttons tacked onto either side. These two buttons (the up/down or left/right arrows) move the elevator one position. This method eliminated a lot of code duplication between the pushbutton class and the scrollbars, and I would highly recommend that you take a look at doing something similar.

Now that we’ve got scrollbars, we can tackle the most complex control of them all… the listbox.

The Listbox Control

Resign yourself to this now - the listbox control is where you’re going to be spending the most time.

// represents a column in our listbox

class gui_listbox_column {

public:

 gui_listbox_column() {}

 virtual ~gui_listbox_column() {}

 virtual void draw(uti_rectangle& where);

 void setname(const char *name) { m_name = name; }

 uti_string getname(void) { return(m_name); }

 int getwidth(void) { return(m_width); }

 void setwidth(int w) { m_width = w; }

private:

 uti_string m_name;

 int m_width;

};

// an item in our listbox

class gui_listbox_item {

public:

 gui_listbox_item() { m_isselected = 0; m_indent = 0; }

 virtual ~gui_listbox_item() {}

 virtual drawitem(int colnum, uti_rectangle& where);

 void clearallcolumns(void); // boring

 void setindent(int i) { m_indent = i; }

 int getindent(void) { return(m_indent); }

 void settext(int colnum, const char *text); // boring

 uti_string gettext(int colnum = 0); // boring

 void setitemdata(UL itemdata) { m_itemdata = itemdata; }

 UL getitemdata(void) { return(m_itemdata); }

 void setselected(int s = 1) { m_isselected = s; }

 int getselected(void) { return(m_isselected); }

private:

 int m_isselected;

 int m_indent; // # of pixels to indent this item

 UL m_itemdata;

 uti_pointerarray m_coltext;

};

// the listbox itself

class gui_fancylistbox: public gui_window {

public:

 gui_fancylistbox() { m_multiselect = 0; }

 virtual ~gui_fancylistbox() { clear(); }

 int getselected(int iter = 0);

 virtual int wm_command(gui_window *win, int cmd, int param);

 virtual int wm_paint(coord x, coord y);

 virtual int wm_lbuttondown(coord x, coord y);

 gui_fancyscrollbar_horz& gethscroll(void) { return(m_hscroll); }

 gui_fancyscrollbar_vert& getvscroll(void) { return(m_vscroll); }

 virtual int wm_sizechanged(void); // the window's size has changed somehow

 gui_listbox_item *getitemat(int index); // boring

 gui_listbox_item *additem(const char *text); // boring

 int delitem(int index); // boring

 int delallitems(void); // boring

 gui_listbox_column *getcolumn(int index); // boring

 int addcolumn(const char *name, int width); // boring

 gui_listbox_column *getcolumnat(int index); // boring

 int delcolumn(int index); // boring

 int delallcolumns(void); // boring

 int clear(void); // delete columns & items

 int getnumitems(void);

 int getnumcols(void);

 void deselectall(void);

 void selectitem(int item);

 void selecttoggleitem(int item);

 void deselitem(int item);

private:

 int m_numdispobjsy;

 int m_vertgutterwidth; // # of pixels between items vertically

 gui_fancyscrollbar_horz m_hscroll;

 gui_fancyscrollbar_vert m_vscroll;

 bool m_multiselect; // is this multi-selectable?

 uti_pointerarray m_items; // array of gui_listbox_items

 uti_pointerarray m_columns; // array of gui_listbox_columns

};

The listbox is by far the most complex control you’ll make… but that’s only because it’s the most versatile. A good listbox control, capable of multiple columns, indenting, and multi-selection will prove practically indispensable in your game’s GUI. Stop and think for a moment about all the places that listboxes are used in the average game, and you’ll quickly see my point.

I tackled my listbox control by splitting it up into two separate controls: a multi-column “report-style” list control, and an icon list control, which creates a view similar to what you’d see when selecting “large icons” in an explorer window.

The icon list control was fairly easy to do. It kept track of a list of static icons (again, note the code reuse), all the same size. I divided the listbox width by the width of the icons, which gave me the number of columns available. (If it turned out that my listbox was smaller than the largest icon, I assume I have only one column, and let the gui system take care of clipping the icons so that they don’t overrun my client area). Once I had the number of columns, I calculated how many rows I’d need by dividing the total number of icons by the number of columns. This told me how to setup my included scrollbar (again - complex controls as combinations of simple ones).