Making a subclass of Fl_Object

Your subclasses can directly descend from Fl_Object or any other subclass of Fl_Object. There are only four virtual methods that you can override to change how FL interacts with your object. They are described quickly here:

virtual int handle(int event);

virtual void draw();

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

virtual ~Fl_Object();

Protected methods of Fl_Object

These methods are provided for subclasses to use.

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

uchar type() const;
void type(uchar);

uchar flags() const;
void set_flag(int c);
void clear_flag(int c);
enum {ACTIVE=1, VISIBLE=2, SHORTCUT_LABEL=64, CHANGED=128};

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

void x(int);
void y(int);
void w(int);
void h(int);

void damage(uchar mask);

void clear_damage(uchar value = 0);

uchar damage()

void set_visible();
void clear_visible();

int Fl_Object::handle()

The method int handle(int event) is called to handle each event passed to the object. 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 handle(), for an object that acts as a pushbutton and also accepts the keystroke 'x' to cause the callback:

int Fl_Pushbutton::handle(int event) {
  switch(event) {
    case FL_PUSH:
      highlight = 1; redraw();
      return 1;
    case FL_DRAG:
      {int t = Fl::event_inside(this);
      if (t != highlight) {highlight = t; redraw();}}
      return 1;
    case FL_RELEASE:
      if (highlight) {
	highlight = 0; redraw();
        do_callback();
	// never do anything after a callback, so that the callback
	// may delete the object!
      }
      return 1;
    case FL_SHORTCUT:
      if (Fl::event_key() == 'x') {do_callback(); return 1;}
      return 0;
    default:
      return 0;
    }
  }
}

If your object has children objects, it is your responsibility to call handle() on them, after deciding which object to send the event to, and return their return value. For this reason handle() is a public method. You also need to set Fl::belowmouse() or Fl::focus() if you want these events passed to the child.

The return value can be used to communicate information to the parent object. In general non-zero means you understood and used the event, and zero means nothing was done. A return value of zero means the parent object is free to try sending the event to a different object.

Usually it is not useful to pass events to your base class by calling their handle() routine.

void Fl_Object::draw()

The virtual method draw() is called when Fl wants you to redraw your object. It should completely update your object. It will be called if damage() is non-zero, and damage() will be cleared to zero after it returns.

The functions you can use to draw are described in <FL/fl_draw.H>.

You can also call some protected methods on Fl_Object. Most of these are shortcuts for things that can be accomplished with the fl_draw calls:

void draw_box() const ;

void draw_box(uchar b,ulong c) const ;

void draw_label() const ;

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

Minimal Update

damage() contains the bitwise-or of all the damage(n) calls to this object since it was last drawn. You can use this for minimal update, by assigning meanings to the bits and using damage(n) rather than redraw() from inside your handle() method.

The redraw() method just does damage(-1), that is it turns all the bits on. The best way to handle this is to pick a bit (such as damage()&128) to cause all non-minimul-update parts of your object (such as the box()) to redraw.

Damage from the window system will also turn all the bits on, but the static variable fl_clipped will be true, and output will be clipped to the rectangle in the static structure fl_current_clip. You can use this to avoid calculating drawings that will not be seen.

Drawing children objects

If your object has children objects, your draw() routine must draw them. This is a bit clumsy to allow for minimal update. Calling redraw() or damage(n) on a child will also do damage(1) on all parent objects. Thus the 1 bit in damage() indicates that a child needs to be redrawn.

The following methods on Fl_Object are designed to be called by a parent object as part of that parent's draw() method. You should not call them in any other context:

How to use these methods:

You must reserve the '1' bit of damage() to indicate that a child object needs to be drawn. If possible, you should not do anything other than draw children when damage()==1.

If any other bits of damage() are on, then your object itself needs to be redrawn. If in the course of redrawing yourself you obscure part of a child, call child->redraw_child(). This will draw the child if it is not clipped and if it does not have damage() set (this is so when you call the child again below it does not draw it twice). If you know you have only obscured a portion of the child it may be faster to use fl_clip to clip to only that area.

If all the bits of damage() are on, then your whole object is clobbered. This is in fact the state you will be in if your object is called by redraw_child() from the parent. You must draw everything, and then call child->redraw_child() on every child.

If the '1' bit in damage() is set, then at some point in your drawing code, usually at the end, you must call child->draw_child() on every child. If the child is not clipped and has damage() set, it will redraw it.

If you are interested in the labels of the child objects, you must call child->draw_outside_label() on each of them. You can do this whenever it is appropriate. Or grab the labels and print them with your own mechanism.

(back to contents)