Beginner
fltk_homepageTutorial
version 1.1

Index
News
Who is this website for?
Prerequisite
Why use FLTK?
Getting the Software
Goto FLTK Basics
Flash video ! 
Simple Window Function
Widget Label Pitfall  (NEW)
Simple Window with widgets that talk to each other
Two widgets talking  (NEW)
Simple Inherited Window
Events (NEW)
More sections to come in the future
About me


News
Date: Jan 4/05
This tutorial has been updated. Cleaned up code and explanations in all sections. More examples and screenshots. Updated to match FLTK roadmap. New Sections: Widget Label Pitfall, Two widgets Talking.
Under construction: Events, Layouts, Browser, Makefiles

Date: Jan 1/05
Released Fl_RPNCalc version 1.1

Date: Sept 7/04
Check out my latest contribution. Fl_RPNCalc 1.0 is a simple RPN Calculator with keyboard numpad functionality.



Who is this website for?

This website is for C++ programmers who wish to start coding GUI (Graphical User Interface) applications using FLTK.  The documentation of FLTK is very good. However, I found the tutorial examples went from very easy (Hello World) to a little too diffucult (editor.cxx). So this site is intended as a follow up to  FLTK Basics. However, there is new documentation for FLTK 2.0 (Beta)  which is also an excellent resource. It is my hope that when you finish this tutorial you will be able to code your own FLTK applications with pleasure. O
Enjoy!



Prerequisite:

I assume you already have somewhat of a foundation in C++ programming. Most importantly you need to understand classes, inheritance, pointers and dynamic memory allocation before you can attempt to learn GUI programming.

Here are some C++ tutorial sites:

Thinking In C++ 2nd Edition by Bruce Eckel (Free Online Book)
http://cplus.about.com/library/blcplustut.htm
http://www.cplusplus.com/doc/tutorial/

top


Why use FLTK as opposed to other GUI toolkits?


- it's Free Open Source Software under the GNU LGPL.
  See the FLTK license agreement for more details
- it's efficient and straightforward
- it uses C++
- it makes small statically linked stand alone executables that are fast
- it's cross platform with Linux/Unix, Windows and MacOSX (write once compile anywhere)
- it supports OpenGL
- it has a graphical user interface builder called FLUID
- it's fun and easy to learn
top


Getting the Software:

Linux Install:

- Download FLTK software from www.fltk.org  (get latest stable version 1.1.x)
- Install it with the following commands in a console
        tar -xvzf fltk-1.1.6-source.tar.gz
        cd fltk-1.1.6
       ./configure
       make
       #make install

Note: if you download the .tar.bz2  file use   -xvjf   instead of  -xvzf
Linux has lots and lots of wonderful text editors and IDE's. I personally like Anjuta and gedit.
"#make install" will put files in /usr/local/ and its subdirectories.
Also, you need the X development libraries on your system. If you don't, ./configure will fail. Most linux distros come with X development libraries installed by default.

                   =============================================


Windows Install:


Option 1

- Get the latest version of (Dev-C++ with MinGW) from http://www.bloodshed.net
- Then  use the update feature to get the FLTK Devpak (get the latest 1.1.x)
- Or Download DevPak FLTK1 from FLTK.net
- If this DevPak from fltk.net does not work properly try the older one below.
- The older one is 1.1.4 from this sourceforge server
- FLTK2 Devpak is for version 2.0. Although it's already pretty stable this tutorial is about 1.1.x.

-Then create a new FLTK gui project. You should automatically have the "Hello World" code.



Option 2

- Download FLTK software from www.fltk.org 
- Get the latest MinGW, MSYS and msysDTK executable install files from here at sourceforge

1)  MinGW-3.x.x-x.exe  (32 bit Windows)
2)  MSYS-1.x.x.exe         (32 bit Windows)
      - Follow the MSYS post install script instructions carefully! (Note: forward slash / )
3)  msysDTK-1.x.x.exe  (32 bit Windows)


- Install them in the same order 1,2,3
- Launch MSYS
- Then in MSYS type the following

        tar -xvzf fltk-1.1.6-source.tar.gz

        cd fltk-1.1.6
       ./configure
       make
       make install

- Use your favorite editor to write code
- Compile from the MSYS console using the handy "fltk-config --compile" script
- If you don't have a good Windows editor then you can use Dev-C++ without MinGW
- Note: MSYS comes with Vim for all you vi addicts out there! Newbies beware: Vim text editor takes time to learn. But once people spend the time to learn it, they seem to really like it. I think it's an acquired taste.

Option 1 is easier but I personally recommend Option 2 for a couple of good reasons. Firstly, you will learn to compile things by hand from a shell console. This might encourage you to learn console commands (the true power behind Linux) and maybe even makefiles. You can always tell a Linux pro because they usually prefer the command line for getting work done. Secondly, you will also get an entire directory full of example demo programs using most, if not all, FLTK widgets. This is the /test directory, which you will NOT get from the Devpak in Option 1.

top



Goto FLTK Basics

I don't want to duplicate too much stuff from the FLTK documentation, so at this point goto the above link. Read the section and come back when your finished. See you soon.
top


Flash Video !

This section has a 13 minute Flash video you can watch using any browser with a shockwave flash plugin. I have tried it with Mozilla, Firefox, Konqueror and Opera browsers. I don't recommend anyone use IE (Internet Explorer) browser anytime. Firefox is what I recommend to everyone. The author of the video is Greg Ercolano. I have his permission to mirror the video. He has also contributed Fl_Table widget to the project and is an active member of the FLTK community. I experienced a real WOW! factor when I first saw it. Hope you do too.

Download the "Hello World" video. fltk_video.zip (6.7 M) contains 4 files

Greg Ercolano has made another great flash video. This time on FLUID (FLTK User Interface Designer)
Check out both videos here.

Note: the first video is zipped because I would prefer you download it once and have the ability to watch it multiple times from your own hard drive. Instead of streaming it from this site everytime. Unzip the file and its contents then open tutorial-fltk-hello.html with your browser. All 4 files must be in the same directory. There are no viruses/malware in these files. But if you are a paranoid MS Windows user and don't like downloading anything. Then you can stream it from Greg's site.
Make sure you have flash support in your browser and don't forget turn on your speakers. Note for Linux users: "aumix" may help set your mixer settings if you can't hear any sound.

top


Simple Window Function

Okay time to look at some code. Let's look at a way to make a window and a simple button. You should be able to copy/paste the code into an editor, save it then compile it in a console/MSYS with the convenient script

fltk-config  --compile  myprogram.cpp 

where myprogram.cpp is the name of the file.  fltk-config script is installed with FLTK.


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
using namespace std;

//--------------------------------------------
void but_cb( Fl_Widget* o, void*  ) {
   Fl_Button* b=(Fl_Button*)o;
   b->label("Good job"); //redraw not necessary
 
   b->resize(10,150,140,30); //redraw needed
   b->redraw();
}

//-------------------------------------------- 
int main() {

    Fl_Window win( 300,200,"Testing" );
    win.begin();
       Fl_Button but( 10, 150, 70, 30, "Click me" );
    win.end();
    but.callback( but_cb );
    win.show();
    return Fl::run();
}


Before:                                                After:
ex_1a.png      ex_1b.png


Let's analyse this code

   Fl_Window win(300,200, "Testing");
creates a new window object setting the width, height and the name in the title bar. Note: this object is allocated from stack memory. So it will be deleted at the end of the main function scope.

   win.begin()
this line is optional but I recommend using it as it makes the code easier to read. It's purpose is to say whatever new widgets that are created will be added as children to the current Fl_Window that was just created. Until you reach win.end().

    Fl_Button but( 10, 150, 70, 30, "Click me");
this line creates a button with  input parameters (x, y, width, height, label) where x, y are the position in pixels relative to the top left corner (0,0). Note: this button is now a child of the parent window. This means that the button is the first child of the window with index 0.  See the Fl_Group for more info on children. Also, note that only objects derived from Fl_Group have this child parent relationship. Fortunately, Fl_Window is derived from Fl_Group. A BIG benefit of this structure is that you only need to destroy the Fl_Window (the parent). Doing so will automatically delete all the children of the window.  So when 'win' goes out of scope then 'but' will be deleted automatically. This is a very beautiful aspect of FLTK. This might not seem that important in this example since both win and but are allocated on the stack. But you will see later when objects are allocated on the heap, it's a godsend.

   win.end();
this line sets the current group to the parent of the window (which in this case is null since the window has no parent)

   but.callback(  but_cb );
This line is important. Callbacks are a means of executing code when an event occurs. This is the basis for GUI programming. Usually an event is a mouse click, keyboard pressed etc.. There is more to be said about events later in the Events section. However, for this example we wish to change some of the properties of the the button when a user clicks on it.

Remember 'but' is an Fl_Button which is derived from Fl_Widget. So 'but' can use its base classes callback member function.


void Fl_Widget::callback(Fl_Callback*, void* = 0)

The second parameter (void*) is for userdata (any data structure you wish to pass that the callback might need).
Notice the second parameter is optional. That's why our example works. More about userdata later.

'but_cb' is set as the name of our callback function using the .callback base class member function. 'but_cb' must accept an Fl_Widget* and a void* to qualify as a callback function. FLTK passes the address of the calling object to the Fl_Widget* input argument of the callback function.  In this case, a reference to the button 'but' is passed.  Notice that I cast the Fl_Widget*  'o'  to an Fl_Button* 'b'. I did not HAVE to do this since I only called member functions of the base class ie. (label, resize and redraw) on 'b'. However, it's good practise just in case I needed to call an Fl_Button specific method.

BTW it's good convention, for code readability, to end or start callback function names with 'cb'.

  win.show();
This line puts the window on the screen. In other words, it makes it visible.

  void but_cb( Fl_Widget* o , void* ) {
This line just declares our callback function. Note the missing void* variable. We don't need it here, since we passed null by default.

   return Fl::run();
This line is part of most GUI toolkits. It sends the program into the main event loop. In other words, the program waits for events to happen.  The function run() returns 0 and ends the program when all windows are closed or hidden.  Another quick and easy way to end the program is to call exit(0). More on this later.

Now the but_cb callback function.

   void but_cb( Fl_Widget* o, void* ) {
Even thought the widget is a button, the input arguments for a callback are Fl_Widget* and void*. We don't use the void* so it's left out.  Since Fl_Widget is the base class for an Fl_Button "o" will accept the button pointer.


   Fl_Button* b=(Fl_Button*)o;
This line casts the Fl_Widget pointer to an Fl_Button pointer. Note that in this particular case it is unneccessary as the methods called on "b" are all of the base widget class members. However, if we did need to call Fl_Button specific methods then we would definetely need this cast.



   b->label("Good job");
This line changes the displayed label of the button. Note: label() and value() are the only two widget members that automatically call redraw. Everything else requires a manual call of redraw(). As you will see in the next two lines. BTW widgets don't automatically store a copy of their label. They only store a pointer to a char array. More on this in the next section.

    b->resize(10,150,140,30);
This line resizes the button. The parameters indicate the button keeps it's position but the width is doubled from 70 to 140. What's important is that this member function will NOT redraw the widget. So you will not see the change unless redraw() is called. Hence, the next line.
     
    b->redraw();
This line redraws the widget. Needed after the resize(). Manually calling redraw only when needed is one of the reasons FLTK is so fast.





**  Widget Label Pitfall **

By default FLTK does not store a copy of any widget label in the widget object. It ONLY stores the const pointer to the label. So if the label is a string literal like "Good job" that is not a problem. But if it is a const pointer, the programmer must ensure that the pointer exists as long as the widget does. Because if the pointer goes out of scope before the widget does then when the widget gets redrawn the label will be undefined. For example take a look at the following tiny program. Lets call it labeltest.cc


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
using namespace std;

void butcb(Fl_Widget* o, void*){
    char newcap[]="changed";
    o->label(newcap);
}

int main(){
    Fl_Window win (200,100, "window");
    Fl_Button but (50,50,80,25,"caption");
    but.callback (butcb);
    win.end();
    win.show();
    return Fl::run();
}


Before:                               After moving window:
ex_2a.png       ex_2b.png

Now save it and compile it. So from a console.

"fltk-config --compile labeltest.cc"

Now run it. "./labeltest"

This program has a BIG problem. Click on the button. Then try forcing a redraw by moving the window or minimize/restore. Notice the garbage the label displays! FLTK is trying to deference a pointer which no longer exists. NOT GOOD. The original label for the button was "caption". The newcap char array goes out of scope when the butcb callback function is finished. However, the button continues to exist in the main scope. So how do we solve this dilemma? We could make our own class of widget which includes a copy of the label as part of the widget but this is now unneccessary. Because, version 1.1.6 and later of FLTK come with a copy_label function. If we change the line in the callback function from

o->label(newcap);

to

o->copy_label(newcap);

The problem disappears since the widget now stores an actual copy of the label. Fortunately, this behavior ONLY exists for labels.



Simple Window with widgets that talk to each other

One of the main reasons for this tutorial was that I wanted to show how widgets (buttons, input boxes, output boxes, etc..)  can communicate with each other in a window. Most of the examples in the documentation simply instantiate (create) a window object in a function and add some widgets to it. But this makes it difficult to send information from one widget to another.  Take a look at the following program. It's like the previous one except the widgets are created dynamically in a function. So they are allocated on the heap. Again try compiling it yourself. Note: this example shows a method which works but is NOT recommended.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <cstdlib>                   //for exit(0)
#include <string.h>
using namespace std;

void copy_cb( Fl_Widget* , void* );  //function prototypes
void close_cb( Fl_Widget* , void* );
void make_window();

  
int main() {

   make_window();
   return Fl::run();
}


void make_window() {
 
   Fl_Window* win= new Fl_Window(300,200, "Testing 2");
   win->begin();      
      Fl_Button*  copy = new Fl_Button( 10, 150, 70, 30, "C&opy"); //child 0   : 1st widget
      Fl_Button* close = new Fl_Button(100, 150, 70, 30, "&Quit"); //child 1    : 2nd widget
      Fl_Input*       inp = new Fl_Input(50, 50, 140, 30, "In");              //child 2 : 3rd widget
      Fl_Output*    out = new Fl_Output(50, 100, 140, 30, "Out");     //child 3   : 4th widget
   win->end();
   copy->callback(  copy_cb );
   close->callback( close_cb );
   win->show();
 }


void copy_cb( Fl_Widget* o , void* ) {

   Fl_Button* b=(Fl_Button*)o;
   Fl_Input* iw = (Fl_Input*) b -> parent() -> child(2);
   Fl_Output* ow = (Fl_Output*) b -> parent() -> child(3);
   ow->value( iw->value() );
}


void close_cb( Fl_Widget* o, void*) {

   exit(0);
}


----------------------------------------------------------------------------

When run this program should look like this

simple win 2



Notice the Copy and Quit have underlined letters. These buttons can be invoked by pressing ALT-c  and ALT-q. This was achieved by simply placing an "&" in front of the letter in the label parameter in the constructor.

This program just copies whatever is in "In" to "Out" when "Copy" is pressed. The purpose though is to show communicating widgets.  Let's look at the copy_cb callback function.

void copy_cb( Fl_Widget* o , void* ) {
   Fl_Button* b=(Fl_Button*)o;

Notice 'o' is an Fl_Widget input parameter but 'copy_cb' is an Fl_Button callback function, so 'o' refers to an Fl_Button.  Therefore, we declare an Fl_Button pointer called 'b' and cast 'o' to it.  Done.

Next comes the ugly looking communication.

   Fl_Input* iw =  (Fl_Input*) b ->parent()->child(2);
   Fl_Output* ow = (Fl_Output*) b ->parent()->child(3);
   ow->value( iw->value() );

Get the parent() of Fl_Button b which returns an Fl_Group*. That's good because child(2) is a member function of an Fl_Group, so no cast is required. Then child(2) returns an Fl_Widget* of the 3rd widget created (counting from zero).  This widget is an Fl_Input, so we need a cast to an Fl_Input* and set all that to Fl_Input* iw. The next line does the same thing but to the output widget. Finally, the last line gets the value from the input widget and sets it to the value of the output widget.

I must warn you that this is not a good way to do widget communication. For one thing it's ugly and hard to read. Second you must manually keep track of the widget indices (ie 0,1,2,3 etc..) Third, there is no range checking on the child(int n).

Also note: Who or what deletes the dynamically allocated window widget 'win'? Yes, FLTK will delete the children of the window when the window gets deleted or goes out of scope as in the first example. But how does the window get deleted. I don't call delete. The window was allocated on the heap so it does NOT go out of scope. So do we have a memory leak? Not really. We feably rely upon exit(0) to clean up for us. Not good programming. Keep this in mind. The solution is coming.

Let's look at another example. If you only need two widgets to talk then send the other widget as the void* userdata.  See next section.


Two Widgets Talking


This next program produces a slightly different GUI interface and it's coded to utilize the user data feature of callbacks. It copies whatever is in the input box to the label of the button.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <cstdlib>                   //for exit(0)
using namespace std;

void copy_cb( Fl_Widget* , void* );  //function prototypes
void close_cb( Fl_Widget* , void* );
void make_window();

  
int main() {

   make_window();
   return Fl::run();
}


void make_window() {
 
   Fl_Window* win= new Fl_Window(300,200, "Testing 2");
   win->begin();      
      Fl_Button*  copy = new Fl_Button( 50, 100, 140, 30, "change me");
      Fl_Button* close = new Fl_Button(100, 150, 70, 30, "&Quit");
      Fl_Input*       inp = new Fl_Input(50, 50, 140, 30, "In");            
   win->end();
   copy->callback(  copy_cb, inp );  //userdata is the inp pointer
   close->callback( close_cb );
   win->show();
 }


void copy_cb( Fl_Widget* o , void* v) {

   Fl_Button* b=(Fl_Button*)o;
   Fl_Input* i=(Fl_Input*)v;
   b->copy_label(i->value()); 
}


void close_cb( Fl_Widget* o, void*) {

   exit(0);
}



Before:                                                   After:
ex_4a.png          ex_4b.png

Here are the important lines in this program.

copy->callback( copy_cb, inp );

Notice in this line I send the Fl_Input* inp to the void* as the userdata in the callback function of the copy button.

Fl_Input* i = (Fl_Input*)v;
b->copy_label (i->value()); 

Notice I not only cast "b" to an Fl_Button but I also cast the void* v to an Fl_Input. Hence, I have access to both the calling button and the input widget. So I get the value() of the input and set it to the label of the button. Clean and simple.

However, what if we needed access to more than just two widgets in a callback?
Callback functions only take two specific arguments so we can't just keep adding input arguments to suit our needs. The key is to send everything we need with the void* userdata. There is a way to code fltk applications in just such a way. So that ALL the widgets can be accessible in ANY callback. The next section will show how to do it.

Also, if you are keen in your C++ skills you will have noticed that again we are relying exit(0) to clean up our allocated memory. I am not your mother but I will teach you how to clean up after yourself in the next section ;)



Simple Inherited Window

This next program produces a similar GUI interface but it's coded completely different. I recommend this method as the best.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
using namespace std;

//---------------------------------------------------
 
class SimpleWindow : public Fl_Window{
 
   public:
      SimpleWindow(int w, int h, const char* title );
      ~SimpleWindow();
      Fl_Button* copy;
      Fl_Button* quit;
      Fl_Input* inp;
      Fl_Output* out;
   
   private:
      static void cb_copy(Fl_Widget*, void*);
      inline void cb_copy_i();
 
      static void cb_quit(Fl_Widget*, void*);
      inline void cb_quit_i();
};

//----------------------------------------------------

int main (){
 
   SimpleWindow win(300,200,"SimpleWindow");
   return Fl::run();
}

//----------------------------------------------------

SimpleWindow::SimpleWindow(int w, int h, const char* title):Fl_Window(w,h,title){
   
   begin();
      copy = new Fl_Button( 10, 150, 70, 30, "C&opy");
      copy->callback( cb_copy, this );
    
      quit = new Fl_Button(100, 150, 70, 30, "&Quit");
      quit->callback(cb_quit, this);
  
      inp = new Fl_Input(50, 50, 140, 30, "Input:");
      out = new Fl_Output(50, 100, 140, 30, "Output:");
   end();
   resizable(this);
   show();
}

//----------------------------------------------------

SimpleWindow::~SimpleWindow(){}

//----------------------------------------------------

void SimpleWindow::cb_copy(Fl_Widget* o, void* v) {
 
   //SimpleWindow* T=(SimpleWindow*)v;
   //T->cb_copy_i();
   
   // or just the one line below
    ( (SimpleWindow*)v )->cb_copy_i();
}


void SimpleWindow::cb_copy_i() {

   out->value(inp->value()); 
}

//----------------------------------------------------

void SimpleWindow::cb_quit(Fl_Widget* , void* v) {

   ( (SimpleWindow*)v )->cb_quit_i();
}


void SimpleWindow::cb_quit_i() {

    hide();
}

//----------------------------------------------------



ex_5.png

Okay let's analyse this improved version.

The first thing you will notice is that I have created a class called SimpleWindow which is derived from an Fl_Window.  However, I have added public pointer members of all the widgets I want to add to my window. Since these pointers are public I can access them outside of the class if I need to. Now lets look at how callbacks are done in classes.

private:

   static void cb_copy (Fl_Widget*, void*);

   inline void cb_copy_i ();


//----------------------------------------------------------------
void SimpleWindow::cb_copy(Fl_Button* o, void* v) { 
 
  
( (SimpleWindow*)v )->cb_copy_i();

}

void SimpleWindow::cb_copy_i() {

   out->value ( inp->value() );   // Clean and simple
}

//----------------------------------------------------------------

These two functions are important. They go together. They are the method for having member function callbacks. First I would like to say that callbacks in a class can only be static. In other words the THIS pointer does not exist. The way to overcome this is by having two functions. Where the simple static function calls a second inlined member function of the class that DOES have the THIS pointer initialized. This second inlined function has the actual callback code. 
Plus, don't forget, since the second function is inlined there really is only one function call. So there is no penalty for making two functions.

Note the second function has an '_i' added to the end of it to denote that it's inlined. Also, the second inlined function doesn't really need any input arguments as it already has access to everything.
Also, notice I don't have to put a pointer in front of begin() or end() or show() etc... in the constructor as the THIS pointer is implicit.

Just in case you forgot about void* in C++, here is a little refresher.
--------------------------------------------------------------
Brief info on Void Pointers:
Some of you that are new to C++ may have not seen void pointers before.  Basically, a void* is a pointer that can point to anything. Usually pointers are typed, in other words, you know the type of data to which they point. But a void* has no type. We do some casting in the callback function to deal with this issue.

From another perspective, pointers usually know the size of the object to which they are pointing. But void pointers don't, they just contain the address. Therefore, one can never dereference a void*. Therefore, we must utilize some casting to do the job.
--------------------------------------------------------------

In the constructor of the SimpleWindow we use new to create an Fl_Button and then we set the callback info of the Button in the line:

copy->callback (cb_copy, this);

Now this line sets the static callback function name and the userdata which is passed. I pass the address of the class instance (the THIS pointer) as the userdata to the void* of the callback function. Therefore, I have access to the entire class in the callback with only the tiny overhead of passing an address!! So simple and powerful.


Get/Set methods
One aspect of FLTK which might take some getting use to is the get/set functions. They both have the same name but are overloaded with respect to their return and input parameters. Get functions have no input parameter, so inp->value() gets or returns the value of the widget (a const char* in this case). On the other hand, out->value(const char*) sets the value of the out object.  Also, compare how clean and efficient this sinlge line of communication is compared to the two ugly lines in the first example of talking widgets. No more messy counting children or casting. This is a consequence of making the wrapper class. 


   resizable(this);
This line allows the program window to be resized. However, I could have just as easily called  resizable(copy) which would make the window resizable also. The difference being that my copy button would have been the widget to resize both horizontally and vertically.  Whereas, in my case everything in the window is resizeable. Remember only one widget per group can be resizable. So if you want a certain layout behavior you need to add appropriate horizontal and vertical groups. This is copied from fltk general newsgroup date Jan 17 2004.

Marc R.J. Brevoort wrote:

Here are a few hints. Read them carefully then try again. Good luck!

- to make things more predictable, it helps to fill groups with
  widgets only in one direction: either horizontally or vertically.
  (this also helps explain the following hints).

- If you need to fill groups both horizontally and vertically,
  fill a group WITH GROUPS in one direction, then those 'sub'groups
  in the other direction.

- In a group, at most one widget can be set to "resizable".
  Attempts to setting several widgets to 'resizable' causes
  only the last one to be marked resizable.

- Setting a widget to "resizable" means that that widget can be
  resized BOTH horizontally and vertically, not that it is the
  only resizable widget in the group.

- all other widgets in the group may resize along proportionally to
  the size of the group, but only in one direction (if the group
  is populated horizontally, 'nonresizable' widgets only resize
  vertically, only the 'resizable' widget resizes in both directions.

- if a group is only resizable in one direction, only the resizable
  widget will resize, all other widgets will stay the way they are.

Hope this helps,

grtz
MRJB


Thank you Marc. I have printed this message and included it with my FLTK documentation. It's a keeper.

Finally the last line to analyse is

   
hide();  // which calls hide() on the SimpleWindow

You can quit your programs in one of two ways. One way is to call exit(0) and depend on your operating system to free all allocated memory. The other is to call hide() on all windows which will cause Fl::run() to return. Hence, SimpleWindow win goes out of scope and its destructor is called normally. One imporatant thing to keep in mind though is that if you have global objects then using hide() may not be a good idea as those objects will not have their destructors called since they were not created in the main function scope.  But I personally don't like declaring objects globally anyway. Note: calling exit(0) will not return Fl::run() but all memory WILL be freed by the OS.

Again be aware that I don't delete any of the dynamic objects created of the class in the destructor. This is because SimpleWindow is a sub-class of an Fl_Group and as such it has all of it's children destroyed by the virtual base class destructor. This is possible since the base class has the ability to iterate through all the children with child(int n) and children(). Who needs Java when you've got FLTK. ; )

Side note: I would like to post a bit of code Jason Bryan came up with on FLTK general newsgroup.

void fl_exit()
{
  while( Fl::first_window() )
    Fl::first_window()->hide();
}

This little function should make sure that all windows hide, therebye, ensuring that Fl::run() is returned and all destructors called properly. Thanks Jason.



Events

Events are what GUI programs respond to. In the FLTK docs they are explained here
. However, I always like to learn by example so I coded a small program to display almost every event. Here it is


 
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <iostream>
using namespace std;

class MyButton : public Fl_Button
{
     static int count;
public:
     MyButton(int x,int y,int w,int h,const char*l=0)
     :Fl_Button(x,y,w,h,l) {}
            
     int handle(int e)
     {
         int ret = Fl_Button::handle(e);
         cout<<endl<<count++<<" ******** button "<<label()<<" receives ";
         
     
         switch(e)
         {
             case FL_PUSH:
                cout<<"push"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_RELEASE:
                cout<<"release"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_ENTER:
                color(FL_CYAN);
                cout<<"enter"<<" event and returns:"<<ret<<endl;
                redraw();
             break;
                 
             case FL_LEAVE:
                color(FL_BACKGROUND_COLOR);
                cout<<"leave"<<" event and returns:"<<ret<<endl;
                redraw();
             break;
                 
             case FL_DRAG:
                cout<<"drag"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_FOCUS:
                cout<<"focus"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_UNFOCUS:
                cout<<"unfocus"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_KEYDOWN:
                cout<<"keydown"<<" event and returns:"<<ret<<endl;
             break;

             case FL_KEYUP:
                 if ( Fl::event_key() == shortcut() ){
                      box(FL_UP_BOX);
                    redraw();
                    ret=1; //return handled so keyup event stops
                 }         //being sent to ALL other buttons unecessarily
               
                 cout<<"keyup"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_CLOSE:
                cout<<"close"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_MOVE:
                cout<<"move"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_SHORTCUT:
                if ( Fl::event_key() == shortcut() ){
                    box(FL_DOWN_BOX);
                    redraw();
                }
                cout<<"shortcut"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_DEACTIVATE:
                cout<<"deactivate"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_ACTIVATE:
                cout<<"activate"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_HIDE:
                cout<<"hide"<<" event and returns:"<<ret<<endl;
             break;
                 
             case FL_SHOW:
                cout<<"show"<<" event and returns:"<<ret<<endl;
             break;
             
             case FL_PASTE:
                cout<<"paste"<<" event and returns:"<<ret<<endl;
             break;
                 
             case  FL_SELECTIONCLEAR:
                cout<<"selectionclear"<<" event and returns:"<<ret<<endl;
             break;
                 
             case  FL_MOUSEWHEEL:
                cout<<"mousewheel"<<" event and returns:"<<ret<<endl;
             break;
                 
             case  FL_NO_EVENT:
                cout<<"no event"<<" and returns:"<<ret<<endl;
             break;
                 
                 
        
     }     
     return(ret);
     }
     
};

int MyButton::count=0;

void but_a_cb(Fl_Widget* w, void* v){
    cout <<endl<< "Button A callback!"<<endl;
}

void but_b_cb(Fl_Widget* w, void* v){
    cout <<endl<< "Button B callback!"<<endl;
}

void but_c_cb(Fl_Widget* w, void* v){
    cout <<endl<< "Button C callback!"<<endl;
}


int main()
{
    Fl_Window win(120,150);
    win.begin();

    MyButton but_a(10,10,100,25,"A");
    but_a.shortcut('a');
    but_a.callback(but_a_cb);

    MyButton but_b(10,50,100,25,"B");
    but_b.shortcut('b');
    but_b.callback(but_b_cb);

    MyButton but_c(10,90,100,25,"C");
    but_c.shortcut('c');
    but_c.callback(but_c_cb);

    win.end();
    win.show();
    return(Fl::run());
}



Okay lets compile this program.

(for Linux)
g++ -I/usr/local/include -I/usr/X11R6/include -o events events.cc -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libfltk.a -lm -lXext -lX11 -lsupc++


or

(for Linux or Windows)
fltk-config --compile events.cc

Here is what we get when we run it from a console (Note: Don't launch the program from Dev-C++ since you won't see the console output) You will need to use MSYS/MinGW in Windows or better yet use Linux.

  console1 


     event1


Under Construction..............




 

More sections to come in the future

As I learn more and more about FLTK I will add new sections to this site.




About Me

I teach senior high school Physics and Computer Programming (C++ of course) in British Columbia, Canada. Programming is my hobby and passion. I discovered FLTK in 2003 and I am really happy that Open Source/Free Software like this exists. FLTK has opened up a whole new world of programming possibilities.  I would like to thank the FLTK general newsgroup usual suspects like Bill, Mike, Matt, Greg, Jason, Marc, Alexey, Roman and Dejan. Sorry if I left anyone out. You guys are what keeps FLTK alive and well.  BRAVO!

Also please feel free to post comments, suggestions or rate this site on the FLTK links/bazaar tutorial page that links to this site. Or if you would like to send me an email. Send it to

fltk_beginner_tutorial at yahoo dot com

Robert Arkiletian


no epatents    w3c_html