[Class] eAutocomplete - Custom word completion for (Rich)Edit controls

Post your working scripts, libraries and tools
A_AhkUser
Posts: 884
Joined: 06 Mar 2017, 16:18
Location: France

Re: [Class] eAutocomplete - Custom word completion for edit controls

30 Jul 2018, 16:17

eAutocomplete v1.2.00

Changes:

Internal:
- Internal global redesign (enhancement of the OOP structure; in particular, catch-all methods has been removed).
- The script starts searching for entries that match when a user starts to type in the edit control and a brief rest (225ms) occured since the last keystroke, for efficiency (rapid key sequences will trigger only one search) and ergonomic purposes. For this very reason, the onValueChanged callback has been removed. An alternative onSuggestionsAvailable callback might be implemented soon.
Interface/behaviour:
- The top most suggestion is always automatically selected by default.
- The create base method accepts three parameters - namely: the host window's HWND; the edit control's options and an option object.
- dropDownList object member renamed to listbox (for an internal semantic clarity reason since, as of v1.1.11, if the host edit control is a single-line edit control, the list of choices is displayed beneath the control, rather like a comboBox list rather than a... dropdown list); onCompletionCompleted property renamed to onCompletion.

Addition:

Internal:
- Internal support for rich edit controls (e.g. Wordpad) - it will be made available soon after further debugging.
Interface/behaviour:
- All options may at any time be modified with imediate effect after the instance is created by setting the value of the respective property. The setOptions method can now be used alternatively to set one or more option of a given eAutocomplete instance.
- Added tabStops option.

[EDIT] | v1.2.10

- Added support for rich edit (RICHEDIT50W) controls (e.g. Wordpad).
- Description/replacement strings can be multiline text strings (in this case, both the newline (linefeed/LF) and the carriage return (CR) characters must be escaped).
- Fixed Down also moving down to next line when querying for suggestions if autosuggest was set to false and causing unwanted listbox displays after it has been closed by pressing Esc.


I may add some options later, but, for now, the script corresponds very closely to what I wanted to do (i.e. as part of another project) in addition to offer others the possibility to integrate a rather customizable word autocomplete function in their ahk script/project. I brought back up all information on the OP, including some ideas on future features. Let me know about any suggestions or bugs :D Cheers.
zotune
Posts: 73
Joined: 17 Nov 2014, 17:57

Re: [Class] eAutocomplete - Custom word completion for (Rich)Edit controls

12 Nov 2018, 15:41

Great work!

Any chance this can be modified to work with any/specific windows (window hwnd ahk_id)?
A_AhkUser
Posts: 884
Joined: 06 Mar 2017, 16:18
Location: France

Re: [Class] eAutocomplete - Custom word completion for (Rich)Edit controls

13 Nov 2018, 12:11

Hi zotune,

Thanks. Furthermore, I'm glad to know you may find this script useful. :)
zotune wrote:Any chance this can be modified to work with any/specific windows (window hwnd ahk_id)?
In any case, eAutocomplete is so far designed to wrap only those controls belonging to this following two specific classes: Edit and RICHEDIT50W. Having said this and with regard to windows... since, on one side, it is still a class after all (it ideally remains efficient to create numerous instances), and since, on another side, the attach method only requires you to pass it the target host control's HWND upon call, you can set up a listener, which function consists to automatically wrap, if need be, any matching Edit/RICHEDIT50W control (i.e. given a window class, a window hwnd, etc.).

Here's a sample code going in that direction: it automatically adds a custom word autocomplete functionality to each instance of Notepad (i.e. its own main Edit control), if it is still not wrapped into an eAutocomplete object:

Code: Select all

#NoEnv
#SingleInstance force
SetWorkingDir % A_ScriptDir
#Warn
; Windows 8.1 64 bit - Autohotkey v1.1.29.01 32-bit Unicode
CoordMode, ToolTip, Screen

#Include %A_ScriptDir%\eAutocomplete.ahk
eAutocomplete.getInstances := Func("eAutocomplete_getInstances") ; a simple wrapper to access the intended to be internal '_instances' property
eAutocomplete.disposeAll := Func("eAutocomplete_disposeAll")

Autocompletion_en =
(
abandon
ability
able
about
above
absent
absorb
abstract
absurd
abuse
access
accident
account
) ; from https://raw.githubusercontent.com/A-AhkUser/keypad-library/master/Keypad/Autocompletion/en
eAutocomplete.setSourceFromVar("Autocompletion_en", Autocompletion_en) ; https://github.com/A-AhkUser/eAutocomplete#available-methods

Gui +HWNDID
DllCall("RegisterShellHookWindow", "Ptr", ID) ; https://autohotkey.com/board/topic/80644-how-to-hook-on-to-shell-to-receive-its-messages/
matchingWindowClassName := "Notepad" ; here, we test with all windows which belong to the class 'Notepad'
matchingControlClassName := "Edit" ; otherwise, could also be 'RICHEDIT50W'
options := {autoSuggest: true, suggestAt: 2, onCompletion: "test_onCompletion", source: "Autocompletion_en"} ; some eAutocomplete options (cf. also https://github.com/A-AhkUser/eAutocomplete#options)
msgNum := DllCall("RegisterWindowMessage", "Str", "SHELLHOOK")
fn := Func("autoAttachByClassName").bind("Notepad", "Edit", options)
OnMessage(msgNum, fn)
OnExit, handleExit
return

autoAttachByClassName(_wClass, _cClass, _options, _wParam, _lParam) { ; automatically attach any matching control which is not yet wrapped into an eAutocomplete object
	if not ((_wParam = 32772) || (_wParam = 4)) ;  HSHELL_WINDOWACTIVATED := 4 or 32772 > https://superuser.com/questions/971992/how-to-make-autohotkey-automatically-close-a-pop-up-dialog
		return
	; if a new window has been activated...
	WinGetClass, _class, % "ahk_id " . _lParam
	if not (_class == _wClass) ; we first check the window class name agaisnt the one passed to the caller...
		return
	WinGet, _list, ControlListHwnd, % "ahk_id " . _lParam
	Loop, parse, % _list, `n
	{
		WinGetClass, _class, % "ahk_id " . A_LoopField
		if not (_class == _cClass) ; ...then we check each control class name against the control class name passed the caller
			return
		; if it is question of a matching control...
		(!eAutocomplete.getInstances().hasKey(A_LoopField) && eAutocomplete.attach(A_LoopField, _options)) ; ...wrap it if it is stiill not wrapped
		ToolTip % eAutocomplete.getInstances().count() . " control wrapped into a eAutocomplete object.", 0, 0, 1
	}
}

!i::
for k, v in eAutocomplete.getInstances()
	MsgBox % v.AHKID
return

handleExit:
	eAutocomplete.disposeAll()
ExitApp

test_onCompletion(_instance, _text, _isRemplacement) { ; a sample 'onCompletion' callback (cf. also: https://github.com/A-AhkUser/eAutocomplete#event-handling)
ToolTip % A_ThisFunc " `r`n" _text,,, 2
}

eAutocomplete_getInstances() {
return eAutocomplete._instances
}
eAutocomplete_disposeAll() { ; cf also https://github.com/A-AhkUser/eAutocomplete#dispose-method
	for _each, _instance in eAutocomplete.getInstances().clone() ; we must use a shallow copy of the object returned by getInstances since 'dispose' internally call the 'delete' method
		_instance.dispose()
}
On a side note, I just noticed, experimenting with the example above that, in cases in which multipe controls are wrapped into an object while collectWords option is used, any collected words, being instance-specific are not "seen" by any other instance even though they share the same source - which one might consider counterintuitive. I might change this behaviour if I can allocate some of my free time to it.

Return to “Scripts and Functions”

Who is online

Users browsing this forum: adegard, Bing [Bot], Google [Bot] and 20 guests