In a certain sense, glGUI is a scenegraph system with user event routing and external rendering, with a restricted class of graphical objects, called widgets.
CUI_UI *ui; int main(...) { ui = new CUI_UI();This object is the root of the whole UI data structure; it keeps track of the UI structure, calls the external renderer and is called for mouse and keyboard events.
CUI_UI *ui; CUI_OpenGLRenderer *renderer; int main(...) { ui = new CUI_UI(); renderer = new CUI_OpenGLRenderer(); ui->SetRenderer( renderer );A pointer to the renderer object is made global, because the sample application uses a function LoadTexture of the renderer to load a texture. As text characters are simply special textures, a function like this is nearly always necessary.
Thus, the default shades and commonly used colors are defined next:
v2_f defborder[] = { {0,0}, {1,0}, {1,1}, {0,1} }; ui->AddShader( "default", new CUI_SimpleShader() ); ui->AddCoordSys( "default", new CUI_CoordSys() ); ui->AddBorder( "default", new CUI_PolyEdgeBorder( defborder, 4); ui->AddShader( "white", new CUI_SimpleShader( 1.0, 1.0, 1.0, 1.0 ) ); ...Here a number of helper objects are created and memorized via the glGUI registry under a string name.
A main panel or frame is the container for the other widgets like buttons:
CUI_Frame *mainF = new CUI_Frame(); mainF->SetShader( ui->GetShader("grey") ); ui->AddRootFrame( "intro", mainF ); ui->SetActiveRootFrame( "intro" ); ui->SetCallbackFunc( "intro", introHandler );It is added as a root frame and then declared the current active frame, which is the frame to be rendered from now on. An arbitrary number of such root frames containing UI widgets may be defined and switched as required.
Most often, the callback for mouse and keyboard actions is already registered here with the call to SetCallbackFunc.
CUI_Widget *title = new CUI_Widget(); title->Move( 0.1, 0.85, 0.35, 0.95 ); // x left, y bottom, x right, y top title->SetShader( ui->GetShader( "black" ) ); title->SetText( "glGUI " ); mainF->AddChild( title ); ui->AddFrame( title );A new widget is created, sized and positioned, colored, and given a text. (The - default - font in the example is white, so black is a useful background).
As far as more widgets are needed, this last step has to be repeated, e.g. for a mouse button:
In general, the user interface should not be hidden, obscured or garbled by other graphical objects, thus rendered last.
Redering is performed by calling the render method of the main CUI_UI object. In a typcial GLUT application this will be in the update callback function registered via glutDisplayFunc, which corresponds to the refesh callback in Windows.
ui->UpdateStatus(); // save and set display status, // e.g. PROJECTION and MODELVIEW matrix ui->Render( dTime ); // restore matricesThe UpdateStatus method updates the internal flags depending on previous mouse or keyboard actions, so that e.g. color changes of buttons by hoovering and clicking are visible. Cases where there is no status update before rendering are not yet known.
Depending on the implemetation of the rendering function, the graphic context has to be set. In case of openGL, the rendering interface should set (and restore) all flags and attributes that are always necessary, leaving the other to the caller.
The render call should be last in the refresh callback of the host system, so that the widgets are not damaged by other graphical objects. Widgets may be made transparent, so that the underlying scene is not completely obscured.
The render method of CUI_UI recursively calls the external rendering for each widget in the proper order, delivering the corresponding shaders as registered with each widget.
Two methods are available for keyboard and mouse actions:
(bool) ui->UpdateCursor( x, y, state ); (bool) ui->ProcessKey( key );UpdateCursor traverses the tree of widgets and checks if a (clickable) frame is at the cursor position. If it is, it will return true, and it will return false otherwise. A callback function will be called if an active widget is hit. Currently, the callback function can be registerd with a main frame only; future versions might allow callback functions for any clickable widget.
UpdateCursor saves the cursor position and displays any mouse cursor next time the rendering is called.
ProcessKey is used for keyboard input. glGUI tries the key input handler of the active widget which may process the key itself or pass it back, where some default action will be done. The logic for key processing is not yet clear.
The callback function currently receives the number of the widget
affected as a long (unsigned) integer.
This interface is clumsy.
The user has to track the assigned numbers and build himself
another registry to save relevant data.
The user may have objects of his own corresponding to the widgets
of glGUI and like to call pure virtual functions depending
of the widget affected.
Solutions may be:
The first one would be easiest to implement, but neither sufficient
nor compatible.
The second one would not clean up the structure.
The third one would be a clean and minimalistic soulution,
still easy to implement, and fully backwards compatible.
The fourth one would be clean and more flexible.
The fifth one would be most flexible and require the
greatest effort in implemation.