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.
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;
MxGUI programs always use the main() procedure as their entry point, even on Windows systems. This application follows the recommended sequence of actions
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(); }
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; }
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; }