diff options
Diffstat (limited to 'documentation/src/subclassing.dox')
| -rw-r--r-- | documentation/src/subclassing.dox | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/documentation/src/subclassing.dox b/documentation/src/subclassing.dox new file mode 100644 index 000000000..a13e0a011 --- /dev/null +++ b/documentation/src/subclassing.dox @@ -0,0 +1,531 @@ +/** + + \page subclassing 7 - Adding and Extending Widgets + + +This chapter describes how to add your own widgets or extend existing +widgets in FLTK. + +\section subclassing_subclassing Subclassing + +New widgets are created by <I>subclassing</I> an existing FLTK widget, +typically <tt>Fl_Widget</tt> for controls and <tt>Fl_Group</tt> for +composite widgets. + +A control widget typically interacts with the user to receive and/or +display a value of some sort. + +A composite widget widget holds a list of child widgets and handles moving, +sizing, showing, or hiding them as needed. <tt>Fl_Group</tt> is the +main composite widget widget class in FLTK, and all of the other composite +widgets (<tt>Fl_Pack</tt>, <tt>Fl_Scroll</tt>, <tt>Fl_Tabs</tt>, +<tt>Fl_Tile</tt>, and <tt>Fl_Window</tt>) are subclasses of it. + +You can also subclass other existing widgets to provide a different +look or user-interface. For example, the button widgets are all +subclasses of <tt>Fl_Button</tt> 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. + +\section subclassing_fl_widget Making a Subclass of Fl_Widget + +Your subclasses can directly descend from <tt>Fl_Widget</tt> or any +subclass of <tt>Fl_Widget</tt>. <tt>Fl_Widget</tt> has only four +virtual methods, and overriding some or all of these may be necessary. + +\section subclassing_constructor The Constructor + +The constructor should have the following arguments: + +\code +MyClass(int x, int y, int w, int h, const char *label = 0); +\endcode + +This will allow the class to be used in +<A href="fluid.html#FLUID">FLUID</A> +without problems. + +The constructor must call the constructor for the base class and +pass the same arguments: + +\code +MyClass::MyClass(int x, int y, int w, int h, const char *label) +: Fl_Widget(x, y, w, h, label) { +// do initialization stuff... +} +\endcode + +<tt>Fl_Widget</tt>'s protected constructor sets <tt>x()</tt>, <tt>y()</tt>, +<tt>w()</tt>, <tt>h()</tt>, and <tt>label()</tt> to the passed values +and initializes the other instance variables to: + +\code +type(0); +box(FL_NO_BOX); +color(FL_BACKGROUND_COLOR); +selection_color(FL_BACKGROUND_COLOR); +labeltype(FL_NORMAL_LABEL); +labelstyle(FL_NORMAL_STYLE); +labelsize(FL_NORMAL_SIZE); +labelcolor(FL_FOREGROUND_COLOR); +align(FL_ALIGN_CENTER); +callback(default_callback,0); +flags(ACTIVE|VISIBLE); +image(0); +deimage(0); +\endcode + +\section subclassing_protected Protected Methods of Fl_Widget + +The following methods are provided for subclasses to use: + +\li <A href="#clear_visible"><tt>Fl_Widget::clear_visible</tt></A> +\li <A href="#damage"><tt>Fl_Widget::damage</tt></A> +\li <A href="#draw_box"><tt>Fl_Widget::draw_box</tt></A> +\li <A href="#draw_focus"><tt>Fl_Widget::draw_focus</tt></A> +\li <A href="#draw_label"><tt>Fl_Widget::draw_label</tt></A> +\li <A href="#set_flag"><tt>Fl_Widget::set_flag</tt></A> +\li <A href="#set_visible"><tt>Fl_Widget::set_visible</tt></A> +\li <A href="#test_shortcut"><tt>Fl_Widget::test_shortcut</tt></A> +\li <A href="#type"><tt>Fl_Widget::type</tt></A> + +<A name="damage"></A> <!-- For old HTML links only ! --> +void Fl_Widget::damage(uchar mask) <br> +void Fl_Widget::damage(uchar mask, int x, int y, int w, int h) <br> +uchar Fl_Widget::damage() + +\par +The first form indicates that a partial update of the object is +needed. The bits in mask are OR'd into <tt>damage()</tt>. Your <tt> +draw()</tt> routine can examine these bits to limit what it is +drawing. The public method <tt>Fl_Widget::redraw()</tt> simply does +<tt> Fl_Widget::damage(FL_DAMAGE_ALL)</tt>, but the implementation of +your widget can call the private <tt>damage(n)</tt>. + +\par +The second form indicates that a region is damaged. If only these +calls are done in a window (no calls to <tt>damage(n)</tt>) then FLTK +will clip to the union of all these calls before drawing anything. + This can greatly speed up incremental displays. The mask bits are +OR'd into <tt>damage()</tt> unless this is a <tt>Fl_Window</tt> widget. + +\par +The third form returns the bitwise-OR of all <tt>damage(n)</tt> +calls done since the last <tt>draw()</tt>. + +\par +<I>When redrawing your widgets you should look at the damage bits to +see what parts of your widget need redrawing.</I> The <tt>handle()</tt> +method can then set individual damage bits to limit the amount of drawing +that needs to be done: +\code +MyClass::handle(int event) { + ... + if (change_to_part1) damage(1); + if (change_to_part2) damage(2); + if (change_to_part3) damage(4); +} + +MyClass::draw() { + if (damage() & FL_DAMAGE_ALL) { + ... draw frame/box and other static stuff ... + } + + if (damage() & (FL_DAMAGE_ALL | 1)) draw_part1(); + if (damage() & (FL_DAMAGE_ALL | 2)) draw_part2(); + if (damage() & (FL_DAMAGE_ALL | 4)) draw_part3(); +} +\endcode + +<A name="draw_box"></A> <!-- For old HTML links only ! --> +void Fl_Widget::draw_box() const <br> +void Fl_Widget::draw_box(Fl_Boxtype b, ulong c) const + +\par +The first form draws this widget's <tt>box()</tt>, using the +dimensions of the widget. The second form uses <tt>b</tt> as the box +type and <tt>c</tt> as the color for the box. + +<A name="draw_focus"></A> <!-- For old HTML links only ! --> +void Fl_Widget::draw_focus() const <br> +void Fl_Widget::draw_focus(Fl_Boxtype b, int x, int y, int w, int h) const + +\par +Draws a focus box inside the widgets bounding box. The second +form allows you to specify a different bounding box. + +<A name="draw_label"></A> <!-- For old HTML links only ! --> +void Fl_Widget::draw_label() const <br> +void Fl_Widget::draw_label(int x, int y, int w, int h) const <br> +void Fl_Widget::draw_label(int x, int y, int w, int h, Fl_Align align) const + +\par +This is the usual function for a <tt>draw()</tt> method to call to +draw the widget's label. It does not draw the label if it is supposed +to be outside the box (on the assumption that the enclosing group will +draw those labels). + +\par +The second form uses the passed bounding box instead of the widget's +bounding box. This is useful so "centered" labels are aligned with some +feature, like a moving slider. + +\par +The third form draws the label anywhere. It acts as though <tt> +FL_ALIGN_INSIDE</tt> has been forced on so the label will appear inside +the passed bounding box. This is designed for parent groups to draw +labels with. + +<A name="set_flag"></A> <!-- For old HTML links only ! --> +void Fl_Widget::set_flag(SHORTCUT_LABEL) + +\par +Modifies <tt>draw_label()</tt> so that '&' characters cause an underscore +to be printed under the next letter. + +<A name="set_visible"></A> <!-- For old HTML links only ! --> +<A name="clear_visible"></A> <!-- For old HTML links only ! --> +void Fl_Widget::set_visible() <br> +void Fl_Widget::clear_visible() + +\par +Fast inline versions of <tt>Fl_Widget::hide()</tt> and <tt> +Fl_Widget::show()</tt>. These do not send the <tt>FL_HIDE</tt> and <tt> +FL_SHOW</tt> events to the widget. + +<A name="test_shortcut"></A> <!-- For old HTML links only ! --> +int Fl_Widget::test_shortcut() const <br> +static int Fl_Widget::test_shortcut(const char *s) + +\par +The first version tests <tt>Fl_Widget::label()</tt> against the +current event (which should be a <tt>FL_SHORTCUT</tt> event). If the +label contains a '&' character and the character after it matches the key +press, this returns true. This returns false if the <tt>SHORTCUT_LABEL</tt> +flag is off, if the label is <tt>NULL</tt> or does not have a +'&' character in it, or if the keypress does not match the character. + +\par +The second version lets you do this test against an arbitrary string. + +<A name="type"></A> <!-- For old HTML links only ! --> +uchar Fl_Widget::type() const <br> +void Fl_Widget::type(uchar t) + +\par +The property <tt>Fl_Widget::type()</tt> can return an arbitrary 8-bit +identifier, and can be set with the protected method <tt>type(uchar t)</tt>. +This value had to be provided for Forms compatibility, but you can +use it for any purpose you want. Try to keep the value less than 100 +to not interfere with reserved values. + +\par +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. + +\par +If you don't have RTTI you can use the clumsy FLTK mechanisim, by +having <tt>type()</tt> use a unique value. These unique values must +be greater than the symbol <tt>FL_RESERVED_TYPE</tt> (which is 100). +Look through the header files for <tt>FL_RESERVED_TYPE</tt> to find an +unused number. If you make a subclass of <tt>Fl_Window</tt> +you must use <tt>FL_WINDOW + n</tt> (<tt>n</tt> must be in the +range 1 to 7). + +<A NAME="handle"></A> <!-- For old HTML links only ! --> +\section subclassing_events Handling Events + +The virtual method <tt>int Fl_Widget::handle(int event)</tt> is called +to handle each event passed to the widget. It can: + +\li Change the state of the widget. +\li Call + <A href="Fl_Widget.html#Fl_Widget.redraw"><tt>Fl_Widget::redraw()</tt></A> + if the widget needs to be redisplayed. +\li Call + <A href="Fl_Widget.html#Fl_Widget.damage"><tt>Fl_Widget::damage(n)</tt></A> + if the widget needs a partial-update (assuming you provide support for + this in your + <A href="#draw"><tt>Fl_Widget::draw()</tt></A> + method). +\li Call + <A href="Fl_Widget.html#Fl_Widget.do_callback"><tt>Fl_Widget::do_callback()</tt></A> + if a callback should be generated. +\li Call <tt>Fl_Widget::handle()</tt> on child widgets. + +Events are identified by the integer argument. Other information +about the most recent event is stored in static locations and aquired +by calling the +<A href="events.html#events"><tt>Fl::event_*()</tt></A> +functions. This information remains valid until another event is +handled. + +Here is a sample <tt>handle()</tt> method for a widget that acts as +a pushbutton and also accepts the keystroke 'x' to cause the callback: + +\code +int MyClass::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, as the callback + // may delete the widget! + } + return 1; + case FL_SHORTCUT: + if (Fl::event_key() == 'x') { + do_callback(); + return 1; + } + return 0; + default: + return Fl_Widget::handle(event); + } +} +\endcode + +You must return non-zero if your <tt>handle()</tt> method +uses the event. If you return zero, the parent widget will try +sending the event to another widget. + +<A NAME="draw"></A> <!-- For old HTML links only ! --> +\section subclassing_drawing Drawing the Widget + +The <tt>draw()</tt> virtual method is called when FLTK wants +you to redraw your widget. It will be called if and only if +<tt>damage()</tt> is non-zero, and <tt>damage()</tt> will be +cleared to zero after it returns. The <tt>draw()</tt> method +should be declared protected so that it can't be called from +non-drawing code. + +The <tt>damage()</tt> value contains the bitwise-OR of all +the <tt>damage(n)</tt> 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 on the +<tt>FL_DAMAGE_ALL</tt> bit if it thinks the entire widget must +be redrawn, e.g. for an expose event. + +Expose events (and the above <tt>damage(b,x,y,w,h)</tt>) will cause <tt> +draw()</tt> to be called with FLTK's <A href="drawing.html#clipping"> +clipping</A> turned on. You can greatly speed up redrawing in some +cases by testing <tt>fl_not_clipped(x,y,w,h)</tt> or <tt>fl_clip_box(...)</tt> +and skipping invisible parts. + +Besides the protected methods described above, FLTK provides a large +number of basic drawing functions, which are described +<A href="drawing.html#drawing">below</A>. + +\section subclassing_resizing Resizing the Widget + +The <tt>resize(int x, int y, int w, int h)</tt> method is called when +the widget is being resized or moved. The arguments are the new +position, width, and height. <tt>x()</tt>, <tt>y()</tt>, <tt>w()</tt>, +and <tt>h()</tt> still remain the old size. You must call <tt>resize()</tt> +on your base class with the same arguments to get the widget size to +actually change. + +This should <I>not</I> call <tt>redraw()</tt>, at least if only the <tt> +x()</tt> and <tt>y()</tt> change. This is because composite widgets like +<A href="Fl_Scroll.html#Fl_Scroll"><tt>Fl_Scroll</tt></A> +may have a more efficient way of drawing the new position. + +\section subclassing_composite Making a Composite Widget + +A "composite" widget contains one or more "child" widgets. +To make a composite widget you should subclass +<A href="Fl_Group.html#Fl_Group"><tt>Fl_Group</tt></A>. +It is possible to make a composite object that is not a subclass of +<tt>Fl_Group</tt>, but you'll have to duplicate the code in <tt>Fl_Group</tt> +anyways. + +Instances of the child widgets may be included in the parent: + +\code +class MyClass : public Fl_Group { + Fl_Button the_button; + Fl_Slider the_slider; + ... +}; +\endcode + +The constructor has to initialize these instances. They are automatically +<tt>add()</tt>ed to the group, since the Fl_Group constructor does +Fl_Group::begin(). +<I>Don't forget to call Fl_Group::end() or use the Fl_End pseudo-class:</I> + +\code +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! +} +\endcode + +The child widgets need callbacks. These will be called with a pointer +to the children, but the widget itself may be found in the <tt>parent()</tt> +pointer of the child. Usually these callbacks can be static private +methods, with a matching private method: + +\code +void MyClass::static_slider_cb(Fl_Widget* v, void *) { // static method + ((MyClass*)(v->parent())->slider_cb(); +} +void MyClass::slider_cb() { // normal method + use(the_slider->value()); +} +\endcode + +If you make the <tt>handle()</tt> method, you can quickly pass all the +events to the children using the <tt>Fl_Group::handle()</tt> method. +You don't need to override <tt>handle()</tt> if your composite widget +does nothing other than pass events to the children: + +\code +int MyClass::handle(int event) { + if (Fl_Group::handle(event)) return 1; + ... handle events that children don't want ... +} +\endcode + +If you override <tt>draw()</tt> you need to draw all the +children. If <tt>redraw()</tt> or <tt>damage()</tt> is called +on a child, <tt>damage(FL_DAMAGE_CHILD)</tt> is done to the +group, so this bit of <tt>damage()</tt> can be used to indicate +that a child needs to be drawn. It is fastest if you avoid +drawing anything else in this case: + +\code +int MyClass::draw() { + Fl_Widget *const*a = array(); + if (damage() == FL_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 need to do this + } + } +} +\endcode + +<tt>Fl_Group</tt> provides some protected methods to make drawing +easier: + +\li <A href="#draw_child">draw_child</A> +\li <A href="#draw_outside_label">draw_outside_label</A> +\li <A href="#update_child">update_child</A> + +<A name="draw_child"></A> <!-- For old HTML links only ! --> +void Fl_Group::draw_child(Fl_Widget&) + +\par +This will force the child's <tt>damage()</tt> bits all to one and call <tt> +draw()</tt> on it, then clear the <tt>damage()</tt>. 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 <tt>visible()</tt> or if it is +clipped. + +<A name="draw_outside_label"></A> <!-- For old HTML links only ! --> +void Fl_Group::draw_outside_label(Fl_Widget&) const + +\par +Draw the labels that are <I>not</I> drawn by <A href="#draw_label"><tt> +draw_label()</tt></A>. If you want more control over the label +positions you might want to call <tt>child->draw_label(x,y,w,h,a)</tt>. + +<A name="update_child"></A> <!-- For old HTML links only ! --> +void Fl_Group::update_child(Fl_Widget&) + +\par +Draws the child only if its <tt>damage()</tt> is non-zero. You +should call this on all the children if your own damage is equal to +FL_DAMAGE_CHILD. Nothing is done if the child is not <tt>visible()</tt> +or if it is clipped. + +\section subclassing_cutnpaste Cut and Paste Support + +FLTK provides routines to cut and paste 8-bit text (in the future this +may be UTF-8) between applications: + +\li <A href="Fl.html#Fl.paste"><tt>Fl::paste</tt></A> +\li <A href="Fl.html#Fl.selection"><tt>Fl::selection</tt></A> +\li <A href="Fl.html#Fl.selection_owner"><tt>Fl::selection_owner</tt></A> + +It may be possible to cut/paste non-text data by using +<A href="osissues.html#add_handler"><tt>Fl::add_handler()</tt></A>. + +\section subclassing_dragndrop Drag And Drop Support + +FLTK provides routines to drag and drop 8-bit text between applications: + +Drag'n'drop operations are are initiated by copying data to the +clipboard and calling the function +<A href="Fl.html#Fl.dnd"><tt>Fl::dnd()</tt></A>. + +Drop attempts are handled via <A href="events.html#dnd">events</A>: + +\li <tt>FL_DND_ENTER</tt> +\li <tt>FL_DND_DRAG</tt> +\li <tt>FL_DND_LEAVE</tt> +\li <tt>FL_DND_RELEASE</tt> +\li <tt>FL_PASTE</tt> + +\section subclassing_fl_window Making a subclass of Fl_Window + +You may want your widget to be a subclass of +<tt>Fl_Window</tt>, <tt>Fl_Double_Window</tt>, or +<tt>FL_Gl_Window</tt>. 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 <tt>Fl_Window</tt>is almost exactly like +subclassing <tt>Fl_Group</tt>, and in fact you can easily +switch a subclass back and forth. Watch out for the following +differences: + +-# <tt>Fl_Window</tt> is a subclass of <tt>Fl_Group</tt> so + <I>make sure your constructor calls <tt>end()</tt></I> + unless you actually want children added to your window. + +-# When handling events and drawing, the upper-left corner is at + 0,0, not <tt>x(),y()</tt> as in other <tt>Fl_Widget</tt>'s. + For instance, to draw a box around the widget, call + <tt>draw_box(0, 0, w(), h())</tt>, rather than + <tt>draw_box(x(), y(), w(), h())</tt>. + +You may also want to subclass <tt>Fl_Window</tt> in order to +get access to different visuals or to change other attributes of +the windows. See +<A href="osissues.html">"Appendix F - Operating System Issues"</A> +for more information. + +\htmlonly +<hr> +<a class="el" href="index.html">[Index]</a> +<a class="el" href="events.html">[Previous] 6 - Handling Events</a> +<a class="el" href="opengl.html">[Next] 8 - Using OpenGL</a> +\endhtmlonly +*/ |
