libgfx: Minimalist GUI Framework
Author: Michael Garland

## Minimalist GUI Framework

All interactive graphics applications need to create some sort of graphical user interface. While many applications may require a fairly sophisticated interface, there is a large class of applications which can be built with a very simple interface. The `libgfx` library provides a framework to ease the construction of such simple interfaces.

The kind of application interface supported by the GUI framework is pictured in the following figure:

The application window consists of three primary components:

1. A menu bar, which is managed by the GUI framework,
2. An OpenGL canvas window where the application is free to draw its content and receive user events such as mouse clicks, and
3. A status bar where the application can print text messages.

The code for the application pictured above provides a concrete example of using this GUI framework.

### Creating a GUI Application

To begin building your GUI application, you'll need to include the GUI framework header:

        #include <gfx/gui.h>

The framework itself is packaged as a C++ class named `MxGUI`. You create your application by subclassing it and overriding various virtual methods. You can then create a single instance of this class, which will represent your application. For consistency, it is recommended that you name the derived subclass `GUI` and its instance `gui` as in the following:

        class GUI : public MxGUI
        {
        public:
            // Application variables and methods

        public:
    	// Override virtual methods to handle events
        };

        GUI gui; // Create application instance

Programs based using this framework always use the `main()` procedure as their entry point, even on Windows systems. Inside this main procedure, you should perform this general sequence of operations:

1. Initialize global variables and member variables defined in the `GUI` class.
2. Call the `gui.initialize()` procedure to parse the command line and create the application.
3. Customize the default application by changing labels, adding menus, etc.
4. Call `gui.run()` to open the application window and begin the event loop.

The `MxGUI::initialize` method must be called to initialize the GUI framework.

        virtual void initialize(int argc, char **argv,
    			    Fl_Menu_Item *layout=NULL, int xw=640, int yw=480);

You should pass to it the `argc` and `argv` command-line variables as passed into `main()`. The framework will take care of parsing the command-line and calling the appropriate handlers. The optional `layout` argument can be used to specify an initial menu structure; the default value of `NULL` directs the framework to use the default menu layout. The optional `xw` and `yw` arguments specify the desired width and height (in pixels) of the OpenGL canvas.

#### Command-Line Processing

The `MxGUI` framework uses the FLTK `Fl::args` facility to parse the command line passed to `MxGUI::initialize()`. For each option on the command line, it invokes the following handler:

        virtual int cmdline_option(int argc, char **argv, int& index);

The `argc` and `argv` values are the complete array originally passed to `initialize()`. The `index` argument indicates which element of `argv` contains the current switch being processed. This `index` variable is passed by reference to allow the handler to consume additional elements of `argv` by incrementing `index`. The handler should return `1` if it handled the switch and `0` otherwise.

Once all options have been processed, the initialization code invokes the following handler for each file listed on the command line.

        virtual void cmdline_file(const char *file);

If no files are specified this handler will be invoked with a `NULL` value.

#### Drawing

The `MxGUI` framework uses two virtual methods to control drawing in the OpenGL canvas. The first of these is:

        virtual void setup_for_drawing();

This method is called whenever the canvas has been reconfigured. Primarily, this occurs when the canvas has either just been shown on the screen (e.g., after creation) or when it has been resized.

Whenever the canvas needs to be repainted, the framework will invoke the handler:

        virtual void draw_contents();

You should override this method and place all your drawing code in it.

#### Handling Mouse Events

Interactive applications will typically want to receive mouse events taking place on the OpenGL canvas. The framework classifies mouse events into three types:

1. Button down: the user has depressed a mouse button
2. Button up: the user has released a previously depressed button
3. Drag: the user is moving the mouse with a button depressed

When one of these events occur, the corresponding event handler will be invoked:

        virtual bool mouse_down(int *where, int which);
        virtual bool mouse_up(int *where, int which);
        virtual bool mouse_drag(int *where, int *last, int which);

The `where` argument is a 2-D array containing the pixel location in the OpenGL canvas at which the mouse was pointing when the event occurred. The number of the button involved (either 1, 2, or 3) is passed in the `which` argument. To simplify drag operations, that event handler is also passed the location of the `last` received drag event.

All of these mouse handlers should return `true` if the canvas should be redrawn in response to the event just handled. If the canvas does not need to be updated, they should return `false`.

#### Handling Key Presses

Key press events can be received through the virtual function:

        virtual bool key_press(int key);

The return value of this function indicates whether the key press event was consumed by the handler. You should make sure to return `false` if you do not use the given key, otherwise keyboard accelerators for the menu bar will no longer function correctly.

Note that FLTK defines symbolic names for non-ASCII keys. You can also query the state of the modifier keys using the `Fl::event_state()` function.

#### Animation

A very simple mechanism for supporting animation is also provided by the framework. To take advantage of this, first set the `MxGUI` instance variable

        float default_fps;

to the desired number of frames per second (defaults to 24). The method

        void animate(bool will);

can then be used to start and stop animation. The system works by setting a system timer to go off every 1/fps of a second. When that timer goes off, the framework will call the handler

        virtual void update_animation();

so that the application can update its state appropriately, after which the framework will invoke the `draw_contents()` handler to repaint the canvas. Note that this system makes no effort to guarantee a consistent update rate.

The default menu bar contains items to toggle animation on and off and to set the desired `default_fps` value.

### The Menu Bar

Many GUI applications use menu systems to provide the user control over the application. By default, the `MxGUI` framework creates a very simple menu layout for its applications. This standard menu provides basic features such as saving a snapshot of the OpenGL canvas to an image file, toggling animation, and setting the size of the display canvas. However, the application also has complete freedom to customize the menu system.

#### Static Menu Layouts

The `initialize()` method accepts an optional argument which specifies the layout of the menu system. This layout is an array of Fl_Menu_Item elements, whose format is described in the FLTK documentation. Passing a menu layout to `initialize()` will completely replace the default menu structure.

#### Adding Menus at Run Time

Once an initial menu system has been created, either the default or an application-specific one, additional items can be added at run time. To do this, you can invoke the `add()` method of the `MxGUI::menu_bar` object.

### The Status Bar

The status bar at the bottom of the GUI application can be used to display short textual messages to the user. You can use the following method to print text in the status bar:

        int status(const char *fmt, ...);

It accepts argument lists of exactly the same form as the `printf` function of the C standard library.