Page 1 of 1

CheckboxList Window [Win Only & some hints]

Posted: Wed Nov 19, 2025 4:58 pm
by Axolotl
Inspired by a question in the German forum and because I actually need something similar, I have created a CheckboxList.Window
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 

Re: CheckboxList Window [Win Only & some hints]

Posted: Thu Nov 20, 2025 10:25 am
by acreis
Very Usefull!

Just when I needed it most!

Thank you!