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++.
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.
#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(); }
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.
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
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.
#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
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; }
// 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.
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.
// 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);
case FL_KEYBOARD: return handle_key(event,Fl::event_key());
// 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; } }
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.
// 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.
http://www.fltk.org/doc-1.0/toc.html
is listed. So, at this point we can go back to our code and add this line in the list of includes.
#include <FL/Fl_Round_Button.H>
Fl_Round_Button* round_b;
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.
void round_b_cb(Fl_Widget* w) { }
Therefore, our code should resemble:
if ( ((Fl_Round_Button*)w)->value() == 1 ) { } else { }
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.
round_b = new Fl_Round_Button(25, 75, 140, 20, "Activate Light B"); round_b->labelcolor(FL_CYAN); round_b->callback(round_b_cb);
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:
FL_Button - A mouse button; use Fl_Button + n for mouse button n. FL_BackSpace - The backspace key. FL_Tab - The tab key. FL_Enter - The enter key. FL_Pause - The pause key. FL_Scroll_Lock - The scroll lock key. FL_Escape - The escape key. FL_Home - The home key. FL_Left - The left arrow key. FL_Up - The up arrow key. FL_Right - The right arrow key. FL_Down - The down arrow key. FL_Page_Up - The page-up key. FL_Page_Down - The page-down key. FL_End - The end key. FL_Print - The print (or print-screen) key. FL_Insert - The insert key. FL_Menu - The menu key. FL_Num_Lock - The num lock key. FL_KP - One of the keypad numbers; use FL_KP + n for number n. FL_KP_Enter - The enter key on the keypad. FL_F - One of the function keys; use FL_F + n for function key n. FL_Shift_L - The lefthand shift key. FL_Shift_R - The righthand shift key. FL_Control_L - The lefthand control key. FL_Control_R - The righthand control key. FL_Caps_Lock - The caps lock key. FL_Meta_L - The left meta/Windows key. FL_Meta_R - The right meta/Windows key. FL_Alt_L - The left alt key. FL_Alt_R - The right alt key. FL_Delete - The delete key.
That's it! Your ready to develop your own interfaces using FLTK.