Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

2    Working with Forms and Dialogs

User Interface

Exploring Palm OS®

This chapter describes how to programmatically work with forms and dialogs. It covers:

Specifying Constraints
Displaying a Form
Initializing a Form
Setting a Form's Event Handler
Laying Out a Form or Dialog
Opening a Form
Drawing or Updating a Form
Closing a Form
Displaying Dialogs
Working With UI Elements

A form is a container for your application's user interface (see Figure 2.1). An application must have at least one form, and most applications have more than one.

Figure 2.1  Form

A dialog or modal dialog is a special type of form (see Figure 2.2). Dialogs look different from forms in that they are often shorter and use a different title bar; however, the main difference between a dialog and a form is in the way it handles events.

Figure 2.2  Modal dialog

In either case, the main purposes of a form or dialog are to:

  • Display other UI elements
  • Handle the events that an application receives

Specifying Constraints ^TOP^

As mentioned in "Display Layout", when you create a form resource, you should also specify a its constraints. Palm OS® uses the constraints to determine how to create and size windows for your form. Because the way you write code for a form depends significantly on how you set its constraints, this section provides guidelines for setting the constraints resource. The rest of this chapter focuses on how to write code to work with forms.

See your resource editor's documentation for specific information on how to set the constraints resource and all other resources.

The resource file that defines your form should contain a WINDOW_CONSTRAINTS_RESOURCE with the same ID as the form or dialog. See Listing 2.1.

Listing 2.1  Sample constraints resource


<WINDOW_CONSTRAINTS_RESOURCE RESOURCE_ID="1800"> 
   <VERSION> 1 </VERSION> 
   <WINDOW_CREATE_FLAGS> 0x00000004 </WINDOW_CREATE_FLAGS> 
   <WINDOW_X_POS> 0 </WINDOW_X_POS> 
   <WINDOW_Y_POS> 0 </WINDOW_Y_POS> 
   <WINDOW_WIDTH_MIN> 160 </WINDOW_WIDTH_MIN> 
   <WINDOW_WIDTH_MAX> 0x7fff </WINDOW_WIDTH_MAX> 
   <WINDOW_WIDTH_PREF> 160 </WINDOW_WIDTH_PREF> 
   <WINDOW_HEIGHT_MIN> 160 </WINDOW_HEIGHT_MIN> 
   <WINDOW_HEIGHT_MAX> 0x7fff </WINDOW_HEIGHT_MAX> 
   <WINDOW_HEIGHT_PREF> 240 </WINDOW_HEIGHT_PREF> 
</WINDOW_CONSTRAINTS_RESOURCE> 

The constraints resource contains two types of information: the window type (which includes the layer) and the size constraints.

Window Type ^TOP^

The WINDOW_CREATE_FLAGS attribute is a bit mask that contains the window type and the window layer. The WinFlagsType enum specifies the values that this attribute can have. In most cases, you'll only need to set one flag:

  • The back-buffer flag 0x00000004

You don't need to set the modal flag in a constraints resource. The window's modal flag is set according to the form resource's modal flag. You set the window layer only very rarely. All application windows appear in the normal layer, which is the default layer. If you're creating a resource for a pinlet, the window layer in the WINDOW_CREATE_FLAGS attribute is ignored. The system automatically displays the pinlet's window in the system layer.

The back-buffer flag controls which type of window is created for the form. The window type is important because it controls whether the form follows the Palm OS Cobalt rules for drawing or the legacy rules. In Palm OS Cobalt, drawing is not done directly to the screen as it was in previous releases. Instead, you draw to a graphics context, which is described in more detail in Chapter 8, "Drawing." The window type determines when this graphics context is available.

Palm OS Cobalt supports three window types:

  • Legacy windows. Legacy windows are used for forms that do not have a constraints resource. They work just like windows on previous Palm OS releases. Because they do not have constraints, they do not resize or move, and users cannot open or close the dynamic input area when a legacy window is displayed.

    Legacy windows only exist if an application's resources aren't changed when it is ported to Palm OS Cobalt. Because the dynamic input area is disabled when legacy windows are displayed, PalmSource does not recommend that you leave your application in this state.

  • Update-based windows. Update-based windows are created for forms that use the default window creation flags (specifically, that don't set the back-buffer bit). Update-based windows follow the Palm OS Cobalt drawing rules.

    For update-based windows, the graphics context is only available at certain times. The system notifies you that the graphics context is available by sending the frmUpdateEvent. Therefore, if you use update-based windows, you must only draw in response to this event.


IMPORTANT: A pinlet or any form displayed outside of the application process must use an update-based window.
  • Transitional windows. Transitional windows are created if you set the back-buffer bit in WINDOW_CREATE_FLAGS. The graphics context is created once and is then stored in the back buffer shared between the application process and the system process. (The system process is where rendering is performed). For a transitional window, you must wait to draw until you receive the first winResizedEvent, which specifies the size of the form. After that, you may draw at any time.

You'll probably want to use transitional windows for ported applications, at least until you've discovered all of the places where your application draws to the screen.

For new applications, you may choose to use either transitional windows or update-based windows. Transitional windows are more efficient when rendering is done entirely in software, but update-based windows are more efficient on devices that use graphics accelerators. Note that the back buffer is allocated once and shared by all applications, so there is no size penalty for using transitional windows.

Regardless of which style of window you use, it's a good idea to try to follow the drawing rules used for update-based windows.

Size Constraints ^TOP^

Follow these guidelines when setting the size constraints:

  • Set WINDOW_X_POS and WINDOW_Y_POS to the location of the top-left corner of the window. In most cases, you can use winUndefConstraint (-32768), which has the system place the window for you. This is particularly important for modal dialogs that are shorter than the available space.
  • The constraints you specify should be the ideal size without taking into consideration the device size.
    • For the minimum, specify the minimum space that is realistically necessary to use the form and read its contents. Keep in mind that some pinlets, such as Asian handwriting recognition pinlets, may need more space than the Latin-based ones do. It is strongly suggested that you set the minimum height to between 100 and 160 coordinates.
    • For the preferred size, specify the amount of space needed to read the entire contents of the form. For example, if the main element on the form is a list that always contains ten items, the preferred size would be to show all ten items. For a form whose main element is a large text field, the preferred size would be large enough for the text field to show its maximum number of characters.

      Of course, the amount of space needed to display the entire contents of the form may not be known until runtime. If so, you can either set the preferred size to the constant winMaxConstraint (32767), which means make the window as large as possible, or set new size constraints at runtime using WinSetConstraints().

    • The maximum size is often the same as the preferred size. If the form does not contain any variable length items such as tables or editable text fields, the maximum size should be the size required to read the entire contents of the form. If the form contains variable length items, set the maximum to the constant winMaxConstraint (32767) because the user can use that extra space for editing.
    • A dialog is often already shorter than the available space, so the minimum, maximum, and preferred sizes are often the same.

Resizing only happens on devices that use a dynamic input area. Devices that use a static input area do not resize application windows.

Displaying a Form ^TOP^

Displaying a form involves several steps:

  1. Call FrmGotoForm(). Pass it the resource ID of the form to be loaded and a pointer to the database that contains the resource. For example:

    if ((error = SysGetModuleDatabase(SysGetRefNum(), &gDbID,  
       &gDbP)) < errNone) 
       return error; 
    FrmGotoForm(gDbP, MainForm); 
    

    This function adds a frmLoadEvent to the event queue followed by a frmOpenEvent.

  2. Respond to the frmLoadEvent by initializing the form and settings its event handler. See "Initializing a Form" and "Setting a Form's Event Handler."
  3. Respond to the winResizedEvent by rearranging the elements on the form. See "Laying Out a Form or Dialog."
  4. Respond to the frmOpenEvent by performing any initialization that must occur before the form is displayed. See "Opening a Form."
  5. Respond to the frmUpdateEvent by drawing the form. See "Drawing or Updating a Form."

Steps 3 through 5 may not require you to write any code. The default behavior is sufficient for many forms.

Displaying a dialog is very different from displaying a form. See "Displaying Dialogs."

Initializing a Form ^TOP^

Initialize the form in response to a frmLoadEvent by doing the following:

These steps are typically performed in the AppHandleEvent() function as shown in Listing 2.2.

Listing 2.2  Initializing a form


static Boolean AppHandleEvent(EventType *eventP) 
{ 
   uint16_t formId; 
   FormType *frmP; 
 
   if (eventP->eType == frmLoadEvent) { 
      formId = eventP->data.frmLoad.formID; 
      frmP = FrmInitForm(gDbID, formId); 
      FrmSetActiveForm(frmP); 
      ... 
      return true; 
   } 
   return false; 
} 

The FrmInitForm() function initializes the internal FormType data structure for the form. This data structure is passed to all form functions.

The FrmSetActiveForm() function requests that the form be granted input focus. The request is granted asynchronously and can fail if a process with a window in a higher layer requests the input focus at the same time. If the form receives the input focus as a result of this request when initializing a form, it receives these events:

See "Input Focus" for more information about these events.

Setting a Form's Event Handler ^TOP^

You typically assign a custom event handler to a form in response to the frmLoadEvent. As you learned in Exploring Palm OS: Programming Basics, the standard application event loop calls the system function FrmDispatchEvent() to send events to a form. FrmDispatchEvent() does the following:

  • If your form has an event handler set, it calls that event handler.
  • If your form's event handler returns false, it then calls FrmHandleEvent() to let the built-in code provide default behavior.

To set the form's event handler, call FrmSetEventHandler() in the AppHandleEvent() function as shown in Listing 2.3.

Listing 2.3  Setting the event handler


static Boolean AppHandleEvent(EventPtr eventP) 
{ 
   uint16_t formId; 
   FormType *frmP; 
 
   if (eventP->eType == frmLoadEvent) { 
      ... 
      case MainForm: 
         FrmSetEventHandler(frmP, MainFormHandleEvent); 
         break; 
      case EditForm: 
         FrmSetEventHandler(frmP, EditFormHandleEvent); 
         break; 
      // one case statement per form typically. 
      return true; 
   } 
   return false; 
} 

Laying Out a Form or Dialog ^TOP^

Before your form is first displayed or when the user performs an action that causes the form to be resized, the application receives a winResizedEvent. The event structure contains the bounds into which the form should be drawn.

If the form is a dialog that remains a constant size, the system takes care of the winResizedEvent for you as long as you have specified winUndefConstraint for the position of the dialog. The system moves the dialog so that it is always at the bottom of the area allocated to the application.

For forms and full-screen dialogs, the system can take care of much of the resizing for you if you set up the form properly, but you may need to adjust it afterwards. To set up the form for automatic resizing, create an array of FormLayoutType structures that defines a layout rule for each element within the form. Pass the array to FrmInitLayout() in response to the frmLoadEvent. See Listing 2.4.


IMPORTANT: The FormLayoutType structure must remain in memory for the life of the form.

Listing 2.4  Automatic form layout


struct FormLayoutType editFormLayout[] = { 
   { sizeof(FormLayoutType), EditFormSelectorTrigger, 0,  
      frmFollowTop }, 
   { sizeof(FormLayoutType), EditFormTextField, 0,  
      frmFollowTopBottom }, 
   { sizeof(FormLayoutType), EditFormScrollBar, 0,  
      frmFollowTopBottom }, 
   { sizeof(FormLayoutType), EditFormDoneButton, 0,  
      frmFollowBottom }, 
   { sizeof(FormLayoutType), EditFormDetailsButton, 0,  
      frmFollowBottom }, 
 
   // NOTE: Don't forget the shift indicator! Use the special 
   // value frmLayoutGraffitiShiftID as its ID.  
   { sizeof(FormLayoutType), frmLayoutGraffitiShiftID, 0,  
      frmFollowBottom }, 
   { 0, 0, 0, 0 } // always end the array with a 0 element.  
}; 
 
if (eventP->eType == frmLoadEvent) { 
   formId = eventP->data.frmLoad.formID; 
   frmP = FrmInitForm(gDbID, formId); 
   FrmSetActiveForm(frmP); 
   switch (formId) { 
      case EditForm: 
         FrmSetEventHandler(frmP, EditFormHandleEvent); 
         FrmInitLayout(frmP, editFormLayout); 
         break; 
   return true; 
} 

The layout rule specifies which sides of the form the element is anchored to. If the element is anchored to one side, it moves. If the element is anchored to opposite sides, it resizes. For example, frmFollowBottom specifies that the element is always at a fixed location relative to the bottom of the form. If the form's bottom changes (for example, if the form resizes because the user closes the dynamic input area), the element moves. If the element is anchored to both the top and bottom with frmFollowTopBottom, the element resizes. Similarly, if you anchor an element to the left or right side of the form with frmFollowLeft or frmFollowRight, the element moves if the form is resized horizontally. If you use frmFollowLeftRight, the element resizes with the form. Note that the horizontal resizing rule is independent of the vertical resizing rule. For example, you can have a command button use the following rule:


frmFollowBottom | frmFollowLeftRight 

Such a button resizes horizontally but moves if the form is resized vertically.

Figure 2.3 shows how Listing 2.4 resizes its form.

Figure 2.3  Resizing a form

For some forms, you do not need to respond to winResizedEvent at all if you call FrmInitLayout(). FrmHandleEvent() by default calls FrmPerformLayout(), which applies the layout rules to all of the elements in the form. However, FrmPerformLayout() only changes an element's bounds. Specifically, you will need to handle the following:

  • Scroll bars attached to tables or fields. You need to call SclSetScrollBar() with the new scroll values for the field or table so that the scroll bar automatically appears or disappears as necessary.
  • Tables. You may want to increase the number of rows a table displays.
  • In any element that scrolls (such as a field, a list, or a table), you may need to make sure that the current selection is still visible when the form shrinks.

In each of these cases, call FrmPerformLayout() first, then adjust the elements, and then return true to specify that you've handled the event. Listing 2.5 shows an example of adjusting the number of table rows in response to winResizedEvent.

Listing 2.5  Responding to winResizedEvent


FormType *frmP; 
WinHandle winH; 
TableType *tableP; 
int16_t rowHeight; 
int16_t numRows; 
RectangleType *tableBounds; 
 
case winResizedEvent: 
 
   frmP = FrmGetActiveForm(); 
   winH = FrmGetWindowHandle( frmP ); 
   if( winH == eventP->data.winResized.window ) 
   { 
      FrmPerformLayout(frmP,  
      &(eventP->data.winResized.newBounds)); 
 
      // Fix up the resized table 
      tableP = FrmGetObjectPtr(frmP,  
         FrmGetObjectIndex(frmP, MyTable)); 
      TblGetBounds(tableP, &tableBounds); 
      rowHeight = TblGetRowHeight(tableP, 0); 
      numRows = bounds.extent.y / rowHeight; 
 
      // keep track of visible rows in a global 
      gNumRowsVisibleInTable = numRows; 
      // Load records into the table 
      LoadRecordsIntoTable(); 
 
      handled = true; 
   } 
   break; 

If you ever need to reposition an element on the form using FrmSetObjectBounds(), you cannot use FrmInitLayout() or FrmPerformLayout() to resize or reposition that element. Instead, you must leave it out of the array that you pass to FrmInitLayout(), respond to winResizedEvent as shown above, and manually reposition and resize that element.

Opening a Form ^TOP^

When the system is ready to open a form, it sends the frmOpenEvent. This is a request for the application to perform any initialization that needs to happen before a form is displayed. You might, for example, retrieve a database record that the form should display or set the initial selection for a group of push buttons on the form.


IMPORTANT: Do not call FrmDrawForm() in response to frmOpenEvent.

Drawing or Updating a Form ^TOP^

Palm OS Cobalt sends a frmUpdateEvent when it is ready for the application to draw. This event contains a rectangle specifying the region that needs to be drawn. All drawing is clipped to this rectangle.

Most applications do not need to respond to frmUpdateEvent. The default event handler redraws all user interface elements that a form contains. You only need to respond to this event if you are performing custom drawing. Respond by doing the following:

  1. Call FrmDrawForm().
  2. Draw using the functions described in Chapter 8, "Drawing."

Note that because the clipping rectangle is set to the invalidated region when frmUpdateEvent is sent, FrmDrawForm() only redraws what is necessary to redraw.

If you are performing custom drawing, always respond to frmUpdateEvent for any type of window because a window from another process can obscure and then expose your window at any time. The frmUpdateEvent is the signal that your window needs to be redrawn.

Invalidating a Form ^TOP^

If you want to refresh the contents of the form, do not directly redraw. Instead, invalidate the form by calling WinInvalidateWindow() or WinInvalidateRect(). These two functions mark all or a portion of the form as dirty. The next time drawing occurs, the form receives the frmUpdateEvent specifying the area that needs to be redrawn.

As an optimization, you can use WinInvalidateRectFunc() instead. This function takes a pointer to a callback function, which the system calls instead of sending a frmUpdateEvent.

It is not an error to directly redraw into a transitional or legacy window. It is an error to directly redraw into an update-based window. Because the update-based window does not have a graphics context until it receives a frmUpdateEvent, redrawing directly into an update-based windows causes an application crash.

Closing a Form ^TOP^

A form is closed by one of two functions:

Respond to frmCloseEvent by undoing anything you did in response to frmOpenEvent. Deallocate memory you allocated, write changes to a database record to the database, and so on.


TIP: Return false from your frmCloseEvent handler to make sure that the default event handler deletes the form.

Displaying Dialogs ^TOP^

Displaying a dialog is different from displaying a form. Depending on the dialog, it can be much simpler than displaying a form. Specifically, if you display an alert, a progress dialog, or a tips dialog, there are special functions that you may use for convenience.

This section first describes how to display modal dialogs in general and then describes how to display special types of dialogs.

Displaying Any Modal Dialog ^TOP^

A dialog displays on top of another form. You do not call FrmGotoForm() to display a dialog because FrmGotoForm() closes the current form before it opens the new one. Instead, you should call FrmDoDialog() to display a dialog.

The differences between displaying a form and displaying a dialog are:

  • You do not receive frmLoadEvent or frmOpenEvent if you call FrmDoDialog(). Perform any necessary initialization, including calling FrmInitForm() and setting the form's event handler, before you call that function. FrmDoDialog() calls FrmSetActiveForm() for you.
  • You also do not receive the frmCloseEvent. Instead, FrmDoDialog() returns when the dialog is closed. The return value is the value of the button that was tapped to dismiss the dialog. Therefore, you must call FrmDeleteForm() when you are done displaying the dialog.
  • FrmDoDialog() runs its own event loop. You can still set an event handler for the dialog, and FrmDoDialog() will call it before calling FrmHandleEvent().

Listing 2.6  Using FrmDoDialog


frmP = FrmInitForm(gDbP, MyDialogForm); 
FrmSetEventHandler(frmP, MyDialogEventHandler); 
/* initialize the dialog's controls here if necessary */ 
 
/* open the dialog, and wait until a button is pressed to 
close it. */ 
whichButton = FrmDoDialog(frmP); 
 
if (whichButton == DetailsOKButton) { 
  /* get data from controls on the form to save/apply changes  
  */ 
} 
FrmDeleteForm(frmP); 

If you need more control over the dialog and how it behaves, call FrmPopupForm() instead of FrmDoDialog(). FrmPopupForm() is like FrmGotoForm() except that it does not close the current form. Your dialog will receive the frmLoadEvent and frmOpenEvent and you should otherwise follow the instructions in "Displaying a Form." When your dialog should be dismissed, perform any necessary cleanup and then call FrmReturnToForm() with a form ID of 0 to return to the previously displayed form.

Alert Dialogs ^TOP^

An alert dialog is a modal dialog that is primarily informational (see Figure 2.4). It might prompt the user to confirm an action, or it might display an error message.

Figure 2.4  Alert dialog

The following functions display alerts:

  • FrmAlert() displays a simple alert that contains a message string and has several command buttons.
  • FrmCustomAlert() displays an alert with a customized string. You create the string using the strings ^1, ^2, and ^3 as placeholders. When you call FrmCustomAlert(), you pass strings to substitute for those placeholders.
  • FrmCustomResponseAlert() displays an alert with a text field as well as command buttons. You pass a pointer to a callback function that verifies the text the user enters in the text field. This is typically used for password dialogs.

All of these functions return the item number of the button that the user tapped to dismiss the alert. Buttons are numbered from left to right starting with button 0. The functions handle all steps for you, from calling FrmInitForm() to calling FrmDeleteForm().

Help Dialogs ^TOP^

A help or tips dialog displays when the user taps the "i" button on a modal dialog. You typically only provide a string resource containing the help text and the system handles displaying the dialog for you.

If you want to display a help dialog through some other means, use the FrmHelp() function. As with the alert functions, FrmHelp() initializes the form for you before it opens and deletes it when the user closes it. You only need to make the one function call.

Progress Dialogs ^TOP^

If your application performs a lengthy process, such as data transfer during a communications session, consider displaying a progress dialog to inform the user of the status of the process. The Progress Manager provides the mechanism to display progress dialogs.

You display a progress dialog by calling PrgStartDialog(). Then, as your process progresses, you call PrgUpdateDialog() to update the dialog with new information for the user. In your event loop you call PrgHandleEvent() to handle the progress dialog update events queued by PrgUpdateDialog(). The PrgHandleEvent() function makes a callback to a PrgCallbackFunc() function that you supply to get the latest progress information.

Note that whatever operation you are doing that is the lengthy process, you do the work either inside your normal event loop or in a separate thread, not in the callback function. That is, you call EvtGetEvent() and do work when you get a nilEvent. Each time you get a nilEvent, do a chunk of work, but be sure to continue to call EvtGetEvent() frequently (like every half second), so that pen taps and other events get noticed quickly enough.

The dialog can display a few lines of text that are automatically centered and formatted. You can also specify an icon that identifies the operation in progress. The dialog has two optional buttons:

  • A Cancel or OK button. The label of this button is automatically controlled by the Progress Manager and depends on the current progress state (no error, error, or user canceled operation).
  • An optional skip button. You can enable the use of a skip button by setting a flag in the structure returned by PrgCallbackFunc(). If the user taps the skip button, you should skip the current stage of the process and move on to the next stage. Of course, you only enable the button if stages in the process can be skipped.

Progress Callback Function

When you want to update the progress dialog with new information, you call the function PrgUpdateDialog(). To get the current progress information to display in the progress dialog, PrgHandleEvent() calls a function that you supplied in your call to PrgStartDialog().

The system passes the callback function one parameter, a pointer to a PrgCallbackData structure. To learn what type of information is passed in this structure, see Chapter 26, "Progress Manager Reference."

Your callback function should return a Boolean. Return true if the progress dialog should be updated using the values you specified in the PrgCallbackData structure. If you specify false, the dialog is still updated, but with default status messages. (Returning false is not recommended.)

In the callback function, you should set the value of the PrgCallbackData's textP buffer to the string you want to display in the progress dialog when it is updated. You can use the value in the stage field to look up a message in a string resource. You also might want to append the text in the message field to your base string. Typically, the message field would contain more dynamic information that depends on a user selection, such as a phone number, device name, or network identifier, etc.

For example, the PrgUpdateDialog() function might have been called with a stage of 1 and a messageP parameter value of a phone number string, "555-1212". Based on the stage, you might find the string "Dialing" in a string resource, and append the phone number, to form the final text "Dialing 555-1212" that you place in the text buffer textP.

Keeping the static strings corresponding to various stages in a resource makes it easier to localize your application. More dynamic information can be passed in using the messageP parameter to PrgUpdateDialog().


NOTE: The callback function is called only if the parameters passed to PrgUpdateDialog() have changed from the last time it was called. If PrgUpdateDialog() is called twice with exactly the same parameters, the callback function is called only once.

Working With UI Elements ^TOP^

One of the main purposes of a form is to display other user interface elements (sometimes called objects) such as command buttons, text fields, and tables. Specifics on programmatically working with each type of element are given in most of the remaining chapters of this book.

You use the form to gain initial access to an element on that form. You also use form functions to control when the element is displayed. Both of these topics are described in this section.

Accessing an Element ^TOP^

The elements that a form contains are accessed through the form. Functions tend to reference a UI element in one of the following ways:

  • By the resource ID that you specified when you created the element in a resource file.
  • By the element's index, which is the index into the form's internal list of elements. If you have a resource ID, you can obtain the element's index using the function FrmGetObjectIndex().
  • By a pointer to the element's internal data structure. If you have an element's index, you can obtain its pointer using the function FrmGetObjectPtr().

Most functions use the pointer reference to an element. For this reason, many applications define a function similar to the one shown in Listing 2.7.

Listing 2.7  Obtaining a pointer to a UI element


void *GetObjectPtr(uint16_t objectID) 
{ 
  FormType *frmP; 
 
  frmP = FrmGetActiveForm(); 
  return FrmGetObjectPtr(frmP,  
      FrmGetObjectIndex(frmP, objectID)); 
} 
 
void SomeOtherFunction(void)  
{ 
  ControlType *controlP =  
    (ControlType *)GetObjectPtr(MainPopupTrigger); 
  ListType *listP = (ListType *)GetObjectPtr(MainPopupList); 
  ... 
} 

Dynamically Displaying Elements ^TOP^

If you need to have a user interface element appear or disappear at runtime, do the following:

  1. When creating your resource file, define all of the elements that may conceivably be displayed on the form at any time during the life of the application.
  2. If an element should not be displayed when the form is first displayed, set its usable attribute to false in the resource file.
  3. When an event occurs that should change the form's display, use FrmShowObject() to show any previously undisplayed element. Use FrmHideObject() to remove any element that should no longer be displayed.

For update-based windows, both FrmShowObject() and FrmHideObject() invalidate the area of the form where that element appears. That is, they asynchronously trigger a frmUpdateEvent. When your application receives the frmUpdateEvent, the new show and hide settings take effect. For all other windows, FrmHideObject() and FrmShowObject() are immediate.


TIP: In Palm OS Cobalt, hiding an element means filling its region with the color set for UIFormFill. This is true regardless of the type of window used for the form (legacy, transitional, or update-based).

Some applications must create their displays at runtime. These applications can use the dynamic UI APIs:

You can use the FrmNewForm() function to create new forms dynamically. Palm's UI guidelines encourage you to keep modal dialogs at the bottom of the screen, using the entire screen width. This isn't enforced by the function, but is strongly encouraged in order to maintain a look and feel that is consistent with the built-in applications.

The FrmNewLabel(), FrmNewBitmap(), FrmNewGadget(), LstNewList(), FldNewField() and CtlNewControl() functions can be used to create new elements on forms.

It is fine to add new items to an active form, but doing so is very likely to move the form structure in memory; therefore, any pointers to the form or to controls on the form might change. Make sure to update any variables or pointers that you are using so that they refer to the form's new memory location, which is returned when you create the element.

If you're not absolutely sure that you need to change your UI dynamically, don't do it—unexpected changes to an application's interface are likely to confuse or frustrate the end user.

The FrmRemoveObject() function removes a dynamically created element from a form. This function doesn't free memory referenced by the element (if any) but it does shrink the form chunk. For best efficiency when removing items from forms, remove items in order of decreasing index values, beginning with the item having the highest index value. When removing items from a form, you need to be mindful of the same concerns as when adding items: the form pointer and pointers to controls on the form may change as a result of any call that moves the form structure in memory.

When creating forms dynamically, or just to make your application more robust, use the FrmValidatePtr() function to ensure that your form pointer is valid and the form it points to is valid. This routine can catch lots of bugs for you—use it!

Summary of Form and Dialog Functions ^TOP^

Form Functions

Loading a Form

FrmGotoForm()

FrmPopupForm()

Initialization

FrmInitForm()
FrmInitLayout()

FrmInitFormWithFlags()
FrmSetActiveForm()

Event Handling

FrmHandleEvent()
FrmDispatchEvent()

FrmSetEventHandler()

Displaying a Modal Dialog

FrmDoDialog()
FrmAlert()
FrmHelp()
FrmAlertWithFlags()
FrmNewGsi()
FrmCustomAlert()

FrmCustomAlertWithFlags()
FrmCustomResponseAlert()
FrmCustomResponseAlertWithFlags()
FrmHelpWithFlags()
FrmUIAlert()

Updating the Display

FrmDrawForm()
FrmShowObject()
FrmRemoveObject()
WinInvalidateRect()
WinInvalidateWindow()

FrmHideObject()
FrmUpdateScrollers()
FrmUpdateForm()
WinInvalidateRectFunc()

Form Attributes

FrmVisible()
FrmGetDefaultButtonID()
FrmSetDefaultButtonID()

FrmGetHelpID()
FrmSetHelpID()

Accessing a Form Programmatically

FrmGetActiveForm()
FrmGetFirstForm()
FrmGetFormPtr()
FrmValidatePtr()
ECFrmValidatePtr()

FrmGetActiveFormID()
FrmGetFormId()
FrmGetWindowHandle()
FrmGetFormWithWindow()

Obtaining Focus

FrmGetFocus()
FrmGetActiveField()

FrmSetFocus()

Accessing Objects Within a Form

FrmGetObjectId()
FrmGetObjectType()
FrmGetObjectPtr()
FrmGetObjectIndexFromPtr()
FrmGetObjectUsable()

FrmGetObjectIndex()
FrmGetObjectPosition()
FrmGetNumberOfObjects()
FrmGetObjectIdFromObjectPtr()

Title and Menu

FrmCopyTitle()
FrmPointInTitle()
FrmSetMenu()

FrmGetTitle()
FrmSetTitle()
FrmGetMenuBarID()

Labels

FrmCopyLabel()
FrmGetLabel()
FrmGetLabelFont()

FrmSetCategoryLabel()
FrmNewLabel()
FrmSetLabelFont()

Controls

FrmSetControlValue()
FrmGetControlValue()

FrmGetControlGroupSelection()
FrmSetControlGroupSelection()

Gadgets

FrmGetGadgetData()
FrmNewGadget()

FrmSetGadgetData()
FrmSetGadgetHandler()

Bitmaps

FrmNewBitmap()

Coordinates and Boundaries

FrmGetObjectBounds()
FrmSetObjectPosition()
FrmGetObjectInitialBounds()

FrmSetObjectBounds()
FrmGetFormBounds()
FrmGetFormInitialBounds()

Resizing a Form

FrmPerformLayout()

frmLayoutRule()

Removing a Form From the Display

FrmCloseAllForms()
FrmSaveAllForms()

FrmEraseForm()
FrmReturnToForm()

Releasing a Form's Memory

FrmDeleteForm()

Pen Tracking

FrmAmIPenTracking()

FrmSetPenTracking()

Dynamically Creating a Form

FrmNewForm()
FrmRestoreActiveState()

FrmNewFormWithConstraints()
FrmSaveActiveState()