Making a subclass of Fl_Widget

Your subclasses can directly descend from Fl_Widget or any subclass of Fl_Widget. Fl_Widget has only four virtual methods, and overriding some or all of these may be necessary.

Parts of this document:

Constructing your Fl_Widget

I recommend your constructor be of this form:

    Class(int x, int y, int w, int h, const char* label = 0);

This will allow the class name to be typed into fluid and it will produce the correct call.

The constructor must call the constructor for the base class and pass the same arguments. Fl_Widget's protected constructor sets x(), y(), w(), h(), and label() to the passed values and initializes the other instance variables to:

    type(0);
    box(FL_NO_BOX);
    color(FL_GRAY);
    selection_color(FL_GRAY);
    labeltype(FL_NORMAL_LABEL);
    labelstyle(FL_NORMAL_STYLE);
    labelsize(FL_NORMAL_SIZE);
    labelcolor(FL_BLACK);
    align(FL_ALIGN_CENTER);
    callback(default_callback,0);
    flags(ACTIVE|VISIBLE);

Protected methods of Fl_Widget

These methods are provided for subclasses to use.

uchar Fl_Widget::type() const;
void Fl_Widget::type(uchar);

void Fl_Widget::set_flag(SHORTCUT_LABEL);

int Fl_Widget::test_shortcut() const;
static int Fl_Widget::test_shortcut(const char *);

void Fl_Widget::x(short);
void Fl_Widget::y(short);
void Fl_Widget::w(short);
void Fl_Widget::h(short);

void Fl_Widget::damage(uchar mask);

void Fl_Widget::damage(uchar mask,int x,int y,int w,int h);

void Fl_Widget::clear_damage(uchar value = 0);

uchar Fl_Widget::damage()

void Fl_Widget::set_visible();
void Fl_Widget::clear_visible();

void Fl_Widget::draw_box() const ;

void Fl_Widget::draw_box(Fl_Boxtype b,ulong c) const ;

void Fl_Widget::draw_label() const ;

void Fl_Widget::draw_label(int x,int y,int w,int h) const ;

void Fl_Widget::draw_label(int x,int y,int w,int h,Fl_Align align) const ;

virtual int Fl_Widget::handle()

The virtual method int Fl_Widget::handle(int event) is called to handle each event passed to the widget. It can:

Events are identified the small integer argument. Other information about the most recent event is stored in static locations and aquired by calling Fl::event_*(). This other information remains valid until another event is read from the X server.

Here is a sample Fl_Widget::handle(), for a widget that acts as a pushbutton and also accepts the keystroke 'x' to cause the callback:

You must return non-zero if your handle() method used the event. If you return zero it indicates to the parent that it can try sending another widget the event.

It looks like it is best to make the handle() method public.

virtual void Fl_Widget::draw()

The virtual method Fl_Widget::draw() is called when fltk wants you to redraw your widget. It will be called if and only if damage() is non-zero, and damage() will be cleared to zero after it returns. draw() should be declared protected, so that subclasses may call it but it can't be called from non-drawing code.

damage() contains the bitwise-or of all the damage(n) calls to this widget since it was last drawn. This can be used for minimal update, by only redrawing the parts whose bits are set. Fltk will turn all the bits on if it thinks the entire widget must be redrawn (for instance due to an expose event). It is easiest to program to handle this by pretending a bit (usually damage()&128) draw the non-minimal-update parts of your widget (such as the box()).

Expose events (and the above damage(b,x,y,w,h)) will cause draw() to be called with fltk's clipping turned on. You can greatly speed up redrawing in some cases by testing fl_clipped and fl_current_clip and skipping invisible parts.

The functions you can use to draw are described in <FL/fl_draw.H> or any of the protected Fl_Widget::draw_* methods described above.

virtual void Fl_Widget::resize(int,int,int,int);

This is called when the widget is being resized or moved. The arguments are the new position, width, and height. x(), y(), w(), and h() still return the old size. You must call resize() on your base class with the same arguments to get the widget size to actually change.

This should not call redraw(), at least if only the x() and y() change. This is because group objects like Fl_Scroll may have a more efficient way of drawing the new position.

It may be useful to refer to the size the widget was constructed at, these are stored in Fl_Widget::ix(), iy(), iw(), and ih().

Resize should be declared public.

virtual Fl_Widget::~Fl_Widget();

We all know why the destructor must be virtual don't we? Don't forget to make it public.

Making a Composite/Group Widget

A "composite" widget contains one or more "child" widgets. To do this you should subclass
Fl_Group (it is possible to make a composite object that is not a subclass of Fl_Group, but this is very difficult).

Instances of the child widgets may be included in the parent:

The constructor has to initialize these instances. They are automatically add()ed to the group, since the Fl_Group constructor does begin(). Don't forget to call end():

The child widgets need callbacks. These will be called with a pointer to the children, but the widget itself may be found in the parent() pointer of the child. Usually these callbacks can be static private methods, with a matching private method:

If you make the handle() method, you can quickly pass all the events to the children (notice that you don't need to override handle() if your composite widget does nothing other than pass events to the children):

If you override draw() you need to draw all the children. If redraw() or damage() is called on a child, damage(1) is done to the group. Thus the 1 bit of damage() can be used to indicate that a child needs to be drawn. It is fastest if you avoid drawing anything else in this case:

Fl_Group provides some protected methods to make drawing easier:

void Fl_Group::draw_outside_label(Fl_Widget&) const;

void Fl_Group::draw_child(Fl_Widget&);

void Fl_Group::update_child(Fl_Widget&);

Making a subclass of Fl_Window

You may want your widget to be a subclass of Fl_Window. This can be useful if your widget wants to occupy an entire window, and can also be used to take advantage of system-provided clipping, or to work with a library that expects a system window id to indicate where to draw.

Subclassing Fl_Window is almost exactly like subclassing Fl_Widget, in fact you can easily switch a subclass back and forth. Watch out for the following differences:

  1. Fl_Window is a subclass of Fl_Group so make sure your constructor calls end() (unless you actually want children added to your window).
  2. When handling events and drawing, the lower-left corner is at 0,0, not x(),y() as in other Fl_Widgets. For instance, to draw a box around the widget, call draw_box(0,0,w(),h()), rather than draw_box(x(),y(),w(),h()).

You may also want to subclass Fl_Window in order to get access to different X visuals or to change other X attributes of the windows, See here for details.

(back to contents)