Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Managing Widgets in Application Code

We recommend that you create your application's UI in PhAB — it's easier than doing it in your code. However, if the interface is dynamic, you'll probably have to create parts of it “on the fly.”

This chapter includes:

Creating widgets

Creating a widget in your application code is a bit more work than creating it in PhAB. That's because PhAB looks after a lot of the physical attributes for you, including size, location, and so on. If you create the widget in your code, you'll have to set these resources yourself.

To create a widget in your code, call PtCreateWidget(). The syntax is as follows:

PtWidget_t *PtCreateWidget(
                PtWidgetClassRef_t *class,
                PtWidget_t *parent,
                unsigned n_args,
                PtArg_t *args );

The arguments are:

class
The type of widget to create (e.g. PtButton)
parent
The parent of the new widget. If this is Pt_DEFAULT_PARENT, the new widget is made a child of the default parent, which is the most recently created container-class widget. If parent is Pt_NO_PARENT, the widget has no parent.
n_args
The number of elements in the args array.
args
An array of PtArg_t structures (see the Photon Library Reference) that store your settings for the widget's resources. These settings are like the ones used for PtSetResources(); see the Manipulating Resources in Application Code chapter.

You can specify the default parent (used if the parent argument to PtCreateWidget() is Pt_DEFAULT_PARENT) by calling PtSetParentWidget(). To assign a widget to a different container, call PtReparentWidget().

Here are a few things to note about widgets created in application code:

Ordering widgets

The order in which widgets are given focus depends on the order in which they were created or on the widget order specified in PhAB (see Ordering widgets in the Creating Widgets in PhAB chapter). The backmost widget is the first in the tab order; the frontmost widget is the last.

If you're creating widgets programmatically, you can create them in the order in which you want them to get focus, or you can use these functions to change the order:

PtWidgetInsert()
Insert a widget in the widget family hierarchy
PtWidgetToBack()
Move a widget behind all its brothers
PtWidgetToFront()
Move a widget in front of all its brothers

Alternatively, you can use a widget's Pt_CB_LOST_FOCUS callback (defined by PtBasic) to override the tab order by giving focus to another widget.

In the lost-focus callback, use PtContainerGiveFocus() to give focus to the desired widget, and return Pt_END from the callback to prevent focus from being given to the original target of the focus change.


Note: The Pt_CB_LOST_FOCUS callback is called a second time as focus is removed from the widget to go to the new target. To avoid an endless loop, use a static variable to indicate that this callback has already redirected focus.

Working in the widget family

The following functions can be used to work with the widget family hierarchy, and may be useful in setting the focus order:

PtChildType()
Determine the relationship between two widgets
PtFindDisjoint()
Return the nearest disjoint parent widget
PtFindFocusChild()
Find the closest focusable child widget
PtFindGuardian()
Find the widget responsible for another widget's actions
PtGetParent()
Find the nearest parent widget that matches the specified class
PtGetParentWidget()
Return the current default widget parent
PtNextTopLevelWidget()
Get a pointer to the next top-level widget
PtValidParent()
Identify a valid parent for a widget
PtWidgetBrotherBehind()
Get the brother behind a widget
PtWidgetBrotherInFront()
Get the brother in front of a widget
PtWidgetChildBack()
Get the child that's farthest back in a container
PtWidgetChildFront()
Get the child at the very front of a container
PtWidgetFamily()
Traverse the widget hierarchy from back to front
PtWidgetParent()
Get a widget's parent
PtWidgetSkip()
Skip to a widget in the next hierarchy
PtWidgetTree()
Walk the widget tree from front to back
PtWidgetTreeTraverse()
Walk the widget family hierarchy from front to back

Callbacks

You can add and remove callbacks in your code as well as from PhAB — just watch for differences between the two types!

Adding callbacks

An application registers callbacks by manipulating the widget's callback resources. The Photon widget classes employ a naming convention for these resources — they all begin with Pt_CB_.

Callbacks can be added to the callback list kept by these resources using PtAddCallbacks() to add several callback functions to the list or PtAddCallback() to add just one. In either case, the first two arguments to the function are the widget and the name of the callback resource to be augmented. The remaining arguments depend on which function is used.

The third argument to PtAddCallbacks() is an array of callback records. Each record contains a pointer to a callback function and the associated client data pointer that will be passed to the callback function when it's invoked. Each of these callback records is copied to the widget's internal callback list.

For example, we might want to have the application perform some action when the user selects (i.e. presses) a button. The PtButton widget class provides the Pt_CB_ACTIVATE callback resource for notifying the application when the button has been pressed. To create the widget and attach a callback function to this callback resource, we'd have to use code like this:

{
   PtWidget_t *button;
   int push_button_cb( PtWidget_t *, void *,
                       PtCallbackInfo_t *);
   PtCallback_t callbacks[] = { {push_button_cb, NULL} };

   ...

   button = PtCreateWidget(PtButton, window, 0, NULL);
   PtAddCallbacks(button, Pt_CB_ACTIVATE, callbacks, 1);
}

where push_button_cb is the name of the application function that would be called when the user presses the button. The PtCallback_t structure is used to define lists of callbacks; for details, see the Photon Widget Reference.

When adding only one callback function to the callback list (as in this case), it's simpler to use PtAddCallback(). This function takes the pointer to the callback function as the third argument, and the client data pointer as the final argument. The above code fragment could be written more concisely as:

{
   PtWidget_t *button;
   int push_button_cb( PtWidget_t *, void *,
                       PtCallbackInfo_t *);
   button = PtCreateWidget(PtButton, window, 0, NULL);
   PtAddCallback(button, Pt_CB_ACTIVATE, push_button_cb,
                 NULL);
}

You can also give an array of callback records as the value for the callback resource when using argument lists in conjunction with PtCreateWidget() or PtSetResources(). Since the callback list is an array, you should specify the array's base address as the third argument to PtSetArg(), and the number of elements as the final argument. In this case, the callback records are added to the current callback list, if there is one. This gives us another way to specify the callback for the above example:

{
   PtArg_t arg[5];
   int push_button_cb( PtWidget_t *, void *,
                       PtCallbackInfo_t *);
   PtCallback_t callbacks[] = { {push_button_cb, NULL} };
...
   PtSetArg(&args[0], Pt_CB_ACTIVATE, callbacks, 1);
   PtCreateWidget(PtButton, window, 1, arg);
}

Each of these methods has its advantages. PtAddCallback() is of course simple. PtAddCallbacks() is more efficient when there are several callbacks. Using PtSetArg() and passing the result to PtCreateWidget() allows the widget creation and callback list attachment to be performed atomically.

Callback invocation

When called, the callback function is invoked with the following parameters:

PtWidget_t *widget
The widget that caused the callback function to be called, i.e. the one on which the action took place.
void *client_data
Application-specific data that was associated with the callback when it was registered with the widget.

Note: The client data that's passed to a callback you add from your code isn't the same as the apinfo data passed to a PhAB callback.

PtCallbackInfo_t *call_data
A pointer to a PtCallbackInfo_t structure (see the Photon Widget Reference) that holds data specific to this invocation of the callback. It relates to the reason the callback was called and may have data specific to the callback's behavior.

The PtCallbackInfo_t structure is defined as:

typedef struct Pt_callback_info {
        unsigned long     reason;
        unsigned long     reason_subtype;
        PhEvent_t         *event;
        void              *cbdata;
} PtCallbackInfo_t;

The elements of PtCallbackInfo_t have the following meaning:

For more information, see the descriptions of the callbacks defined for each widget in the Widget Reference.

Removing callbacks

You can remove one or more callbacks from a callback list associated with a widget resource using the PtRemoveCallbacks() and PtRemoveCallback() functions.


Caution: Don't try to remove a callback that was added through PhAB; unexpected behavior may result.

PtRemoveCallbacks() takes an array of callback records as an argument and removes all the callbacks specified by it from the callback list. PtRemoveCallback() removes just one callback function from the callback list. Both functions take the widget as the first argument and the widget resource as the second argument.

To remove the callback from the button we've created above, we could do this:

int push_button_cb( PtWidget_t *, void *,
                    PtCallbackInfo_t *);
PtCallback_t callbacks[] = { {push_button_cb, NULL} };
PtRemoveCallbacks(button, Pt_CB_ACTIVATE, callbacks, 1);

or this:

int push_button_cb( PtWidget_t *, void *,
                    PtCallbackInfo_t *);
PtRemoveCallback(button, Pt_CB_ACTIVATE, push_button_cb,

Both the callback function pointer and the client data pointer are important when removing callbacks. Only the first element of the callback list that has both the same callback function and the same client data pointer will be removed from the callback list.

Examining callbacks

You can examine the callback list by getting the value of the appropriate callback list resource. The type of value you get from a callback list resource is different from the value used to set the resource. Although this resource is set with an array of callback records, the value obtained by getting the resource is a pointer to a list of callback records. The type of the list is PtCallbackList_t. Each element of the list contains a cb member (i.e. the callback record) and a next pointer (which points to the next element of the list).

The following example shows how you can traverse through the Pt_CB_ACTIVATE callback list for widget to find all instances of a particular callback function, cb:

...
PtCallbackList_t *cl;

PtGetResources(widget, Pt_CB_ACTIVATE, &cl, 0);
for ( ; cl; cl = cl->next )
{
    if ( cl->cb.func == cb )
        break;
}

Event handlers

You can add and remove event handlers (raw and filter callbacks) in your application code as well as in PhAB — however, there are some differences between the two types.


Note: For a description of raw and filter callbacks and how they're used, see Event handlers — raw and filter callbacks in the Events chapter.

For information on adding event handlers in PhAB, see Event handlers — raw and filter callbacks in the Editing Resources and Callbacks in PhAB chapter.


Adding event handlers

As with callbacks, you can also set or examine event handlers by performing a set or get directly on the event handler resource. The following resources of PtWidget let you specify handlers for Photon events:

For more information about these callback resources, see the Photon Widget Reference.

The set operation requires an array of event handler records of type PtRawCallback_t. These are similar to the callback records mentioned above, having event_mask, event_f, and data fields.

The event mask is a mask of Photon event types (see PhEvent_t in the Photon Library Reference) indicating which events will cause the callback function to be invoked. The event_f and data members are the event handler function and client data, respectively.


Note: If you add an event handler to a realized widget and the widget's region isn't sensitive to one or more of the events contained in the event mask, then the region is made sensitive to them.

If you add the event handler before realizing the widget, you have to adjust the region's sensitivity yourself after realizing the widget. See PhRegionChange() in the Photon Library Reference.


A get operation yields a PtRawCallbackList_t * list of event handler records. As with callback lists, the list contains two members: next and cb. The cb member is an event handler record.

You can add Pt_CB_RAW event handlers using either the PtAddEventHandler() or PtAddEventHandlers() function.

You can add Pt_CB_FILTER event handlers using either the PtAddFilterCallback() or PtAddFilterCallbacks() function.

The arguments to PtAddEventHandler() and PtAddFilterCallback() are:

widget
Widget to which the event handler should be added.
event_mask
Event mask specifying which events should cause the event handler to be called.
event_f
Event-handling function.
data
A pointer to pass to the event handler as client data.

The arguments to PtAddEventHandlers() and PtAddFilterCallbacks() are:

widget
Widget to which the event handlers should be added.
handlers
Array of event handler records.
nhandlers
Number of event handlers defined in the array.

Removing event handlers

You can remove Pt_CB_RAW event handlers by calling either PtRemoveEventHandler() or PtRemoveEventHandlers().

You can remove Pt_CB_FILTER event handlers by calling either PtRemoveFilterCallback() or PtRemoveFilterCallbacks()


Caution: Don't remove event handlers that were added through PhAB; unexpected behavior may result.

The parameters to PtRemoveEventHandler() and PtRemoveFilterCallback() are:

widget
Widget from which the event handler should be removed.
event_mask
Event mask specifying the events the handler is responsible for.
event_f
Event-handling function.
data
Client data associated with the handler.

This looks for an event handler with the same signature — i.e. the same event_mask, data and event_f — in the widget and removes one if it's found.

The parameters to PtRemoveEventHandlers() and PtRemoveFilterCallbacks() are:

widget
Widget from which the event handlers should be removed.
handlers
Array of event-handler records.
nhandlers
Number of event handlers defined in the array.

As with PtRemoveEventHandler() and PtRemoveFilterCallback(), an event handler is removed only if it has the exact same signature as one of the event handler specifications in the array of event handler records.

Event handler invocation

When invoked, event handlers receive the same arguments as callback functions, i.e. the parameters are:

Event handlers return an integer value that the event handler must use to indicate whether or not further processing should be performed on the event. If the event handler returns the value Pt_END, this indicates that no further processing is to be performed on the Photon event, and the event is consumed.

The event member of the info parameter contains a pointer to the event that caused the event handler to be invoked. You should check the type member of this event to determine how to deal with the event. It will be one of the event types specified in the event_mask given when the event handler was added to the widget.

To retrieve the data associated with the particular event, call the PhGetData() with the pointer to the event as a parameter. This will return a pointer to a structure with the data specific to that particular event type. This structure's type depends on the event type.

Widget styles

Widget class styles let you customize or modify a widget's appearance, size, and behavior at runtime. They also let multiple looks for a single type of widget exist at the same time. Essentially, a widget class style is a collection of methods and data that define the look and feel of instances of the widget class.

Each widget class has a default style, but you can add or modify an arbitrary number of additional styles at any time. You can even modify the default style for a class, changing the look and feel of any instances of that class that are using the default style.

Each instance of a widget can reference a specific style provided by its class. You can change the style that any widget is using whenever you want.

Each style has a set of members, including a name for the style and functions that replace or augment some of the widget class's methods. Methods are class-level functions that define how the widget initializes itself, draws itself, calculates its extent, and so on. For more information about methods, see the Building Custom Widgets guide.

The members of a style are identified by the following manifests:

Pt_STYLE_DRAW
The address of a function that's called whenever any widget that's using this style needs to draw itself.
Pt_STYLE_EXTENT or Pt_STYLE_SIZING
The address of a function that whenever a widget that's using this style is moved, resized, or modified in some fashion that may require the widget to move or resize (change in widget data). This function is responsible for setting the widget's dimension to the appropriate values.
Pt_STYLE_ACTIVATE
The address of a function that's called whenever a widget is created that defaults to this style, and whenever a widget's style is changed from some other style to this one. This function is the place to put manipulation of a widget's control surfaces, the addition of callbacks, or the setting of resources (to override the widget's defaults).
Pt_STYLE_CALC_BORDER
The address of a function that's responsible for reporting how much space is required to render the widget's edge decorations and margins.
Pt_STYLE_CALC_OPAQUE
The address of a function that's responsible for calculating the list of tiles that represents the opaque areas of a widget. This list is used to determine what needs to be damaged below this widget when it's modified.
Pt_STYLE_DEACTIVATE
The address of a function that's called whenever a widget using this style is either being destroyed or is switching to a different style.
Pt_STYLE_NAME
The name of the style.
Pt_STYLE_DATA
A pointer to an arbitrary data block for the style's use.

For details about the members, see PtSetStyleMember().

The following functions let you create and manipulate the widget class styles:

PtAddClassStyle()
Add a style to a widget class
PtCreateClassStyle()
Create a class style
PtDupClassStyle()
Get a copy of a widget class style
PtFindClassStyle()
Find the style with a given name
PtGetStyleMember()
Get a member of a style
PtGetWidgetStyle()
Get the style that a widget is currently using
PtSetClassStyleMethods()
Set multiple members of a style from an array
PtSetStyleMember()
Set a member of a style
PtSetStyleMembers()
Set multiple members of a style from a variable-length argument list
PtSetWidgetStyle()
Set the current style for a widget

Some of these functions require or return a pointer to a PtWidgetClassStyle_t structure. Don't access the members of this structure directly; call PtGetStyleMember() instead.


Note: You can also set the style for a widget instance by setting its Pt_ARG_STYLE resource (see PtBasic in the Widget Reference). Setting this resource has the same effect as calling PtSetWidgetStyle().

This example creates a style called blue and some buttons. Note that your widgets can use a style before you've added the style to the class or even before you've created the style. When you do create the style and add it to the class, any widgets that use the style are updated immediately.

#include <Pt.h>

PtWidget_t *win, *but;
PtWidgetClassStyle_t *b;

void blue_draw (PtWidget_t *widget, PhTile_t *damage)
{

    /* This is the drawing function for the blue style.
       It draws a blue rectangle (without a label) for
       the widget. */

    PgSetFillColor( Pg_BLUE);
    PgDrawRect( PtWidgetExtent (widget,NULL),
                Pg_DRAW_FILL);
}

int use_blue_style( PtWidget_t *widget, void *data,
                    PtCallbackInfo_t *cbinfo)
{
    /* This callback sets the current style for the given
       widget instance. If you haven't attached the blue
       style to the class, there shouldn't be any change
       in the widget's appearance. */

    PtSetWidgetStyle (widget, "blue");
    return Pt_CONTINUE;
}

int attach_blue_style( PtWidget_t *widget, void *data,
                       PtCallbackInfo_t *cbinfo)
{

    /* This callback adds the style to the widget class.
       If you've clicked on one of the "Use blue style"
       buttons, the style of all buttons should change. */

    PtAddClassStyle (PtButton, b);
    return Pt_CONTINUE;
}

int main()
{
    PhArea_t area = {{0,50},{100,100}};
    PtArg_t argt[10];
    PtStyleMethods_t meth;
    PtCallback_t cb = {use_blue_style, NULL};
    PtCallback_t cb2 = {attach_blue_style, NULL};
    int unsigned n;

    /* Initialize the methods for the style. */
    meth.method_index = Pt_STYLE_DRAW;
    meth.func = blue_draw;

    PtInit(NULL);

    /* Create the window. */
    PtSetArg (&argt[0], Pt_ARG_DIM, &area.size, 0);
    win = PtCreateWidget (PtWindow, NULL, 1, argt);

    /* Create some buttons.  When you click on one of these
       buttons, the callback makes the widget instance use
       the blue style. */
    n = 0;
    PtSetArg (&argt[n++], Pt_ARG_TEXT_STRING,
              "Use blue style", 0);
    PtSetArg (&argt[n++], Pt_CB_ACTIVATE, &cb, 1);
    but = PtCreateWidget (PtButton, NULL, n, argt);

    PtSetArg (&argt[0], Pt_ARG_TEXT_STRING,
              "Use blue style also", 0);
    PtSetArg (&argt[n++], Pt_ARG_POS, &area.pos, 0);
    but = PtCreateWidget (PtButton, NULL, n, argt);

    /* Create another button.  When you click on it, the
       callback attaches the blue style to the widget class. */

    n = 0;
    PtSetArg (&argt[n++], Pt_ARG_TEXT_STRING,
              "Attach blue style", 0);
    PtSetArg (&argt[n++], Pt_CB_ACTIVATE, &cb2, 1);
    PtSetArg (&argt[n++], Pt_ARG_POS, &area.pos, 0);
    area.pos.y = 85;
    but = PtCreateWidget (PtButton, NULL, n, argt);

    /* Copy the default style to make the blue style.
       Replace the drawing member of the new style. */
    b = PtDupClassStyle (PtButton, NULL, "blue");
    PtSetClassStyleMethods (b,1,&meth);

    PtRealizeWidget (win);
    PtMainLoop();

    return EXIT_SUCCESS;
}

Photon hook

Photon provides a mechanism for you to allow a block of user code to be pulled in and executed during the initialization of Photon applications. This functionality is most frequently used to customize widget styles, allowing you to change the appearance and behavior of widgets without having to re-compile, re-link, or otherwise reconstruct executables.


Note: The Photon hook can be used for many other things besides widget styles. For example, it can be used to log application usage information, or for more complicated situations such as remote control of an application.

PtInit() looks for a DLL, PtHook.so, in the search path, and executes the symbol for PtHook() in the DLL.

Multi-hook

You can use the pt_multihook.so DLL and rename it as PtHook.so to load one or several DLLs, pointed to by the PHOTON_HOOK environment variable. If PHOTON_HOOK points to a DLL, that DLL is loaded and its PtHook() function is executed. If PHOTON_HOOK points to a directory, each DLL in it is loaded and its PtHook() function executed.


Note: The PtHook.so feature may introduce security holes if the DLL code is insecure. If you use the pt_multihook.so, you may wish to modify its code to add your own security features. See the code listing below.

Example PtHook.so - the pt_multihook:



#include <stdio.h>
#include <stdlib.h>

#include <dlfcn.h>
#include <dirent.h>

#include <photon/PtHook.h>

static int hookit( const char *hookname, PtHookData_t *data ) {
  void *handle;
  if ( ( handle = dlopen( hookname, 0 ) ) == NULL )
    return -1;
  else {
    PtHookF_t *hook;
    if  ( ( hook = (PtHookF_t*) dlsym( handle, "PtHook" ) ) == NULL
      ||  (*hook)( data ) == 0
        )
      dlclose( handle );
    return 0;
  } }

int PtHook( PtHookData_t *data ) {
  const char *hookname;
  DIR *dir;
  if  ( ( hookname = getenv( "PHOTON_HOOK" ) ) != NULL
    &&  hookit( hookname, data ) != 0
    &&  ( dir = opendir( hookname ) ) != NULL
      ) {
    struct dirent *de;
    while ( ( de = readdir( dir ) ) != NULL)
      if ( de->d_name[0] != '.' ) {
        char path[512];
        if ( (unsigned) snprintf( path, sizeof(path), "%s/%s",
             hookname, de->d_name ) < sizeof(path) )
          hookit( path, data );
        }
    closedir( dir );
    }
  return Pt_CONTINUE;
  }

The PtHook function, declared in Photon/PtHook.h, looks like this:

int PtHook( PtHookData_t *data );

PtHookData_t has at least these members:

int size
The size of the PtHookData_t structure.
int version
The version of the Photon library that loaded the DLL.

The function can return Pt_END to ensure the DLL is not unloaded by PtInit(), or return Pt_CONTINUE to ensure DLL is unloaded.

Setting widget styles using the Photon Hook

Here is a simple example of changing widget styles using the Photon Hook. The following code changes the fill for all buttons to blue, based on the previous widget style example.

To compile this code, use:

cc -shared button_sample.c -o PtHook.so

Place the PtHook.so in the search path to change the button style for all Photon applications. You can get the search path with getconf _CS_LIBPATH.

#include <Pt.h>

static void (*button_draw)(PtWidget_t *widget, PhTile_t const *damage ) = NULL;

void blue_draw (PtWidget_t *widget, PhTile_t *damage)
{

    /* This is the drawing function for the blue style.
       It draws a blue rectangle (without a label) for
       the widget. */

    PgSetFillColor( Pg_BLUE);
    PgDrawRect( PtWidgetExtent (widget,NULL),
                Pg_DRAW_FILL);
}


int
PtHook (void *data)
{
    PtStyleMethods_t button_meth        = { Pt_STYLE_DRAW, blue_draw };
    PtWidgetClassStyle_t        *button_style   = PtFindClassStyle( PtButton, NULL );

    button_draw = button_style->draw_f;
    PtSetClassStyleMethods( button_style, 1, &button_meth );
    return( Pt_END );
}