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.
There are two steps to defining the resources of your widget class (aside from deciding what they should be in the first place):
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 )
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.
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;
The value (manifest) to which this record is connected through a widget instance member. For example, Pt_ARG_FLAGS.
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:
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:
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.
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. |
arg_value | C type of member1 |
---|---|
Pt_ARG_IS_NUMBER(wgt, member1) | char, short, or long (signed or unsigned) |
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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 */ }
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.
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 ]; };
Let's look at the PtWidgetClass_t structure one member at time:
These are the valid flag bits for the flags member:
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.
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" },
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.
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] ) }, };
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.
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.
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; }
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:
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.
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 ); }
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, ®ion, &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, ®ion, &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.
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.”
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( );
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:
For more information about these functions, see the Photon Library Reference.
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; }
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; }
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”).
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”).
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; }
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; }
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 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.
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) ); }
The example code above is similar but not identical to the Photon library code; some optimizations have been removed for clarity. |
This section applies to creating PtContainer class widgets:
PtWidget → PtBasic → PtContainer → MyContainer
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.
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 ); }
Type: Inherited
Constraint flag: Pt_CHILD_REALIZING
Called whenever a child is in the process of being realized below this container in the hierarchy.
Type: Inherited
Constraint flag: Pt_CHILD_REALIZED
Called whenever a child is realized below this container in the hierarchy.
Type: Inherited
Constraint flag: Pt_CHILD_MOVED_RESIZED
Called whenever a child is moved or resized below this container in the hierarchy.
Type: Inherited
Constraint flag: Pt_CHILD_UNREALIZING
Called whenever a child is in the process of being unrealized below this container in the hierarchy.
Type: Inherited
Constraint flag: Pt_CHILD_UNREALIZED
Called whenever a child is unrealized below this container in the hierarchy.
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.
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.
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.
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.
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.
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.
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. |
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.
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.
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). |
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 ); } }
If your container creates any subordinate widgets with the Pt_DELAY_REALIZE flag set, they can be realized in the Realization method.
This section applies to creating PtCompound class widgets:
PtWidget → PtBasic → PtContainer → PtCompound → MyCompound
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.
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:
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 ) ); }
|
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.
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.
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.
Compound widgets require a few fundamental methods in addition to those common to every widget.
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:
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.
|
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; }
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; }
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.
The Destruction method must destroy any redirected callback lists of exported subordinates having callback resources set on them.