"{ Package: 'stx:libwidg' }"
Object subclass:#EditTextViewCompletionSupport
instanceVariableNames:'completionView completionProcess editView autoSelect'
classVariableNames:'LastCompletions'
poolDictionaries:''
category:'Views-Text'
!
!EditTextViewCompletionSupport class methodsFor:'documentation'!
documentation
"
An abstract supperclass to support completion in text views.
Individual completion engines may create a subclass of
EditTextCompletionSupport and customize it.
Basucally, they have to implement #computeCompletions
[author:]
Claus Gittinger
[instance variables:]
[class variables:]
[see also:]
"
! !
!EditTextViewCompletionSupport class methodsFor:'instance creation'!
for:anEditView
^ self new editView:anEditView
! !
!EditTextViewCompletionSupport class methodsFor:'queries'!
isAbstract
^true
"Created: / 26-09-2013 / 16:22:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!EditTextViewCompletionSupport methodsFor:'accessing'!
editView:anEditTextView
editView := anEditTextView.
! !
!EditTextViewCompletionSupport methodsFor:'events'!
buttonPress:button x:x y:y
self closeCompletionView.
!
editViewLostFocus
completionView notNil ifTrue:[
"/ this is a hack for Windows:
"/ on windows, an activate:false event is first sent to my textView,
"/ then an activate is sent to the completion popup.
"/ this is done BEFORE the buttonPress event is delivered.
"/ therefore, allow for the activate of the completionMenu and it's button event to be processed.
"/ before forcing it to be closed...
editView graphicsDevice anyButtonPressed ifTrue:[
editView sensor pushUserEvent:#editViewLostFocus for:self.
] ifFalse:[
self closeCompletionView
]
].
!
handleKeyPress:key x:x y:y
"return true, if I have eaten this keypress"
|ch completeImmediate eatCursorLeftOrRight eatCursorUpDown|
completeImmediate := UserPreferences current immediateCodeCompletion.
"/ open on CTRL- or TAB-key?
(completionView isNil or:[completionView realized not]) ifTrue:[
editView hasSelection ifFalse:[
((ch := editView characterBeforeCursor) notNil "/ i.e. not at begin of line
and:[ ch isLetterOrDigit or:[ch == $_] ]) ifTrue:[
(key == #Control_L or:[ key == #Ctrl or:[ key == #Control_R or:[ key == #Control]]]) ifTrue:[
UserPreferences current codeCompletionOnControlKey ifTrue:[
autoSelect := true.
self updateCompletionList
]
].
((key == #Tab) and:[editView sensor shiftDown not]) ifTrue:[
UserPreferences current codeCompletionOnTabKey ifTrue:[
autoSelect := true.
self updateCompletionList.
^ true
].
]
].
].
^ false.
].
"/ key for completion view ? (careful: do not forward too many, it would disturb user's typing)
key isCharacter ifFalse:[
"/ forward to menu
(completionView notNil) ifTrue:[
"/never
eatCursorLeftOrRight := false.
"/ completeImmediate not
"/ or:[ editView sensor shiftDown
"/ or:[ editView sensor ctrlDown ]].
"/ only with shift or ctrl
eatCursorUpDown :=
(UserPreferences current codeCompletionViewKeyboardNavigationNeedsModifier not)
or:[ editView sensor shiftDown
or:[editView sensor ctrlDown]].
((key == #CursorDown and:[eatCursorUpDown])
or:[ (key == #CursorUp and:[eatCursorUpDown])
or:[ ((key == #CursorLeft) and:[eatCursorLeftOrRight])
or:[ ((key == #CursorRight) and:[eatCursorLeftOrRight])
or:[ ((key == #Return) and:[ completionView hasSelection ])
]]]]) ifTrue:[
"/ forward to completion view
completionView sensor pushUserEvent:#value for:[ completionView keyPress:key x:0 y:0 ].
^ true.
].
(key == #Control_L or:[ key == #Control_R or:[ key == #Control or:[ key == #Ctrl ]]]) ifTrue:[
"/ CTRL is a toggle
"/ self closeCompletionView.
"/ ^ true.
^ false "/ don't eat
].
(key == #Escape) ifTrue:[
self closeCompletionView.
^ true "/ EAT
].
"/ shift does not close
(key == #Shift_L or:[ key == #Shift_R or:[ key == #Shift]]) ifTrue:[
^ false "/ don' eat
].
(key == #BackSpace) ifTrue:[
^ false "/ don' eat
].
self closeCompletionView.
^ false "/ don' eat
].
].
^ false.
!
postKeyPress:key
UserPreferences current immediateCodeCompletion ifFalse:[
"/ only update, if already open
completionView isNil ifTrue:[^ self].
].
(key == #BackSpace or:[key == #BasicBackspace]) ifTrue:[
autoSelect := false.
self updateCompletionList.
^ self
].
key isCharacter ifTrue:[
key isSeparator ifTrue:[
self closeCompletionView
] ifFalse:[
autoSelect := false.
self updateCompletionList.
].
^ self
].
! !
!EditTextViewCompletionSupport methodsFor:'private'!
computeCompletions
"Actually compute the completions and update the completion view."
self subclassResponsibility
"Created: / 26-09-2013 / 17:35:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
release
self stopCompletionProcess.
self closeCompletionView.
super release
!
startCompletionProcess
"start the code completion process in the background"
|initialList cursorX cursorY|
"/ terminate any previous process
self stopCompletionProcess.
(editView sensor hasKeyPressEventFor:nil) ifTrue:[
"/ 'cl' printCR.
self closeCompletionView.
^ self
].
((cursorX := editView xOfCursor) isNil
or:[ (cursorY := editView yOfCursor) isNil ]) ifTrue:[
"/ no cursor - user is selecting, or cursor has been scrolled out of sight.
"/ 'cl2' printCR.
self closeCompletionView.
^ self
].
completionView isNil ifTrue:[
initialList := #( 'Busy...' ).
"/ 'op1' printCR.
] ifFalse:[
initialList := completionView list.
"/ 'op2' printCR.
].
self openCompletionView:initialList.
completionProcess :=
[
self computeCompletions.
] forkAt:(Processor activePriority - 1).
"Modified: / 26-09-2013 / 17:36:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
stopCompletionProcess
"kill any background completion process"
|p|
(p := completionProcess) notNil ifTrue:[
completionProcess := nil.
p terminate.
].
!
updateCompletionList
"called for keypress events"
self startCompletionProcess.
! !
!EditTextViewCompletionSupport methodsFor:'private-API'!
closeCompletionView
|v|
(v := completionView) notNil ifTrue:[
completionView := nil.
"/ let it close itself - avoids synchronization problems
v sensor
pushUserEvent:#value
for:[ v topView destroy ]
].
!
openCompletionView
"Opens the completion view with an initial list. Called as soon as
completion is initiated but completion options are not yet computed."
self openCompletionView: (Array with: 'Busy...')
"Created: / 26-09-2013 / 17:06:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
openCompletionView: list
"Makes sure the completion view is opened and with given `list`."
| cursorPos movePos topView limit |
"/ move the window
cursorPos := editView device translatePoint:(editView xOfCursor @ editView yOfCursor) fromView:editView toView:nil.
cursorPos := cursorPos - (editView viewOrigin x @ 0).
"/ currently, we have to stay away a bit, to avoid getting the focus
movePos := cursorPos + (60 @ (editView font height)).
completionView isNil ifTrue:[
completionView := CodeCompletionHelpMenuView new.
completionView name:'completion'.
completionView level:0.
completionView list:list.
completionView enable:false.
completionView extent:completionView preferredExtentForContents.
"/ completionView font: editView font.
topView := CodeCompletionHelpView with:completionView.
topView editView:editView.
] ifFalse:[
completionView list:list.
topView := completionView topView.
].
topView ~~ completionView ifTrue:[
topView resizeToFit.
"/ movePos := editView device
"/ translatePoint:((editView right - topView width) @ (editView top))
"/ fromView:editView toView:nil.
"/ make sure, the window is visible
limit := topView device monitorBoundsAt:topView origin.
movePos x + topView extent x > limit corner x ifTrue:[
movePos := (cursorPos x - 60 - (topView extent x)) @ movePos y.
].
movePos y + topView extent y > limit corner y ifTrue:[
movePos := movePos x @ (cursorPos y - (topView extent y)).
].
topView origin:movePos.
].
"Created: / 26-09-2013 / 17:07:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!EditTextViewCompletionSupport class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libwidg/EditTextViewCompletionSupport.st,v 1.10 2014-06-14 11:18:07 cg Exp $'
!
version_CVS
^ '$Header: /cvs/stx/stx/libwidg/EditTextViewCompletionSupport.st,v 1.10 2014-06-14 11:18:07 cg Exp $'
! !