GadgetString's Autocomplete list

Share your advanced PureBasic knowledge/code with the community.
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

GadgetString's Autocomplete list

Post by thyphoon »

I am currently working on a big project. I needed an autocompletion list for string gadget
My goal was also that it be the most possible crossplatform
And this morning I had an idea ....
What do you think? I am looking for new improvements

2019-16-01 11h05 : Update Code
2019-16-01 15h22 : Works Fine :mrgreen:
2019-16-01 15h52 Fix when move Window !
2019-16-01 16h28 Fix move Windows with no open autocomplete window
2019-16-01 16h43 hide autocomplete window if empty
2019-16-01 21h15 use Srod advice ! Better code

Code: Select all

; ******************************************************************** 
; Program:           AutoComplete List
; Description:       Add an AutoComplete list to a String gadget 

; Author:            Thyphoon
; Date:              January, 2019
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; Thanks to:         Srod
; ******************************************************************** 

EnableExplicit

DeclareModule AutoComplete
  Declare AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
  Declare CheckEvent(Event.i)
EndDeclareModule  

Module AutoComplete
  Declare MoveWindow()

  Prototype.s CallBack(String.s)
  
  Structure ac
    Window.i                  ;Original Window
    Gadget.i                  ;Original Gadget
    FirstEventKey.l           ;First Eventkey=Up +1=Down +2=Enter +3=Escape
    CallBack.CallBack         ;CallBack who return choice
  EndStructure
  
  Structure params
    acWindow.i                ;Autocomplete window
    acGadget.i                ;AutoComplete GadgetList
    CurrentName.s             ;CurrentName at last open autocomplete windows
    Map AutoCompleteGadget.ac()
    FirstEventKey.i
  EndStructure
  
  Global params.params
  
  
  Procedure AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
    Name.s=Str(Window)+"-"+Str(Gadget)
    params\AutoCompleteGadget(Name)\CallBack=*CallBack
    params\AutoCompleteGadget(Name)\Gadget=Gadget
    params\AutoCompleteGadget(Name)\Window=Window
    params\AutoCompleteGadget(Name)\FirstEventKey=FirstEventKey
    AddKeyboardShortcut(Window, #PB_Shortcut_Up, FirstEventKey)
    AddKeyboardShortcut(Window, #PB_Shortcut_Down, FirstEventKey+1)
    AddKeyboardShortcut(Window, #PB_Shortcut_Return, FirstEventKey+2)
    AddKeyboardShortcut(Window, #PB_Shortcut_Escape, FirstEventKey+3)
  EndProcedure
  
  Procedure RefreshAutocompleteWindow(Name.s)
    Gadget.i=params\AutoCompleteGadget(Name)\Gadget
    X.l=GadgetX(Gadget,#PB_Gadget_ScreenCoordinate)
    Y.l=GadgetY(Gadget,#PB_Gadget_ScreenCoordinate)+GadgetHeight(Gadget)
    W.l=GadgetWidth(Gadget)
    H.l=150
    ResizeWindow(params\acWindow,X,Y,W,H)
    ResizeGadget(params\acGadget,0,0,W,H)
    HideWindow(params\acWindow, #False,#PB_Window_NoActivate)
    StickyWindow(params\acWindow,#True)
  EndProcedure
  
  Procedure UpdateAutocompleteList(Name.s)
    ClearGadgetItems(params\acGadget)
    If params\AutoCompleteGadget(Name)\CallBack<>0
      BackString.s=params\AutoCompleteGadget(Name)\CallBack(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
      For n=1 To CountString(BackString,Chr(10))+1
        line.s=StringField(BackString,n,Chr(10))
        If LCase(Left(line,Len(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))))=LCase(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
          AddGadgetItem(params\acGadget,-1,line)
        EndIf 
      Next
      ;Hide Autocomplete Window if no Choice
      If CountGadgetItems(params\acGadget)=0
        HideWindow(params\acWindow,#True)
      Else
        HideWindow(params\acWindow,#False,#PB_Window_NoActivate)
      EndIf 
    Else
      Debug "Error with callback"
    EndIf
  EndProcedure
  
  Procedure OpenAutocompleteWindow(Name.s)
    If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
    If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acWindow):EndIf
    params\acWindow=OpenWindow(#PB_Any,0,0,50,50,"AutoComplete",#PB_Window_BorderLess|#PB_Window_NoActivate,WindowID(params\AutoCompleteGadget(Name)\Window));
    If params\acWindow
      params\CurrentName=Name
      SetWindowData(params\acWindow,params\AutoCompleteGadget(Name)\Gadget)
      params\acGadget=ListViewGadget(#PB_Any, 0, 0, 50,50)
    EndIf
    RefreshAutocompleteWindow(Name)
    UpdateAutocompleteList(Name)
    BindEvent(#PB_Event_MoveWindow, @MoveWindow(), params\AutoCompleteGadget(Name)\Window)
  EndProcedure
  
  Procedure CloseAutocompleteWindow(Name.s)
      If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
      If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acWindow):EndIf
      UnbindEvent(#PB_Event_MoveWindow, @MoveWindow(), Windows)
  EndProcedure
  
  Procedure MoveWindow()
    If EventWindow()=params\AutoCompleteGadget()\Window And params\acWindow>0 And IsWindow(params\acWindow)
      RefreshAutocompleteWindow(params\CurrentName)
    EndIf
  EndProcedure
  

  Procedure CheckEvent(Event.i)
    Static bbclick.b
    ;-Orignal Gadget Event
    Protected Name.s=Str(EventWindow())+"-"+Str(EventGadget())   
    If FindMapElement(params\AutoCompleteGadget(),Name)
      Select Event
        Case  #PB_Event_Gadget
          Select EventGadget()
              ; Main gadget Event  
            Case params\AutoCompleteGadget()\Gadget
              Select EventType()
                Case #PB_EventType_Focus
                  If params\acWindow=0 Or IsWindow(params\acWindow)=#False
                    If bbclick=#False
                      OpenAutocompleteWindow(Name)
                    Else
                      bbclick=#True
                    EndIf 
                  EndIf 
                Case #PB_EventType_LostFocus
                  If GetActiveGadget()<>params\acGadget
                    CloseAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf  
                Case #PB_EventType_Change
                  If bbclick=#False 
                    If IsWindow(params\acWindow)
                      UpdateAutocompleteList(Name)
                    Else
                      Debug "pas de windows"
                    EndIf
                  Else
                    OpenAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf 
              EndSelect 

          EndSelect 
     EndSelect
    EndIf
    
    Select Event
      ;if Keyword  
      Case #PB_Event_Menu
        Select EventMenu()
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey;UP
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)-1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+1;Down
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)+1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+2;Enter
            SetGadgetText(params\AutoCompleteGadget()\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+3;
            CloseAutocompleteWindow(params\CurrentName)
        EndSelect
      Case  #PB_Event_Gadget
        If EventGadget()=params\acGadget
          If EventType()=#PB_EventType_LeftDoubleClick
            SetGadgetText(params\AutoCompleteGadget(params\CurrentName)\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
            CloseAutocompleteWindow(params\CurrentName)
            bbclick=#True
          EndIf
        EndIf 
    EndSelect  
    
    
    
  EndProcedure  
  
  
EndModule

;- MAIN TEST

CompilerIf #PB_Compiler_IsMainFile 
  
  ; Callback to return keyword list. you must use a database en filter if you want ^_^ 
  Procedure.s ToDisplayInList(String.s)
    ProcedureReturn "Amiga"+Chr(10)+"Amstrad"+Chr(10)+"Atari"+Chr(10)+"Commodore"+Chr(10)+"BeBox"+Chr(10)+"Macintosh"+Chr(10)+"Spectrum"
  EndProcedure
  
  Procedure.s ToDisplayInListB(String.s)
    ProcedureReturn "Alain"+Chr(10)+"Bernard"+Chr(10)+"Charly"+Chr(10)+"David"+Chr(10)+"Eric"+Chr(10)+"Filipe"+Chr(10)+"Horace"+Chr(10)+"Jérome"+Chr(10)+"Kevin"+Chr(10)+"Laurent"+Chr(10)+"Marc"
  EndProcedure
  
  If OpenWindow(0, 200, 200, 800, 100, "AutoComplete Teste", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    StringGadget(1, 10, 10, 380, 26, "")
    StringGadget(2, 410, 10, 380, 26, "")
    
    AutoComplete::AddAutocompleteWindow(0,1,@ToDisplayInList()) ;Just init the gadget who must support AutoComplete list
    AutoComplete::AddAutocompleteWindow(0,2,@ToDisplayInListB())
    Repeat
      Define Event.i = WaitWindowEvent()
      
      
      AutoComplete::CheckEvent(Event)
      
      Select Event
        Case #PB_Event_Gadget
      EndSelect
    Until Event=#PB_Event_CloseWindow
  EndIf
  End
CompilerEndIf
Last edited by thyphoon on Wed Jan 16, 2019 9:16 pm, edited 6 times in total.
User avatar
STARGÅTE
Addict
Addict
Posts: 1496
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: GadgetString's Autocomplete list

Post by STARGÅTE »

thyphoon wrote:1) First problem is how to find the height of the window title bar ? and also thickness of the window to correctly position.
You have to use #PB_Window_InnerCoordinate:

Code: Select all

    MainAC(Name)\X=WindowX(Windows, #PB_Window_InnerCoordinate)+GadgetX(Gadget)
    MainAC(Name)\Y=WindowY(Windows, #PB_Window_InnerCoordinate)+GadgetY(Gadget)+GadgetHeight(Gadget)
PB 5.73 ― Win 10, 20H2 ― Ryzen 9 3900X ― Radeon RX 5600 XT ITX ― Vivaldi 3.6 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
Kukulkan
Addict
Addict
Posts: 1261
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: GadgetString's Autocomplete list

Post by Kukulkan »

Maybe also interesting: viewtopic.php?f=12&t=52079
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

STARGÅTE wrote:
thyphoon wrote:1) First problem is how to find the height of the window title bar ? and also thickness of the window to correctly position.
You have to use #PB_Window_InnerCoordinate:

Code: Select all

    MainAC(Name)\X=WindowX(Windows, #PB_Window_InnerCoordinate)+GadgetX(Gadget)
    MainAC(Name)\Y=WindowY(Windows, #PB_Window_InnerCoordinate)+GadgetY(Gadget)+GadgetHeight(Gadget)
YEEEEEEEEEEEEEEEEEEES :mrgreen: Thanks
Kukulkan wrote:Maybe also interesting: viewtopic.php?f=12&t=52079
Very interesting. May be a mix of both is a goos idea :idea: :D
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

Big update 8)

Code: Select all

code updated on the first post
Last edited by thyphoon on Wed Jan 16, 2019 4:30 pm, edited 1 time in total.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: GadgetString's Autocomplete list

Post by srod »

The auto complete seems to work well. However, if I drag the window then any visible autocomplete box is left behind and the program crashes when I release the mouse on line 56 (Gadget# is not initialised).
I may look like a mule, but I'm not a complete ass.
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

srod wrote:The auto complete seems to work well. However, if I drag the window then any visible autocomplete box is left behind and the program crashes when I release the mouse on line 56 (Gadget# is not initialised).
thanks ! I will correct it !
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

I fix the problem when move the window :D Thanks Srod

Code: Select all

Code updated on the first Post
Last edited by thyphoon on Wed Jan 16, 2019 4:30 pm, edited 1 time in total.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: GadgetString's Autocomplete list

Post by srod »

Nearly! :) Still crashes on the same line. Drag the window as soon as you run the program - before opening one of the autocomplete boxes.
I may look like a mule, but I'm not a complete ass.
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

srod wrote:Nearly! :) Still crashes on the same line. Drag the window as soon as you run the program - before opening one of the autocomplete boxes.
:lol: Oups!!!!

Code: Select all

; ******************************************************************** 
; Program:           AutoComplete List
; Description:       Add an AutoComplete list to a String gadget 

; Author:            Thyphoon
; Date:              January, 2019
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; ******************************************************************** 

EnableExplicit

DeclareModule AutoComplete
  Declare AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
  Declare CheckEvent(Event.i)
EndDeclareModule  

Module AutoComplete
  
  Prototype.s CallBack(String.s)
  
  Structure ac
    Window.i                  ;Original Window
    Gadget.i                  ;Original Gadget
    FirstEventKey.l           ;First Eventkey=Up +1=Down +2=Enter +3=Escape
    CallBack.CallBack         ;CallBack who return choice
  EndStructure
  
  Structure params
    acWindow.i                ;Autocomplete window
    acGadget.i                ;AutoComplete GadgetList
    CurrentName.s             ;CurrentName at last open autocomplete windows
    Map AutoCompleteGadget.ac()
    FirstEventKey.i
  EndStructure
  
  Global params.params
  
  
  Procedure AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
    Name.s=Str(Window)+"-"+Str(Gadget)
    params\AutoCompleteGadget(Name)\CallBack=*CallBack
    params\AutoCompleteGadget(Name)\Gadget=Gadget
    params\AutoCompleteGadget(Name)\Window=Window
    params\AutoCompleteGadget(Name)\FirstEventKey=FirstEventKey
    AddKeyboardShortcut(Window, #PB_Shortcut_Up, FirstEventKey)
    AddKeyboardShortcut(Window, #PB_Shortcut_Down, FirstEventKey+1)
    AddKeyboardShortcut(Window, #PB_Shortcut_Return, FirstEventKey+2)
    AddKeyboardShortcut(Window, #PB_Shortcut_Escape, FirstEventKey+3)
  EndProcedure
  
  Procedure RefreshAutocompleteWindow(Name.s)
    Gadget.i=params\AutoCompleteGadget(Name)\Gadget
    Windows.i=params\AutoCompleteGadget(Name)\Window
    Debug Gadget
    X.l=WindowX(Windows,#PB_Window_InnerCoordinate)+GadgetX(Gadget)
    Y.l=WindowY(Windows,#PB_Window_InnerCoordinate)+GadgetY(Gadget)+GadgetHeight(Gadget)
    W.l=GadgetWidth(Gadget)
    H.l=150
    ResizeWindow(params\acWindow,X,Y,W,H)
    ResizeGadget(params\acGadget,0,0,W,H)
    HideWindow(params\acWindow, #False,#PB_Window_NoActivate)
    StickyWindow(params\acWindow,#True)
  EndProcedure
  
  Procedure UpdateAutocompleteList(Name.s)
    ClearGadgetItems(params\acGadget)
    If params\AutoCompleteGadget(Name)\CallBack<>0
      BackString.s=params\AutoCompleteGadget(Name)\CallBack(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))

      For n=1 To CountString(BackString,Chr(10))+1
        line.s=StringField(BackString,n,Chr(10))
        If LCase(Left(line,Len(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))))=LCase(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
          AddGadgetItem(params\acGadget,-1,line)
        EndIf 
      Next
      ;Hide Autocomplete Window if no Choice
      If CountGadgetItems(params\acGadget)=0
        HideWindow(params\acWindow,#True)
      Else
        HideWindow(params\acWindow,#False,#PB_Window_NoActivate)
      EndIf 
    Else
      Debug "Error with callback"
    EndIf
  EndProcedure
  
  Procedure OpenAutocompleteWindow(Name.s)
    If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
    If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acGadget):EndIf
    params\acWindow=OpenWindow(#PB_Any,0,0,50,50,"AutoComplete",#PB_Window_BorderLess|#PB_Window_NoActivate,WindowID(Window));
    If params\acWindow
      params\CurrentName=Name
      SetWindowData(params\acWindow,params\AutoCompleteGadget(Name)\Gadget)
      params\acGadget=ListViewGadget(#PB_Any, 0, 0, 50,50)
    EndIf
    RefreshAutocompleteWindow(Name)
    UpdateAutocompleteList(Name)
  EndProcedure
  
  Procedure CloseAutocompleteWindow(Name.s)
      Gadget.i=params\AutoCompleteGadget(Name)\Gadget
      Windows.i=params\AutoCompleteGadget(Name)\Window
      If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
      If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acWindow):EndIf
  EndProcedure
  
  Procedure CheckEvent(Event.i)
    Static bbclick.b
    ;-Orignal Gadget Event
    Protected Name.s=Str(EventWindow())+"-"+Str(EventGadget())   
    If FindMapElement(params\AutoCompleteGadget(),Name)
      Select Event
        Case  #PB_Event_Gadget
          Select EventGadget()
              ; Main gadget Event  
            Case params\AutoCompleteGadget()\Gadget
              Select EventType()
                Case #PB_EventType_Focus
                  If params\acWindow=0 Or IsWindow(params\acWindow)=#False
                    If bbclick=#False
                      OpenAutocompleteWindow(Name)
                    Else
                      bbclick=#True
                    EndIf 
                  EndIf 
                Case #PB_EventType_LostFocus
                  If GetActiveGadget()<>params\acGadget
                    CloseAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf  
                Case #PB_EventType_Change
                  If bbclick=#False 
                    If IsWindow(params\acWindow)
                      UpdateAutocompleteList(Name)
                    Else
                      Debug "pas de windows"
                    EndIf
                  Else
                    OpenAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf 
              EndSelect 

          EndSelect 
     EndSelect
    EndIf
    
    Select Event
      ;If Original Window Move  
      Case #PB_Event_MoveWindow,#PB_Event_SizeWindow 
        If EventWindow()=params\AutoCompleteGadget()\Window And params\acWindow>0 And IsWindow(params\acWindow)
          RefreshAutocompleteWindow(params\CurrentName)
        EndIf
      ;if Keyword  
      Case #PB_Event_Menu
        Select EventMenu()
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey;UP
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)-1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+1;Down
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)+1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+2;Enter
            SetGadgetText(params\AutoCompleteGadget()\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+3;
            CloseAutocompleteWindow(params\CurrentName)
        EndSelect
      Case  #PB_Event_Gadget
        If EventGadget()=params\acGadget
          If EventType()=#PB_EventType_LeftDoubleClick
            SetGadgetText(params\AutoCompleteGadget(params\CurrentName)\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
            CloseAutocompleteWindow(params\CurrentName)
            bbclick=#True
          EndIf
        EndIf 
    EndSelect  
    
    
    
  EndProcedure  
  
  
EndModule

;- MAIN TEST

CompilerIf #PB_Compiler_IsMainFile 
  
  ; Callback to return keyword list. you must use a database en filter if you want ^_^ 
  Procedure.s ToDisplayInList(String.s)
    ProcedureReturn "Amiga"+Chr(10)+"Amstrad"+Chr(10)+"Atari"+Chr(10)+"Commodore"+Chr(10)+"BeBox"+Chr(10)+"Macintosh"+Chr(10)+"Spectrum"
  EndProcedure
  
  Procedure.s ToDisplayInListB(String.s)
    ProcedureReturn "Alain"+Chr(10)+"Bernard"+Chr(10)+"Charly"+Chr(10)+"David"+Chr(10)+"Eric"+Chr(10)+"Filipe"+Chr(10)+"Horace"+Chr(10)+"Jérome"+Chr(10)+"Kevin"+Chr(10)+"Laurent"+Chr(10)+"Marc"
  EndProcedure
  
  If OpenWindow(0, 200, 200, 800, 300, "AutoComplete Teste", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    StringGadget(1, 10, 10, 380, 26, "")
    StringGadget(2, 410, 10, 380, 26, "")
    
    
    AutoComplete::AddAutocompleteWindow(0,1,@ToDisplayInList()) ;Just init the gadget who must support AutoComplete list
    AutoComplete::AddAutocompleteWindow(0,2,@ToDisplayInListB())
    Repeat
      Define Event.i = WaitWindowEvent()
      
      
      AutoComplete::CheckEvent(Event)
      
      Select Event
        Case #PB_Event_Gadget
      EndSelect
      
    Until Event=#PB_Event_CloseWindow
  EndIf
  End
CompilerEndIf
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: GadgetString's Autocomplete list

Post by srod »

Yep - that's better.

I have made a simple modification so that when you drag the window, the auto complete box moves at the same time. Just a simple BindEvent(). You should use a suitable UnbindEvent() at some point.

At the moment this code is not easily used in a different program because of the way you have effectively hard coded parent window info into the module. You could easily change this, however, and create a library which had no dependency whatsoever on the parent window. GadgetX and GadgetY calculations would need to use the #PB_Gadget_ScreenCoordinate flag. Also, you would need to use a hidden window as the parent window of your 'pop up' to stop a taskbar entry appearing. Your host app would need to call a suitable library function everytime a #PB_Event_MoveWindow event was received. I've done this sort of thing a few times.

One thing I have spotted is that you are relying upon #PB_EventType_LostFocus events firing for ListView gadgets. This is undocumented for this gadget which is why you will need to test this on the other platforms.

Anyhow, I post your program with the move window alteration :

Code: Select all

; ******************************************************************** 
; Program:           AutoComplete List
; Description:       Add an AutoComplete list to a String gadget 

; Author:            Thyphoon
; Date:              January, 2019
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; ******************************************************************** 

EnableExplicit

DeclareModule AutoComplete
  Declare AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
  Declare CheckEvent(Event.i)
EndDeclareModule  

Module AutoComplete
  Declare MoveWindow()

  Prototype.s CallBack(String.s)
  
  Structure ac
    Window.i                  ;Original Window
    Gadget.i                  ;Original Gadget
    FirstEventKey.l           ;First Eventkey=Up +1=Down +2=Enter +3=Escape
    CallBack.CallBack         ;CallBack who return choice
  EndStructure
  
  Structure params
    acWindow.i                ;Autocomplete window
    acGadget.i                ;AutoComplete GadgetList
    CurrentName.s             ;CurrentName at last open autocomplete windows
    Map AutoCompleteGadget.ac()
    FirstEventKey.i
  EndStructure
  
  Global params.params
  
  
  Procedure AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
    Name.s=Str(Window)+"-"+Str(Gadget)
    params\AutoCompleteGadget(Name)\CallBack=*CallBack
    params\AutoCompleteGadget(Name)\Gadget=Gadget
    params\AutoCompleteGadget(Name)\Window=Window
    params\AutoCompleteGadget(Name)\FirstEventKey=FirstEventKey
    AddKeyboardShortcut(Window, #PB_Shortcut_Up, FirstEventKey)
    AddKeyboardShortcut(Window, #PB_Shortcut_Down, FirstEventKey+1)
    AddKeyboardShortcut(Window, #PB_Shortcut_Return, FirstEventKey+2)
    AddKeyboardShortcut(Window, #PB_Shortcut_Escape, FirstEventKey+3)
    BindEvent(#PB_Event_MoveWindow, @MoveWindow(), Window)

  EndProcedure
  
  Procedure RefreshAutocompleteWindow(Name.s)
    Gadget.i=params\AutoCompleteGadget(Name)\Gadget
    Windows.i=params\AutoCompleteGadget(Name)\Window
    X.l=WindowX(Windows,#PB_Window_InnerCoordinate)+GadgetX(Gadget)
    Y.l=WindowY(Windows,#PB_Window_InnerCoordinate)+GadgetY(Gadget)+GadgetHeight(Gadget)
    W.l=GadgetWidth(Gadget)
    H.l=150
    ResizeWindow(params\acWindow,X,Y,W,H)
    ResizeGadget(params\acGadget,0,0,W,H)
    HideWindow(params\acWindow, #False,#PB_Window_NoActivate)
    StickyWindow(params\acWindow,#True)
  EndProcedure
  
  Procedure UpdateAutocompleteList(Name.s)
    ClearGadgetItems(params\acGadget)
    If params\AutoCompleteGadget(Name)\CallBack<>0
      BackString.s=params\AutoCompleteGadget(Name)\CallBack(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
      For n=1 To CountString(BackString,Chr(10))+1
        line.s=StringField(BackString,n,Chr(10))
        If LCase(Left(line,Len(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))))=LCase(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
          AddGadgetItem(params\acGadget,-1,line)
        EndIf 
      Next
    Else
      Debug "Error with callback"
    EndIf
  EndProcedure
  
  Procedure OpenAutocompleteWindow(Name.s)
    If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
    If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acGadget):EndIf
    params\acWindow=OpenWindow(#PB_Any,0,0,50,50,"AutoComplete",#PB_Window_BorderLess|#PB_Window_NoActivate,WindowID(Window));
    If params\acWindow
      params\CurrentName=Name
      SetWindowData(params\acWindow,params\AutoCompleteGadget(Name)\Gadget)
      params\acGadget=ListViewGadget(#PB_Any, 0, 0, 50,50)
    EndIf
    RefreshAutocompleteWindow(Name)
    UpdateAutocompleteList(Name)
  EndProcedure
  
  Procedure CloseAutocompleteWindow(Name.s)
      Gadget.i=params\AutoCompleteGadget(Name)\Gadget
      Windows.i=params\AutoCompleteGadget(Name)\Window
      If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
      If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acWindow):EndIf
  EndProcedure
  
  Procedure MoveWindow()
    If EventWindow()=params\AutoCompleteGadget()\Window And params\acWindow>0 And IsWindow(params\acWindow)
      RefreshAutocompleteWindow(params\CurrentName)
    EndIf
  EndProcedure
  

  Procedure CheckEvent(Event.i)
    Static bbclick.b
    ;-Orignal Gadget Event
    Protected Name.s=Str(EventWindow())+"-"+Str(EventGadget())   
    If FindMapElement(params\AutoCompleteGadget(),Name)
      Select Event
        Case  #PB_Event_Gadget
          Select EventGadget()
              ; Main gadget Event  
            Case params\AutoCompleteGadget()\Gadget
              Select EventType()
                Case #PB_EventType_Focus
                  If params\acWindow=0 Or IsWindow(params\acWindow)=#False
                    If bbclick=#False
                      OpenAutocompleteWindow(Name)
                    Else
                      bbclick=#True
                    EndIf 
                  EndIf 
                Case #PB_EventType_LostFocus
                  If GetActiveGadget()<>params\acGadget
                    CloseAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf  
                Case #PB_EventType_Change
                  If bbclick=#False 
                    If IsWindow(params\acWindow)
                      UpdateAutocompleteList(Name)
                    Else
                      Debug "pas de windows"
                    EndIf
                  Else
                    OpenAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf 
              EndSelect 

          EndSelect 
     EndSelect
    EndIf
    
    Select Event
      ;If Original Window Move  
      Case #PB_Event_MoveWindow,#PB_Event_SizeWindow 
;        If EventWindow()=params\AutoCompleteGadget()\Window And params\acWindow>0 And IsWindow(params\acWindow)
;          RefreshAutocompleteWindow(params\CurrentName)
;        EndIf
      ;if Keyword  
      Case #PB_Event_Menu
        Select EventMenu()
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey;UP
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)-1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+1;Down
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)+1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+2;Enter
            SetGadgetText(params\AutoCompleteGadget()\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+3;
            CloseAutocompleteWindow(params\CurrentName)
        EndSelect
      Case  #PB_Event_Gadget
        If EventGadget()=params\acGadget
          If EventType()=#PB_EventType_LeftDoubleClick
            SetGadgetText(params\AutoCompleteGadget(params\CurrentName)\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
            CloseAutocompleteWindow(params\CurrentName)
            bbclick=#True
          EndIf
        EndIf 
    EndSelect  
    
    
    
  EndProcedure  
  
  
EndModule

;- MAIN TEST

CompilerIf #PB_Compiler_IsMainFile 
  
  ; Callback to return keyword list. you must use a database en filter if you want ^_^ 
  Procedure.s ToDisplayInList(String.s)
    ProcedureReturn "Amiga"+Chr(10)+"Amstrad"+Chr(10)+"Atari"+Chr(10)+"Commodore"+Chr(10)+"BeBox"+Chr(10)+"Macintosh"+Chr(10)+"Spectrum"
  EndProcedure
  
  Procedure.s ToDisplayInListB(String.s)
    ProcedureReturn "Alain"+Chr(10)+"Bernard"+Chr(10)+"Charly"+Chr(10)+"David"+Chr(10)+"Eric"+Chr(10)+"Filipe"+Chr(10)+"Horace"+Chr(10)+"Jérome"+Chr(10)+"Kevin"+Chr(10)+"Laurent"+Chr(10)+"Marc"
  EndProcedure
  
  If OpenWindow(0, 200, 200, 800, 100, "AutoComplete Teste", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    StringGadget(1, 10, 10, 380, 26, "")
    StringGadget(2, 410, 10, 380, 26, "")
    
    AutoComplete::AddAutocompleteWindow(0,1,@ToDisplayInList()) ;Just init the gadget who must support AutoComplete list
    AutoComplete::AddAutocompleteWindow(0,2,@ToDisplayInListB())
    Repeat
      Define Event.i = WaitWindowEvent()
      
      
      AutoComplete::CheckEvent(Event)
      
      Select Event
        Case #PB_Event_Gadget
      EndSelect
    Until Event=#PB_Event_CloseWindow
  EndIf
  End
CompilerEndIf
I may look like a mule, but I'm not a complete ass.
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

srod wrote:Yep - that's better.

I have made a simple modification so that when you drag the window, the auto complete box moves at the same time. Just a simple BindEvent(). You should use a suitable UnbindEvent() at some point.

At the moment this code is not easily used in a different program because of the way you have effectively hard coded parent window info into the module. You could easily change this, however, and create a library which had no dependency whatsoever on the parent window. GadgetX and GadgetY calculations would need to use the #PB_Gadget_ScreenCoordinate flag. Also, you would need to use a hidden window as the parent window of your 'pop up' to stop a taskbar entry appearing. Your host app would need to call a suitable library function everytime a #PB_Event_MoveWindow event was received. I've done this sort of thing a few times.

One thing I have spotted is that you are relying upon #PB_EventType_LostFocus events firing for ListView gadgets. This is undocumented for this gadget which is why you will need to test this on the other platforms.

Anyhow, I post your program with the move window alteration :
Big Thanks :D I will analyse all your tips and try to update my code.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: GadgetString's Autocomplete list

Post by srod »

Don't get me wrong, that's a nice utility which could be very handy indeed. Very useful. Thanks. :)
I may look like a mule, but I'm not a complete ass.
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 253
Joined: Sat Dec 25, 2004 2:37 pm

Re: GadgetString's Autocomplete list

Post by thyphoon »

srod wrote:Don't get me wrong, that's a nice utility which could be very handy indeed. Very useful. Thanks. :)
Big Thanks to you for your Help :D I love Purebasic and I love to share my work if can help somebody
srod wrote: At the moment this code is not easily used in a different program because of the way you have effectively hard coded parent window info into the module. You could easily change this, however, and create a library which had no dependency whatsoever on the parent window.
I can't remove all parent window dependency. I must use AddKeyboardShortcut() who ask the parend window. And i don't found how to found Window with only Gadget Id.
srod wrote: One thing I have spotted is that you are relying upon #PB_EventType_LostFocus events firing for ListView gadgets. This is undocumented for this gadget which is why you will need to test this on the other platforms.
I don't understant , In documentation EventType() speak about #PB_EventType_LostFocus and is compatible with StringGadget()

Last Version

Code: Select all

; ******************************************************************** 
; Program:           AutoComplete List
; Description:       Add an AutoComplete list to a String gadget 

; Author:            Thyphoon
; Date:              January, 2019
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; Thanks to:         Srod
; ******************************************************************** 

EnableExplicit

DeclareModule AutoComplete
  Declare AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
  Declare CheckEvent(Event.i)
EndDeclareModule  

Module AutoComplete
  Declare MoveWindow()

  Prototype.s CallBack(String.s)
  
  Structure ac
    Window.i                  ;Original Window
    Gadget.i                  ;Original Gadget
    FirstEventKey.l           ;First Eventkey=Up +1=Down +2=Enter +3=Escape
    CallBack.CallBack         ;CallBack who return choice
  EndStructure
  
  Structure params
    acWindow.i                ;Autocomplete window
    acGadget.i                ;AutoComplete GadgetList
    CurrentName.s             ;CurrentName at last open autocomplete windows
    Map AutoCompleteGadget.ac()
    FirstEventKey.i
  EndStructure
  
  Global params.params
  
  
  Procedure AddAutocompleteWindow(Window.i,Gadget.i,*CallBack,FirstEventKey.l=5000)
    Name.s=Str(Window)+"-"+Str(Gadget)
    params\AutoCompleteGadget(Name)\CallBack=*CallBack
    params\AutoCompleteGadget(Name)\Gadget=Gadget
    params\AutoCompleteGadget(Name)\Window=Window
    params\AutoCompleteGadget(Name)\FirstEventKey=FirstEventKey
    AddKeyboardShortcut(Window, #PB_Shortcut_Up, FirstEventKey)
    AddKeyboardShortcut(Window, #PB_Shortcut_Down, FirstEventKey+1)
    AddKeyboardShortcut(Window, #PB_Shortcut_Return, FirstEventKey+2)
    AddKeyboardShortcut(Window, #PB_Shortcut_Escape, FirstEventKey+3)
  EndProcedure
  
  Procedure RefreshAutocompleteWindow(Name.s)
    Gadget.i=params\AutoCompleteGadget(Name)\Gadget
    X.l=GadgetX(Gadget,#PB_Gadget_ScreenCoordinate)
    Y.l=GadgetY(Gadget,#PB_Gadget_ScreenCoordinate)+GadgetHeight(Gadget)
    W.l=GadgetWidth(Gadget)
    H.l=150
    ResizeWindow(params\acWindow,X,Y,W,H)
    ResizeGadget(params\acGadget,0,0,W,H)
    HideWindow(params\acWindow, #False,#PB_Window_NoActivate)
    StickyWindow(params\acWindow,#True)
  EndProcedure
  
  Procedure UpdateAutocompleteList(Name.s)
    ClearGadgetItems(params\acGadget)
    If params\AutoCompleteGadget(Name)\CallBack<>0
      BackString.s=params\AutoCompleteGadget(Name)\CallBack(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
      For n=1 To CountString(BackString,Chr(10))+1
        line.s=StringField(BackString,n,Chr(10))
        If LCase(Left(line,Len(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))))=LCase(GetGadgetText(params\AutoCompleteGadget(Name)\Gadget))
          AddGadgetItem(params\acGadget,-1,line)
        EndIf 
      Next
      ;Hide Autocomplete Window if no Choice
      If CountGadgetItems(params\acGadget)=0
        HideWindow(params\acWindow,#True)
      Else
        HideWindow(params\acWindow,#False,#PB_Window_NoActivate)
      EndIf 
    Else
      Debug "Error with callback"
    EndIf
  EndProcedure
  
  Procedure OpenAutocompleteWindow(Name.s)
    If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
    If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acWindow):EndIf
    params\acWindow=OpenWindow(#PB_Any,0,0,50,50,"AutoComplete",#PB_Window_BorderLess|#PB_Window_NoActivate,WindowID(params\AutoCompleteGadget(Name)\Window));
    If params\acWindow
      params\CurrentName=Name
      SetWindowData(params\acWindow,params\AutoCompleteGadget(Name)\Gadget)
      params\acGadget=ListViewGadget(#PB_Any, 0, 0, 50,50)
    EndIf
    RefreshAutocompleteWindow(Name)
    UpdateAutocompleteList(Name)
    BindEvent(#PB_Event_MoveWindow, @MoveWindow(), params\AutoCompleteGadget(Name)\Window)
  EndProcedure
  
  Procedure CloseAutocompleteWindow(Name.s)
      If params\acGadget>0 And IsGadget(params\acGadget):FreeGadget(params\acGadget):EndIf
      If params\acWindow>0 And IsWindow(params\acWindow):CloseWindow(params\acWindow):EndIf
      UnbindEvent(#PB_Event_MoveWindow, @MoveWindow(), Windows)
  EndProcedure
  
  Procedure MoveWindow()
    If EventWindow()=params\AutoCompleteGadget()\Window And params\acWindow>0 And IsWindow(params\acWindow)
      RefreshAutocompleteWindow(params\CurrentName)
    EndIf
  EndProcedure
  

  Procedure CheckEvent(Event.i)
    Static bbclick.b
    ;-Orignal Gadget Event
    Protected Name.s=Str(EventWindow())+"-"+Str(EventGadget())   
    If FindMapElement(params\AutoCompleteGadget(),Name)
      Select Event
        Case  #PB_Event_Gadget
          Select EventGadget()
              ; Main gadget Event  
            Case params\AutoCompleteGadget()\Gadget
              Select EventType()
                Case #PB_EventType_Focus
                  If params\acWindow=0 Or IsWindow(params\acWindow)=#False
                    If bbclick=#False
                      OpenAutocompleteWindow(Name)
                    Else
                      bbclick=#True
                    EndIf 
                  EndIf 
                Case #PB_EventType_LostFocus
                  If GetActiveGadget()<>params\acGadget
                    CloseAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf  
                Case #PB_EventType_Change
                  If bbclick=#False 
                    If IsWindow(params\acWindow)
                      UpdateAutocompleteList(Name)
                    Else
                      Debug "pas de windows"
                    EndIf
                  Else
                    OpenAutocompleteWindow(Name)
                    bbclick=#False
                  EndIf 
              EndSelect 

          EndSelect 
     EndSelect
    EndIf
    
    Select Event
      ;if Keyword  
      Case #PB_Event_Menu
        Select EventMenu()
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey;UP
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)-1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+1;Down
            SetGadgetState(params\acGadget,GetGadgetState(params\acGadget)+1)
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+2;Enter
            SetGadgetText(params\AutoCompleteGadget()\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
          Case params\AutoCompleteGadget(params\CurrentName)\FirstEventKey+3;
            CloseAutocompleteWindow(params\CurrentName)
        EndSelect
      Case  #PB_Event_Gadget
        If EventGadget()=params\acGadget
          If EventType()=#PB_EventType_LeftDoubleClick
            SetGadgetText(params\AutoCompleteGadget(params\CurrentName)\Gadget,GetGadgetItemText(params\acGadget,GetGadgetState(params\acGadget)))
            CloseAutocompleteWindow(params\CurrentName)
            bbclick=#True
          EndIf
        EndIf 
    EndSelect  
    
    
    
  EndProcedure  
  
  
EndModule

;- MAIN TEST

CompilerIf #PB_Compiler_IsMainFile 
  
  ; Callback to return keyword list. you must use a database en filter if you want ^_^ 
  Procedure.s ToDisplayInList(String.s)
    ProcedureReturn "Amiga"+Chr(10)+"Amstrad"+Chr(10)+"Atari"+Chr(10)+"Commodore"+Chr(10)+"BeBox"+Chr(10)+"Macintosh"+Chr(10)+"Spectrum"
  EndProcedure
  
  Procedure.s ToDisplayInListB(String.s)
    ProcedureReturn "Alain"+Chr(10)+"Bernard"+Chr(10)+"Charly"+Chr(10)+"David"+Chr(10)+"Eric"+Chr(10)+"Filipe"+Chr(10)+"Horace"+Chr(10)+"Jérome"+Chr(10)+"Kevin"+Chr(10)+"Laurent"+Chr(10)+"Marc"
  EndProcedure
  
  If OpenWindow(0, 200, 200, 800, 100, "AutoComplete Teste", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    StringGadget(1, 10, 10, 380, 26, "")
    StringGadget(2, 410, 10, 380, 26, "")
    
    AutoComplete::AddAutocompleteWindow(0,1,@ToDisplayInList()) ;Just init the gadget who must support AutoComplete list
    AutoComplete::AddAutocompleteWindow(0,2,@ToDisplayInListB())
    Repeat
      Define Event.i = WaitWindowEvent()
      
      
      AutoComplete::CheckEvent(Event)
      
      Select Event
        Case #PB_Event_Gadget
      EndSelect
    Until Event=#PB_Event_CloseWindow
  EndIf
  End
CompilerEndIf
And excuse me for my bad english :P
Post Reply