Geometry Managers

Geometry managers are used for managing window layouts. They control position and size attributes based on given alignment rules and properties of sub controls to be managed.

It is very important that you understand how geometry managers work. Geometry managers are heavily used in Realsoft 3D user interface.

In old days developers used 'dialog editors' to design user interfaces. One positioned controls, such as sliders, buttons and check boxes to a dialog or window manually. Althought this approach is easy to understand and it quickly allows you to design dialogs consisting of couple of controls, building up and managing dynamic, complex interfaces this way is time consuming.

To insert new control to an existing dialog means you'll often have to move existing controls around to make room for the new controls to be added.

When you need to translate your dialogs to another language, some of the translated label may not fit.

To make the situtation even worse, controls in different platforms look completely different. For example, sliders in Motif are taller than sliders in Windows operating systems. In order to support multiple platforms, one would have to maintain multiple sets of dialogs.

Geometry managers provide a solution to this problem. Geometry managers are widgets which manage the position and size attributes of other objects widgets based on given alignment rules and other properties of controls to be managed. For example, you can ask a 'packer' geometry manager to pack a number of 'sliders' vertically. Alignments, spacing and many other things are automatically taken care of. The size of the dialog would be slightly higher on Motif due to higher slider control but the layout would look just perfect.

You can insert new controls to your dialog just in some seconds instead of spending minutes if not hours by trying to reposition dozens of controls manually using dialog editors.

To create a window with a check box in the middle of the window:

    #include <oops/r3packer.h>
    #include <oops/r3window.h>

    R3OBJ *gmanager, *window;

    window = R3New(R3CLID_WINDOW, ..

    gmanager = R3New(R3CLID_PACKER,
                     R3PA_Orientation, R3PAOF_VERTICAL,
                     R3TAG_END);

    checkbox = R3New(R3CLID_CHECKBOX, ....);

    /* ask gmanager to manage the check box */
    R3Do(gmanager, R3GMM_INSERT,
          R3GMA_Slave, checkbox,
          R3PA_Anchor, R3PAAF_CENTER,
          R3TAG_END);

    /* attach gmanager to window */
    R3SetAttrs(window, R3WA_Gmanager, gmanager, R3TAG_END);

You can freely size the window and the check box is always centered correctly.

The kernel library defines three geometry managers:

    inc/oops/r3packer.h 
    inc/oops/r3placer.h
    inc/oops/r3rowcol.h 

Packer can pack controls horizontally or vertically. For example, to create a tool bar window consisting of 10 buttons:

    #include <oops/r3packer.h>
  
    window = R3New(R3CLID_WINDOW, ...
    packer = R3New(R3CLID_PACKER,
                   R3PA_Orientation, R3PAOF_HORIZONTAL,
                   R3TAG_END);

    for(i = 0; i < 10; i++) {
        button = R3New(R3CLID_BUTTON,
                       R3GA_Parent, window,
                       R3GA_Text, "My button",
                       R3TAG_END);
  
        R3Do(packer, R3GMM_INSERT,
             R3GMA_Slave, button,
             R3PA_PackFlags, R3PAPF_EXPAND | R3PAPF_FILLX | R3PAPF_FILLY,
             R3TAG_END);
    }
    R3SetAttrs(window,
               R3WA_Gmanager, packer,
               R3TAG_END);

If you stretch the created window, also the buttons will be stretched.

Try giving longer names to buttons. For example:

        button = R3New(R3CLID_BUTTON,
                       R3GA_Parent, window,
                       R3GA_Text, "My button with more descriptive name",
                       R3TAG_END);

Now compile and run. Buttons get automatically stretched to make room for longer labels! Also the size of the window was increased correspondingly, to make room for the bigger buttons.

The packer defines several attributes which determine how the controls to be managed are positioned and stretched over their parent window.

    R3PA_PackFlags, R3PAPF_EXPAND,

If you specify:

    R3PA_PackFlags, R3PAPF_EXPAND | R3PAPF_FILLX | R3PAPF_FILLY,

the packer will stretch the buttons so that they fill the entire window and stretch with window.

Another attribute defined by packer class is R3PA_Anchor. This allows you to specify the position of the control using R3PAAF_ALIGN, R3PAAF_N, R3PAAF_E etc. codes. For example, if you specify:

    R3PA_PackFlags, R3PAPF_EXPAND,
    R3PA_Anchor, R3PAAF_W,

Each control attemps to remain move as left (west) as possible. To get controls positioned as north as possible, use R3PAAF_N.

Note: Geometry mangers are widgets, so you can insert it to other geometry managers similar to other widgets, such as buttons. This allows you to group geometry managers hierarchically to get all possible layouts designed.

For example, you can insert three horizontal packers to one vertical packer to get three rows of tools. This is actually how the r3rowcol.c geometry manager works.

[Note] Note

You can position controls explicitely by defining R3WGA_Left, R3WGA_Top, R3WGA_Width, R3WGA_Height attributes. For example:


    button = R3New(R3CLID_BUTTON,
                   R3WGA_parent, window,
                   R3GA_Text, "my button",
                   R3WGA_Width, 100,
                   R3WGA_Height, 10,
                   R3WGA_Left, 10,
                   R3WGA_Top, 12,
                   R3TAG_END);

Newer do this. Let the geometry managers design layouts for you.

Creating Dynamic Layouts with Geometry Managers

One of the biggest advantages of geometry managers is that they allow you to create dynamic layouts which show the user only the tools that make sense.

Geometry manager base class defines the attribute R3GMA_Stealth for this. Different tool sets can be inserted into separate geometry managers, and hidden from the user by setting this attribute. In addition to this, you will have to ask the window in question to update its layout by calling R3WGM_FIT method.

The following code demonstrates this:

    R3SetAttrs(packer1, R3GMA_Stealth, TRUE, R3TAG_END);
    R3SetAttrs(packer2, R3GMA_Stealth, FALSE, R3TAG_END);

    R3DoA(window, R3WGM_FIT, (void*)R3WFP_BESTFIT);

Note that R3GMA_Stealt not only makes all the widgets invisible but it also reports zero size to the window so that the user inteface don't allocate space for the invisible controls.

Realsoft interface is heavily based on this idea. For example, when you select a tool the control bar shows options corresponding the newly selected tool and hides others.