Programmer's Reference

Example: a primitive extended widget

Two example extended widgets are provided in the next two sections. The first, below, is a subclass of CwExtendedPrimitive, and the second, on page Example: a composite extended widget, is a subclass of CwExtendedComposite. The prefix 'Cew' is used to differentiate these new widgets from basic widgets. For simplicity, the examples do not include robust error-checking, nor does each widget provide a complete set of resources.

The CewEntryField widget, shown below, has a label on the left and a text box on the right. It allows a user to enter text into its text box, and it invokes a valueChanged callback if a new value is present when the user either hits the tab key or clicks on a different widget.

The extended widget is implemented using a CwForm as the primary widget with CwLabel and CwText children. A losingFocus callback on the CwText enables the widget to test entered data, and possibly call any registered valueChanged callbacks.
Extended widget

CwExtendedPrimitive subclass: #CewEntryField
  instanceVariableNames: 'label value valueChangedCallback labelWidget textWidget '
  classVariableNames: ''
  poolDictionaries: ''
 
createPrimaryWidget: theName parent: parent argBlock: argBlock
"Private - Create and answer the basic widget that is the root of the
 widget hierarchy for the receiver's widget system."
 
   ^self parent
      createForm: theName, 'Form'
      argBlock: argBlock
 
createWidgetSystem
"Private - Create the children of the receiver's primary widget which form the
 widget hierarchy."
   | pw |
 
   pw := self primaryWidget.
   self labelWidget:
      (pw
         createLabel: pw name , 'Label'
         argBlock: [:w | w labelString: self label])
            manageChild.
 
   self textWidget:
      (pw
         createText: pw name , 'Text'
         argBlock: [:w |
            w
               borderWidth: 1;
               value: self value])
         manageChild.
   self textWidget
      addCallback: XmNlosingFocusCallback
      receiver: self
      selector: #losingFocus:clientData:callData:
      clientData: nil.
 
   "Add form attachments for the two children."
   self labelWidget
      setValuesBlock: [:w |
         w
            topAttachment: XmATTACHFORM;
            leftAttachment: XmATTACHFORM;
            rightAttachment: XmATTACHNONE;
            bottomAttachment: XmATTACHFORM].
 
   self textWidget
      setValuesBlock: [:w |
         w
            topAttachment: XmATTACHFORM;
            leftAttachment: XmATTACHWIDGET;
            leftWidget: self labelWidget;
            rightAttachment: XmATTACHFORM;
            bottomAttachment: XmATTACHFORM].
 
initializeResources
"Private - Set the default extended widget resource values. This is sent 
 during create with isCreated set to false. All extended resource 
 variables should be initialized to default values here."
   label := String new.
   value := String new.
 
initializeAfterCreate
"Private - Perform any widget-specific post-create initialization."
   self primaryWidget
      horizontalSpacing: 4;
      verticalSpacing: 4;
      borderWidth: 1.
 
"Resources that involve the children of the primary widget have to be set
 after the children are created."
   self labelWidget labelString: label.
   self textWidget value: value.
 
label
"Answer the value of the label resource.
 Resource type: String
 Default setting: ''
 Resource access: CSG
 Description:
    Specifies the string for the CewEntryField's label.
    This label is to the left of the CewEntryField's text box."
   ^label
 
label: resourceValue
"Set the value of the label resource to resourceValue.
 Resource type: String
 Default setting: ''
 Resource access: CSG
 Description:
    Specifies the string for the CewEntryField's label.
    This label is to the left of the CewEntryField's text box."
   label := resourceValue.
   self isCreated
      ifTrue: [labelWidget labelString: resourceValue]
 
value
"Answer the value of the value resource.
 Resource type: String
 Default setting: '
 Resource access: CSG
 Description:
   Specifies the string for the CewEntryField's value.
   This value is displayed in the CewEntryField's text box if
   set using the #value: message, or if a valid string followed
   by a tab key is entered by the user.
   Note that while the user is typing, the string displayed in the
   text box might not be the same as the CewEntryField's value."
 
   ^value
 
value: resourceValue
"Set the value of the value resource to resourceValue.
 Resource type: String
 Default setting: '
 Resource access: CSG
 Description:
   Specifies the string for the CewEntryField's value.
   This value is displayed in the CewEntryField's text box if
   set using the #value: message, or if a valid string followed
   by a tab key is entered by the user.
   Note that while the user is typing, the string displayed in the
   text box might not be the same as the CewEntryField's value."
 
   value := resourceValue.
   self isCreated
      ifTrue: [textWidget value: resourceValue]
 
valueChangedCallback
"Private - Answer the value of valueChangedCallback."
   valueChangedCallback isNil
      ifTrue: [self valueChangedCallback: OrderedCollection new].
   ^valueChangedCallback
 
valueChangedCallback: resourceValue
"Set the value of the XmNvalueChangedCallback resource to resourceValue.
 Resource type: OrderedCollection of CwCallbackRec
 Default setting: OrderedCollection new
 Resource access: C
 Callback reason: XmCRVALUECHANGED
 Calldata structure: CwValueCallbackData
 Description:
   Specifies the list of callbacks that is called when the value
   of the CewEntryField has changed. The reason sent by the
   callback is XmCRVALUECHANGED. The structure returned
   by this callback is CwValueCallbackData."
 
   valueChangedCallback := resourceValue.
 
labelWidget
"Private - Answer the value of labelWidget."
   ^labelWidget
 
labelWidget: aCwLabel
"Private - Set the value of labelWidget to aCwLabel."
 
   labelWidget := aCwLabel.
 
textWidget
"Private - Answer the value of textWidget."
   ^textWidget
 
textWidget: aCwText
"Private - Set the value of textWidget to aCwText."
 
   textWidget := aCwText.
 
losingFocus: widget clientData: clientData callData: callData
"Private - Process a losing focus callback for the primary widget."
   | newValue |
 
   newValue := self textWidget value.
 
"If the new value is different, invoke the entryField widget's 
 valueChanged callback."
   self value = newValue
      ifTrue: [
         self
            value: newValue;
            callCallbacks: XmNvalueChangedCallback
            callData: (CwValueCallbackData new value: newValue)].

Using the CewEntryField primitive extended widget

The following code creates a CewEntryField instance, sets its name and label resources inside the create argBlock, and hooks a valueChanged callback to it. The new widget is shown in the diagram at the beginning of this section, on page Example: a primitive extended widget.

| shell entryField |
 
   shell := CwTopLevelShell
      createApplicationShell: 'CewEntryField Test'
      argBlock: nil.
 
   entryField := CewEntryField
      createManagedWidget: 'entryField'
      parent: shell
      argBlock: [:w |
         w
            label: 'Name :';
            value: 'Your name here'].
   entryField
      addCallback: XmNvalueChangedCallback
      receiver: self
      selector: #valueChanged:clientData:callData:
      clientData: nil.
   shell realizeWidget.
 
valueChanged: widget clientData: clientData callData: callData
"Display the new value on the transcript."
 
   Transcript cr; show: 'Value changed to: ' , callData value printString.

The CewEntryField class can be subclassed to provide a slightly different extended widget by simply overriding one method, as in the following class definition for CewNumericEntryField:

CewEntryField subclass: #CewNumericEntryField
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
 
losingFocus: widget clientData: clientData callData: callData
"Private - Process a losing focus callback for the primary widget."
 
   | newValue |
 
   newValue := self textWidget value.
"Verify that the new value string represents a number.
 If it doesn't, reset the text widget and return."
   (newValue notEmpty and: [newValue conform: [:c | c isDigit]])
      ifFalse: [ ^self value: value ].
"If the new value is different, invoke the entryField widget's 
 valueChanged callback."
 
   self value = newValue
      ifTrue: [
         self
            value: newValue;
            callCallbacks: XmNvalueChangedCallback
            callData: (CwValueCallbackData new value: newValue)]


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]