Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Looking for a Dialog Example with Interactive List Filtering
#1
Hi
I would like to create a dialog where I can:
- Type text into a field
- As the text is typed, display a list of items that contain the text
- Be able to use the arrow keys and Enter, or mouse-click to select one of the matching items to perform an action.

The QM Editor Find dialog does everything I need, but I cannot find a dialog file for it.

Is there an example of a dialog that has this functionality?
Thanks for your help,
John
#2
Function dlg_list_filtering
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

str- sItems=
;January
;February
;March
;April
;May

str controls = "3 4"
str e3 lb4
if(!ShowDialog("dlg_list_filtering" &dlg_list_filtering &controls)) ret

;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;3 Edit 0x54030080 0x200 0 0 96 14 ""
;4 ListBox 0x54230101 0x200 0 16 96 102 ""
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x203000E "*" "" ""

ret
;messages
sel message
,case WM_INITDIALOG
,case WM_DESTROY
,case WM_COMMAND goto messages2
,case WM_TIMER
,sel wParam
,,case 1
,,KillTimer hDlg wParam
,,int hlb=id(4 hDlg)
,,SendMessage hlb LB_RESETCONTENT 0 0
,,str s sEdit.getwintext(id(3 hDlg))
,,if(!sEdit.len) ret
,,foreach s sItems
,,,if(find(s sEdit 0 1)<0) continue
,,,LB_Add hlb s
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3
,SetTimer hDlg 1 300 0
,case IDOK
,case IDCANCEL
ret 1
#3
Thank you, Gintaras.
That is exactly what I was looking for.
Your support is the stuff of legends Smile
Cheers,
John
#4
Hi Gintaras
I tinkered with your example to display all items when the edit box is empty and to work out which item is clicked.
I would now like get messages when a key is pressed so I can changed the selected item with the up and down arrow keys (without pressing tab first), leaving the typing focus in the edit box (like the Gmail / Compose Mail / To: drop down list that lets you either keep typing or select from the list with the arrow keys).
Thanks for your help,
John


\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

str- sItems=
;January
;February
;March
;April
;May

str controls = "3 4"
str e3 lb4
if(!ShowDialog("dlg_list_filtering" &dlg_list_filtering &controls)) ret

;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;3 Edit 0x54030080 0x200 0 0 96 14 ""
;4 ListBox 0x54230101 0x200 0 16 96 102 ""
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x203000E "*" "" ""

ret
;messages
int hlb=id(4 hDlg)
str s
int- c
if(message=WM_SETCURSOR) ret
c+1
out "in message: %i %x %x" c message WM_SETCURSOR
sel message
,case WM_INITDIALOG
,,foreach s sItems
,,,LB_Add hlb s
,,LB_SelectItem hlb 0
,case WM_DESTROY
,case WM_COMMAND goto messages2
,case WM_CTLCOLORLISTBOX
,,int pos=LB_SelectedItem(hlb s)
,,out "Mouse item=%i %s" pos s
,case WM_TIMER
,out "in message2: %i %i" c message
,sel wParam
,,case 1
,,KillTimer hDlg wParam
,,hlb=id(4 hDlg)
,,SendMessage hlb LB_RESETCONTENT 0 0
,,str sEdit.getwintext(id(3 hDlg))
,,;if(!sEdit.len) ret
,,foreach s sItems
,,,if(sEdit.len>0 && find(s sEdit 0 1)<0) continue
,,,LB_Add hlb s
,,LB_SelectItem hlb 0
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3
,SetTimer hDlg 1 100 0
,case IDOK
,case IDCANCEL
ret 1
#5
Key messages are sent to focused control. Dialog does not receive them. To get messages sent to a control, subclass the control.

Function dlg_list_filtering
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

str- sItems=
;January
;February
;March
;April
;May

str controls = "3 4"
str e3 lb4
if(!ShowDialog("dlg_list_filtering" &dlg_list_filtering &controls)) ret

;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;3 Edit 0x54030080 0x200 0 0 96 14 ""
;4 ListBox 0x54230101 0x200 0 16 96 102 ""
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x203000E "*" "" ""

ret
;messages
sel message
,case WM_INITDIALOG
,int he=id(3 hDlg)
,SetProp he "wndproc" SubclassWindow(he &MyEditSubclassProc)
,
,case WM_DESTROY
,RemoveProp he "wndproc"
,
,case WM_COMMAND goto messages2
,case WM_TIMER
,sel wParam
,,case 1
,,KillTimer hDlg wParam
,,int hlb=id(4 hDlg)
,,SendMessage hlb LB_RESETCONTENT 0 0
,,str s sEdit.getwintext(id(3 hDlg))
,,if(!sEdit.len) ret
,,foreach s sItems
,,,if(find(s sEdit 0 1)<0) continue
,,,LB_Add hlb s
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3
,SetTimer hDlg 1 300 0
,case IDOK
,case IDCANCEL
ret 1

(changed only WM_INITDIALOG and WM_DESTROY code)

Function MyEditSubclassProc
Code:
Copy      Help
;/
function# hWnd message wParam lParam

;__OutWinMsg message wParam lParam
sel message
,case WM_DESTROY
,case [WM_KEYDOWN,WM_KEYUP]
,sel wParam ;;virtual key code
,,case [VK_DOWN,VK_UP,VK_PRIOR,VK_NEXT]
,,;relay these keys to the listbox and not to the edit box
,,SendMessage id(4 GetParent(hWnd)) message wParam lParam
,,ret

int wndproc=GetProp(hWnd "wndproc"); if(!wndproc) ret
ret CallWindowProcW(wndproc hWnd message wParam lParam)
#6
Perfect.
Thanks for your support, Gintaras.
Regards,
John
#7
Is there anyway to have it fill the edit box with the text selected from the list?

Also is there anyway to make the box drop down instead of being constantly displayed? Like changing visibility options?

Thanks,
Jimmy Vig
#8
I've made a bit of progress with my selection dialog.
TheVig: the MyEditSubclassProc SubclassWindow function inserts the select listbox line when the insert key is pressed.

Gintaras:
1. When a key I want to use with the listbox is detected in the MyEditSubclassProc function, can I "eat" the key so that the parent "listDialog" function does not receive it?

2. Is there some way to detect the typing cursor position in the edit field?
I would like to be able to use the right-arrow key to insert the currently selected listbox item, but only when the cursor is at the end of the edit field.

Thanks,
John


Function MyEditSubclassProc
Code:
Copy      Help
;/
function# hWnd message wParam lParam

;__OutWinMsg message wParam lParam
sel message
,case WM_DESTROY
,case [WM_KEYDOWN] ;; ,WM_KEYUP
,int he=id(3 GetParent(hWnd))
,int hlb=id(4 GetParent(hWnd))
,;out "wParam=%i" wParam
,sel wParam ;;virtual key code
,,
,,case [VK_DOWN,VK_UP,VK_PRIOR,VK_NEXT]
,,;relay these keys to the listbox and not to the edit box
,,;out "%s: %i %i %i" fnName wParam=VK_UP LB_SelectedItem(hlb) LB_GetCount(hlb)
,,if wParam = VK_UP and LB_SelectedItem(hlb) = -1
,,,;If the up arrow is pressed and nothing is highlighted, then highlight the last entry
,,,LB_SelectItem hlb LB_GetCount(hlb)-1
,,else
,,,SendMessage hlb message wParam lParam
,,ret
,,
,,case [VK_INSERT]
,,; Copy current list value to text entry field
,,str sEdit.getwintext(he)
,,out "right: %s" sEdit
,,str s
,,LB_SelectedItem(hlb s) ;; Set edit box to selected item text
,,;; Use LB_GetItemText
,,s.setwintext(he)
,,SendMessage he message VK_END lParam ;; Move cursor to end of new entry field
,,ret
,,;case else
,,;SendMessage hlb message wParam lParam
,,;ret
,; Select one of the list items
,if(wParam>='0' && wParam<='9')
,,int ind=wParam-'0'
,,LB_SelectItem hlb ind

int wndproc=GetProp(hWnd "wndproc"); if(!wndproc) ret
ret CallWindowProcW(wndproc hWnd message wParam lParam)


Attached Files
.qml   ListSelect.qml (Size: 5.71 KB / Downloads: 511)
#9
1. Parent does not receive messages that are received by the subclass function. To prevent default edit control processing of some keys, don't call CallWindowProcW for these keys. The function also relays the message to another control. If not needed, remove SendMessage.

2. Look in MSDN library, EM_GETSEL.
#10
Thanks, Gintaras.
EM_GETSEL worked well.
Cheers,
John

Function listDialogEditSubclassProc
Code:
Copy      Help
;/
function# hWnd message wParam lParam

sel message
,case WM_DESTROY
,case [WM_PAINT, WM_GETTEXTLENGTH, WM_GETTEXT];; These messages happen lots
,case WM_GETDLGCODE ;; Happens twice per letter keypress
,case WM_SYSKEYDOWN ;; Happens repeatedly while Alt key down
,case [WM_KEYUP]; ret ;; Ignore keyup messages
,case [WM_KEYDOWN]
,int he=id(3 GetParent(hWnd))
,int hlb=id(4 GetParent(hWnd))
,;out "wParam=%i" wParam
,sel wParam ;;virtual key code
,,
,,case [VK_DOWN,VK_UP,VK_PRIOR,VK_NEXT]
,,;relay these keys to the listbox and not to the edit box
,,;out "%s: %i %i %i" fnName wParam=VK_UP LB_SelectedItem(hlb) LB_GetCount(hlb)
,,if wParam = VK_UP and LB_SelectedItem(hlb) = -1
,,,;If the up arrow is pressed and nothing is highlighted, then highlight the last entry
,,,LB_SelectItem hlb LB_GetCount(hlb)-1
,,else
,,,SendMessage hlb message wParam lParam
,,ret
,,
,,case [VK_INSERT,VK_RIGHT]
,,int cursorPos; SendMessage(he EM_GETSEL 0 &cursorPos)
,,str sEdit.getwintext(he)
,,if cursorPos <> len(sEdit)
,,,goto passMessageUp ;; Only replace edit text when cursor is at end of edit field
,,if(LB_SelectedItem(hlb) = -1); ret ;; Exit if no list item selected
,,; Copy current list value to text entry field
,,str s
,,LB_SelectedItem(hlb s) ;; Set edit box to selected item text
,,s.setwintext(he)
,,SendMessage he message VK_END lParam ;; Move cursor to end of new entry field
,,ret

,case WM_CHAR ;; Standard letter
,;out "WM_CHAR %i %c" wParam wParam
,;sel wParam ;;virtual key code
,,;case ['a']
,,;ret
,case WM_SYSKEYUP ;; Fires when Alt-letter keyup (but alt still held down)
,hlb=id(4 GetParent(hWnd))
,; Select one of the list items
,if(wParam>='A' && wParam<='Z')
,,int ind = wParam - 'A'
,,LB_SelectItem hlb ind
,,ret
,case else ;; for sel message
,int- cxx; cxx+1
,;out "%s uncaught message %i: x%X(%i) %i %i %i" fnName cxx message message wParam lParam hWnd

;passMessageUp
int wndproc=GetProp(hWnd "wndproc"); if(!wndproc) ret
ret CallWindowProcW(wndproc hWnd message wParam lParam)
#11
how to get data from control for this dialog?
#12
I was referring to the May 31 post. I will look at the follow up posts for how to get the data from the controls.
#13
found it in ListSelect.qml download.
#14
I tried to add a horizontal scrollbar to ListSelect.qml but can't make it work. I want to be able to add lines in the list that are longer than the dialog window width and scroll horizontally to see the rest of the lines.


Attached Files
.qml   ListSelectWithHorizScrollbar.qml (Size: 2.26 KB / Downloads: 448)
#15
Hi!

I find this thread very useful for what I want to do....
But I have one little problem: I'm using QM 2.2.1.5
LB_Add doesn't exist in this release, so I want ask you for two things:
1st. If I add the file LB_add will just run fine with my old version?
And second, only if the first question was true, can someone send me this function?

Thanks for all...
#16
Function LB_Add
Code:
Copy      Help
;/
function# hlb $text [itemdata]

;Adds an item to a list box control.
;Returns new item index. On error, returns a negative value.
;Added in QM 2.3.0.


int ni=SendMessageW(hlb LB_ADDSTRING 0 @text)
if(itemdata) SendMessage(hlb LB_SETITEMDATA ni itemdata)
ret ni
#17
It's not working
SendMessageW is not recognized Sad
#18
Change to SendMessage, and delete the @.
#19
Fine! Thank you very much! Big Grin Big Grin
#20
Goodday!

The dialog closes after using it, how can i use it use the selection and use it over again? Is that possible?

Thanx
#21
Function dlg_list_filtering
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

str- sItems=
;January
;February
;March
;April
;May

str controls = "3 4"
str e3 lb4
if(!ShowDialog("dlg_list_filtering" &dlg_list_filtering &controls)) ret

;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;3 Edit 0x54030080 0x200 0 0 96 14 ""
;4 ListBox 0x54230101 0x200 0 16 96 102 ""
;5 Button 0x54032000 0x0 110 16 110 34 "Select an item in the list and click me; or double click an item"
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x2030304 "*" "" ""

ret
;messages
sel message
,case WM_INITDIALOG
,int he=id(3 hDlg)
#ifdef MyEditSubclassProc
,SetProp he "wndproc" SubclassWindow(he &MyEditSubclassProc)
#endif
,
,case WM_DESTROY
,RemoveProp he "wndproc"
,
,case WM_COMMAND goto messages2
,case WM_TIMER
,sel wParam
,,case 1
,,KillTimer hDlg wParam
,,int hlb=id(4 hDlg)
,,SendMessage hlb LB_RESETCONTENT 0 0
,,str s sEdit.getwintext(id(3 hDlg))
,,if(!sEdit.len) ret
,,foreach s sItems
,,,if(find(s sEdit 0 1)<0) continue
,,,LB_Add hlb s
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3
,SetTimer hDlg 1 300 0
,
,case LBN_DBLCLK<<16|4
,goto gShowSelected
,
,case 5
,goto gShowSelected
,
,case IDOK
,case IDCANCEL
ret 1

;gShowSelected
hlb=id(4 hDlg)
_i=LB_SelectedItem(hlb)
LB_GetItemText hlb _i s
mes s
#22
Thanks!
#23
How to show at first all items, when typing only matched items are shown?
#24
Replace
Code:
Copy      Help
,,if(!sEdit.len) ret
,,foreach s sItems
,,,if(find(s sEdit 0 1)<0) continue
to
Code:
Copy      Help
,,foreach s sItems
,,,if(sEdit.len and find(s sEdit 0 1)<0) continue
Also insert under case WM_INITDIALOG:
Code:
Copy      Help
,SetTimer hDlg 1 10 0
#25
One other question,

Is it possible to 'tag' searches, like i search for fruit, apple and banana show up?
#26
With Sqlite database.

Function dlg_list_filtering_tag
Code:
Copy      Help
\Dialog_Editor

str databaseFile="$my qm$\test5725w.db3" ;;change this
int createTableNow=1 ;;change 1 to 0 if don't want to create table everytime here

Sqlite- db
db.Open(databaseFile 0 4)

;create an example table and add several items
if createTableNow
,str sql=
,;PRAGMA journal_mode=WAL;
,;BEGIN;
,;DROP TABLE IF EXISTS t;
,;CREATE TABLE t(name,tag);
,;INSERT INTO t VALUES
,;('January','month'),
,;('Apple','fruit'),
,;('Banana','fruit'),
,;('April','month'),
,;('Other','');
,;COMMIT
,db.Exec(sql)
;Edit the above sql string.
;To add/remove/edit items later, you can anywhere use code like this (except the DROP TABLE and CREATE TABLE lines).
;;;Or use a database management program, eg SQLite Expert Personal.
;;;Then set createTableNow=0 or remove the creation code.

str dd=
;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;3 Edit 0x54030080 0x200 0 0 96 14 ""
;4 ListBox 0x54230101 0x200 0 16 96 102 ""
;5 Button 0x54032000 0x0 112 76 110 34 "Select an item in the list and click me; or double click an item"
;6 Static 0x54000000 0x0 104 0 18 12 "Tag"
;7 ComboBox 0x54230242 0x0 124 0 96 213 ""
;8 Button 0x54012003 0x0 124 16 48 10 "Sort"
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x2040201 "*" "" "" ""

str controls = "3 4 7 8"
str e3 lb4 cb7 c8Sor
if(!ShowDialog(dd &sub.DlgProc &controls)) ret


#sub DlgProc
function# hDlg message wParam lParam

Sqlite- db
int hlb hcb i
str sql sName sTag
ARRAY(str) a

sel message
,case WM_INITDIALOG
,SetProp id(3 hDlg) "wndproc" SubclassWindow(id(3 hDlg) &sub.EditSubclassProc) ;;optional, just to select listbox items with arrow keys when the control is not focused
,
,;populate the Tag combo
,hcb=id(7 hDlg)
,CB_Add(hcb "")
,db.Exec("SELECT DISTINCT tag FROM t" a)
,for(i 0 a.len) CB_Add(hcb a[0 i])
,
,SetTimer hDlg 1 10 0
,
,case WM_DESTROY
,RemoveProp id(3 hDlg) "wndproc"
,
,case WM_COMMAND goto messages2
,
,case WM_TIMER
,sel wParam
,,case 1 KillTimer hDlg wParam; goto gUpdateList
ret
;messages2
sel wParam
,case [EN_CHANGE<<16|3, CBN_EDITCHANGE<<16|7]
,SetTimer hDlg 1 300 0
,case [CBN_SELCHANGE<<16|7, 8]
,SetTimer hDlg 1 10 0
,
,case LBN_DBLCLK<<16|4 goto gShowSelected
,case 5 goto gShowSelected
,
,case IDOK
,case IDCANCEL
ret 1

;gUpdateList
hlb=id(4 hDlg)
SendMessage hlb LB_RESETCONTENT 0 0
sName.getwintext(id(3 hDlg)); sName.SqlEscape
sTag.getwintext(id(7 hDlg)); sTag.SqlEscape
;get items from database
sql="SELECT name FROM t"
if(sName.len) sql+F" WHERE name LIKE '%{sName}%'"; if(sTag.len) sql+F" AND tag='{sTag}'"
else if(sTag.len) sql+F" WHERE tag='{sTag}'"
if(but(8 hDlg)) sql+" ORDER BY name"
db.Exec(sql a)
for i 0 a.len
,LB_Add hlb a[0 i]

ret

;gShowSelected
hlb=id(4 hDlg)
_i=LB_SelectedItem(hlb); if(_i<0) ret
LB_GetItemText hlb _i _s
mes _s
ret


#sub EditSubclassProc
function# hWnd message wParam lParam

;OutWinMsg message wParam lParam
sel message
,case WM_DESTROY
,case [WM_KEYDOWN,WM_KEYUP]
,sel wParam ;;virtual key code
,,case [VK_DOWN,VK_UP,VK_PRIOR,VK_NEXT]
,,;relay these keys to the listbox and not to the edit box
,,SendMessage id(4 GetParent(hWnd)) message wParam lParam
,,ret

int wndproc=GetProp(hWnd "wndproc"); if(!wndproc) ret
ret CallWindowProcW(wndproc hWnd message wParam lParam)
#27
For some reason the dialog function gave me error when running, so I modified just the SQL string a litte bit and it works just fine.

Function dlg_list_filtering_tag
Code:
Copy      Help
;create an example table and add several items
if createTableNow
,str sql=
,;PRAGMA journal_mode=WAL;
,;BEGIN;
,;DROP TABLE IF EXISTS t;
,;CREATE TABLE t(name,tag);
,;INSERT INTO t(name,tag) VALUES('January','month');
,;INSERT INTO t(name,tag) VALUES('Apple','fruit');
,;INSERT INTO t(name,tag) VALUES('Banana','fruit');
,;INSERT INTO t(name,tag) VALUES('April','month');
,;INSERT INTO t(name,tag) VALUES('Other','');
,;COMMIT;
,db.Exec(sql)
#28
The code changes when copy/paste to/from forum.
Because when pasting, QM thinks that the commas are escaped tabs, and replaces them to tabs. But must be commas.
Now I changed the code to avoid it.
#29
It works as expected. Big Grin
Thanks for the nice piece of code that works wonderfully with Sqlite3 DB.


Forum Jump:


Users browsing this thread: 1 Guest(s)