Button & Callback Tutorial

First lets expand FirstWindow, into ButtonSample.cpp, it will display a both the window and a button:

00001:   #include <WKGL.h>
00002:   
00003:   using namespace wkgl;
00004:   
00005:   //Callback prototypes
00006:   BOOL OnButton( Component*, UINT, WPARAM, LPARAM );
00007:   
00008:   WPARAM main()
00009:   {
00010:     Window win( "My Second Window" );
00011:     Button button( "Test Button", 0, 0 );
00012:     
00013:     //Add the button to the window.
00014:     win.add( &button );
00015:     //Show the window
00016:     win.show();
00017:        
00018:     return win.beginMessageLoop();
00019:   }

The important line here of course is the Button constructor. The first parameter is the label of the button, i.e. what text is displayed on the button. Next comes the x, and y coordinate of the button reletive to the upper-left corner of the window minus the title bar. A button does us no good if it is not attached to a window, and that is what the next line does. This tells the component what window it is to be added to.

You may have noticed this is not an especially useful program since the button does nothing. To make a component "do something" you have to specify a callback function. Callback functions are used to process the various events that the component generates. So lets add one (do not forget to put the prototype above the WinMain function).

00001:   #include <WKGL.h>
00002:   
00003:   using namespace wkgl;
00004:   
00005:   //Callback prototypes
00006:   BOOL OnButton( Component*, UINT, WPARAM, LPARAM );
00007:   
00008:   WPARAM main()
00009:   {
00010:     Window win( "My Second Window" );
00011:     Button button( "Test Button", 0, 0 );
00012:     
00013:     //Add the button to the window.
00014:     win.add( &button );
00015:   
00016:     //Add the callback for the button
00017:     button.addCallBack( OnButton );
00018:   
00019:     //Show the window
00020:     win.show();
00021:        
00022:     return win.beginMessageLoop();
00023:   }
00024:   
00025:   //Callback for button
00026:   BOOL OnButton( Component *com, UINT message, WPARAM wParam, LPARAM lParam )
00027:   {
00028:     static char buf[50];      //Buffer for buttons text.
00029:     static int  clicked;      //Number of times the button has been clicked
00030:   
00031:     if ( message == BN_CLICKED )
00032:     {
00033:      sprintf( buf, "You clicked the button: %i times", ++clicked );
00034:         
00035:       //Set the button's text.
00036:       ((Control*)com)->setText( buf );
00037:   
00038:       return TRUE;
00039:     }
00040:     
00041:     return FALSE;
00042:   }

First notice the addCallBack() method, it is used to tell the component that the passed in function wants to recieve events. Next lets look at the OnButton() callback. This is the general structure of how you will process the event. The message parameter contains the type of event that occured. If you look in the windows documentation the actual event that occured is WM_COMMAND in the win window, however the library automatically detects this event, and only this event, for you and calls the approriate child component callbacks. So message will contain event BN_CLICKED instead of HIGHWORD(wParam). If you wish to keep windows from preforming the default behavior you must return TRUE. Otherwise windows will preform some unspecified operation by default. You can add multiple callbacks to a component, but you should be aware that they are all called for a given event. The com parameter is a pointer to the component that generated the event, in this case it will point to button. The wParam and lParam parameters hold various information depending on the event that was generated. There are so many events you will have to check the windows documentation on the control to find a list of them.

.You are probably wondering what the deal is with the static local variables. Well static local variables stick around after the function returns. They are only created and initialized once. This is a neat trick to maintain data between callbacks, this is a better way than using global variables. The down side is that in some debugers (namely MSVC 6.0) you cannot place a watch on them.

When you click the button for the first time, notice how it dynamically resizes itself. It does this because we did not set its size manually insead we left the w parameter in the constructor to its defalut, AUTOSIZE.