Navigation Changes
The DataFlex keyboard navigation logic has been overhauled. A number of the inconsistencies that occurred when moving from object to object with a keyboard have been analyzed, restructured, and improved. In some cases, this involved fixing bugs. In other cases, the logic had to be changed by adding and changing interfaces. These changes should not require any changes in your applications – keyboard navigation in your applications should just start working better.
The major changes you will see are:
-
Tab Dialog keyboard navigation is vastly improved. The various navigation mode properties (
rotate_mode,skip_button_mode) now work as expected, making it easier to navigate into a tab page, out of a tab page, and between tab pages. New messages and properties have been added to allow more control over this process. -
Beginning and End of Panel logic has been changed. Previously, this logic would attempt to give the focus to the first (or last) DEO object, skipping focusable non-DEO objects. Now, the first (or last) focusable object gets the focus.
-
Forward and backwards keyboard navigation is more consistent. Prior to 8.2, keyboard navigation would be inconsistent when nested containers were encountered and when disabled objects and groups of objects were encountered. We now use better logic to handle these conditions.
To make this all work, we have made changes in the navigation program interface. These changes are described below. Most of the message changes were made in low-level messages that you are not likely to be calling or augmenting.
Next_object_id / Prior_object_id
The messages next_object_id and prior_object_id are used to determine what the next or prior object would be for keyboard navigation. They are called by the next and previous messages to change focus. You should also be able to call them to figure out where the focus should be. If a value is returned, that's the object to move to. If zero is returned, no focus change should take place.
Prior to VDF8.2, a return of zero sometimes meant "no focus change," and it sometimes meant "a focus change has taken place." This was a bad design. You would send a message to get the next object, and instead of getting an object, a focus event might take place. This occurred because these messages sent child_wrapping, and our packages used this to occasionally perform focus changes. Child_wrapping would change the focus and return 1, which caused the next_object_id and prior_object_id to return 0. You had no way to determine what that return of 0 actually meant.
In 8.2, we no longer use child_wrapping for this purpose (actually, we do not use it at all). Instead, we use a new method named OnChildWrapping. OnChildWrapping never changes the focus; it returns an object handle that indicates where the focus should go. The next_object_id / prior_object_id messages use this so they can determine where the next focus will be.
This means that next_object_id / prior_object_id will never change the focus anymore. They return a value that indicates where the focus ought to go. If 0 is returned, a focus change is not possible. Therefore, you can always safely call these methods without worrying that the focus might get changed without you knowing it.
Additional changes were made to let next_object_id / prior_object_id do a better job. If, while looking for the next object, you get into a ring you cannot get out of (usually all items in the ring are disabled), it will send the new function StuckInRing to the ring owner. This method will either jump out of the ring or return a 0, stopping navigation.
IMPORTANT NOTE: Child_wrapping is now obsolete. It still works, but you should use the new message OnChildWrapping.
OnChildWrapping
- Type: Event/Function
- Arguments:
handle hoDestinationboolean bDown- Returns:
handle
This message is sent any time a wrapping event occurs. A wrapping event occurs when navigation would "ring" either from the last object to the first object or from the first object to the last object. When this occurs, the OnChildWrapping event is sent to the owner of the ring (the container object that has ring_state set to true), passing the destination object and a boolean indicating if the ring is moving from last to first (bDown=True) or from first to last (bDown=False).
The function must return the object handle ID of the object to navigate to. Often, this will be hoDestination, the object that was passed. If zero is returned, the navigation event should not take place. By default, the runtime returns the passed object. In Flex code, it would look like this:
// Example Flex code
By default, this changes nothing. If you return a 0, navigation is stopped and no change takes place. If you return any other value, navigation will continue from that object. Tab dialogs use this extensively to handle all of the custom navigations required.
You should never send this message yourself. It may be augmented. If you do augment this function, never use it to change the focus.
This event is called whenever the old message child_wrapping is sent, but it is more flexible.
Changes in Beginning_of_Panel / End_of_Panel
Prior to VDF8.2, the standard behavior for moving to the beginning or end of a panel was to go to the first or last focusable DEO object. So, if your first object in a view was a button or a form, it would get skipped, and focus would move to the first DEO object, like a dbForm. This created much confusion and has been changed. Now, beginning_of_panel and end_of_panel move to the first or last focusable object.
In addition, the beginning_of_panel message will send a new event message, OnBeginningOfPanel, to all objects inside the panel. This notification can be used to perform custom beginning of panel initializations in objects. This is currently used by dbGrids and dbLists to make sure the first column of a grid is visible. It is used by tab dialogs to make sure that a default tab page is brought forward.
New UI_Object Messages
The following messages are added to the UI_Object class, which is the ancestor of all user interface objects.
OnBeginningOfPanel
- Type: Event
When a beginning_of_panel message is executed, the panel will notify all its objects by sending the message OnBeginningOfPanel. By default, this does nothing. DbGrids, dbLists, and Tab Dialogs use this message to perform special changes.
You can augment this method to perform any desired initialization required by a beginning of panel event.
ContainsFocus
- Type: Function
- Arguments: None
- Returns:
Boolean
This function returns true if the object or one of its descendants contains the focus.
This method is used by DataFlex to control navigation logic and will probably not be used by developers. If used, this would most often be sent to a container object to determine if the container contains the focus.
RingParent
- Type: Function
- Arguments: None
- Returns:
Handle
This function returns the object handle of the object that is its ring parent – this will be a parent or some ancestor whose ring_state is set to true. If 0 is returned, there is no ring parent.
This method is used by DataFlex to control navigation logic and will probably not be used by developers.