PureBasic Forum
http://forums.purebasic.com/english/

GadgetString's Autocomplete list
http://forums.purebasic.com/english/viewtopic.php?f=12&t=72084
Page 1 of 1

Author:  thyphoon [ Tue Jan 15, 2019 10:03 am ]
Post subject:  GadgetString's Autocomplete list

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:
; ********************************************************************
; 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

Author:  STARGÅTE [ Tue Jan 15, 2019 1:28 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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:
    MainAC(Name)\X=WindowX(Windows, #PB_Window_InnerCoordinate)+GadgetX(Gadget)
    MainAC(Name)\Y=WindowY(Windows, #PB_Window_InnerCoordinate)+GadgetY(Gadget)+GadgetHeight(Gadget)

Author:  Kukulkan [ Tue Jan 15, 2019 4:52 pm ]
Post subject:  Re: GadgetString's Autocomplete list

Maybe also interesting: https://www.purebasic.fr/english/viewtopic.php?f=12&t=52079

Author:  thyphoon [ Tue Jan 15, 2019 4:56 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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:
    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:

Very interesting. May be a mix of both is a goos idea :idea: :D

Author:  thyphoon [ Wed Jan 16, 2019 3:24 pm ]
Post subject:  Re: GadgetString's Autocomplete list

Big update 8)
Code:
code updated on the first post

Author:  srod [ Wed Jan 16, 2019 3:28 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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).

Author:  thyphoon [ Wed Jan 16, 2019 3:47 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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 !

Author:  thyphoon [ Wed Jan 16, 2019 3:54 pm ]
Post subject:  Re: GadgetString's Autocomplete list

I fix the problem when move the window :D Thanks Srod
Code:
Code updated on the first Post

Author:  srod [ Wed Jan 16, 2019 3:56 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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

Author:  thyphoon [ Wed Jan 16, 2019 4:27 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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:
; ********************************************************************
; 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

Author:  srod [ Wed Jan 16, 2019 5:01 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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:
; ********************************************************************
; 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

Author:  thyphoon [ Wed Jan 16, 2019 5:12 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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.

Author:  srod [ Wed Jan 16, 2019 5:17 pm ]
Post subject:  Re: GadgetString's Autocomplete list

Don't get me wrong, that's a nice utility which could be very handy indeed. Very useful. Thanks. :)

Author:  thyphoon [ Wed Jan 16, 2019 9:13 pm ]
Post subject:  Re: GadgetString's Autocomplete list

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:
; ********************************************************************
; 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

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/