libgfx: Simple GUI Example Program
Author: Michael Garland

## Simple GUI Example Program

This is a very simple program which uses the minimalist GUI facility provided by `libgfx` to create a window a draw a red square in it. When first started, it presents the user with a window which looks like the one shown below. The full source code for this example application can be found in the file `tests/t-gui.cxx` in the `libgfx` source distribution.

### Setup

The program begins by including some of the standard `libgfx` headers.

    #include <gfx/gfx.h>
    #include <gfx/gui.h>
    #include <gfx/gltools.h>

The first step in using the `libgfx` GUI framework is to subclass the `MxGUI` class and override whatever handlers you want to write code for. After defining the subclass, you must then create a single instance. In this application, we override the handlers involved with drawing and mouse events. We also follow the recommended idiom of naming our derived subclass `GUI` and its lone instance `gui`.

    class GUI : public MxGUI
    {
    public:
        float angle, opt_theta, center[2];
        bool dragging;

    public:
        // Drawing-related handler methods
        virtual void setup_for_drawing();
        virtual void draw_contents();
        virtual void update_animation();

        // Mouse-related handler methods
        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);
    };

    GUI gui;

### Application Entry Point

`MxGUI` programs always use the `main()` procedure as their entry point, even on Windows systems. This application follows the recommended sequence of actions

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

These four distinct phases are indicated in the program source by whitespace-separated blocks.

    main(int argc, char **argv)
    {
        gui.opt_theta = 10.0f;
        gui.angle = 0.0f;
        gui.dragging = false;
        gui.center[0] = gui.center[1] = 0.0f;

        gui.initialize(argc, argv);

        gui.toplevel->label("Simple GUI Example");

        return gui.run();
    }

### Drawing-Related Handler Methods

Whenever our OpenGL drawing canvas is created, resized, or otherwise reconfigured, the `setup_for_drawing()` handler is invoked. This is where you want to set up any graphics state that is the same for every frame.

    void GUI::setup_for_drawing()
    {
        glClearColor(0.65f, 0.65f, 0.65f, 0.0f);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glMatrixMode(GL_PROJECTION);
        gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
    }

If the contents of the OpenGL canvas need to be redrawn, the `draw_contents()` handler is invoked.

    void GUI::draw_contents()
    {
        glClear(GL_COLOR_BUFFER_BIT);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        // Draw the black axes
        glColor3f(0.0, 0.0, 0.0);
        glBegin(GL_LINES);
          glVertex2f(-1.0, 0.0);
          glVertex2f(1.0, 0.0);
          glVertex2f(0.0, -1.0);
          glVertex2f(0.0, 1.0);
        glEnd();

        glTranslatef(center[0], center[1], 0);
        glRotatef(angle, 0, 0, 1);

        // Draw the red rectangle
        glEnable(GL_BLEND);
        glColor4d(0.8, 0.15, 0.15, 0.85);
        glBegin(dragging?GL_LINE_LOOP:GL_POLYGON);
        glBegin(GL_POLYGON);
          glVertex2f(-0.5, -0.5);
          glVertex2f(-0.5, 0.5);
          glVertex2f(0.5, 0.5);
          glVertex2f(0.5, -0.5);
        glEnd();
        glDisable(GL_BLEND);

        glPopMatrix();
    }

The `MxGUI` framework provides a simplistic animation mechanism. Whenever animation is activated --- the user controls this via a menu entry --- the event loop will periodically call the `update_animation()` handler.

    void GUI::update_animation()
    {
        angle += opt_theta;
    }

### Mouse-Related Handler Methods

When the user clicks the left mouse button, we want to re-center the square around that position. We use a call to the `unproject_pixel()` utility function to map the pixel location into a world-space coordinate.

    static bool center_on_click(float *ctr, int *where)
    {
        double world[3];

        unproject_pixel(where, world);
        ctr[0] = (float)world[0];
        ctr[1] = (float)world[1];

        return true;
    }

Given the above code to center the rectangle on a click location, it is a simple matter to write our mouse handlers. Whenever the left button is pressed or dragged, we recenter the rectangle. We also maintain the value of `gui.dragging` to indicate whether the mouse is being dragged. If it is, the `draw_contents()` handler will draw the rectangle as an outline rather than a filled box.

    bool GUI::mouse_down(int *where, int which)
    {
        if( which==1 )
        {
    	dragging = true;
    	return center_on_click(center, where);
        }
        else return false;
    }

    bool GUI::mouse_up(int *where, int which)
    {
        dragging = false;
        return (which==1);
    }

    bool GUI::mouse_drag(int *where, int *last, int which)
    {
        if( which==1 )
    	return center_on_click(center, where);
        else
    	return false;
    }