Jump to content

AutoComplete Input Text


Recommended Posts

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:

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.

Link to post
Share on other sites
  • 5 weeks later...

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)
Link to post
Share on other sites
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.

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
Link to post
Share on other sites
  • 9 months later...

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?

Link to post
Share on other sites
  • 3 months later...

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?

Link to post
Share on other sites
  • 8 months later...

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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...