Automatic GUI Updating
 
What is Automatic GUI Updating?
 

Complex graphical user interfaces can have hundreds of Controls.  Applications typically have a number of different states, and not every Control is applicable in every state.  Some Buttons or other Controls may even be dangerous in some states.   Controls which are not appropriate in certain application states are typically made unavailable to the user by  desensitizing them (``grey-out''), or hiding them, or some other means.

Another issue is that Controls are often used to reflect the application's state, rather than just control it; for example, CheckButtons are shown as checked, Sliders indicate by their position the currently selected value, and so on.

The application programmer is faced with the issue of making sure that each and every Control is properly set in each state of the application.  As each action from the user could potentially cause a state change in every Control in the application, the situation quickly becomes a combinatorial nightmare.

A new type of message called the GUI-Update message can solve this problem.  The quintessential idea is that Controls update themselves by interrogating the application state.

Thus, rather than N action messages updating M controls (involving an amount of programming proportional to N*M), we just have N messages from Controls to perform some sort of action changing the application state, and M update messages from Controls to inquire about the new application state.

Clearly, the amount of programming involved is simply proportional to M+N.  For realistic size projects, this is a big win.
 
 

GUI Updating Example.

An example of GUI Updating is given in the ScribbleApp program.  The ScribbleApp program allows lines to be drawn in some canvas.  To clear the canvas, the users invokes the Clear Button.  The Clear Button is to be available only when something has been scribbled; otherwise, it is to be grayed out or desensitized.  One can accomplish this in FOX as follows:
 
 

    // Construct the Clear Button
    new FXButton(buttonFrame,"&Clear",NULL,app,ScribbleApp::ID_CLEAR,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);
This constructs a new Button object, which will send a message ID_CLEAR to the application object (app).

In the application object, we catch two message types from the Clear button:
 

    // Message Map for the Scribble App class
    FXDEFMAP(ScribbleApp) ScribbleAppMap[]={
      FXMAPFUNC(SEL_LEFTBUTTONPRESS,   ScribbleApp::ID_MOUSE,  ScribbleApp::onMouseDown),
      FXMAPFUNC(SEL_LEFTBUTTONRELEASE, ScribbleApp::ID_MOUSE,  ScribbleApp::onMouseUp),
      FXMAPFUNC(SEL_MOTION,            ScribbleApp::ID_MOUSE,  ScribbleApp::onMouseMove),
      FXMAPFUNC(SEL_COMMAND,           ScribbleApp::ID_CLEAR,  ScribbleApp::onCmdClear),
      FXMAPFUNC(SEL_UPDATE,            ScribbleApp::ID_CLEAR,  ScribbleApp::onUpdClear),
      };
The SEL_COMMAND message indicates that the Clear button has been pressed; its action will be to clear the drawing canvas:
 
 
    // Handle the clear message
    long ScribbleApp::onCmdClear(FXObject*,FXSelector,void*){
      canvas->clearWindow();
      dirty=0;
      return 1;
      }
The SEL_UPDATE message is sent when the Clear Button updates itself.  The GUI-Update handler determines whether the Clear Button should be sensitized or not depending on whether any drawing has taken place in the canvas; this is kept track of through a flag variable dirty:
 
    // Handle the GUI Update from the Clear button
    long ScribbleApp::onUpdClear(FXObject* sender,FXSelector,void*){
      FXButton* button=(FXButton*)sender;
      // Button is available when canvas is dirty only
      dirty ? button->enable() : button->disable();
      return 1;
      }
Note that in this case we know the origin of the message (the sender) to be of the type FXButton, so we can simply cast the sender object down to the appropriate type.
In general however, we may not always know [the only thing we know is that the sender is of type FXObject].
In such a case, the GUI Update handler should send a message back to the sender.   We can safely do this since all FOX objects are derived from FXObject, and FXObject's can be sent messages.  This leads to the following code:
 
    // Update sender
    long ScribbleApp::onUpdClear(FXObject* sender,FXSelector,void*){
      FXuint msg=dirty ? ID_ENABLE : ID_DISABLE;
      sender->handle(this,MKUINT(msg,SEL_COMMAND),NULL);
      return 1;
      }
Many FOX Widgets understand the ID_ENABLE, and ID_DISABLE messages;  if however, a message is sent to a sender that doesn't, nothing bad will happen as no message handler will be associated with the message.
 

When is GUI Updating Performed?

In FOX, automatic GUI Updating is performed automatically after handled events.  This is why it is important to return 1 or 0 in your message handlers.:
 

For increased efficiency, FOX does not perform GUI Updating until catching up with the event stream; also, FOX checks for new events between each GUI Update message, to prevent ``event-deafness'' for extended periods of time.  Even so, it is important to restrict your GUI Update message handlers to small routines, and to not perform any major computations in GUI Update message handlers.

As part of the GUI Update of each widget, layout is performed also.  Thus, many layout changes may be peformed during regular (non gui-update) message handling without any additional overhead:- FOX not only delays painting, but also layout computation until idle-processing time.  This makes FOX extremely fast.
However, since layout computation is performed during GUI-Update processing, GUI Update message handlers should avoid making changes to Controls that could cause layout computation, as these changes will not be handled till the next GUI Update cycle.
 
 
Automatic Gray Out or Hide.
 
FOX also has the option to automatically gray out or hide certain Widgets.  Both options work very similar, and differ only visually.  When automatic grayout is in effect, the Widget will be automatically grayed out (disabled, or desensitized to user inputs), when one of the following is true:
 

  1. The Widget's target is NULL.
  2. The Widget's target does not handle the Widget's SEL_UPDATE message.

If the above is not true, the Widget will be sensitized or enabled again.

Why is this useful?  If a Widget's target is an object that performs some sort of message delegation (for example, FXMDIClient and FXMDIChild do this), then the ability to handle a certain SEL_UPDATE message may depend on the delegate object that is in effect at the time the update message is sent.   If the particular delegate in effect does not handle the update message, there is no handler to make a Widget assume the correct state.

With automatic gray out, however, the absence of a handler for the SEL_UPDATE message can be turned into an action to gray out the Widget instead.  This will keep the GUI consistent even in the absence of update message handlers.

The automatic gray out technique is of particular significance when using MDI (Multiple Document Interface) widgets as both FXMDIClient and FXMDIChild perform message delegation.  Messages from the pulldown menus are typically sent to the FXMDIClient, and then subsequently forwarded by the FXMDIClient to the active FXMDIChild (Sometimes, there are no FXMDIChild windows, and the message can not be forwarded and then the message handler returns 0).

As automatic gray out of Widgets will cause a gray out if no handler for the SEL_UPDATE message is found, it is imperative that the SEL_UPDATE must always be handled when the Widget should be sensitive.  The update message handler does not necessarily have to do anything, it just needs to return 1.

To handle this common situation, FXWindow defines just such a message handler for you:  FXWindow::onUpdYes() will do nothing but return a 1.  You can simply append this to your message map as in:
 

FXMAPFUNC(SEL_UPDATE,ID_MYMENU,FXWindow::onUpdYes)

That will take care of it.  Of course if the update message should do something, you should write your own handler and make it return 1.
 



Copyright © 1998 Jeroen van der Zijp, all rights reserved.