#FEATURE by exept
authorClaus Gittinger <cg@exept.de>
Thu, 19 Sep 2019 21:27:13 +0200
changeset 6708 f11899d4bd49
parent 6707 187a9556ae38
child 6709 9646e32786b7
#FEATURE by exept class: EditField support cancel key handling class definition added: #cancelKey #cancelKeys #cancelKeys: #onKey:cancelWith: comment/format in: #leaveKey changed: #keyPress:x:y: #onKey:leaveWith: class: EditField class comment/format in: #documentation
EditField.st
--- a/EditField.st	Thu Sep 19 14:53:24 2019 +0200
+++ b/EditField.st	Thu Sep 19 21:27:13 2019 +0200
@@ -1,3 +1,5 @@
+"{ Encoding: utf8 }"
+
 "
  COPYRIGHT (c) 1990 by Claus Gittinger
 	      All Rights Reserved
@@ -19,7 +21,8 @@
 		lengthLimit entryCompletionBlock entryCompletionCharacter
 		passwordCharacter autoScrollHorizontally acceptOnTab
 		acceptOnLostFocus acceptOnPointerLeave acceptIfUnchanged leaveKey
-		doubleClickAction emptyFieldReplacementText leaveKeyActions'
+		doubleClickAction emptyFieldReplacementText leaveKeyActions
+		cancelKeys cancelKey cancelKeyActions'
 	classVariableNames:'DefaultForegroundColor DefaultBackgroundColor
 		DefaultSelectionForegroundColor DefaultSelectionBackgroundColor
 		DefaultBorderColor DefaultBorderWidth DefaultLevel'
@@ -97,8 +100,26 @@
       The physical completion-key is the Tab key (this one cannot be defined by
       the keyboardTranslation mechanism, since that would disable the Tab-key on regular text views.
 
-
-
+    special key handling:
+      usually, editFields are embedded in a form (as in a dialog), 
+      which has OK and Cancel buttons at the bottom.
+      However, if the editField is in some other popup, it may be required to customize its behavior
+      upon certain key events. 
+      For this, the following flags are used:
+        leaveKeys       - the set of keys, which will lead to an accept (of the value into the model),
+                          and leave the focus. If that happens, the responsible key is left in the leaveKey variable.
+                          Default are CursorUp, Down and Return
+
+        leaveAction     - if not nil, that action is called after the accept, passing the leaveKey as argument
+
+        acceptOnLeave   - if true, an accept will be performed on leave.
+
+        cancelKeys      - the set of keys which will leve without accepting. If that happens, the responsible
+                          key is left in the cancelKey variable.
+
+        cancelAction    - if not nil, that action is called, passing the cancelKey as argument.
+
+        
     [Instance variables:]
 
       leaveAction    <Block | nil>              if non-nil, this is evaluated with
@@ -957,6 +978,30 @@
     autoScrollHorizontally := aBoolean
 !
 
+cancelKey
+    "only valid after the editField has been left via a cancelCharacter.
+     Allows for users of the EditField to advance the selection 
+     (especially: DataSets to select next/prev if editor is left via CrsrUp/Down).
+     This is probably a stupid interface - better use onKey:cancelWith: for a directed action"
+
+    ^ cancelKey
+!
+
+cancelKeys
+    "return (a copy of) the collection of keys which are interpreted as cancelKeys.
+     I.e. those that make the field inactive and pass the focus to the next field
+     (without accept)"
+
+    ^ cancelKeys copy
+!
+
+cancelKeys:aCollectionOfKeySymbols 
+    "define the set of keys which are interpreted as cancelKeys.
+     I.e. those that make the field inactive without accept."
+
+    cancelKeys := aCollectionOfKeySymbols
+!
+
 clickAction:aBlock
     "define an action to be evaluated when being clicked upon"
 
@@ -1143,7 +1188,8 @@
 leaveKey
     "only valid after the editField has been left via a leaveCharacter.
      Allows for users of the EditField to advance the selection 
-     (especially: DataSets to select next/prev if editor is left via CrsrUp/Down)"
+     (especially: DataSets to select next/prev if editor is left via CrsrUp/Down).
+     This is probably a stupid interface - better use onKey:leaveWith: for a directed action"
 
     ^ leaveKey
 !
@@ -1177,6 +1223,24 @@
     "Created: 27.4.1996 / 17:09:48 / cg"
 !
 
+onKey:aKey cancelWith:aBlock
+    "arrange for aKey to leave the field (without accept) and evaluate aBlock.
+     On cancel, the precedence of actions is:
+       1) explicit cancelAction per key
+       1) explicit leaveAction per key
+       2) general leaveAction
+       3) tab or return action
+     Can be used to arrange for escape to deal with special operations (such as closing some view),
+     The cancel-action block will be called with 0,1 (the key) or 2 (self + key) arguments"
+
+    cancelKeys := (cancelKeys ? #()) asSet.
+    cancelKeys add:aKey.
+    cancelKeyActions isNil ifTrue:[
+        cancelKeyActions := Dictionary new.
+    ].
+    cancelKeyActions at:aKey put:aBlock.
+!
+
 onKey:aKey leaveWith:aBlock
     "arrange for aKey to leave the field and evaluate aBlock.
      On leave, the precedence of actions is:
@@ -1187,7 +1251,7 @@
      or to override builtin ctrl- shortcuts.
      The leave-action block will be called with 0,1 (the key) or 2 (self + key) arguments"
 
-    leaveKeys := leaveKeys asOrderedCollection.
+    leaveKeys := (leaveKeys ? #()) asSet.
     leaveKeys add:aKey.
     leaveKeyActions isNil ifTrue:[
         leaveKeyActions := Dictionary new.
@@ -1681,7 +1745,7 @@
                           #DeleteLine #GotoLine #EndOfLine #EndOfText
                           #CursorDown #CursorUp)>
 
-    |rawKey leave doAccept keyAction numArgs lastEvent|
+    |rawKey leave cancel doAccept keyAction numArgs lastEvent|
 
     enabled ifFalse:[
         ^ self
@@ -1707,18 +1771,48 @@
         keyAction := crAction.
     ].
 
-    leave := leaveKeys includes:key.
-    leave ifFalse:[
-        lastEvent := WindowGroup lastEvent.
-        (lastEvent notNil and:[lastEvent isKeyEvent]) ifTrue:[
-            leave := leaveKeys includes:lastEvent rawKey.
+    lastEvent := WindowGroup lastEvent.
+    (lastEvent notNil and:[lastEvent isKeyEvent]) ifTrue:[
+        rawKey := lastEvent rawKey.
+    ].
+
+    cancel := leave := false.
+
+    cancel := (cancelKeys ? #()) includes:key.
+    cancel ifFalse:[
+        cancel := (cancelKeys ? #()) includes:rawKey.
+        cancel ifFalse:[
+            leave := (leaveKeys ? #()) includes:key.
+            leave ifFalse:[
+                leave := (leaveKeys ? #()) includes:rawKey.
+            ].
         ].
     ].
-
-    leave ifTrue:[
-        leaveKey := key.
-        doAccept := doAccept ? acceptOnLeave.
-
+    cancel ifTrue:[
+        cancelKey := key.
+        doAccept := false.
+    ] ifFalse:[
+        leave ifTrue:[
+            leaveKey := key.
+            doAccept := doAccept ? acceptOnLeave.
+        ].
+    ].
+
+    cancel ifTrue:[
+        "/ on cancel, the precedence of actions is:
+        "/  1) explicit cancelAction per key
+        cancelKeyActions notNil ifTrue:[
+            keyAction := cancelKeyActions 
+                            at:key 
+                            ifAbsent:[
+                                cancelKeyActions
+                                    at:rawKey 
+                                    ifAbsent:nil].
+        ].
+    ].
+    ((cancel and:[keyAction isNil])
+      or:[cancel not and:[leave]]
+    ) ifTrue:[
         "/ on leave, the precedence of actions is:
         "/  1) explicit leaveAction per key
         "/  2) general leaveAction
@@ -1740,7 +1834,6 @@
 
     keyAction notNil ifTrue:[
         numArgs := keyAction argumentCount.
-
         numArgs == 2 ifTrue:[
             ^ keyAction value:self value:key
         ].