Simplified ComboBox Autocomplete

Share your advanced PureBasic knowledge/code with the community.
ebs
Enthusiast
Enthusiast
Posts: 561
Joined: Fri Apr 25, 2003 11:08 pm

Simplified ComboBox Autocomplete

Post by ebs »

Code in this post updated to 5.20+
I needed an autocompleting ComboBox and found a tip posted by Saboteur. I simplified his code by using "GetGadgetItemText()". I also made the routine accept the gadget number of the ComboBox to be completed, so it can be used for any number of ComboBoxes.

Code: Select all

;- autocomplete specified combobox
Procedure AutocompleteComboBox(ComboBox.l)
	; get text entered by user
	TextTyped.s = UCase(GetGadgetText(ComboBox))
	LenTextTyped.l = Len(TextTyped)
	; done if no text entered
	If LenTextTyped
		; search combo contents for item that starts with text entered
		MaxItem.l = CountGadgetItems(ComboBox) - 1
		For Item.l = 0 To MaxItem
			If TextTyped = UCase(Left(GetGadgetItemText(ComboBox, Item, 0), LenTextTyped))
				; found matching item
				; set combo state
				SetGadgetState(ComboBox, Item)
				; select added text only
				
				MyPoint.POINT
				hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), MyPoint);5, 5)
				SendMessage_(hComboEdit, #EM_SETSEL, LenTextTyped, -1)
				; exit For loop
				Item = MaxItem
			EndIf
		Next
	EndIf
EndProcedure

If OpenWindow(0, 0, 0, 300, 300, "Autocompletion", #PB_Window_SystemMenu|#PB_Window_TitleBar)
	;       If CreateGadgetList(WindowID(0))
	ComboBoxGadget(0, 10, 10, 200, 100, #PB_ComboBox_Editable)
	AddGadgetItem(0, -1, "Autocomplete")
	AddGadgetItem(0, -1, "Bernard")
	AddGadgetItem(0, -1, "Car")
	AddGadgetItem(0, -1, "Explorer")
	AddGadgetItem(0, -1, "Fantastic")
	AddGadgetItem(0, -1, "General protection error")
	AddGadgetItem(0, -1, "Purebasic")
	AddGadgetItem(0, -1, "Purepower")
	AddGadgetItem(0, -1, "Purevisual")
	AddGadgetItem(0, -1, "Question")
	AddGadgetItem(0, -1, "Zas")
	;       EndIf
EndIf

Repeat
	Event = WaitWindowEvent()
	Select Event
		Case #PB_Event_Gadget
			If EventGadget() = 0
				AutocompleteComboBox(0)
			EndIf
	EndSelect
Until Event = #PB_Event_CloseWindow
End
If you need this capability, please try it and let me know how it works for you.

Eric
Saboteur
Enthusiast
Enthusiast
Posts: 273
Joined: Fri Apr 25, 2003 7:09 pm
Location: (Madrid) Spain
Contact:

Post by Saboteur »

Good update, ebs ;)
[:: PB Registered ::]

Win10 Intel core i5-3330 8GB RAM Nvidia GTX 1050Ti
User avatar
aszid
Enthusiast
Enthusiast
Posts: 162
Joined: Thu May 01, 2003 8:38 pm
Location: California, USA
Contact:

Post by aszid »

i tested this out and found a little problem, once it has "autocompleted" the line, i can no longer use backspace... that was actually almost the first thing i did when i tried the code.

anyhow, running win2k here, 128mb ram.
--Aszid--

Making crazy people sane, starting tomorrow.
ebs
Enthusiast
Enthusiast
Posts: 561
Joined: Fri Apr 25, 2003 11:08 pm

Post by ebs »

aszid,

I'm aware of that, and I'm not sure how to handle it the best way.

After autocompletion, backspace does actually work, but the text is autocompleted again immediately, so you don't see it. I'm trying to figure out a straightforward way to delay autocompleting until another character is typed after a backspace, but so far, it's kind of messy. If I come up with something good, I'll post it here.

On another note, sometimes you may not want to autocomplete until a certain number of characters have been entered - I often use 3 characters. You can get this behavior easily by changing the line

Code: Select all

If LenTextTyped
to

Code: Select all

If LenTextTyped >= 3
ebs
Enthusiast
Enthusiast
Posts: 561
Joined: Fri Apr 25, 2003 11:08 pm

Autocomplete Works With Backspace

Post by ebs »

Here's a new version of the autocomplete code which works properly when Backspace is pressed:

Code: Select all

#NONE = -1

;- autocomplete specified combobox
Procedure AutocompleteComboBox(ComboBox.l)
  Shared LenTextSave.l
  Shared GadgetSave.l

  ; get text entered by user
  TextTyped.s = UCase(GetGadgetText(ComboBox))
  LenTextTyped.l = Len(TextTyped)
  
  ; skip if same gadget and same length or shorter text (backspace?)
  If ComboBox = GadgetSave And LenTextTyped <= LenTextSave
    LenTextSave = LenTextTyped
  ElseIf LenTextTyped
    GadgetSave = #NONE
    ; search combo contents for item that starts with text entered
    MaxItem.l = CountGadgetItems(ComboBox) - 1
    For Item.l = 0 To MaxItem
      If TextTyped = UCase(Left(GetGadgetItemText(ComboBox, Item, 0), LenTextTyped))
        ; found matching item
        ; set combo state
        SetGadgetState(ComboBox, Item)
        ; select added text only
        hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), 5, 5)
        SendMessage_(hComboEdit, #EM_SETSEL, LenTextTyped, -1)
        ; save gadget number and text length for next pass
        LenTextSave = LenTextTyped
        GadgetSave = ComboBox
        ; exit For loop
        Item = MaxItem
      EndIf
    Next
  EndIf
EndProcedure

If OpenWindow(0, 0, 0, 300, 300, #PB_Window_SystemMenu|#PB_Window_TitleBar, "Autocompletion")
  If CreateGadgetList(WindowID())
    ComboBoxGadget(0, 10, 10, 200, 100, #PB_ComboBox_Editable)
    AddGadgetItem(0, -1, "Autocomplete")
    AddGadgetItem(0, -1, "Bernard")
    AddGadgetItem(0, -1, "Car")
    AddGadgetItem(0, -1, "Explorer")
    AddGadgetItem(0, -1, "Fantastic")
    AddGadgetItem(0, -1, "General protection error")
    AddGadgetItem(0, -1, "Purebasic")
    AddGadgetItem(0, -1, "Purepower")
    AddGadgetItem(0, -1, "Purevisual")
    AddGadgetItem(0, -1, "Question")
    AddGadgetItem(0, -1, "Zas")
  EndIf
EndIf

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_Gadget
      If EventGadgetID() = 0
        AutocompleteComboBox(0)
      EndIf
  EndSelect
Until Event = #PB_Event_CloseWindow

End
Let me know if it works for you!
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

It works for me when I run it in your example but I'm getting some pretty strange stuff when I included it in my own project.

I copied the function exactly, then changed the

Code: Select all

hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), 5, 5)
to

Code: Select all

hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), 117, 100)
Which should compensate for my gadget being in a different position.. Now, what I see happening in my project is very much what I expected to happen because you're calling the function every time any event occurs on the gadget, which means it gets called every time a key is pressed.. So if that happens then the TextTyped variable is set to what ever the current text in the gadget is. Now that's all fine and dandy until you get a match on the first character of what you type, then that item is selected which writes the full text of what ever the first match was into the TextTyped variable (because you set the state to that match every time), causing the entire thing to be selected and thus making my life hell!

What I can't figure out for the life of me (and this might be because this is hour 16 today) is - how the hell the example works like it does! I've taken my exact combo box that's dysfunctional in my project, put those items in the one in the example and it works like a charm! I'm using the *exact* function as in the example, less the change I outlined above, and it just doesn't work in my project!

Help!? :-)
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

All fixed up - just me being a cluebie..
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
ebs
Enthusiast
Enthusiast
Posts: 561
Joined: Fri Apr 25, 2003 11:08 pm

Post by ebs »

Mitch,

I didn't even get a chance to reply before you figured it out! :D

You went back to using

Code: Select all

...ChildWindowFromPoint_(GadgetID(ComboBox), 5, 5)
Right?

Eric
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

Actually, no, if I do that then none of the text at all gets selected.. Aren't those the x/y coors of the combo?

Code: Select all

ComboBoxGadget(#Gadget_invoice_cmb_customer,65,75,234,100,#PB_ComboBox_Editable)
... that's my combo box..

All the text is being selected on the first match.. I'm scratching my head a bit on this one :-)
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

Grrrrrrrrrr.. I changed it back to 5,5 (after reading the docs!).. Still getting nothing at all selected in the combo though.. Still bug hunting!
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
ebs
Enthusiast
Enthusiast
Posts: 561
Joined: Fri Apr 25, 2003 11:08 pm

Post by ebs »

Mitch,

If you like, post your code and I'll see if I can figure it out.

Eric
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

The thing is - I'm using the exact code from the example and callling the procedure in the exact way the example does. My only guess is that something in the callback is messing with that combobox as the only place that gadget is mentioned is in the event handler (when the gadget id gets passed to the autocomplete procedure) and when the gadget is created and populated.

Crazy!
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

So what was happening was that SetGadgetState() was actually triggering the event on the combo. I'm still totally confused as to why it happens in my project and not in the example but it happens just the same.. I commented out the callback to make sure it wasn't something in there fussing with the combobox and got the same broken result..

If I do this in my event handling loop it works like a charm.. :

Code: Select all

      Case #PB_Event_Gadget
        
        Select EventGadgetID()
          
          Case #Gadget_invoice_cmb_customer
            
            If EventType() = 5
              
             AutocompleteComboBox(#Gadget_invoice_cmb_customer)
          
            EndIf
The EventType() that appears to come from SetGadgetState() is type 6, but I'm unable to find anything that might tell me what that means :-)
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
Post Reply