Chapter 12. Geometry Management

The Intrinsics provide the basic mechanisms and policies that underlay geometry management in Motif. This chapter describes the Intrinsics support for managing geometry within widgets. This chapter also explains the geometry management tools that Motif provides for widget writers.

The fundamental principle of geometry management is that parent widgets control the geometry of their children. Despite this principle, child widgets are not as powerless as it might seem. For example, a child widget may request a geometry change from its parent, or a parent widget may ask a child widget for its opinion on an impending geometry change.

Overview

Your widget needs to manage geometry when it encounters any of the following situations:

  • Your widget is initially realized.

  • An application calls XtSetValues to change one of the geometry resources in your widget.

  • The user resizes a shell containing one of your widgets.

  • An application resizes one of the parents of your widget.

  • An application resizes one of the children managed by your widget.

In addition to these typical cases, an application may also generate a geometry request by calling one of the Xt geometry management routines. Most of these routines require a response from your widget.

The Intrinsics provide the five geometry resources shown in the following table. The Intrinsics monitor the values of these resources in a variety of ways. For example, the XtSetValues call automatically monitors the five geometry resources and takes certain actions when they change.

Table 12-1. Geometry Resources of Core

FieldResourceMeaning
core.border_widthXmNborderWidthSpecifies the width of the border that surrounds the widget's window on all four sides. The width is specified in pixels. A width of 0 means that no border shows.
core.heightXmNheightSpecifies the inside height (excluding the border) of the widget's window.
core.widthXmNwidthSpecifies the inside width (excluding the border) of the widget's window.
core.xXmNxSpecifies the x-coordinate of the upper-left outside corner of the widget's window. The value is relative to the upper-left inside corner of the parent's window.
core.yXmNySpecifies the y-coordinate of the upper-left outside corner of the widget's window. The value is relative to the upper-left inside corner of the parent's window.

The following seven Core class methods handle most of the work of geometry management:

  • change_managed (of the parent widget)

  • geometry_manager (of the parent widget)

  • initialize (of the child widget)

  • set_values (of the child widget)

  • set_values_almost (of the child widget)

  • resize (of the child widget)

  • query_geometry (of the child widget)

Each of these methods is examined in the following subsections.

The Parent's changed_managed Method

The Intrinsics call a parent widget's change_managed method when either of the following happens:

  • One of its unmanaged children becomes managed.

  • One of its managed children becomes unmanaged.

When one of these things happen, the parent widget often must move or resize some of its children. The parent can

  • Move a child by calling XtMoveWidget

  • Resize a child by calling XtResizeWidget

  • Move and/or resize a child by calling either XtConfigureWidget or XmeConfigureObject

Of these four routines, we recommend using XmeConfigureObject because it is the only routine that knows how to update the appropriate drag and drop fields. Each of the four functions update the appropriate geometry resources of the child and, if the child is realized, reconfigure the child's window.

The change_managed method of a Motif manager widget must call the XmeNavigChangeManaged routine. The XmeNavigChangeManaged function establishes the correct keyboard traversal policy for all the children of the manager. Typically, your widget calls XmeNavigChangeManaged at the end of the change_managed method.

The following code from the ExmGrid demonstration widget illustrates how to write a change_managed method:

static void
ChangeManaged(
        Widget w
)
{
 Dimension gridWidth, gridHeight;
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass(w);

    if (!XtIsRealized(w))  {
  /* If the user or application has set an initial (creation)
     size, honor it. If the user or application has not set
     an initial size, XtWidth and XtHeight will return 0. */
        gridWidth = XtWidth(w);
        gridHeight = XtHeight(w);
    } else {
  /* Otherwise, just force width and height to 0 so that CalcSize
     will recalculate the appropriate size. */
        gridWidth = 0;
        gridHeight = 0;
    }

  /* The CalcSize method determines the ideal size of Grid. */
    if (gwc->grid_class.calc_size)
        (*(gwc->grid_class.calc_size))(w, NULL, &gridWidth, &gridHeight);
    else
        CalcSize (w, NULL, &gridWidth, &gridHeight);

 /* Ask parent of Grid if Grid's new size is acceptable. Keep asking until
    parent returns either XtGeometryYes or XtGeometryNo. */
    while (XtMakeResizeRequest (w, gridWidth, gridHeight,
                                &gridWidth, &gridHeight) == XtGeometryAlmost);

    /* Now that we have a size for the Grid, we can layout the children
       of the grid. */
    if (gwc->grid_class.layout)
        (*(gwc->grid_class.layout))(w, NULL);
    else
        Layout (w, NULL);

    /* Update keyboard traversal. */
    XmeNavigChangeManaged (w);
}

The Parent's geometry_manager Method

A managed child widget can make a geometry request of a realized parent by calling XtMakeGeometryRequest. The Intrinsics automatically call XtMakeGeometryRequest on a parent when an application changes a geometry resource of one of its children. Any call to XtMakeGeometryRequest automatically invokes the parent's geometry_manager method. The arguments of the geometry_manager method are the same as those to XtMakeGeometryRequest.

The child passes the specifics of its geometry request inside the second argument to XtMakeGeometryRequest. The geometry_manager method must respond to this geometry request in one of the following three ways:

  • Grant the child's request by returning XtGeometryYes.

  • Deny the child's request by returning XtGeometryNo.

  • Suggest a compromise to the child by returning XtGeometryAlmost.

If the geometry_manager method can grant the request, the geometry_manager method must do the following:

  • Update the appropriate geometry resources of the child.

  • Return XtGeometryYes.

If geometry_manager returns XtGeometryYes, then XtMakeGeometryRequest will take care of changing the geometry of the child's window. (In other words, the geometry_manager method does not have to do it.) The child is responsible for calling its own resize method to handle the new size.

The geometry_manager methods for all standard Motif widgets return XtGeometryYes, not XtGeometryDone. All geometry_manager methods of widgets that you write should return XtGeometryYes.

The geometry_manager method may be able to satisfy some, but not all, of a child's request. For example, it may be able to grant the requested width, but not the requested height. In this case, the geometry_manager method may offer the child a compromise geometry. It fills in the reply XtWidgetGeometry structure with the parameters it intends to allow, and it sets the corresponding bit in the reply bitmask for any parameter it intends to change from the value requested. If the child immediately makes another geometry request by using the compromise parameters, the geometry_manager method must grant the request.

Following is the first part of the GeometryManager method of the ExmGrid demonstration widget:

static XtGeometryResult
GeometryManager (
        Widget w,  /* instigator */
        XtWidgetGeometry *request,
        XtWidgetGeometry *reply
)
{
 ExmGridWidget gw = (ExmGridWidget) XtParent(w);
 XtWidgetGeometry parentRequest;
 XtGeometryResult result;
 Dimension curWidth, curHeight, curBW;
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass((Widget)gw);

  /* If the request was caused by ConstraintSetValues reset the flag */
    if (gw->grid.processing_constraints) {
      gw->grid.processing_constraints = False;
        /* The ConstraintSetValues added one to border_width;
           This is the Xt trick used to fire the GM when a non core
           geometry resource (like a constraint) changes.
           now take it away. */
        request->border_width -= 1;
    }

  /* Save the original child resources. */
    curWidth = w->core.width;
    curHeight = w->core.height;
    curBW = w->core.border_width;

  /* Deny any requests for a new position. */
    if ((request->request_mode & CWX) || (request->request_mode & CWY))
        return XtGeometryNo;

   if (request->request_mode & CWWidth)
     w->core.width = request->width;
   if (request->request_mode & CWHeight)
     w->core.height = request->height;
   if (request->request_mode & CWBorderWidth)
     w->core.border_width = request->border_width;

  /* Calculate a new ideal size based on these requests. */
  /* Setting width and height to 0 tells CalcSize to override these
     fields with the calculated width and height. */
    parentRequest.width = 0;
    parentRequest.height = 0;
    if (gwc->grid_class.calc_size)
        (*(gwc->grid_class.calc_size))((Widget)gw, w,
                                       &parentRequest.width,
                                       &parentRequest.height);
    else
        CalcSize ((Widget)gw, w, &parentRequest.width, &parentRequest.height);

Often a parent widget must change its own geometry in order to satisfy a child's request. The parent's geometry_manager method uses XtMakeGeometryRequest to ask its own parent for a geometry change. If an XtMakeGeometryRequest request to the grandparent returns XtGeometryYes, the geometry_manager method of the grandparent must update the requesting parent's geometry fields and may resize other child, but it should not call the requesting parent's resize method. The parent may call its own resize method as long as that routine does not call the requesting child's resize method. The parent's geometry_manager method then returns XtGeometryYes.

Sometimes the parent needs to make a geometry request to its own parent just to find out whether the grandparent will accept a proposed change. For example, the parent may intend to offer a compromise geometry to the child but it must first determine whether the grandparent will allow the parent to change its own geometry in order to offer the compromise. In this case, the parent does not want the grandparent actually to make the proposed change; it just wants the grandparent to tell the parent whether the change is acceptable.

In making its own geometry request to the grandparent, the parent sets the XtCWQueryOnly bit in the request bitmask. The grandparent can return XtGeometryYes, but it must not actually change any of its children. The parent then returns XtGeometryAlmost to the child, along with its compromise parameters. If the child accepts the compromise, the parent repeats its request to the grandparent without setting XtCWQueryOnly. The grandparent should grant the parent's request, and the parent can then grant the child's request.

If the grandparent's response is XtGeometryAlmost and the parent still wishes to offer a compromise to the child, it caches the grandparent's reply and returns XtGeometryAlmost to the child. If the child accepts this compromise, the parent then makes another request of the grandparent, using the cached compromise parameters from the grandparent and without setting XtCWQueryOnly . The grandparent should grant the parent's request, and the parent can then grant the child's request.

The following code forms the second half of the GeometryManager method of ExmGrid:

  /* Ask the Grid's parent if new calculated size is acceptable. */
    parentRequest.request_mode = CWWidth | CWHeight;
    if (request->request_mode & XtCWQueryOnly)
        parentRequest.request_mode |= XtCWQueryOnly;
    result = XtMakeGeometryRequest ((Widget)gw, &parentRequest, NULL);

  /*  Turn XtGeometryAlmost into XtGeometryNo. */
    if (result == XtGeometryAlmost)
      result = XtGeometryNo;

    if (result == XtGeometryNo ||
        request->request_mode & XtCWQueryOnly) {
        /* Restore original geometry. */
        w->core.width = curWidth;
        w->core.height = curHeight;
        w->core.border_width = curBW;
   } else {
       /* result == XtGeometryYes and this wasn't just a query */
       if (gwc->grid_class.layout)
           (*(gwc->grid_class.layout))((Widget)gw, w);
       else
           Layout ((Widget)gw, w); /* Layout with this child as the instigator,
                                      so that we don't resize this child. */
   }

   return (result);
}

The initialize Method

The way you write the geometry management sections of an initialize method depends heavily on whether you are writing a primitive widget or a manager widget.

Primitive Widgets

If the value of core.width or core.height is 0, then the initialize method of your primitive widget should generate a starting widget size. If the user or application has set a starting value for XmNwidth or XmNheight other than 0, then the initialize method should honor the user's or application's preference for its starting size.

A parent will treat the child's starting size as a suggestion. In other words, the parent may choose to honor or to ignore the child's starting size, or possibly to compromise on the starting size.

For consistency with other Motif widgets, your widget should set the core.width field to 0 when you want the widget to calculate a new preferred width and the core.height field to 0 when you want the widget to calculate a new preferred height.

Manager Widgets

Theinitialize methods of most manager widgets seldom perform any geometry management. In general, the initialize method should not specify values for any of the Core geometry resources shown in Table 12-1. Typically, the change_managed method of your manager's parent will establish the starting position and dimensions of your manager.

The Child's set_values and set_values_almost Method

When a user or application invokes XtSetValues on a geometry resource, XtSetValues makes a geometry request. (The five geometry resources are shown in Table 12-1.) After invoking all the widget's set_values methods, XtSetValues checks for changes to any geometry resources. If any of those resources have changed, it sets their values to those in effect before XtSetValues was called and then makes a geometry request with the new values as the requested geometry parameters. If the geometry request returns XtGeometryYes, XtSetValues calls the widget's resize method. If the parent's geometry_manager method returns XtGeometryDone, XtSetValues does not call the widget's resize method.

If the geometry request returns XtGeometryNo or XtGeometryAlmost, XtSetValues calls the widget's set_values_almost method. The child's set_values_almost method determines whether to accept the compromise, reject the compromise, or request an alternate geometry change.

XtSetValues passes set_values_almost the request and reply XtWidgetGeometry structures. If the request returns XtGeometryNo, the bitmask in the reply structure is 0. The set_values_almost method can accept a compromise geometry by copying the reply parameters into the request structure. It can also construct another request by altering the request structure, or it can end the negotiation by setting the request bitmask to 0. If the request bitmask is nonzero when the set_values_almost method returns, XtSetValues makes another geometry request and treats the result in the same way as for the original request.

A widget's set_values method can initiate a geometry request by changing any of the geometry resources. For example, if XtSetValues is invoked on a Label's text, the set_values method can calculate how large the widget should be to contain the new text and then set the relevant geometry fields accordingly. The set_values method should not do any resizing itself; in particular, it should not resize any child widgets because the geometry request might be denied. Resizing is usually done in the widget's resize method. The widget's set_values_almost method may need to restore some widget state in the event the geometry request is denied.

The Child's resize Method

When a child's size changes, the Intrinsics automatically call the child's resize method. This call informs the child that its size has been changed. The child's resize method must make any internal changes necessary to conform to the new dimensions. If the child is itself a composite widget, its resize method can move or resize its own children.

A widget's resize method is invoked in the following circumstances:

  • Whenever a parent calls XtConfigureWidget, XtResizeWidget, or XmeConfigureObject to resize the child

  • By XtSetValues when the widget's geometry resources are changed and the resulting geometry request returns XtGeometryYes

In addition, a shell's resize method is invoked when the size of the shell is changed, often by a user through the window manager.

When the Intrinsics call the resize method, the widget's Core geometry resources contain the new position and dimensions. The resize method must take these resource values as given; the resize method cannot issue a geometry request. The resize method uses these resource values to recalculate the widget's layout.

The resize method of a manager widget should not do layout itself. Rather, a manager widget should provide a separate layout routine. This layout routine should take as an argument the child that is making the request (if any) so that the layout routine can avoid resizing that child.

What to Cut When Space is Limited

One of the responsibilities of a resize method is to determine what should be displayed when there is not enough space to display everything. Motif provides the following precedence recommendations for handling this situation:

  1. Try to reduce or eliminate any unused space (white space) inside the widget. In many widgets, this unused space is caused by the widget margins. The purpose of the margins is to provide a break between the widget's visual and the widget's border decorations. When there is plenty of space inside the widget, the margins are helpful. However, when space becomes tight, the margins must be reduced or eliminated.

  2. Try to reduce the size of the widget's visual. For example, if the widget's visual is some sort of geometric shape, your widget should maintain the shape but scale down its size. If the widget's visual is text, then portions of the text will have to be clipped.

  3. Try to reduce the size of the widget's shadows.

  4. Try to reduce the size of the border highlight. (This is a last resort; always try to maintain at least the border highlight.)

The child's query_geometry Method

When calculating its layout, a parent widget may take account of a child's preferred size and location. The parent uses XtQueryGeometry to inquire about a child's preferred geometry. The parent passes to XtQueryGeometry pointers to two XtWidgetGeometry structures, one containing the parameters that the parent intends to impose and the other containing the preferred parameters returned by the child.

When the parent calls XtQueryGeometry, the Intrinsics automatically call the geometry_manager method of the child. The child's query_geometry method is responsible for determining the widget's preferred geometry. The method stores the parameters into the return XtWidgetGeometry structure, setting corresponding bits in the bitmask for fields that it cares about. The query_geometry method must return one of the following values:

  • If the parent's intended geometry is acceptable, the child's query_geometry method returns XtGeometryYes.

  • If the parent's and child's parameters differ for some field that both widgets care about, or if the child has expressed interest in a field that the parent does not care about, it returns XtGeometryAlmost.

  • If the child's preferred geometry is the same as its current geometry, it returns XtGeometryNo.

After the query_geometry method returns, XtQueryGeometry fills in any fields in the return XtWidgetGeometry structure that the child does not care about with the current values of the resources in the child widget. XtQueryGeometry returns the value returned by the query_geometry method.

Most composite widgets should call XtQueryGeometry whenever they intend to change the geometry of a child that is not in the process of making a geometry request. A geometry_manager method should not call XtQueryGeometry for the child making the request. For a widget making a geometry request, the requested geometry is the preferred geometry.

Handling Easy Query Geometry Requests

In many cases, the query_geometry method needs to consist of only two parts. The first part should calculate the widget's preferred width and height. The second part should pass the preferred dimensions to the XmeReplyToQueryGeometry convenience function.

Later on in this chapter, we'll see a sample query_geometry method that calls XmeReplyToQueryGeometry.

Exposure and Redisplay

A widget can recompute its layout in its resize, set_values, or geometry_manager method, but usually it does not actually generate the window contents in those methods. A widget usually regenerates its window contents in response to an Expose event, which causes the widget's expose method to be invoked. This method takes as arguments the widget, the event, and the set of rectangles to be redisplayed. Using the current state of the widget (including its geometry resources), the expose method generates the contents of either the affected rectangles or the window as a whole.

XmeConfigureObject, XtConfigureWidget, XtResizeWidget, and XtMoveWidget cause the server to generate Expose events when necessary. XtMakeGeometryRequest also causes the server to generate Expose events when the parent's geometry_manager method returns XtGeometryYes.

The expose method of all Motif manager widgets should call the XmeRedisplayGadgets routine. This routine passes exposure events down to all the gadget children of the manager. For example, following is the entire Redisplay method of ExmGrid:

static void
Redisplay (
        Widget w,
        XEvent *event,
        Region region)
{
  /* Pass exposure event down to gadget children. */
    XmeRedisplayGadgets (w, event, region);
}

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 12-2. Geometry Variables of ExmSimple

FieldMeaning
simple.pref_widthHolds an integral value representing the widget's current preferred width
simple.pref_heightHolds 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_reconfigureHolds 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 12-1. Initialization Geometry Management Call Sequence

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 12-2. A Labeled ExmSimple Widget

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:

  • A rectangle or arc

  • The shadow

  • 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.