[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Since button-like object is one of the most important, if not
the most important, classes in graphical user interfaces, Forms
Library provides, in addition to the ones explained earlier, a few
more routines that make create new buttons or button-like objects even
easier. These routines take care of the communication between the main
module and the button handler so all new button classes created using
this scheme behave consistently. Within this scheme, the programmer
only has to write a drawing function that draws the button. There is
no need to handle events or messages from the main module and all
types of buttons, radio, pushed or normal are completely taken care of
by the generic button class. Further, fl_get_button()
and
fl_set_button()
work automatically without adding any
code for them.
Forms Library provides two routines to facilitate the creation of new button object classes. One of the routines is
FL_OBJECT *fl_create_generic_button(int objclass, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label); |
which can be used to create a generic button that has all the
properties of a real button except that this generic button does not
know what the real button looks like. The other routine
fl_add_button_class()
, discussed below, can be used to register
a drawing routine that completes the creation of a new button.
All button or button-like objects have the following instance-specific structure, defined in `forms.h', that can be used to obtain information about the current status of the button:
typedef struct { Pixmap pixmap; /* for bitmap/pixmap button only */ Pixmap mask; /* for bitmap/pixmap button only */ unsigned int bits_w, /* for bitmap/pixmap button only */ bits_h; int val; /* whether it's pushed */ int mousebut; /* mouse button that caused the push */ int timdel; /* time since last touch (TOUCH buttons)*/ int event; /* what event triggered the redraw */ long cspecl; /* for non-generic class specific data */ void * cspec; /* for non-generic class specific data */ char * file; /* filename for the pixmap/bitmap file */ } FL_BUTTON_STRUCT; |
Of all its members, only val
and mousebut
probably will
be consulted by the drawing function. cspecl
and cspecv
are useful for keeping track of class status other than those
supported by the generic button (e.g., you might want to add a third
color to a button for whatever purposes.) These two members are
neither referenced nor changed by the generic button class.
Making this structure visible somewhat breaks the Forms Library's convention of hiding the instance specific data but the convenience and consistency gained by this far outweights the compromise on data hiding.
The basic procedures in creating a new button-like object are as
follows. First, just like creating any other object classes, you have
to decide on a class ID, an integer between FL_USER_CLASS_START
(1001) and FL_USER_CLASS_END
(9999) inclusive. Then write a
header file so that application programs can use this new class. The
header file should include the class ID definition and function
prototypes specific to this new class.
After the header file is created, you will have to write C functions that create and draw the button. You also will need an interface routine to place the newly created button onto a form.
After creating the generic button, the new button class should be made known to the button driver via the following function
void fl_add_button_class(int objclass, void (*draw)(FL_OBJECT *), void (*cleanup)(FL_BUTTON_SPEC *)); |
where objclass
is the class ID, and draw
is a function
that will be called to draw the button. cleanup
is a function
that will be called prior to destroying the button. You need a cleanup
function only if the drawing routine uses the cspecv
field of
FL_BUTTON_STRUCT
to hold memory allocated dynamically by the new
button.
We use two examples to show how new buttons are created. The first
example is taken from the button class in the Forms Library, i.e.,
its real working source code that implements the button class. To
illustrate the entire process of creating this class, let us call this
button class FL_NBUTTON
.
First we create a header file to be included in an application program that uses this button class:
#ifndef NBUTTON_H_ #define NBUTTON_H_ #define FL_NBUTTON FL_USER_CLASS_START extern FL_OBJECT *fl_create_nbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); extern FL_OBJECT *fl_add_nbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); #endif |
Now to the drawing function. We use obj->col1
for the normal
color of the box and obj->col2
for the color of the box when
pushed. We also add an extra property so that when mouse moves over
the button box, the box changes color. The following is the full
source code that implements this:
static void draw_nbutton(FL_OBJECT *obj) { FL_COLOR col; /* box color. If pushed we use obj->col2, otherwise use obj->col1 */ col = ((FL_BUTTON_STRUCT *) obj->spec)->val ? obj->col2 : obj->col1; /* if mouse is on top of the button, we change the color of * the button to a different color. However we only do this * if the * box has the default color. */ if (obj->belowmouse && col == FL_COL1) col = FL_MCOL; /* If original button is an up_box and it is being pushed, * we draw a down_box. Otherwise, don't have to change * the boxtype */ if ( obj->boxtype == FL_UP_BOX && ((FL_BUTTON_STRUCT *) obj->spec)->val) fl_drw_box(FL_DOWN_BOX, obj->x, obj->y, obj->w, obj->h, col, obj->bw); else fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h, col, obj->bw); /* draw the button label */ fl_drw_object_label(obj); /* if the button is a return button, draw the return symbol. * Note that size and style are 0 as they are not used when * drawing symbols */ if (obj->type == FL_RETURN_BUTTON) fl_drw_text(FL_ALIGN_CENTER, obj->x + obj->w - 0.8 * obj->h - 1, obj->y + 0.2 * obj->h, 0.6 * obj->h, 0.6 * obj->h, obj->lcol, 0, 0, "@returnarrow"); } |
Note that when drawing symbols, the style and size are irrelevent and
set to zero in fl_drw_text()
above.
Since we don't use the cspecv
field to point to dynamically
allocated memory we don't have to write a clean-up function.
Next, following the standard procedures of the Forms Library, we code a separate routine that creates the new button(15)
FL_OBJECT *fl_create_nbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; obj = fl_create_generic_button(FL_NBUTTON, type, x, y, w, h, label); fl_add_button_class(FL_NBUTTON, draw_nbutton, NULL); obj->col1 = FL_COL1; /* normal color */ obj->col2 = FL_MCOL; /* pushed color */ obj->align = FL_ALIGN_CENTER; /* button label placement */ return obj; } |
You will also need a routine that adds the newly created button to a form
FL_OBJECT *fl_add_nbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj = fl_create_nbutton(type, x, y, w, h, label); fl_add_object(fl_current_form, obj); return obj; } |
This concludes the creation of button class FL_NBUTTON
. The
next example implements a button that might be added to the Forms
Library in the future. We call this button a crossbutton. Normally,
this button shows a small up box with a label on the right. When
pushed, the up box becomes a down box and a small cross appears on top
of it. This kind of button obviously is best used as a push button or
a radio button. However, the Forms Library does not enforce this. It
can be enforced, however, by the application program or by the object
class developers.
We choose to use obj->col1
as the color of the box and
obj->col2
as the color of the cross (remember these two colors
are changeable by the application program via
fl_set_object_color()
). Note that this decision on color
use is somewhat arbitrary, we could have easily made obj->col2
the color of the button when pushed and use obj->spec->cspecl
for the cross color (another routine named e.g.,
fl_set_crossbutton_crosscol()
should be provided to change the
cross color in this case).
We start by defining the class ID and declaring the utility routine prototypes in the header file `crossbut.h':
#ifndef CROSSBUTTON_H_ #define CROSSBUTTON_H_ #define FL_CROSSBUTTON (FL_USER_CLASS_START + 2) extern FL_OBJECT *fl_add_crossbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); extern FL_OBJECT *fl_create_crossbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); #endif |
Next we write the actual code that implements crossbutton class and put it into `crossbut.c':
/* routines implementing the "crossbutton" class */ #include <forms.h> #include "crossbut.h" /** How to draw it */ static void draw_crossbutton(FL_OBJECT *obj) { FL_Coord xx, yy, ww, hh; FL_BUTTON_STRUCT *sp = obj->spec; /* There is no visual change when mouse enters/leaves the box */ if (sp->event == FL_ENTER || sp->event == FL_LEAVE) return; /* draw the bounding box first */ fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h, obj->col1, obj->bw); /* Draw the box that contains the cross */ ww = hh = (0.5 * FL_min(obj->w, obj->h)) - 1; xx = obj->x + FL_abs(obj->bw); yy = obj->y + (obj->h - hh) / 2; /* If pushed, draw a down box with the cross */ if (sp->val) { fl_drw_box(FL_DOWN_BOX, xx, yy, ww, hh, obj->col1, obj->bw); fl_drw_text(FL_ALIGN_CENTER, xx - 2, yy - 2, ww + 4, hh + 4, obj->col2, 0, 0, "@9plus"); } else fl_drw_box(FL_UP_BOX, xx, yy, ww, hh, obj->col1, obj->bw); /* Draw the label */ if (obj->align == FL_ALIGN_CENTER) fl_drw_text(FL_ALIGN_LEFT, xx + ww + 2, obj->y, 0, obj->h, obj->lcol, obj->lstyle, obj->lsize, obj->label); else fl_draw_object_label_outside(obj); if (obj->type == FL_RETURN_BUTTON) fl_drw_text(FL_ALIGN_CENTER, obj->x + obj->w - 0.8 * obj->h, obj->y + 0.2 * obj->h, 0.6 * obj->h, 0.6 * obj->h, obj->lcol, 0, 0, "@returnarrow"); } |
This button class is somewhat different from the normal button class
(FL_BUTTON
) in that we enforce the appearance of a crossbutton
so that an un-pushed crossbutton always has an upbox and a pushed one
always has a downbox. Note that the box that contains the cross is not
the bounding box of a crossbutton although it can be if the drawing
function is coded so.
The rest of the code simply takes care of interfaces:
/* creation routine */ FL_OBJECT * fl_create_crossbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; fl_add_button_class(FL_CROSSBUTTON, draw_crossbutton, NULL); /* if you want to make cross button only available for * push or radio buttons, do it here as follows: if (type != FL_PUSH_BUTTON && type != FL_RADIO_BUTTON) type = FL_PUSH_BUTTON; */ obj = fl_create_generic_button(FL_CROSSBUTTON, type, x, y, w, h, label); obj->boxtype = FL_NO_BOX; obj->col2 = FL_BLACK; /* cross color */ return obj; } /* interface routine to add a crossbutton to a form */ FL_OBJECT *fl_add_crossbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj = fl_create_crossbutton(type, x, y, w, h, label); fl_add_object(fl_current_form, obj); return obj; } |
The actual code is in the demo directory, see the files `crossbut.c' and `crossbut.h'. An application program only needs to include the header file `crossbut.h' and link with `crossbut.o' to use this new object class. There is no need to change or re-compile the Forms Library. Of course, if you really like the new object class, you can modify the system header file `forms.h' to include your new class header file automatically (either through inclusion at compile time or by including the actual header). You can also place the object file (`crossbut.o') in `libforms.a' and `libforms.so' if you wish. Note however that this will make your application programs dependent on your personal version of the library.
Since the current version of Form Designer does not support any new object classes developed as outlined above, the best approach is to use another object class as stubs when creating a form, for example, you might want to use checkbutton as stubs for the crossbutton. Once the position and size are satisfactory, generate the C-code and then manually change checkbutton to crossbutton. You probably can automate this with some scripts.
Finally there is a demo program utilizing this new button class. The program is `newbutton.c'.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Build Daemon on October 16, 2020 using texi2html 1.82.