Developing GUI (3) using C and DirectX
Welcome to the third part of "Using C and DX Development GUI". (Here is the first part and the second part). Next our theme (describe how I build GUI for my future game), this article will explore the construction of the GUI office Some general controls needed. We will detail several different control forms, including buttons, list boxes, text boxes, and more.
This section is not like a lot of code as other chapies - this is mainly because our programmer is very pickily about the look of the GUI. We like to do our button, text boxes, and GUI look unique, and in line with Our own aesthetic standard. Such a result is that everyone's control code is very different, and it will not want my special drawing code. In addition, the code to write the GUI element is very interesting, in fact, I will Look, this is the most interesting part in the process of writing the GUI. Now continue.
A very important question is that before we start - make your GUI_Window destructor into virtual functions. I didn't mention this in the second part, because we didn't send any subclasses from Gui_Window, but now I propose this - makes your virtual functions to the destructive function of your GUI_WINDOW and all its derived classes, because this will ensure that there is no memory leak - because the derived destructor is not called. Care C Trap.
After you have finished this, let's first determine what kind of GUI controls we need.
We need the GUI control
I don't want to spend too much time to develop controls for my GUI; I will only focus on the simplest control set. So, I will list the control I think is the minimum control set:
1. Static text, icons, and combo boxes (most important). These spaces will have other controls or groupings in the dialog. Static controls are important; we may not need frame controls, but it is very simple, and in some cases It will enable the dialog to easily navigate, so I will include it. The icon control is also very simple, but it should be able to express animation - provide a cool background animation for our dialogs and men (IT: Dark Plan).
2. Button and Select box (most important). Special forms of buttons are not required, however, most games do not have basic buttons and selection boxes.
3. List box (Important) I discover the list box, especially multi-list control, is indispensable when creating a game GUI. Their application is omnipotent. You need a smart, heavyweight list control, and Windows The list control is as good or excellent. For me, the list control is the most difficult control.
4. Slide strips and scroll bar (important). The most common on volume control. Bad news is that we may need horizontal and vertical scroll bars, good news is very similar, so develop is simple.
5. Text box (most important). You must be able to type your MEGA-3133T, Super-Kewl player logo, right?
6. Progress bar - It is necessary to show the value of life, "I am going to be loaded!" And so on.
The lack of spindle-shaped buttons (SPIN Button), radio box (we can replace it with a single-selection list box), drop-down combo box (also we can replace it with a list box) and a tree control. By design clever list control To indent specific objects, we can implement a tree list.
Since my game doesn't have enough GUI to ensure the expression control, there is no contains it, although you may need.
Even if there are these omissions, the above "minimum" list may look very complicated, but we can simplify a little.
Break it: Combine simple space to achieve complex control
If we realize that the complex control is just a clever combination of simple controls, the list will be more likely to control. For example, a scroll bar is basically only two buttons and a sliding bar. A selection box is a static text and two buttons (one "Open" button, a "closed" button). A flat button can use three icon controls (only display / hide the appropriate icon to make the button look down) so you can reuse your drawing code. If You don't have time, you can even use a progress bar as a sliding bar, although I prefer to use a separate control.
However, this is defective, and your GUI control is more system resources than they actually need. Carefully consider it - each control is a form. Let us say that you have created the reuse One is actually a button control for three static controls. Then each button is three forms. Now you use two button controls to create a scroll bar, that is, each scroll bar 6 forms. Use horizontal and vertical scrolling Bars create a list control, then each list is 12 forms. It increases very quickly.
So this is another example of another classic "I am developing" and "how much resources I will use". If you need a high-performance, there is no waste of GUI, from the foundation to develop every control. If You want to develop quickly, then don't mind the performance loss, you may choose the development control to make the static controls on the screen, all other controls are combined by static controls. When I developed GUI I do my best to achieve good balance between the two extremes.
Now, let us start paying attention to the actual development of each control, starting from everyone's favorite static sign.
We need to pay attention to three static controls: static text controls, static icons controls and frame controls. These three controls are simple because they don't receive messages - they just draw themselves in a certain location.
Static text control is the simplest control you will develop - just draw the title of the window in the upper left corner of the window. If you want to increase the code to adjust your text in some way - eg, in the drawing box Your text, you might use a classic center algorithm. Using the width of the form minus the width of the text to be drawn, then divide 2, tell you how much pixels from the left side begins to draw.
The static icon control is slightly difficult. In fact, the term "static icon control" is some ambiguous, assuming that we want our icon controls to express animation. Even so, it is not difficult to develop these icon controls, suppose you already have Rich Elf Library to deal with all the details of the development of animations: Test the time difference between the two frames, use this difference to determine how many frames will be taken, and so on.
The icon control is only cumbersome when you don't draw the entire GUI system at each frame. In this case, how much you have to deal with the cropping of the icon control, so that each frame is drawn, it will not overwrite The pixels of the window on it (but there are no changes, so there is no way to develop this - I will call every frame of my gui - but if you face this problem, you may try to set up each icon. Treated the list, use it to draw icons, recalculate it when you have any form move, turn it off, or open. This may be a feasible method - I just think - but this is a good entry point.
The frame control is also very simple. I only draw a border around M_Position when I develop my frame control, and then draw the window title in the vicinity of the coordinate (5, 5) point (about 5 pixels from the upper left corner of the frame control). You can decide yourself in your own imagination.
The trouble you may encounter in the development of static controls is a slightly changing the functionality of the FindWindow function to make it skip all static control windows. This way, if a static text control is above a button, the user can be static Controls to press this button. When developing the "Simple Mobile" window (ie you can move the window by pressing any part of the window, not just the title bar, just like Winamp), this is useful.
Let us now look at how to develop buttons.
Button control
The button is only difficult than the static control. Your button control needs to keep track of whether it is pressed or released. It implements two virtual functions, wm_mousedown () and wm_mouseup (), your Calcall () function needs Call them when appropriate.
Basically, in WM_MOUSEDOWN (), you have to set a Boolean variable, I called "Depressed Flag" as true, and set it in wm_mouseup (), set it to false. And then yours If you press the logo as true, the point of drawing the button is drawn, otherwise, the release state is drawn.
Then, add an additional state - ie "Only when the flag is pressed as the true and the mouse pointer, the button is drawn to the point, otherwise, set the sign to false." If you remove the mouse out button, this will Make your button to bounce, and when it is very important for accurate judgment a button.
For ordinary GUI, when a button is clicked, the event will cause an event for his parent form, the form will make anything represented by the button - for example, click the Close button will close the window, click the Storage button to store the file, etc. Wait. My GUI is only a clicked button in WM_MouseUp (), and the flag is true. Press the flag in mouseup () is really unique to the only situation is that the user is pressed within the button. And release the mouse button. This allows the user to release the selection in the final way - loosen by keeping the mouse button and dragging the mouse pointer to the button, just like other GUI.
This is the button. Let's take a look at the text box.
Insert and text control
I chose a very simple text control. It only captures the key, but it doesn't have a screen - but you may have to be more complicated, that is, a precise handle jump to the beginning (HOME), jump to the end (END) , Insert and delete characters, or may also support cut, copy, paste. But before we do a text box, we need an insert. If you are not familiar with this term, explain it here. Insert It is another statement of the cursor - yes, it is the vertical line of the small flash. The insert tells the user where their hit will appear.
From my GUI consideration, I am very simple to deal with these things - I specified the event window to have an insert and a window (here Rick is not very clear) window. Most gui is like this, it seems to be the best solution. And my GUI is handled as the title of the text box as the text in the text box like WINDOWS.
So how do you develop insert? Ok, I think we know that the insert is always drawn in the active window, and the insert is only when the active window is the text box, it is easy to think of the insertion code is Part of the text box and is done in the drawing function of the text box. This makes it easy to develop - as long as you use a shaped variable to represent the index of the window title character array, your text box has all the information to draw insert. .
This basically said that if it is a text box, all the drawings you have to do is to draw a window title within the side line, then draw the window title, and then draw the insert in the correct position. In my GUI, the maximum length of characters in the text box is determined by the size of the text box window, which means that I don't have to handle scroll within the text box. However, you may want users to be small text. Enter a long string in the box and scroll over the text box.
Now let's take a look at the hardest thing of the text box - keyboard processing. Once you have a hit button, it is easy to build a wm_keypressed () virtual function and call it, it is easy to develop text box processors for WM_KeyPressed, and then Characters are placed at the end of the window title, or handle special keys (Backspace keys, etc. - this is your string class to pay attention to the thing), then move the insert.
The difficult place is to get a hit button in the first location. Windows provides at least three completely different ways to query the keyboard -WM_keyDown event, getKeyboardState () and getasynckeystate () functions, of course there is DirectInput. I use DirectInput method, this Because I have made a lot of DirectInput related work when I develop my mouse cursor, and the keyboard status is also the most simple and elegant approach to me through DirectInput.
To use the DirectInput's keyboard function, the first thing you have to do is to build a keyboard device. This is difficult to believe in the mouse device for building DirectInput in Chapter 1. Basically, the only difference is not Tell DirectInput to handle our new equipment as a mouse, but as a keyboard. If you have learned how DirectInput is handled by the mouse, then do the same thing to do it again.
Once you get the keyboard device, we can query it.
To actually determine if a button is pressed, it is necessary to work. Basically, it is necessary to determine which key is pressed, you need two snapshots for all 101 keys - a current frame from the previous frame. Current Frame. Current The frame in the frame is pressed and the key that is not pressed is "click", and you want to send a WM_KEYPRESSED () message for them.
Let's take a look at the progress bar?
progress bar
The progress bar is as easy to develop as static controls because they only receive few messages.
Basically, you need to do two things for progress bars - you have to tell it the maximum / minimum range and steps. For example, I want to create a load schedule, because I want to load 100 different game resources. I will Create a progress bar that ranges from 0 to 100. I will initiate the progress bar for 0, then, when I load a resource, I will use the length of the unit to make the progress bar into one step. When the progress bar moves forward, It will override itself, with a length of the proportion of the drawing zone on a graphic to indicate how long it is.
The progress bar is very like a scroll bar; in fact, it can be developed with the method of scroll bar. I have developed the progress bar and scroll bar because I want them to have very different appearances and thin behavior - your needs May be different.
Sliding bar and scroll bar
Draw a sliding strip or scroll bar and drawing progress bar very similar, this manifests in the percentage of the rectangle of the slider, it provides a smooth location information to express its current location. You want vertical and horizontal Controls have made some subtle modifications - I first made a base class, GUI_SLIDER, which contain all public code and all member variables, then develop two different derived classes, GUI_SLIDER_HORZ and GUI_SLIDER_VERT, they handles and click logic Different. Like the mouse click, I chose a simple way for the slider. If the mouse click to occur in the scroll bar drawing zone, directly scroll to that position. In my sliding strip, you can't at the same axis Click and move the position - jump directly to your click place. I am mainly because this will be simple, and I don't like the default method of Windows.
About the logic of the scroll bar / slider, you know the basic settings of the progress bar is the same - minimum, maximum, current location. However, the user can change the current position by clicking on the control.
Now take a look at the scroll bar. My GUI roller bar is the slider of each other with a button. These two buttons (up / down or left / right arrow) will move the smooth unit distance. This method eliminates a lot The code copy between the button and the scroll bar, I strongly recommend that you look at the similar thing.
After reading the scroll bar, look at the most complex control.
List box control
Shift from this, the list box control is where you have to spend the last time.
// REPRESENTS A Column in Our ListBoxClass 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 outboxclass gui_listbox_item {public: gui_listbox_item () {m_isselected = 0; m_indent = 0;} Virtual ~ GUI_ListBox_Item () {}
Virtual Draw (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 (unsigned long itemdata) {m_itemdata = itemdata;} unsigned long 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 unsigned long m_itemdata; uti_pointerarray m_coltext;}; // the listbox itselfclass gui_fancylistbox: public gui_window {public: gui_fancylistbox () {m_multiselect = 0;} virtual ~ gui_fancylistbox () {clear ();
INT getSelected (int uit = 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_scrollbar_horz & gethscroll (void) {return (m_hscroll);} GUI_SCROLLBAR_VERT & GETVSCROLL (Void) {return (m_vscroll);}
Virtual int wm_sizechanged (void); // the window's size has change 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 Desitem (IT ITEM);
Private: int m_numdispobjsy; int m_vertgutterwidth; // # of Pixels Between items Vertically
GUI_SCROLLBAR_HORZ M_HSCROLL; Gui_ScrollBar_vert m_vscroll;
BOOL M_MULTITITITTABLE? UTI_POINTERARRAY M_ITEMS; // Array Of GUI_LISTBOX_ITEMS UTI_POINTERARRAY M_COLUMNS; // Array Of GUI_Listbox_columns};
The list box is the most difficult control you do so, but this is just because it is most common. A multi-column, indentation, multiple selection list box control will prove his game to you in practice. It is indispensable. Stop and think about the list of list boxes in most games, you will quickly discover this. I divide my list box control into two parts: a multi-column "report style "List control and an icon list control, which creates a display similar to when you select a large image inspection method in Windows" My Computer ".
The chart list control is easier to build. It uses a row of static icons (reused in a code), all of the same size. I use the width of the icon in the list box, which makes me know that there are several columns available. (If proof My list box is smaller than the big chart. I assume that I only have a column and let the system cut icon so that they will not exceed my drawing area). Once I have columns, I am divided by the total number of icons. The number of rows I need. So I know how I set the scroll bar to be included.
Note When the control change is larger. These values must be recalculated. To this, I set a WM_SIZECHANGED () message, and calcall () will call it when the window drawn area is changed.
Report-style list controls are more complicated. I first wrote two secondary classes, gui_listbox_column and gui_listbox_item, which contains all information about given objects and columns in the list.
GUI_LISTBOX_COLUMN is more simple. The main list box class has a dynamic array of GUI_ListBox_column of a member variable identity, which represents the columns in the current list box. GUI_Listbox_column contains all information required in the list box, Includes the name of the column, alignment, display or hidden, size, and so on.
The main list box class also has a direct number of GUI_ListBox_Item. The GUI_ListBox_item class contains all the information related to a particular row (or object) in our report style list box. The most important data member of this class is the word representing each column data. A string array. I also allow each item to store an additional 32-bit data via M_ItemData. This technology is similar to Windows allows you to call setItemData () and getItemData () and getItemData () in your list item. This details is very Important because it allows users of the list box to store a pointer for each item - usually a particular class associated with the object to make it available later.
How to draw columns and objects? I tend to have an absolute control on each individual object / column in the list box to be drawn. At the end, I decided to let the list control pass the two virtual functions, GUI_Listbox_Item: : Draw () and GUI_ListBox_column :: Draw () to draw his objects and columns. Each function uses a representative column or a rectangle of the object on the screen. The default is only divided by the development of these DRAW () functions. Text-related text-specific columns in the rectangle; however, I can get a simple appearance of items or columns that need unique appearance, this technology is working very well, but I am not enough to claim this. It is the best way.
However, drawing objects require more work than line. The object needs to be drawn with high light, which is determined whether they have been selected. This is not difficult, but it must not forget.
Then it is the problem of scroll bar. My list box contains two members, M_horzscrollbar and M_Vertscrollbar, they are all GUI scroll bars. When the size of the list box is changed (WM_SIZECHANGED ()), he will look at the width of the data and The height and decide whether the scroll bar is displayed.
to sum up
It's really a big circle, but fortunately, you have a rough idea for the creation of controls for the GUI. Here I want to emphasize "the style is fun". Don't be afraid of innovation when you do your GUI - do you. Dreams, and make your game the coolest thing. If your game relies on your GUI's effectiveness, this is especially important, such as you are making an instant strategy game.
Also remember that when you create a control, you need to consider balanced problems for your other part of your game - the details of the performance and development time are proportional. Try to give your players easier to get the GUI, but do not take time Do 50 different controls. You have to balance in the function, good things, and complexity.
The control is here. The next chapter is also the last chapter we will look at the resource editor, serialization window, and create dialogs. I wish you a happy!