University of Wisconsin Computer Sciences Header Map (repeated with 
textual links if page includes departmental footer) Useful Resources Research at UW-Madison CS Dept UW-Madison CS Undergraduate Program UW-Madison CS Graduate Program UW-Madison CS People Useful Information Current Seminars in the CS Department Search Our Site UW-Madison CS Computer Systems Laboratory UW-Madison Computer Sciences Department Home Page UW-Madison Home Page

Assignment 1: Hands on Demo

Introduction to FLTK

Since FLTK is a graphical user interface toolkit, its main purpose is to create interfaces. In most cases, this involves creating windows with widgets ( buttons, sliders, dials, browsers, etc. ) on them. FLTK provides a way to create an event driven program based on these interfaces. Although FLTK provides many classes for specific widgets, one of its beauties lies in the fact that you can inherit from these classes. Through inheritance, you can override certain functionalities of each widget, and configure it to suit your own application, without the need to code everything from scratch. As you go through this tutorial, keep in mind that these are specific examples to help you see and learn FLTK. The most important piece of information you should come away from this tutorial with is how to build interfaces. It is not as important that you create the specific files that are mentioned in this tutorial.

The first thing we are going to need in order to produce an interface is a Visual C++ workspace. Using the process we went through in the project type walkthrough, create a new FLTK Workspace in Visual C++.

Bringing Up A Window

Now that you have created a workspace to build interfaces, let's create a very basic interface. The most basic interface consists of just a window by itself.

  1. Choose File->New.
  2. Click on the Files Tab at the top of the dialog box. Click on C++ Source file. Name the file BasicWindow on the right hand side of the dialog box and press OK
  3. At this point you should see a cursor blinking in the editor window. If not, click on the FileView tab in the workspace window. Expand (if necessary) the source files folder. Double-click on BasicWindow.cpp.
  4. This is where you need to type all the necessary code for the BasicWindow.cpp file. As with any C/C++ program, you need a main function to run the program. Type the following lines of code into this file.
  5. #include <FL/Fl.H>
    #include <FL/Fl_Window.H>
    
    int main()
    {
      Fl_Window b(20, 20, 400, 400,"Title for our window");
      b.show();               
      return Fl::run();
    } 
    
    
  6. At this point, save the file by either choosing File->Save or hitting Ctrl-S on the keyboard.
  7. Now that we have our file saved, we can compile our program. To compile the program, choose Build->Build [project name].exe or press F7.
  8. Now, to execute the program, choose Build->Execute [project name].exe or press Ctrl-F5 on the keyboard.

You should see two windows appear. One window is the console window which will have an MS-DOS symbol and the project path at the top. It will have a blinking cursor in it as well. The other window that will appear is your FLTK window that you have just created. The console window is where you will see all of your output from your program that you generate using cout or printf. The FLTK window is your Fl_Window that was just created. To close the Fl_Window, either press the Esc key or press the X is the upper right corner of the window.

Handling Mouse Events

Now that you can bring up a basic FLTK window, it is imperative that you be able to handle certain events. Mouse events are extremely important to handle because it is the most common input device used to interface with GUIs

  1. Create a new C++ Header file called EventWindow.
  2. Type the following lines of code into this file, or copy them from the EventWindow.h file in the democode/ directory.

    Take note that the EventWindow class is inheriting from the Fl_Window class. The Fl_Window class is the widget that FLTK provides for us. Since we know that we want to treat mouse events differently, we are going to want to override the default code for the handle() method. This is what makes FLTK so powerful. We don't have to spend our time writing windowing code. We simply want to add additional code that specifies what to do when a mouse event occurs.

  3. #ifndef __EVENTWINDOW_H
    #define __EVENTWINDOW_H
    #include <FL/Fl_Window.H>
    #include <FL/Fl.H>
    #include <stdio.h>
    
    // FlTk demo program for CS638
    // Fall 2000, Mark Pingel
    // Comments added, Fall 2000, Michael Gleicher
    
    // this class implements a very simple widget that responds to 
    // mouse and keyboard events
    // the idea is to show how to handle events in fltk
    
    class EventWindow: public Fl_Window
    {
            private:
    				// Handle events generated by mouse actions
    				// event is the integer event number, 
    				// button corresponds to the mouse button depressed, 
    				// x, y refer to the x and y location of the mouse
    				// when the event occurred
    				int handle_mouse(int event, int button, int x, int y);
    
            public:
    				// Constructor for the EventWindow
                    EventWindow(int width, int height);
    				// Overloaded function to handle events for the window
                    int handle(int e);
    		
    
    };
    
    #endif
    
  4. Create a new C++ Source file called EventWindow.
  5. Type the following lines of code into this file, or copy them from the EventWindow.cpp file in the democode/ directory.

    Again, take note that the constructor for the EventWindow calls the constructor for the Fl_Window. We are not going to change anything about the window except for how it handles events.

    // FLTK demo program for CS559
    // Fall, 2000, Mark Pingel
    // implementation of EventWindow class - see documentation in header
    
    #include "EventWindow.h"
    char * foo;
    
    // Constructor for the EventWindow
    // Arguments are the initial width and heigth for the window
    EventWindow::EventWindow(int width, int height)
    :Fl_Window(width, height, "")
    {       
            label("Demo Window for CS 559");
    
    }
    
    // This function is called by FLTK and passes off responsibility
    // accordingly...
    int EventWindow::handle(int event)
    {
            switch (event) {
                            case FL_PUSH:
                            case FL_RELEASE:
                            case FL_DRAG:
                            case FL_MOVE:
                                            return handle_mouse(event,
    							    Fl::event_button(), 
    							    Fl::event_x(),
    							    Fl::event_y());
                            case FL_FOCUS:
                                            label ("Gained focus");
                                            damage(1);
                                            return 1;
                            case FL_UNFOCUS:
                                            label ("Lost focus");
                                            damage(1);
                                            return 1;
                            default:
                                            return Fl_Window::handle(event);
            };
    }
    
    // This function changes the label on the window based
    // which mouse button was activated, where it was activated, and what
    // activity the mouse was doing at that time ( push, drag, release )
    int EventWindow::handle_mouse(int event, int button, int x, int y) {
    	// Deallocate the memory for foo if necessary
    	if (foo) delete [] foo;
    	// Reallocate new memory
    	foo = new char[100];
    	int ret = 0;
    	// Look for drag events and switch on the event_state(), which will return
    	//the button that was depressed last.
    	switch( event ) {
    
    		case FL_DRAG :
    			switch (button) {
    				case 1:
    					ret = 1;
    					sprintf(foo,"LMB Drag ( %d , %d )",x,y); break;
    				case 2:
    					ret = 1;
    					sprintf(foo,"MMB Drag ( %d , %d )",x,y); break;
    				case 3:
    					ret = 1;
    					sprintf(foo,"RMB Drag ( %d , %d )",x,y); break;
    			}
    			break;
    		case FL_PUSH:
    		
    			// Switch on the mouse button activated
    			// 1 - Left Mouse Button
    			// 2 - Middle Mouse Button
    			// 3 - Right Mouse Button
    			switch ( button ) {
    				case 1: // LMB
    					ret = 1;
    					// Based on the action, print the action and
    					// coordinates where it occurred.
    					sprintf(foo,"LMB PUSH ( %d , %d )",x,y);
    					break;
    				case 2:
    					ret  = 1;
    					sprintf(foo,"MMB Push ( %d , %d )",x,y);
    					break;
    				case 3:
    					ret = 1;
    					sprintf(foo,"RMB Push ( %d , %d )",x,y);
    					break;
    			}
    			break;
    		case FL_RELEASE:
    			// Switch on the mouse button activated
    			// 1 - Left Mouse Button
    			// 2 - Middle Mouse Button
    			// 3 - Right Mouse Button
    			switch ( button ) {
    				case 1: // LMB
    					ret = 1;
    					// Based on the action, print the action and
    					// coordinates where it occurred.
    					sprintf(foo,"LMB RELEASE ( %d , %d )",x,y);
    					break;
    				case 2:
    					ret  = 1;
    					sprintf(foo,"MMB RELEASE ( %d , %d )",x,y);
    					break;
    				case 3:
    					ret = 1;
    					sprintf(foo,"RMB RELEASE ( %d , %d )",x,y);
    					break;
    			}
    			break;
    	}
    	if (ret) {
    		label(foo);
    		damage(1);
    	}
    	return ret;
    }
    
    
    
  6. Select the FileView tab at the bottom of the workspace window to see the files currently in the project. Delete BasicWindow.cpp from the project workspace by selecting it in the workspace window and pressing the Delete key. Note, this does not delete the file from your directory, it only removes it from the project.
  7. Create a new C++ source file called EventWindowMain.
  8. Type the following lines code into this file, or copy them from the EventWindowMain.cpp file in the democode/ directory.
  9. // FLTK demo program for CS559
    // Fall, 2000, Mark Pingel
    
    // Program that creates and instantiates an EventWindow
    
    #include <Fl/Fl.H>
    #include <stdio.h>
    #include "EventWindow.h"
    
    int main(int argc, char **argv) {
    	EventWindow b(250,250);
    	b.show();
    	return Fl::run();
    }
    

Now, using the previously mentioned methodology, build and run this application.

Watch the label change as it corresponds to what you're doing with the mouse. Try clicking on the window with each mouse button. What happens as you drag the mouse? What occurs as you minimize and restore the window? Take a look at the code. See if you can follow what is occurring on the screen and how it is being accomplished in the code.

Handling Keyboard Events

Another useful way to obtain information from the user is via the keyboard. FLTK provides a pretty simple way to handle keyboard events. We've already seen how the handle function handles mouse events, now let's take a look at how the handle function may incorporate keyboard events as well.

  1. In the EventWindow.h file, add the following function prototype underneath the prototype for handle_mouse.
  2. 				// Function to handle keyboard events
    				// event is the integer event number
    				// key is the integer corresponding to which key on the
    				// keyboard that was depressed
    				int handle_key(int event, int key);
    
  3. Add the following case statement into the switch statement in the handle function in EventWindow.cpp.
  4. 	                case FL_KEYBOARD:  
                                            return handle_key(event,Fl::event_key());
    
  5. Add the following function body at the end of the EventWindow.cpp file.
  6. // This function handles keyboard events
    int EventWindow::handle_key(int event, int key) {
    
    	switch ( key ) {
    		// If the 'c' key is depressed, change the window
    		// label
    		case 'c': label ("letter c was depressed");
    				  damage(1);
    				  return 1;
    		default: label ("Nothing to do!");
    				 damage(1);
    				 printf("nothing to do\n");
    				 return 1;
    	}
    }
    
  7. Recompile the project and run the application again. Note what happens when you depress a key on the keyboard. What happens when the `c' key is depressed.
  8. Try altering some of the lines of code and see what happens.

Adding Basic Components to a Window

At this point you've seen how to handle events, and how to fire up a quick interface. The one thing that remains is the ability to add some basic components to your windows. So, let's go through how to add a few buttons to a window and register callbacks for those buttons.

  1. Remove all files from your current project space by selecting them with the mouse and pressing the Delete key.
  2. Create a new C++ source file called ButtonDemoMain.
  3. Type the following lines code into this file, or copy them from the ButtonDemoMain.cpp file in the democode/ directory.
  4. // FLTK demo program for CS559
    // Fall, 2000, Mark Pingel
    
    #include <FL/Fl.H>
    #include <FL/Fl_Window.H>
    #include <FL/Fl_Check_Button.H>
    #include <FL/Fl_Light_Button.H>
    
    
    // Create pointers to a check button and a light button
    Fl_Check_Button* check_b;
    Fl_Light_Button* light_b;
    
    
    
    // Callback function for the check button. 
    // Turn the label red and change it to read Checked if the 
    // check button is checked.
    void checkbutton_cb(Fl_Widget* w) {
    	Fl_Check_Button* flcb = ((Fl_Check_Button*)w);
    	if ( flcb->value() == 1 ) {
    		flcb->labelcolor(FL_RED);
    		flcb->label("Checked");
    		
    	}
    	else {
    		flcb->labelcolor(FL_CYAN);
    		flcb->label("Check Button");
    	}	
    	flcb->damage(1);
    	flcb->parent()->redraw();
    }
    
    // Callback function for the light button. 
    // Turn the label yellow and change it to read Light On if the 
    // light button is pressed.  A yellow square will also appear in
    // the box to the left of the label if selected
    void lightbutton_cb(Fl_Widget* w) {
    	Fl_Light_Button* fllb = ((Fl_Light_Button*)w);
    	if ( fllb->value() == 1 ) {
    		check_b->hide();
    	}
    	else {
    		check_b->show();
    	}	
    	fllb->damage(1);
    	fllb->parent()->redraw();
    }
    
    
    int main(int argc, char ** argv) {       
    	// Create a window to add our buttons to
    	Fl_Window* w = new Fl_Window(190,80);
    	
    	w->label("559 Demo Window");
    	// Begin adding children to this window
    	w->begin();
    
    		// Instantiate the check button
    		// x , y , width, height, label
    		check_b = new Fl_Check_Button(25,15,140,20,"Check Button");
    		// Instantiate the light button
    		// x , y , width, height, label
    		light_b = new Fl_Light_Button(25,45,140,20,"Hide Check Button");
    
    		// Make all of the default colors for the labels cyan
    		check_b->labelcolor(FL_CYAN);
    		light_b->labelcolor(FL_CYAN);
    
    		// Register the callbacks for each of the buttons
    		check_b->callback(checkbutton_cb);
    		light_b->callback(lightbutton_cb);
    
    	// Stop adding children to this window
    	w->end();
    	// Display the window
    	w->show();
    	// Run
    	return Fl::run();
    	
    }
    

    Now, using the code that is already there as a model, let's add another button that controls whether or not the Hide Check Button should be grayed out (activated) or not.

  5. First off, we need to choose a button type for this button we are going to create. Fl_Round_Button, Fl_Light_Button, and Fl_Check_Button would all be acceptable choices because they each represent boolean choices. By that I mean that they are either on or off.
  6. Since we've already used an Fl_Light_Button and an Fl_Check_Button, we'll use an Fl_Round_Button to add a little variety.
  7. First thing we should always do before adding a new component is consult the documentation for the component at the FLTK documentation site.
    http://www.fltk.org/documentation.php/doc-1.1/toc.html
  8. We scroll down until Appendix A - Widget Reference. There we choose the link to Fl_Round_Button.
  9. First thing to look for is what include files need to be included. This information is usually listed second, right after the class hierarchy. There, we see for Fl_Round_Button that
    #include <FL/Fl_Round_Button.H>
    
    is listed. So, at this point we can go back to our code and add this line in the list of includes.
  10. Once we have this included, we can procede to creating the button. By looking at the other button declarations, we can see how to declare a button. So, we can add the following line of code right after the other button declarations:
    Fl_Round_Button* round_b;
    
  11. At this point, we have our new button declared, but it doesn't do anything. In order for this button to do something, we need to write a callback function. Callbacks are functions that are called when the value of a widget, a button in this case, changes. In the case of a button, the value of the button changes when it is pressed. Now, what does this callback function need to include. A callback function is sent an Fl_Widget pointer of the widget that changed and optionally a pointer to data of some sort. In this case, no additional data needs to be included, so we'll just pass in an Fl_Widget pointer to our function. So, it should look like
    void round_b_cb(Fl_Widget* w) {
    
    }
    
    Note: You can name the function anything you like. I generally try to name the callback function something intuitive. Otherwise it may become more difficult when you have complex interfaces with over fifty widgets and you're trying to match up a callback with a widget.
  12. At this point we need to determine when the button is `on' and when the button is `off.' The value() function is provided for all Fl_Buttons to do just this task (See Fl_Button documentation on the FLTK web site ). It returns a 1 when the button's value is `on' and 0 for the `off' case. So, how do we encode this test. We can use a simple if test on the widget we passed in. However, we need to cast this Fl_Widget* to be an Fl_Round_Button* in order for us to use the value() function, because Fl_Widgets do not have the value function.

    Therefore, our code should resemble:

    if ( ((Fl_Round_Button*)w)->value() == 1 ) {
    }
    else {
    }
    
  13. Now that we have that part taken care of, the only other piece of information we need to encode is how to activate and deactivate the Hide Check Button button. Well, we have access to this button through the light_b pointer. So, we just need to find a function that can handle what we're asking it to do. So, consulting the Fl_Light_Button documentation, we don't find anything directly. If we keep following the documentation up the class hierarchy, we eventually find the activate() and deactivate() functions in the Fl_Widget class. Since an Fl_Light_Button is an Fl_Widget, we don't need to do any casting, we can simply call those functions as necessary. Placing that code into our callback function we get:
    void round_b_cb(Fl_Widget* w) {
         if ( ((Fl_Round_Button*)w)->value() == 1 ) {
                         light_b->activate();
         }
         else {
                         light_b->deactivate();
         }
         w->damage(1);
         w->parent()->redraw();
    }
    

    The damage(1) tells FLTK that the widget needs to be redrawn. The redraw() function tells FLTK that the round button's parent (the window itself) needs to be redrawn. Add this function definition to your source file, now.

  14. At this point, we only need to create the button itself and register the callback. To create the button, we just need to specify a location on the window, a width, a height, and a label. To do so, we place this code into our main function.
         round_b = new Fl_Round_Button(25, 75, 140, 20, "Activate Light B");
         round_b->labelcolor(FL_CYAN);
         round_b->callback(round_b_cb);
    
  15. Now, we can just compile and run the program and watch how it works. Feel free to try altering other aspects of the widgets such as labelcolor, label, labelsize, etc.

This Concludes the Demo

We strongly recommend reading through some of the documentation on FLTK's website and playing around with the extra widgets. Try adding some other widgets besides buttons.

Some things to note:

That's it! Your ready to develop your own interfaces using FLTK.