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_LEFT ) ; // Makes
the label print on the button's
// left
Other allowed values for the label position are PUPLACE_RIGHT, PUPLACE_ABOVE, PUPLACE_BELOW, PUPLACE_TOP_CENTER, PUPLACE_BOTTOM_CENTER, PUPLACE_LEFT_CENTER, and PUPLACE_RIGHT_CENTER. Their meanings should be obvious.
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