csvsj@garnet.berkeley.edu (Steve Jacobson) (10/01/88)
The following bug report exposes another problem associated with the unsigned int Dimension typecast: VERSION: X11 release 2 CLIENT MACHINE: Sun 3/50 CLIENT OPERATING SYSTEM: Sun OS 3.2 DISPLAY: Sun monochrome SYNOPSIS: Form widget behaves improperly when downsized to a point where a child's geometry constraints causes it not to be visible. A subsequent upsize making the form large enough for the child to be visible again reveals that the geometry constraints have been violated. DESCRIPTION: The Resize() procedure in Form.c goes through some careful calculations to adhere to a child's geometry constraint. The resulting width and height results may be negative given a child's constraints and the form's new size. The current code does a dirty fix; if height or width is less than 1, it is reset to one before the child is moved and resized. There are similar problems with x and y. This action causes the form to "forget" the original constraint geometry, so when the form is resized large enough to restore the original layout, the results are wrong. REPEAT-BY: Compile and run this program: #include <X11/Intrinsic.h> #include <X11/StringDefs.h> #include <X11/Command.h> #include <X11/Form.h> #include <X11/Cardinals.h> main(argc, argv) int argc; char **argv; { Arg args[15]; Widget top_level, form, button_1, button_2, button_3; top_level = XtInitialize(NULL, "bug", "BUg", 0, &argc, argv); form = XtCreateManagedWidget("Form", formWidgetClass, top_level, NULL, ZERO); XtSetArg(args[0], XtNbottom, XtChainTop); XtSetArg(args[1], XtNtop, XtChainTop); XtSetArg(args[2], XtNleft, XtChainLeft); XtSetArg(args[3], XtNright, XtChainLeft); button_1 = XtCreateManagedWidget("this is button_1", commandWidgetClass, form, args, FOUR); XtSetArg(args[4], XtNfromHoriz, button_1); button_2 = XtCreateManagedWidget("this is button_2", commandWidgetClass, form, args, FIVE); XtSetArg(args[3], XtNright, XtChainRight); XtSetArg(args[4], XtNfromHoriz, button_2); button_3 = XtCreateManagedWidget("this is button_3", commandWidgetClass, form, args, FIVE); XtRealizeWidget(top_level); XtMainLoop(); } Resize the window larger and see how button_3 tracks the window's right edge while button_1 and button_2 stay the same size. Now resize the window smaller, but not so small that there is no room for button_3. Notice that button_3 shrinks correctly, preserving the space between its right border and the window's right edge. If you resize the window larger again, button_3 again tracks the window's right edge correctly. Now resize the window smaller, so small that there is no space for button_3. Notice that the window correctly clips button_2. Now resize the window large enough to show button_3. Notice that button_2 is redisplayed correctly, but button_3 is apparently clipped by the window; its right border and the space between it and the window's right edge is not visible. The form has altered button_3's geometry constraint behavior. No matter what you do now, the window will clip button_3's right edge when it is visible. FIX: Here is diff output comparing a fixed Form.c with the original: 220c220 < return (loc); --- > return (loc > 0) ? loc : 0; 252a253,254 > if (width < 1) width = 1; > if (height < 1) height = 1; This fix removes the damaging resetting actions. Note that this fix is dependent on non-portable int/unsigned int type conversions. The core width and height are unsigned ints, so a negative width or height is treated as a very large number, so the child widget's window is clipped by the form widget, as it is under the original code. However, when the form is upsized, the value is then treated as a negative number in the coordinate calculations. While this will work on most architectures, it is not guarenteed to be portable. To fix it right would require, at the minimum, two additional integer variables in the core private data structure to hold negative dimension values in a portable manner. The resize calculations would use these instead of the unsigned ints.