Contents
Previous
Next
This >chapter >describes >how >to >add >your >own >
widgets >or >extend >existing > widgets >in >FLTK. >
New >widgets >are >created >by >subclassing> an >
existing >FLTK >widget, > typically >Fl_Widget> for >
controls >and >Fl_Group> 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. >Fl_Group> is >the >
main >composite >widget >widget >class >in >FLTK, >and >
all >of >the >other >composite >widgets >(> Fl_Pack>
, >Fl_Scroll>, >Fl_Tabs>, >Fl_Tile>
, > and >Fl_Window>) >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 >
Fl_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. >
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. >
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)
>: Fl_Widget(x, y, w, h, label) {
>// do initialization stuff...
>}
>
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);
>selection_color(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);
>
The >following >methods >are >provided >for >subclasses >
to >use: >
The >first >form >indicates >that >a >partial >update >
of >the >object >is > needed. > The >bits >in >mask >
are >OR'd >into >damage()>. > Your > draw()>
routine >can >examine >these >bits >to >limit >what >
it >is >drawing. > The >public >method >
Fl_Widget::redraw()> simply >does >
Fl_Widget::damage(FL_DAMAGE_ALL)>, >but >the >
implementation >of >your >widget >can >call >the >
private >damage(n)>. >
The >second >form >indicates >that >a >region >is >
damaged. > If >only >these > calls >are >done >in >a >
window >(no >calls >to >damage(n)>) >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 >damage()> unless >this >is >a >Fl_Window>
widget. >
The >third >form >returns >the >bitwise-OR >of >all >
damage(n)> calls >done >since >the >last >draw()>
.>
When >redrawing >your >widgets >you >should >look >
at >the >damage >bits >to >see >what >parts >of >your >
widget >need >redrawing.> The >handle()> method >
can >then >set >individual >damage >bits >to >limit >
the >amount >of >drawing >that >needs >to >be >done: >
>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();
>}
>
The >first >form >draws >this >widget's >box()>
, >using >the > dimensions >of >the >widget. > The >
second >form >uses >b> as >the >box > type >
and >c> as >the >color >for >the >box. >
This >is >the >usual >function >for >a >draw()>
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). >
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. >
The >third >form >draws >the >label >anywhere. >It >
acts >as >though > FL_ALIGN_INSIDE> has >been >
forced >on >so >the >label >will >appear >inside > the >
passed >bounding >box. >This >is >designed >for >parent >
groups >to >draw > labels >with. >
Modifies >draw_label()> so >that >'&' >characters >
cause >an >underscore >to >be >printed >under >the >
next >letter. >
Fast >inline >versions >of >Fl_Widget::hide()> and >
Fl_Widget::show()>. >These >do >not >send >the >
FL_HIDE> and > FL_SHOW> events >to >the >
widget. >
The >first >version >tests >Fl_Widget::label()>
against >the > current >event >(which >should >be >a >
FL_SHORTCUT> event). > If >the > label >contains >a >
'&' >character >and >the >character >after >it >matches >
the >key > press, >this >returns >true. > This >returns >
false >if >the >SHORTCUT_LABEL> flag >is >off, >
if >the >label >is >NULL> or >does >not >have >
a > '&' >character >in >it, >or >if >the >keypress >
does >not >match >the >character. >
The >second >version >lets >you >do >this >test >
against >an >arbitrary > string. >
The >property >Fl_Widget::type()> can >return >an >
arbitrary >8-bit > identifier, >and >can >be >set >with >
the >protected >method >type(uchar >t)> . >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. >
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()>
use >a >unique >value. > These >unique >values >must >
be >greater >than >the >symbol >FL_RESERVED_TYPE>
(which >is >100). > Look >through >the >header >files >
for >FL_RESERVED_TYPE> to >find >an > unused >
number. > If >you >make >a >subclass >of >Fl_Window>
you >must >use >FL_WINDOW >+ >n> (>n>
must >be >in >the > range >1 >to >7). >
The >virtual >method >int >Fl_Widget::handle(int >event)>
is >called > to >handle >each >event >passed >to >
the >widget. >It >can: >
- Change >the >state >of >the >widget. >
- Call >
Fl_Widget::redraw()> if >the >widget >needs >to >
be >redisplayed. >
- Call >
Fl_Widget::damage(n)> if > the >widget >needs >a >
partial-update > (assumming >you >provide > support >for >
this >in >your >Fl_Widget::draw()> method). >
- Call >
Fl_Widget::do_callback()> if >a >callback >should >
be >generated. >
- Call >Fl_Widget::handle()> 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 >Fl::event_*()>
functions. >This >information >remains >valid >until >
another >event >is > handled. >
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 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);
> }
>}
>
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. >
The >draw()> virtual >method >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 >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 >on >the >
FL_DAMAGE_ALL> bit >if >it >thinks >the >entire >
widget >must >be >redrawn >(e.g. >for >an >expose >
event). >
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 >fl_not_clipped(x,y,w,h)> or >
fl_clip_box(...)> and > skipping >invisible >parts. >
Besides >the >protected >methods >described >above, >
FLTK >provides >a >large > number >of >basic >drawing >
functions, >which >are >described >
below>. >
The >resize(int >x, >int >y, >int >w, >int >h)>
method >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 >remain >the >
old >size. > You >must >call >resize()> on >your >
base >class >with >the >same >arguments >to >get >the >
widget >size >to > actually >change. >
This >should >not> call >redraw()>, >at >
least >if >only >the > x()> and >y()>
change. > This >is >because >composite >widgets >like >
Fl_Scroll> may >have >a >more >efficient >way >
of >drawing >the >new > position. >
A >"composite" >widget >contains >one >or >more >"child" >
widgets. > To >make >a >composite >widget >you >should >
subclass >Fl_Group> . >
It >is >possible >to >make >a >composite >object >that >
is >not >a >subclass >of > Fl_Group>, >but >
you'll >have >to >duplicate >the >code >in >Fl_Group>
anyways. >
Instances >of >the >child >widgets >may >be >included >
in >the >parent: >
>class MyClass : public Fl_Group {
> Fl_Button the_button;
> Fl_Slider the_slider;
> ...
>};
>
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()>
or >use >the >Fl_End>
pseudo-class:>
>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!
>}
>
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(Fl_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 >Fl_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 (Fl_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(FL_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() {
> 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 want to do this
> }
> }
>}
>
Fl_Group> provides >some >protected >methods >to >
make >drawing > easier: >
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. >
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)>. >
Draws >the >child >only >if >its >damage()> 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 >visible()> or >if >it >is >clipped. >
FLTK >provides >routines >to >cut >and >paste >8-bit >
text >(in >the >future >this > may >be >UTF-8) >between >
applications: >
It >may >be >possible >to >cut/paste >non-text >data >
by >using >
Fl::add_handler()>. >
You >may >want >your >widget >to >be >a >subclass >
of >Fl_Window>. > 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 >Fl_Window >is >almost >exactly >like >
subclassing > Fl_Widget>, >and >in >fact >you >
can >easily >switch >a >subclass >back >and > forth. >
Watch >out >for >the >following >differences: >
- Fl_Window> is >a >subclass >of >Fl_Group>
so >make > sure >your >constructor >calls >end()>
(unless > you >actually > want >children >added >to >
your >window). >
- When >handling >events >and >drawing, >the >upper-left >
corner >is > at >0,0, > not >x(),y()> as >in >
other >Fl_Widget>'s. > For >instance, >to > draw >
a >box >around >the >widget, >call >draw_box(0, >0, >
w(), >h())>, > rather >than >draw_box(x(), >y(), >
w(), >h())>. >
You >may >also >want >to >subclass >Fl_Window>
in >order >to >get > access >to >different >visuals >
or >to >change >other >attributes >of >the > windows. >
See >"Appendix >F >- >
Operating > System >Issues"> for >more >information. >
Contents
Previous
Next
No comments for this page.
|