Creating and processing composite windows
This topic includes the following sections:
- Composite window development steps
- Placing, removing, and deleting windows
- Processing composite windows
- Controlling tabbing, focus, and input context
- Buttons in composite windows
- Positioning composite windows, child windows, and lists
- Getting composite window information
UI Toolkit enables you to combine multiple windows and/or lists into a single window, called a composite window. For example, you could create a composite window that includes an input window, a table, an ActiveX control, and buttons. To the user, a composite window looks and functions as a single window.
A composite window consists of a parent window (called the composite container window) and child windows or lists. A child can be any type of UI Toolkit window or list, including an ActiveX window, a tab set window, an ActiveX Toolkit list, or another composite window. The composite window routines and methods enable you to create and process composite windows. Note the following:
- On Windows, child windows and lists are wholly contained within the composite container window.
- On Unix and OpenVMS, the display area for a child window or list is circumscribed if necessary to make it fit within the display area of the composite container window. Subsequent moving or resizing, however, may cause a child to exceed the boundaries of the container.
- A composite window can have a maximum of 2,047 children.
A composite window not only appears to users as a single, unified window, it is also treated in many ways as a single window by the application. Code that affects the container affects the composite window as a whole:
- When a container is placed, all placed child windows and lists become visible.
- When a container is moved, all child windows and lists are moved with it.
- When a container is removed, all child windows and lists become invisible. (When a container is deleted, though, Toolkit automatically removes placed child windows and lists. It does not delete them.)
Composite window development steps
The following steps include example code to illustrate composite window development. For a more complete code example, see %C_CONTAINER.
1. | Create the composite container window |
Start by using the DC_CREATE subfunction for %C_CONTAINER to create the composite container window. The example below creates a composite container window that
- has a Toolkit-assigned name. (Passing a null string for the name argument causes Toolkit to assign a unique name to the composite window.)
- fills the entire body of the application window. (Passing g_bdysiz, a global variable defined in tools.def, instructs Toolkit to use the number of rows in the body of the application window. Passing %W_INFO with WIF_SCOLS instructs Toolkit to use the number of columns in the application window.)
Additionally, this example returns the ID of the new composite container window in the id_contain variable.
id_contain = %c_container(DC_CREATE, "", g_bdysiz, %w_info(WIF_SCOLS))
2. | Add child windows and/or lists |
To add child windows and lists to the composite window, use the DC_ADD subfunction for %C_CONTAINER. In this call, you specify the ID of the container window as well as the ID of the window or list you want to add as a child. Additionally, you can set properties for the child, including placement, tabbing position, and a menu column to be loaded when the child is processed. For example, the following adds a list that starts on the first column of the first row of the composite window and loads a menu column with the ID id_selcol:
xcall c_container(DC_ADD, id_contain, DC_LIST, id_list, 1, 1,,,,id_selcol)
3. | Place the composite window |
To place a composite window, place its container window. For example, the following call places the composite window whose container window has the window ID id_contain.
xcall u_window(D_PLACE, id_contain, 1, 1)
When a container window is placed, only child windows and lists that have been placed are displayed in the window. Unplaced child windows and lists remain invisible. Additionally, when you place a child, it remains invisible until its container window is placed. See Placing, removing, and deleting windows below for more information.
4. | Process the composite window |
To process a composite window, call C_PROCESS. This routine calls the method for the active child. Toolkit includes default composite window processing methods, but you can create your own methods and specify one in the DC_ADD call for a child. The following processing loop example tests for menu entries and calls C_PROCESS, which passes the variables chan_icontacts, chan_contacts, list_contact, and inp_contact to the method for the active child.
; ; do_process - run input loop for composite window do_process, xcall c_process(id_contain, chan_icontacts, chan_contacts, & list_contact, inp_contact) if (g_select) begin using g_entnam select ("O_ADD "), call add ("O_DEL "), call delete ("O_SEL "), call select ("O_EXIT "), call update ;Leaves g_select == TRUE endusing end return
5. | Remove and delete windows |
To remove a composite window, remove the composite container window with the D_REMOVE operation for U_WINDOW. Use D_REMOVE to remove child windows as well. For a child list, use L_REMOVE. See Placing, removing, and deleting windows below for more information.
Placing, removing, and deleting windows
When you place a child, the child remains invisible until the composite container window is placed. When you place a composite window (by placing its container), only child windows and lists that have already been placed will appear on the window. Note that a container and its child windows and lists don’t have to be logged in the same environment. When a container is placed, placed windows and lists will be displayed, even if they are logged in different environments.
- To instruct Toolkit to place a child, either pass both the row and col arguments in the DC_ADD call that adds the child to the composite window, or use the D_PLACE operation for U_WINDOW or L_PLACE to place the child after it has been added to a composite window.
- To place the composite container window, pass the window ID for the container to the D_PLACE operation for U_WINDOW.
To instruct Toolkit to remove a child from the screen, use the D_REMOVE operation for U_WINDOW or use L_REMOVE. Note the following:
- If the composite container window has not yet been placed, the remove operation will have no visible effect because the child is already invisible.
- Removing a child with D_REMOVE or L_REMOVE removes the child from the display but does not remove (disassociate) the child from the container. The DC_ADD call that added it to the container still stands, and the child retains its position in the tabbing order for the composite window.
To remove a child from the display and from the container, use the DC_REMOVE subfunction for %C_CONTAINER.
To remove a composite window from the screen, pass the composite container window ID to the D_REMOVE operation for U_WINDOW. Removing the composite container window makes all child windows and lists invisible, no matter which environments they are logged in.
To delete a composite window, delete its composite container window. To delete a composite container window or a child, leave the environment in which it was logged or use the D_DELETE operation for U_WINDOW. Note, however, that deleting the container removes all child windows and lists but does not delete them.
Processing composite windows
Once you have created and placed a composite window, you can process it by calling C_PROCESS, which in turn calls methods that process the child windows and lists. Toolkit includes default processing methods, but you can instruct C_PROCESS to call your own custom methods instead. See C_PROCESS and The composite window processing methods for more information.
Controlling tabbing, focus, and input context
When a composite window is being processed, tabbing moves through placed child windows and lists according to the composite window’s tabbing order, which by default is the order they were added to the composite window. Then, after child windows and lists, tabbing moves through any enabled buttons added to the composite container window.
Every child window and list for a composite window has a tabbing index. This number specifies the child’s position in the tabbing order. Tabbing indices are base 1, so the first child for a composite window has a tabbing index of 1. (To get the tabbing index of a child, use the DC_CHILDINDEX subfunction for %C_CONTAINER.) Buttons on the composite container window also have tabbing indices, which are also base 1. |
You can do the following to control the tabbing order or activate a specific child or button:
- To control a child’s place in the tabbing order, add child windows and lists in the order that you want users to tab through them. (When you add a child, it is added to the end of the tabbing index by default. However, you can use the tabindex argument for DC_ADD to specify a different position when you add a child.)
- To change the active child, use one of the C_ menu entries (see the C_PROCESS Discussion) or use the DC_CONTEXT subfunction for %C_CONTAINER. You can also use DC_CONTEXT to determine which child is active in a composite window.
- To change focus to a specified button on the composite container window, use the DC_BTNFOCUS subfunction for %C_CONTAINER. You can also use this function to determine whether a button on the composite container window has focus.
Tabbing within child windows and lists
When processing a child window or list with L_INPUT, L_INPFLD, S_SELECT, T_EDIT, T_VIEW, or U_CHR, pressing Tab (or otherwise signaling the C_NEXT menu entry) moves focus to the first button for the window. Subsequent tabbing advances through remaining buttons for the window and then, if the window is on a tab, advances through buttons on the tab set. Finally, tabbing causes the input routine to return with g_select set to true and g_entnam set to “C_NEXT”, which instructs C_PROCESS to move focus to the next child window. Shift+Tab works the same way, but in reverse. It returns g_select set to true, but it sets g_entnam to “C_PREV” to move to the preceding child.
The above also applies to I_INPUT and I_INPFLD, except that
- these include input fields in the tabbing order.
- signaling “C_NEXT” or “C_PREV” while in the tabbing order of the window itself (which includes buttons for the window and buttons for a tab set) does not move from field to field or button to button. Instead, these menu entries are returned to C_PROCESS. To move from among fields and buttons in the window (and tab set if applicable), use the I_NEXTCTL and I_PREVCTL menu entries.
In any case, users will not be able to tab out of a window or list if the child processing method changes the value of g_entnamtab or g_entnamstab (defined in tkctl.def) and if the ActiveX control for a child ActiveX window processes Tab and Shift+Tab as an accelerator without returning it as a keystroke (see ActiveX controls and tabbing below).
Additionally, Toolkit includes several routines that enable you to control the context of a child window or list. All the following except I_NEXT and L_NEXT determine context based on the reason the child received focus. This enables child processing methods (the default methods and any methods you create) to provide an interface where users can smoothly tab through and click into composite windows.
- To control context for a child composite window, use the DC_CTRCONTEXT subfunction for %C_CONTAINER.
- To control context for a window or list on a child tab set, use the DTS_CTRCONTEXT subfunction for %TS_TABSET.
- To control context for a child input window, use I_NEXT or I_CTRCONTEXT.
- To control context for a child list, use L_NEXT or L_CTRCONTEXT.
- To control context for other types of windows, use U_CTRCONTEXT.
Note that when processing tab sets, Ctrl+Tab and Ctrl+Shift+Tab apply to the innermost tab set. For example, if a tab set contains a composite window, and that composite window in turn contains a tab set, these key combinations apply to the inner tab set when the inner tab is being processed. When Toolkit is processing any other window in the composite window on the tab, they apply to the outer tab set. See %TS_TABSET for more information on navigating tab sets.
For Windows, note the following:
- If an enabled tab on an inactive child window is clicked, Toolkit activates the child window and moves focus to the tab (instead of signaling the TS_TABn menu entry). It then signals the “C_RESET” menu entry (see the C_PROCESS Discussion).
- If a disabled tab on an inactive child window is clicked, Toolkit activates the child window, moves focus to the default tab, and then signals the “C_RESET” menu entry (instead of signaling the TS_TABn menu entry).
- For selection windows that are part of a composite window, Tab and Shift+Tab move focus to the next or previous child (unless Tab or Shift+Tab is a shortcut on a placed menu column). Tab instructs S_SELECT to signal “C_NEXT”, and Shift+Tab instructs S_SELECT to signal “C_PREV”.
There are a couple of situations in which tabbing may not automatically work seamlessly when a composite window includes an ActiveX control. The mechanisms for working around these issues are illustrated in ActiveX_Child_Tabbing, available from Synergy CodeExchange in the Synergex Resource Center.
- If the ActiveX control uses Tab or Shift+Tab internally (i.e., processes them as accelerators), users might not be able to tab out of the child unless you create a mechanism for this. To do this, code the control to invoke a method that signals the “C_NEXT” or “C_PREV” menu entry at the point a user would tab out of the control. (For information on “C_NEXT” and “C_PREV,” see the C_PROCESS Discussion.) Note that often, however, this isn’t necessary. If a control uses Tab and Shift+Tab internally, but returns them as unprocessed when it reaches the end of its own tabbing order, Tab and Shift+Tab do tab out of the child. This is usually how controls created with VB6 work.
- If the ActiveX control consists of more than one control and has its own tabbing order, focus within the ActiveX control might not be where you want it when the user tabs to the ActiveX control. For example, if the ActiveX control consists of two edit controls, focus within the ActiveX control will generally be on the edit control that last had focus. To work around this, create a method for the child (a composite window processing method) that in turn uses a method in the ActiveX control to set focus.
Buttons in composite windows
To add buttons to a composite window, you can use B_BUTTON to add buttons to the container, and you can use B_BUTTON or L_BUTTON to add buttons to a child window or list. Note the following:
- Buttons on a composite window are enabled when the composite window is being processed and disabled when it is not. This is true even of buttons in child windows and lists that are not active. See the .BUTTON Discussion for details.
- You can create a panel of buttons that can be placed anywhere in the client area of a composite window. To do this, create an input window with buttons but with an empty input set (see .SET or IB_SET). You can then place the input window wherever you want the button panel to appear in the composite window’s client area.
The default button
The following rules determine which button is the default for a composite window as a whole. Note that for usability and aesthetics, we recommend that you specify only one default button anywhere within a composite window — in other words, only one child window or list should have a default button (unless a default button is set for the composite container window). This prevents Toolkit from switching the default as context changes.
1. | If the window being processed specifies a default button, and that button is enabled, that button becomes the default button for the composite window. |
2. | Otherwise, if the container for the active window specifies an enabled default button, that button becomes the default. If the container doesn’t specify an enabled default button, but is itself contained, the default button for its container shall be the default, and so forth. |
3. | If no default button can be determined by the above, the default for another descendant window of a common container may become the default for the composite window. This, of course, will only come into play if there are several layers of containment (composite windows within composite windows). In this case, Toolkit starts with the innermost child and, if it is unable to establish a default from that, moves to its container, its container’s container, and so forth. |
4. | If none of the above results in a default (i.e., if no default button is specified on any window with a common ancestor), the first button for the composite window becomes the default. The first button is determined according to the same hierarchy discussed in the previous rules. If the active window has a button, the first button on that window will be the default. If the active window doesn’t have a button, the first button on the active window’s container will be the default. If the parent has no button, the first button on the grandparent becomes the default, and so on. If none of these have buttons, the first button on a window that shares a common ancestor to the current window will be the default. |
See the BUTTON_SET Discussion for more information on default buttons.
The following rules determine which button, if any, will be activated by an Alt+key combination.
For usability and aesthetics, we recommend that only one button for each Alt+key combination be specified anywhere within a container. This prevents Toolkit from switching key associations as context changes. |
1. | If the window being processed contains an enabled button that specifies the entered key combination, Toolkit activates that button. |
2. | Otherwise, if the container for the active window has an enabled button that specifies the entered combination, Toolkit activates that button. If the container doesn’t have an enabled button for the key combination, but is itself contained, Toolkit uses the enabled button for the key combination on the container’s container, and so forth. |
3. | If no button is activated as a result of the above, Toolkit may activate a button that specifies the key combination in another descendant window of a common container. In this case, Toolkit starts with the innermost child and, if no button specifies the key combination in that window, moves to its container, its container’s container, and so forth. |
Positioning composite windows, child windows, and lists
To position a composite window (as a whole), position the composite container window by using one of the following:
- D_PLACE operation for U_WINDOW when placing the container
- D_MOVE operation for U_WINDOW to move the container after it has been placed
To control the placement of a child window or list, use one of the following. In all cases, settings are relative to the client area of the immediate parent.
- The row and col arguments for DC_ADD (a subfunction for %C_CONTAINER) when adding a child to the composite window
- .PLACEMENT when defining a child window or the window for a child list. This will work only if you use the D_MOVE operation for U_WINDOW to place the child.
- L_CREATE when creating a child list. This will work only if the row and col arguments are omitted in the DC_ADD call and the row and column arguments are omitted when using L_PLACE to place the child.
- The D_PLACE operation for U_WINDOW or L_PLACE when placing a child
- The D_MOVE operation for U_WINDOW or the WP_POSITION function for W_PROC to move a child after it has been placed
Getting composite window information
The %C_CONTAINER function includes several subfunctions that enable you to get child and composite container window information. With %C_CONTAINER you can
- determine how many child windows and lists are associated with a composite container window (DC_NUMCHILD).
- determine if a child window or list is active (DC_CONTEXT).
- determine the tabbing index for a specified child window or list (DC_CHILDINDEX).
- determine if a child with a specified tabbing index exists, and/or get the ID and type of the child (DC_CHILD).
- determine if a window or list is a child (DC_CONTAINER, DC_CHILDINDEX).
- get the ID of the associated container (DC_CONTAINER).
- determine if focus is on a button for the container (not a button for a child window or list) and, if so, which button (DC_BTNFOCUS).
- get the address of a method routine for a child window/event pair (DC_GETEVENT).
Additionally, you can
- find out if a window is a composite container window by using the D_WINDOW_TYPE subfunction for %E_INFO.
- get the method set ID for a child by using the D_GETEVENTS subfunction for %U_WNDEVENTS or by using the D_LGETEVENTS flag for L_STATUS.
- get the address of a method routine for a given event in a method set by using the D_GETEVENTS subfunction for %U_WNDEVENTS.
- get the ID of the container window for a window or list that’s part of a composite window on a tab set by using the DTS_TABSET subfunction for %TS_TABSET.
Event methods
When a window or list is added to a composite window (even if it is part of tab set), its %UWNDEVENTS_METHOD set is replaced by one that handles mouse clicks in composite windows. The previously registered method set, however, is registered as a set of extensions to the new method set if there is a difference between the two. You can use the DC_EVENT subfunction for %C_CONTAINER to replace these extensions. (See the DC_EVENT Discussion for information on the default behavior and how to replace it.)
To determine if a method set has been registered for a UI Toolkit window and, if it has, what the ID for the method is, use the D_GETEVENTS subfunction for %U_WNDEVENTS.
To retrieve the address of a method routine for the specified event in the specified method set, use the DC_GETEVENT subfunction for %U_WNDEVENTS.
When a child is removed from its container, the %UWNDEVENTS_METHOD set for the child will remain as it was before the DC_REMOVE call, but the click event will no longer alter the container’s context.