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).
Note that these values will have to be recalculated whenever the control is resized. For this reason, I set up a wm_sizechanged() message that calcall() would call whenever the client area of a window was modified.
The report-style list control was a little more complex. I first created two helper classes, gui_listbox_column and gui_listbox_item, which contained all of the information about a given item and column in the list.
gui_listbox_column is the simpler of the two classes. The main listbox class keeps, as a member variable, a dynamic array of gui_listbox_columns, which represent the columns in the listbox right now. gui_listbox_column contains all of the information needed for a column in our list box, including the name of the column, the alignment of the column, whether it’s shown or hidden, its size, etc.
The main listbox class also keeps a dynamic array of gui_listbox_items. The gui_listbox_item class contains everything related to a particular row (or item) in our report-style listbox. By far the most important data member of this class is the array of strings, representing the data for each column. I also decided to let each item store an additional 32-bits of data with it, via the m_itemdata member. This technique is similar to how Windows allows you to store 32-bits of data by calling SetItemData() and GetItemData() for your listbox items. This feature is important because it allows clients of the listbox to store a pointer with each item - usually a pointer to the specific class associated with the item, so that it’s readily available later.