Simplicity and portability are words seldom associated with a graphical user interface. V is an interesting exception to this unfortunate rule.
I've been developing software for over 20 years, mostly for the PC environment. Today, almost any application must be written with a graphical user interface (GUI). It has always seemed to me that programming GUI applications is far harder than it should be, especially for simple quick and dirty utilities.
V is a C++ graphical user interface framework designed to provide a system for building GUI applications that is both easy to use and to program. The framework is small, elegant, and provides the tools required for building all but the most specialized applications. The resulting applications are consistent with the look and feel of the host environment.
I had two major design goals for V. The first was to make programming with V easy. Feedback from my computer science students who have been using V for the past three semesters indicates I've met that goal far better than I ever thought possible. I also designed V to be portable. Versions for the X Window System and Microsoft Windows 3.1, 95, NT are available now. The V system is freely available for use by anyone under the terms of the GNU Library General Public License.
V is not too big. It has less than 15 C++ classes that you have to interact with. This is unlike many other frameworks, which provide dozens and dozens of classes that you must learn and understand. The V framework supports only GUIs. It does not have templates, containers, and bunches of other C++ classes. If you need a good list class, use your favorite one from another class library. Use V for your interface.
The V View of the World
When designing V, I needed to account for two factors:
- what elements a typical GUI application needs
- how these elements work and look on different GUI platforms
Applications available for the major GUI platforms typically have a great deal in common. While the visual details may differ, most applications have windows that show views of the data being manipulated, and use menus and dialogs to control interaction with the user. The user interacts with the program using a pointing device, usually a mouse, and the keyboard.
The window is usually the main interface object used by an application. The data being manipulated by the user (e.g., text, graphics, spreadsheet cells) is displayed in the window. Often, several windows may be open at the same time, each giving a different view of the data. There is usually a menu associated with the window. The menu is used to enter commands, either to manipulate the data directly or to bring up dialogs for additional interaction.
V follows this general model. The top level interface object used by V is a Command Window. Each command window minimally consists of a menu bar, usually placed at the top of the window. A command window also usually has a drawing canvas area, used to draw text and graphics to display the data. A command window can optionally have a command bar, which includes command buttons and other command objects, and a status bar to display state information.
V supports modal and modeless dialogs with a comprehensive set of controls. These include command buttons, text labels, text input, list selection boxes, combo boxes, radio buttons, check boxes, icons, spinners, sliders, and progress bars. These controls may be grouped into boxes. Layout of controls in a dialog is defined by a dialog definition list in the source code. All controls may be used in window command bars as well as dialogs.
One of the constraints of V is that it has its own look and feel. While this may be a limitation, it is not necessarily bad. First, the look and feel is designed so that applications will be portable across platforms. More importantly, they look just like native applications on each platform. This means that some things that are possible on one GUI platform, but not another, are not included in V.
V also incorporates much of my own experience. I really like simplicity, and believe that just because you can do something, it is not necessarily a good idea to do so. Thus, for example, there are limitations on the number of menu items per menu, and how deeply you can nest pull down menus. These limits in fact help enforce good interface design.
Hide the Dirty Details
One of the problems with using most native GUI tool kits such as Xt, Motif, or the Windows SDK is the huge amount of overhead and detail required to perform even the simplest tasks. While part of this complexity may be necessary to allow total flexibility in what you can do, the vast majority of applications just do not need that flexibility. V was designed to hide the details of the underlying GUI library. Things such as library initialization, specific window handles, and calls required to build low-level controls are all hidden. Instead, you work at the much higher level of objects needed to build a typical GUI.
It has always seemed to me that a GUI object such as a menu could most simply be thought of as a single object consisting of a list of items on that menu with their associated attributes. Rather than requiring a set of complicated calls to build that menu list, in V you can simply define a menu as a static C++ struct array -- a list, in other words. The same applies to dialogs. A dialog is a list of control objects with associated attributes, including how the controls are positioned in the dialog. This philosophy leads to very easy to maintain code. Menu and dialog lists are well defined in a single place in your code, and it is very easy to modify and change the list definitions. Actions for each menu or dialog command are defined in a single C++ method that responds to command events.
One data object used by most, but not all, native GUI libraries is what is usually called a resource file. A resource file is most often used to specify layout of dialogs and menus. One reason resource files are used is that specifying such layouts directly in the code is often very difficult for the native tool kits. The combination of the way V lets you specify menus and dialogs as lists, and the way C++ makes responding to event messages so easy has really removed the need for resource files. This in turn eliminates one of the more complicated aspects of portability across platforms.
Getting Control of Events
Interaction with a GUI application from the user's viewpoint consists of a series of mouse movements and clicks, and text and command input through the keyboard. From the programmer's viewpoint, each of these is an event. The important thing about an event is that it can occur at any time, and the program cannot simply stop and wait for the event to happen.
Handling all these events is a messy business at best when programming with the native GUI tools. In Microsoft Windows, for example, you have to provide a Window Proc to handle events. You can provide a single Window Proc, in which case you have to figure out which window generated the event; or you can have a Window Proc for each class of windows. In X, you need to register a callback function for each event in each window.
Once you have the code in place to handle events, you then need to figure out just which events you really want, and which you can ignore, and when. You also usually have to come up with some mechanism for associating an event with a particular window with a particular set of data. It is all very complicated, and typically results in hundreds of lines of code, even for the simplest applications.
As an aside, the GUI class libraries such as MFC for Microsoft C++ or OWL for Borland help a little. While these systems provide tools for generating shell applications, you still have to understand the whole library, and they are huge, with tens or hundreds of classes. And using such aids pretty much restricts your future choice of platform.
C++ makes dealing with events much easier. Each event can be considered a message, and the message is central to object-oriented programming. In V, each object, such as a command window, has methods that the system sends event messages to. For example, there is a WindowCommand method that responds to command events such as menu picks. The application overrides the default V WindowCommand method to handle commands as needed by the application. All the details of handling events are hidden in the V implementation. Overriding default methods to handle events is incredibly easy when compared to the complications of working with native events.
An Example V Application
A complete, but simplified, V application is included in the accompanying listings. It lets you draw lines in different widths and colors, and save the drawing to a file. There are limitations and simplifications with the program, but it does represent a good example of a typical V program Figure 1 and 2 show screen shots of this sample running on Windows and X.
Listing 1 (drawapp.cxx) and Listing 2 (drawapp.h) are the source for the draw application class. All V applications consist of a single application object, here declared as draw_App. All of the details of initializing the native GUI system are hidden in the code of the base app class. The main purpose of the drawApp class is to define and start the initial window using the NewAppWin method.
Listing 3 (drawcmdw.cxx) and Listing 4 (drawcmdw.h) define the command window. They contain most of the code for handling user commands. Notice how the main menu and command button bar are very simply defined as lists at the beginning of drawcmdw.cxx. The draw program handles the commands defined in these lists in the single WindowCommand method. This program structure makes it very easy to change the commands supported, and to add the code required for carrying out the commands. The listing also shows the use of some V utility classes such as the notice box and the file selection dialog.
Listing 5 (drawcnv.cxx) and Listing 6 (drawcnv.h) define the drawing canvas. Mouse events are handled in this class. All the code for drawing lines and saving the drawing in a file is defined in this class. Rather than requiring you to define and register handlers for the various mouse and drawing events, the appropriate methods receive corresponding messages for the events.
Compared to a similar application using a native GUI library, this example is very simple and short. It has the added benefit of being portable to several GUI platforms.
Writing Your Own V Applications
If you want to try V for yourself, the complete source code is available via anonymous FTP from ftp.cs.unm.edu in the directory /pub/wampler as a compressed UNIX tar file vx-1.12.tar.gz. The Windows code is available as vwin112.zip. The V reference manual is also available as an HTML document on my WWW home page, http://www.cs.unm.edu/~wampler. V will compile on Linux and most other UNIX platforms. It also compiles with several Windows and NT compilers.
Once you have the V distribution, the best way to use V is to start with one of the example shell applications provided, then modify the code to fit your needs. The distribution includes a much more complete version of the draw program listed here, as well as a 150-page reference manual. Experience with my computer science students has shown that you can get a good working understanding of V in just a day or two.
V has achieved two major goals. First, it is easy to use to develop GUI applications. Second, it is portable to X Windows for UNIX platforms, and to MS Windows for PC platforms. Work on other versions is underway.
Even if you cannot use V directly, you should find its design useful for developing your own programs. The source code is available under the GNU license, and contains many extensive, well documented, examples of how to use the X and Windows toolkits. o
Dr. Bruce E. Wampler is currently an Adjunct Professor of Computer Science at the University of New Mexico, and teaches software engineering. Prior to that, he founded two PC software companies, including Reference Software International, which published various writing tools before being acquired by WordPerfect in 1992.