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:
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); color2(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);
These methods are provided for subclasses to use.
uchar Fl_Widget::type() const;
void Fl_Widget::type(uchar);
Fltk does not use RTTI (Run Time Typing Infomation), to enhance portability. But this may change in the near future if RTTI becomes standard everywhere.
If you don't have RTTI you can use the clumsy fltk mechanisim, by having type() have a unique value. These unique values must be greater than the symbol FL_RESERVED_TYPE (which is 100). Grep through the header files for "FL_RESERVED_TYPE" to find an unused number. If you make a subclass of Fl_Group you must use FL_GROUP+n, and if you make a subclass of Fl_Window you must use FL_WINDOW+n (in both cases n is in the range 1-7).
void Fl_Widget::set_flag(SHORTCUT_LABEL);
int Fl_Widget::test_shortcut() const;
static int Fl_Widget::test_shortcut(const char *);
The second version lets you do this test to an arbitrary string.
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(uchar 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,uchar align) const ;
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:
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 widget! } return 1; case FL_SHORTCUT: if (Fl::event_key() == 'x') {do_callback(); return 1;} return 0; default: return 0; } } }
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.
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 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.
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.
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:
There are two reasons to make a subclass of Fl_Window. First, you
may want to make a widget that is an entire window. In that case,
just override the draw() and handle() routines exactly like you would
do for a subclass of Fl_Widget. The only
visible difference is that you should ignore x() and y() when looking
at event postitions or drawing graphics.
The other reason for making a subclass of Fl_Window is to change X
stuff, such as the visual or background pixel or any of the other
complexities that X sometimes requires. There are then several
virtual functions you must override to change how your window is
created, destroyed, and drawn. This entire section is very subject
to change in future versions!
Return the Fl_Window that corresponds to the given xid, or
null if not found. This uses a cache so it is slightly faster than
iterating through the windows yourself.
virtual void Fl_Widget::draw()
fl_clipped
and fl_current_clip
and skipping invisible parts.
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.
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).
class MyClass : public Fl_Group {
Fl_Button the_button;
Fl_Slider the_slider;
...
};
MyClass::MyClass(int x,int y,int w,int h) :
Fl_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!
}
void MyClass::slider_cb(Fl_Widget* v, void *) { // static method
((MyClass*)(v->parent())->slider_cb();
}
void MyClass::slider_cb() { // normal method
use(the_slider->value());
}
int MyClass::handle(int event) {
if (Fl_Group::handle(event)) return 1;
... handle events that children don't want ...
}
int MyClass::draw() {
Fl_Widget*const* a = array();
if (damage()==1) { // 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
}
}
}
void Fl_Group::draw_outside_label(Fl_Widget&) const;
Draw the labels that are not drawn by draw_label(). If you want more control over the
label positions you might want to call child->draw_label(x,y,w,h,a).
void Fl_Group::draw_child(Fl_Widget&);
This will force the child's damage() bits all to one and call draw()
on it, then clear the damage(). You should call this on all children
if a total redraw of your widget is requested, or if you draw
something (like a background box) that damages the child. Nothing is
done if the child is not visible() or if it is clipped.
void Fl_Group::update_child(Fl_Widget&);
Draws the child only if it's damage() is non-zero. You should call
this on all the children if your own damage is equal to 1. Nothing is
done if the child is not visible() or if it is clipped.
Making a subclass of Fl_Window
#include <FL/x.H>
Fl_X* Fl_X::set_xid(Fl_Window*, ulong, Colormap = fl_colormap);
When a Fl_Window is shown() there is a pointer stored in it to an
instance of the class Fl_X, which is defined in <FL/x.H>. This
static method creates an Fl_X and stores the passed xid and colormap
(fltk needs the colormap so it can set it when the window gets the
focus). After calling this Fl_Window::shown() returns true.
void Fl_X::make_xid(Fl_Window*, XVisualInfo* =
fl_visual, Colormap = fl_colormap);
This static method does the most onerous parts of creating an X window,
including setting the label, resize limitations, etc. It then does
set_xid with this new window and maps the window.
ulong fl_xid(const Fl_Window*);
Returns the xid for a window, or zero if not shown().
Fl_Window* fl_find(ulong xid)
Virtual methods on Fl_Window
virtual void Fl_Window::show()
This creates the window on the window server. It will be something
similar to:
// these variables are often static and filled in only once by a class:
static XVisualInfo *visual;
static Colormap colormap;
void MyWindow::show() {
if (shown()) {Fl_Window::show(); return;} // you must do this!
fl_open_display(); // necessary if this is first window
if (!visual) {
visual = figure_out_visual();
colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen),
vis->visual, AllocNone);
}
fl_make_xid(this, visual, colormap);
}
virtual void Fl_Window::flush()
Cause the window to be updated, by making the server draw into it,
calling draw(), and then clearing the damage(). To do minimal update
it should resemble this:
void MyWindow::flush() {
fl_window = Fl_X::i(this)->xid;
fl_gc = a gc;
if (damage()&~6) {
// draw everything:
draw();
} else {
// Fl_X::i(this)->region is an X region to update, draw into it...
fl_set_region(Fl_X::i(this)->region);
draw();
Fl_X::i(this)->region = 0; // because fl_pop_clip deletes it
fl_pop_clip();
}
}
virtual void Fl_Window::hide()
Destroy the window server copy of the window. Usually you will
destroy contexts, pixmaps, or other resources used by the window, and
then call Fl_Window::hide() to get rid of the main window identified
by xid(). If you override this, you must also override the destructor
as shown:
void MyWindow::hide() {
if (mypixmap) {
XFreePixmap(fl_display,mypixmap);
mypixmap = 0;
}
Fl_Window::hide(); // you must call this
}
MyWindow::~MyWindow() {
hide();
}