1. | Introduction: Graphical User Interfaces and Windowed Programs |
2. | Elementary PUI |
2.1 Creating User Interface Widgets | |
2.2 Invoking PUI | |
2.3 Widget Callbacks | |
3. | Conclusion |
Basic to a GUI is the idea of a widget. A widget is something that appears on the computer screen and which transfers data from the user to the program or from the program to the user. The user can activate a widget by pressing a key, clicking a mouse button, or by using some other input device. This lets the user send information to the program. A widget can also display text or a graphic, allowing the program to transfer information to the user.
A windowing system requires a specific style of program which differs from a text-based program in several fundamental ways. First, instead of starting at the beginning of a problem and stopping at the end, a windowed program starts by creating the windows and then enters an infinite loop in which it responds to user inputs. Second, instead of having a self-contained hierarchy of functions which call each other, a windowed program is controlled by the windowing system which calls functions as it sees fit. The following diagram shows the structure of a typical windowed program.
+------------------+ | Main Program | The application developer | Set up window(s) | supplies this. | and widget(s) | +------------------+ | V +----------------+ | Window Manager | The window manager library | (like GLUT) | supplies this. +----------------+ | +------+--------+---------------+---------------+----> etc. | | | | V V V V +----------+ +----------+ +----------+ +----------+ | Window | | Window | | Window | | Window | The application | Mouse | | Keyboard | | Idle | | Display | developer | Callback | | Callback | | Callback | | Callback | supplies these. +----------+ +----------+ +----------+ +----------+ | | | | V V V V +----------+ +----------+ May call +----------+ | PUI | | PUI | PUI functions | PUI | PUI | Mouse | | Keyboard | for default | Display | supplies | Function | | Function | behaviors | Function | these. +----------+ +----------+ +----------+ | | +-------+-------+ | +-------+-------+---------------+------> etc. | | | V V V +----------+ +----------+ +----------+ | Widget 1 | | Widget 2 | | Widget 3 | The application developer | Callback | | Callback | | Callback | supplies these. +----------+ +----------+ +----------+
The functions that the application developer supplies are called "callbacks" because the windowing system or user interface system "calls back" into the application.
A windowed program with a user interface, then, consists of a main program and a set of callbacks. The callbacks may invoke other function in turn, but these are all arranged in hierarchical trees underneath the callbacks.
A windowed program executes differently from a typical hierarchical program as well. It begins with the main program setting up the window(s) and the user interface. The main program then calls the window manager, which contains an infinite loop and never returns control to the main program. (GLUT in particular has this behavior; I cannot speak for all window managers.) When the window manager needs to redraw the display that is shown on the screen, it calls the application's Window Display Callback. When the user moves the mouse or clicks a mouse button, the window manager calls the application's Window Mouse Callback. When the user presses a key on the keyboard, the window manager calls the application's Window Keyboard Callback. Other input devices cause the window manager to call other callbacks in the application. Finally, if the user hasn't done anything for a while, the window manager will call the application's Window Idle Callback.
In building a window callback, the application developer must be sure to call the appropriate PUI functions. The applications's Window Mouse Callback, for example, must call the PUI mouse function "puMouse" if the user interface is to process a mouse click. The application's Window Display Callback, besides rendering any graphics in the window, must also call the PUI display function "puDisplay" in order to display the widgets in the user interface.
When the application developer's window callback calls the appropriate PUI function, PUI determines which (if any) widget the user has just activated with his mouse, keyboard, or other input device. PUI then calls that widget's callback function which the application developer must supply and which provides the actual heart of the interface. The callback for an input widget will probably read the text that the user has typed there and pass it on to the application. The callback for a button widget may turn something in the program on or off. What a callback does is limited only by the application developer's imagination.
PUI was written in C++, designed to be invoked from C++ programs. The following text and examples will assume a program written in C++.
Each widget in the Picoscopic User Interface is an object in C++. The application developer creates a widget by defining a variable to point to the object and creating a new object:
puButton *button = new puButton ( 10, 10, 100, 30 ) ;
To set the properties of a widget, the application developer calls methods in the object:
button->setPosition ( x, y ) ; // Sets the position of the widget to (x, y) button->setSize ( w, h ) ; // Sets the size of the widget to (w, h) button->setCallback ( widget_cb ) ; // Sets the callback to "void widget_cb ( puObject *ob )" button->setLegend ( "Press Me" ) ; // Sets the legend button->setLabel ( "A Button:" ) ; // Sets the label button->setLabelPlace ( PUPLACE_LOWER_LEFT ) ; // Makes the label print on the button's left
Other allowed values for the label position are:
For a detailed description of these have a look at the PUI Programmers Guide.
All of these calls are optional. If the application developer does not want to label a widget, he simply does not call "setLabel" or "setLabelPlace". Similarly, if he has set the widget's position and size while creating the widget, there is no need to call "setPosition" or "setSize".
The following subsections describe some of the widget types that PUI supports.
The button has two possible methods of construction:
char **labels = { "Label1", "Label2", "This is Label3", "Four", NULL } ; puButtonBox my_box = new puButtonBox ( x1, x2, y1, y2, labels, <0|1> ) ;
This creates a button box with lower left-hand coordinates of (x1, y1) and upper right-hand coordinates (x2, y2). The number of buttons is specified by the number of entries in the "labels" array; each string before the NULL gets a button. The final argument in the constructor specifies whether the user can select multiple buttons (0) or whether selecting a new button will deactivate the previously-selected button (1).
The button box should not be given a legend.
An input box is constructed by the following function call:
Once the menu bar has been constructed, it must be loaded with its
entries and submenus. A submenu is the list that drops down when
the user clicks on an entry in the menu. A typical menu bar will
have the entries "File", "Edit", "Search", and "Help". Under the
"File" entry is a submenu with entries "New", "Open", "Save", "Save As",
a separator, "Page Setup", "Print", another separator, and "Exit".
Each entry in the submenu has its own widget callback function. To
create a submenu, the application developer must first create two null-terminated
lists of character strings and callback functions:
char *file_submenu [] = { "Exit", "-----", "Print", "Page Setup", "-----", "Save As", "Save", "Open", "New", NULL } ; puCallback file_submenu_cb[] = { exit_cb, NULL, print_cb, page_setup_cb, NULL, save_as_cb, save_cb, open_cb, new_cb, NULL } ;
Notice here that the submenu entries are placed in the list from bottom to top. After the last character string is a NULL entry; this tells PUI that there aren't any more entries in the submenu. The callbacks for the separators are also null, indicating that there is no callback for the separator. The submenu is added to the menu bar with a call to the "add_submenu" function:
menu->add_submenu ( "File", file_submenu, file_submenu_cb ) ;
Further calls to "add_submenu" add the other submenus:
char *edit_submenu [] = { "Select All", "-----", "Paste", "Copy", "Cut", "-----", "Undo", NULL } ; puCallback edit_submenu_cb[] = { select_all_cb, NULL, paste_cb, copy_cb, cut_cb, NULL, undo_cb, NULL } ; menu->add_submenu ( "Edit", edit_submenu, edit_submenu_cb ) ; char *search_submenu [] = { "Find Next", "Find", NULL } ; puCallback search_submenu_cb[] = { find_next_cb, find_cb, NULL } ; menu->add_submenu ( "Search", search_submenu, search_submenu_cb ) ; char *help_submenu [] = { "About", "Help", NULL } ; puCallback help_submenu_cb[] = { about_cb, help_cb, NULL } ; menu->add_submenu ( "Help", help_submenu, help_submenu_cb ) ;
When the last submenu has been added, the application developer must close the menu bar:
menu->close() ;
This is absolutely necessary or else PUI will think that any widgets the application defines later are supposed to go into the menu bar.
The menu bar does not support a legend. It does not support a callback itself, although all the entries in the submenus certainly do support callbacks. Repositioning and resizing a menu bar will be problematic as well. There may be an occasion when a menu bar can take a label, but this would be rare.
// Create the GLUT window and the OpenGL context glutCreateWindow ( "PUI Application" ) ; // Set up the GLUT window callbacks glutDisplayFunc ( displayfn ) ; // display the window glutMouseFunc ( mousefn ) ; // process mouse clicks glutMotionFunc ( motionfn ) ; // process mouse moves glutPassiveMotionFunc ( motionfn ) ; glutKeyboardFunc ( keyboardfn ) ; // process key presses glutIdleFunc ( displayfn ) ; // what to do when nothing's happening // Initialize PUI puInit () ; // Set up the widgets here
void displayfn ( void ) { // stuff to display any non-PUI drawings // update any PUI widgets that update by themselves ... like a timer display // redisplay PUI puDisplay () ; // Finish up glutSwapBuffers () ; glutPostRedisplay () ; } void mousefn ( int button, int updown, int x, int y ) { // Invoke the PUI mouse function puMouse ( button, updown, x, y ) ; glutPostRedisplay () ; } void motionfn ( int x, int y ) { // Invoke the PUI mouse motion function puMouse ( x, y ) ; glutPostRedisplay () ; } void keyboardfn ( unsigned char key, int, int ) { // Invoke the PUI keyboard function puKeyboard ( key, PU_DOWN ) ; glutPostRedisplay () ; }
void widget_cb ( puObject *ob ) { // code goes here }
where "puObject" is the C++ type for a generic widget. When PUI calls a widget callback, it passes to it the address of the widget whose activation caused the callback. This is useful because it allows a single callback for multiple widgets:
void widget_cb ( puObject *ob ) { if ( ob == button ) // the button from section 2.1 { ... } else if ( ob == my_box ) // the button box from section 2.1.2 { ... } else { printf ( "Error - unknown widget" ) ; } }
A widget callback needs to be either a regular function (global or defined in the same file that contains the widget's definition) or a static member function of a class. It cannot be a regular member function of a class.
An application program can use the following function to retrieve the value of a PUI widget ("ob" is the pointer to the object):
int ivalue1 = ob->getValue () ; // No argument, return the integer value int ivalue2 ; ob->getValue ( &ivalue2 ) ; // Place the integer value in the argument float fvalue ; ob->getValue ( &fvalue ) ; // Place the floating-point value in the argument char svalue[PUSTRING_MAX] ; // Create a string ob->getValue ( svalue ) ; // Copy the string value into the argument // Note that the argument is of type "char *" char *sptr ; ob->getValue ( &sptr ) ; // Have the pointer point to the string value // Note that the argument is of type "char **"
The values of the various widgets have the following meanings:
The PUI has considerably greater capabilities than have been listed here. There are well over a dozen additional types of widgets. Additionally, the application developer can do many more things with this widgets than have been described here.
"As to more than these, my son, beware. Of the making of many books there is no end, and in much study there is weariness for the flesh." - Ecclesiastes 12:12