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

Anatomy of a Widget

The first part of this chapter discusses requirements applicable to all widget classes:

This section: Describes:
Defining resources All aspects of defining resources
Defining the widget class The widget class structure and each of its fields
Class methods Every class method and how each is used
Widget actions How to make your widget interactive

You'll find information about the additional requirements for building container and compound widgets at the end of the chapter.

Defining resources

There are two steps to defining the resources of your widget class (aside from deciding what they should be in the first place):

  1. Defining the resource manifests and numbers.
  2. Defining the resource records to provide access to the resources of the widget class via resource manifests.

Resource manifests

Define the resources to introduce for a widget class (i.e. the resources that don't already exist in any of the widget superclasses). This is done in the widget's header file (e.g. PtButton.h) as a series of #define statements:

/*
 * PtButton public
 */

extern PtWidgetClassRef_t *PtButton;

#define Pt_BUTTON_ID    6

/* Resources */
#define Pt_ARG_ARM_COLOR        Pt_RESOURCE( 6, 0 )
#define Pt_ARG_ARM_IMAGE        Pt_RESOURCE( 6, 1 )
#define Pt_ARG_ARM_DATA         Pt_ARG_ARM_IMAGE
#define Pt_ARG_ARM_FILL         Pt_RESOURCE( 6, 2 )
#define Pt_ARG_SET_FILL         Pt_ARG_ARM_FILL

/*
 * PtButton private
 */

/* Widget structure */
typedef struct Pt_button_widget {
    PtLabelWidget_t         label;
    PgColor_t               arm_color;
    PhImage_t               *arm_data;
    PhImage_t               *unarmed_data;
    unsigned char                 arm_fill;
    PtCallbackList_t        *activate;
} PtButtonWidget_t;

All resource manifest names and numbers must be unique. In the above example, Pt_ARG_ARM_COLOR represents a unique resource manifest name, Pt_RESOURCE( 6, 0 ) represents a unique resource number, and the resource itself has a corresponding entry in the widget instance structure called arm_color.

When you build a new widget, you need to pick a unique widget number for it. The header file PtT.h contains two macros to help you do this:

Pt_RESOURCE( widget_number, resource_number)
Pt_USER( widget_number )

Note: The Pt_USER() macro is designed for widgets being created for in-house use only. If you intend to distribute the widgets you create as a public domain or commercial library, please contact QNX Software System's Customer Service. They'll give you a unique range of widget numbers and assign your widget set a prefix. This will prevent your widget library from conflicting with another third party's commercial widget library.

In your first in-house widget, use:

#define MY_1ST_WIDGET_RESOURCE1 Pt_RESOURCE( Pt_USER( 1 ), 0 );
#define MY_1ST_WIDGET_RESOURCE2 Pt_RESOURCE( Pt_USER( 1 ), 1 );

In your second in-house widget, use:

#define MY_2ND_WIDGET_RESOURCE1 Pt_RESOURCE( Pt_USER( 2 ), 0 );
#define MY_2ND_WIDGET_RESOURCE2 Pt_RESOURCE( Pt_USER( 2 ), 1 );

The macro Pt_USER( 1 ) defines a widget number that allows up to 1000 resources: it specifies the widget number 5,001,000. The macro Pt_USER( 2 ) defines the number 5,002,000, and so on.

The second part of the Pt_RESOURCE() macro defines the resource number: Pt_RESOURCE( Pt_USER( 2 ), 5 ) defines the resource number 5,002,005. The widget defined by Pt_USER( 2 ) can define unique resources numbers from 5,002,000 to 5,002,999.

PtResourceRec_t resource records

Resource records are used to connect the resource manifests described above with the widget's structure members. The resource records are defined as a table in the source code file and are passed as an argument when you first create the widget class.

This is a table of PtResourceRec_t items. This structure is declared as follows:

typedef struct Pt_resource_rec {
  unsigned long  type;
  void           (*mod_f)( PtWidget_t *, PtArg_t const *,
                  struct Pt_resource_rec const *  );
  int            (*query_f)( PtWidget_t *,
                  PtArg_t *, struct Pt_resource_rec const *  );
  unsigned long  arg_value;
  unsigned long  arg_len;
}  PtResourceRec_t;


type member

The value (manifest) to which this record is connected through a widget instance member. For example, Pt_ARG_FLAGS.

mod_f member

The function to call when this resource is being set by the user. You can provide your own function if you want to do addtional processing. The third argument is a pointer to a PtResourceRec_t with the data filled in by the library which your mod_f function can pass to PtSetStruct() to modify the resource.

This member recognizes several special convenience values other than the address of a function. Special values include:

Pt_CHANGE_INVISIBLE
Set the widget member but otherwise leave the widget unaffected.
Pt_CHANGE_REDRAW
Change the widget member and damage the widget (this causes a redraw).
Pt_CHANGE_INTERIOR
Same as Pt_CHANGE_REDRAW, except the widget's canvas alone is damaged (the borders aren't redrawn).
Pt_CHANGE_RESIZE
Change the widget structure member and flag the widget for resize. The resize is held off until the end of the PtSetResources() call in case other Pt_CHANGE_RESIZE-type resources are set in the same call. The resize is performed via PtMoveResizeWidget(), and the Extent method of the widget class is called.
Pt_CHANGE_RESIZE_REDRAW
Same as Pt_CHANGE_RESIZE, but forces a redraw even if the widget's dimensions or extent aren't changed.
Pt_CHANGE_PREVENT
Don't change the widget structure member and don't affect the widget. Indicates a read-only resource.
Pt_CHANGE_CANVAS
Invalidate the widget's canvas so that it's recalculated the next time it's requested. Additionally, resize the widget.
Pt_CHANGE_CANVAS_REDRAW
The same as Pt_CHANGE_CANVAS, but force the widget to be redrawn even if its canvas and extent aren't changed.

query_f member

The function to call when this resource is being queried via PtGetResources(). You can provide your own function if you want to do addtional processing. The third argument is a pointer to a PtResourceRec_t with the data filled in by the library which your query_f function can pass to PtGetStruct() to get the resource.

If no function is provided (i.e query_f is NULL), the resource is reported in the normal manner (see PtSetArg() and PtGetResources() in the Photon Library Reference).

The special values of the query_f field member include:

Pt_QUERY_PREVENT
Prevents access to the resource. Any pointers provided are set to NULL. Indicates a write-only resource.

arg_value and arg_len members

These are bit-encoded fields that set members of a widget structure. The arg_value member is used for all resources; arg_len is used to set a second widget structure member. For an array, arg_len has the type and offset of the array counter. For a Boolean value, arg_len is a bitmask. Unless the resource is an array or Boolean type, arg_len is normally 0.

The data encoded into these fields includes:

The following macros make using arg_value and arg_len more convenient:

The sections that follow describe the values of the arg_value and arg_len members for each type of resource. For most resources, arg_len isn't used; unless mentioned otherwise below, set it to 0.


Note: Memory for some of the resources (indicated below) is allocated and freed as needed. If you have a Destruction method, you don't need to free the memory for these resources. If you do free any memory, you must set the pointers freed to NULL or unexpected results may occur.

Scalar resources

arg_value C type of member1
Pt_ARG_IS_NUMBER(wgt, member1) char, short, or long (signed or unsigned)

Flags resources

arg_value C type of member1
Pt_ARG_IS_FLAGS(wgt, member1) char, short, or long (preferably unsigned)

For Flags resources, the “mask” isn't part of the resource — it's just a way of telling the resource-setting API which bits the application wants to preserve.

String resources

arg_value C type of member1
Pt_ARG_IS_STRING(wgt, member1) char *

This resource is allocated, based on the value returned by strlen(); see the note above.

Struct resources

arg_value C type of member1
Pt_ARG_IS_STRUCT(wgt, member1) Any type

These are widget members that are of a fixed size. When setting such resources, you pass a pointer to the value, and the value is copied into the widget structure. This type is useful for structures, as well as other data types that won't fit into a long (such as float or double).

Pointer resources

arg_value C type of member1
Pt_ARG_IS_POINTER(wgt, member1) Any type of pointer, including void *

The widget does a shallow copy of the pointer's value.

Alloc resources

arg_value C type of member1
Pt_ARG_IS_ALLOC(wgt, member1) Any type of pointer, including void *

Space is allocated for the resource, with the size specified by the application; see the note above.

Link resources

arg_value C type of member1
Pt_ARG_IS_LINK(wgt, member1) A pointer to a structure

The structure must start with a “next” pointer. Space is allocated for the resource; see the note above.

Callback resources

arg_value C type of member1
Pt_ARG_IS_CALLBACK_LIST(wgt, member1) A pointer to a structure

The structure must start with a “next” pointer. Space is allocated for the resource; see the note above.

Boolean resources

arg_value C type of member1
Pt_ARG_IS_BOOLEAN(wgt, member1) char, short, int, or long (preferably unsigned)

The arg_len is the bitmask. It's not stored anywhere in the widget — it's just a constant that determines in which bit of the widget structure the resource is stored.

Array resources

arg_value C type of member1
Pt_ARG_IS_ARRAY(wgt, member1) A pointer to some type

This type of resource also uses arg_len:

arg_len C type of member2
Pt_ARG_IS_NUMBER(wgt, member2) char, short, or long

The size of each array element is the size of the type pointed to by member1. Space is allocated for the resource; see the note above.

Image resources

arg_value C type of member1
Pt_ARG_IS_IMAGE(wgt, member1) PhImage_t *

Space is allocated for the resource; see the note above. For more information about the PhImage_t structure, see the Photon Library Reference.

Examples

Now let's look at some sample resource declarations. First let's look back at our original widget example, the ShadowedBox widget. It defines two resources in the header file:

/* widget resources */
#define SBW_SHADOW_COLOR   Pt_RESOURCE( Pt_USER( 0 ), 0 )
#define SBW_SHADOW_OFFSET   Pt_RESOURCE( Pt_USER( 0 ), 1 )

/* widget instance structure */
typedef struct shadowed_box_widget{
  PtBasicWidget_t   basic;
  PgColor_t         shadow_color;
  short             shadow_offset;
}   ShadowedBoxWidget;

The source code file defines the table of resources, which connects the resources to the widget class:

static PtResourceRec_t resources[] = {
    SBW_SHADOW_COLOR, Pt_CHANGE_REDRAW, 0,
        Pt_ARG_IS_NUMBER( ShadowedBoxWidget, shadow_color ), 0,
    SBW_SHADOW_OFFSET, Pt_CHANGE_RESIZE_REDRAW, 0,
        Pt_ARG_IS_NUMBER( ShadowedBoxWidget, shadow_offset ), 0,
    };

Let's examine the first resource in the table in more detail:

Now let's look at a more detailed example. In the widget header file we have:

/* widget resources */
#define MW_ARG_MY_CHARACTER     Pt_RESOURCE( Pt_USER(1), 0 )
#define MW_ARG_MY_STRING        Pt_RESOURCE( Pt_USER(1), 1 )
#define MW_ARG_MY_SHORT         Pt_RESOURCE( Pt_USER(1), 2 )
#define MW_ARG_MY_FLAGS         Pt_RESOURCE( Pt_USER(1), 3 )
#define MW_ARG_MY_FLAG_BIT      Pt_RESOURCE( Pt_USER(1), 4 )
#define MW_ARG_MY_POINT_ARRAY   Pt_RESOURCE( Pt_USER(1), 5 )
#define MW_ARG_MY_TAG_DATA      Pt_RESOURCE( Pt_USER(1), 6 )
#define MW_CB_MY_CALLBACK       Pt_RESOURCE( Pt_USER(1), 7 )

#define MW_MY_FLAG_BIT 0x04000000

/* widget instance structure */
typedef struct my_widget{
   PtBasicWidget_t  basic;          //Subclass of PtBasic.
   char       character;
   char       *my_string;
   short      my_short;
   long       flags;
   PhPoint_t  *points;              //Array of points.
   unsigned short   num_points;
   void       *tag_data;
   PtCallbackList_t  *my_callbacks; //Linked list of callbacks.
  } MyWidget_t;

In the class-creation function in the source code file we have:

static const PtResourceRec_t resources = {
     Pt_ARG_POS, arg_pos_override, 0,
       Pt_ARG_IS_NUMBER( PtWidget_t, area.pos ), 0,

     MW_ARG_MY_CHARACTER, Pt_CHANGE_REDRAW, 0,
       Pt_ARG_IS_NUMBER( MyWidget_t, character ), 0,

     MW_ARG_MY_STRING, Pt_CHANGE_RESIZE_REDRAW, 0,
       Pt_ARG_IS_STRING( MyWidget_t, my_string ), 0,

     MW_ARG_MY_SHORT, set_my_short, get_my_short, 0, 0,

     MW_ARG_MY_FLAGS, Pt_CHANGE_INVISIBLE, 0,
       Pt_ARG_IS_FLAGS( MyWidget_t, flags ), 0,

     MW_ARG_MY_FLAG_BIT, Pt_CHANGE_INVISIBLE, 0,
               Pt_ARG_IS_BOOLEAN ( MyWidget_t, flags ),
               MW_MY_FLAG_BIT,

     MW_ARG_MY_POINT_ARRAY, Pt_CHANGE_RESIZE_REDRAW, 0,
               Pt_ARG_IS_ARRAY( MyWidget_t, points ),
               Pt_ARG_IS_NUMBER( MyWidget_t, num_points ),

     MW_ARG_MY_TAG_DATA, Pt_CHANGE_INVISIBLE, 0,
       Pt_ARG_IS_ALLOC( MyWidget_t, tag_data ), 0,

     MW_CB_MY_CALLBACK, Pt_CHANGE_INVISIBLE, 0,
       Pt_ARG_IS_CALLBACK_LIST( MyWidget_t, my_callback ), 0,
          }

Pt_ARG_POS is inherited from PtWidget. Here we're overriding its mod_f() function with one of its own.

The MW_ARG_MY_SHORT resource and mod_f()/query_f() functions could look like this:

static void set_my_short( PtWidget_t *widget, PtArg_t *argt )
{
     MyWidget_t *mw = (MyWidget_t *)widget;

    if( mw->my_short == (short)argt->value )
       return; // don't do work if nothing is changing.

    mw->my_short = argt->value;

    // My widget needs to redraw when my_short changes...
    PtDamageWidget( widget );
}

static int get_my_short( PtWidget_t *widget, PtArg_t *argt )
{
    MyWidget_t *mw = (MyWidget_t *)widget;

    if( argt->value )
    // The address of a pointer to a short is in argt->value
          *(short **)argt->value = &mw->my_short;
     else
          argt->value = (long) mw->my_short;
    return Pt_TRUE;
    /* The only time one would return Pt_FALSE is if no data
       is given as a result of the query */
}

Defining the widget class

In Photon, a widget class is defined much a like a widget. You set up a list of arguments or class resources and call PtCreateWidgetClass(). This list is used to set the field members of the widget class structure that Photon uses to determine how to handle the widget.

Widget class structure

Let's look at the general form of a PtWidgetClass_t structure:

struct Pt_widget_class {
        char            *description;
        struct Pt_widget_class  *superclass;
        PtWidgetClassRef_t      *class_ref;
        unsigned        class_len;
        unsigned        state_len;
        unsigned long   flags;
        void    (*dflts_f)( PtWidget_t * );
        int     (*init_f)( PtWidget_t * );
        int     (*connect_f)( PtWidget_t * );
        void    (*unrealize_f)( PtWidget_t * );
        void    (*destroy_f)( PtWidget_t * );
        void    (*realized_f)( PtWidget_t * );
        PtClassRawCallback_t const *callbacks;
        int     (*setres_f)( PtWidget_t *, int,
                             PtArg_t const *,
                             PtResourceRec_t const *rrec );
        int     (*getres_f)( PtWidget_t const *,
                             int, PtArg_t * );
        unsigned int    ex_state_len;
        struct Pt_resource_range *res_ranges;
        void    (*syncwidget_f)( PtWidget_t *widget );
        int     (*calc_region_f)( PtWidget_t *,
                                  unsigned int *,
                                  PhRegion_t *,
                                  PhRect_t * );
        PtResourceRec_t const   **res_all;
        unsigned short  res_nranges;
        unsigned char         num_actions;
        unsigned char   num_callbacks;
        PtWidgetAction_t        *actions;
        short           max_style_index;
        unsigned short  res_nall;
        PtWidgetClassStyle_t    **styles;
        unsigned        reserved[ 5 ];
};

Widget class structure description

Let's look at the PtWidgetClass_t structure one member at time:

char *description
The name of the widget type, for example “PtTree” if the widget is a PtTree.
PtWidgetClass_t *superclass
A pointer to the superclass of this widget class. This value defines the superclass this class inherits from. If a widget class is at the top of the hierarchy, this pointer is NULL.
PtWidgetClassRef_t *class_ref
A pointer to the class_ref structure for this widget class.
unsigned class_len
The size of this widget class (this differs from the superclass only if the class extends the superclass definition).
unsigned state_len
The size of the widget instance structure for this widget class.
unsigned long flags
A general flag field for widget classes. For valid flag bits, see the list below.
void (*dflts_f)(PtWidget_t *)
Defines the Defaults method.
int (*init_f)(PtWidget_t)
Defines the Initialization method.
int (*connect_f)(PtWidget_t *)
Defines the Connection method.
void (*unrealized_f)(PtWidget_t *)
Defines the Unrealization method.
void (*destroy_f)(PtWidget_t *)
Defines the Destruction method.
void (*realized_f)(PtWidget_t *)
Defines the Realization method.
PtClassRawCallback_t *callbacks
The array of widget actions (raw callbacks) for the widget class. If a function in the callback list has an event mask matching the event being handled, that function is invoked.
int (*setres_f)(PtWidget_t *, int, PtArg_t *)
Defines the Set Resources method that's called when a user calls PtSetResources() on a widget of this class.
int (*getres_f)(PtWidget_t *, int, PtArg_t *)
Defines the Get Resources method that's called when a user calls PtGetResources() on a widget of this class.
unsigned int ex_state_len
The length of state_len. Used internally.
struct Pt_resource_range *res_ranges
Used internally.
void (*syncwidget_f)( PtWidget_t *widget )
Defines the Synchronize Widget method that's called when a user calls PtSyncWidget() on a widget of this class.
int (*calc_region_f)( PtWidget_t *, unsigned int *, PhRegion_t *, PhRect_t * )
Defines the Calculate Region method that's called when the Photon library calls PtCalcRegion() on a widget of this class.
PtResourceRec_t const **res_all
Defines the resource manifest table for the widget class. The resource manifest table is an array of pointers to all the resource records defined or inherited by this class.
unsigned short res_nranges
The length of res_ranges. Used internally.
unsigned char num_actions
The number of items in the actions array.
unsigned char num_callbacks
The number of items in the callbacks array.
PtWidgetAction_t *actions
A list of widget actions.
short max_style_index
The number of items in the styles array.
unsigned short res_nall
The number of items in the res_all array. Used internally
PtWidgetClassStyle_t **styles;
An array of styles for this widget class.
unsigned reserved[ 5 ]
Reserved for future use.

These are the valid flag bits for the flags member:

Pt_CONTAINER
The widget class is a container.
Pt_RECTANGULAR
Rectangular widgets are opaque when filled. Opaque widgets don't damage widgets below them when they're modified (unless their size or position is modified).
Pt_FORCE_UNREALIZE
(set automatically when required)
The Unrealization method (unrealize_f()) for this class and its superclasses is called when this widget is unrealized.
Pt_DISJOINT
(e.g. PtWindow, PtMenu, PtRegion)
Indicates that widgets of this class own regions that aren't children of the regions of their widget parents. This means that a disjoint widget is *not* assumed to sit inside its parent, and therefore is responsible for collecting exposes and emitting draw stream for itself and its non-disjoint descendants.
Pt_NO_INHERITED_RESOURCES
Prevents the search for a resource from walking up through superclasses. Only the resources for this class are handled; all others are ignored. This doesn't prevent resources from being propagated to procreated widgets.

This is handy for allowing common resources such as Pt_ARG_COLOR to pass to procreated children without having to write a resource-redirector function.

Pt_OCCLUSIVE
Drawing routines skip all children of a widget derived from an occlusive class. The rendering of these children is the responsibility of the occlusive widget.
Pt_BASIC
Indicates that the widget is a PtBasic or subclass.
Pt_FORCE_SYNC
Indicates widgets must be sized if the Pt_WIDGET_RESIZE flag is set during a set resource regardless of the widgets realized state.

Widget class resource table

Not all members of the widget class structure can be set directly; some are used internally by the widget library. The members you can change using the class resource manifests are:

Member Description Resource manifest
state_len Instance length Pt_SET_STATE_LEN
flags Flags Pt_SET_FLAGS
dflts_f Defaults method Pt_SET_DFLTS_F
init_f Initialization method Pt_SET_INIT_F
extent_f Extent method Pt_SET_EXTENT_F
connect_f Connection method Pt_SET_CONNECT_F
draw_f Draw method Pt_SET_DRAW_F
unrealized_f Unrealization method Pt_SET_UNREALIZE_F
realized_f Realization method Pt_SET_REALIZED_F
destroy_f Destruction method Pt_SET_DESTROY_F
resources Table of resources Pt_SET_RESOURCES
num_resources Number of resources Pt_SET_NUM_RESOURCES
callbacks Raw callbacks Pt_SET_RAW_CALLBACKS
setres_f Set Resources method Pt_SET_SETRESOURCES_F
getres_f Get Resources method Pt_SET_GETRESOURCES_F
version Version number Pt_SET_VERSION
description Textual description of the class; see below. Pt_SET_DESCRIPTION

For the description, you might include something like this in your class definition:

static const PtArg_t args[] =
{
  ...

  { Pt_SET_DESCRIPTION, (long) "PtButton" },
  ...
};

In general, it's assumed that the description corresponds to the widget's name (i.e. applications might conceivably use this field to display the name of the widget's class) and hence it's recommended you follow this practise. If additional information is required, the convention adopted is to append a colon (:) followed by the text; applications that use this description field will look for the first : and parse based on it.

So this would translate the above example to

  { Pt_SET_DESCRIPTION, (long) "PtButton:dave was here" },

Note: This is a convention, not a rule. You can use this field for whatever you want, as long as you realize that some applications might use this field (for informational purposes only). The most graceful way to have such applications disregard this field altogether is to precede the text with a a colon, like this:
  { Pt_SET_DESCRIPTION, (long) ":dave was here" },

in which case, the application should indicate a “value not known” state (i.e. disregard anything past the : in all cases).


When defining a custom widget class, you can extend the definition of its superclass with new “class-level” methods and/or callbacks. For example, the PtWidget class is extended by PtBasic, which adds new class methods for providing focus notification. This was done as shown in the following excerpt from the PtBasic.h header file:

typedef struct Pt_basic_widget_class {
     PtWidgetClass_t     core;
     void                (*got_focus_f)( PtWidget_t *,
                                         PhEvent_t * );
     void                (*lost_focus_f)( PtWidget_t *,
                                          PhEvent_t * );
     void                (*calc_opaque_f)( PtWidget_t * );
     } PtBasicWidgetClass_t;

#define Pt_SET_GOT_FOCUS_F (Pt_ARG_IS_POINTER(PtBasicWidgetClass_t,got_focus_f))
#define Pt_SET_LOST_FOCUS_F (Pt_ARG_IS_POINTER(PtBasicWidgetClass_t,lost_focus_f))
#define Pt_SET_CALC_OPAQUE_F (Pt_ARG_IS_POINTER(PtBasicWidgetClass_t,calc_opaque_f))

When a class definition is extended in this way, the new class structure size must be passed as the second parameter to PtCreateWidgetClass():

PtBasic->wclass = PtCreateWidgetClass( PtWidget,
                      sizeof(PtBasicWidgetClass_t ),… );

If the class isn't extended, the second parameter may be passed as 0. Also, when the class is extended, you must define new Pt_SET* manifests to provide access to the new class members from the create class function.

PtBasic class resource additions

The PtBasic widget class provides the following three additional structure members:

Member Description Resource manifest
got_focus_f Got Focus method Pt_SET_GOT_FOCUS_F
lost_focus_f Lost Focus method Pt_SET_LOST_FOCUS_F
calc_opaque_f Calc Opaque Rect method Pt_SET_CALC_OPAQUE_F

Let's look at the class resource table from our ShadowedBox example in more detail:

static PtArg_t args[] = {
    { Pt_SET_VERSION, 110},
    { Pt_SET_STATE_LEN, sizeof( ShadowedBoxWidget ) },
    { Pt_SET_DFLTS_F, (long)shadowedbox_dflts },
    { Pt_SET_DRAW_F, (long)shadowedbox_draw },
    { Pt_SET_FLAGS, 0, Pt_RECTANGULAR },
    { Pt_SET_NUM_RESOURCES,
      sizeof( resources ) / sizeof( resources[0] ) },
    { Pt_SET_RESOURCES, (long)resources,
      sizeof( resources ) / sizeof( resources[0] ) },
    };
Pt_SET_VERSION
Tells the widget library what style this widget is. For your widgets, this value is always at least 110 (110 corresponds to Photon 1.10 style).
Pt_SET_STATE_LEN
Defines the length of the widget instance structure.
Pt_SET_DFLTS_F
Defines the Defaults method, which is called to set default values for the widget when it's first created. In our example, this function is called shadowedbox_dflts().
Pt_SET_DRAW_F
Defines the Draw method (e.g. shadowedbox_draw()), which is called when the widget needs to be drawn.
Pt_SET_FLAGS
Defines behavioral flags related to this widget class. This example turns off the Pt_RECTANGULAR flag, which indicates the widget can't take advantage of flicker-free, opaque-drawing methods.
Pt_SET_NUM_RESOURCES
Defines the number of resources in the table.
Pt_SET_RESOURCES
Defines the table of resources unique to this widget, as described above.

Class methods

This section describes the role and responsibilities of the class methods defined in the widget class structure. The fundamental methods defined by PtWidget are:

The PtBasic widget extends these with three additional methods:

These are the methods you write to define the functionality of your widget. Not all methods need to be defined and coded by a widget class; they can be inherited from a superclass. Almost all widgets need at least the Draw method, since the primary purpose of building a custom widget is to draw something.

Defaults method

Type: Chained down, unstoppable

This method sets the default values for all widget instance structure members. It's called during the creation of a widget instance (call to PtCreateWidget()). When a widget instance is first created, all members of the instance structure are zeroed out and then the Defaults method for each parent class (starting from PtWidget at the top) are called in sequence from top to bottom. This allows the lower-level classes to override the default values set by the parent classes in the hierarchy. Here's an example showing how to initialize PtBasic variables:

static void basic_dflts( PtWidget_t *widget )
{

PtBasicWidget_t *basic = (PtBasicWidget_t *) widget;

  widget->border_width = 2;
  widget->cursor_color = Ph_CURSOR_DEFAULT_COLOR;
  widget->resize_flags |= Pt_RESIZE_XY_AS_REQUIRED;

  basic->color = Pg_BLACK;
  basic->fill_color = Pg_GREY;
  basic->top_border_color = Pg_WHITE;
  basic->bot_border_color = Pg_DGREY;
  basic->flags = Pt_DAMAGE_ON_FOCUS;
}

Although the instance variables in this example are specific to PtBasic, the initialization technique is common to all Defaults methods.

Initialization method

Type: Chained up, stoppable

This is the first method called when a widget is realizing (call to PtRealizeWidget()). The Initialization method does final checks to ensure the widget is “extentable.” This check ensures all members used in the subsequent Extent method are correctly assigned.

The Initialization method is also the best place to create unexported subordinate widgets. Unexported subordinate widgets are used by the widget but not available to the user. For example, a widget such as PtPrintSel uses other widgets (PtText, PtLabel, and PtButton) but doesn't allow the user to act directly on the subordinate widgets. Instead, the widget provides new unique resources used internally to set the subordinate widgets. For more information about exported subordinate widgets, see Compound widget anatomy later in this chapter.

Initialization is executed each time the widget is realized. The Initialization method is called first for this widget followed by its parent superclass and so on until the core widget class (PtWidget) is reached. Any widget class in the chain can terminate the chaining process by returning Pt_END.

Here's an example showing how a PtContainer widget is initialized:

static in PtContainerInit( PtWidget_t *widget )
{
  PtContainerRegister( widget );
  return( Pt_CONTINUE );
}

The Initialization method is also used to register widgets for balloon handling. All container widgets provide a default balloon-handling mechanism. If you want your widget to support popup help balloons, you must register the widget with the container in this function. Here's an excerpt from the PtLabel code:

static int label_init( PtWidget_t *widget )
{
    PtLabelWidget_t *label = (PtLabelWidget_t *) widget;
    PtBalloonCallback_t bcalls;
    PtArg_t argt;

  if( (label->flags & Pt_SHOW_BALLOON)
     && ( !( label->flags & Pt_BALLOON_REGISTERED ) ) )
  {
     bcalls.widget  = widget;
     bcalls.event_f = label_balloon_callback;
     PtSetArg( &argt, Pt_CB_BALLOONS, &bcalls, 0 );
     PtSetResources( widget->parent, 1, &argt );
     label->flags |= Pt_BALLOON_REGISTERED;

  }
  return Pt_CONTINUE;
}

Extent method

Type: Inherited

This is the second method called during the realization of a widget (call to PtRealizeWidget()). The Extent method is responsible for determining the widget's screen real estate (bounding box) with regard to its resize policy. The Extent method is called frequently during the life of a widget — whenever the widget is moved or resized, or resources marked as Pt_CHANGE_RESIZE or Pt_CHANGE_RESIZE_REDRAW are modified.

The following are examples of these types of resources, as defined by the PtWidget class:

static PtResourceRec_t resources[] = {
{  Pt_ARG_AREA, Pt_CHANGE_RESIZE, 0,
   Pt_ARG_IS_STRUCT(PtWidget_t, area) },

{  Pt_ARG_POS, Pt_CHANGE_RESIZE, 0,
   Pt_ARG_IS_STRUCT(PtWidget_t, area.pos) },

{  Pt_ARG_DIM, Pt_CHANGE_RESIZE, 0,
   Pt_ARG_IS_STRUCT(PtWidget_t, area.size) },
};

In addition, the Extent method is called whenever any other resource causes a widget to change size or position. The following example code is taken from PtLabel:

static PtResourceRec_t resources[] = {
{ Pt_ARG_LABEL_TYPE, Pt_CHANGE_RESIZE_REDRAW, 0,
  Pt_ARG_IS_NUMBER(PtLabelWidget_t, type), 0 },
};

The widget engine uses the extent calculated in this method to determine when this widget is involved in an event, when the widget needs to be repaired, and which other widgets need to be repaired when this one changes.

Every widget class inherits the Extent method of its superclass. If this method is suitable, you won't need to provide your own Extent method. If the superclass's Extent method isn't quite what your widget needs, you should provide only what the widget's superclass doesn't provide, then call the Extent method of the superclass to do the rest of the work.

It's unusual to write an Extent method that calculates the entire extent without using the superclass's method. Quite often you'll want most, but not all, of the superclass's extent behavior. There's usually a way to prevent the Extent method of the superclass from performing any set of extenting behaviors (see the “Extent method” sections in the Using Widget Superclasses chapter for details).

Extenting a widget usually involves four stages:

  1. Calculate the canvas. Check the chapter on Using Widget Superclasses for canvas services. The canvas is the area within which a widget must restrict its rendering. The canvas function is PtCalcCanvas().
  2. Calculate the render rectangle. This is the bounding box area required for this widget to display all its data, including points, widget children, any text strings, etc.
  3. Call PtResizeCanvas() passing to it the widget and desired size. PtResizeCanvas() applies the widget's resize policy automatically and modifies the widget's dimensions as required to ensure the widget conforms to its resize policy.

    If a widget's canvas can't be resized to encompass the render rectangle, PtResizeCanvas() sets the Pt_UCLIP bit in the widget's resize flags. The widget's Draw method should check the Pt_UCLIP bit. If this bit is set, the Draw method should apply clipping to prevent the widget from rendering outside its canvas.

  4. Call:
    PtSuperClassExtent( PtBasic, widget)

    to have the final extent calculation performed. The Extent method of PtBasic uses the widget's position, dimension, border width, and flags to determine the extent.

Here's an example showing how to apply these guidelines:

mywidget_extent( PtWidget_t *widget )
{
  MyWidgetUnion_t *mwu = (MyWidgetUnion_t *)widget;
  PhRect_t  canvas, render;
  PhDim_t size;

  PtCalcCanvas( widget, &canvas );
  render.ul = render.lr = canvas.ul;
  PgExtentText( &render, &render.ul, mwu->label.font,
                mwu->label.string, 0 );
  size.w = render.lr.x - render.ul.x + 1;
  size.h = render.lr.y - render.ul.y + 1;
  PtResizeCanvas( widget, &size );
  PtSuperClassExtent( PtBasic, widget );
}

Connection method

Type: Chained up, stoppable

This method creates Photon regions whenever a widget requires them. Generally, it isn't necessary to explicitly create your own region since the Connection method of PtWidget does this for you.

However, it may be beneficial to create your own region if you need to modify the region based on the current environment. For example, PtMenu widgets have to modify a region whenever they're displayed:

static int menu_connect( PtWidget_t *widget )
{
  PhRegion_t     region;
  unsigned       fields;
  PhRect_t       rect;
  PtMenuWidget_t *menu = (PtMenuWidget_t *)widget;
  PtWidget_t     *wp;

  /* calculate menu region */
  fields = UINT_MAX;
   if( !PtCalcRegion( &fields, widget, &region, &rect ) )
     return( -1 );

  /* open menu region */
  region.parent     = menu->ff_wgt->rid;
  region.events_opaque  |= Ph_EV_DRAW | Ph_EV_PTR_ALL;
  region.events_opaque  &= ~Ph_EV_KEY; region.events_sense
       |= Ph_EV_PTR_ALL; region.events_sense   &= ~Ph_EV_KEY;
  region.flags      = 0;
  fields |= Ph_REGION_PARENT | Ph_REGION_EV_SENSE |
            Ph_REGION_EV_OPAQUE;
  fields &= ~( Ph_REGION_BEHIND | Ph_REGION_IN_FRONT );
  widget->rid = PhRegionOpen( fields, &region, &rect, NULL );
  wp = widget;
 while( PtWidgetIsClass( wp, PtMenu ) )
  {  menu_pdr_start( wp );
     if ( !wp->parent )
       break;
     wp = wp->parent->parent;
  }
    if( widget->parent &&
        PtWidgetIsClassMember( widget->parent, PtContainer ) )
  {  if( !( menu->flags & Pt_MENU_CHILD ) )
     {
       // If the menu is off a window, focus that window.
       PhEvent_t event;
       memset( &event, 0, sizeof( event ) );
       menu->prev_focus =
          PtContainerFindFocus( widget->parent );
       if( widget->parent->class_rec->flags & Pt_DISJOINT )
          PtContainerNullFocus( widget->parent, &event );
       else
       {
          int flags = widget->parent->flags;
          widget->parent->flags |= Pt_GETS_FOCUS;
          PtContainerGiveFocus(
          widget->parent, &event );
          widget->parent->flags = flags;
       }
     } else
       menu->flags |= Pt_MENU_SUBFOCUS;
     ((PtContainerWidget_t *)widget->parent)->focus = widget;
  }
  wp = PtFindDisjoint( widget );
  for( wp = wp->parent;
       wp && (PtWidgetIsClassMember(wp, PtMenu)
       || PtWidgetIsClassMember(wp, PtMenuButton));
       wp = wp->parent);
  if( wp )
  {
     wp = PtFindDisjoint( wp );
     if (PtWidgetIsClassMember( wp, PtWindow ))
        ((PtContainerWidget_t *)wp)->last_focus = NULL;
     if( !( PtWindowGetState( wp ) & Ph_WM_STATE_ISFOCUS ) ) {
       PtWindowFocus( wp );
     }
    }
  }

  /* stop init chaining */
  return( Pt_END );
}

If you create your own region, the Connection method of PtWidget won't create another region — it modifies the current region. To prevent modification of the current region, have the widget class return Pt_END.

Realization method

Type: Inherited

This method is called after a widget has been realized. Its function is similar to that of the Pt_CB_REALIZED callback, but unlike the Pt_CB_REALIZED callback, it can't be accessed by any developer using your widget.

This method is used primarily by compound widgets to perform any postrealize operations, such as realizing any subordinate widgets that couldn't be realized to this point. It's the last method to be called prior to the Draw method, and it's invoked just prior to the user's Pt_CB_REALIZED callbacks. For more information, see the section on Compound widget anatomy.”

Draw method

Type: Inherited

This is the last method called during the realization process. The Draw method is used to render the widget on the screen during realization and is called to redraw a widget afterwards whenever that widget is damaged.

When a widget is damaged, a pointer to the damaged widget and a list of tiles describing the damage are passed to the Draw method. The first tile in the damage list reveals the total extent of the damage (its bounding box encompasses all remaining tiles). The remaining tiles contain the actual extents damaged. Unless the widget is complex or passes a lot of draw data (large images) and can be sped up by drawing damaged areas only, you should ignore the damage list.

The widget library uses the damage list to clip parts that shouldn't be drawn. If you plan to use the damage tiles, make sure to translate the widget canvas using the widget's offset. Use PtWidgetOffset() to obtain the offset — the damage list is relative to the disjoint parent widget (usually a PtWindow widget).

The Draw method restricts its updates to the canvas of the damaged widget. This is done by setting a clipping rectangle if necessary. Here's a drawing example taken from the code for PtButton:

static void button_draw( PtWidget_t *widget, PhTile_t *damage )
{
  PtButtonWidget_t  *button = (PtButtonWidget_t *)widget;
  PgColor_t     tcolor;

  button->label.data = button->unarmed_data;
  /* set fill_color to arm_color if required */
  if ( widget->flags & Pt_SET )
  {
     if( button->arm_fill == 1 )
     {
       tcolor = button->label.basic.fill_color;
       button->label.basic.fill_color = button->arm_color;
     }
     if( button->arm_data )
       button->label.data = button->arm_data;
  }

  /* draw button - includes highlight */
  PtSuperClassDraw( PtLabel, widget, damage );

  /* restore fill_color */
  if ( widget->flags & Pt_SET )
  {
     if ( button->arm_fill == 1 )
        button->label.basic.fill_color = tcolor;
  }
}

The following code excerpt (from PtArc) shows how setting the Pt_UCLIP bit in the widget's resize flags affects clipping:

  PtCalcCanvas( widget, &rect );
  if ( widget->resize_flags & Pt_UCLIP )
     PtClipAdd( widget, &rect );
  PgDrawArc( &pos, &dim, start, end, arc->type | flags );
  if ( widget->resize_flags & Pt_UCLIP )
     PtClipRemove( );

Using the Pg library safely

You'll use the Pg* functions in your widget's Draw method, but you need to use them safely. Here are some things to remember:

PgSetClipping()
PgSetMultiClip()
PgSetUserClip()
These are used by the library. The only safe way of changing clipping in a widget's Draw method is to use PtClipAdd() and PtClipRemove() (see the Widget Building Library API chapter).
PgSetDrawMode()
PgSetPlaneMask()
PgSetStrokeCap()
PgSetTranslation()
PgSetUnderline()
These aren't set by the library, but widgets assume they're set to their default values. If your Draw method changes any of them, you have to restore it.
PgSetPalette()
PgSetFillDither()
PgSetFillTransPat()
PgSetStrokeTransPat()
PgSetTextTransPat()
PgSetStrokeWidth()
PgSetStrokeDash()
PgSetStrokeDither()
PgSetStrokeJoin()
These are automatically reset to the default after a widget's Draw method returns. It's safe to change them.
PgSetFillColor()
PgSetStrokeColor()
PgSetTextColor()
PgSetFillXORColor()
PgSetStrokeXORColor()
PgSetTextXORColor()
There's no default for these. Set them as needed before drawing and don't worry about restoring them.
PgSetRegion()
This is set by the library. If you change it, be sure to restore it.

For more information about these functions, see the Photon Library Reference.

Unrealization method

Type: Chained up

This method is called when a widget is being unrealized. The Unrealization method is responsible for removing any regions created by a widget (widget->rid is removed automatically by the PtWidget class's Unrealization method). The Unrealization method should free any memory that would be reallocated during the realization process.

The Unrealization method is also used to deregister widgets. For example, if a label widget registered a balloon in its Initialization method, it must deregister the balloon in the Unrealization method or the balloon will try to inflate whenever the mouse pointer pauses over the last location of the widget. Here's an example taken from the code for PtLabel:

static int label_unrealize(PtWidget_t *widget )
  { PtLabelWidget_t *label = (PtLabelWidget_t*)widget;
  PtBalloonCallback_t bcalls;
  PtArg_t arg;

  if(label->balloon_widget)
      PtDestroyWidget( label->balloon_widget );

  bcalls.widget = widget;
  bcalls.event_f = label_balloon_callback;

  if( label->flags & Pt_SHOW_BALLOON ){
     PtSetArg( &arg, Pt_CB_BALLOONS, &bcalls, Pt_LINK_DELETE );
     PtSetResources( widget->parent, 1, &arg );
  }
  label->flags &= ~Pt_BALLOON_REGISTERED;
  return Pt_CONTINUE;
}

Destruction method

Type: Chained up, unstoppable

This method is called when a widget is being destroyed by the application. The Destruction method is responsible for releasing all resources allocated by a widget class during its lifetime. The Destruction method doesn't deal with memory allocated by a widget's superclass, because each class is responsible for freeing its own memory. Here's an example taken from the code for PtLabel:

static int
label_destroy( PtWidget_t *widget )
  {
  PtLabelWidget_t *label = (PtLabelWidget_t *)widget;
  PtArg_t arg;
  PtBalloonCallback_t bcalls;
  if( label->flags & Pt_BALLOON_REGISTERED ) {
    bcalls.widget = widget;
    bcalls.event_f  = (void*)label_balloon_callback;
    if( label->flags & Pt_SHOW_BALLOON )
      {
      PtSetArg( &arg, Pt_CB_BALLOONS, &bcalls, Pt_LINK_DELETE );
      PtSetResources( widget->parent, 1, &arg );
    }
    label->flags &= ~Pt_BALLOON_REGISTERED;
  }
  return 0;
}

Set Resources method

Type: Inherited

This method is used to take over the standard resource setting process built into the Photon library. Compound widgets are the only widgets that set this method (see the section on Compound widget anatomy).

Get Resources method

Type: Inherited

This method is used to take over the standard resource retrieval process built into the Photon library. Compound widgets are the only widgets that set this method (see the section on Compound widget anatomy).

Got Focus method

Type: Inherited

This method is used by subclasses of PtBasic only. It's called when a widget gets focus. If your widget gets focus in such a way that the whole widget need not be redrawn, clear the Pt_DAMAGE_ON_FOCUS flag (from PtBasic) and damage the appropriate area when your widget gets focus.

This method isn't chained. If your class defines a Got Focus method, you'll have to either call PtSuperClassLostFocus() to preserve automatic highlighting and widget-level Got Focus method behavior or implement the behavior in the Got Focus method of your class. Here's an excerpt from the code for PtBasic:

static basic_got_focus( PtWidget_t *widget,
                        PhEvent_t *event)
{
  PtBasicWidget_t *basic =( PtBasicWidget_t *) widget;
  PtCallbackInfo_t cbinfo;
  PtArg_t arg;

  /* damage the widget so that focus rendering
     will take effect */
  if( ( widget->flags & Pt_FOCUS_RENDER ) &&
      ( basic->flags & Pt_DAMAGE_ON_FOCUS ) )
     PtDamageWidget( widget );

  /* setup callback structure */
  cbinfo.reason_subtype = 0;
  cbinfo.event = event;
  cbinfo.cbdata = NULL;

  /* an autohighlight widget with focus should invoke ARM */
  if( widget->flags & Pt_AUTOHIGHLIGHT )
  {
     PtSetArg( &arg, Pt_ARG_FLAGS,
               Pt_HIGHLIGHTED, Pt_HIGHLIGHTED);
     PtSetResources( widget, 1, &arg );
     cbinfo.reason = Pt_CB_ARM;
     PtInvokeCallbackList( basic->arm, widget, &cbinfo );
  }

  /* invoke got focus callback */
  cbinfo.reason = Pt_CB_GOT_FOCUS;
 PtInvokeCallbackList( basic->got_focus, widget, &cbinfo );
  return Pt_CONTINUE;
}

Lost Focus method

Type: Inherited

This method is used by subclasses of PtBasic only. It's called when a widget loses focus. This method isn't chained. If your class defines a Lost Focus method, you'll have to either call PtSuperClassLostFocus() to preserve automatic highlighting and widget-level Lost Focus method behavior or implement the behavior in the Lost Focus method of your class. Here's an excerpt from the code for PtBasic:

static basic_lost_focus( PtWidget_t *widget, PhEvent_t *event )
{
  PtBasicWidget_t *basic =( PtBasicWidget_t *) widget;
  PtCallbackInfo_t cbinfo;
  PtArg_t arg;
  PhRect_t wrect, rect = widget->extent;

  if( (widget->flags & Pt_FOCUS_RENDER) &&
      ( basic->flags & Pt_DAMAGE_ON_FOCUS )  )
     if( basic->fill_color == Pg_TRANSPARENT )
     {
       PhTranslateRect( &rect,
             (PhPoint_t*)PtCalcCanvas( widget->parent,
                                       &wrect ) );
       PtDamageExtent( widget->parent, &rect );
     }else
       PtDamageWidget( widget );
  cbinfo.reason_subtype = 0;
  cbinfo.event = event;
  cbinfo.cbdata = NULL;
  if( widget->flags & Pt_AUTOHIGHLIGHT )
  {
     PtSetArg( &arg, Pt_ARG_FLAGS, 0, Pt_HIGHLIGHTED );
     PtSetResources( widget, 1, &arg );
     cbinfo.reason = Pt_CB_DISARM;
     PtInvokeCallbackList( basic->disarm, widget, &cbinfo );
  }
  cbinfo.reason = Pt_CB_LOST_FOCUS;
  PtInvokeCallbackList( basic->lost_focus, widget, &cbinfo );
  return Pt_CONTINUE;
}

Calc Opaque Rect method

Type: Inherited

This method is used only by subclasses of PtBasic. It sets or clears the widget's Pt_OPAQUE flag (Pt_ARG_FLAGS resource) and the Pt_RECTANGULAR widget class flag.

When the Pt_OPAQUE flag is set for a widget, it means the widget draws over the entire widget extent area. This allows the widget library to be smart about redrawing the widget, because it knows that nothing beneath the widget needs to be redrawn. This flag is essential for creating flicker-free effects. If any part of the widget is transparent (i.e. any widget beneath can be seen), the Pt_OPAQUE flag must be cleared. Here's an excerpt from the code for PtBasic:

static void basic_calc_opaque( PtWidget_t *widget )
{
  PtBasicWidget_t *basic = (PtBasicWidget_t *) widget;

  /* if widget is transparent or round it can't be opaque */
  if ( basic->fill_color == Pg_TRANSPARENT ||
                               basic>roundness )
     widget->flags &= ~Pt_OPAQUE;
  else
     /* must have RECTANGULAR class flag set */
     if ( widget->class_rec->flags &
          Pt_RECTANGULAR ) {
       widget->flags |= Pt_OPAQUE;
       basic_opaque_rect( widget );
     }
}

static void basic_opaque_rect( PtWidget_t *widget )
{
  if( widget->flags & Pt_HIGHLIGHTED )
     memcpy( &widget->opaque_rect,
             &widget->extent, sizeof( PhRect_t ) );
  else
     PtCalcCanvas( widget, &widget->opaque_rect );
}

Widget actions

Widget actions are used to make a widget interactive. For example, when you click a button widget, you can see it press in and then pop out again. This is achieved by setting up raw callbacks sensitive to specific Photon events. The way a widget interacts with events defines the widget's behavior.

The callbacks list, Pt_SET_RAW_CALLBACKS (see the Widget class resource table section earlier in this chapter), defines a widget's response if it's different from the behavior of its superclasses. This method is defined in the same manner as Pt_CB_RAW. The primary difference is that the class's raw callback list is invoked before the user's raw callback list. Here's an excerpt from the code for PtBasic:

static const PtClassRawCallback_t callback = {

  Ph_EV_BUT_PRESS | Ph_EV_BUT_RELEASE |
  Ph_EV_BUT_REPEAT | Ph_EV_BOUNDARY,
  basic_callback
  };

static PtArg_t args[] = {
  …
  { Pt_SET_RAW_CALLBACKS, &callback },
  …
  };

In the example above, whenever the widget receives one of the four events (Ph_EV_BUT_PRESS, Ph_EV_BUT_RELEASE, Ph_EV_BUT_REPEAT, or Ph_EV_BOUNDARY), the basic_callback() function is invoked. This function can check the event type and act accordingly.

For clarity, let's walk through a simplified description of the “click” process of a PtButton widget.

First, PtButton receives the Ph_EV_BUT_PRESS event. The widget interprets the press event, changes the widget flags to Pt_SET (possibly causing the widget to be damaged and redrawn), and then invokes the Pt_CB_ARM callback.

When the user releases the mouse button, PtButton receives a Ph_EV_BUT_RELEASE release event. The widget clears the Pt_SET flag and then invokes the Pt_CB_DISARM and Pt_CB_ACTIVATE callbacks.

The action PtButton takes when the button is released is a little more complicated, because the widget actually doing the work is PtBasic, not PtButton. The PtButton class doesn't define any raw callbacks because the handling done by PtBasic is sufficient. The PtBasic class handles the common Photon arm, disarm, repeat, activate, and menu callbacks for all widgets.

Raw callback list

Type: Chained up, stoppable

The class's list of raw callbacks is used to make a widget sensitive to raw Photon event messages. This allows the widget to define a specific behavior related to external events or user interaction.

Returning Pt_HALT from a class's raw callback prevents any superclass (or the user) from processing the event. The event is propagated up through the widget hierarchy and may be handled by a parent widget.

Returning Pt_END from a class's raw callback prevents any superclass, user, or parent widget from processing the event. This is called consuming the event. Here's an example taken from the code for PtTimer:

static int timer_callback( PtWidget_t *widget, PhEvent_t *event )
{
  PtTimerWidget_t *timer = (PtTimerWidget_t *)widget;

  {
    PtCallbackInfo_t  cbinfo;
    cbinfo.event = event;
    cbinfo.cbdata = NULL;
     cbinfo.reason = Pt_CB_TIMER_ACTIVATE;
     if( timer->state == Pt_TIMER_INITIAL )
       cbinfo.reason_subtype = Pt_TIMER_INITIAL;
     else
       cbinfo.reason_subtype = Pt_TIMER_REPEAT;
     timer->state = Pt_TIMER_REPEAT;
     PtInvokeCallbackType( widget, Pt_CB_TIMER_ACTIVATE, &cbinfo );
     if(timer->msec_repeat && timer->state == Pt_TIMER_REPEAT)
      PtTimerArm(widget,timer->msec_repeat);
  }
  return( Pt_CONTINUE );
}
//
// PtTimer class-creation function
//
static PtWidgetClass_t *PtCreateTimerClass( void )
{
    static const PtResourceRec_t resources[] =
    {
      { Pt_ARG_TIMER_INITIAL, timer_modify, 0,
        Pt_ARG_IS_NUMBER( PtTimerWidget_t, msec_value ) },
      { Pt_ARG_TIMER_REPEAT, timer_modify, 0,
        Pt_ARG_IS_NUMBER( PtTimerWidget_t, msec_repeat ) },
      { Pt_ARG_AREA, Pt_CHANGE_PREVENT, 0,
        Pt_ARG_IS_STRUCT( PtTimerUnion_t, core.area ) },
      { Pt_ARG_DIM, Pt_CHANGE_PREVENT, 0,
        Pt_ARG_IS_STRUCT( PtTimerUnion_t, core.area.size ) },
      { Pt_ARG_POS, Pt_CHANGE_PREVENT, 0,
        Pt_ARG_IS_STRUCT( PtTimerUnion_t, core.area.pos ) },
      { Pt_CB_TIMER_ACTIVATE, Pt_CHANGE_INVISIBLE, 0,
        Pt_ARG_IS_IN_CB_LISTS( PtCallback_t ) },
    };

    static const PtClassRawCallback_t callback =
    {
      Ph_EV_TIMER, timer_callback
    };

    static const PtClassArg_t args[] = {
      { Pt_SET_DFLTS_F,  timer_dflts },
      { Pt_SET_EXTENT_F,  PtNullWidget_f },
      { Pt_SET_REALIZED_F,  timer_realized },
      { Pt_SET_RAW_CALLBACKS,  &callback },
      { Pt_SET_RESOURCES,  resources },
      { Pt_SET_DESCRIPTION,  "PtTimer" },
      { Pt_SET_VERSION, 200},
      { Pt_SET_STATE_LEN, sizeof( PtTimerWidget_t ) },
      { Pt_SET_FLAGS, Pt_FORCE_UNREALIZE, Pt_FORCE_UNREALIZE },
      { Pt_SET_NUM_RESOURCES, sizeof( resources )/sizeof( resources[0] ) },

    };
    return( PtTimer->wclass = PtCreateWidgetClass(
      PtWidget, 0, sizeof( args )/sizeof( args[0] ), args) );
}

Note: The example code above is similar but not identical to the Photon library code; some optimizations have been removed for clarity.

Container widget anatomy

This section applies to creating PtContainer class widgets:

PtWidgetPtBasicPtContainerMyContainer

Child-constraint support

Container widgets extend the PtBasic widget class definition with a number of constraint methods. These methods allow the container to adjust itself automatically according to changes made to its children.

The container class extension is shown in the example code below (taken from the PtContainer.h header file):

typedef struct Pt_container_widget_class {
  PtBasicWidgetClass_t basic;
  void (*child_created_f)( … );
  int  (*child_settingresource_f)( … );
  int  (*child_gettingresource_f)( … );
  int  (*child_realizing_f)( … );
  void (*child_realized_f)( … );
  void (*child_unrealizing_f)( … );
  void (*child_unrealized_f)( … );
  void (*child_destroyed_f)( … );
  void (*child_move_resize_f)( … );
  int  (*child_getting_focus_f)( … );
  int  (*child_losing_focus_f)( … );
  PtWidget_t * (*child_redirect_f)( … );
} PtContainerClass_t;

Constraint methods can be individually enabled or disabled by setting or clearing the appropriate container->flags bits. You can enable or disable all the constraint methods at once by setting or clearing Pt_IGNORE_CONSTRAINTS.

Method Description Resource manifest
child_created_f Child Created Pt_SET_CHILD_CREATED_F
child_settingresource_f Child Setting Resource Pt_SET_CHILD_SETTINGRESOURCE_F
child_gettingresource_f Child Getting Resource Pt_SET_CHILD_GETTINGRESOURCE_F
child_realizing_f Child Realizing Pt_SET_CHILD_REALIZING_F
child_realized_f Child Realized Pt_SET_CHILD_REALIZED_F
child_unrealizing_f Child Unrealizing Pt_SET_CHILD_UNREALIZING_F
child_unrealized_f Child Unrealized Pt_SET_CHILD_UNREALIZED_F
child_destroyed_f Child Destroyed Pt_SET_CHILD_DESTROYED_F
child_move_resize_f Child Moved/Resized Pt_SET_CHILD_MOVED_RESIZED_F
child_getting_focus_f Child Getting Focus Pt_SET_CHILD_GETTING_FOCUS_F
child_losing_focus_f Child Losing Focus Pt_SET_CHILD_LOSING_FOCUS_F
child_redirect_f Child Redirection Pt_SET_CHILD_REDIRECT_F

For convenience, the Photon widget building library lets you call these methods from a superclass using the provided functions. For more information, see the Widget Building Library API chapter.

Child Created method

Type: Inherited
Constraint flag: Pt_CHILD_CREATED

Called whenever a new child is being created in this widget or one of its subordinate widgets. Here's an excerpt taken from the code for PtGroup:

static void child_created( PtWidget_t *widget,
                           PtWidget_t *child )
{
  PtCallback_t callback = { group_exclusive_callback,
                            NULL };
  PtGroupUnion_t *group = (PtGroupUnion_t *)widget;
  PtArg_t argt[2];
  int n = 0;

  callback.data = widget;

  if( group->basic.activate ) {
     PtSetArg( &argt[n++], Pt_CB_ACTIVATE,
               &group->basic.activate->cb, 0 );
  }
  if( group->group.group_flags & Pt_GROUP_EXCLUSIVE ) {
     PtSetArg( &argt[n++], Pt_CB_ACTIVATE, &callback, 0 );
  }
  if( n )
     PtSetResources( child, n, argt );
}

Child Realizing method

Type: Inherited
Constraint flag: Pt_CHILD_REALIZING

Called whenever a child is in the process of being realized below this container in the hierarchy.

Child Realized method

Type: Inherited
Constraint flag: Pt_CHILD_REALIZED

Called whenever a child is realized below this container in the hierarchy.

Child Moved/Resized method

Type: Inherited
Constraint flag: Pt_CHILD_MOVED_RESIZED

Called whenever a child is moved or resized below this container in the hierarchy.

Child Unrealizing method

Type: Inherited
Constraint flag: Pt_CHILD_UNREALIZING

Called whenever a child is in the process of being unrealized below this container in the hierarchy.

Child Unrealized method

Type: Inherited
Constraint flag: Pt_CHILD_UNREALIZED

Called whenever a child is unrealized below this container in the hierarchy.

Child Destroyed method

Type: Inherited
Constraint flag: Pt_CHILD_DESTROYED

Called whenever a direct child of this widget or a direct child of one of its subordinates is destroyed.

Child Setting Resource method

Type: Inherited
Constraint flag: Pt_CHILD_SETTING_RESOURCE

Called whenever a resource is being set on a direct child of this widget or one of its subordinates.

Child Getting Resource method

Type: Inherited
Constraint flag: Pt_CHILD_GETTING_RESOURCE

Called whenever a resource is being retrieved from a direct child of this widget or one of its subordinates.

Child Getting Focus method

Type: Inherited
Constraint flag: Pt_CHILD_GETTING_FOCUS

Called whenever a child of this widget or one of its subordinates is about to be given focus.

Child Losing Focus method

Type: Inherited
Constraint flag: Pt_CHILD_LOSING_FOCUS

Called whenever a child of this widget or one of its subordinates is about to lose focus.

Child Redirection method

Type: Inherited
Constraint flag: Pt_CHILD_REDIRECTOR

Container widgets provide a mechanism for redirecting child widgets as they're added to a container. You can use this mechanism to prevent certain widget classes from being added as direct children or redirect those children to other containers within the container (this feature is typically used by PtCompound widgets).

The child-redirector function is specified in the class-creation function and indicates where children should be attached in the widget hierarchy. Redirection is achieved by setting the child_redirect_f member of the Pt_container_widget_class structure to the child-redirector function.

The arguments to the child-redirector function are a widget pointer and the class type of the widget being added to the container. Using this information, the child-redirector function determines whether to accept the widget into the container or redirect the widget to another container, such as the parent of the container widget.

A good example is the PtMenuBar widget. This widget accepts only PtMenuButton widgets. Its class-creation function includes this:

{ Pt_SET_CHILD_REDIRECT_F, (long)menubar_redirect },

The menubar_redirect() function would look like this:

static PtWidget_t *menubar_redirect(
                       PtWidget_t *menubar,
                       PtWidgetClassRef_t *cref )
{
  if (cref != PtMenuButton )
     return menubar->parent;
  return menubar;
}

The widget returned by the function becomes the parent for the widget being added.

You can use PtCompoundRedirect() as the default child-redirector function for any containers that won't accept other widgets. PtCompoundRedirect() redirects child creation to the parent of the container widget. This causes a new widget to become a sibling of the container widget rather than a child of the container widget.


Note: If you set up a child-redirector function in your widget class, Pt_CHILD_REDIRECTOR is set. When you create an instance, this bit is automatically turned off before Defaults method is called. It's turned back on after the Defaults chaining is complete. This lets you create subordinate children in your Defaults method without having to clear and set this bit yourself.

Fundamental methods

When using container widgets, you must provide a number of container-specific processes within the fundamental methods in addition to those common to every widget.

Defaults method

To enable the container-constraint methods, set the appropriate bits in container->flags. You can turn on all bits as follows:

ctnr->flags |= Pt_CONTAINER_CONSTRAINT_BITS;

Only the constraint methods whose bits are on are invoked. If you turn a bit on and the corresponding constraint method is undefined, the bit is ignored. For example:

ctnr->flags |= Pt_CHILD_CREATED;

For each bit set in the flag, you should provide a constraint method in the argument list of the class-creation function:

static PtArg_t args[] = {
  …
  { Pt_SET_CHILD_CREATED_F, child_created },
  …
  };

The child_created() function should modify the child or container as required to suit the situation.


Note: The Common User Access (CUA) mechanism built into the Photon library automatically passes focus around the application. If you want to prevent widgets inside the container from getting focus, set the Pt_BLOCK_CUA_FOCUS flag. The PtMenuBar widget does this (i.e. you can't press the Tab key to move the focus from a widget outside a menubar to a widget inside a menubar).

Extent method

This method is responsible for determining the “screen real estate” of a widget. Anchoring is applied in this method. Widgets subclassed under PtContainer normally call the Extent method of PtContainer to do the final extent calculation and anchor the widget according to its anchor flags. When a widget's extent has been calculated, its widget-> extent_valid flag should be set to Pt_TRUE.

The following example demonstrates how to handle anchoring if you choose not to let the PtContainer class do it for you (see also PtSuperClassExtent()):

mycontainerwidget_extent( PtWidget_t *widget )
{
  PtWidget_t *widget
  PhRect_t canvas, old_extent;
  PhArea_t area;

  // Store the old size for comparison later.
  old_extent = widget->extent;
  PhRectToArea( &old_extent, &area );

  if( PtResizePolicy( widget ) )
  {
     PhRect_t render;
     render.lr.x = render.lr.y = SHRT_MIN;
     render.ul.x = render.ul.y = SHRT_MAX;
     PtChildBoundingBox( widget, &canvas, &render );
     PhTranslateRect( &render, &canvas.ul );
     PtAttemptResize( widget, &render, &canvas );
  }
  PtSuperClassExtent( PtBasic, widget );
  widget->extent_valid = Pt_TRUE;

  // Containers must anchor their children. Flux to
  // minimize flicker.

  PtStartFlux( widget );
  if( widget->parent && widget->parent->extent_valid
      && (ctnr->anchor_flags & Pt_IS_ANCHORED ) &&
      !( widget->class_rec->flags &
      (Pt_DISJOINT|Pt_DISCONTINUOUS) ) )
    PtAnchorWidget( widget );
  else
    if( !(ctnr->anchor_flags & Pt_ANCHORS_LOCKED ) &&
        memcmp( &widget->area.size, &area.size,
                sizeof(PhDim_t) ) )
     for( wlp = ctnr->ctnrs; wlp; wlp = next )
     {
       next = wlp->next;
       if( ((PtContainerWidget_t *)wlp->widget)->anchor_flags &
                      Pt_IS_ANCHORED )
         PtAnchorWidget( wlp->widget );
     }
  PtEndFlux( widget );

  // If the size changes, accommodate the new size.
  if( memcmp( &old_extent, &widget->extent,
              sizeof( old_extent ) ) )
  {
     if( ( widget->flags & Pt_REALIZED ) && (widget->rid) )
       PtCoreChangeRegion( Ph_REGION_ORIGIN | Ph_REGION_RECT,
                           widget );
     if( !(ctnr->anchor_flags & Pt_ANCHORS_LOCKED)
         && memcmp( &widget->area.size,
         &area.size, sizeof(PhDim_t) ) )
       PtInvokeResizeCallbacks( widget );
  }
}

Realization method

If your container creates any subordinate widgets with the Pt_DELAY_REALIZE flag set, they can be realized in the Realization method.

Compound widget anatomy

This section applies to creating PtCompound class widgets:

PtWidgetPtBasicPtContainerPtCompoundMyCompound

The PtCompound superclass supports extended functionality provided by “exported” subordinate children. The export mechanism allows users to set/get the resources of subordinate children through a compound widget without defining any of these resources in the compound widget. Compound widgets can modify or block the default resources inherited by their exported subordinate children.


Note: Widgets don't have to be a subclass of PtCompound to export subordinate widgets. However, because PtContainer provides powerful child-constraint and child-redirector mechanisms, we recommend that if you're building a widget that creates subordinate children, you should subclass that widget to PtContainer (if not PtCompound). This greatly simplifies the management of subordinate children. It also makes the widget easier to use since new resources don't have to be learned.

To achieve this exporting mechanism, the compound class extends the PtContainer widget class definition as shown below:

typedef struct Pt_compound_class {
    PtContainerClass_t  container;
    unsigned short            num_subordinates;
    unsigned short            *subordinates;
    unsigned short            num_blocked_resources;
    unsigned long             *blocked_resources;
} PtCompoundClass_t;

The members of this structure are:

num_subordinates (Resource manifest Pt_SET_NUM_SUBORDINATES)
The number of subordinate widgets (in the subordinate offset array) to be exported. This value must be supplied when defining an array of offsets.
subordinates (Resource manifest Pt_SET_SUBORDINATES)
An array of offsets to widget pointers to subordinate widgets. Every widget defined in the array of offsets must be created in the Defaults method of the compound widget.
num_blocked_resources (Resource manifest Pt_SET_NUM_BLOCKED_RESOURCES)
The number of resources in the array of blocked resources.
blocked_resources (Resource manifest Pt_SET_BLOCKED_RESOURCES)
An array of resources to be blocked.

Here's a sample class-creation function from PtComboBox:

//
// PtComboBox class creation function
//

static PtWidgetClass_t *PtCreateComboBoxClass( void )
{
    static const PtResourceRec_t resources[] = {

        { Pt_ARG_CURSOR_TYPE, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtWidget_t, cursor_type ) },

        { Pt_ARG_CURSOR_COLOR, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtWidget_t, cursor_color ) },

        { Pt_ARG_BORDER_WIDTH, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, border_width ) },

        { Pt_ARG_FLAGS, combobox_modify, get_text_res,
                Pt_ARG_IS_FLAGS( PtWidget_t, flags ) },

        { Pt_ARG_TOP_BORDER_COLOR, set_border_color, 0,
                Pt_ARG_IS_NUMBER( PtBasicWidget_t, top_border_color ) },

        { Pt_ARG_HORIZONTAL_ALIGNMENT, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtLabelWidget_t, h_alignment ) },
        { Pt_ARG_VERTICAL_ALIGNMENT, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtLabelWidget_t, v_alignment ) },
        { Pt_ARG_MARGIN_HEIGHT, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtBasicWidget_t, margin_height ) },
        { Pt_ARG_MARGIN_WIDTH, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtBasicWidget_t, margin_width ) },
        { Pt_ARG_MARGIN_TOP, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtLabelWidget_t, margin_top ) },
        { Pt_ARG_MARGIN_BOTTOM, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtLabelWidget_t, margin_bottom ) },
        { Pt_ARG_MARGIN_LEFT, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtLabelWidget_t, margin_left ) },
        { Pt_ARG_MARGIN_RIGHT, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtLabelWidget_t, margin_right ) },

        { Pt_ARG_COLOR, set_all_same_res, get_text_res,
                Pt_ARG_IS_NUMBER( PtComboBoxUnion_t, basic.color ) },

        { Pt_ARG_FILL_COLOR, combobox_modify, 0,
                Pt_ARG_IS_COLOR( PtBasicWidget_t, fill_color ) },

        { Pt_ARG_BOT_BORDER_COLOR, set_border_color, 0,
                Pt_ARG_IS_NUMBER( PtBasicWidget_t, bot_border_color ) },
        { Pt_ARG_TEXT_STRING, set_text_res, get_text_res },
        { Pt_ARG_TEXT_FLAGS, combobox_modify, get_text_res,
                Pt_ARG_IS_FLAGS( PtComboBoxWidget_t, flags ) },
        { Pt_ARG_CBOX_FLAGS, combobox_modify, 0,
                Pt_ARG_IS_FLAGS( PtComboBoxWidget_t, flags ) },
        { Pt_ARG_CBOX_SEL_ITEM, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, sel_item ) },
        { Pt_ARG_ITEMS, set_list_res, get_list_res },
        { Pt_ARG_CBOX_ITEMS, set_list_res, get_list_res },
        { Pt_ARG_CBOX_SPACING, set_list_res, get_list_res },
        { Pt_ARG_CBOX_VISIBLE_COUNT, set_list_res, get_list_res },
        { Pt_ARG_CBOX_BUTTON_WIDTH, combobox_modify, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, butn_size.w ) },
        { Pt_ARG_CBOX_MAX_LENGTH, set_text_res, get_text_res },
        { Pt_ARG_CBOX_CURSOR_POSITION, set_text_res, get_text_res },
        { Pt_ARG_CBOX_SELECTION_FILL_COLOR, set_list_res, get_list_res },
        { Pt_ARG_CBOX_EDIT_MASK, set_text_res, get_text_res },
        { Pt_ARG_CBOX_SELECTION_TEXT_COLOR, set_list_res, get_list_res },
        { Pt_ARG_CBOX_TEXT_FONT, set_text_res, get_text_res },
        { Pt_ARG_CBOX_TEXT_STRING, set_text_res, get_text_res },
        { Pt_ARG_CBOX_TEXT_FILL_COLOR, set_text_res, get_text_res },
        { Pt_CB_SELECTION, (long) Pt_CHANGE_INVISIBLE, 0,
                Pt_ARG_IS_IN_CB_LISTS( PtCallback_t ) },
        { Pt_CB_LIST_INPUT, (long) Pt_CHANGE_INVISIBLE, 0,
                Pt_ARG_IS_IN_CB_LISTS( PtCallback_t ) },
        { Pt_CB_CBOX_ACTIVATE, (long) Pt_CHANGE_INVISIBLE, 0,
                Pt_ARG_IS_IN_CB_LISTS( PtCallback_t ) },
        { Pt_ARG_CBOX_BUTTON_BORDER_WIDTH, (long) Pt_CHANGE_RESIZE_REDRAW, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, butn_border_width ) },
        { Pt_ARG_CBOX_BUTTON_TOP_BORDER_COLOR, (long) Pt_CHANGE_REDRAW, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, butn_top_border_color ) },
        { Pt_ARG_CBOX_BUTTON_BOT_BORDER_COLOR, (long) Pt_CHANGE_REDRAW, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, butn_bot_border_color ) },
        { Pt_ARG_CBOX_BUTTON_COLOR, (long) Pt_CHANGE_REDRAW, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, butn_color ) },
        { Pt_ARG_CBOX_MAX_VISIBLE_COUNT, (long) Pt_CHANGE_INVISIBLE, 0,
                Pt_ARG_IS_NUMBER( PtComboBoxWidget_t, max_visible ) },
        { Pt_CB_CBOX_CLOSE, (long) Pt_CHANGE_INVISIBLE, 0,
                Pt_ARG_IS_IN_CB_LISTS( PtCallback_t ) },
    };

    static const unsigned short subs[] =
    {
        offsetof( PtComboBoxWidget_t, text_wgt ),
        offsetof( PtComboBoxWidget_t, list_wgt ),
    };

    static const PtClassRawCallback_t callback = {
        Ph_EV_KEY, combobox_callback
    };

    static const unsigned long blocked[] = {
        Pt_ARG_SELECTION_MODE,
        Pt_ARG_SEL_INDEXES,
        Pt_ARG_COLUMNS,
    };

    static const PtWidgetAction_t actions[] =
    {
        { Ph_EV_BUT_PRESS | Ph_EV_BUT_RELEASE | Ph_EV_BUT_REPEAT,
          { combobox_but_action } },
    };

    static const PtClassArg_t args[] = {
        { Pt_SET_DFLTS_F, (void*) combobox_dflts },
        { Pt_SET_RAW_CALLBACKS, (void*) &callback },
        { Pt_SET_RESOURCES, (void*) resources },
        { Pt_SET_EXTENT_F, (void*) combobox_extent },

        { Pt_SET_CONNECT_F, (void*) combobox_init },
        { Pt_SET_REALIZED_F, (void*) combobox_realized },
        { Pt_SET_SUBORDINATES, (void*) subs },
        { Pt_SET_BLOCKED_RESOURCES, (void*) blocked },
        { Pt_SET_CHILD_MOVED_RESIZED_F, (void*) combobox_child_resize },
        { Pt_SET_GOT_FOCUS_F, (void*) combobox_got_focus },
        { Pt_SET_LOST_FOCUS_F, (void*) combobox_lost_focus },
        { Pt_SET_DESCRIPTION, (void*) "PtComboBox" },
        { Pt_SET_CHILD_REDIRECT_F, (void*) PtCompoundRedirect },
        { Pt_SET_ACTIONS, (void*) actions },
        { Pt_SET_DRAW_F, (void*) combobox_draw },
        { Pt_SET_CALC_BORDER_F, (void*) combobox_calc_border },
        { Pt_SET_VERSION, 200},
        { Pt_SET_STATE_LEN, sizeof( PtComboBoxWidget_t ) },
        { Pt_SET_NUM_RESOURCES, (sizeof( resources )
          /sizeof( resources[0] )) },
        { Pt_SET_NUM_SUBORDINATES, (sizeof( subs )
          / sizeof( subs[0] )) },
        { Pt_SET_NUM_BLOCKED_RESOURCES, (sizeof( blocked )
          / sizeof( blocked[0] )) },
        { Pt_SET_NUM_ACTIONS, (sizeof( actions )
          /sizeof( actions[0] )) },
    };
    return( PtComboBox->wclass = PtCreateWidgetClass(
            PtCompound, 0, sizeof( args )/sizeof( args[0] ),
            args ) );
}

Note:
  • The example code above is similar but not identical to the Photon library code; some optimizations have been removed for clarity.
  • In the example code shown above, the PtComboBox widget creates subordinate widgets and sets the pointers text_wgt and list_wgt in the Defaults method to the appropriate widget pointer.

In the following example, the widget class MyCompound creates subordinate widgets PtText, PtProgress, PtScrollArea, and a few PtLabels. The PtText, PtProgress, and PtScrollArea widgets are exported, but the PtLabel widgets aren't. All resources are applied to the subordinate widgets. No extra code is required for MyCompound:

  …
  n = 0;
  PtSetArg( &argt[n], Pt_ARG_POS, &pos, 0 ); n++;
  PtSetArg( &argt[n], Pt_ARG_TEXT_STRING, "A text field",
            0 ); n++;
  PtSetArg( &argt[n], Pt_ARG_SCROLL_AREA_MAX_X, 1000, 0 ); n++;
  PtSetArg( &argt[n], Pt_CB_SCROLLED_X, &callback, 1 ); n++;
  PtSetResources( MyCompound, n, argt );
  …
  n = 0;

To get the resources afterwards:

  …
  n = 0;
  n = 0;
  PtSetArg( &argt[n], Pt_ARG_POS, &phpoint_ptr, 0 ); n++;
  PtSetArg( &argt[n], Pt_ARG_TEXT_STRING, &char_ptr, 0 ); n++;
  PtSetArg( &argt[n], Pt_ARG_SCROLL_AREA_MAX_X, &short_ptr,
            0 ); n++;
  PtGetResources( MyCompound, n, argt );
  …
  n = 0;

When the Pt_CB_SCROLLED_X callback is invoked, the first parameter widget is a pointer to MyCompound, not to the subordinate. The PtCompound class also makes it easy to block specific resources from hitting any subordinate.

What happens when you export two or more subordinate widgets supporting the same resource? For example, both a button and a text field accept the resource Pt_ARG_TEXT_STRING; if they're exported with no supporting code, then setting the resource on the compound widget will set the resource on both subordinates.

When getting the resource, the value is retrieved from the first exported widget supporting the resource. If you don't want this to happen, set up a resource-redirector function at the end of the compound widget's Defaults method.

You can have a compound widget override the resource (see Pt_ARG_TEXT_STRING in the example above) by defining it, then using the mod_f() function (named in the mod_f field member of the PtResourceRec_t structure) to apply the resource to the appropriate subordinate.

Any resources defined for a subclass of a PtCompound widget won't automatically be applied to a subordinate widget, exported or otherwise. If you define resource-redirector functions for all user-modified resources of subordinates, consider subclassing your widget below PtContainer instead of PtCompound.

Blocking resources

The exporting mechanism provides a resource-blocking mechanism. You can selectively block resources, making them inaccessible to subordinate widgets.

Suppose you've built a compound widget comprising an exported PtSlider widget, and you want the slider thumb to stay the same size always — blocking access to the Pt_ARG_SLIDER_SIZE resource would prevent the slider from being resized.

You may also find that you have more than one occurrence of a single type of widget as subordinate widget. For example, your widget might have a vertical and a horizontal scrollbar. In this case, you should block Pt_ARG_SCROLL_POSITION and create two new resources: Pt_ARG_X_SCROLL_POSITION and Pt_ARG_Y_SCROLL_POSITION. The Set Resources method for each would set the Pt_ARG_SCROLL_POSITION resource of the appropriate subordinate scrollbar widget.

Compound redirection

The PtCompound class provides a standard child-redirector function called PtCompoundRedirect(). This function prevents the user from creating widgets in your compound widget and redirects the user's widget to another container instead. To target another widget, redirect child creation in the class-creation function:

{ Pt_SET_CHILD_REDIRECT_F, (long)PtCompoundRedirect },

In the template for the PtSampCompound widget, new children are redirected to one of the procreated subordinate widgets, scroll_area, by calling PtValidParent() (described in the Photon Library Reference):

PtWidget_t * PtSampContainerRedirect( PtWidget_t *widget )
{
  PtWidget_t *parent;

  if( ( parent =
        PtValidParent( samp->scroll_area,
                       widget->class_ref ) ) == widget )
     return PtWidgetParent( widget );
  return( parent );

  /*
  * Returning samp->scroll_area would allow the child
  * to be created as a direct child of samp->scroll_area.
  * This may be undesirable, as scroll_area is a container
  * widget that redirects its children.
  */
}

PtValidParent() honors any child-redirector functions existing in subordinate widgets and their subordinates. PtValidParent() should be used only to redirect parentage to a subordinate child. This prevents infinite loops when the child's redirector function returns control.

Fundamental methods

Compound widgets require a few fundamental methods in addition to those common to every widget.

Defaults method

For compound widgets, all exported subordinate widgets must be created in the Defaults method. To export widgets, set the following in the class-creation function:

Pt_SET_SUBORDINATES
An array of offsets to the subordinate widget pointers.
Pt_SET_NUM_SUBORDINATES
The number of entries in the array.

Resources of subordinate widgets may be overridden, overloaded, or blocked. Subordinate widgets can be created but not realized in the Defaults method.

For the container-constraint mechanisms to work correctly, the Pt_PROCREATED flag has to be set in each subordinate widget, and the Pt_ARG_DATA ( not Pt_ARG_USER_DATA) resource has to point back to the parent widget.


Note:
  • For each subordinate widget, you must set the Pt_ARG_DATA resource before setting the Pt_PROCREATED flag, or a SIGSEGV error will result.
  • Your custom widget shouldn't set its own Pt_ARG_DATA resource because this resource is used internally by the Photon libraries. It is safe to set it in your widget's subordinate children.

Here's an example from PtComboBox:

static void combobox_dflts( PtWidget_t *widget )
{
  PtComboBoxUnion_t   *combobox = (PtComboBoxUnion_t *)widget;
  PtArg_t       args[13];
  int         n = 0;
  PtCallback_t    callback;
  PtRawCallback_t   raw_cb;

  combobox->core.flags |= Pt_HIGHLIGHTED | Pt_SET;
  combobox->core.flags &= ~Pt_GETS_FOCUS;
  combobox->core.resize_flags = Pt_RESIZE_XY_ALWAYS;
  combobox->basic.fill_color = Pg_LGREY;
  combobox->basic.margin_height = 0;
  combobox->basic.margin_width = 0;
  combobox->basic.flags = Pt_STATIC_GRADIENT | Pt_ALL_ETCHES | Pt_ALL_OUTLINES;
  widget->border_width = combobox->combobox.border_width = 2;
  combobox->combobox.butn_size.w = 13;
  combobox->combobox.butn_border_width = 2;
  combobox->combobox.butn_bot_border_color = Pg_DGREY;
  combobox->combobox.butn_top_border_color = Pg_WHITE;
  combobox->combobox.butn_color = Pg_GREY;
  combobox->combobox.flags = Pt_COMBOBOX_DAMAGE_BUTTON;
  callback.event_f = combobox_text_callback;
  callback.data = (void*)widget;

  n = 0;
  PtSetArg( &args[n], Pt_ARG_BEVEL_WIDTH, 0, 0 );
  n++;
  PtSetArg( &args[n], Pt_ARG_MARGIN_HEIGHT, 2, 0 );
  n++;
  PtSetArg( &args[n], Pt_ARG_MARGIN_WIDTH, 2, 0 );
  n++;
  PtSetArg( &args[n], Pt_ARG_DATA, &widget, sizeof( widget ) );
  n++;
  PtSetArg( &args[n], Pt_ARG_FLAGS, Pt_PROCREATED, Pt_PROCREATED);
  n++;
  PtSetArg( &args[n], Pt_CB_MODIFY_VERIFY, &callback, 1);
  n++;
  PtSetArg( &args[n], Pt_CB_MOTION_VERIFY, &callback,1);
  n++;
  PtSetArg( &args[n], Pt_CB_ACTIVATE, &callback, 1);
  n++;
  PtSetArg( &args[n], Pt_CB_GOT_FOCUS, &callback, 1);
  n++;
  PtSetArg( &args[n], Pt_CB_LOST_FOCUS, &callback, 1);
  n++;
  PtSetArg( &args[n], Pt_ARG_RESIZE_FLAGS, Pt_RESIZE_Y_AS_REQUIRED,
    Pt_RESIZE_XY_BITS );
  n++;
  PtSetArg( &args[n], Pt_ARG_BASIC_FLAGS, Pt_TOP_LEFT_INLINE |
    Pt_RIGHT_OUTLINE | Pt_FLAT_FILL, ~0 );
  n++;
  combobox->combobox.text_wgt =
    PtCreateWidget( PtText, widget, n, args );

  n = 0;
  callback.event_f = combobox_list_callback;
  callback.data = (void*)widget;
  raw_cb.event_f = combobox_action;
  raw_cb.event_mask = Ph_EV_BOUNDARY;
  raw_cb.data = combobox;
  PtSetArg( &args[n], Pt_ARG_BEVEL_WIDTH, 1, 0 );
  n++;
  PtSetArg( &args[n], Pt_ARG_SCROLLBAR_WIDTH,
            combobox->combobox.butn_size.w +3, 0 );
  n++;
  PtSetArg( &args[n], Pt_ARG_SEL_MODE, Pt_SELECTION_MODE_SINGLE
            | Pt_SELECTION_MODE_AUTO, 0 );
  n++;
  PtSetArg( &args[n], Pt_ARG_FLAGS, Pt_PROCREATED | Pt_DELAY_REALIZE,
            Pt_GETS_FOCUS | Pt_ETCH_HIGHLIGHT
            | Pt_PROCREATED | Pt_DELAY_REALIZE | Pt_ETCH_HIGHLIGHT);
  n++;
  PtSetArg( &args[n], Pt_CB_SELECTION, &callback, 1 );
  n++;
  PtSetArg( &args[n], Pt_CB_LIST_INPUT, &callback, 1 );
  n++;
  PtSetArg( &args[n], Pt_CB_RAW, &raw_cb, 1 );
  n++;
  PtSetArg( &args[n], Pt_ARG_DATA, &widget,
            sizeof( widget ) );
  n++;
  PtSetArg( &args[n++], Pt_ARG_BASIC_FLAGS, 0, Pt_ALL_ETCHES
            | Pt_ALL_INLINES );
  combobox->combobox.list_wgt = PtCreateWidget( PtList, widget,
                                                   n, args );

  n = 0;
  callback.event_f = combobox_action;
  callback.data = (void*)widget;
  PtSetArg( &args[n], Pt_CB_ARM, &callback, 1 );
  n++;
  PtSetArg( &args[n], Pt_CB_REPEAT, &callback, 1 );
  n++;
  PtSetResources( combobox->combobox.text_wgt, n, args );

  combobox->container.flags |= Pt_AUTO_EXTENT | Pt_CHILD_MOVED_RESIZED;
}

Realization method

The Realization method is the last class-member function called before the Draw method. It's where you realize subordinate widgets created in the Defaults method. Here's an example from PtComboBox:

static int combobox_realized( PtWidget_t *widget )
{
  PtComboBoxWidget_t *combobox = (PtComboBoxWidget_t *)widget;
  long flags;

  if ( combobox->flags & Pt_COMBOBOX_STATIC )
     PtRealizeWidget( combobox->list_wgt );
  else {
     PtRealizeWidget( combobox->butn_wgt );
  }
  return Pt_CONTINUE;
}

Get Resources and Set Resources methods

To date, the PtCompound widget class is the only class to redefine the Get Resources and Set Resources methods, which are used internally. You don't have to set a function for this method unless you intend to duplicate the behavior of PtCompound.

Destruction method

The Destruction method must destroy any redirected callback lists of exported subordinates having callback resources set on them.