Ruby-FLTK Guide
Last Updated 22-Feb-2002

[NOTE: This document describes a some features that are not available yet]

0. Introduction

Ruby-FLTK is a set of Ruby bindings that allow Ruby programs to use the 
FLTK C++ GUI toolkit.

FLTK is an extremely lightweight toolkit. The C++ developers have aimed 
for extremely small memory footprints, and have provided only a basic 
collection of widgets. The event model is very simple, both in C++ and 
in Ruby. The font and color models are also refreshingly straightforward. 
Because FLTK is already object oriented, the Ruby bindings are very clean. 

Some of the FLTK terminology is a bit unusal. Radio buttons are known as 
RoundButton with a RADIO_BUTTON type. Checkboxes are called CheckButton. 
The base container class is Group, and the list widget is named Browser. 
Those are just examples, so when you're looking for a particular kind of 
widget, be creative in your search.

FLTK runs directly on top of Win32 and X. A Mac version of FLTK is in 
beta, but Ruby-FLTK has only been tested on Linux. FLTK draws all its 
own widgets, so it does not exactly match the native look and feel. 

1. Ruby-FLTK vs. C++ FLTK

Whenever possible, the C++ API has been directly exposed, so the existing 
FLTK documentation and C++ samples can be used as a reference. This 
section describes the few places where the Ruby bindings do not match the 
C++ API.

Ruby-FLTK consists of a module named "FLTK" that contains all the classes, 
methods, and constants. Because it has its own namespace, the leading 
"Fl_", "Fl::" and "fl_" prefixes are not required. Fl_Widget becomes 
FLTK::Widget, and FL_MAJOR_VERSION becomes FLTK::MAJOR_VERSION.

All underlines have been removed from the class names, so Fl_Round_Button 
becomes simply FLTK::RoundButton. Underlines in function/method names have 
been retained, such as FLTK::event_key.

The family of FLTK::Group has the method initialize() which automatically
invokes begin() and end(), when a block is given to the method. So the following
two codes are equivalent:

  -- using begin() and end() --
  win = FLTK::Window.new(...)
  begin
    win.begin()
    # some codes
  ensure
    win.end()
  end

  -- using a block with initialize() --
  FLTK::Window.new(...){|win|
    # some codes
  }


A handful of FLTK method names conflict with Ruby keywords. These have 
been renamed:
	Fl_Button::type		-> FLTK::Button#button_type
	Fl_Group::end		-> FLTK::Group#group_end

Note that these methods are also defined without the underline, for 
convenience. So either button_type or buttontype may be used.

FLTK uses the same family of classes (Window) for both toplevel and popup 
(child) windows. In C++, the only way to indicate which type of window you 
wanted was to pass a specific number of parameters in the constructor.

Ruby-FLTK instead defaults to creating toplevel windows when you use the 
normal constructor (new). If you want to create a popup child window, you 
must use the 'popup' constructor instead. The parameters are the same. For 
example: child = Window.popup(20, 40, 200, 100, 'Search')

To follow the Ruby style, certain getter and setter methods are available 
through different names. Any boolean get method, such as 
FLTK::Widget#visible is also available as visible?. Setters such as 
FLTK::Widget#label can be called using w.label("stuff") or w.label= "stuff".

Certain FLTK functions allow you to pass a char* (string) and length. Since 
Ruby has great string handling, Ruby-FLTK only supports passing entire 
Strings into these methods.

2. Ruby-FLTK Enhancements

FLTK provides only global methods to draw lines, text, and shapes. These 
methods are not specific to a widget, so the origin (0,0) is always the 
upper left corner of the group that is currently being drawn. This means 
that within a custom draw method, you would have to offset every draw call 
by the widget's x and y position.

Ruby-FLTK provides a helper mixin named FLTK::Drawable, which provides 
similar functionality to a GDK "Drawable", a Java "GraphicsContext", or a 
Win32 "DC". Include Drawable in any class that needs to do custom drawing, 
and that class will have member drawing methods like 'point' and 'pie',
that automatically use the correct offset for the widget. The Drawable 
mixin also provides a clear() method that blanks out the entire widget 
client area.

[The paragraphs below are not yet implemented]
FLTK event handling relies on static variables to hold details about each 
event. This means that each time an event is pulled off the queue, details 
about the previous event are lost. 

Ruby-FLTK provides an FLTK::Event class that allows you to retain all the 
details of an event in one convenient package.
[The paragraphs above are not yet implemented]

FLTK has only limited support for layout managers. It currently assumes that 
most layout work will be handled on a pixel basis by the application. Future 
versions of FLTK are expected to have stronger layout support.

Ruby-FLTK has a few features that help the current situation. First, Every 
widget supports two additional constructors beyond the standard (x,y,w,h) 
and (x,y,w,h,label). These are (w,h) and (w,h,label), and in both cases x 
and y are set to zero. If you are creating widgets inside an FLTK::Pack 
object (which does primative automatic layout), the offsets would be 
ignored anyway.

[The paragraph below is not yet implemented]
Second, the Button classes provide a constructor that takes only a label. 
These can automatically size the button in one of two ways: By default, the 
button is created to be just large enough to hold the label, assuming the 
default font. Alternately, if you call the class method 
Button.default_size (or defaultsize), any future buttons created using this 
constructor will automatically be the size you specified.
[The paragraph above is not yet implemented]

3. FLTK Events, Action Callbacks, and Drawing

FLTK distinguishes between system events and action callbacks. System 
events include mouse and keyboard activity, focus and activation events, 
and clipboard events. An action callback occurs when a widget is changed, 
pressed, or otherwise manipulated. For example, a Button (push button) 
callback will occur when the button is clicked (pressed and released). 
A text editing widget callback can be triggered any time the text changes. 
We will refer to this as the widget being "hit". When a widget is hit, its 
callback is called.

3.1 Events

To handle an event, a widget must define a handle method. The handle 
method takes a single parameter, which is the event code (a number). 
Enumerations like FLTK::RELEASE (mouse button release) are available to 
decipher the event. In order to define a handle method, you should create 
a subclass of the widget class you are using.

Most events are sent to the relevant Group. For example, a mouse move event 
(FLTK::MOVE) would only be sent to the Group that is under the mouse. 
Certain events are sent directly to the affected widget, such as: a 
keyboard event would only be sent to the widget that has the focus.

Every overridden handle method must return true if it handled the event, 
and false if it is not interested. Returning false allows the system to 
offer the event to other widgets that might be interested. For example, 
if a text widget ignores a keyboard message, it would be offered to the 
Group that contains the widget, which might be the main window.

3.2 Callbacks

Each widget can only set up a single action callback. This is done by 
invoking the callback method and passing a proc, along with an optional 
data value. When the widget is hit, the proc that was passed to the 
callback will be invoked, passing the data value as the only parameter. 
If no data value was specified, nil will be passed.

Callbacks can be filtered using various WHEN settings. See the C++ 
documentation for details.

3.3 Drawing

Standard widgets, such as buttons and text labels, draw themselves. But 
sometimes you want to create a widget where you can specify exactly how 
it is drawn. A simple example would be a painting program where you need 
to draw various lines and shapes. Another example would be if you are 
creating a custom widget--perhaps something like a button that draws an 
extra icon on one end.

As with most GUI toolkits, FLTK keeps track of which widgets are on top, 
and which are hidden by other windows. It knows when any widget needs to 
be redrawn, for any reason. When a widget needs to be drawn, its draw 
method is invoked, with no parameters. The default draw method does 
nothing, resulting in a blank widget.

Usually, you will define your custom widget as being a subclass of the 
FLTK::Widget class, and include 'FLTK::Drawable'. Inside the draw method, 
you can invoke any of the drawing methods that are part of the Drawable 
module, such as line or pie. You can also access the global drawing 
functions like FLTK::line if you need to draw outside your own space.

If you are extending an existing widget other than FLTK::Widget, your 
draw method may need to call the parent's draw method. For example, 
perhaps you want to create a label widget that has a grid drawn over it. 
In that case, your draw method would invoke the parent's draw method 
(by calling super), and then it would draw the grid lines.

4. Layout Management

FLTK still has roots in the pixel-placement approach to laying out GUI 
screens. It expects that you know how large each widget should be, and 
you can position them accordingly. However, there are some layout 
capabilities you can use.

First, a Group object can automatically resize its children when it 
changes size. Each Group can contain a single "resizable" widget, and 
the placement of other widgets relative to that resizable widget will 
determine how those other widgets get resized. This is described somewhat 
in the C++ FLTK manual. [We should have a good sample for this!]

Second, Pack objects can be combined to create surprisingly complex 
layouts. A Pack object contains either a horizontal or vertical stack of 
widgets, similar to a Box object in Java or GTK+. A Pack object will 
automatically position its children to avoid overlapping, so when you 
create objects within a Pack, you can specify a position of (0,0) for 
all of them.

By nesting Pack objects, you can achieve most layouts that would be 
required of a typical forms application. You might have a horizontal 
Pack that contains just two other Packs. The left Pack could contain a 
vertical stack of field labels, while the right Pack could contain a 
vertical stack of actual data fields.

FLTK also has a very advanced interactive design tool named FLUID. 
There are versions available that output Perl and Python code, so it 
should be possible to have it generate Ruby code, if someone is 
interested enough to try.

5. Sample Programs

The current samples are in the `samples' directory.
