Most commonly the fltk::Widget class is subclassed to make new types of controls.
You can also subclass other existing widgets to make a similar control with a different look or user-interface. For example, the check button widgets are all subclasses of fltk::Button since they all interact with the user via a mouse button click. The only difference is the code that draws the face of the button. The class fltk::Valuator provides useful methods for storing a single floating-point "value" and is subclassed for a number of control widgets.
fltk::Widget has only four virtual methods, and overriding some or all of these may be necessary.
The constructor should have the following arguments:
MyClass(int x, int y, int w, int h, const char *label = 0);
This will allow the class to be used in FLUID without problems.
The constructor must call the constructor for the base class and pass the same arguments:
MyClass::MyClass(int x, int y, int w, int h, const char *label) : fltk::Widget(x, y, w, h, label) { // do initialization stuff... }
fltk::Widget's protected constructor sets x(), y(), w(), h(), and label() to the passed values and initializes the other instance variables to:
style(default_style); callback(default_callback,0); image(0); tooltip(0); shortcut(0); flags(0); // active, visible, fltk::ALIGN_CENTER type(0); set_damage(fltk::DAMAGE_ALL); layout_damage(fltk::LAYOUT_DAMAGE); when(fltk::WHEN_RELEASE);
The virtual method int fltk::Widget::handle(int event) is called to handle each event passed to the widget. It can:
Here is a sample handle() method for a widget that acts as a pushbutton and also accepts the keystroke 'x' to cause the callback:
int MyClass::handle(int event) { switch(event) { case fltk::PUSH: highlight = 1; redraw(); return 1; case fltk::DRAG: { int t = fltk::event_inside(this); if (t != highlight) { highlight = t; redraw(); } } return 1; case fltk::RELEASE: if (highlight) { highlight = 0; redraw(); do_callback(); // never do anything after a callback, as the callback // may delete the widget! } return 1; case fltk::SHORTCUT: if (fltk::event_key() == 'x') { do_callback(); return 1; } return 0; default: return 0; } }
You must return non-zero if your handle() method uses the event. If you return zero it indicates to the parent widget that it can try sending the event to another widget.
damage() contains the bitwise-OR of all the redraw(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. If the flag fltk::DAMAGE_EXPOSE is on in damage() then the widget is expected to draw every pixel inside it's bounding box. If you wish to simulate a non-square widget, you should call fltk::Group::draw_group_box() with appropriate clipping.
Expose events (and the method expose(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 fltk::not_clipped(x,y,w,h) or fltk::clip_box(...) and skipping invisible parts.
FLTK provides a large number of basic drawing functions. And fltk::Widget provides several convienent drawing methods.
layout_damage() will have the reason that layout() is being called. This can be fltk::LAYOUT_X, fltk::LAYOUT_Y, fltk::LAYOUT_W, fltk::LAYOUT_H or fltk::LAYOUT_DAMAGE if relayout() was called, or any combination of these.
You must call the base class implementation to get the layout_damage() bits turned off.
For composite widgets the bit fltk::LAYOUT_CHILD will be turned on to indicate that a child widget needs to have layout() called. Your implementation must call this.
layout() should call redraw() if it determines that the display needs to be redrawn.
A "composite" widget contains a set of child widgets and makes it look like a single large widget. fltk::Group is the main composite widget widget class in FLTK, and all of the other composite widgets (fltk::Pack, fltk::Scroll, fltk::Tabs, fltk::Tile, and fltk::Window) are subclasses of it.
For most uses the set of child widgets is fixed by the composite widget. Instances of the child widgets may be included in the parent:
class MyClass : public fltk::Group { fltk::Button the_button; fltk::Slider the_slider; ... };The constructor has to initialize these instances. They are automatically add()ed to the group, since the fltk::Group constructor does begin(). Don't forget to call end() or use the fltk::End pseudo-class:
MyClass::MyClass(int x, int y, int w, int h) : fltk::Group(x, y, w, h), the_button(x + 5, y + 5, 100, 20), the_slider(x, y + 50, w, 20) { ...(you could add dynamically created child widgets here)... end(); // don't forget to do this! }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:
void MyClass::slider_cb(fltk::Widget* v, void *) { // static method ((MyClass*)(v->parent())->slider_cb(); } void MyClass::slider_cb() { // normal method use(the_slider->value()); }If you make the handle() method, you can quickly pass all the events to the children using the fltk::Group::handle() method. You don't need to override handle() if your composite widget does nothing other than pass events to the children:
int MyClass::handle(int event) { if (fltk::Group::handle(event)) return 1; ... handle events that children don't want ... }If you override draw() you need to draw all the children. If redraw() or damage() is called on a child, damage(fltk::DAMAGE_CHILD) is done to the group, so this 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:
int MyClass::draw() { fltk::Widget *const*a = array(); if (damage() == fltk::DAMAGE_CHILD) { // only redraw some children for (int i = children(); i --; a ++) update_child(**a); } else { // total redraw ... draw background graphics ... // now draw all the children atop the background: for (int i = children_; i --; a ++) { draw_child(**a); draw_outside_label(**a); // you may not want to do this } } }fltk::Group provides some protected methods to make drawing easier:
Subclassing fltk::Windowis almost exactly like subclassing fltk::Widget, and in fact you can easily switch a subclass back and forth. Warning: fltk::Window is a subclass of fltk::Group so make sure your constructor calls end() (unless you actually want children added to your window).
You may also want to subclass fltk::Window in order to get access to different visuals or to change other attributes of the windows. See the X-specific documentation for more information.