Erco's FLTK Cheat Page


These are some common FLTK code snippets I often find hard to remember how to do when looking at the docs. Besides, I like having working code examples handy that I can cut + paste from to get started with right away. I often start with these mini-programs and turn them into working programs.

This is always a work in progress; I add examples as I encounter the need for them.

Table Of Contents
Fl_Browser Tricks for using a browser
Fl_Text_Display Simple example of Fl_Text_Display
Fl_Text_Display with colors Simple example of Fl_Text_Display with color styles
Fl_Multiline_Output Simple example of Fl_Multiline_Output
Fl_Menu_Bar: Changing Labels Shows how to access and change menu item labels at runtime
Fl_Menu_Bar: Toggling menu items Shows how to toggle a menu item procedurally
Fl_Menu_Bar callbacks One callback for all menu items, w/out userdata!
Fl_Menu_Bar: Miscellaneous Miscellaneous techniques to manipulate Fl_Menu_Bar/Fl_Menu's
Fl_Radio_Button How to handle radio buttons
Fl_JPG_Image Simple example to display a JPEG image
Fl_Image How to walk pixels of an Fl_Image
Fl_Wizard How to make a 'wizard' using Fl_Wizard
Animate Image How to animate images in FLTK
Animate Line Drawing How to animate line drawing in FLTK
Fltk Event Names How to print FLTK event names from within your program (for debugging)
popen() + add_fd() How to use popen() with add_fd()
Fltk + Fifos How to use fifos to control a console program from fltk, showing output in Fl_Browser
Slider+Input How to tie an Fl_Slider to an Fl_Int_Input
Slider+Tooltip An Fl_Slider with a floating 'tooltip' to show the current value
Fltk-tty Shows how to fork() child processes and display their real-time output
Draw An X Simple FLTK custom widget to draw an 'X'
Scrollable Canvas Shows how to make a scrollable custom canvas
Custom Bar Graph Shows how to draw a customized 'trigger' shaped bar graph
Table Of Widgets Shows how to make a resizable 'table' of widgets, using Fl_Scroll and Fl_Tile
Draggable Boxes Simple app shows user can drag boxes on scrollable 'desk'
Popup Menu Shows how to make a popup menu appear
Right-Click Popup Copy/Paste Menu Shows how to add a copy/paste popup menu to Fl_Input
Popup Text Window Shows how to make a borderless text window pop up
Draw Coordinates How to draw mouse coordinates w/out redrawing screen
Progress Bar Demo How to update a progress bar in fltk
Scrollable Widget Browser Demonstrates a scrollable browser of widgets that are resizable.
Scrollable Image Viewer How to display an image in a scrollable viewer
Disable Symbols How to disable '@' symbols throughout your FLTK application
OpenGL Simple Example Simple OpenGL example showing how to draw an 'X'
OpenGL App with FLTK Widgets Simple OpenGL app with FLTK widget controls
OpenGL Shape Interpolation Shows how to animate simple shape interpolation in opengl
OpenGL Sphere With Light Simple OpenGL Shaded Sphere with Light
Fl_File_Chooser example Shows how to use Fl_File_Chooser (simple, and full test)
Clicks on Scrollable Box How to get the coordinates for mouse clicks on scrollable box
Fl_Browser with Columns How to use the Fl_Browser methods column_widths() and column_char()
Fl_Browser Sorting How to sort an Fl_Browser
Strike Through Text Demonstrate how to draw strike through text
Fl_Gl_Window + Cursor Demonstrate how to change the mouse cursor for an Fl_Gl_Window
Fl_Resize_Browser How to extend Fl_Browser to have interactively resizable columns
Fl_Tile (4) Port Fl_Gl_Window How to make a 4 port proportionally resizable openGL window using Fl_Tile
OpenGL with Dynamic Popup Menu How to make a dynamic popup menu for an openGL window
Drop Shadow Box Example How to make text with a drop shadow effect
OpenGL Text on 3D Object How to make text appear on a spinning OpenGL object
Simple Fl_Tabs Example Simple demo of how to use the Fl_Tabs widget
Making a Mac "Bundle" (.app) How to turn a Mac FLTK application into an ".app bundle"
Making icons for Mac "Bundles" How to make icons for your Mac application
Touch Pad How to make a pop-up numeric keypad for touch screens
Drawing into overlay planes Two examples on drawing into the overlay planes
Thumbnails In Scroller How to show a scroller of thumbnail images with labels
Drag And Drop How to use Drag+Drop
fl_draw_image() How to draw into a pixel buffer, and display it using fl_draw_image()
FLTK/WIN32 File Chooser How to open a WIN32 File Chooser directly from FLTK
Fl_Group Event vs. Draw Clipping Fl_Group xywh clips children of events, but not graphics
Vertical Tabs How to use Fl_Browser to implement a 'vertical tab' dialog
Fl_Chart How to use Fl_Chart
Fl_Scrollbar How to use Fl_Scrollbar
OpenGL Texture Mapped Cube A texture mapped cube with perspective.
OpenGL Image Textured Cube A PNG image texture mapped to a cube with lighting model, materials, and perspective.
UTF8 Japanese Font Test An test program using Japanese UTF8 (fltk-1.3.x and up)
Alpha Blending Example of alpha blending with PNG images
Simple Terminal Emulator Example of how to make a scrolling terminal for executing commands using Fl_Text_Editor.

Also, some of my other fltk related stuff; videos, LGPL widgets, and GPL apps:




Fl_Browser

One thing I use a lot is browsers, which often involves remembering certain programming techniques to manipulate them.

Fl_Browser "How To" Code Snippets

Fl_Browser browser(10,10,500,500,"Test");

// CLEAR BROWSER
browser.clear();

// ADD LINES TO BROWSER
browser.add("One");            // fltk does strdup() internally       
browser.add("Two");
browser.add("Three");

// FORMAT CHARACTERS: CHANGING TEXT COLORS IN LINES
//    Warning: format chars are also returned back to you via ::text()
//    @C# - text color             @b  - bold
//    @B# - background color       @i  - italic
//    @F# - set font number        @f  - fixed pitch font
//    @S# - set point size         @u  - underline
//    @.  - terminate '@' parser   @-  - strikeout
//
browser.add("Black @C1Red @C2Green @C3Yellow");

// DISABLING FORMAT CHARACTERS
browser.format_char(0);

// ACCESS ALL SELECTED ITEMS IN BROWSER
// Note: browser's text() array is 1 based..!
//
for ( int t=1; t<=browser->size(); t++ ) {
    if ( browser->selected(t) ) {
        printf("%d) '%s'\n", t, browser->text(t));
    }
}

// PRE-SELECT ALL LINES IN BROWSER
//   Note: index numbers are 1 based..!
//
for ( int t=1; t<=browser->size(); t++ ) {
    browser->select(t);
}

// DE-SELECT ALL LINES IN BROWSER
browser->deselect();

// GET SINGLE SELECTED ITEM FROM BROWSER
int index = browser->value();

// USING INDEX NUMBER, RETURN TEXT
//   Note: index numbers are 1 based..!
//
if ( index > 0 ) {
    const char *s = browser->text(index);
    ..
}

Here's an older example that shows how to implement a swap() method for older versions of Fl_Browser (Fltk 1.1.5 or less)



Fl_Multiline_Output

Making windows that display multiple lines of text is another common thing I find myself doing.

Displaying Multiple Lines Of Text - "How To" Code Snippets
Fl_Multiline_Output output(0,0,500,500);

// CLEAR OUTPUT
output->value("");

// SET OUTPUT
output->value("one\ntwo");          // fltk does strdup() internally       

// APPENDING OUTPUT
//     Would be nice if this were an add() method in fltk ;)
//
char *news = "Add this text";
char *cat = (char*)malloc(strlen(output->value()) + 
i                         strlen(news) + 1);
strcpy(cat, output->value());
strcat(cat, news);
output->value(cat);
free(cat);



Fl_Menu_Bar: Changing Menu Item's Labels At Runtime

Shows how to access and change menu items at run time.

Access and Change Menu Item's Labels

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <stdlib.h>
//
// Show how to change submenu names and menu item names
//    Click on Edit -> Change to change the contents of the Edit menu.
//
void Change_CB(Fl_Widget *w, void *) {
    Fl_Menu_Bar *menu = (Fl_Menu_Bar*)w;
    Fl_Menu_Item *p;
    // Change submenu name
    p = (Fl_Menu_Item*)menu->find_item("Edit/Submenu");
    if ( p ) p->label("New Submenu Name");
    // Change item name
    p = (Fl_Menu_Item*)menu->find_item("Edit/New Submenu Name/Aaa");
    if ( p ) p->label("New Aaa Name");
}
void Quit_CB(Fl_Widget *, void *) {
    exit(0);
}
int main() {
    Fl_Window *win = new Fl_Window(400,400);
    Fl_Menu_Bar *menu = new Fl_Menu_Bar(0,0,400,25);
    menu->add("File/Quit",   FL_CTRL+'q', Quit_CB);
    menu->add("Edit/Change", FL_CTRL+'c', Change_CB);
    menu->add("Edit/Submenu/Aaa");
    menu->add("Edit/Submenu/Bbb");
    win->end();
    win->show();
    return(Fl::run());
}
    



Fl_Menu_Bar: Toggling Menu Item At Runtime

Shows how to toggle menu items at run time.

Toggle Menu Items

#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>

//
// Demonstrate how to toggle a menu item procedurally -- erco 05/14/09
//

// SET A MENU ITEM'S STATE
//    Find the menuitem by name, and change its state.
//    Returns -1 if named menu item not found.
//
int SetMenuItemState(Fl_Menu_Bar *menubar, const char *name, int state) {
    Fl_Menu_Item *m = (Fl_Menu_Item*)menubar->find_item(name);
    if ( ! m ) return(-1);
    if ( state ) { m->set(); }
    else         { m->clear(); }
    return(0);
}
int main(int argc, char **argv) {
    Fl_Double_Window *win = new Fl_Double_Window(720,486);
    Fl_Menu_Bar *menubar = new Fl_Menu_Bar(0,0,720,25);
    menubar->add("Options/One",   0, 0,0, FL_MENU_TOGGLE);
    menubar->add("Options/Two",   0, 0,0, FL_MENU_TOGGLE);
    menubar->add("Options/Three", 0, 0,0, FL_MENU_TOGGLE);
    win->end();
    win->show();
    // TEST THE STATE CHANGES -- TURN 'ONE' OFF, OTHERS 'ON'
    if ( SetMenuItemState(menubar, "Options/One",   0) < 0 ) { fprintf(stderr, "FAILED[1]\n"); }
    if ( SetMenuItemState(menubar, "Options/Two",   1) < 0 ) { fprintf(stderr, "FAILED[2]\n"); }
    if ( SetMenuItemState(menubar, "Options/Three", 1) < 0 ) { fprintf(stderr, "FAILED[3]\n"); }
    return(Fl::run());
}
    



Fl_Menu_Bar Single Callback Example

Here's an example program showing a neat trick that lets the menubar's callback determine the full menu item's 'pathname' (eg. "File/Quit") without using any userdata.

Accessing Menu Items Without Using Callback Data

// Show the use of item_pathname() in a custom class.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Bar.H>

class MyApp {
    Fl_Window *win;
    Fl_Menu_Bar *menubar;

    // Static menu callback
    static void Menu_CB(Fl_Widget*w, void*data) {
        MyApp *o = (MyApp*)data;
        o->Menu_CB2();
    }

    // Callback method with class access
    void Menu_CB2() {
        char picked[80];
        menubar->item_pathname(picked, sizeof(picked)-1);
        printf("CALLBACK: You picked '%s'\n", picked);

        // How to handle callbacks..
        if ( strcmp(picked, "File/Quit") == 0 ) exit(0);
        if ( strcmp(picked, "Help/Help") == 0 ) printf("Help goes here\n");
    }

public:
    MyApp() {
        // Make the app window and menu bar w/callbacks
        win = new Fl_Window(720, 486);
        menubar = new Fl_Menu_Bar(0, 0, win->w(), 25);
        menubar->add("File/Open", 0, Menu_CB, (void*)this);    // userdata is always 'this'
        menubar->add("File/Quit", 0, Menu_CB, (void*)this);
        menubar->add("Help/Help", 0, Menu_CB, (void*)this);
        win->end();
        win->show();
    }
};

// MAIN
int main() {
    MyApp app;
    return(Fl::run());
}
    

Here's an older example that works with fltk 1.1.5 and back that doesn't have item_pathname() built in.



Fl_Menu_Bar

Dynamically creating/clearing submenus in the main menu bar is something I often need to do. Since it's not well documented how this is done, this is what I've figured out.

WARNING: when creating or modifying menus using add(), beware that if you name items in fluid, the pointers may become invalid, since dynamic manipulation of the menus can cause the array to be realloc()ed, causing menu item pointers to become STALE. The submenus and all their contents in the menu bar are really all part of a single linear internal array to the Fl_Menu_Bar class, and are accessed as:

Accessing Menu Items In Fl_Menu_Bar
for ( int t=0; t < menubar->size(); t++ ) {
    Fl_Menu_Item *m = (Fl_Menu_Item*)&(menubar->menu()[t]);
    :
    :
}
    

Given the above on how the menus are arranged in the array, here are some guides for how to write routines to work with FLTK menus dynamically.

Dynamic Manipulation Of Menus in FLTK

// FIND MENU ITEM INDEX, GIVEN MENU PATHNAME
//     eg. "Edit/Copy"
//     Will also return submenus, eg. "Edit"
//     Returns -1 if not found.
//
int GetIndexByName(Fl_Menu_Bar* menubar, const char *findname) {
    char menupath[1024] = "";            // File/Export
    for ( int t=0; t < menubar->size(); t++ ) {
        Fl_Menu_Item *m = (Fl_Menu_Item*)&(menubar->menu()[t]);
        if ( m->submenu() ) {
            // Submenu?
            if ( menupath[0] ) strcat(menupath, "/");
            strcat(menupath, m->label());
            if ( strcmp(menupath, findname) == 0 ) return(t);
        } else {
            if ( m->label() == NULL ) {
                // End of submenu? Pop back one level.
                char *ss = strrchr(menupath, '/');
                if ( ss ) *ss = 0;
                else      menupath[0] = '\0';
                continue;
            }
            // Menu item?
            char itempath[1024];         // eg. Edit/Copy
            strcpy(itempath, menupath);
            if ( itempath[0] ) strcat(itempath, "/");
            strcat(itempath, m->label());
            if ( strcmp(itempath, findname) == 0 ) {
                return(t);
            }
        }
    }
    return(-1);
}

// FIND A MENU ITEM BY MENU PATHNAME
//     eg. "Edit/Copy"
//     Will also return submenus, eg. "Edit"
//     Returns NULL if not found.
//
Fl_Menu_Item *GetMenuItemByName(Fl_Menu_Bar* menubar, const char *findname) {
    int index = GetIndexByName(menubar, findname);
    if ( index == -1 ) return(NULL);
    Fl_Menu_Item *m = (Fl_Menu_Item*)&(menubar->menu()[index]);
    return(m);
}

// TURN ON RADIO BUTTON GIVEN MENU PATHNAME
//    eg. SetRadioByName(menubar, "File/Radio-1")
//
void SetRadioByName(Fl_Menu_Bar *menubar, const char *menuname) {
    Fl_Menu_Item *m = GetMenuItemByName(menubar, menuname);
    if ( m == NULL ) return;
    m->setonly();
}

// SET TOGGLE BUTTON ON OR OFF, GIVEN MENU PATHNAME
//    eg. SetToggleByName("File/Toggle-1", "on")
//
void SetToggleByName(Fl_Menu_Bar *menubar, const char *menuname, int val ) {
    Fl_Menu_Item *m = GetMenuItemByName(menubar, menuname);
    if ( m == NULL ) return;
    if ( val ) m->set();
    else       m->clear();
}

// ACTIVATE MENU ITEM, GIVEN MENU ITEM PATHNAME
//    eg. SetActivateByName("File/Radio-1")
//
void SetActivateByName(Fl_Menu_Bar *menubar, const char *menuname, int val) {
    Fl_Menu_Item *m = GetMenuItemByName(menubar, menuname);
    if ( m == NULL ) return;
    if ( val ) m->activate();
    else       m->deactivate();
}

// GET TOGGLE BUTTON STATE, GIVEN MENU PATHNAME
//    eg. GetToggleByName("Edit/Preferences/Toggle-1")
//
int GetToggleValueByName(Fl_Menu_Bar *menubar, const char *menuname) {
    Fl_Menu_Item *m = GetMenuItemByName(menubar, menuname);
    if ( m == NULL ) return;
    return(m->value());
}

// CLEAR ALL ITEMS IN SUBMENU BELOW THE NAMED ITEM
//    eg. ClearItemsBelow("File/Save As");
//    where menu looks like:
//
//       File..New
//             Open
//             Save
//             Save As
//             -------          __
//             /usr/tmp/foo.st    |__ Clear all these so they
//             /usr/tmp/bar.st    |   can be recreated later.
//             /usr/tmp/bla.st  __|
//                    __
//       Edit..Cut      |
//             Copy     | These are unaffected.
//             Paste  __|
//
static void ClearItemsBelow(Fl_Menu_Bar *menubar, const char *menuname) {
    int index = GetIndexByName(menubar, menuname);
    if ( index == -1 ) return;
    int level = 0;
    ++index;    // skip the item itself, so we delete stuff _below_ it
    while ( index <= menubar->size() ) {
        Fl_Menu_Item *m = (Fl_Menu_Item*)&(menubar->menu()[index]);
        if ( m->label() == NULL ) {
            // Pop out of submenu, keep track of level
            if ( --level < 0 ) return;
        } else {
            // Descending into a submenu? Keep track of leveL
            if ( m->submenu() )
                ++level;
        }
        // Remove all menu items *and* submenus
        menubar->remove(index);
    }
}
    



Fl_Radio_Button

Dealing with radio buttons in menus is fairly easy, using the Fl_Menu_Bar routines described above. I guess fltk groups radio buttons together based on dividers and submenus.

Using Radio Buttons In Menus
        int index = GetIndexByName(menubar, "Edit/Preferences/Radio 1");
        Fl_Menu_Item *m = menubar->menu();
        m[index]->setonly();             // set radio on, turns other(s) off
        int onoff = m[index]->value();   // get radio button's state
    
Fl_Text_Display
A simple example of how to use Fl_Text_Display, a read only viewer.

If you want to make it an editor, change all instances of the string "Fl_Text_Display" to "Fl_Text_Editor".

Simple Example of How To Use Fl_Text_Display
// Fl_Text_Display example. -erco
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Text_Display.H>

int main() {
     Fl_Window *win = new Fl_Window(640, 480);
     Fl_Text_Buffer *buff = new Fl_Text_Buffer();
     Fl_Text_Display *disp = new Fl_Text_Display(20, 20, 640-40, 480-40, "Display");
     disp->buffer(buff);
     win->resizable(*disp);
     win->show();
     buff->text("line 0\nline 1\nline 2\n"
                "line 3\nline 4\nline 5\n"
                "line 6\nline 7\nline 8\n"
                "line 9\nline 10\nline 11\n"
                "line 12\nline 13\nline 14\n"
                "line 15\nline 16\nline 17\n"
                "line 18\nline 19\nline 20\n"
                "line 21\nline 22\nline 23\n");
     return(Fl::run());
}
    
Fl_Text_Display with colors
A simple example of how to use Fl_Text_Display with colors.

This example shows how to add text with different colors by creating a 'style buffer', where each character in the style buffer corresponds to a character in the text buffer, and the contents of the style buffer specifies characters (A, B, C.. ) that correspond to entries in your pre-defined 'style table', that defines the actual attributes of each character.

This scheme is similar to a 'color map' in images, where the image data is not an RGB color, but an index into a color table.

Simple Example of Fl_Text_Display with colors
// Fl_Text_Display example with color styles . -erco
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Text_Display.H>
int main() {
     // Style table
     Fl_Text_Display::Style_Table_Entry stable[] = {
         // FONT COLOR      FONT FACE   FONT SIZE
         // --------------- ----------- --------------
         {  FL_RED,         FL_COURIER, 18 }, // A - Red
         {  FL_DARK_YELLOW, FL_COURIER, 18 }, // B - Yellow
         {  FL_DARK_GREEN,  FL_COURIER, 18 }, // C - Green
         {  FL_BLUE,        FL_COURIER, 18 }, // D - Blue
     };
     Fl_Window *win = new Fl_Window(640, 480);
     Fl_Text_Display *disp = new Fl_Text_Display(20, 20, 640-40, 480-40, "Display");
     Fl_Text_Buffer *tbuff = new Fl_Text_Buffer();      // text buffer
     Fl_Text_Buffer *sbuff = new Fl_Text_Buffer();      // style buffer
     disp->buffer(tbuff);
     int stable_size = sizeof(stable)/sizeof(stable[0]);
     disp->highlight_data(sbuff, stable, stable_size, 'A', 0, 0);
     // Text
     tbuff->text("Red Line 1\nYel Line 2\nGrn Line 3\nBlu Line 4\n"
                 "Red Line 5\nYel Line 6\nGrn Line 7\nBlu Line 8\n");
     // Style for text
     sbuff->text("AAAAAAAAAA\nBBBBBBBBBB\nCCCCCCCCCC\nDDDDDDDDDD\n"
                 "AAAAAAAAAA\nBBBBBBBBBB\nCCCCCCCCCC\nDDDDDDDDDD\n");
     win->resizable(*disp);
     win->show();
     return(Fl::run());
}
    


Fl_JPG_Image: A simple example
A simple example of how to load and display a JPEG image.

For PNG's, just substitute all occurances of "JPEG" with "PNG". Same for GIFs, and other supported image formats.

Simple example to display a JPEG image
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_JPEG_Image.H>
#include <FL/Fl_Box.H>

int main() {
    fl_register_images();                       // initialize image lib
    Fl_Window     win(720,486);                 // make a window
    Fl_Box        box(10,10,720-20,486-20);     // widget that will contain image
    Fl_JPEG_Image jpg("/var/tmp/foo.jpg");      // load jpeg image into ram
    box.image(jpg);                             // attach jpg image to box
    win.show();
    return(Fl::run());
} 
    


Fl_Image
An example of how to walk the r/g/b pixel data of an Fl_BMP_Image image.

NOTE: There are faster ways of coding this to avoid recalculating 'index' at every pixel. This is just to make a clear example of how pixel indexes should be calculated, eg. for random access. Optimization is left as an exercise to the reader.

Accessing Raw RGB Data from Fl_Image
#include <stdio.h>
#include <FL/Fl_BMP_Image.H>

int main() {
    Fl_BMP_Image *img = new Fl_BMP_Image("tiny.bmp");
    if ( img->d() == 0 ) {
        perror("tiny.bmp");
        exit(1);
    }
    char r,g,b;
    for ( int y=0; y<img->h(); y++ ) {                               // X loop
        for ( int x=0; x<img->w(); x++ ) {                           // Y loop
            long index = (y * img->w() * img->d()) + (x * img->d()); // X/Y -> buf index  
            switch ( img->count() ) {
                case 1: {                                            // bitmap
                    const char *buf = img->data()[0];
                    switch ( img->d() ) {
                        case 1: {                                    // 8bit
                            r = g = b = *(buf+index);
                            break;
                        }
                        case 3:                                      // 24bit
                            r = *(buf+index+0);
                            g = *(buf+index+1);
                            b = *(buf+index+2);
                            break;
                        default:                                     // ??
                            printf("Not supported: chans=%d\n", img->d());
                            exit(1);
                    }
                    break;
                }
                default:                                             // ?? pixmap, bit vals
                    printf("Not supported: count=%d\n", img->count());
                    exit(1);
            }
            printf("%2x %2x %2x\n",                                  // hex dump r/g/b
                (unsigned char)r,
                (unsigned char)g,
                (unsigned char)b);
        }
    }
}
    


Fl_Wizard Example

A simple 3 screen 'wizard' using Fl_Wizard, showing how groups can be used to move through the 'pages' of the wizard.

FLTK's Wizard

#include <stdlib.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Wizard.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Multiline_Output.H>

// Simple 'wizard' using fltk's new Fl_Wizard widget

Fl_Window *G_win = 0;
Fl_Wizard *G_wiz = 0;

void back_cb(Fl_Widget*,void*) { G_wiz->prev(); }
void next_cb(Fl_Widget*,void*) { G_wiz->next(); }
void done_cb(Fl_Widget*,void*) { exit(0); }

int main(int argc, char **argv) {
    G_win = new Fl_Window(400,300,"Example Wizard");
    G_wiz = new Fl_Wizard(0,0,400,300);

    // Wizard: page 1
    {
        Fl_Group *g = new Fl_Group(0,0,400,300);
        Fl_Button *next = new Fl_Button(290,265,100,25,"Next"); next->callback(next_cb);
        Fl_Multiline_Output *out = new Fl_Multiline_Output(10,30,400-20,300-80,"Welcome");
        out->labelsize(20);
        out->align(FL_ALIGN_TOP|FL_ALIGN_LEFT);
        out->value("This is First page");
        g->end();
    }
    // Wizard: page 2
    {
        Fl_Group *g = new Fl_Group(0,0,400,300);
        Fl_Button *next = new Fl_Button(290,265,100,25,"Next"); next->callback(next_cb);
        Fl_Button *back = new Fl_Button(180,265,100,25,"Back"); back->callback(back_cb);
        Fl_Multiline_Output *out = new Fl_Multiline_Output(10,30,400-20,300-80,"Terms And Conditions");
        out->labelsize(20);
        out->align(FL_ALIGN_TOP|FL_ALIGN_LEFT);
        out->value("This is the Second page");
        g->end();
    }
    // Wizard: page 3
    {
        Fl_Group *g = new Fl_Group(0,0,400,300);
        Fl_Button *done = new Fl_Button(290,265,100,25,"Finish"); done->callback(done_cb);
        Fl_Button *back = new Fl_Button(180,265,100,25,"Back"); back->callback(back_cb);
        Fl_Multiline_Output *out = new Fl_Multiline_Output(10,30,400-20,300-80,"Finish");
        out->labelsize(20);
        out->align(FL_ALIGN_TOP|FL_ALIGN_LEFT);
        out->value("This is the Last page");
        g->end();
    }
    G_wiz->end();
    G_win->end();
    G_win->show(argc, argv);
    return Fl::run();
}
    


Image Animation Example
Shows how to animate some JPG files. Here are some jpgs you can use for playback.

Animating Images in FLTK

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_JPEG_Image.H>
#include <FL/Fl_Box.H>

// animate.cxx -- Animate playback of three jpg images (0001.jpg, 0002.jpg..)
//                Hold each image for RATE seconds
//

#define RATE    0.1             // how long to hold each image
#define TOTAL   3               // total images: /var/tmp/000{1,2,3}.jpg

Fl_Window     *win = 0;         // main window
Fl_JPEG_Image *jpgs[TOTAL];     // loaded images
Fl_Group      *grp = 0;         // group in which images are displayed

// SHOW NEXT IMAGE
//     Slaps next image up on screen, resets frame timer.
//
void ShowNextImage_CB(void*) {
    static int x = 0;
    grp->image(jpgs[x++ % TOTAL]);
    win->redraw();
    // Fl::repeat_timeout(RATE, ShowNextImage_CB);                      // steady rate
    Fl::repeat_timeout(((x%TOTAL)==0)?2.0:RATE, ShowNextImage_CB);      // eye blink: hold 0003.jpg for 2 secs
}

// LOAD ALL IMAGES INTO MEMORY
int LoadImages() {
    for ( int t=0; t<TOTAL; t++ ) {
        char filename[80];
        sprintf(filename, "%04d.jpg", t+1);     // 0001.jpg, 0002.jpg..
        jpgs[t] = new Fl_JPEG_Image(filename);
        if ( jpgs[t]->w() == 0 ) { perror(filename); return(1); }
    }
    return(0);
}

// MAIN -- OPEN DOUBLE BUFFERED WINDOW, LOAD IMAGES, START PLAYBACK
int main() {
    fl_register_images();                               // initialize image lib
    win = new Fl_Double_Window(720,486);                // make a window
    grp = new Fl_Group(0,0,win->w(),win->h());
    grp->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
    win->show();
    if ( LoadImages() ) return(1);
    Fl::add_timeout(RATE, ShowNextImage_CB);
    return(Fl::run());
}
    


Line Drawing Animation Example
Shows how to animate line drawing, showing a simple timer application that animates the 'second hand'.

Animating Line Drawing in FLTK

//
// FLTK drawing example showing simple line drawing animation
// erco 03/22/07
//
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#define BG_COLOR   45
#define TICK_COLOR 50
#define CIRC_COLOR 0
class MyTimer : public Fl_Box {
    void draw() {
        // COMPUTE NEW COORDS OF LINE
        static long start = time(NULL);
        long tick = time(NULL) - start;
        char secs[80]; sprintf(secs, "%02ld:%02ld", tick/60, tick%60);
        float pi = 3.14 - (((tick % 60) / 60.0) * 6.28);
        int radius = h() / 2;
        int x1 = (int)(x() + w()/2),
            y1 = (int)(y() + h()/2),
            x2 = (int)(x1 + (sin(pi) * radius)),
            y2 = (int)(y1 + (cos(pi) * radius));

        // TELL BASE WIDGET TO DRAW ITS BACKGROUND
        Fl_Box::draw();

        // DRAW 'SECOND HAND' OVER WIDGET'S BACKGROUND
        fl_color(TICK_COLOR);
        fl_line(x1, y1, x2, y2);
        fl_color(CIRC_COLOR);
        fl_pie(x1-10, y1-10, 20, 20, 0.0, 360.0);

        // DRAW TIMER TEXT STRING
        fl_color(TICK_COLOR);
        fl_font(FL_HELVETICA,16);
        fl_draw(secs, x()+4, y()+h()-4);
    }
    static void Timer_CB(void *userdata) {
        MyTimer *o = (MyTimer*)userdata;
        o->redraw();
        Fl::repeat_timeout(0.25, Timer_CB, userdata);
    }
public:
    // CONSTRUCTOR
    MyTimer(int X,int Y,int W,int H,const char*L=0) : Fl_Box(X,Y,W,H,L) {
        box(FL_FLAT_BOX);
        color(BG_COLOR);
        Fl::add_timeout(0.25, Timer_CB, (void*)this);
    }
};
// MAIN
int main() {
     Fl_Double_Window win(220, 220);
     MyTimer tim(10, 10, win.w()-20, win.h()-20);
     win.show();
     return(Fl::run());
}
    


Printing FLTK Event Names
Here is an eventnames.h file that you can include in your FLTK programs to make it easy to print FLTK event names from within your program, useful for debugging.

Example use:

Printing FLTK Event Names
#include "eventnames.h"
:
class YourClass : public Fl_Window {
   int handle(int e) {
       :
       fprintf(stderr, "YOURCLASS EVENT: %s(%d)\n", eventnames[e], e);
       :
   }
};
    


Using popen() and Fl::add_fd()
The following code shows how to use popen() with Fl::add_fd().

You can highlight text and scroll while output is coming in, showing that the FLTK interface is 'alive' while the command is running.

The add_fd() technique works well under unix, but does not work under Windows.

Example: Using popen() and Fl::add_fd()

// demo use of popen() and Fl::add_fd() - erco 10/04/04
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Multi_Browser.H>
#include <stdio.h>
#include <unistd.h>

#define PING_CMD "ping -i 3 localhost"        // 'slow command' under unix

FILE *G_fp = NULL;

void HandleFD(int fd, void *data) {
    Fl_Multi_Browser *brow = (Fl_Multi_Browser*)data;
    char s[1024];
    if ( fgets(s, 1023, G_fp) == NULL ) {
        Fl::remove_fd(fileno(G_fp));
        pclose(G_fp);
        return;
    }
    brow->add(s);
}

int main() {
    Fl_Window win(600,600);
    Fl_Multi_Browser brow(10,10,580,580);
    if ( ( G_fp = popen(PING_CMD, "r") ) == NULL ) {
        perror("popen failed");
        return(1);
    }
    Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow);
    win.resizable(brow);
    win.show();
    return(Fl::run());
}
    

Here's a similar example, showing how a button can be used to invoke the command, and the button grays out and cursor changes to a timer while the command runs, and when the command finishes, the interface is restored to normal.



Fltk + Fifos
The following shows how to use fifos to control a console oriented menu program from fltk, showing its output in Fl_Browser. Unix only.

This demo uses a bourne shell script [menu.sh] to be the 'menu program' to be controlled by the fltk program [demo-fifo.cxx].

Example: Using FLTK with named pipes and popen()
[menu.sh]

#!/bin/bash while [ 1 ]; do echo "" echo "Menu Options" echo " a) Do an ls -la" echo " b) netstat" echo " c) date/uptime/w" echo " q) quit" echo "" echo "Your choice?" read choice if [ ! $? ]; then echo 'Parent closed'; exit 1; fi case $choice in a) ls -la ;; b) netstat ;; c) date; uptime; w ;; q) exit 0 ;; esac done [demo-fifo.cxx]
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> // mkfifo #include <sys/stat.h> // mkfifo #include <fcntl.h> // open #include <signal.h> // killpg #include <FL/Fl.H> #include <FL/Fl_Window.H> #include <FL/Fl_Multi_Browser.H> #include <FL/Fl_Button.H> // // Demonstrate how to use fltk with named pipes // erco 1.00 09/21/2005 // // Globals FILE *G_in = NULL; // how we read the child int G_out = 0; // how we write to the child // Close all descriptors, remove fifo void CleanExit_CB(Fl_Widget*, void *data) { unlink("tomenu.fifo"); killpg(getpid(), SIGKILL); _exit(0); } // Read output from child, append to browser void HandleInput_CB(int, void *data) { Fl_Multi_Browser *brow = (Fl_Multi_Browser*)data; static int x = 0; static char s[1024]; char c = fgetc(G_in); // read one char at a time if ( c == '\n' || x == (sizeof(s)-1) ) { s[x] = 0; brow->add(s); x = 0; } else { s[x++] = c; } } // Handle sending commands to child when button pressed void HandleButton_CB(Fl_Widget*, void *data) { write(G_out, data, strlen((char*)data)); // 'data' is a string, eg. "a\n" } int main() { // Process group leader (for killpg()) setsid(); // Make fifo unlink("tomenu.fifo"); if ( mkfifo("tomenu.fifo", 0666) < 0 ) { perror("mkfifo(tomenu.fifo)"); exit(1); } // Popen child for reading, set child to read fifo if ( ( G_in = popen("./menu.sh < tomenu.fifo", "r") ) == NULL ) { perror("popen failed"); exit(1); } setbuf(G_in, NULL); // disable buffering // Now open fifo if ( ( G_out = open("tomenu.fifo", O_WRONLY) ) < 0 ) { perror("open(tomenu.fifo) for write failed"); unlink("tomenu.fifo"); exit(1); } // Fltk stuff Fl_Window win(600,600); win.callback(CleanExit_CB); Fl_Button a(10, 10, 20, 20, "A"); a.callback(HandleButton_CB, (void*)"a\n"); Fl_Button b(30, 10, 20, 20, "B"); b.callback(HandleButton_CB, (void*)"b\n"); Fl_Button c(50, 10, 20, 20, "C"); c.callback(HandleButton_CB, (void*)"c\n"); Fl_Button q(70, 10, 20, 20, "q"); q.callback(CleanExit_CB, (void*)"q\n"); Fl_Multi_Browser brow(10,30,580,560); brow.textfont(FL_COURIER); Fl::add_fd(fileno(G_in), HandleInput_CB, (void*)&brow); win.resizable(brow); win.show(); return(Fl::run()); }


Tying an Fl_Slider and Fl_Int_Input Together
The following code shows how to tie Fl_Int_Input and Fl_Slider widgets together, making a new 'composite' widget called 'SliderInput'. The widget is derived from an Fl_Group, so that the two widgets can exist together.

Dragging the slider changes the input widget, and changing the input widget repositions the slider. eg:

SliderInput - Tie Fl_Slider and Fl_Int_Input together
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Slider.H>
#include <stdio.h>

// sliderinput -- simple example of tying an fltk slider and input widget together
// 1.00 erco 10/17/04

class SliderInput : public Fl_Group {
    Fl_Int_Input *input;
    Fl_Slider    *slider;

    // CALLBACK HANDLERS
    //    These 'attach' the input and slider's values together.
    //
    void Slider_CB2() {
        static int recurse = 0;
        if ( recurse ) { 
            return;
        } else {
            recurse = 1;
            char s[80];
            sprintf(s, "%d", (int)(slider->value() + .5));
            // fprintf(stderr, "SPRINTF(%d) -> '%s'\n", (int)slider->value(), s);
            input->value(s);    // pass slider's value to input
            recurse = 0;
        }
    }

    static void Slider_CB(Fl_Widget *w, void *data) {
        ((SliderInput*)data)->
        Slider_CB2();
    }

    void Input_CB2() {
        static int recurse = 0;
        if ( recurse ) {
            return;
        } else {
            recurse = 1;
            int val = 0;
            if ( sscanf(input->value(), "%d", &val) != 1 ) {
                val = 0;
            }
            // fprintf(stderr, "SCANF('%s') -> %d\n", input->value(), val);
            slider->value(val);         // pass input's value to slider
            recurse = 0;
        }
    }
    static void Input_CB(Fl_Widget *w, void *data) {
        ((SliderInput*)data)->
        Input_CB2();
    }

public:
    // CTOR
    SliderInput(int x, int y, int w, int h, const char *l=0) : Fl_Group(x,y,w,h,l) {
        int in_w = 40;
        int in_h = 25;

        input  = new Fl_Int_Input(x+10, y+10, in_w, in_h);
        input->callback(Input_CB, (void*)this);
        input->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);

        slider = new Fl_Slider(x+10+in_w, y+10, w - 20 - in_w, in_h);
        slider->type(1);
        slider->callback(Slider_CB, (void*)this);

        bounds(1, 10);     // some usable default
        value(5);          // some usable default
        end();             // close the group
    }

    // MINIMAL ACCESSORS --  Add your own as needed
    int value() { return((int)(slider->value() + 0.5)); }
    void value(int val) { slider->value(val); Slider_CB2(); }
    void minumum(int val) { slider->minimum(val); }
    int minumum() { return((int)slider->minimum()); }
    void maximum(int val) { slider->maximum(val); }
    int maximum() { return((int)slider->maximum()); }
    void bounds(int low, int high) { slider->bounds(low, high); }
};

int main() {
    Fl_Window win(240,90);
    SliderInput *si = new SliderInput(20,20,200,50,"Slider Input");
    si->color(44);
    si->box(FL_FLAT_BOX);
    si->bounds(1,100);
    si->value(50);
    win.show();
    return(Fl::run());
}
    


Fl_Slider with a floating tooltip to show current value
The following code shows how to get a floating tooltip to show the current value of the slider while it's being moved. Not well tested. 'It works under linux'. Example:

Fl_Slider with a floating tooltip to show current value

// Demonstrate a slider with tooltip that tracks the mouse - erco 11/18/04
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <FL/fl_draw.H>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Slider.H>
#include <FL/Fl_Menu_Window.H>
#include <FL/Fl_Tooltip.H>
// FLOATING TIP WINDOW
class TipWin : public Fl_Menu_Window {
    char tip[40];
public:
    TipWin():Fl_Menu_Window(1,1) {      // will autosize
        strcpy(tip, "X.XX");
        set_override();
        end();
    }
    void draw() {
        draw_box(FL_BORDER_BOX, 0, 0, w(), h(), Fl_Color(175));
        fl_color(FL_BLACK);
        fl_font(labelfont(), labelsize());
        fl_draw(tip, 3, 3, w()-6, h()-6, Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_WRAP));
    }
    void value(float f) {
        sprintf(tip, "%.2f", f);
        // Recalc size of window
        fl_font(labelfont(), labelsize());
        int W = w(), H = h();
        fl_measure(tip, W, H, 0);
        W += 8;
        size(W, H);
        redraw();
    }
};
// VALUE SLIDER WITH FLOATING TIP WINDOW
class MyValueSlider : public Fl_Slider {
    TipWin *tipwin;
    void value_cb2() {
        tipwin->value(value());
        tipwin->position(Fl::event_x_root(), Fl::event_y_root()+20);
    }
    static void value_cb(Fl_Widget*, void*data) {
        MyValueSlider *val = (MyValueSlider*)data;
        val->value_cb2();
    }
public:
    MyValueSlider(int x,int y,int w,int h,const char*l=0):Fl_Slider(x,y,w,h,l) {
        type(FL_HOR_SLIDER);
        callback(value_cb, (void*)this);
        Fl_Group *save = Fl_Group::current();   // save current widget..
        tipwin = new TipWin();                  // ..because this trashes it
        tipwin->hide();
        Fl_Group::current(save);                // ..then back to previous.
    }
    int handle(int e) {
        switch(e) {
            case FL_PUSH:
                // XXX: if offscreen, move tip ABOVE mouse instead
                tipwin->position(Fl::event_x_root(), Fl::event_y_root()+20);
                tipwin->value(value());
                tipwin->show();
                break;
            case FL_HIDE:       // valuator goes away
            case FL_RELEASE:    // release mouse
            case FL_LEAVE:      // leave focus
                // Make sure tipwin closes when app closes
                tipwin->hide();
                break;
        }
        return(Fl_Slider::handle(e));
    }
};

int main() {
    Fl_Double_Window *win = new Fl_Double_Window(640, 100);
    MyValueSlider *val1 = new MyValueSlider(20,20,200,30);
    MyValueSlider *val2 = new MyValueSlider(20,50,200,30);
    win->show();
    return(Fl::run());
}
    


Show Real-Time Output of Child Processes in Fl_Text_Display

The following is a unix example that starts three child processes, showing their real-time output in separate FLTK Fl_Text_Display widgets. Also demonstrates how to use Fl::add_fd(), Fl::remove_fd(), and the unix calls waitpid(), fork(), execlp() and pipe() all together in one example. When the window is closed, any child processes that were running are killed first.

The design is this:

As the processes run, FLTK's event loop invokes callbacks whenever data is generated by the child processes, thanks to FLTK's add_fd() mechanism. The FLTK event loop also handles all FLTK events, such as when the user moves the scrollbars, highlights text, etc.

This is unix specific code. (Won't work on Windows)

Fltk-Tty example

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Text_Display.H>

//
// fltk-ttys - Open several processes, display their output in fltk widgets
// Greg Ercolano 02/21/2005 1.00
//

// Globals
Fl_Text_Display *G_disp[3];     // one display per child
Fl_Text_Buffer *G_buff[3];      // one buffer per child
int G_outfd[3];                 // read pipe for child's stderr, one per child
pid_t G_pids[3];                // pid for each child

// Start child process, makes a read pipe to its stderr
void start_child(int t) {
    int out[2]; pipe(out);
    switch ( ( G_pids[t] = fork() ) ) {
        case -1: // Error
            close(out[0]); close(out[1]);
            perror("fork()");
            exit(1);
        case 0: // Child
            close(out[0]); dup2(out[1], 2); close(out[1]);
            switch (t) {
                case 0: execlp("/bin/sh", "sh", "-c", "ps auxww 1>&2",0);
                        perror("execlp(ps)");
                        exit(1);
                case 1: execlp("/bin/sh", "sh", "-c", "perl -e 'for($t=0; sleep(1); $t++)"
                               "{print STDERR rand().\"\\n\"; if ($t>5) {kill(9,$$);}}' 1>&2", 0);
                        perror("execlp(perl)");
                        exit(1);
                case 2: execlp("/bin/sh", "sh", "-c", "(ls -la; ping -c 8 localhost) 1>&2", 0);
                        perror("execlp(ls/ping)");
                        exit(1);
                default: exit(1);
            }
        default: // Parent
            G_outfd[t] = out[0]; close(out[1]);
            return;
    }
}

// Data ready interrupt
void data_ready(int fd, void *data) {
    int t = (int)data;
    char s[4096];
    int bytes = read(fd, s, 4096-1);
    // fprintf(stderr, "Data ready for %d) pid=%ld fd=%d bytes=%d\n", t, (long)G_pids[t], fd, bytes);  
    if ( bytes == -1 ) {                // ERROR
        perror("read()");
    } else if ( bytes == 0 ) {          // EOF
        G_buff[t]->append("\n\n*** EOF ***\n");
        int status;
        if ( waitpid(G_pids[t], &status, WNOHANG) < 0 ) {
            sprintf(s, "waitpid(): %s\n", strerror(errno));
        } else {
            if ( WIFEXITED(status) ) {
                sprintf(s, "Exit=%d\n", WEXITSTATUS(status));
                close(fd); Fl::remove_fd(fd); G_pids[t] = -1;
            } else if ( WIFSIGNALED(status) ) {
                sprintf(s, "Killed with %d\n", WTERMSIG(status));
                close(fd); Fl::remove_fd(fd); G_pids[t] = -1;
            } else if ( WIFSTOPPED(status) ) {
                sprintf(s, "Stopped with %d\n", WSTOPSIG(status));
            }
        }
        G_buff[t]->append(s);
    } else {                            // DATA
        s[bytes] = 0;
        G_buff[t]->append(s);
    }
}

// Clean up if someone closes the window
void close_cb(Fl_Widget*, void*) {
    printf("Killing child processes..\n");
    for ( int t=0; t<3; t++ ) {
        if ( G_pids[t] == -1 ) continue;
        kill(G_pids[t], 9);
    }
    printf("Done.\n");
    exit(0);
}

int main() {
    Fl_Double_Window win(620,520,"fltk-tty");
    win.callback(close_cb);             // kill children if window closed

    // Start children, one tty for each
    for ( int t=0; t<3; t++ ) {
        start_child(t);
        G_buff[t] = new Fl_Text_Buffer();
        G_disp[t] = new Fl_Text_Display(10+t*200, 10, 200, 500);
        G_disp[t]->buffer(G_buff[t]);
        G_disp[t]->textfont(FL_COURIER);
        G_disp[t]->textsize(12);
        Fl::add_fd(G_outfd[t], data_ready, (void*)t);
    }
    win.resizable(win);
    win.show();
    return(Fl::run());
}
    


Show how to draw a simple 'X' in FLTK


Here's how to make your own FLTK widget that just draws a simple 'X' to the four corners of the widget. A good starting point for anyone that wants to make a custom widget that uses custom drawing code.

This example uses FLTK's own drawing code, so it has no dependence on OpenGL libraries. If you want an OpenGL example, click here.

Example: Draw 'X'

// DEMONSTRATE HOW TO DRAW AN 'X' IN FLTK
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Double_Window.H>

// SIMPLE BOX WIDGET THAT DRAWS AN 'X'
class DrawX : public Fl_Widget {
public:
     DrawX(int X, int Y, int W, int H, const char*L=0) : Fl_Widget(X,Y,W,H,L) {
     }
     void draw() {
         // DRAW BLACK 'X'
         fl_color(FL_BLACK);
         fl_line(x(), y(),       x()+w()-1, y()+h()-1);
         fl_line(x(), y()+h()-1, x()+w()-1, y());
     }
};
int main() {
     Fl_Double_Window win(200,200);
     DrawX draw_x(0, 0, win.w(), win.h());
     win.resizable(draw_x);
     win.show();
     return(Fl::run());
}
    


How to make a Scrollable 'Canvas'

A scrollable version of the simple FLTK widget that just draws an 'X'. Scrollers let you pan around to look at the "canvas" drawing of the 'X'.

Example: A Scrollable Custom Widget "Canvas"

// DEMONSTRATE HOW TO MAKE A SCROLLABLE "CANVAS" DRAWING OF AN 'X'
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_draw.H>
// SCROLLABLE CANVAS EXAMPLE -- JUST DRAWS AN 'X'
class MyCanvas : public Fl_Widget {
public:
     MyCanvas(int X,int Y,int W,int H,const char*L=0) : Fl_Widget(X,Y,W,H,L) {
     }
     void draw() {
         // DRAW BG
         fl_color(color());
         fl_rectf(x(),y(),w(),h());
         // DRAW 'X' OVER BG
         //   Do your graphics here..
         //
         int x1=x(), y1=y();               // Fl_Scroll works by changing our widget's x() and y(),
         int x2=x()+w()-1, y2=y()+h()-1;   // so take these into account for our drawing coordinates  
         fl_color(FL_BLACK);
         fl_line(x1,y1,x2,y2);
         fl_line(x1,y2,x2,y1);
     }
};
int main() {
     Fl_Double_Window win(200,200);
       Fl_Scroll scroll(0,0,200,200);
         MyCanvas canvas(0,0,350,350);     // purposely make drawing area larger than scroll
       scroll.end();
     win.end();
     win.resizable(canvas);
     win.show();
     return(Fl::run());
}
    


Show how to draw a custom bar graph

          -----------     
        ---------
       --------
       ------ 
       -----
        ----
         ---
           -
    

The following is an example I posted to the newsgroup, in response to a request for a very specific 'trigger shaped' bar graph to appear in the left corner of an eliptical LCD screen.

Example: Custom 'Trigger Shaped' Bar Graph

// DEMONSTRATE HOW TO DRAW A TRIGGER SHAPED BAR GRAPH
#include <stdlib.h>
#include <math.h>
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Group.H>

// LED DISPLAY CLASS
class MyDisplay : public Fl_Group {
     int _value;         // 0 - 100
public:
     MyDisplay(int X, int Y, int W, int H, const char*L=0) : Fl_Group(X,Y,W,H,L) {
         _value = 0;
     }
     void value(int val) {
         _value = val;
         redraw();
     }
     void draw() {
         Fl_Group::draw();
         // TRIGGER GRAPHIC DRAWING CODE
         fl_color(42);
         int w = 1;
         for ( int t=0; t<_value; t+=2) {
             float f = ( t / 100.0 );            // f = 0.0 ~ 1.0
             float f2 = ( abs(t-50) / 100.0 );   // f = .5 ~ 0 ~ .5
             int xoff = (int)( f2 * f2 * 100 + .5);
             w += 1;
             int x1 = x() + 10 + xoff;
             int x2 = x() + 10 + w + xoff;
             int y1 = y() + h() - 10 - t;
             fl_line(x1, y1, x2, y1);
         }
     }
};
//
// TEST THE CLASS -- Send a sine wave to the class off a timer
//
MyDisplay *disp = 0;
void Timer_CB(void *) {
     static float f = 6.28/4; f += .1;
     float v = ( sin(f) + 1.0 ) * .5;    // sine wave: 0 ~ 1
     v = v * 100;                        // sine wave: 0 ~ 100
     disp->value((int)(v + .5));
     Fl::repeat_timeout(0.03, Timer_CB);
}

int main() {
     Fl_Double_Window win(240,240);
     disp = new MyDisplay(20,20,240-40,240-40);
     disp->box(FL_BORDER_BOX);
     disp->value(100);
     Fl::add_timeout(1.0, Timer_CB);
     win.show();
     return(Fl::run());
}
    


Cell Table

A table of Fl_Box, Fl_Input and Fl_Float_Input widgets.
You can change both the size of the window, and interactively
resize the individual cell rows and columns.

An example showing how one can create a table of cells using simple FLTK widgets, without needing an Fl_Table.

Example: Table of Cells

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Tile.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Float_Input.H>
#define COLS 9
#define ROWS 20
class RateTable : public Fl_Scroll {
    void *w[ROWS][COLS];        // widget pointers
public:
    RateTable(int X, int Y, int W, int H, const char*L=0) : Fl_Scroll(X,Y,W,H,L) {  
        static char *header[COLS] = {
            "Time",    "In Rate", "Out Rate", "Coeff A",
            "Coeff B", "Coeff C", "Std Dev",  "Pkg In",  "Pkg Out"
        };
        int cellw = 80;
        int cellh = 25;
        int xx = X, yy = Y;
        Fl_Tile *tile = new Fl_Tile(X,Y,cellw*COLS,cellh*ROWS);
        // Create widgets
        for ( int r=0; r<ROWS; r++ ) {
            for ( int c=0; c<COLS; c++ ) {
                if ( r==0 ) {
                    Fl_Box *box = new Fl_Box(xx,yy,cellw,cellh,header[c]);
                    box->box(FL_BORDER_BOX);
                    w[r][c] = (void*)box;
                } else if ( c==0 ) {
                    Fl_Input *in = new Fl_Input(xx,yy,cellw,cellh);
                    in->box(FL_BORDER_BOX);
                    in->value("");
                    w[r][c] = (void*)in;
                } else {
                    Fl_Float_Input *in = new Fl_Float_Input(xx,yy,cellw,cellh);
                    in->box(FL_BORDER_BOX);
                    in->value("0.00");
                    w[r][c] = (void*)in;
                }
                xx += cellw;
            }
            xx = X;
            yy += cellh;
        }
        tile->end();
        end();
    }
};

int main() {
    Fl_Double_Window win(720,486);
    RateTable rate(10,10,720-20,486-20);
    win.resizable(win);
    win.show();
    return(Fl::run());
}
    



Draggable Boxes on Scrollable Desk


A scrollable 'desk' containing boxes the user can click to drag around.

Demonstrates how to make boxes (containing images) that the user can click to drag around on a scrollable desktop.

Just click and drag the cat boxes around.. note that when you drag a box off the edge of the 'desktop', scrollbars appear, so you can scroll around the desktop to view all the boxes.

All of the work is done by FLTK.. only a few lines of custom code (shown in red) are needed to make the boxes 'draggable'.

Example: Draggable Boxes on a Scrollable Desk

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Pixmap.H>
#include <stdio.h>
//
// Demonstrate user-movable boxes in a scroll region
// erco@netcom.com 08/06/02
// erco@3dsite.com 01/06/05 -- added call to Fl_Box::handle()
//
static char *cat_xpm[] = {                       // XPM
"50 34 4 1",
"  c black",
"o c #ff9900",
"@ c #ffffff",
"# c None",
"##################################################",
"###      ##############################       ####",
"### ooooo  ###########################  ooooo ####",
"### oo  oo  #########################  oo  oo ####",
"### oo   oo  #######################  oo   oo ####",
"### oo    oo  #####################  oo    oo ####",
"### oo     oo  ###################  oo     oo ####",
"### oo      oo                     oo      oo ####",
"### oo       oo  ooooooooooooooo  oo       oo ####",
"### oo        ooooooooooooooooooooo        oo ####",
"### oo     ooooooooooooooooooooooooooo    ooo ####",
"#### oo   ooooooo ooooooooooooo ooooooo   oo #####",
"####  oo oooooooo ooooooooooooo oooooooo oo  #####",
"##### oo oooooooo ooooooooooooo oooooooo oo ######",
"#####  o ooooooooooooooooooooooooooooooo o  ######",
"###### ooooooooooooooooooooooooooooooooooo #######",
"##### ooooooooo     ooooooooo     ooooooooo ######",
"##### oooooooo  @@@  ooooooo  @@@  oooooooo ######",
"##### oooooooo @@@@@ ooooooo @@@@@ oooooooo ######",
"##### oooooooo @@@@@ ooooooo @@@@@ oooooooo ######",
"##### oooooooo  @@@  ooooooo  @@@  oooooooo ######",
"##### ooooooooo     ooooooooo     ooooooooo ######",
"###### oooooooooooooo       oooooooooooooo #######",
"###### oooooooo@@@@@@@     @@@@@@@oooooooo #######",
"###### ooooooo@@@@@@@@@   @@@@@@@@@ooooooo #######",
"####### ooooo@@@@@@@@@@@ @@@@@@@@@@@ooooo ########",
"######### oo@@@@@@@@@@@@ @@@@@@@@@@@@oo ##########",
"########## o@@@@@@ @@@@@ @@@@@ @@@@@@o ###########",
"########### @@@@@@@     @     @@@@@@@ ############",
"############  @@@@@@@@@@@@@@@@@@@@@  #############",
"##############  @@@@@@@@@@@@@@@@@  ###############",
"################    @@@@@@@@@    #################",
"####################         #####################",
"##################################################",
};

Fl_Double_Window *G_win    = NULL;
Fl_Scroll        *G_scroll = NULL;
static Fl_Pixmap  G_cat(cat_xpm);

#define BOXWIDTH  80
#define BOXHEIGHT 50

// A 'MOVABLE' BOX
class Box : public Fl_Box {
protected:
    int handle(int e) {
        static int offset[2] = { 0, 0 };
        int ret = Fl_Box::handle(e);
        switch ( e ) {
            case FL_PUSH:
                offset[0] = x() - Fl::event_x();    // save where user clicked for dragging
                offset[1] = y() - Fl::event_y();
                return(1);
            case FL_RELEASE:
                return(1);
            case FL_DRAG:
                position(offset[0]+Fl::event_x(), offset[1]+Fl::event_y());     // handle dragging
                G_win->redraw();
                return(1);
        }
        return(ret);
    }
public:
    Box(int X, int Y, int W, int H, const char *L=0) : Fl_Box(X,Y,W,H,L) {
        image(G_cat);
        box(FL_UP_BOX);
        color(FL_GRAY);
    }
    Box(int X, int Y) : Fl_Box(X,Y,BOXWIDTH,BOXHEIGHT,0) {
        image(G_cat);
        box(FL_UP_BOX);
        color(FL_GRAY);
    }
};

/// MAIN
int main() {
    G_win = new Fl_Double_Window(420,300);
    G_scroll = new Fl_Scroll(10,10,420-20,300-20);
    G_scroll->box(FL_FLAT_BOX);
    G_scroll->color(Fl_Color(46));
    G_scroll->begin();
    {
        // CREATE NEW BOXES ON THE SCROLLABLE 'DESK'
        for ( int x=20; x<=G_scroll->w()-BOXWIDTH; x+= BOXWIDTH+20)
            for ( int y=20; y<=G_scroll->h()-BOXHEIGHT; y+= BOXHEIGHT+20)
                new Box(x,y);
    }
    G_scroll->end();
    G_win->resizable(G_win);
    G_win->show();
    return(Fl::run());
}
    



Popup Menu Example

Demonstrates how to make a popup menu appear, so the user can choose an item, and cause a callback and return the menuitem.

Two examples below showing doing a popup menu with, and without callbacks. Sometimes popup menus are simple enough that all the functions can be done within a single procedure, avoiding the need for separate callbacks.

Example: Popup Menu (with callbacks)

// Popup menu using callbacks -erco
#include <FL/Fl.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Menu.H>
#include <stdio.h>
// Callback invoked when menu item selected
void handle_menu(Fl_Widget *w, void *v) {
    if(!w || !v) return;
    switch((int)v) {
        case 1: fl_choice("Thing 1 happened", "OK", NULL, NULL); break;
        case 2: fl_choice("Thing 2 happened", "OK", NULL, NULL); break;
        case 3: fl_choice("Thing 3 happened", "OK", NULL, NULL); break;
    }
}
// Callback invoked when button pushed
void push_cb(Fl_Widget *w, void*) {
    Fl_Menu_Item rclick_menu[] = {
        { "Do Thing 1",  0, handle_menu, (void*)1 },
        { "Do Thing 2",  0, handle_menu, (void*)2 },
        { "Do Thing 3",  0, handle_menu, (void*)3 },
        { 0 }
    };
    const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
    if ( m ) m->do_callback(w, m->user_data());
    return;
}
int main() {
    Fl_Window win(140,45,"Simple Popup Menu (using callbacks)");
    Fl_Button butt(10,10,120,25,"Push For Menu");
    butt.callback(push_cb);
    win.show();
    return(Fl::run());
}
    

Example: Popup Menu (WITHOUT callbacks)

// Popup menu WITHOUT callbacks -erco 09/07/09
#include <FL/Fl.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Menu.H>
#include <stdio.h>
// Callback invoked when button pushed
void push_cb(Fl_Widget *w, void*) {
    Fl_Menu_Item rclick_menu[] = {
        { "Do Thing 1" },
        { "Do Thing 2" },
        { "Do Thing 3" },
        { 0 }
    };
    const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
    if ( !m ) {
        return;
    } else if ( strcmp(m->label(), "Do Thing 1") == 0 ) {
        fl_choice("Thing 1 happened", "OK", NULL, NULL);
    } else if ( strcmp(m->label(), "Do Thing 2") == 0 ) {
        fl_choice("Thing 2 happened", "OK", NULL, NULL);
    } else if ( strcmp(m->label(), "Do Thing 3") == 0 ) {
        fl_choice("Thing 3 happened", "OK", NULL, NULL);
    }
    return;
}
int main() {
    Fl_Window win(140,45,"Simple Popup Menu (No callbacks)");
    Fl_Button butt(10,10,120,25,"Push For Menu");
    butt.callback(push_cb);
    win.show();
    return(Fl::run());
}
    



Right-Click Popup Copy/Paste Menu for Fl_Input

Demonstrates how to make a popup copy/paste menu appear over an Fl_Input widget.

There's probably a lot of ways to do this; this is probably the most explicit.

Example: Right-Click Popup Copy/Paste Menu for Fl_Input

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Menu.H>
#include <stdio.h>              // printf
//
// How to implement a copy/paste menu for Fl_Input -- erco 02/04/09
//
class MyInput : public Fl_Input {
    static void Copy_CB(Fl_Widget*, void *userdata) {
        printf("*** COPY ***\n");
        MyInput *in = (MyInput*)userdata;
        in->copy(0);    // text selection clipboard
        in->copy(1);    // copy/paste clipboard
    }
    static void Paste_CB(Fl_Widget*, void *userdata) {
        printf("*** PASTE ***\n");
        MyInput *in = (MyInput*)userdata;
        Fl::paste(*in);
    }
public:
    int handle(int e) {
        switch (e) {
            case FL_PUSH:
                // RIGHT MOUSE PUSHED? Popup menu on right click
                if ( Fl::event_button() == FL_RIGHT_MOUSE ) {
                    Fl_Menu_Item rclick_menu[] = {
                        { "Copy",   0, Copy_CB,  (void*)this },
                        { "Paste",  0, Paste_CB, (void*)this },
                        { 0 }
                    };
                    const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
                    if ( m ) m->do_callback(0, m->user_data());
                    return(1);          // (tells caller we handled this event)
                }
                break;
            case FL_RELEASE:
                // RIGHT MOUSE RELEASED? Mask it from Fl_Input
                if ( Fl::event_button() == FL_RIGHT_MOUSE ) {
                    return(1);          // (tells caller we handled this event)
                }
                break;
        }
        return(Fl_Input::handle(e));    // let Fl_Input handle all other events
    }
    MyInput(int X,int Y,int W,int H,const char*L=0):Fl_Input(X,Y,W,H,L) {
    }
};
int main() {
    Fl_Window win(200,45,"Test");
    MyInput input(50,10,120,25,"Text:");
    win.show();
    return(Fl::run());
}
    



Popup Text Window

Demonstrates how to popup a borderless text message window when user clicks anywhere in the window.

Example: Popup Text Window


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Menu_Window.H>
#include <FL/fl_draw.H>
#include <stdio.h>
//
// Demonstrate how to popup a simple window of information
// erco 1.1 01/04/06
//
class PopupWindow : public Fl_Menu_Window {
    Fl_Box *output;
    // Size window to just fit output's label text
    void SizeToText() {
        int W=0, H=0;
        fl_font(output->labelfont(), output->labelsize());
        fl_measure(output->label(), W, H, 0);
        resize(x(), y(), W+10, H+10);                           // +10: leaves +5 margin on all sides
        output->resize(0, 0, W+10, H+10);
    }
public:
    PopupWindow() : Fl_Menu_Window(10,10) {
        output = new Fl_Box(0, 0, w(), h());                    // box will have the text of user's msg
        output->box(FL_UP_BOX);                                 // popup window will have an 'Up Box' border
        end();
        hide();
        border(0);                                              // popup will be borderless
        output->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);           // text should be left aligned
        output->label("No text defined");                       // (default msg if none defined)
        SizeToText();
    }
    // Change text in box
    void text(const char*s) {
        output->label(s);                                       // set message text
        SizeToText();                                           // resize window to size of text
    }
    // Pop up window at current mouse position
    void popup() {
        position(Fl::event_x_root(), Fl::event_y_root());       // position window at cursor
        show();
    }
};

// A window where mouse events pops open text messages
class MyWindow : public Fl_Window {
    PopupWindow *popup;
    int handle(int e) {
        int ret = Fl_Window::handle(e);
        switch (e) {
            case    FL_PUSH: popup->popup(); return(1);
            case FL_RELEASE: popup->hide(); return(1);
        }
        return(ret);
    }

public:
    MyWindow(int w,int h) : Fl_Window(w,h) {
        popup = new PopupWindow();
        popup->text("This is a test\nSo is this, a much longer line of text.");
        end();
    }
};

int main(int argc, char** argv) {
    MyWindow win(300,300);
    win.show();
    return(Fl::run());
}
    



Draw Mouse Coordinates Example

Screen shows x/y coords of mouse in upper left, while large font shows the full screen's redraw count to show that the entire screen is not being redrawn as the mouse is moved.

Demonstrates how to draw the coordinates of a mouse into a window without causing the entire screen to redraw, without using overlay planes, separate widgets or windows for the coordinates.

I liked Hartmut's idea of only drawing the coords over the graphic, avoiding overlays and complete redraws, while still updating the coords IN the screen area, and doing it fast.

Here's a cute technique abusing damage(FL_DAMAGE_USER1), to avoid the whole problem of keeping a separate 'flag'. This works in the knowledge that setting the damage flag also causes a redraw, and the flag gets ORed with other bits if other kinds of damage occur.

Note that the main screen graphic counts the redraws, so you can see whenever the entire screen is redrawn. While moving around the mouse, /only/ the coords will change, showing entire redraws are being avoided.

To get the screen to fully redraw, you can resize the screen, or on some window managers, overlaying then revealing again will count it up.

Example: Drawing Mouse Coordinates Without Redrawing Entire Screen

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Group.H>
#include <FL/fl_draw.H>
#include <stdio.h>
// Demonstrate drawing mouse coords:
//      o w/out redrawing entire screen
//      o w/out using overlay planes or windows
//      o w/out using XOR mode
class MyDesk : public Fl_Group {
protected:
    int handle(int e) {
        int ret = Fl_Group::handle(e);
        switch ( e ) {
            case FL_ENTER:
                ret = 1;                // FL_ENTER: must return(1) to receive FL_MOVE
                break;
            case FL_MOVE:               // FL_MOVE: mouse movement causes 'user damage' and redraw..
                damage(FL_DAMAGE_USER1);
                ret = 1; 
                break;
        }
        return(ret);
    }
    // Draw mouse coords in small black rectangle
    void draw_coords() {
        // Coordinates as a string
        char s[80];
        sprintf(s, "x=%d y=%d", (int)Fl::event_x(), (int)Fl::event_y());
        // Black rect
        fl_color(FL_BLACK);
        fl_rectf(10,10,200,25);
        // White text
        fl_color(FL_WHITE);
        fl_font(FL_HELVETICA, 18);
        fl_draw(s, 15, 25);
    }
    void draw() {
        // User damage ONLY? just draw coords and done
        if ( damage() == FL_DAMAGE_USER1 ) {
            draw_coords();
            return;
        }
        // Let group draw itself
        Fl_Group::draw(); 
        {
            // Show redraw count, so we can tell when full redraws occur.
            static int redraws = 0;
            char s[80]; sprintf(s, "redraw #%d", ++redraws);
            fl_color(FL_BLACK);
            fl_font(FL_COURIER, 80);
            fl_draw(s, 50, h()/2);
        }
        // Draw coords last
        draw_coords();
    }
public:
    MyDesk(int X, int Y, int W, int H, const char *L=0) : Fl_Group(X,Y,W,H,L) {
        color(48);
    }
};

/// MAIN
int main() {
    Fl_Window win(720,486);
    MyDesk desk(10,10,700,466);
    win.resizable(win);
    win.show();
    return(Fl::run());
}
    



Progress Bar Example

Demonstrates how to update a progress bar within a cpu intensive operation.

Example: Progress Bar Demonstration

#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Progress.H>

// Demonstrate progress bar in app window (windows|linux)
// erco 05/02/05

#ifdef _WIN32
// WINDOWS
#include <windows.h>
#define usleep(v) Sleep(v/1000)
#else
// UNIX
#include <unistd.h>                     // usleep
#endif

// Button callback
void butt_cb(Fl_Widget *butt, void *data) {

    // Deactivate the button
    butt->deactivate();                 // prevent button from being pressed again                   
    Fl::check();                        // give fltk some cpu to gray out button

    // Make the progress bar
    Fl_Window *w = (Fl_Window*)data;    // access parent window
    w->begin();                         // add progress bar to it..
    Fl_Progress *progress = new Fl_Progress(10,50,200,30);
    progress->minimum(0);               // set progress bar attribs..
    progress->maximum(1);
    w->end();                           // end of adding to window

    // Computation loop..
    for ( int t=1; t<=500; t++ ) {
        progress->value(t/500.0);       // update progress bar
        Fl::check();                    // give fltk some cpu to update the screen
        usleep(1000);                   // 'your stuff' that's compute intensive
    }

    // Cleanup
    w->remove(progress);                // remove progress bar from window
    delete(progress);                   // deallocate it
    butt->activate();                   // reactivate button
    w->redraw();                        // tell window to redraw now that progress removed
}

// Main
int main() {
    Fl_Window win(220,90);
    Fl_Button butt(10,10,100,25,"Press");
    butt.callback(butt_cb, &win);
    win.resizable(win);
    win.show();
    return(Fl::run());
}
    



Scrollable Image Viewer

Demonstrates how to display an image in a scrollable window.

Example: Scrollable Image Viewer

#include <stdlio.h>
#include <stdlib.h>
#include <FL/Fl.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_JPEG_Image.H>
#include <FL/Fl_Box.H>

#define JPGFILE "/var/tmp/foo.jpg"

// Show a jpg image in a scrolled window - erco 05/07/2005

int main() {
    fl_register_images();
    Fl_Double_Window win(720,486);
    Fl_Scroll scr(0,0,720,486);
    Fl_JPEG_Image jpg(JPGFILE);
    if ( jpg.h() == 0 ) { perror(JPGFILE); exit(1); }    // error check
    Fl_Box box(0,0,jpg.w(),jpg.h());
    box.image(jpg);
    win.resizable(win);
    win.show();
    return(Fl::run());
}

    



Scrollable Widget Browser

Demonstrates how to make a scrollable browser of widgets where children of the scroll follow a change in size of the scroller.

Example: Scrollable widget 'browser'

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Box.H>

// demonstrate a scrollable browser of widgets where children follow size
// erco 05/20/05
//
// dialog
// -------------------------------------------
// |   scroll                                  |
// |   -------------------------------------   |
// |  | fixed   |  stretch               | ^|  |
// |  |_________|________________________|--|  |
// |  | fixed   |  stretch               |  |  |
// |  |_________|________________________|  |  |
// |  | fixed   |  stretch               |  |  |
// |  |_________|________________________|  |  |
// |  | fixed   |  stretch               |  |  |
// |  |_________|________________________|__|  |
// |  |__________________________________| v|  |
// |                                      --   |
// |                              _______      |
// |                             |  ADD  |     |
// |                              -------      |
// |___________________________________________|
//


const int fixedWidth = 50;
const int defaultHeight = 25;

// Combo widget to appear in the scroll, two boxes: one fixed, the other stretches
class ScrollItem : public Fl_Group {
    Fl_Box *fixedBox;
    Fl_Box *stretchBox;
public:
    ScrollItem(int X, int Y, int W, int H, const char* L=0) : Fl_Group(X,Y,W,H,L) {
        begin();
            // Fixed width box
            fixedBox = new Fl_Box(X,Y,fixedWidth,defaultHeight,"Fixed");
            fixedBox->box(FL_UP_BOX);
            // Stretchy box
            stretchBox = new Fl_Box(X+fixedWidth,Y,W-fixedWidth,defaultHeight, "Stretch");
            stretchBox->box(FL_UP_BOX);
            resizable(stretchBox);
        end();
    }
};

// Custom scroll that tells children to follow scroll's width when resized
class MyScroll : public Fl_Scroll {
    int nchild;
public:
    MyScroll(int X, int Y, int W, int H, const char* L=0) : Fl_Scroll(X,Y,W,H,L) {
        nchild = 0;
    }
    void resize(int X, int Y, int W, int H) {
        // Tell children to resize to our new width
        for ( int t=0; t<nchild; t++ ) {
            Fl_Widget *w = child(t);
            w->resize(w->x(), w->y(), W-20, w->h());    // W-20: leave room for scrollbar
        }
        // Tell scroll children changed in size
        init_sizes();
        Fl_Scroll::resize(X,Y,W,H);
    }
    
    // Append new scrollitem to bottom
    //     Note: An Fl_Pack would be a good way to do this, too
    //
    void AddItem() {
        int X = x() + 1,
            Y = y() - yposition() + (nchild*defaultHeight) + 1,
            W = w() - 20,                           // -20: compensate for vscroll bar
            H = defaultHeight;
        add(new ScrollItem(X,Y,W,H));
        redraw();
        nchild++;
    }
};

// Callback to add new item to scroll
void add_cb(Fl_Widget*, void *data) {
    MyScroll *scroll = (MyScroll*)data;
    scroll->AddItem();
}

// Main
int main() {
    Fl_Double_Window *win = new Fl_Double_Window(300,300);
    MyScroll *scroll = new MyScroll(10,10,win->w()-20,win->h()-60);
    scroll->box(FL_BORDER_BOX);
    scroll->end();
    Fl_Button *add_butt = new Fl_Button(win->w()-150, win->h()-40, 100, 25, "Add");
    add_butt->callback(add_cb, (void*)scroll);
    // Create a few widgets to start with
    for ( int t=0; t<4; t++ ) {
        scroll->AddItem();
    }
    win->resizable(scroll);
    win->show();
    return(Fl::run());
}
    



How To Globally Disable @ Symbols

Demonstrates how to globally disable fltk's '@' symbols throughout your app. Also shows how to define your own global label drawing code.

Example: Disable Symbols

//
// Example showing how to disable FLTK symbols globally
// erco 05/31/2005
//
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Button.H>
#include <FL/fl_draw.H>
static int G_usesymbols = 1;

// Global FLTK callback for drawing all label text
void MyDraw(const Fl_Label *o, int X, int Y, int W, int H, Fl_Align a) {
    fl_font(o->font, o->size);
    fl_color((Fl_Color)o->color);
    fl_draw(o->value, X, Y, W, H, a, o->image, G_usesymbols);
}

// Global FLTK callback for measuring all labels
void MyMeasure(const Fl_Label *o, int &W, int &H) {
    fl_font(o->font, o->size);
    fl_measure(o->value, W, H, G_usesymbols);
}

// Turn symbols on or off
void FltkUseSymbols(int onoff) {
    G_usesymbols = onoff;
    Fl::set_labeltype(FL_NORMAL_LABEL, MyDraw, MyMeasure);   // real action here
}

// Button callback to toggle symbols on/off
void Button_CB(Fl_Widget*,void*data) {
    FltkUseSymbols(G_usesymbols ? 0 : 1);
    Fl_Window *win = (Fl_Window*)data; win->redraw();
}
int main() {
    Fl_Double_Window win(0,0,400,200);
    Fl_Choice choice(100,10,120,25,"Email@3Dsite");
    choice.add("abc@->");
    choice.add("ABC@<-");
    choice.value(0);
    Fl_Button butt(100,45,120,25,"Toggle Symbols");
    butt.callback(Button_CB, (void*)&win);
    win.resizable(win);
    win.show();
    return(Fl::run());
}
    



OpenGL Simple Example

Demonstrates simplest OpenGL example in Fltk, simply drawing an 'X'.

Example: OpenGL Simple Example

#include <FL/Fl.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>

//
// Simple resizable 2D GL window
// erco 10/08/05
//

class MyGlWindow : public Fl_Gl_Window {
    // DRAW METHOD
    //      OpenGL window: (w,h) is upper right, (-w,-h) is lower left, (0,0) is center
    //
    void draw() {
        // First time? init viewport, etc.
        if (!valid()) {
            valid(1);
            glLoadIdentity();
            glViewport(0,0,w(),h());
            glOrtho(-w(),w(),-h(),h(),-1,1);
        }
        // Clear screen
        glClear(GL_COLOR_BUFFER_BIT);
        // Draw white 'X'
        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_LINE_STRIP); glVertex2f(w(), h()); glVertex2f(-w(),-h()); glEnd();
        glBegin(GL_LINE_STRIP); glVertex2f(w(),-h()); glVertex2f(-w(), h()); glEnd();
    }
    // HANDLE WINDOW RESIZING
    //    If window reshaped, need to readjust viewport/ortho
    //
    void resize(int X,int Y,int W,int H) {
        Fl_Gl_Window::resize(X,Y,W,H);
        glLoadIdentity();
        glViewport(0,0,W,H);
        glOrtho(-W,W,-H,H,-1,1);
        redraw();
    }
public:
    // CONSTRUCTOR
    MyGlWindow(int X,int Y,int W,int H,const char*L=0) : Fl_Gl_Window(X,Y,W,H,L) {
    }
};
// MAIN
int main() {
     Fl_Window win(500, 300);
     MyGlWindow mygl(10, 10, win.w()-20, win.h()-20);
     win.end();
     win.resizable(mygl);
     win.show();
     return(Fl::run());
}
    



OpenGL Example with Widgets


Demonstrates a simple OpenGL application that includes FLTK widgets to control the brightness of the OpenGL's contents (an 'X').

Example: OpenGL Application With Widgets

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Value_Slider.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>

//
// OpenGL App With FLTK Widgets
// erco 11/08/06
//

// OPENGL WINDOW CLASS
class MyGlWindow : public Fl_Gl_Window {
    double fg;                       // foreground brightness
    double bg;                       // background brightness
    // FIX OPENGL VIEWPORT
    //     Do this on init or when window's size is changed
    void FixViewport(int W,int H) {
        glLoadIdentity();
        glViewport(0,0,W,H);
        glOrtho(-W,W,-H,H,-1,1);
    }
    // DRAW METHOD
    void draw() {
        if (!valid()) { valid(1); FixViewport(w(), h()); }      // first time? init
        // Clear screen to bg color
        glClearColor(bg, bg, bg, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        // Draw 'X' in fg color
        glColor3f(fg, fg, fg);
        glBegin(GL_LINE_STRIP); glVertex2f(w(), h()); glVertex2f(-w(),-h()); glEnd();
        glBegin(GL_LINE_STRIP); glVertex2f(w(),-h()); glVertex2f(-w(), h()); glEnd();
    }
    // HANDLE WINDOW RESIZING
    void resize(int X,int Y,int W,int H) {
        Fl_Gl_Window::resize(X,Y,W,H);
        FixViewport(W,H);
        redraw();
    }
public:
    // OPENGL WINDOW CONSTRUCTOR
    MyGlWindow(int X,int Y,int W,int H,const char*L=0) : Fl_Gl_Window(X,Y,W,H,L) {
        fg = 1.0;
        bg = 0.0;
        end();
    }
    void SetBrightness(double new_fg, double new_bg)
        { fg = new_fg; bg = new_bg; redraw(); }
};

// APP WINDOW CLASS
class MyAppWindow : public Fl_Window {
    MyGlWindow *mygl;                    // opengl window
    Fl_Value_Slider *fg_brightness;      // fg brightness slider
    Fl_Value_Slider *bg_brightness;      // bg brightness slider
private:
    // Someone changed one of the sliders
    void ValueChanged_CB2() {
        mygl->SetBrightness(fg_brightness->value(), bg_brightness->value());
    }
    static void ValueChanged_CB(Fl_Widget*, void*userdata) {
        MyAppWindow *appwin = (MyAppWindow*)userdata;
        appwin->ValueChanged_CB2();
    }
public:
    // APP WINDOW CONSTRUCTOR
    MyAppWindow(int W,int H,const char*L=0) : Fl_Window(W,H,L) {
        // OpenGL window
        mygl = new MyGlWindow(10, 10, w()-20, h()-80);
        // Foreground slider
        fg_brightness = new Fl_Value_Slider(120, h()-60, w()/2, 20, "FG Bright");
        fg_brightness->align(FL_ALIGN_LEFT);
        fg_brightness->type(FL_HOR_SLIDER);
        fg_brightness->bounds(0.0, 1.0);
        fg_brightness->value(1.0);
        fg_brightness->callback(ValueChanged_CB, (void*)this);
        // Background slider
        bg_brightness = new Fl_Value_Slider(120, h()-30, w()/2, 20, "BG Bright");
        bg_brightness->align(FL_ALIGN_LEFT);
        bg_brightness->type(FL_HOR_SLIDER);
        bg_brightness->bounds(0.0, 1.0);
        bg_brightness->value(0.0);
        bg_brightness->callback(ValueChanged_CB, (void*)this);
        end();
    }
};

// MAIN
int main() {
    MyAppWindow win(500, 300, "OpenGL Test App");
    win.resizable(win);
    win.show();
    return(Fl::run());
}
    



OpenGL Shape Interpolation


Demonstrates how to animate simple shape interpolation in opengl, using a 24fps timer to run the animation smoothly. (Image above shows only a few frames to give a rough idea of the demo)

Example: Simple OpenGL Shape Interpolation

#include <FL/Fl.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
#include <math.h>

//
// Demonstrate interpolating shapes
// erco 06/10/05
//

class Playback : public Fl_Gl_Window {
    int frame;
    // Linear interpolation between two values based on 'frac' (0.0=a, 1.0=b)
    float Linterp(float frac, float a, float b) {
        return( a + ( frac * (b - a) ));
    }
    // Sinusoidal easein/easeout interpolation between two values based on 'frac' (0.0=a, 1.0=b)
    float SinInterp(float frac, float a, float b) {
        float pi = 3.14159;
        frac = (sin(pi/2 + frac*pi ) + 1.0 ) / 2.0;     // 0 ~ 1 -> 0 ~ 1
        return(Linterp(frac,a,b));
    }
    // DRAW SIMPLE SHAPE INTERPOLATION
    //     Interpolation is based on the current frame number
    //
    void DrawShape(int frame) {
        // Calculate a fraction that represents the frame# being shown
        float frac = ( frame % 48 ) / 48.0 * 2;
        if ( frac > 1.0 ) frac = 2.0-frac;      // saw tooth wave:  "/\/\/\"

        static float a_xy[9][2] = {
            { -.5, -1. }, { 0.0, -.5 }, { -.5, -1. }, { 0.0, -.5 },
            { 0.0, 0.0 },
            { 0.0, -.5 }, { +.5, -1. }, { 0.0, -.5 }, { +.5, -1. },
        };
        static float b_xy[9][2] = {
            { -.25, -1. }, { -.50, -.75 }, { -.75, -1.0 }, { -.50, -.75 },
            { 0.0, 0.0 },
            { +.50, -.75 }, { +.75, -1.0 }, { +.50, -.75 }, { +.25, -1.0 }
        };
        // Linterp a and b to form new shape c
        float c_xy[9][2];
        for ( int i=0; i<9; i++ )
            for ( int xy=0; xy<2; xy++ )
                c_xy[i][xy] = SinInterp(frac, a_xy[i][xy], b_xy[i][xy]);
        // Draw shape
        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_LINE_STRIP);
        for ( int i=0; i<9; i++ )
            glVertex2f(c_xy[i][0], c_xy[i][1]);
        glEnd();
    }
    // DRAW THE WIDGET
    //    Each time we're called, assume
    //
    void draw() {
        if (!valid()) {
            valid(1);
            glLoadIdentity();
            glViewport(0,0,w(),h());
        }
        glClear(GL_COLOR_BUFFER_BIT);
        // Draw shape 4x, rotated at 90 degree positions
        glPushMatrix();
            DrawShape(frame); glRotatef(90.0, 0, 0, 1);
            DrawShape(frame); glRotatef(90.0, 0, 0, 1);
            DrawShape(frame); glRotatef(90.0, 0, 0, 1);
            DrawShape(frame);
        glPopMatrix();
        // Advance frame counter
        ++frame;
    }
    // 24 FPS TIMER CALLBACK
    //     Called 24x per second to redraw the widget
    //
    static void Timer_CB(void *userdata) {
        Playback *pb = (Playback*)userdata;
        pb->redraw();
        Fl::repeat_timeout(1.0/24.0, Timer_CB, userdata);
    }
public:
    // Constructor
    Playback(int X,int Y,int W,int H,const char*L=0) : Fl_Gl_Window(X,Y,W,H,L) {
        frame = 0;
        Fl::add_timeout(1.0/24.0, Timer_CB, (void*)this);       // 24fps timer
        end();
    }
};

int main() {
     Fl_Window win(500, 500);
     Playback  playback(10, 10, win.w()-20, win.h()-20);
     win.resizable(&playback);
     win.show();
     return(Fl::run());
}
    



OpenGL Sphere With Light


Example of an opengl sphere with a light and materials.
Uses glut only for creating the sphere.

OpenGL Sphere With Light

#ifdef _WIN32
#include <windows.h>
#endif
#include <math.h>
#include <FL/Fl.h>
#include <FL/Fl_Window.h>
#include <FL/Fl_Gl_Window.h>
#include <FL/gl.h>
#include <FL/glut.h>
//
// Render a simple opengl shaded sphere with a single side light -- erco 11/28/08
//     NOTE: Glut needed *only* for glutSolidSphere()
//
class MyGlWindow : public Fl_Gl_Window {
public:
    // RESHAPE THE VIEWPORT
    void Reshape(GLfloat W, GLfloat H) {
        // (REFERENCE: SGI light.c DEMO)
        GLfloat ratio = W / H;
        glViewport(0, 0, (GLsizei)W, (GLsizei)H);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-1.5*ratio, 1.5*ratio, -1.5, 1.5, -10.0, 10.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    }
    void draw() {
        if (!valid()) {
            valid(1);
            Reshape(w(), h());
            // (REFERENCE: SGI 'light.c' EXAMPLE)
            GLfloat mat_ambient[]    = { 1.0, 1.0, 1.0, 1.0 };  // RGBA
            GLfloat mat_diffuse[]    = { 1.0, 1.0, 1.0, 1.0 };  // RGBA
            GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };  // RGBA
            GLfloat light_position[] = { 5.0, 5.0, 0.0, 0.0 };  // XYZ
            glClearColor(0.0, 0.0, 0.4, 0.0);                   // bg color
            glShadeModel(GL_SMOOTH);
            //
            glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
            glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
            glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
            glMaterialf(GL_FRONT,  GL_SHININESS, 20.0);
            glLightfv(GL_LIGHT0, GL_POSITION, light_position);
            //
            glEnable(GL_LIGHTING);
            glEnable(GL_LIGHT0);
            glEnable(GL_DEPTH_TEST);
        }
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glPushMatrix();
            glColor3f(0.5, 0.5, 0.5);
            glutSolidSphere(0.5, 30, 30);
        glPopMatrix();
    }
    // CTOR
    MyGlWindow(int X,int Y,int W,int H,const char*L=0) : Fl_Gl_Window(X,Y,W,H,L) {
    }
};
int main() {
     Fl_Window win(640, 480, "sphere");
     MyGlWindow mygl(10, 10, win.w()-20, win.h()-20);
     win.resizable(&mygl);
     win.show();
     return(Fl::run());
}



Fl_File_Chooser Example

What follows is a simple example of how to use Fl_File_Chooser. Or, you can look at this more thorough example.

Example: Fl_File_Chooser Example Usage

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_File_Chooser.H>

//
// Demonstrate how to use Fl_File_Chooser
// erco 08/04/2005
//

// Callback: when use picks 'File | Open' from main menu
void open_cb(Fl_Widget*, void*) {

    // Create the file chooser, and show it
    Fl_File_Chooser chooser(".",                        // directory
                            "*",                        // filter
                            Fl_File_Chooser::MULTI,     // chooser type
                            "Title Of Chooser");        // title
    chooser.show();

    // Block until user picks something.
    //     (The other way to do this is to use a callback())
    //
    while(chooser.shown())
        { Fl::wait(); }

    // User hit cancel?
    if ( chooser.value() == NULL )
        { fprintf(stderr, "(User hit 'Cancel')\n"); return; }

    // Print what the user picked
    fprintf(stderr, "--------------------\n");
    fprintf(stderr, "DIRECTORY: '%s'\n", chooser.directory());
    fprintf(stderr, "    VALUE: '%s'\n", chooser.value());
    fprintf(stderr, "    COUNT: %d files selected\n", chooser.count());

    // Multiple files? Show all of them
    if ( chooser.count() > 1 ) {
        for ( int t=1; t<=chooser.count(); t++ ) {
            fprintf(stderr, " VALUE[%d]: '%s'\n", t, chooser.value(t));
        }
    }
}

// Callback: when user picks 'Quit'
void quit_cb(Fl_Widget*, void*) {
    exit(0);
}

int main() {
    Fl_Window win(300, 180, "Simple Example of Fl_File_Chooser");
    Fl_Menu_Bar menubar(0,0,300,25);
    menubar.add("File/Open", 0, open_cb);
    menubar.add("File/Quit", 0, quit_cb);
    win.show();
    return(Fl::run());
}
    



Get coordinates for mouse clicks on scrollable box

This example shows how to get the coordinates for mouse click events on a scrollable box, the coordinates being relative to the corners of the box.

This can be usful, for instance, in an application that shows large images inside a scroll; allowing the user to click on the image, so as to read back and display pixel data and x/y pixel coordinates on the image, taking into account the scroll positions of the image.

Shows how to use the mouse events (which are relative to the corner of the fltk window) and the offset of the scrollbars to calculate where in the box (or image) the user clicked.

Example: Getting Mouse Clicks on a Scrollable Box

#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Box.H>
//
// Demonstrate getting mouse click coords on a scrollable box
// erco 08/14/2005
//
class ScrollBox : public Fl_Box {
    Fl_Scroll *scroll;
public:
    int handle(int e) {
        if ( e == FL_PUSH ) {
            fprintf(stderr, "event_x,event_y: %d,%d, Hit on box: %d,%d\n",
                Fl::event_x(),
                Fl::event_y(),
                Fl::event_x() - scroll->x() + scroll->hscrollbar.value(),
                Fl::event_y() - scroll->y() + scroll->scrollbar.value());
        }
        return(Fl_Box::handle(e));
    }
    void SetScroll(Fl_Scroll *val) {
        scroll = val;
    }
    ScrollBox(int x,int y,int w,int h,const char*l=0) : Fl_Box(x,y,w,h,l) {
        color(FL_BLUE);
        box(FL_FLAT_BOX);
    }
};
int main() {
    Fl_Double_Window win(400, 400);
    Fl_Scroll scroll(0,0,400,400);
    ScrollBox box(0,0,1000,1000);       // box is bigger than scroll
    box.SetScroll(&scroll);
    scroll.end();
    win.resizable(win);
    win.show();
    return(Fl::run());
} 
    



Using Fl_Browser with columns

This example shows how to use the Fl_Browser column_widths() and column_char() methods for presenting data in columns (even in non-fixed-width fonts).

Example: Using Fl_Browser with columns

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Browser.H>
//
// Demonstrate Fl_Browser with columns
// erco 09/16/05
//
int main() {
    Fl_Window  *w = new Fl_Window(900,300);
    Fl_Browser *b = new Fl_Browser(10,10,w->w()-20,w->h()-20);
    int widths[] = { 50, 50, 50, 70, 70, 40, 40, 70, 70, 50, 0 };               // widths for each column
    b->column_widths(widths);
    b->column_char('\t');                                                       // tabs as column delimiters
    b->type(FL_MULTI_BROWSER);
    b->add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND"); // lines of tab delimited data
    b->add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
    b->add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
    b->add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
    b->add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
    b->add("root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear");
    w->resizable(b);
    w->end();
    w->show();
    return(Fl::run());
} 
    



Fl_Browser Sorting

This example shows how to sort the lines in an Fl_Browser.

Example: Sorting an Fl_Browser

#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Browser.H>
//
// Demo sorting an Fl_Browser with bubble sort
// erco 09/02/2005
//
void ForwardSort(Fl_Browser *b) {
    for ( int t=1; t<=b->size(); t++ ) {
        for ( int r=t+1; r<=b->size(); r++ ) {
            if ( strcmp(b->text(t), b->text(r)) > 0 ) {
                b->swap(t,r);
            }
        }
    }
}
void ReverseSort(Fl_Browser *b) {
    for ( int t=1; t<=b->size(); t++ ) {
        for ( int r=t+1; r<=b->size(); r++ ) {
            if ( strcmp(b->text(t), b->text(r)) < 0 ) {
                b->swap(t,r);
            }
        }
    }
}
void togglesort_cb(Fl_Widget *w, void*data) {
    Fl_Browser *b = (Fl_Browser*)data;
    if ( strcmp(w->label(), "Do Fwd Sort") == 0 ) {
        ForwardSort(b);
        w->label("Do Rev Sort");                // toggle
    } else {
        ReverseSort(b);
        w->label("Do Fwd Sort");                // toggle
    }
}
int main() {
    Fl_Window *win  = new Fl_Window(300,500,"Sort Example");
    Fl_Browser *b = new Fl_Browser(10,40,win->w()-20, win->h()-50);
    b->type(FL_MULTI_BROWSER);
    b->add("Papa");     b->add("Delta"); b->add("Hotel");
    b->add("Charlie");  b->add("Echo");  b->add("Foxtrot");
    b->add("Golf");     b->add("Lima");  b->add("Victor");
    b->add("Alpha");    b->add("Xray");  b->add("Yankee");
    b->add("Oscar");    b->add("India"); b->add("Juliet");
    b->add("Kilo");     b->add("Mike");  b->add("Sierra");
    b->add("November"); b->add("Tango"); b->add("Quebec");
    b->add("Bravo");    b->add("Romeo"); b->add("Uniform");
    b->add("Whisky");   b->add("Zulu");
    Fl_Button *butt = new Fl_Button(10,10,100,28,"Do Fwd Sort");
    butt->callback(togglesort_cb, (void*)b);
    win->show();
    return(Fl::run());
}
    



Strike Through Text

Demonstrates how to draw 'strikethrough' text.
A slider is included to show how font size changes look with the strikethrough line.

You can uncomment the 'DRAW UNDERLINE' code to do underlining as well.

Example: Drawing Strikethrough Text

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Value_Slider.H>
#include <FL/fl_draw.H>
//
// Demonstrate strikethrough text
// erco 10/09/05
//
class MyBox : public Fl_Box {
    void draw() {
        if ( label() ) {
            Fl_Box::draw_box();
            // DRAW LABEL IN CENTER OF WIDGET
            int label_x, label_y, label_w, label_h;
            fl_font(labelfont(), labelsize());
            fl_measure(label(), label_w, label_h, 1);
            fl_color(labelcolor());
            label_x = x() + (w() / 2) - label_w / 2;    // always center text
            label_y = y() + (h() / 2) - label_h / 2;
            fl_draw(label(), label_x, label_y+labelsize());
            // DRAW STRIKETHROUGH
            fl_line_style(FL_SOLID);
            fl_line(label_x, label_y+(labelsize()/3*2), label_x+label_w, label_y+(labelsize()/3*2));
            //// // DRAW UNDERLINE
            //// fl_line_style(FL_SOLID);
            //// fl_line(label_x, label_y+labelsize(), label_x+label_w, label_y+labelsize()+1);
        }
    }
public:
    // CONSTRUCTOR
    MyBox(int X,int Y,int W,int H,const char*L=0) : Fl_Box(X,Y,W,H,L) {
    }
};
// HANDLE SLIDER CHANGING FONT SIZE
void ChangeFontsize_CB(Fl_Widget *w,void *data) {
    Fl_Value_Slider *slider = (Fl_Value_Slider*)w;
    Fl_Box *box = (Fl_Box*)data;
    int newsize = (int)slider->value();
    box->labelsize(newsize);
    box->redraw();
}
int main() {
    Fl_Window win(200, 100);
    MyBox box(10, 40, win.w()-20, win.h()-50, "Testing");
    box.labelsize(24);
    box.box(FL_FLAT_BOX);
    box.color(50);
    Fl_Value_Slider fontsize(10,10,200-20,20);
    fontsize.callback(ChangeFontsize_CB, (void*)&box);
    fontsize.type(FL_HOR_SLIDER);
    fontsize.range(8,40);
    fontsize.value(box.labelsize());
    win.show();
    return(Fl::run());
}
    



Changing the mouse cursor for an Fl_GL_Window

Demonstrates how to change the cursor for an Fl_Gl_Window.

Fl_Gl_Window is a special case on Win32 with Fltk versions 1.1.6 and older, because there was a bug where just setting the cursor wasn't enough, you had to set the cursor for the parent Fl_Window.

The following code should work for all platforms and all versions of 1.1.x.

Changing the mouse cursor for an Fl_GL_Window

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
//
// Test 'cross' cursor when mouse in GL window
// erco 11/03/05
//
class MyGlWindow : public Fl_Gl_Window {
    int handle(int e) {
        int ret = Fl_Gl_Window::handle(e);
        switch ( e ) {
            case FL_ENTER:
                window()->cursor(FL_CURSOR_CROSS);           // 'window()->cursor()' needed on WIN32 for 1.1.6 and older.
                ret = 1;                                     // 1.1.7 and up can probably just use fl_cursor(..)
                break;
            case FL_LEAVE:
                window()->cursor(FL_CURSOR_DEFAULT);
                ret = 1;
                break;
        }
        return(ret);
    }
    void draw() {
        if (!valid()) {
            valid(1);
            glViewport(0,0,w(),h());
        }
        glClear(GL_COLOR_BUFFER_BIT);
    }
public:
    MyGlWindow(int x,int y,int w,int h,const char*l=0) : Fl_Gl_Window(x,y,w,h,l) {
    }
};
int main() {
    Fl_Window win(500, 500);
    MyGlWindow glwin(10,10,500-20,500-20);
    win.show();
    return(Fl::run());
}
    



Extend Fl_Browser to have interactively resizable columns


An interactively resizable Fl_Browser

Demonstrates how to extend the functionality of the standard Fl_Browser to support interactively resizable columns.

The following code tested on Fltk 1.1.6

An Interactively Resizable Fl_Browser

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Browser.H>
#include <FL/fl_draw.H>
//
// Demonstrate how to derive a class extending Fl_Browser with interactively resizable columns
// erco 1.10 12/09/2005
//
class ColResizeBrowser : public Fl_Browser {
    Fl_Color  _colsepcolor;     // color of column separator lines 
    int       _showcolsep;      // flag to enable drawing column separators
    Fl_Cursor _last_cursor;     // saved cursor state info
    int       _dragging;        // 1=user dragging a column
    int       _dragcol;         // col# user is currently dragging
    int      *_widths;          // pointer to user's width[] array
    int       _nowidths[1];     // default width array (non-const)
    // CHANGE CURSOR
    //     Does nothing if cursor already set to value specified.
    //
    void change_cursor(Fl_Cursor newcursor) {
        if ( newcursor != _last_cursor ) {
            fl_cursor(newcursor, FL_BLACK, FL_WHITE);
            _last_cursor = newcursor;
        }
    }
    // RETURN THE COLUMN MOUSE IS 'NEAR'
    //     Returns -1 if none.
    //
    int which_col_near_mouse() {
        int X,Y,W,H;
        Fl_Browser::bbox(X,Y,W,H);            // area inside browser's box()
        // EVENT NOT INSIDE BROWSER AREA? (eg. on a scrollbar)
        if ( ! Fl::event_inside(X,Y,W,H) ) {
            return(-1);
        }
        int mousex = Fl::event_x() + hposition();
        int colx = this->x();
        for ( int t=0; _widths[t]; t++ ) {
            colx += _widths[t];
            int diff = mousex - colx;
            // MOUSE 'NEAR' A COLUMN?
            //     Return column #
            //
            if ( diff >= -4 && diff <= 4 ) {
                return(t);
            }
        }
        return(-1);
    }
protected:
    // MANAGE EVENTS TO HANDLE COLUMN RESIZING
    int handle(int e) {
        // Not showing column separators? Use default Fl_Browser::handle() logic
        if ( ! showcolsep() ) return(Fl_Browser::handle(e));
        // Handle column resizing
        int ret = 0;
        switch ( e ) {
            case FL_ENTER: {
                ret = 1;
                break;
            }
            case FL_MOVE: {
                if ( which_col_near_mouse() >= 0 ) {
                    change_cursor(FL_CURSOR_WE);
                } else {
                    change_cursor(FL_CURSOR_DEFAULT);
                }
                ret = 1;
                break;
            }
            case FL_PUSH: {
                int whichcol = which_col_near_mouse();
                if ( whichcol >= 0 ) {
                    // CLICKED ON RESIZER? START DRAGGING
                    ret = 1;
                    _dragging = 1;
                    _dragcol = whichcol;
                    change_cursor(FL_CURSOR_DEFAULT);
                }
                break;
            }
            case FL_DRAG: {
                if ( _dragging ) {
                    ret = 1;
                    // Sum up column widths to determine position
                    int mousex = Fl::event_x() + hposition();
                    int newwidth = mousex - x();
                    for ( int t=0; _widths[t] && t<_dragcol; t++ ) {
                        newwidth -= _widths[t];
                    }
                    if ( newwidth > 0 ) {
                        // Apply new width, redraw interface
                        _widths[_dragcol] = newwidth;
                        if ( _widths[_dragcol] < 2 ) {
                            _widths[_dragcol] = 2;
                        }
                        redraw();
                    }
                }
                break;
            }
            case FL_LEAVE:
            case FL_RELEASE: {
                _dragging = 0;                          // disable drag mode
                change_cursor(FL_CURSOR_DEFAULT);       // ensure normal cursor
                ret = 1;
                break;
            }
        }
        if ( _dragging ) return(1);                     // dragging? don't pass event to Fl_Browser
        return(Fl_Browser::handle(e) ? 1 : ret);
    }
    void draw() {
        // DRAW BROWSER
        Fl_Browser::draw();
        if ( _showcolsep ) {
            // DRAW COLUMN SEPARATORS
            int colx = this->x() - hposition();
            int X,Y,W,H;
            Fl_Browser::bbox(X,Y,W,H);
            fl_color(_colsepcolor);
            for ( int t=0; _widths[t]; t++ ) {
                colx += _widths[t];
                if ( colx > X && colx < (X+W) ) {
                    fl_line(colx, Y, colx, Y+H-1);
                }
            }
        }
    }
public:
    // CTOR
    ColResizeBrowser(int X,int Y,int W,int H,const char*L=0) : Fl_Browser(X,Y,W,H,L) {
        _colsepcolor = Fl_Color(FL_GRAY);
        _last_cursor = FL_CURSOR_DEFAULT;
        _showcolsep  = 0;
        _dragging    = 0;
        _nowidths[0] = 0;
        _widths      = _nowidths;
    }
    // GET/SET COLUMN SEPARATOR LINE COLOR
    Fl_Color colsepcolor() const {
        return(_colsepcolor);
    }
    void colsepcolor(Fl_Color val) {
        _colsepcolor = val;
    }
    // GET/SET DISPLAY OF COLUMN SEPARATOR LINES
    //     1: show lines, 0: don't show lines
    //
    int showcolsep() const {
        return(_showcolsep);
    }
    void showcolsep(int val) {
        _showcolsep = val;
    }
    // GET/SET COLUMN WIDTHS ARRAY
    //    Just like fltk method, but array is non-const.
    //
    int *column_widths() const {
        return(_widths);
    }
    void column_widths(int *val) {
        _widths = val;
        Fl_Browser::column_widths(val);
    }
};
int main() {
    Fl_Double_Window  *w = new Fl_Double_Window(900,300);
    int widths[] = { 50, 50, 50, 70, 70, 40, 40, 70, 70, 50, 0 };               // widths for each column
    ColResizeBrowser *b = new ColResizeBrowser(10,10,w->w()-20,w->h()-20);
    b->column_widths(widths);
    b->showcolsep(1);
    //b->colsepcolor(FL_RED);
    b->column_char('\t');                                                       // tabs as column delimiters
    b->type(FL_MULTI_BROWSER);

    //// SIMPLE UN-COLORED HEADING
    ////  b->add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND"); 

    // NICER COLORED HEADING
    b->add("@B12@C7@b@.USER\t@B12@C7@b@.PID\t@B12@C7@b@.%CPU\t"                 // tab delimited columns with colors
           "@B12@C7@b@.%MEM\t@B12@C7@b@.VSZ\t@B12@C7@b@.RSS\t"
           "@B12@C7@b@.TTY\t@B12@C7@b@.STAT\t@B12@C7@b@.START\t"
           "@B12@C7@b@.TIME\t@B12@C7@b@.COMMAND");

    // COLUMNS OF DATA
    b->add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
    b->add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
    b->add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
    b->add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
    b->add("root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear");
    w->resizable(b);
    w->end();
    w->show();
    return(Fl::run());
}
    



How to make a 4 port proportionally resizable openGL window using Fl_Tile


An interactively resizable 4 Port openGL window

Demonstrates how to create a 4 port proportionally resizable openGL window using Fl_Tile, with interactively resizable ports.

The following code tested on Fltk 1.1.6

Fl_Tile 4 port openGL Window

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Tile.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
//
// Example of how to make a 4 port tiled GL window
// ting/erco 1.0
//
class Viewport : public Fl_Gl_Window {
    void RectLine(int x1, int y1, int x2, int y2) {
        glBegin(GL_LINE_LOOP);
        glVertex2s(x1, y1); glVertex2s(x2, y1);
        glVertex2s(x2, y2); glVertex2s(x1, y2);
        glEnd();
    }
protected:
    void draw() {
        if ( !valid() ) {
            // First time? init viewport, etc.
            valid(1);
            glLoadIdentity();
            glViewport(0, 0, w(), h());
            glOrtho(-w(), w(), -h(), h(), -1, 1);
        }
        // Clear screen
        glClear(GL_COLOR_BUFFER_BIT);
        // Draw white 'X'
        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_LINE_STRIP); glVertex2f(w(),  h()); glVertex2f(-w(), -h()); glEnd();
        glBegin(GL_LINE_STRIP); glVertex2f(w(), -h()); glVertex2f(-w(),  h()); glEnd();
        // Draw yellow border last, around the outer edge
        glColor3f(1.0, 1.0, 0.0);
        RectLine(-w()+1, -h()+1, w()-1, h()-1);
    }
public:
    Viewport(int x, int y, int w, int h, char* l=0) : Fl_Gl_Window(x, y, w, h, l) {
        end();
    }
};

class Layout:public Fl_Tile {
private:
    Viewport *toplft, *toprig, *botlft, *botrig;
protected:
    // Custom resize behavior : keep viewports proportional during resize
    void resize(int X, int Y, int W, int H) {
        // Get old proportions so we can preserve through resize
        float dw = (float)toplft->w() / w();
        float dh = (float)toplft->h() / h();
        // Carefully construct new edges of ports, keeping proportions
        int xlef = 0, xmid = (int)(W * dw + 0.5), xrig = W;
        int ytop = 0, ymid = (int)(H * dh + 0.5), ybot = H;
        int wlef = xmid - xlef, wrig = xrig - xmid;
        int htop = ymid - ytop, hbot = ybot - ymid;
        // Resize our widget via Fl_Widget (to prevent children resizing)
        Fl_Widget::resize(X, Y, W, H);
        // Resize children with custom computations
        toplft->resize(xlef, ytop, wlef, htop);
        toprig->resize(xmid, ytop, wrig, htop);
        botlft->resize(xlef, ymid, wlef, hbot);
        botrig->resize(xmid, ymid, wrig, hbot);
    }

public:
    Layout(int x, int y, int w, int h) : Fl_Tile(x, y, w, h) {
        box(FL_BORDER_BOX);
        color(FL_RED);          // (shouldn't be seen)
        // Carefully construct edges of ports
        int xlef = 0, xmid = w/2, xrig = w;
        int ytop = 0, ymid = h/2, ybot = h;
        int wlef = xmid - xlef, wrig = xrig - xmid;
        int htop = ymid - ytop, hbot = ybot - ymid;
        // Create the 4 ports
        toplft = new Viewport(xlef, ytop, wlef, htop);
        toprig = new Viewport(xmid, ytop, wrig, htop);
        botlft = new Viewport(xlef, ymid, wlef, hbot);
        botrig = new Viewport(xmid, ymid, wrig, hbot);
        end();
    }
};

int main() {
    Fl_Double_Window *win = new Fl_Double_Window(800, 500);
    Layout *layout = new Layout(0, 0, win->w(), win->h());
    win->resizable(layout);
    win->end();
    win->show();
    return(Fl::run());
}
    



An OpenGL Window with a Dynamic Popup Menu

Demonstrates how to create a dynamic popup menu over an OpenGL window. (Probably works for regular windows too)

The following code tested with Fltk 1.1.6 on OSX/Linux/Windows.

OpenGL + Dynamic Popup Menu

#include <stdio.h>
#include <time.h>
#include <FL/Fl.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/gl.h>
//
// Simple GL window with dynamic popup menu
// erco 01/25/06
//
class MyGlWindow : public Fl_Gl_Window {
    void draw() {
        if (!valid()) {
            glLoadIdentity();
            glViewport(0,0,w(),h());
            glOrtho(-w(),w(),-h(),h(),-1,1);
        }
        glClear(GL_COLOR_BUFFER_BIT);
    }
    static void Menu_CB(Fl_Widget*, void *data) {
        char name[80];
        ((Fl_Menu_Button*)data)->item_pathname(name, sizeof(name)-1);
        fprintf(stderr, "Menu Callback: %s\n", name);
    }
    int handle(int e) {
        int ret = Fl_Gl_Window::handle(e);
        switch ( e ) {
            case FL_PUSH:
                if ( Fl::event_button() == 3 ) {
                    char tmp[80];
                    time_t t = time(NULL);
                    sprintf(tmp, "Time is %s", ctime(&t));
                    // Dynamically create menu, pop it up
                    Fl_Menu_Button menu(Fl::event_x_root(), Fl::event_y_root(), 80, 1);
                    menu.add(tmp);      // dynamic -- changes each time popup opens..
                    menu.add("Edit/Copy",  0, Menu_CB, (void*)&menu);
                    menu.add("Edit/Paste", 0, Menu_CB, (void*)&menu);
                    menu.add("Quit",       0, Menu_CB, (void*)&menu);
                    menu.popup();
                }
        }
        return(ret);
    }
public:
    // CONSTRUCTOR
    MyGlWindow(int X,int Y,int W,int H,const char*L=0) : Fl_Gl_Window(X,Y,W,H,L) {
    }
};

// MAIN
int main() {
     Fl_Window win(500, 300);
     MyGlWindow mygl(10, 10, win.w()-20, win.h()-20);
     win.show();
     return(Fl::run());
}
    



Drop Shadow Effect Example


A clock with the drop shadow effect.

Demonstrates how to make a drop shadow effect with text. Creates a 'DropShadowBox' widget, and demonstrates its use with a simple clock application.

Tested with Fltk 1.1.6 on Linux, probably works on all platforms + all revs of 1.1.x.

Drop Shadow Effect

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H>
#include <stdio.h>
#include <time.h>
//
// Drop shadow example widget
// 1.0 erco 02/11/06
//
class DropShadowBox : public Fl_Box {
    int _levels;
    void draw() {
        Fl_Box::draw_box();
        if ( label() ) {
            // Draw label w/dropshadow effect
            fl_font(labelfont(), labelsize());
            int X = x() + Fl::box_dx(box());
            int Y = y() + Fl::box_dy(box());
            for ( int i=_levels; i>0; i-- ) {                           // loop through shades of color
                fl_color(fl_color_average(color(),                      // bg color (widget's bg)
                                          labelcolor(),                 // fg color (label)
                                          (i / (float)_levels)));       // weight between bg and fg color
                fl_draw(label(), X+i, Y+i, w(), h(), align());
            }
        }
    }
public:
    DropShadowBox(int X,int Y,int W,int H,const char*L=0) : Fl_Box(X,Y,W,H,L) {
        _levels = 5;
    }
    void levels(int val) { _levels = val; }
    int levels() { return(_levels); }
};
 
// Timer callback to update label
void Update_CB(void* userdata) {
    DropShadowBox *box = (DropShadowBox*)userdata;
    time_t lt = time(NULL);
    box->label(ctime(&lt));
    Fl::repeat_timeout(1.0, Update_CB, (void*)box);
}
 
main() {
    Fl_Double_Window *win = new Fl_Double_Window(400, 100, "Drop Shadow Clock");
    DropShadowBox *box = new DropShadowBox(10, 10, win->w()-20, win->h()-20);
    box->labelsize(28);
    box->levels(8);
    Fl::add_timeout(1.0, Update_CB, (void*)box);
    win->show();
    Update_CB((void*)box);              // force update immediately
    return(Fl::run());
}
    



OpenGL Text on a 3D Object


Text on a spinning 3D OpenGL object

Demonstrates how to show text on a 3D object.

Tested with Fltk 1.1.6 on Linux, probably works on all platforms + all revs of 1.1.x.

OpenGL Text on 3D Object

//
// OpenGL example showing text on a rotating 3D object.
// erco 03/03/06
//
#include <FL/Fl.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
#include <GL/glu.h>
#include <string.h>
#include <stdio.h>
// Tetrahedron points
#define TOP    0,  1,  0
#define RIGHT  1, -1,  1
#define LEFT  -1, -1,  1
#define BACK   0, -1, -1
class MyGlWindow : public Fl_Gl_Window {
    float rotangle;
    void draw() {
        // First time? init viewport, etc.
        if (!valid()) {
            valid(1);
            // Initialize GL
            glClearColor(0.0, 0.0, 0.0, 0.0);
            glClearDepth(1.0);
            glDepthFunc(GL_LESS);
            glEnable(GL_DEPTH_TEST);
            glShadeModel(GL_FLAT);
        }
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        // Position camera/viewport init
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glViewport(0,0,w(),h());
        gluPerspective(45.0, (float)w()/(float)h(), 1.0, 10.0);
        glTranslatef(0.0, 0.0, -5.0);
        // Position object
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glRotatef(rotangle, 1, 0, 1);
        glRotatef(rotangle, 0, 1, 0);
        glRotatef(rotangle, 1, 1, 1);
        // Draw tetrahedron
        glColor3f(1.0, 0.0, 0.0); glBegin(GL_POLYGON); glVertex3f(TOP);   glVertex3f(RIGHT);  glVertex3f(LEFT);  glEnd();
        glColor3f(0.0, 1.0, 0.0); glBegin(GL_POLYGON); glVertex3f(TOP);   glVertex3f(BACK);   glVertex3f(RIGHT); glEnd();
        glColor3f(0.0, 0.0, 1.0); glBegin(GL_POLYGON); glVertex3f(TOP);   glVertex3f(LEFT);   glVertex3f(BACK);  glEnd();
        glColor3f(0.5, 0.5, 0.5); glBegin(GL_POLYGON); glVertex3f(RIGHT); glVertex3f(BACK);   glVertex3f(LEFT);  glEnd();
        // Print tetrahedron's points on object
        //     Disable depth buffer while drawing text,
        //     so text draws /over/ object.
        //
        glDisable(GL_DEPTH_TEST);
        {
            const char *p;
            gl_font(1, 12);
            glColor3f(1.0, 1.0, 1.0);
            glRasterPos3f(TOP);   p = "+ top";   gl_draw(p, strlen(p));
            glRasterPos3f(LEFT);  p = "+ left";  gl_draw(p, strlen(p));
            glRasterPos3f(RIGHT); p = "+ right"; gl_draw(p, strlen(p));
            glRasterPos3f(BACK);  p = "+ back";  gl_draw(p, strlen(p));
        }
        glEnable(GL_DEPTH_TEST);
        // Print rotangle value at fixed position at lower left
        char s[40];
        sprintf(s, "ROT=%.2f", rotangle);
        glLoadIdentity(); glRasterPos2f(-3,-2); gl_draw(s, strlen(s));
    }
    static void Timer_CB(void *userdata) {
        MyGlWindow *o = (MyGlWindow*)userdata;
        o->rotangle += 1.0;
        o->redraw();
        Fl::repeat_timeout(1.0/24.0, Timer_CB, userdata);       // 24fps
    }
public:
    // CONSTRUCTOR
    MyGlWindow(int X,int Y,int W,int H,const char*L=0) : Fl_Gl_Window(X,Y,W,H,L) {
        rotangle = 0;
        Fl::add_timeout(3.0, Timer_CB, (void*)this);       // wait 3 secs before animation begins
    }
};
// MAIN
int main() {
     Fl_Window win(500, 300);
     MyGlWindow mygl(10, 10, win.w()-20, win.h()-20);
     win.show();
     return(Fl::run());
}
    



Simple Tabs Example


Tabs Example

Demonstrates how to use the Fl_Tabs widget.

Fl_Tabs Example

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Button.H>
//
// Simple tabs example
//      _____  _____
//   __/ Aaa \/ Bbb \______________________
//  |    _______                           |
//  |   |_______|                          |
//  |    _______                           |
//  |   |_______|                          |
//  |    _______                           |
//  |   |_______|                          |
//  |______________________________________|
//
int main(int argc, char *argv[]) {
    Fl_Window *win = new Fl_Window(500,200,"Tabs Example");
    {
        Fl_Tabs *tabs = new Fl_Tabs(10,10,500-20,200-20);
        {
            // Aaa tab
            Fl_Group *aaa = new Fl_Group(10,35,500-20,200-45,"Aaa");
            {
                Fl_Button *b1 = new Fl_Button(50, 60,90,25,"Button A1"); b1->color(88+1);
                Fl_Button *b2 = new Fl_Button(50, 90,90,25,"Button A2"); b2->color(88+2);
                Fl_Button *b3 = new Fl_Button(50,120,90,25,"Button A3"); b3->color(88+3);
            }
            aaa->end();

            // Bbb tab
            Fl_Group *bbb = new Fl_Group(10,35,500-10,200-35,"Bbb");
            {
                Fl_Button *b1 = new Fl_Button( 50,60,90,25,"Button B1"); b1->color(88+1);
                Fl_Button *b2 = new Fl_Button(150,60,90,25,"Button B2"); b2->color(88+3);
                Fl_Button *b3 = new Fl_Button(250,60,90,25,"Button B3"); b3->color(88+5);
                Fl_Button *b4 = new Fl_Button( 50,90,90,25,"Button B4"); b4->color(88+2);
                Fl_Button *b5 = new Fl_Button(150,90,90,25,"Button B5"); b5->color(88+4);
                Fl_Button *b6 = new Fl_Button(250,90,90,25,"Button B6"); b6->color(88+6);
            }
            bbb->end();
        }
        tabs->end();
    }
    win->end();
    win->show();
    return(Fl::run());
}



How to turn a Mac FLTK application into an ".app bundle"

On OSX, GUI applications must have a few 'resources' in order to make the application run correctly. In the pre-OSX days, this was done by adding a magical 'resource fork' and 'data forks' to the executable.

These days Apple is (rightfully) trying to get away from this 'magic data' approach, since Apple has now adopted Unix as its base operating system, which likes to think of all files as a stream of bytes, and no 'magic data'.

To do this, Apple decided to separate the file and the magic data into separate files, and put them all into a directory hierarchy known as a 'bundle'. From a unix point of view, a 'bundle' is simply a directory of files, so when one wants to run the application from the Finder or with the open(1) command, one refers to the subdirectory, not the files within it.

For instance, if you have an application called 'foo', then the 'bundle' (or directory hierarchy) might look like this:

So if you have just a 'foo' executable lying around, you can create a bundle for it via these unix commands:

That's about as simple as it can get; no icons or other frills. (To set up icons, see the next section)

For a different executable name, just change all the instances of 'foo' in the above to the name of your actual application.

You don't have to worry about the above 'cp' command stripping off your executable's resource fork, as OSX will look at the other files to determine the resource information, assuming you invoke the application by either:

..or by browsing the Finder to the directory in which the foo.app subdirectory lives. The Finder will show the "foo.app" directory as an application, not a directory. When you double-click on it, the Finder will open your app. There will be no indication it's a directory. Only the Unix command line tools (like cd, ls, etc) will show foo.app as a directory. The Finder treates the ".app" extension as a special entity, and looks into the directory to see if the Contents and Contents/MacOS subdirs exist to determine what kind of 'bundle' this is.. in this case, an 'executable bundle'.

To get your application to run from the unix command line, you can put a shell script in your bin directory that runs the app using the 'open' command (above).

For actual OSX examples of bundles, look in your machine's /Applications directory; if you sniff around with 'ls -la', you'll see most OSX applications are implemented as 'bundles' (eg. "Terminal.app", "Mail.app", etc)



How to make icons for your FLTK bundle

Under OSX, GUI applications should be 'released' as bundles, so you can easily assign resources to it, such as icons.

Once you have your Mac Bundle working, you can associate an icon with it, using these steps.

CAVEATS



How to make a pop-up numeric keypad for touch screens

Demonstrates how to create an input widget for touch screen applications, so that you can touch on an input widget, and enter data using the touch screen. This example shows entering of numeric data only, but a more elaborate example can be derived to emulate a full keyboard as well.

Simple Numeric Keypad for Touch Screen Applications

// Demonstrate how to use Fl_Input in a touchscreen application -erco 08/25/06
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>

class MyNumPad : public Fl_Window {
    Fl_Input *in;               // input preview
    Fl_Callback *enter_cb;      // callback when user hits 'enter'
    void *enter_data;

    // Handle numeric keypad buttons pressed
    void Button_CB2(Fl_Widget *w) {
        Fl_Button *b = (Fl_Button*)w;
        if ( strcmp(b->label(),"Del") == 0 ) {                  // handle Delete
            // Delete
            if (in->mark() != in->position()) in->cut();
            else in->cut(-1);
        }
        else if ( strcmp(b->label(), "Ent") == 0 ) {            // handle enter key
            // Do 'Enter' callback
            if ( enter_cb ) (*enter_cb)(in, enter_data);
        }
        else {                                                  // handle all other keys
            // Appends label of button
            in->replace(in->position(), in->mark(), b->label(), 1);
        }
    }
    static void Button_CB(Fl_Widget *w, void *data) {
        MyNumPad *numpad = (MyNumPad*)data;
        numpad->Button_CB2(w);
    }
public:
    MyNumPad(int X,int Y,int W=100,int H=140,const char *L=0):Fl_Window(X,Y,W,H,L) {
        const int bsize = 20;
        // Preview input
        in = new Fl_Input(X+10,Y+10,W-20,20);
        // Numeric keypad
        Fl_Button *b;
        int colstart = 10,
            col = colstart,
            row = in->y()+in->h()+10;
        b = new Fl_Button(col,row,bsize,bsize,  "7");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "8");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "9");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "Del"); b->callback(Button_CB, (void*)this); b->labelsize(10);
                                                                                             col=colstart; row+=b->h();
        b = new Fl_Button(col,row,bsize,bsize,  "4");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "5");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "6");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "-");   b->callback(Button_CB, (void*)this); col=colstart; row+=b->h();
        b = new Fl_Button(col,row,bsize,bsize,  "1");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "2");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "3");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "+");   b->callback(Button_CB, (void*)this); col=colstart; row+=b->h();
        b = new Fl_Button(col,row,bsize,bsize,  ".");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize,bsize,  "0");   b->callback(Button_CB, (void*)this); col+=b->w();
        b = new Fl_Button(col,row,bsize*2,bsize,"Ent"); b->callback(Button_CB, (void*)this); col+=b->w(); b->color(10);
        end();
        enter_cb = 0;
        enter_data = 0;
    }
    // Return current value
    const char *value() {
        return(in->value());
    }
    // Clear current value
    void clear() {
        in->value("");
    }
    // Set callback for when Enter is pressed
    void SetEnterCallback(Fl_Callback *cb, void *data) {
        enter_cb = cb;
        enter_data = data;
    }
};

class MyInput : public Fl_Input {
    MyNumPad *numpad;                   // local instance of numeric keypad widget

    // Called when user finishes entering data with numeric keypad
    void SetNumPadValue_CB2() {
        value(numpad->value());         // pass value from numpad to our input
        numpad->hide();                 // hide numpad
    }
    static void SetNumPadValue_CB(Fl_Widget*,void *data) {
        MyInput *in = (MyInput*)data;
        in->SetNumPadValue_CB2();
    }
    // Handle when user right clicks on our input widget
    int handle(int e) {
        int ret = 0;
        switch(e) {
            // Mouse click on input field? Open numpad dialog..
            case FL_PUSH:
                if ( Fl::event_button() == FL_LEFT_MOUSE ) { ret = 1; }
                break;
            case FL_RELEASE:
                if ( Fl::event_button() == FL_LEFT_MOUSE ) {
                    ret = 1;
                    if ( ! numpad ) numpad = new MyNumPad(0,0);
                    numpad->SetEnterCallback(SetNumPadValue_CB, (void*)this);
                    numpad->position(parent()->x(),parent()->y());
                    numpad->clear();
                    numpad->show();
                }
                break;
        }
        return(Fl_Input::handle(e)?1:ret);
    }
public:
    MyInput(int X,int Y,int W,int H,const char *L=0):Fl_Input(X,Y,W,H,L) {
        numpad = 0;
    }
};

int main(int argc, char **argv) {
    Fl_Window win(400,50);
    MyInput in(150,10,200,30,"Value:");
    in.value("-click here-");
    win.end();
    win.resizable(win);
    win.show();
    return(Fl::run());
}



Drawing into overlay planes

If you're just drawing rectangles into the overlay plane, you can just use fl_overlay_rect(x,y,w,h).

But for more complex graphics, or graphics where the child widgets want to define the overlay drawing behavior, here's an example that shows how to register an array of callbacks to the parent Fl_Overlay_Window so that it can call the children when drawing needs to be done.

In the following example, resize the window to cause the overlays to redraw. You would use your own mechanism (Timer, events) to change the overlays. The example draws a random colored X over each of the four instances of the custom Fl_Group's. (The use of random colors makes it clear when the overlay is redrawn.)

How Child Widgets Can Draw Into Overlay Planes

// Demonstrate using callbacks to draw into overlays -erco 8/23/06
#include <stdlib.h>
#include <vector>
using namespace std;
#include <FL/Fl.H>
#include <FL/Fl_Overlay_Window.H>
#include <FL/Fl_Group.H>
#include <FL/fl_draw.H>

typedef void (OverlayCallback)(void*);

class MyOverlayWindow : public Fl_Overlay_Window {
    vector<OverlayCallback*> oly_callbacks;    // array of child callbacks to draw overlays
    vector<void*> oly_data;                    // array of child userdata
public:
    MyOverlayWindow(int W,int H,const char*L=0):Fl_Overlay_Window(W,H,L) {
    }
    // SETUP AN OVERLAY DRAWING CALLBACK
    //     Called by child widgets to arrange callback to draw into overlay plane.
    //
    void AddOverlayCallback(OverlayCallback *cb, void *data) {
        oly_callbacks.push_back(cb);        // add callback to array
        oly_data.push_back(data);           // add userdata to array
    }
    // INVOKE CHILDREN'S CALLBACKS TO DRAW OVERLAYS
    void draw_overlay() {
        // Invoke all the children's overlay callbacks
        for ( unsigned int t=0; t<oly_callbacks.size(); t++ ) {
            (*oly_callbacks[t])(oly_data[t]);
        }
    }
};

class MyGroup : public Fl_Group {
    MyOverlayWindow *oly;

    // DRAW THE OVERLAY GRAPHIC FOR THIS INSTANCE OF CHILD
    static void OverlayCallback(void *data) {
        MyGroup *o = (MyGroup*)data;
        // Draw a random colored 'x'
        fl_color(rand() % 8);
        fl_line(o->x(), o->y(),        o->x()+o->w(), o->y()+o->h());
        fl_line(o->x(), o->y()+o->h(), o->x()+o->w(), o->y());
    }
public:
    MyGroup(MyOverlayWindow *win,int X,int Y,int W,int H,const char*L=0):Fl_Group(X,Y,W,H,L) {
        oly = win;
        win->AddOverlayCallback(OverlayCallback, (void*)this);
        end();
    }
    void draw() {
        Fl_Group::draw();
        oly->redraw_overlay();          // tell parent to redraw child overlays
    }
};

int main(int argc, char **argv) {
    // Create overlay window
    MyOverlayWindow win(400,400);
    // Create four child groups, each with its own overlay graphic
    MyGroup g1(&win, 0,  0,  200,200);
    MyGroup g2(&win, 200,0,  200,200);
    MyGroup g3(&win, 0,  200,200,200);
    MyGroup g4(&win, 200,200,200,200);
    win.end();
    win.resizable(win);
    win.show();
    return(Fl::run());
}



Thumbnail Scroller With Labels

Someone asked about the issue of how to draw thumbnail images inside a scroller with labels, because when the labels were drawn outside the thumbnail widget, the top row of labels would truncate off, due to how the scroller clips.

Here's one way to do it by deriving a class from Fl_Box, and drawing the image and label in a custom draw routine. The result of the above code example creates a window that looks like this:



Demo of Drag + Drop (DND) with FLTK

Here's a simple demo of drag+drop with FLTK.

When you run the program, just drag the red square over to the green square to show a 'drag and drop' sequence.

Credit: Thanks to Michael Sephton's original example posted to fltk.general; some code cleanup and event handle()er modifications were added.

Drag And Drop

#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>

//
// Demo of Drag+Drop (DND) from red sender to green receiver
//
                                                                                              
// SENDER CLASS
class Sender : public Fl_Box {
public:
    // Sender Ctor
    Sender(int x,int y,int w,int h) : Fl_Box(x,y,w,h) {
        box(FL_FLAT_BOX); color(9); label("Drag from here");
    }
    // Sender event handler
    int handle(int event) {
        int ret = Fl_Box::handle(event);
        switch ( event ) {
            case FL_PUSH:               // do 'copy/dnd' when someone clicks on box
                Fl::copy("message",7,0);
                Fl::dnd();
                ret = 1;
                break;
        }
        return(ret);
    }
};
// RECEIVER CLASS
class Receiver : public Fl_Box {
public:
    // Receiver Ctor
    Receiver(int x,int y,int w,int h) : Fl_Box(x,y,w,h) {
        box(FL_FLAT_BOX); color(10); label("to here");
    }
    // Receiver event handler
    int handle(int event) {
        int ret = Fl_Box::handle(event);
        switch ( event ) {
            case FL_DND_ENTER:          // return(1) for these events to 'accept' dnd
            case FL_DND_DRAG:
            case FL_DND_RELEASE:
                ret = 1;
                break;
            case FL_PASTE:              // handle actual drop (paste) operation
                label(Fl::event_text());
                fprintf(stderr, "PASTE: %s\n", Fl::event_text());
                ret = 1;
                break;
        }
        return(ret);
    }
};
int main(int argc, char **argv) {
    // Create sender window and widget
    Fl_Window win_a(0,0,200,100,"Sender");
    Sender a(0,0,100,100);
    win_a.end();
    win_a.show();
    // Create receiver window and widget
    Fl_Window win_b(400,0,200,100,"Receiver");
    Receiver b(100,0,100,100);
    win_b.end();
    win_b.show();
    return(Fl::run());
}



How to use fl_draw_image() to display a pixel buffer


Freeze-frame from animating demo that draws into an image buffer, and displays the resulting image 20x per second.

This is a simple animated demo of how to draw pixels into a pixel buffer, and then display that buffer in FLTK using the fl_draw_image() function.

Display Pixel Buffer in FLTK

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/fl_draw.H>
#include <stdio.h>
#include <time.h>

#define XSIZE 500
#define YSIZE 500
#define UPDATE_RATE 0.05     // update rate in seconds

// Demonstrate how to display a pixel buffer in FLTK

// WINDOW CLASS TO HANDLE DRAWING IMAGE
class MyWindow : public Fl_Double_Window {
    unsigned char pixbuf[YSIZE][XSIZE][3];              // image buffer

    // FLTK DRAW METHOD
    void draw() {
        fl_draw_image((const uchar*)&pixbuf, 0, 0, XSIZE, YSIZE, 3, XSIZE*3);
    }

    // TIMER CALLBACK: CALLED TO UPDATE THE DRAWING
    static void RenderImage_CB(void *userdata) {
        MyWindow *win = (MyWindow*)userdata;
        win->RenderImage();
        Fl::repeat_timeout(UPDATE_RATE, RenderImage_CB, userdata);
    }

public:
    // CTOR
    MyWindow(int w, int h, const char *name=0) : Fl_Double_Window(w,h,name) {
        end();
        RenderImage();                   // show first drawing
        // Start timer updating
        Fl::add_timeout(UPDATE_RATE, RenderImage_CB, (void*)this);
    }

    // PLOT A PIXEL AS AN RGB COLOR INTO THE PIXEL BUFFER
    void PlotPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b) {
        pixbuf[y][x][0] = r;
        pixbuf[y][x][1] = g;
        pixbuf[y][x][2] = b;
    }

    // MAKE A NEW PICTURE IN THE PIXEL BUFFER, SCHEDULE FLTK TO DRAW IT
    void RenderImage() {
        static unsigned char drawcount = 0;
        for ( int x=0; x<XSIZE; x++ )
            for ( int y=0; y<XSIZE; y++ )
                PlotPixel(x, y, x+drawcount, y+drawcount, x+y+drawcount);
        ++drawcount;
        redraw();
    }
};

int main(int argc, char**argv) {
    Fl::visual(FL_RGB);         // prevents dithering on some systems
    MyWindow *win = new MyWindow(XSIZE, YSIZE);
    win->show();
    return(Fl::run());
}



Opening WIN32 File Chooser from FLTK


Simple demonstration of opening a WIN32 file chooser from within FLTK. All the various WIN32 file choosers (Open, Save, Directory, Mutli-select, etc) can be accessed from FLTK using similar WIN32-specific techniques.

(Or, just use the Fl_Native_File_Chooser widget, to avoid WIN32-specific code in your app.)

FLTK + WIN32 File Chooser

#include <windows.h>
#include <commdlg.h>            // OPENFILENAME, GetOpenFileName()
#include <stdio.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>

// HOW TO MIX THE WINDOWS FILE BROWSER WITH FLTK

OPENFILENAME ofn;          // global
void Browse_CB(Fl_Widget*, void*) {
    // OPEN WINDOWS FILE BROWSER
    //     The following is WIN32-specific code, and therefore not pretty and 'non-fltk-ish' ;)
    //
    if ( ofn.lpstrFile       ) { delete [] ofn.lpstrFile; }
    if ( ofn.lpstrInitialDir ) { delete [] ofn.lpstrInitialDir; }
    memset((void*)&ofn, 0, sizeof(ofn));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.Flags       |= OFN_NOVALIDATE;          // prevent disabling of front slashes
    ofn.Flags       |= OFN_HIDEREADONLY;        // hide readonly flag
    ofn.Flags       |= OFN_EXPLORER;            // use 'newer' explorer windows
    ofn.Flags       |= OFN_ENABLESIZING;        // allow window to be resized
    ofn.Flags       |= OFN_NOCHANGEDIR;         // prevent dialog for messing up the cwd
    ofn.nMaxFile     = 4096-1;
    ofn.lpstrFile    = new char[4096];
    ofn.lpstrFile[0] = 0;
    ofn.hwndOwner    = GetForegroundWindow();
    ofn.lpstrTitle   = "Open some file";
    int err = GetOpenFileName(&ofn);
    if ( err == 0 ) {
        err = CommDlgExtendedError();           // extended error check
        if ( err == 0 ) return;                 // user hit 'cancel'
        fprintf(stderr, "CommDlgExtendedError() code=%d", err);
        return;
    }
    printf("User picked '%s'\n", ofn.lpstrFile);
}
int main() {
    Fl_Window win(200,200);
    Fl_Button but(10,10,100,25,"Browse");
    but.callback(Browse_CB);
    win.end();
    win.show();
    memset((void*)&ofn, 0, sizeof(OPENFILENAME));
    return(Fl::run());
}



Fl_Group clips children of events, but not graphics

Demonstration of a common problem where children of an Fl_Group won't receive mouse events on areas of the widgets that lay outside the parent group's xywh area, even though the children will still draw correctly (ie. events will be clipped, but not the drawing of children's graphic elements)

By default, Fl_Group does not clip drawing of children. This is mainly for speed efficiency, to keep the clipping stack size small in deep group hierarchies.

But due to how events are delivered, children that lay outside the parent group's bounding area will not receive mouse events. For children that are partially outside the parent's xywh area, only the parts of the child inside the parent's perimeter will be sensitive to mouse events.

Fl_Group Event vs. Visual Clipping

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
//
// Demonstrate clipping problem with widgets outside their parent's xywh extents
// erco 04/20/08
//
Fl_Window *win = 0;
Fl_Group  *grp = 0;
void Why_CB(Fl_Widget*, void*) {
    grp->box(FL_FLAT_BOX);
    grp->color(FL_RED);
    win->redraw();
}
int main() {
    win = new Fl_Window(500,500);
    grp = new Fl_Group(5,5,100,155);
    new Fl_Button(10,10 ,40,40,"7"); new Fl_Button(60,10 ,40,40,"8"); new Fl_Button(110,10 ,40,40,"9");
    new Fl_Button(10,60 ,40,40,"4"); new Fl_Button(60,60 ,40,40,"5"); new Fl_Button(110,60 ,40,40,"6");
    new Fl_Button(10,110,40,40,"1"); new Fl_Button(60,110,40,40,"2"); new Fl_Button(110,110,40,40,"3");
    grp->end();
    Fl_Button *but = new Fl_Button(100, 400, 300, 25, "Why doesn't 9,6,3 work?");
    but->callback(Why_CB);
    win->end();
    win->show();
    return(Fl::run());



Using Fl_Browser to make a 'vertical tabbed' dialog


Demonstration of how to use an Fl_Browser to implement a 'vertical tab' style dialog.

Vertical Tab Demo

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Input.H>

// Demonstrate a "vertical tabbed" window dialog
// erco 05/09/08

Fl_Window       *win = 0;
Fl_Hold_Browser *bro = 0;
Fl_Group        *grp[3] = { 0,0,0 };

// CALLBACK FOR SOMEONE CLICKING ON A BROWSER TAB
void SelectGroup_CB(Fl_Widget*, void*) {
    // Show the 'selected' group
    for ( int t=0; t<3; t++ ) {
        if ( t == (bro->value()-1) ) { grp[t]->show(); }
        else                         { grp[t]->hide(); }
    }
}

int main() {
    win = new Fl_Window(600,400);

    // Browser to act as "tab selector"
    bro = new Fl_Hold_Browser(10,10,150,400-20);
    bro->add("Tab One");
    bro->add("Tab Two");
    bro->add("Tab Three");

    // Make three groups with different contents
    grp[0] = new Fl_Group(170,10,450-30,400-20,"Employee Name");
    grp[0]->box(FL_ENGRAVED_BOX); grp[0]->align(FL_ALIGN_INSIDE|FL_ALIGN_TOP); grp[0]->labelsize(24);
        new Fl_Input(170+140,160+00,200,20,"First:");
        new Fl_Input(170+140,160+30,200,20,"Last:");
        new Fl_Input(170+140,160+60,200,20,"Title:");
    grp[0]->end();

    grp[1] = new Fl_Group(170,10,450-30,400-20,"Employee Home");
    grp[1]->box(FL_ENGRAVED_BOX); grp[1]->align(FL_ALIGN_INSIDE|FL_ALIGN_TOP); grp[1]->labelsize(24);
        new Fl_Input(170+140,160+00,200,20,"Street:");
        new Fl_Input(170+140,160+30,200,20,"City:");
        new Fl_Input(170+140,160+60,200,20,"State:");
        new Fl_Input(170+140,160+90,200,20,"ZIP:");
    grp[1]->end();

    grp[2] = new Fl_Group(170,10,450-30,400-20,"Employee Contact");
    grp[2]->box(FL_ENGRAVED_BOX); grp[2]->align(FL_ALIGN_INSIDE|FL_ALIGN_TOP); grp[2]->labelsize(24);
        new Fl_Input(170+140,160+00,200,20,"Email:");
        new Fl_Input(170+140,160+30,200,20,"Extension:");
        new Fl_Input(170+140,160+60,200,20,"Parking Space:");
        new Fl_Input(170+140,160+90,200,20,"Cell/Pager:");
    grp[2]->end();

    // Set a callback for the browser, initialize first selection
    bro->callback(SelectGroup_CB);
    bro->select(2);
    SelectGroup_CB(0,0);                // (updates visible group based on our select()tion)

    win->show();
    return(Fl::run());
}



How to use Fl_Chart


Demonstration of how to use Fl_Chart.

Fl_Chart Demo

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Chart.H>
#include <math.h>
// Demonstration of how to use Fl_Chart -- erco 11/20/08
int main() {
    Fl_Window *win = new Fl_Window(1000, 480);
    Fl_Chart  *chart = new Fl_Chart(20, 20, win->w()-40, win->h()-40, "Chart");
    chart->bounds(-125.0, 125.0);
    for ( double t=0; t<15; t+=0.5 ) {
        double val = sin(t) * 125.0;
        static char val_str[20];
        sprintf(val_str, "%.0lf", val);
        chart->add(val, val_str, (val<0)?FL_RED:FL_GREEN);
    }
    win->resizable(win);
    win->show();
    return(Fl::run());
}



How to use Fl_Scrollbar


Demonstration of how to use Fl_Scrollbar.

Fl_Scrollbar Demo

#include <stdio.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scrollbar.H>
#include <FL/Fl_Output.H>
//
// Demonstrate Fl_Scrollbar -- erco 02/18/09
//
Fl_Double_Window *win = 0;
Fl_Output        *out = 0;
Fl_Scrollbar     *scrollbar = 0;

// Scrollbar changed: show scrollbar's value when changed
void Scrollbar_CB(Fl_Widget*, void *) {
    char s[20];
    sprintf(s, "%d", scrollbar->value());
    out->value(s);
}
int main(int argc, char **argv) {
    win = new Fl_Double_Window(500,300,"Fl_Scrollbar Demo");
    {
        // Create scrollbar
        scrollbar = new Fl_Scrollbar(0,50,500,25,"Scrollbar");
        scrollbar->type(FL_HORIZONTAL);
        scrollbar->slider_size(.5);              // the fractional size of the scrollbar's tab, 1/2 scollbar's size
        scrollbar->bounds(100,200);              // min/max value of the slider's positions
        ((Fl_Valuator*)scrollbar)->value(150.0); // the initial value (in fltk1.3+ you can use scrollbar->value(150); 
        scrollbar->step(10);                     // force step rate to 10 (slider move changes value: 100, 110, 120..)
        scrollbar->callback(Scrollbar_CB, (void*)&out);
        // Create output to show scrollbar's value
        out = new Fl_Output(200,150,100,40,"Scrollbar Value:");
        out->textsize(24);
    }
    win->end();
    win->show();
    Scrollbar_CB(0,0);                  // show scrollbar's initial position
    return(Fl::run());
}



How to draw text over an image


Demonstration of deriving a class that draws text over an image.

Displays an image with no text until the button is pushed.

Shows several things: how to load and display an image, how a button callback can change a widget's label(), and how a custom widget can display an image with its label text over the image.

Text over image demo

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_JPEG_Image.H>
#include <FL/fl_draw.h>
//
// Demonstrate how to draw text over an image - erco 02/20/09
//
class MyImage : public Fl_Widget {                      // Create a custom widget
    Fl_JPEG_Image *jpg;                                 // will contain the jpeg image
    void draw() {                                       // Take full control of drawing our widget
        fl_color(FL_RED); fl_rectf(x(),y(),w(),h());    // draw red filled rectangle as background
        jpg->draw(x(), y(), w(), h());                  // draw image over background
        if ( label() ) {                                // any label assigned?
            fl_font(labelfont(), labelsize());          // set font/size
            fl_color(labelcolor());                     // set color
            fl_draw(label(), x(),y(),w(),h(), align()); // draw text over image and background
        }
    }
public:
    MyImage(int X,int Y,int W,int H) : Fl_Widget(X,Y,W,H) {     // ctor
        jpg = new Fl_JPEG_Image("/tmp/foo.jpg");                // load jpeg image (change filename as needed)
    }
};

// Globals
Fl_Window *win = 0;
Fl_Button *but = 0;
MyImage   *img = 0;

void ButtonCallback(Fl_Widget*,void*) {                 // callback invoked when button pressed
    img->label("Your text goes here.");                 // label to show text
    img->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);        // label draws 'center' and 'inside' the widget
    img->labelcolor(FL_WHITE);                          // label color
    img->labelsize(30);                                 // label size
}
int main(int argc, char **argv) {
    win = new Fl_Window(660, 540, "test");              // create window
        img = new MyImage(10,10,640,480);               // create our custom widget
        but = new Fl_Button(300,500,120,25,"Push");     // create 'Push' button
        but->callback(ButtonCallback);                  // assign callback to button
    win->end();
    win->show(argc,argv);
    return(Fl::run());
}



OpenGL Texturemapped Cube

GL_MODULATE GL_DECAL

Demonstration of checkerboard texture mapped rotating cube with lights and perspective frustum. Uses FLTK timer to handle rotation, and procedural generation of texture mapped 'checkerboard' image.

Cube rotates showing all sides. Texture map is a white checkerboard that by default inherits colors from the cube's polygons, due to opengl's default for glTexEnv() defaulting to GL_MODULATE. To have the texture shown in its own color (pure white), add a call to glTexEnv() setting the GL_DECAL parameter.

Cube rotates showing all sides.

Thanks to Loic for commented cube object posted on fltk.opengl (03/17/09).

Texture Mapped Cube

#include <stdio.h>
#include <math.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
//
// OpenGL spinning cube with checker texturemap -- erco/loic 03/17/09
//
#define WIN_W 400       // window width
#define WIN_H 400       // window height
#define TEX_W 64        // texturemap width
#define TEX_H 64        // texturemap height
#define FPS (1.0/24.0)  // frames per second playback
//
// OpenGL class to show texturemapped cube
//
class MyGlWindow : public Fl_Gl_Window {
    double spin;
    GLuint TexID;
    // TIMER CALLBACK
    //    Handles rotation the object
    //
    static void Timer_CB(void *userdata) {
        MyGlWindow *mygl = (MyGlWindow*)userdata;
        mygl->spin += 2.0;       // spin
        mygl->redraw();
        Fl::repeat_timeout(FPS, Timer_CB, userdata);
    }
public:
    // CTOR
    MyGlWindow(int x,int y,int w,int h,const char *l=0) : Fl_Gl_Window(x,y,w,h,l) {
        spin = 0.0;
        Fl::add_timeout(FPS, Timer_CB, (void*)this);       // 24fps timer
    }
    // PERSPECTIVE VIEW
    //    Same as gluPerspective()..
    //
    void Perspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) {
        GLdouble xmin, xmax, ymin, ymax;
        ymax = zNear * tan(fovy * M_PI / 360.0);
        ymin = -ymax;
        xmin = ymin * aspect;
        xmax = ymax * aspect;
        glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
    }
    // RESHAPED VIEWPORT
    //     OpenGL stuff to do whenever window changes size
    //
    void ReshapeViewport() {
        // Viewport
        glViewport(0, 0, w(), h());
        // Projection
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        GLfloat ratio = w() / h();
        Perspective(30.0, 1.0*ratio, 1.0, 30.0);
        glTranslatef(0.0, 0.0, -8.0);
        // Model view
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    }
    // OPENGL INITIALIZATION
    //    OpenGL stuff to do *only once* on startup.
    //
    void GlInit() {
        // Make sure we only do this once
        static int first_time = 1;
        if ( first_time ) {
            first_time = 0;
            // Texture Map Init
            GLubyte img[TEX_W][TEX_H][3]; // after glTexImage2D(), array is no longer needed
            glGenTextures(1, &TexID);
            glBindTexture(GL_TEXTURE_2D, TexID);
      
            /*** Texture Mapping Mode
             ***    Uncomment one of the following lines: GL_DECAL or GL_MODULATE
             ***/
            //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);   // use actual texture colors
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  // texture colors affected by poly's color
      
            for (int x=0; x<TEX_W; x++) {
                for (int y=0; y<TEX_H; y++) {

                    /*** Texture Pattern
                     ***     Uncomment one of the following lines: checkboard or basketweave
                     ***/
                    //GLubyte c = ((x&16)^(y&16)) ? ((x%16)<<4) : (((x%16)^15)<<4); // basket weave
                    GLubyte c = ((x&16)^(y&16)) ? 255 : 0;                          // checkerboard

                    img[x][y][0] = c;
                    img[x][y][1] = c;
                    img[x][y][2] = c;
                }
            }
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_W, TEX_H, 0, GL_RGB, GL_UNSIGNED_BYTE, &img[0][0][0]);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glEnable(GL_TEXTURE_2D);
            // Misc OpenGL settings
            glShadeModel(GL_FLAT);
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);
        }
    }
    // FLTK DRAW
    //    Called by FLTK to draw the scene.
    //
    void draw() {
        // Initialize/handle reshaped viewport
        if ( !valid() ) {
            valid(1);
            GlInit();
            ReshapeViewport();
        }
        // Clear
        glClearColor(.5,.5,.5, 0.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // Setup model matrix
        glLoadIdentity();
        glRotatef(spin, 0.5, 1.0, 0.0); // show all sides of cube
        // Draw cube with texture assigned to each face
        glBegin(GL_QUADS);
          // Front Face
          glColor3f(1.0, 0.0, 0.0); // red
              glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0); // Top Left Of The Texture and Quad
              glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0); // Top Right Of The Texture and Quad
              glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0); // Bottom Right Of The Texture and Quad
              glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0); // Bottom Left Of The Texture and Quad
          // Back Face
          glColor3f(0.0, 1.0, 1.0); // cyn
              glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0, -1.0); // Top Left Of The Texture and Quad
              glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0, -1.0); // Top Right Of The Texture and Quad
              glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); // Bottom Right Of The Texture and Quad
              glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0); // Bottom Left Of The Texture and Quad
          // Top Face
          glColor3f(0.0, 1.0, 0.0); // grn
              glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0); // Top Left Of The Texture and Quad
              glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0); // Top Right Of The Texture and Quad
              glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0,  1.0); // Bottom Right Of The Texture and Quad
              glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0,  1.0); // Bottom Left Of The Texture and Quad
          // Bottom Face
          glColor3f(1.0, 0.0, 1.0); // mag
              glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0); // Top Left Of The Texture and Quad
              glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0); // Top Right Of The Texture and Quad
              glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0); // Bottom Right Of The Texture and Quad
              glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0); // Bottom Left Of The Texture and Quad
          // Right face
          glColor3f(0.0, 0.0, 1.0); // blu
              glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0,  1.0); // Top Left Of The Texture and Quad
              glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0); // Top Right Of The Texture and Quad
              glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0); // Bottom Right Of The Texture and Quad
              glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0); // Bottom Left Of The Texture and Quad
          // Left Face
          glColor3f(1.0, 1.0, 0.0); // yel
              glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0); // Top Left Of The Texture and Quad
              glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0,  1.0); // Top Right Of The Texture and Quad
              glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0); // Bottom Right Of The Texture and Quad
              glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); // Bottom Left Of The Texture and Quad
        glEnd();
        // DEBUG: CHECK FOR ERRORS
        GLenum err = glGetError();
        if ( err != GL_NO_ERROR ) {
            fprintf(stderr, "GLGETERROR=%d\n", (int)err);
        }
    }
};
int main(int argc, char *argv[]) {
    MyGlWindow* mygl = new MyGlWindow(10, 10, WIN_W-20, WIN_H-20, "Texture Test");
    mygl->end();
    mygl->resizable(mygl);
    mygl->show();
    return(Fl::run());
}



OpenGL Texture Mapped Image With Lighting

Spinning cube demonstration with a PNG image texture-mapped onto the cube, with a simple lighting model and materials applied.

Shows how FLTK's Fl_Shared_Image class can be used to load any of the image types that FLTK supports, and assign the image as a texture map.

Thanks to Ian MacArthur for a pointer to the Fl_Shared_Image class (which I'd never used before -- I was going going to post an example with Fl_PNG_Image instead, but Fl_Shared_Image is more flexible).

Cube object and normals nabbed from an SGI OpenGL example called scubes.c by Mark J. Kilgard.


Screenshot of this image-texturemapped-cube.cxx demo program.

I added a lighting model and materials so that the texture reacts to lighting.

Fortunately, Fl_Shared_Image's internal image format for 3 channel depth images is fully compatible with opengl's glTexImage2D() call, so no 'conversion' step is necessary; the image can be applied with almost two lines of code:


---- snip
        Fl_Shared_Image *img = Fl_Shared_Image::get(filename);
        /* ..error checking snipped.. */
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img->w(), img->h(), 0, GL_RGB, GL_UNSIGNED_BYTE, img->data()[0]);
---- snip
    
..which loads the image and assigns the texture. To get the lighting model to work, I used a cube object and normals from one of SGI's example programs that included pre-computed normals. (Without the correct normals on the cube, it looked like the light was moving around even though it was stationary..!)



UTF8 Japanese Test

Demonstration of Japanese UTF8 text display.


This is a screenshot under Ubuntu of this utf8-japanese-songs.cxx demo program.

Under Ubuntu, I had to make sure Japanese fonts were installed, eg:

    apt-get install xfonts-intl-japanese
..and in my case, I needed to make sure the following line was uncommented:
    Fl::set_font(FL_HELVETICA, "Kochi Gothic"); // uncomment for linux
..for the text to display using Kochi Gothic. You may need to comment out this line or change it for your specific OS.



Alpha Blending with PNG files

Demonstration of alpha blending these two PNG files over a widget:


left-alpha.png

right-alpha.png

What follows is the code that combines the two above images (which you can download by right clicking to save on your machine), and displays them over the top of the window with a text string underneath.

This is one way to do it.. in this case showing two images over one another. I could have also just assigned an image() to the widget using this technique, but in this case I wanted full control over how the text is drawn.

Alpha Blending PNG images
#include <stdio.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_PNG_Image.H>
#include <FL/fl_draw.H>
//
// Demonstrate overlapping alpha blended images -- erco 06/09/09
//
class MyWindow : public Fl_Window {
    Fl_PNG_Image *left;
    Fl_PNG_Image *right;
    void GetFLTKVersion(char *s) {
        sprintf(s, "FLTK %d.%d.%d", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION);
    }
public:
    void draw() {
        Fl_Window::draw();                    // Draw window widget first
        fl_font(FL_HELVETICA, 40);            // set font
        fl_color(FL_BLACK);                   // set color
        fl_draw("This is a test", 10, 150);   // draw a text string
        left->draw(0,0);                      // draw left alpha image over the above
        right->draw(0,0);                     // draw right alpha image over the above
    }
    MyWindow(int W, int H) : Fl_Window(W,H) {
        char s[80]; GetFLTKVersion(s); copy_label(s);
        left  = new Fl_PNG_Image("./left-alpha.png");        // assumes images in cwd
        right = new Fl_PNG_Image("./right-alpha.png");       // assumes images in cwd
        show();
    }
};
int main() {
    fl_register_images();
    Fl::scheme("plastic");
    MyWindow win(300,300);
    win.show();
    return(Fl::run());
}



Dumb Terminal Emulator


Linux Example


Windows Example

Demonstrates how to make a simple terminal that lets the user type commands and see the output in a scrolling FLTK text editor widget. In this example the user can type commands that are run in the shell [via popen()], but you can change this to run your own commands.

Dumb Terminal Emulator
#include <string.h>
#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Text_Editor.H>
#ifdef _WIN32
#define popen  _popen
#define pclose _pclose
#endif
//
// Simple terminal simulator (PROOF OF CONCEPT)
// 1.00 erco 07/30/2009 -- initial implementation
// 1.10 erco 09/06/2009 -- use Fl::event_text() to handle non-ascii codes
//
class MyTerminal : public Fl_Text_Editor {
    Fl_Text_Buffer *buff;
    int in, out;
    char cmd[1024];
public:
    MyTerminal(int X,int Y,int W,int H,const char* L=0) : Fl_Text_Editor(X,Y,W,H,L) {
        buff = new Fl_Text_Buffer();
        buffer(buff);
        textfont(FL_COURIER);
        textsize(12);
        in = out = 0;
        cmd[0] = 0;
    }
    // Append to buffer, keep cursor at end
    void append(const char*s) {
        buff->append(s);
        // Go to end of line
        insert_position(buffer()->length());
        scroll(count_lines(0, buffer()->length(), 1), 0);
    }
    // Run the specified command in the shell, append output to terminal
    void RunCommand(const char *command) {
        append("\n");
        fprintf(stderr, "EXECUTING: '%s'\n", command);
        FILE *fp = popen(command, "r");
        if ( fp == 0 ) {
            append("Failed to execute: '");
            append(command);
            append("'\n");
        } else {
            char s[1024];
            while ( fgets(s, sizeof(s)-1, fp) ) {
                append(s);
            }
            pclose(fp);
        }
    }
    // Handle events in the Fl_Text_Editor
    int handle(int e) {
        switch (e) {
            case FL_KEYUP: {
                int key = Fl::event_key();
                if ( key == FL_Enter ) return(1);              // hide Enter from editor
                if ( key == FL_BackSpace && cmd[0] == 0 ) return(0);
                break;
            }
            case FL_KEYDOWN: {
                int key = Fl::event_key();
                // Enter key? Execute the command, clear command buffer
                if ( key == FL_Enter ) {
                    // Execute your commands here
                    strcat(cmd, " 2>&1");                // stderr + stdout
                    RunCommand(cmd);
                    cmd[0] = 0;
                    append("\nEnter a shell command: ");
                    return(1);                          // hide 'Enter' from text widget
                }
                if ( key == FL_BackSpace ) {
                    // Remove a character from end of command buffer
                    if ( cmd[0] ) {
                        cmd[strlen(cmd)-1] = 0;
                        break;
                    } else {
                        return(0);
                    }
                } else {
                    // Append text to our 'command' buffer
                    strncat(cmd, Fl::event_text(), sizeof(cmd)-1);
                    cmd[sizeof(cmd)-1] = 0;
                }
                break;
            }
        }
        return(Fl_Text_Editor::handle(e));
    }
};
int main() {
    Fl_Double_Window win(620,520,"Terminal Test");
    MyTerminal edit(10,10,win.w()-20,win.h()-20);
    edit.append("Line one\nLine Two\nEnter a shell command: ");
    win.resizable(win);
    win.show();
    return(Fl::run());
}






-- End --