B - Function Reference

When we created the window and box widgets and widgets inside the window. Here a single Fl_Box is created. The arguments to the constructor are a value for the box() property (most constructors do not have this), values for x(), y(), w(), h() to define the position and size of the box, and a value for label() to define the text printed in the box.

All the widgets have several attributes and there is a method for setting and getting the current value of each of them. box->labelsize(36) sets the labelsize() to 36. You could get the value with box->labelsize(). Often you have to set many properties, so you will be relieved to know that almost all of these methods are trivial inline functions.

labelfont() is set to a symbolic value which is compiled into a constant integer, 3 in this case. All properties that cannot be described by a single small number use a 1-byte index into a table. This makes the widget smaller, allows the actual definition of the property to be deferred until first use, and you can redefine existing entries to make global style changes.

labeltype(FL_SHADOW_LABEL) also stores a 1-byte symbolic value, in this case indicating a procedure to draw drop shadows under the letters should be called to draw the label.

The constructor for widgets adds them as children of the "current group" (usually a window). window->end() stops adding them to this window. For more control over the construction of objects, you can end() the window immediately, and then add the objects with window->add(box). You can also do window->begin() to switch what window new objects are added to.

window->show() finally puts the window on the screen. It is not until this point that the X server is opened. FLTK provides some optional and rather simple command-line parsing if you call show(argv,argc). If you don't want this, just call show() with no arguments, and the unused argument code is not linked into your program, making it smaller!

Fl::run() makes FLTK enter a loop to update the screen and respond to events. By default when the user closes the last window FLTK exits by calling exit(0). run() does not actually return, it is declared to return an int so you can end your main() function with "return Fl::run()" and outwit the stupid compiler made by a certain very large software company.

The following command compiles this program, assuming the FLTK library has been put in /usr/local/lib and the header files in /usr/local/include/FL:

The first thing your program should do is construct one or more trees of Fl_Widgets. The base widget of each of these is an Fl_Window widget. The constructors for widgets automatically add them as children of the most recent created window widget (use window->end() to stop this). Constructing the widgets does not require the display to be open and does not open it, unless you purposely open it to get information such as the width of a font.

Fl_Windows are displayed on the screen with Fl_Window::show(). For the first window you may also use Fl_Window::show(argc,argv) and FLTK will automatically parse some startup arguments such as -display.

Then the program repeatedly calls Fl::wait(). Each time "something happens" Fl::wait() returns, usually after a block of X events have been read and processed. It is often useful for a program to check global state after each event, and FLTK makes this easy by leaving the main loop under your control.

Each widget has a single "callback". This is a function that is called when something happens (such as the user pressing a button). FLTK avoids all the complexities of signals/slots by having only a single callback. Instead a when() method on the object selects when the callback is done (ie. when a slider is moved or when the mouse is released).

The callback is passed a pointer to the widget and a void* user_data field. This is redundant, as the user_data can be determined from the widget, but was done for XForms compatability and to make the same callbacks useful for menu items. Typically you want to turn the callback into a method on some C++ object. A simple way is to use the user_data as a pointer to the object. A more common but harder to understand way is to store the object in the parent widget's user_data field, since usually all the controls on a window are for the same object, this lets you use the user_data for an abitrary method argument.

To display graphic data, you must subclass either Fl_Window or Fl_Widget and define the virtual draw() method. This can use functions defined in <FL/fl_draw.H>, or can use system-specific calls such as Xlib. If the data being displayed changes, your main program calls the redraw() method on your widget, and FLTK will call draw() while waiting for the next event. Subclassing Fl_Window or Fl_Widget is so easy that I felt it unnecessary to provide the "canvas" widget that most toolkits have.

If your program needs to monitor another device (such as stdin) you can provide a callback routine for when it becomes ready, by using Fl::add_fd(i). If your program needs something to happen at regular intervals you can define a timeout callback with Fl::add_timeout(time).

Building a large hierarchy is made much easier with fluid (the Fast Light User Interface Designer). This is a program that lets you interactively design the widget layout and set all the properties visually. It outputs C++ source code that you compile and link with your program. All you have to write is the main loop and any callbacks. This chapter demonstrates the basics of FLTK programming with examples.

Compiling a FLTK Program

Include Files

Library Files

A "Hello, World" Program

Creating the Window

The Main Loop

FLTK example: ask.C

ask.C

#include <stdio.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>

int get_string(char*buffer, const char *from) {
  Fl_Window window(320,75);
  window.set_modal();
  Fl_Input input(60, 40, 250, 25,"Input:");
  input.value(buffer);
  Fl_Button cancel(60, 10,80, 25,"cancel");
  Fl_Return_Button ok(150, 10,80, 25,"OK");
  window.end();
  window.show();
  for (;;) {
    Fl::wait();
    Fl_Widget *o;
    while (o = Fl::readqueue()) {
      if (o == &ok) {
	strcpy(buffer, input.value());
	return 1;
      } else if (o == &cancel || o == &window) {
	return 0;
      }
    }
  }
}

int main(int argc, char **argv) {
  char buffer[128];
  if (get_string(buffer, argv[1])) {
    puts(buffer);
    return 0;
  } else {
    return 1; // exit with error
  }
}

Widgets don't need to have callback() set. The default callback puts a pointer to the widget on a "queue" from which it can later be read with Fl::readqueue(). This was done for Forms compatibility but it is useful for modal windows. In this example the "get_string" function puts up a modal window and loops until one of the buttons is pushed. Fl::wait() does exactly one cycle of what Fl::run() does repeatedly: it updates the screen and then waits for and responds to an event (or several events if they are all ready at the same time). It then returns, allowing the user program to check any state information it wants to after each group of events. One thing the user program can check is Fl::readqueue() which returns each object without a callback that was triggered. It returns null when the queue is empty. It is possible for more than one object to be on the queue (or the same object several times) so if your program wants to read the queue it should always read it until empty and ignore unrecognized widgets (don't look at them as they may have been deleted).

modal() on a window prevents any interaction with other program windows below it, and prevents the user from raising a program window above it (well, it tries, but X is broken). It won't make any difference in this program because there is only one window, but this allows the "get_string" function to be used as subroutine by a larger program and have the expected behavior.

This program also demonstrates that FLTK widgets may be constructed as C++ automatic objects (local variables). You have to be careful about destruction, however. Always make sure all automatic children are destructed before the container (by declaring the children after the container), since the destructor for a container will attempt to delete all remaining children, and you don't want to delete automatic objects.

[Next example]
[back to contents] FLTK example: button.C

button.C

#include <stdlib.h>
#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>

void beepcb(Fl_Widget *, void *) {
  printf("\007"); fflush(stdout);
}

void exitcb(Fl_Widget *, void *) {
  exit(0);
}

int main(int argc, char ** argv) {
  Fl_Window *window = new Fl_Window(320,65);
  window->begin();
  Fl_Button *b1 = new Fl_Button(20, 20, 80, 25, "Beep");
  b1->callback(beepcb,0);
  Fl_Button *b2 = new Fl_Button(120,20, 80, 25, "no op");
  Fl_Button *b3 = new Fl_Button(220,20, 80, 25, "Exit");
  b3->callback(exitcb,0);
  window->end();
  window->show(argc,argv);
  return Fl::run();
}

In this example we make some button widgets and make them do something through callbacks.

All widgets have a single callback() function. It is called in response to an event on that widget, exactly which event depends on the type of widget. The function takes two arguments: a pointer to the widget (you will usually need to cast this to the correct subclass) and a void* pointer to a piece of arbitrary user_data.

You don't have to give all the widgets a callback, as the "no op" b2 widget demonstrates. What these do is described in the next program.

[Next example]
[back to contents] FLTK methods

#include <FL/Fl.H>

You will have to include at least this header file in your main code so that you can call the methods described here.

Initialization

You can construct all your widgets (and menus and boxtypes and images and other FLTK types) without "initializing". The constructors do not require a connection to the X display. This makes it a lot easier, especially if your program has a mode where it does not use a gui, and guarantees that code you don't use is not linked in.

FLTK is usually "initialized" when you show() the first window. At this time the X display is opened and everything is set up so the calls described in the rest of this document work. A few other calls can open the X display, amoung them are fl_width() to measure the size of a font. Be careful that the following calls are done before the display is opened, if not you will get lots of strange X errors.

Most of these "initialization" calls are to get around stupid X things. I have tried to make these as simple to call as possible and they have no effect on systems which aren't as badly designed as X. But you should call them to make your program as portable as possible.

int Fl::visual(int)

int Fl::gl_visual(int)

void Fl::own_colormap();

void Fl::get_system_colors();

void Fl::background(uchar, uchar, uchar);

void Fl::foreground(uchar, uchar, uchar);

void Fl::background2(uchar, uchar, uchar);

int Fl::args(int argc, char** argv, int &i, int (*callback)(int,char**,int&)=0)

int Fl::arg(int argc, char** argv, int &i)

void Fl::args(int argc, char** argv)

const char* const Fl::help;

int Fl_Window::show(int argc, char** argv)

Running

After FLTK is "initialized" by calling show() on some window, you get FLTK to wait for and respond to events by calling the following methods:

int Fl::run()

int Fl::wait()

float Fl::wait(float time)

int Fl::check()

int Fl::ready();

void Fl::add_timeout(float t,void (*cb)(void*),void* v=0);
void Fl::remove_timeout(void (*cb)(void*), void* = 0);

void Fl::set_idle(void (*cb)());

void Fl::flush()

int Fl::damage()

Fl_Widget *Fl::readqueue();

Listening to other file descriptors (Unix only)

void Fl::add_fd(int fd, void (*cb)(int, void*), void* = 0);
void Fl::add_fd(int fd, int when, void (*cb)(int, void*), void* = 0);
void Fl::remove_fd(int);

    Add file descriptor fd to listen to. When the fd becomes ready for reading the callback is done. The callback is passed the fd and the arbitrary void* argument. Fl::wait() will return immediately after calling the callback.

    The second version takes a when bitfield, with the bits FL_READ, FL_WRITE, and FL_EXCEPT defined, to indicate when the callback should be done. This probably only works on Unix.

    There can only be one callback of each type for a file descriptor. Fl::remove_fd() gets rid of all the callbacks for a given file descriptor.

Exiting

When all windows are closed Fl::wait() and Fl::run() return zero. If your main() routine then returns the program exits. You can also call exit(0) at any time in your program. You do not need to do any cleanup code for FLTK. In particular you do not have to destroy any widgets you have created. FLTK also does not sneak any atexit functions in on you either. You will need to do #include <stdlib.h> to call exit().

To stop a window from closing, or conversely to make the closing of a particular window exit the program you must change the callback() function. Here is a typical use:

    static void main_window_cb(Fl_Widget*, void*) {
      if (document_changed()) {
        if (!fl_ask("Exit without saving changes?")) return;
        // window will not go away as hide() has not been called...
      }
      exit(0);
    }
    
    ...somewhere in main():
      main_window->callback(window_cb);
    

void (*Fl::warning)(const char*,...);
void (*Fl::error)(const char*,...);
void (*Fl::fatal)(const char*,...);

    FLTK will call these to print messages when unexpected conditions occur. By default they fprintf to stderr, and Fl::error and Fl::fatal call exit(1). You can override the behavior by setting the function pointers to your own routines.

    Supposedly Fl::warning means that there was a recoverable problem, the display may be messed up but the user can probably keep working (all X protocol errors call this). Fl::error means there is a recoverable error, but the display is so messed up it is unlikely the user can continue (very little calls this now). Fl::fatal must not return, as FLTK is in an unusable state (however your version may be able to use longjmp or an exception to continue, as long as it does not call FLTK again).

(back to contents) FLTK example: hello.C

hello.C

[Next example]
[back to contents]