Lupo73

AutoComplete Input Text

7 posts in this topic

I found different solutions to do it and I finally decided to merge them in a single "optimal" code.

During the typing it checks each new word (from the previous whitespace to the current pointer) and proposes to complete it with words included in a array. Predicted words are shown in a list under the input field and can be selected with mouse or with up/down/enter keys.

These are the sources considered to create this code:

 
And this is the code:
#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#Include <GuiListBox.au3>
#include <WindowsConstants.au3>

Global $asKeyWords[21] = [20, "fight", "first", "fly", "third", "fire", "wall", "hi", "hello", "world", "window", _
        "window 1", "window 2", "window 3", "window 4", "window 5", "window 6", "window 7", "window 8", "window 9", "window 10"]

_Main()

Func _Main()
    Local $hGUI, $hList, $hInput, $aSelected, $sChosen, $hUP, $hDOWN, $hENTER, $hESC
    Local $sCurrInput = "", $aCurrSelected[2] = [-1, -1], $iCurrIndex = -1, $hListGUI = -1

    $hGUI = GUICreate("AutoComplete Input Text", 300, 100)
    GUICtrlCreateLabel('Start to type words like "window" or "fire" to test it:', 10, 10, 280, 20)
    $hInput = GUICtrlCreateInput("", 10, 40, 280, 20)
    GUISetState(@SW_SHOW, $hGUI)

    $hUP = GUICtrlCreateDummy()
    $hDOWN = GUICtrlCreateDummy()
    $hENTER = GUICtrlCreateDummy()
    $hESC = GUICtrlCreateDummy()
    Dim $AccelKeys[4][2] = [["{UP}", $hUP], ["{DOWN}", $hDOWN], ["{ENTER}", $hENTER], ["{ESC}", $hESC]]
    GUISetAccelerators($AccelKeys)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $hESC
                If $hListGUI <> -1 Then ; List is visible.
                    GUIDelete($hListGUI)
                    $hListGUI = -1
                Else
                    ExitLoop
                EndIf

            Case $hUP
                If $hListGUI <> -1 Then ; List is visible.
                    $iCurrIndex -= 1
                    If $iCurrIndex < 0 Then
                        $iCurrIndex = 0
                    EndIf
                    _GUICtrlListBox_SetCurSel($hList, $iCurrIndex)
                EndIf

            Case $hDOWN
                If $hListGUI <> -1 Then ; List is visible.
                    $iCurrIndex += 1
                    If $iCurrIndex > _GUICtrlListBox_GetCount($hList) - 1 Then
                        $iCurrIndex = _GUICtrlListBox_GetCount($hList) - 1
                    EndIf
                    _GUICtrlListBox_SetCurSel($hList, $iCurrIndex)
                EndIf

            Case $hENTER
                If $hListGUI <> -1 And $iCurrIndex <> -1 Then ; List is visible and a item is selected.
                    $sChosen = _GUICtrlListBox_GetText($hList, $iCurrIndex)
                EndIf

            Case $hList
                $sChosen = GUICtrlRead($hList)
        EndSwitch

        Sleep(10)
        $aSelected = _GetSelectionPointers($hInput)
        If GUICtrlRead($hInput) <> $sCurrInput Or $aSelected[1] <> $aCurrSelected[1] Then ; Input content or pointer are changed.
            $sCurrInput = GUICtrlRead($hInput)
            $aCurrSelected = $aSelected ; Get pointers of the string to replace.
            $iCurrIndex = -1
            If $hListGUI <> -1 Then ; List is visible.
                GUIDelete($hListGUI)
                $hListGUI = -1
            EndIf
            $hList = _PopupSelector($hGUI, $hListGUI, _CheckInputText($sCurrInput, $aCurrSelected)) ; ByRef $hListGUI, $aCurrSelected.
        EndIf
        If $sChosen <> "" Then
            GUICtrlSendMsg($hInput, 0x00B1, $aCurrSelected[0], $aCurrSelected[1]) ; $EM_SETSEL.
            _InsertText($hInput, $sChosen)
            $sCurrInput = GUICtrlRead($hInput)
            GUIDelete($hListGUI)
            $hListGUI = -1
            $sChosen = ""
        EndIf
    WEnd
    GUIDelete($hGUI)
EndFunc   ;==>_Main

Func _CheckInputText($sCurrInput, ByRef $aSelected)
    Local $sPartialData = ""
    If (IsArray($aSelected)) And ($aSelected[0] <= $aSelected[1]) Then
        Local $aSplit = StringSplit(StringLeft($sCurrInput, $aSelected[0]), " ")
        $aSelected[0] -= StringLen($aSplit[$aSplit[0]])
        If $aSplit[$aSplit[0]] <> "" Then
            For $A = 1 To $asKeyWords[0]
                If StringLeft($asKeyWords[$A], StringLen($aSplit[$aSplit[0]])) = $aSplit[$aSplit[0]] And $asKeyWords[$A] <> $aSplit[$aSplit[0]] Then
                    $sPartialData &= $asKeyWords[$A] & "|"
                EndIf
            Next
        EndIf
    EndIf
    Return $sPartialData
EndFunc   ;==>_CheckInputText

Func _PopupSelector($hMainGUI, ByRef $hListGUI, $sCurr_List)
    Local $hList = -1
    If $sCurr_List = "" Then
        Return $hList
    EndIf
    $hListGUI = GUICreate("", 280, 160, 10, 62, $WS_POPUP, BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_MDICHILD), $hMainGUI)
    $hList = GUICtrlCreateList("", 0, 0, 280, 150, BitOR(0x00100000, 0x00200000))
    GUICtrlSetData($hList, $sCurr_List)
    GUISetControlsVisible($hListGUI) ; To Make Control Visible And Window Invisible.
    GUISetState(@SW_SHOWNOACTIVATE, $hListGUI)
    Return $hList
EndFunc   ;==>_PopupSelector

Func _InsertText(ByRef $hEdit, $sString)
    #cs
        Description: Insert A Text In A Control.
        Returns: Nothing
    #ce
    Local $aSelected = _GetSelectionPointers($hEdit)
    GUICtrlSetData($hEdit, StringLeft(GUICtrlRead($hEdit), $aSelected[0]) & $sString & StringTrimLeft(GUICtrlRead($hEdit), $aSelected[1]))
    Local $iCursorPlace = StringLen(StringLeft(GUICtrlRead($hEdit), $aSelected[0]) & $sString)
    GUICtrlSendMsg($hEdit, 0x00B1, $iCursorPlace, $iCursorPlace) ; $EM_SETSEL.
EndFunc   ;==>_InsertText

Func _GetSelectionPointers($hEdit)
    Local $aReturn[2] = [0, 0]
    Local $aSelected = GUICtrlRecvMsg($hEdit, 0x00B0) ; $EM_GETSEL.
    If IsArray($aSelected) Then
        $aReturn[0] = $aSelected[0]
        $aReturn[1] = $aSelected[1]
    EndIf
    Return $aReturn
EndFunc   ;==>_GetSelectionPointers

Func GUISetControlsVisible($hWnd) ; By Melba23.
    Local $aControlGetPos = 0, $hCreateRect = 0, $hRectRgn = _WinAPI_CreateRectRgn(0, 0, 0, 0)
    Local $iLastControlID = _WinAPI_GetDlgCtrlID(GUICtrlGetHandle(-1))
    For $i = 3 To $iLastControlID
        $aControlGetPos = ControlGetPos($hWnd, '', $i)
        If IsArray($aControlGetPos) = 0 Then ContinueLoop
        $hCreateRect = _WinAPI_CreateRectRgn($aControlGetPos[0], $aControlGetPos[1], $aControlGetPos[0] + $aControlGetPos[2], $aControlGetPos[1] + $aControlGetPos[3])
        _WinAPI_CombineRgn($hRectRgn, $hCreateRect, $hRectRgn, 2)
        _WinAPI_DeleteObject($hCreateRect)
    Next
    _WinAPI_SetWindowRgn($hWnd, $hRectRgn, True)
    _WinAPI_DeleteObject($hRectRgn)
EndFunc   ;==>GUISetControlsVisible
Any advice to improve the code is welcome.  :thumbsup:
3 people like this

SFTPEx, AutoCompleteInput_DateTimeStandard(), _ImageWriteResize()_GUIGraduallyHide(): some AutoIt functions.

Lupo PenSuite: all-in-one and completely free selection of portable programs and games.

DropIt: a personal assistant to automatically manage your files.

ArcThemALL!: application to multi-archive your files and folders.

Share this post


Link to post
Share on other sites



Nice compact code :)

I appreciate the very handy 'select with mouse' feature, it's a lack in Phoenix' PredictText udf

May I suggest something like this (raw) to make the list auto-resizable

Func _PopupSelector
   ; ...
   StringReplace($sCurr_List, "|", "|")
   Local $iCurrHeight = @extended*GUICtrlSendMsg($hList, $LB_GETITEMHEIGHT, 0, 0)+10
   WinMove($hListGUI, "", Default, Default, Default, $iCurrHeight)
   GUICtrlSetPos($hList, 0, 0, 150, $iCurrHeight)

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

I appreciate the very handy 'select with mouse' feature, it's a lack in Phoenix' PredictText udf

 

 

Mikell,

Maybe you have missed the update that I made on 1st Feb. Do check it out, if you use the PredictText UDF.

Edit : It was due to a bug, now is cleared.

Regards :)

Edited by PhoenixXL

My code:

PredictText: Predict Text of an Edit Control Like Scite. Remote Gmail: Execute your Scripts through Gmail. StringRegExp:Share and learn RegExp.

Run As System: A command line wrapper around PSEXEC.exe to execute your apps scripts as System (LSA). Database: An easier approach for _SQ_LITE beginners.

MathsEx: A UDF for Fractions and LCM, GCF/HCF. FloatingText: An UDF for make your text floating. Clipboard Extendor: A clipboard monitoring tool. 

Custom ScrollBar: Scroll Bar made with GDI+, user can use bitmaps instead. RestrictEdit_SRE: Restrict text in an Edit Control through a Regular Expression.

Share this post


Link to post
Share on other sites

Much nicer Phoenix, thanks  :)

However to fit my personal needs I managed to use this slight modif of Lupo's code for working with several inputs in the same gui

; http://www.autoitscript.com/forum/topic/158070-autocomplete-input-text/

#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <ListBoxConstants.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>

Global $Words1[20] = ["fight", "first", "fly", "third", "fire", "wall", "hi", "hello", "world", "window", _
        "window 1", "window 2", "window 3", "window 4", "window 5", "window 6", "window 7", "window 8", "window 9", "window 10"]
Global $Words2[6] = ["Alain", "Aline", "Bernard", "Beatrice", "Chloe", "Caroline"]

Global $hGUI, $hList
Global $sChosen, $idCurInput, $sCurrInput = "", $hListGUI = -1

$hGUI = GUICreate("AutoComplete Input Text", 300, 200)
GUICtrlCreateLabel('lettres "w, f" ', 10, 10, 280, 20)
$hInput = GUICtrlCreateInput("", 10, 40, 280, 20)
GUICtrlCreateLabel('lettres "a, b, c" ', 10, 70, 280, 20)
$hInput2 = GUICtrlCreateInput("", 10, 100, 280, 20)
GUISetState(@SW_SHOW, $hGUI)

GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

While 1
    Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
    EndSwitch
WEnd



Func WM_COMMAND($hWnd, $Msg, $wParam, $lParam)
    Local $IdFrom = BitAnd($wParam, 0x0000FFFF)
    Local $iCode = BitShift($wParam, 16)
    Switch $IdFrom
    Case $hInput, $hInput2    
        Switch $iCode
            Case $EN_UPDATE
               $idCurInput = $IdFrom
                _Update($idCurInput)
         EndSwitch
    Case $hList
         _Update($idCurInput)
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc


Func _Update($_input)
    If GUICtrlRead($_Input) <> $sCurrInput Then 
        $sCurrInput = GUICtrlRead($_Input)
        If $hListGUI <> -1 Then ; List is visible.
             GUIDelete($hListGUI)
             $hListGUI = -1
        EndIf

        Local $_array
        Switch $_input    
            Case $hInput
                 $_array = $Words1
            Case $hInput2
                 $_array = $Words2
        EndSwitch

        $hList = _PopupSelector($hGUI, $hListGUI, $_Input, _CheckInputText($_Input, $_array)) 
    EndIf

    If $hList <> -1 Then $sChosen = GUICtrlRead($hList)
    If $sChosen <> "" Then
       GuiCtrlSetData($_Input, $sChosen)
       $sCurrInput = GUICtrlRead($_Input)
       GUIDelete($hListGUI)
       $hListGUI = -1
       $sChosen = ""
    EndIf
EndFunc


Func _PopupSelector($hMainGUI, ByRef $hListGUI, $_Input, $sCurr_List)
    Local $hList = -1
    If $sCurr_List = "" Then Return $hList

    Local $pos = ControlGetPos($hMainGUI, "", $_Input)
    $hListGUI = GUICreate("", 280, 160, $pos[0], $pos[1]+$pos[3], $WS_POPUP, BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_MDICHILD), $hMainGUI)
    $hList = GUICtrlCreateList("", 0, 0, 280, 150, BitOR(0x00100000, 0x00200000))

   StringReplace($sCurr_List, "|", "|")
   Local $iCurrHeight = @extended*GUICtrlSendMsg($hList, $LB_GETITEMHEIGHT, 0, 0)+10
   WinMove($hListGUI, "", Default, Default, Default, $iCurrHeight)
   GUICtrlSetPos($hList, 0, 0, 150, $iCurrHeight)

    GUICtrlSetData($hList, $sCurr_List)
    GUISetControlsVisible($hListGUI) ; To Make Control Visible And Window Invisible.
    GUISetState(@SW_SHOWNOACTIVATE, $hListGUI)
    Return $hList
EndFunc   ;==>_PopupSelector


Func _CheckInputText($_Input, $array)
   Local $sPartialData = ""
   $aSelected = _GetSelectionPointers($_Input)
    If (IsArray($aSelected)) And ($aSelected[0] <= $aSelected[1]) Then
        $sCurrInput = GUICtrlRead($_Input)
        Local $aSplit = StringSplit(StringLeft($sCurrInput, $aSelected[0]), " ")
        $aSelected[0] -= StringLen($aSplit[$aSplit[0]])
        If $aSplit[$aSplit[0]] <> "" Then
            For $A = 0 To UBound($array)-1
                If StringLeft($array[$A], StringLen($aSplit[$aSplit[0]])) = $aSplit[$aSplit[0]] _ 
                And $array[$A] <> $aSplit[$aSplit[0]] Then
                    $sPartialData &= $array[$A] & "|"
                EndIf
            Next
        EndIf
    EndIf
    Return $sPartialData
EndFunc   ;==>_CheckInputText


Func _GetSelectionPointers($hEdit)
    Local $aReturn[2] = [0, 0]
    Local $aSelected = GUICtrlRecvMsg($hEdit, 0x00B0) ; $EM_GETSEL.
    If IsArray($aSelected) Then
        $aReturn[0] = $aSelected[0]
        $aReturn[1] = $aSelected[1]
    EndIf
    Return $aReturn
EndFunc   ;==>_GetSelectionPointers


Func GUISetControlsVisible($hWnd) ; By Melba23.
    Local $aControlGetPos = 0, $hCreateRect = 0, $hRectRgn = _WinAPI_CreateRectRgn(0, 0, 0, 0)
    Local $iLastControlID = _WinAPI_GetDlgCtrlID(GUICtrlGetHandle(-1))
    For $i = 3 To $iLastControlID
        $aControlGetPos = ControlGetPos($hWnd, '', $i)
        If IsArray($aControlGetPos) = 0 Then ContinueLoop
        $hCreateRect = _WinAPI_CreateRectRgn($aControlGetPos[0], $aControlGetPos[1], $aControlGetPos[0] + $aControlGetPos[2], $aControlGetPos[1] + $aControlGetPos[3])
        _WinAPI_CombineRgn($hRectRgn, $hCreateRect, $hRectRgn, 2)
        _WinAPI_DeleteObject($hCreateRect)
    Next
    _WinAPI_SetWindowRgn($hWnd, $hRectRgn, True)
    _WinAPI_DeleteObject($hRectRgn)
EndFunc   ;==>GUISetControlsVisible

Share this post


Link to post
Share on other sites

This looks like it fits my needs perfectly but I'm having an issue.  I have to create the list dynamically from Active Directory.  When I alter the code to use the array (and also count of array) it doesn't work ... I'm quite new with AutoIT but I thought this would work?

$Array = Chr(34) & $objRecordSet.Fields ("displayName").Value & Chr(34)
$Count = 1
While Not $objRecordSet.EOF
    $Array = $Array & ", " & Chr(34) & $objRecordSet.Fields ("displayName").Value & Chr(34)
    $Count = $Count + 1
    $objRecordSet.MoveNext
WEnd
$objConnection.Close
$objConnection = ""
$objCommand = ""
$objRecordSet = ""

Global $asKeyWords[($Count + 1)] = [$Count, $Array]

This is only the part where it creates the $Array, adds the required comma and double quotes for each one (outputting the $Array to console shows it's correct and checking the $Count also shows there is a valid figure).

What am I missing here?

Share this post


Link to post
Share on other sites

I like the script, and I've found a good use for it.

One enhancement I'm struggling to add, is the ability to either autocomplete based on any matching (e.g.,  word in dictionary:  dictionary -- typing starts out with "ary", I would want it to match) OR if there are two words, that it would also match based on the second word.

I imagine with the two words, I would be doing a string split based on a space as the delimiter, and edit the _checkinputtext function accordingly to check both parts of the string split.

Any thoughts on the best way to approach this?

Share this post


Link to post
Share on other sites

Very nice. You save me a lot of time.

Thank you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now