Before I try to explain it all, take a look at the code and/or run it, then you'll understand what it does...
Code: Select all
;/=====================================================================================================================
;| WndCheckboxList.pbi -- Windows only
;|
;| Written by Axolotl
;|
;| Free, unrestricted, no warranty whatsoever - Use at your own risk
;|
;| Link: https://www.purebasic.fr/english/viewtopic.php?t=87934
;\=====================================================================================================================
CompilerIf #PB_Compiler_IsMainFile ;{ (if) Main File Test Area needs this .....
EnableExplicit
Enumeration EWindow 1
#WND_Main
EndEnumeration
Enumeration EGadget 1
; WND_Main Gadgets
#GDT_txtInfo
#GDT_btnClick
EndEnumeration
CompilerEndIf ;}
CompilerIf Not Defined(PB_ListIcon_NoHeaders, #PB_Constant) And #PB_Compiler_OS = #PB_OS_Windows
#PB_ListIcon_NoHeaders = #LVS_NOCOLUMNHEADER
CompilerEndIf
; ---
;-
; ---------------------------------------------------------------------------------------------------------------------
;-{ BLOCK: WndCheckboxList
; ---------------------------------------------------------------------------------------------------------------------
Enumeration EWindow ; continued
#WND_CheckboxList
EndEnumeration
Enumeration EGadget ; continued
; WND_CheckboxList Gadgets
#GDT_CheckboxList_lstItems
EndEnumeration
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_PerformCallback(hWnd, uMsg, wParam, lParam)
Protected *lv.NMLISTVIEW
Select uMsg
; Case #WM_NCDESTROY ;:Debug "WM_NCDESTROY "
Case #WM_ACTIVATE
; user clicked outside the window / listbox .....
If wParam = #WA_INACTIVE ;: Debug "WM_ACTIVATE, WA_INACTIVE, hide the fly window now "
ShowWindow_(hWnd, #SW_HIDE) ; use handle instead
EndIf
Case #WM_NOTIFY ; ListIcon style
*lv = lParam
If *lv\hdr\idFrom = #GDT_CheckboxList_lstItems
Select *lv\hdr\code ; NMHDR is always the first member
; Case #NM_CLICK
; Debug #PB_Compiler_Procedure+" WM_NOTITY - NM_CLICK -- " + *lv\iItem
Case #LVN_ITEMCHANGING
; Debug #PB_Compiler_Procedure+" WM_NOTITY - LVN_ITEMCHANGING -- " + *lv\iItem
If *lv\uNewState & #LVIS_SELECTED
If GetGadgetItemState(*lv\hdr\idFrom, *lv\iItem) & #PB_ListIcon_Checked
SetGadgetItemState(*lv\hdr\idFrom, *lv\iItem, 0) ; no checked and no selected
Else
SetGadgetItemState(*lv\hdr\idFrom, *lv\iItem, #PB_ListIcon_Checked)
EndIf
ProcedureReturn #True ;// returns TRUE to prevent the change, or FALSE to allow the change.
EndIf
; Case #LVN_ITEMCHANGED
; Debug #PB_Compiler_Procedure+" WM_NOTITY - LVN_ITEMCHANGED -- "+*lv\iItem
EndSelect
EndIf
EndSelect
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_Create(Width=200, Height=200) ; BOOL .. default size of the open window
Protected oldGadgetList
If OpenWindow(#WND_CheckboxList, 0, 0, Width, Height, "", #PB_Window_Invisible|#PB_Window_BorderLess|#WS_DLGFRAME)
StickyWindow(#WND_CheckboxList, 1) ; because this goes on top of main window (maybe also sticky)
oldGadgetList = UseGadgetList(WindowID(#WND_CheckboxList))
ListIconGadget(#GDT_CheckboxList_lstItems, 0, 0, Width, Height, "", Width-24, #PB_ListIcon_CheckBoxes|#PB_ListIcon_FullRowSelect|#PB_ListIcon_NoHeaders)
; in older PBs
; replace #PB_ListIcon_NoHeaders => #LVS_NOCOLUMNHEADER
Protected hgad = GadgetID(#GDT_CheckboxList_lstItems)
SetWindowLongPtr_(hgad, #GWL_EXSTYLE, GetWindowLongPtr_(hgad, #GWL_EXSTYLE) & ~#WS_EX_CLIENTEDGE)
SetWindowPos_(hgad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW|#SWP_NOZORDER|#SWP_NOSIZE|#SWP_NOMOVE|#SWP_FRAMECHANGED)
UseGadgetList(oldGadgetList)
SetWindowCallback(@WndCheckboxList_PerformCallback(), #WND_CheckboxList)
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_Show(TargetGadget, Width=#PB_Ignore) ; BOOL
Protected x, y, w, h, cnt, itemheight, rc.RECT
If IsWindow(#WND_CheckboxList) And IsGadget(#GDT_CheckboxList_lstItems) ; check on gadget would be enough <!?!?>
; get pos and width of TargetGadget to place the window below .....
If IsGadget(TargetGadget)
x = GadgetX(TargetGadget, #PB_Gadget_ScreenCoordinate)
y = GadgetY(TargetGadget, #PB_Gadget_ScreenCoordinate) + GadgetHeight(TargetGadget)
w = GadgetWidth(TargetGadget) ; use the width from TargetGadget
Else ; default
w = 200
EndIf
If Width <> #PB_Ignore
w = Width ; different size than TargetGadget
EndIf
h = GadgetHeight(#GDT_CheckboxList_lstItems) ; start with default .. see call of WndSelection_Create()
; determine the item height .....
rc\left = #LVIR_LABEL ;// acc. to MSDN this is one of the possible flags -- returns the bounding rectangle of the item text.
If SendMessage_(GadgetID(#GDT_CheckboxList_lstItems), #LVM_GETITEMRECT, 0, @rc) ; <== wParam/Index = 0 (first Item, even if control is empty)
itemheight = rc\bottom - rc\top ; do the math
Else
itemheight = 16 ; if #LVM_GETITEMRECT failed use a default value
EndIf
cnt = CountGadgetItems(#GDT_CheckboxList_lstItems)
If h > cnt * itemheight ; if default height is to big !!!
h = cnt * itemheight ; no empty space
EndIf
; adjust the window size
ResizeWindow(#WND_CheckboxList, x, y, w, h) ;#PB_Ignore)
ResizeGadget(#GDT_CheckboxList_lstItems, #PB_Ignore, #PB_Ignore, w, h) ;#PB_Ignore)
SetGadgetItemAttribute(#GDT_CheckboxList_lstItems, #PB_Ignore, #PB_ListIcon_ColumnWidth, w-24, 0)
HideWindow(#WND_CheckboxList, 0) ; show now
SetActiveGadget(#GDT_CheckboxList_lstItems)
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_ClearItems() ; VOID
ClearGadgetItems(#GDT_CheckboxList_lstItems)
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_CountItems(xCheckedOnly=#False) ; INT
Protected result, index, count
count = CountGadgetItems(#GDT_CheckboxList_lstItems)
If xCheckedOnly
count - 1
For index = 0 To count
If GetGadgetItemState(#GDT_CheckboxList_lstItems, index) & #PB_ListIcon_Checked
result + 1
EndIf
Next Index
Else
result = count
EndIf
ProcedureReturn result
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_AddItem(Index, Item$, xChecked=#False) ; VOID
AddGadgetItem(#GDT_CheckboxList_lstItems, Index, Item$)
If xChecked
If Index = -1
index = CountGadgetItems(#GDT_CheckboxList_lstItems) - 1
EndIf
SetGadgetItemState(#GDT_CheckboxList_lstItems, Index, #PB_ListIcon_Checked)
EndIf
; ProcedureReturn Index
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure.s WndCheckboxList_GetItem(Index) ; STR
ProcedureReturn GetGadgetItemText(#GDT_CheckboxList_lstItems, Index)
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure WndCheckboxList_IsItemChecked(Index) ; BOOL
If GetGadgetItemState(#GDT_CheckboxList_lstItems, Index) & #PB_ListIcon_Checked
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
;- (if) Main File Test Area
; ---------------------------------------------------------------------------------------------------------------------
CompilerIf #PB_Compiler_IsMainFile
Define Index, Count
If OpenWindow(#WND_Main, 0, 0, 300, 80, "Test ", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget(#GDT_txtInfo, 4, 4, 200, 20, "Click here on the Button below .... ")
ButtonGadget(#GDT_btnClick, 4, 32, 72, 20, "Click")
If WndCheckboxList_Create(400, 200)
WndCheckboxList_AddItem(0, "0: Item ONE is the first ")
WndCheckboxList_AddItem(1, "1: Item TWO is the second ", #True)
WndCheckboxList_AddItem(2, "2. Item THREE is the third ")
WndCheckboxList_AddItem(3, "3. Item FOUR is the fourth ")
For Index = 4 To 14
WndCheckboxList_AddItem(Index, Str(Index) + ". Item NEXT is the unkown")
Next
EndIf
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_DeactivateWindow
; Debug "MainLoop: Event_DeactivateWindow " + EventWindow()
Select EventWindow() ; <= important ... huge difference betwwen EventWindow() and WindowEvent() !!!
Case #WND_CheckboxList
SetActiveWindow(#WND_Main) ; ?
Debug ""
Count = WndCheckboxList_CountItems(#True)
Debug "Checked = " + Count
Count = WndCheckboxList_CountItems()
Debug "Count = " + Count
For Index = 0 To Count - 1
If WndCheckboxList_IsItemChecked(Index)
Debug "Checked = " + WndCheckboxList_GetItem(Index)
EndIf
Next Index
EndSelect
Case #PB_Event_Gadget
Select EventGadget()
Case #GDT_btnClick
WndCheckboxList_Show(#GDT_btnClick, 200) ; Use this width, not the Button's width :)
EndSelect
EndSelect
ForEver
EndIf
CompilerEndIf
;} End of WndCheckboxList -- Window Definition
Okay, if you replace the callback with BindEvents and remove some special API stuff, then it should also work on other operating systems.
IMHO it doesn't look nice enough and doesn't behave very well either.
Code: Select all
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
Procedure WndCheckboxList_OnEvent_DeactivateWindow() ; window looses focus <??>
HideWindow(#WND_CheckboxList, 1)
EndProcedure
; ---
Procedure WndCheckboxList_OnEvent_MouseClick()
Protected index, state
index = GetGadgetState(#GDT_CheckboxList_lstItems)
If index > -1
If GetGadgetItemState(#GDT_CheckboxList_lstItems, index) & #PB_ListIcon_Checked
SetGadgetItemState(#GDT_CheckboxList_lstItems, index, 0)
Else
SetGadgetItemState(#GDT_CheckboxList_lstItems, index, #PB_ListIcon_Checked)
EndIf
EndIf
EndProcedure
; ....
BindEvent(#PB_Event_DeactivateWindow, @WndCheckboxList_OnEvent_DeactivateWindow(), #WND_CheckboxList)
BindGadgetEvent(#GDT_CheckboxList_lstItems, @WndCheckboxList_OnEvent_MouseClick(), #PB_EventType_LeftClick)
BindGadgetEvent(#GDT_CheckboxList_lstItems, @WndCheckboxList_OnEvent_MouseClick(), #PB_EventType_RightClick)
CompilerEndIf

