[ Previous | Next | Contents | Glossary | Home | Search ]
Motif 2.1 Widget Writer's Guide



A Widget Case Study: ExmSimple

This section examines the geometry management portions of the ExmSimple widget. ExmSimple only has the capability of being a child widget; it cannot serve as a parent. Therefore, ExmSimple allows us to focus solely on the child side of geometry management.

The ExmSimple widget displays a simple visual that is either a rectangle or an oval.

Variables Influencing Geometry

In addition to the standard geometry resources of Core, the ExmSimple widget provides a few geometry fields of its own, as described in the following table:

Table 24. Geometry Variables of ExmSimple

Field Meaning
simple.pref_width Holds an integral value representing the widget's current preferred width
simple.pref_height Holds an integral value representing the widget's current preferred height
simple.need_to_compute_width Holds a Boolean value; if True, then the widget needs to renegotiate its width
simple.need_to_compute_height Holds a Boolean value; if True, then the widget needs to renegotiate its height
need_to_reconfigure Holds a Boolean value; if True, the widget needs to call the Reconfigure method (typically to calculate a new preferred width or height)

Widget Start Up

The Intrinsics automatically call the Initialize method of ExmSimple when ExmSimple is instantiated. The Initialize method determines whether or not the user or application has specified an initial value for width or height. The following code fragment examines the widget's height:

if (rw->core.height == FIND_NATURAL_SIZE) {
  nw->simple.need_to_compute_height = True;
}
else {
  nw->simple.need_to_compute_height = False;
  nw->simple.pref_height = rw->core.height;
  nw->core.height = rw->core.height;
}

(Initialize also contains parallel code for width.)

If the height is 0 ( FIND_NATURAL_SIZE is a constant 0), then the user or application has not specified an initial value for height. In this case, ExmSimple will need to calculate its preferred height. To do so, ExmSimple sets its need_to_compute_height flag to True.

On the other hand, if the user or application has specified an initial value for height, then the Initialize method should honor those requests. The initialize method does this by setting both simple.pref_height and core.height to the user's or application's request. Note that changing the value of core.height will ultimately cause the Intrinsics to alert the parent of ExmSimple. In fact, the user or application's requested starting size may not be honored by the parent of ExmSimple.

The Initialize method concludes by calling the Reconfigure method of ExmSimple.

Calculating Preferred Size

The Reconfigure method of ExmSimple calls the CalcWidgetSize method. The CalcWidgetSize method begins by calling the CalcVisualSize method. Therefore, the sequence of calls to this point is as follows:

Figure 8. Initialization Geometry Management Call Sequence.




View figure.

The CalcVisualSize method of ExmSimple is responsible for calculating the ideal size of the widget's visual. Note that ExmSimple provides no resources permitting the user any direct control over the size of the visual. For some widgets, the ideal visual size is fairly obvious. For example, the ideal size of the visual in ExmString is the extent of the string that is to be displayed. The ideal visual size of a widget that displays a pixmap would be the dimensions of the pixmap. For ExmSimple, though, the ideal visual size is not so obvious. After all, what is the ideal size of a rectangle or an oval? Does a 10-pixel-wide rectangle look better than a 12-pixel-wide rectangle? Since there is no true ideal size for a rectangle or oval, we have arbitrarily picked an ideal size of 30 pixels. The code in CalcVisualSize is as follows:

sw->simple.visual.height = IDEAL_SHAPE_SIZE;

After calculating visual size, the flow of control returns to the CalcWidgetSize method of ExmSimple. The CalcWidgetSize examines the need_to_compute_height flag. This flag was set to either True or False back in the Initialize method. As you may recall, a value of True means that the user or application has not specified a starting height for ExmSimple.

Therefore, if need_to_compute_height is True, then CalcWidgetSize will need to calculate the preferred widget size.

The preferred widget size must take into account not only the preferred visual size but also the widget's margins, shadows, and highlights. The following code does just that:

if (sw->simple.need_to_compute_height == True)
 sw->core.height = sw->simple.visual.height +
                     (2 * (sw->simple.margin_height +
                     sw->primitive.shadow_thickness +
                     sw->primitive.highlight_thickness));

If the need_to_compute_height flag is False, then the user or application has specified a starting height. ExmSimple needs to honor that preference by setting the core.height variable to the user's preference:

else
  sw->core.height = sw->simple.pref_height;

Upon completing the calculations in CalcWidgetSize, flow of control returns to the Reconfigure method. The widget dimensions calculated by CalcWidgetSize become the new preferred dimensions of ExmSimple, as follows:

nw->simple.pref_height = nw->core.height

Then, Reconfigure conditionally calls the Resize method. For performance reasons, Reconfigure is careful to call Resize only once. Why is this important? Well, consider that ExmSimple is a superclass for several other widgets (including ExmString). The initialize method is chained in superclass-to-subclass order. Therefore, when ExmString is instantiated, the Intrinsics call the Initialize method of ExmSimple prior to calling the Initialize method of ExmString. Consequently, the Reconfigure method will be called twice, once by the Initialize method of ExmSimple and once again by the Initialize method of ExmString. Therefore, if Reconfigure were not careful, it would end up calling the Resize method twice, once by ExmSimple and once again by ExmString. The first call to Resize will be a waste of time. Therefore, in this case, it was more efficient to make sure that the only time ExmSimple called Resize was when Reconfigure had been called by the instantiated class itself and not by one of its chained superclasses.

Resize

The Resize method makes no attempts to change the size of the widget. Rather, the goal of Resize is to fit everything into the amount of space that has been allocated for it.

To help you visualize the relevant portions of the ExmSimple widget, we provide Figure 12-2.

Figure 9. A Labeled ExmSimple Widget.




View figure.

The following portion of the Resize method of ExmSimple implements the rules described in Section 12.6.1. That is, this code figures out whether or not there is enough room in the widget to display everything. If there is not enough room, something will have to be trimmed back or removed altogether.

window_decoration_thickness = sw->primitive.highlight_thickness  +
                              sw->primitive.shadow_thickness;
mh = window_decoration_thickness + sw->simple.margin_height;
total_target_widget_height = (2 * mh) + sw->simple.visual.height;
 
if (sw->core.height >= total_target_widget_height) {
  /* We have enough space to display everything (the visual, the
margins,
     and the border decorations). */
  sw->simple.visual.y = mh;
  sw->simple.visual.height = sw->core.height - (2 * mh);
}
else if (sw->core.height >
          ((2 * window_decoration_thickness) +
sw->simple.visual.height)) {
  /* We do not have enough space to display everything, but we do have
     enough space to display the visual and the border decorations.
     The top and bottom margins will have to be reduced. */
  sw->simple.visual.y = (sw->core.height - sw->simple.visual.height)/2;
}
else if (sw->core.height > 2 * window_decoration_thickness) {
 /* Space is very tight. We will eliminate the top and right margins
    all together. Furthermore, we will reduce the size of the visual. */
  sw->simple.visual.y = window_decoration_thickness;
  sw->simple.visual.height = sw->core.height -
                             (2 * window_decoration_thickness);
}
  else {
 /* We do not have enough space to display even one pixel of the visual.
*/
  sw->simple.visual.height = 0;
}

Notice that the resize method does not actually render any pixels onto the screen. Instead, resize calculates the dimensions of the widget's visuals. Another routine (DrawVisual) will do the rendering.

Handling Size Changes

When an application calls XtSetValues to change a resource value in ExmSimple, the set_values method of ExmSimple must handle the change request. The geometry management code in the set_values method of ExmSimple is similar to the geometry management code in its initialize method. In both methods, the goal is to examine the value of core.width and core.height. If either is 0, then a preferred value for that dimension will have to be calculated. On the other hand, if the application has set the value of core.width or core.height to some value other than 0, then set_values must make that new value the widget's preferred size.

The set_values method of ExmSimple calls the Reconfigure method, just as the initialize method of ExmSimple does. The only difference is that the initialize method always calls Reconfigure, but the set_values method only calls Reconfigure if a reconfiguration is needed.

Handling Geometry Queries

The ExmSimple widget must supply a query_geometry method to handle geometry queries from its parents. This method must return XtGeometryYes, XtGeometryNo, or XtGeometryAlmost.

ExmSimple is not concerned how its parents might change x, y, and border_width. In fact, of the geometry resources, the only two that concern ExmSimple are width and height. Therefore, we can provide a query_geometry method that relies on XmeReplyToQueryGeometry. Prior to calling XmeReplyToQueryGeometry, the widget has to have calculated its preferred size.

Here is the entire query_geometry method of ExmSimple:

QueryGeometry (
        Widget widget,
        XtWidgetGeometry *parent_request,
        XtWidgetGeometry *child_reply)
{
 ExmSimpleWidget sw = (ExmSimpleWidget) widget;
 
   if (!XtIsRealized(widget)) {   /* Simple has not yet been realized.
*/
     child_reply->width  = XtWidth(widget);   /* might be 0 */
     child_reply->height = XtHeight(widget);  /* might be 0 */
   } else {                       /* Simple has been realized. */
     child_reply->width  = sw->simple.pref_width;
     child_reply->height = sw->simple.pref_height;
   }
 
 /* Return ExmSimple's preferred size */
   return XmeReplyToQueryGeometry(widget, parent_request, child_reply);
}

Redisplay

The Redisplay method of ExmSimple triggers the redisplay (or original display) of all visuals components in the widget. ExmSimple widgets consist of three visual components:

  1. A rectangle or arc

  2. The shadow

  3. The border highlights

    ExmSimple calls its DrawVisual method to draw the rectangle or arc. ExmSimple then calls its DrawShadow method to render its shadows. Finally, ExmSimple calls the expose method of XmPrimitive to draw the border highlight.


  4. [ Previous | Next | Contents | Glossary | Home | Search ]