"
COPYRIGHT (c) 1992 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
Object subclass:#EnterFieldGroup
instanceVariableNames:'fields currentField leaveAction wrap'
classVariableNames:''
poolDictionaries:''
category:'Interface-Support'
!
!EnterFieldGroup class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1992 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
documentation
"
EnterFieldGroup controls the interaction between EnterFields
enabling the next/prev field when a field is left.
Instances of this class keep track of which field of the group is the
currentField (i.e. the one getting keyboard input) and forwards input
to the active field (having the inputField delegate its input to me).
The block accessable as leaveAction is evaluated when the last
field of the group is left (by cursor-down or cr).
Usually this block triggers accept on the fields and/or performs some
followup processing and closes the topview (for example: in a dialog).
EnterFieldGroups can be used as a delegate (of the topView) to forward
input (entered into the topView) to the currently active field.
Stepping to previous field is via CursorUp/PreviousField,
to next field via CursorDown/NextField/Tab.
Notice, that by default, the editField takes the tab-character as
a normal character. To step using tab, you have to add the Tab key to the
fields leaveKeys.
Instance variables:
fields <Collection of EditField> the fields of the group
currentField <EditField> the active field
leaveAction <nil|Block> action to perform, when the
last field is left by a non-wrap
wrap <Boolean> if true, non-return next-keys wrap
back to the first field.
If false (the default), next in
the last field is taken as return.
This is ignored, if no leaveAction was
defined.
"
!
examples
"
without a group - user has to enter mouse into the next field to activate it;
Cursor-keys dont work:
|top panel field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
top open
with a group - Return-key or CursorKey enables next field:
(but still, mouse pointer has to be moved into any of the fields,
because the topView does not forward its input into the fields)
|top panel group field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field1; add:field2; add:field3.
top open
same, enables tabbing via the Tab key:
|top panel group field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field1; add:field2; add:field3.
field1 leaveKeys:(EditField defaultLeaveKeys copyWith:#Tab).
field2 leaveKeys:(EditField defaultLeaveKeys copyWith:#Tab).
field3 leaveKeys:(EditField defaultLeaveKeys copyWith:#Tab).
top open
with a group - Return-key or CursorKey enables next field:
input into topView is forwarded to the group:
|top panel group field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field1; add:field2; add:field3.
top delegate:group.
top open
as above, but close the box when the last field is left:
|top panel group field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field1; add:field2; add:field3.
group leaveAction:[top destroy].
top delegate:group.
top open
same as above, with Tab-key stepping:
|top panel group field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field1; add:field2; add:field3.
group leaveAction:[top destroy].
field1 leaveKeys:(EditField defaultLeaveKeys copyWith:#Tab).
field2 leaveKeys:(EditField defaultLeaveKeys copyWith:#Tab).
field3 leaveKeys:(EditField defaultLeaveKeys copyWith:#Tab).
top delegate:group.
top open
the next example shows that the input order is defined by the
order in the group; NOT by the physical layout of the fields in the superview:
(i.e. you can arrange your fields in multiple framedBoxes, panels or
subviews - independent of the tab-stepping order)
|top panel group field1 field2 field3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field3; add:field2; add:field1.
group leaveAction:[top destroy].
top delegate:group.
top open
using a single model for all fields:
(here, we use a Plug to simulate a more complex model):
|top panel group field1 field2 field3 model
value1 value2 value3|
top := StandardSystemView new.
top extent:200@200.
panel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
panel add:(field1 := EditField extent:(1.0 @ nil)).
panel add:(field2 := EditField extent:(1.0 @ nil)).
panel add:(field3 := EditField extent:(1.0 @ nil)).
group := EnterFieldGroup new.
group add:field1; add:field2; add:field3.
group leaveAction:[top destroy].
value1 := 'one'. value2 := 'two'. value3 := 'three'.
model := Plug new.
model respondTo:#value1 with:[value1].
model respondTo:#value1: with:[:arg | value1 := arg].
model respondTo:#value2 with:[value2].
model respondTo:#value2: with:[:arg | value2 := arg].
model respondTo:#value3 with:[value3].
model respondTo:#value3: with:[:arg | value3 := arg].
field1 model:model; aspect:#value1; change:#value1:.
field2 model:model; aspect:#value2; change:#value2:.
field3 model:model; aspect:#value3; change:#value3:.
top delegate:group.
top openModal.
Transcript showCr:'value1: ' , value1.
Transcript showCr:'value2: ' , value2.
Transcript showCr:'value3: ' , value3.
the above is done automatically for you, if you add inputFields
to a dialogBox:
|box model
value1 value2 value3|
box := DialogBox new.
box extent:200@200.
value1 := 'one'. value2 := 'two'. value3 := 'three'.
model := Plug new.
model respondTo:#value1 with:[value1].
model respondTo:#value1: with:[:arg | value1 := arg].
model respondTo:#value2 with:[value2].
model respondTo:#value2: with:[:arg | value2 := arg].
model respondTo:#value3 with:[value3].
model respondTo:#value3: with:[:arg | value3 := arg].
(box addInputFieldOn:model) aspect:#value1; change:#value1:.
box addVerticalSpace.
(box addInputFieldOn:model) aspect:#value2; change:#value2:.
box addVerticalSpace.
(box addInputFieldOn:model) aspect:#value3; change:#value3:.
box open.
Transcript showCr:'value1: ' , value1.
Transcript showCr:'value2: ' , value2.
Transcript showCr:'value3: ' , value3.
"
! !
!EnterFieldGroup methodsFor:'accessing'!
fields
"return a collection of the inputFields contained in the group."
^ fields
!
leaveAction:aBlock
"set the action to perform when the last field is left.
Usually, this is to accept the values of all fields and perform
some additional processing (such as closing a dialog)."
leaveAction := aBlock
!
wrap:aBoolean
"specifies if leaving the last field via non-Return
should wrap back to the first, or leave the group.
The default is to stay in the input sequence and wrap back to
the first field."
wrap := aBoolean
! !
!EnterFieldGroup methodsFor:'adding / removing'!
add:aField
|thisIndex action|
fields isNil ifTrue:[
fields := OrderedCollection new
].
fields add:aField.
thisIndex := fields size.
aField delegate:self.
aField hideCursor.
aField disable.
"set the fields enableAction to disable active field"
aField clickAction:[:field |
self makeActive:field
].
"set the fields leaveAction to enable next field"
aField leaveAction:[:key |
|next wg explicit nFields nextField|
"/ currentField notNil ifTrue:[
"/ currentField disable.
"/ currentField hideCursor.
"/ ].
"/
action := key.
nFields := fields size.
((key == #CursorUp) or:[key == #PreviousField]) ifTrue:[
(thisIndex == 1) ifTrue:[
next := nFields
] ifFalse:[
next := thisIndex - 1
]
].
((key == #CursorDown)
or:[key == #NextField
or:[key == #Tab]]) ifTrue:[
(thisIndex == nFields) ifTrue:[
next := 1.
wrap == false ifTrue:[
action := #Return.
].
] ifFalse:[
next := thisIndex + 1
]
].
(action == #Return) ifTrue:[
(thisIndex == nFields) ifTrue:[
leaveAction notNil ifTrue:[
currentField := nil.
leaveAction value.
next := nil
] ifFalse:[
next := 1
]
] ifFalse:[
next := thisIndex + 1
]
].
next notNil ifTrue:[
nextField := fields at:next.
explicit := false.
(wg := currentField windowGroup) notNil ifTrue:[
wg focusView == currentField ifTrue:[
explicit := true.
]
].
explicit ifTrue:[
wg focusView:nextField.
] ifFalse:[
self makeActive:nextField
]
]
].
fields size == 1 ifTrue:[
"the first one"
self makeActive:aField
]
! !
!EnterFieldGroup methodsFor:'event forwarding'!
buttonPress:button x:x y:y view:aView
"clicking on a field activates it and forwards the click to it"
self makeActive:aView.
aView buttonPress:button x:x y:y
!
buttonShiftPress:button x:x y:y view:aView
"clicking on a field activates it and forwards the click to it"
self makeActive:aView.
aView buttonShiftPress:button x:x y:y
!
handlesButtonPress:button inView:aView
"query from event processor: am I interested in button-events ?
yes I am (to activate the clicked-on field)."
^ true
!
handlesButtonShiftPress:button inView:aView
"query from event processor: am I interested in button-events ?
yes I am (to activate the clicked-on field)."
^ true
!
handlesKeyPress:key inView:aView
"query from event processor: am I interested in key-events ?
yes I am (to forward it to the active field)."
^ true
!
handlesKeyRelease:key inView:aView
"query from event processor: am I interested in key-events ?
yes I am (to forward it to the active field)."
^ true
!
keyPress:key x:x y:y view:aView
"key-press in any field - forward the key to the active field
(with -1/-1 as coordinate to indicate that the key was pressed
outside. However, this info is not used by any view currently)"
currentField notNil ifTrue:[
currentField keyPress:key x:-1 y:-1
]
!
keyRelease:key x:x y:y view:aView
"key-release in any field - forward the key to the active field.
(with -1/-1 as coordinate to indicate that the key was pressed
outside. However, this info is not used by any view currently)"
currentField notNil ifTrue:[
currentField keyRelease:key x:-1 y:-1
]
! !
!EnterFieldGroup methodsFor:'misc'!
activateFirst
"pass controll to my first field"
fields notNil ifTrue:[
self makeActive:fields first
]
"Modified: 7.2.1996 / 15:23:09 / cg"
! !
!EnterFieldGroup methodsFor:'private'!
makeActive:aField
"make a specific field the active one"
currentField == aField ifTrue:[^ self].
currentField notNil ifTrue:[
currentField disable.
currentField hideCursor.
currentField hasKeyboardFocus:false.
].
currentField := aField.
currentField enable.
currentField showCursor.
currentField hasKeyboardFocus:true.
! !
!EnterFieldGroup class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libwidg/EnterFieldGroup.st,v 1.19 1996-02-07 14:23:21 cg Exp $'
! !