[SOLVED] Multi-column ComboBoxGadget and use of separating delimiter

Just starting out? Need help? Post your questions and find answers here.
PBJim
Enthusiast
Enthusiast
Posts: 296
Joined: Fri Jan 19, 2024 11:56 pm

[SOLVED] Multi-column ComboBoxGadget and use of separating delimiter

Post by PBJim »

A delimiting character #LF$ appears to work here, as a means of separating the code from an associated description within the combobox, when we later write it to file.

Is there a better way however, for Windows only?

We have no control over the users' insertion of a space into coded values, nor the length of their codes, otherwise I would obviously just split the value on the space.

Incidentally, I see that multiple-column comboboxes are a frequent requirement and a few other platforms seem to have coded solutions — https://ibb.co/djfd8T4 Perhaps there is something available in PureBasic that I'm not aware of. If there is, I'd be very grateful to know, thanks.

Code: Select all

EnableExplicit

; **
; **  Two-column ComboBoxGadget, see line 37
; **
Define  counter
Define  item, value
Define  Event

NewList CodeDesc.s()

AddElement(CodeDesc()) : CodeDesc() = "PINEAPPLE"
AddElement(CodeDesc()) : CodeDesc() = "BANANA"
AddElement(CodeDesc()) : CodeDesc() = "DRAGON FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "STAR FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "MANGO"
AddElement(CodeDesc()) : CodeDesc() = "RAMBUTAN"
AddElement(CodeDesc()) : CodeDesc() = "WATERMELON"
AddElement(CodeDesc()) : CodeDesc() = "POMEGRANATE"
AddElement(CodeDesc()) : CodeDesc() = "LYCHEE"
AddElement(CodeDesc()) : CodeDesc() = "MANGOSTEEN"
AddElement(CodeDesc()) : CodeDesc() = "TAMARIND"
AddElement(CodeDesc()) : CodeDesc() = "PASSION FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "PAPAYA"

If OpenWindow(0, 0, 0, 270, 180, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

  ComboBoxGadget(2, 10, 20, 250, 21)
  
  ForEach(CodeDesc())
    counter + 1
    
    ; **
    ; **  Insert delimiter #LF$ to enable ComboBoxGadget value to be extracted without its
    ; **  associated description
    ; **
    AddGadgetItem(2, -1, "STK" + RSet(Str(counter), 3, "0") + #LF$ + "   " + CodeDesc())
  Next

  SetGadgetState(2, 1)

  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_Gadget
      item = GetGadgetState(2)
      
      Select EventGadget()
        Case 2
          If item <> -1
            MessageRequester("Stock code", StringField(GetGadgetItemText(2, item), 1, #LF$))
          EndIf
          
      EndSelect
    EndIf
  Until Event = #PB_Event_CloseWindow

EndIf
Last edited by PBJim on Mon Nov 18, 2024 3:24 pm, edited 1 time in total.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 778
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by spikey »

PBJim wrote: Sun Nov 10, 2024 7:24 pm We have no control over the users' insertion of a space into coded values, nor the length of their codes, otherwise I would obviously just split the value on the space.
I don't know why you think you don't have any control over the values, you do. The data in the example you give looks pretty uniform to me, perhaps some more real world problems detail is needed.

I guess you're gathering the data via a user interface or importing the data from an external source. So build clean up code into the user interface or the import procedure. The trick is to make the incoming data conform to an expectation you can handle as you import/gather it and then add correctly formatted values to the gadget when you prepare it.

Examples:
1. You can restrict a StringGadget to numeric, lowercase or uppercase entry when you create it.
2. You can fix its length via SetGadgetAttribute.
3. Or you can do the same with LCase() and UCase() if you're importing.
4. Something like x$ = ReplaceString(x$, " ", " ") will remove double spaces.
5. Left() will truncate strings to a specific length.
6. If you have more complex requirements have a look at the RegEx library, you can do all sorts of manipulations with that.

In this case, if I were constructing this interface I'd be storing the ListIndex value in the SetGadgetItemData() as I add the values to the combo. Then I can retrieve this from the selected item using GetGadgetItemData() when a selection is made. This points me back to the source data in the list. I can add anything to the list I please to make things happen in the background.
Last edited by spikey on Mon Nov 11, 2024 1:07 pm, edited 1 time in total.
Axolotl
Addict
Addict
Posts: 872
Joined: Wed Dec 31, 2008 3:36 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by Axolotl »

Hi,
most flexible way is ownerdraw the content.

Code: Select all

; ComboBox OwnerDraw in PureBasic 
EnableExplicit 

Enumeration EWindow 1 
  #WND_Main 
EndEnumeration 

Enumeration EGadget 1 
  #GDT_strInfo 
  #GDT_cbbValues 
EndEnumeration 

#COMBOBOX_COLUMN_1  =  40   ; Pixels 
#COMBOBOX_COLUMN_2  = 160   ; Pixels 

#DI_NORMAL = $0003  ; Icon 

Global NewList CodeDesc.s()

AddElement(CodeDesc()) : CodeDesc() = "PINEAPPLE"
AddElement(CodeDesc()) : CodeDesc() = "BANANA"
AddElement(CodeDesc()) : CodeDesc() = "DRAGON FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "STAR FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "MANGO"
AddElement(CodeDesc()) : CodeDesc() = "RAMBUTAN"
AddElement(CodeDesc()) : CodeDesc() = "WATERMELON"
AddElement(CodeDesc()) : CodeDesc() = "POMEGRANATE"
AddElement(CodeDesc()) : CodeDesc() = "LYCHEE"
AddElement(CodeDesc()) : CodeDesc() = "MANGOSTEEN"
AddElement(CodeDesc()) : CodeDesc() = "TAMARIND"
AddElement(CodeDesc()) : CodeDesc() = "PASSION FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "PAPAYA"


Procedure WindowCallback(hWnd, Msg, WParam, LParam) 
  Protected result, hBrush, *lpdis.DRAWITEMSTRUCT, rc.RECT 
  Protected text$, t$, tlen, dtFlags  

  Select Msg 
    Case #WM_DRAWITEM 
      *lpdis = lParam  ; typecast to DRAWITEMSTRUCT 
      
      If *lpdis\CtlType = #ODT_COMBOBOX 
        SetBkMode_(*lpdis\hDC, #TRANSPARENT)    ; Text is rendered transparent 
        
        If *lpdis\ItemState & #ODS_FOCUS 
          hBrush = GetSysColorBrush_(#COLOR_HIGHLIGHT)  ; returns a cached brush instead of allocating a new one 
          FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush) 
          
          SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_HIGHLIGHTTEXT)) 
        Else 
          FillRect_(*lpdis\hDC, *lpdis\rcItem, GetSysColorBrush_(#COLOR_WINDOW)) 
          SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_WINDOWTEXT)) 
        EndIf 

        If *lpdis\itemID <> -1 
          tlen = SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXTLEN, *lpdis\itemID, 0) ; lParam .. not used 
          If tlen > 0 
            text$ = Space(tlen) 
            SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXT, *lpdis\itemID, @text$) 
          EndIf 

          DrawIconEx_(*lpdis\hDC, *lpdis\rcItem\left+2   , *lpdis\rcItem\top+1, LoadIcon_(0, #IDI_ASTERISK), 16, 16, 0, 0, #DI_NORMAL) 

          dtFlags = #DT_LEFT | #DT_VCENTER 
          If Asc(text$) 
            ; column 1 
            t$ = StringField(text$, 1, #LF$) 
            If Asc(t$) 
              *lpdis\rcItem\left + #COMBOBOX_COLUMN_1 
              DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags) 
            EndIf 

            ; column 2 
            t$ = StringField(text$, 2, #LF$) 
            If Asc(t$) 
              *lpdis\rcItem\left + #COMBOBOX_COLUMN_2 
              DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags) 
            EndIf 
          EndIf 
        EndIf 
      
      EndIf 
    ; ProcedureReturn result 
  
  EndSelect 
  
  ; default means handling by PB internally 
  ;
  ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure 


Procedure OpenMainWindow(WndW=400, WndH=100) 
  If OpenWindow(#WND_Main, 0, 0, WndW, WndH, "ComboBox OwnerDraw", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_TitleBar ) 
    StringGadget(#GDT_strInfo, 8, 8, WndW-16, 20, "Selected Combobox item is .... ")  

    ComboBoxGadget(#GDT_cbbValues, 8, 40, WndW-16, 21, #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED) 

    SetWindowCallback(@WindowCallback(), #WND_Main) 

    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False
EndProcedure 

Procedure Main() 
  Protected index, text$  

  If OpenMainWindow() 

    ForEach(CodeDesc())
      index + 1
      ; **
      ; **  Insert delimiter #LF$ to enable ComboBoxGadget value to be extracted without its
      ; **  associated description
      ; **
      AddGadgetItem(#GDT_cbbValues, -1, "STK" + RSet(Str(index), 3, "0") + #LF$ + CodeDesc()) 
    Next  

  Repeat 
    Select WaitWindowEvent() 
      Case #PB_Event_CloseWindow 
        Break ; bye 
      Case #PB_Event_Gadget 
        Select EventGadget() 
          Case #GDT_cbbValues 
            text$ = GetGadgetText(#GDT_cbbValues) 
            SetGadgetText(#GDT_strInfo, "Stock code == " + StringField(text$, 1, #LF$)) 
        EndSelect 
    EndSelect 
  ForEver 

  EndIf 
  ProcedureReturn 0 
EndProcedure 

End Main() 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
Axolotl
Addict
Addict
Posts: 872
Joined: Wed Dec 31, 2008 3:36 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by Axolotl »

Hi,
maybe I did not understand your problem correctly. But this looks almost like your picture.... (IMHO) .

Code: Select all

EnableExplicit 

Enumeration EWindow 1 
  #WND_Main 
  #WND_FlyOver 
EndEnumeration 

Enumeration EGadget 1 
  #GDT_strInfo 
  #GDT_strValues
  #GDT_btnValues

  ; #WND_FlyOver 
  #GDT_lbFlyOver
EndEnumeration 


Global NewList CodeDesc.s()

AddElement(CodeDesc()) : CodeDesc() = "PINEAPPLE"
AddElement(CodeDesc()) : CodeDesc() = "BANANA"
AddElement(CodeDesc()) : CodeDesc() = "DRAGON FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "STAR FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "MANGO"
AddElement(CodeDesc()) : CodeDesc() = "RAMBUTAN"
AddElement(CodeDesc()) : CodeDesc() = "WATERMELON"
AddElement(CodeDesc()) : CodeDesc() = "POMEGRANATE"
AddElement(CodeDesc()) : CodeDesc() = "LYCHEE"
AddElement(CodeDesc()) : CodeDesc() = "MANGOSTEEN"
AddElement(CodeDesc()) : CodeDesc() = "TAMARIND"
AddElement(CodeDesc()) : CodeDesc() = "PASSION FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "PAPAYA"


Procedure SetListIconGadgetNoHeader(Gadget) 
  Protected hGad = GadgetID(Gadget) 
  ProcedureReturn SetWindowLongPtr_(hGad, #GWL_STYLE, GetWindowLongPtr_(hGad, #GWL_STYLE) | #LVS_NOCOLUMNHEADER)  
EndProcedure 

;-
;-{--== Window FlyOver ==----------------------------------------------------------------------------------------------
;-

Procedure WindowFlyOver_PerformCallback(hWnd, uMsg, wParam, lParam) 
  Select uMsg 
    Case #WM_ACTIVATE 
      ; user clicked outside the window / listbox 
      ; 
      If wParam = #WA_INACTIVE  
        HideWindow(#WND_FlyOver, 1) 
      EndIf 
  EndSelect 

  ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure 

; --- 

Procedure WindowFlyOver_CreateWindow() 
  Protected OldGadgetList 

  If OpenWindow(#WND_FlyOver, 0, 0, 400, 200, "", #PB_Window_BorderLess | #PB_Window_Invisible | #PB_Window_NoActivate) 
    StickyWindow(#WND_FlyOver, 1) ; because main window is also sticky 
    OldGadgetList = UseGadgetList(WindowID(#WND_FlyOver))  ; Create GadgetList and store old GadgetList
    ListIconGadget(#GDT_lbFlyOver, 0, 0, 320, 200, "", 80, #PB_ListIcon_AlwaysShowSelection|#PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines) 
      AddGadgetColumn(#GDT_lbFlyOver, 1, "", 120) 
      AddGadgetColumn(#GDT_lbFlyOver, 2, "", 80) 
    SetListIconGadgetNoHeader(#GDT_lbFlyOver) 
    SetWindowCallback(@WindowFlyOver_PerformCallback(), #WND_FlyOver) 
    UseGadgetList(oldGadgetList)             ; return to previous GadgetList 
    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False 
EndProcedure 

; ---

Procedure WindowFlyOver_ShowWindow(DockToGadget, FlyOverWidth=#PB_Ignore) 
  Protected hGad, width, rct.RECT, pt.POINT 

  If IsGadget(DockToGadget) 
    hGad = GadgetID(DockToGadget) 
    GetClientRect_(hGad, @rct) 
    pt\x = rct\left - 1 
    pt\y = rct\bottom 
    ClientToScreen_(hGad, @pt) 

    If FlyOverWidth <> #PB_Ignore 
      width = FlyOverWidth 
    Else 
      width = rct\right - rct\left  ; new width of the window / gadget 
    EndIf 
    ;
    ResizeWindow(#WND_FlyOver, pt\x, pt\y, width, #PB_Ignore) 
    ResizeGadget(#GDT_lbFlyOver, #PB_Ignore, #PB_Ignore, width, #PB_Ignore) 
    ; 
    HideWindow(#WND_FlyOver, 0)  ; show now 
    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False 
EndProcedure 

; ---

Procedure WindowFlyOver_FillList(List Input$(), SelIndex, bKeepContent=#False) 
  Protected index 

  If IsGadget(#GDT_lbFlyOver) 
    If bKeepContent <> #False 
      ClearGadgetItems(#GDT_lbFlyOver) 
    EndIf 

    ForEach Input$() 
      index + 1
      AddGadgetItem(#GDT_lbFlyOver, -1, "STK" + RSet(Str(index), 3, "0")  + #LF$ + Input$() + #LF$ + "comment")   ; <== adapt to your needs 
      If (index - 1) & 1 
        SetGadgetItemColor(#GDT_lbFlyOver, index - 1, #PB_Gadget_BackColor, $FFF8F0) ; AliceBlue 
      EndIf 
    Next Input$() 
    SetGadgetState(#GDT_lbFlyOver, SelIndex) 

    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False 
EndProcedure 

; --- 

Procedure.s WindowFlyOver_GetSelectedText() 
  Protected result$ 

  result$ = GetGadgetText(#GDT_lbFlyOver) 
  result$ = StringField(result$, 1, #LF$)     ; <== adapt to your needs 

  HideWindow(#WND_FlyOver, 1) 
  ProcedureReturn result$ 
EndProcedure 

; todo 
; Procedure WindowFlyOver_Events(Event) 
; EndProcedure 

;} Eod of Window_FlyOver 


Procedure OpenMainWindow(WndW=400, WndH=100) 
  If OpenWindow(#WND_Main, 0, 0, WndW, WndH, "ComboBox OwnerDraw", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_TitleBar ) 
    StringGadget(#GDT_strInfo, 8, 8, WndW-16, 20, "Selected Combobox item is .... ")  

    StringGadget(#GDT_strValues, 8, 40, 120, 21, "") 
    ButtonGadget(#GDT_btnValues, 128, 40, 32, 21, Chr($25BC)) ; "V") 
    
    WindowFlyOver_CreateWindow() 

    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False
EndProcedure 

Procedure Main() 
  Protected index, text$ 

  If OpenMainWindow() 

    WindowFlyOver_FillList(CodeDesc(), 1) 

    text$ = WindowFlyOver_GetSelectedText() 
    SetGadgetText(#GDT_strValues, text$) 

    Repeat 
      Select WaitWindowEvent() 
        Case #PB_Event_CloseWindow 
          Break ; bye 
        Case #PB_Event_Gadget 
          Select EventGadget() 
            Case #GDT_btnValues 
              WindowFlyOver_ShowWindow(#GDT_strValues, 320) 
            Case #GDT_lbFlyOver 
              Select EventType() 
                Case #PB_EventType_LeftClick
                  text$ = WindowFlyOver_GetSelectedText() 
                  SetGadgetText(#GDT_strValues, text$) 
                  SetGadgetText(#GDT_strInfo, "Stock code == " + text$) 
              EndSelect 
          EndSelect 
      EndSelect 
    ForEver 

  EndIf 
  ProcedureReturn 0 
EndProcedure 

End Main() 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4991
Joined: Sun Apr 12, 2009 6:27 am

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by RASHAD »

Hi
Workaround for fun :D

Code: Select all

LoadFont(0,"Arial",11)

OpenWindow(0,0,0,400,250,"Window",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ComboBoxGadget(1,10,10,180,24,#PB_ComboBox_Editable)  
SetGadgetFont(1,FontID(0))
OpenWindow(10,GadgetX(1,#PB_Gadget_ScreenCoordinate),GadgetY(1,#PB_Gadget_ScreenCoordinate),180,24,"",#PB_Window_BorderLess,WindowID(0))
SetWindowLongPtr_(WindowID(10), #GWL_EXSTYLE, #WS_EX_LAYERED )
SetLayeredWindowAttributes_(WindowID(10),#Blue , 1, #LWA_ALPHA)
HideWindow(10,1)
OpenWindow(1,GadgetX(1,#PB_Gadget_ScreenCoordinate),GadgetY(1,#PB_Gadget_ScreenCoordinate)+GadgetHeight(1),300,200,"",#PB_Window_BorderLess,WindowID(0))
HideWindow(1,1)
UseGadgetList(WindowID(1))
ListIconGadget(2,0,0,300,200,"",20,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#LVS_NOCOLUMNHEADER)
SetGadgetColor(2,#PB_Gadget_BackColor,$D5F3FE)
SetGadgetColor(2,#PB_Gadget_LineColor,$0)
AddGadgetColumn(2,1,"",100)
AddGadgetColumn(2,2,"",180)
For i = 0 To 10
  AddGadgetItem(2,i,Str(i)+#LF$+"Text "+Str(i)+#LF$+"another text "+Str(i))
Next

UseGadgetList(WindowID(0))

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_CloseWindow
      Quit = 1
      
    Case #PB_Event_Menu
      Select GetActiveGadget()
        Case 1
          SetGadgetState(1,EventMenu()-1)
          SetActiveGadget(10)
          Debug GetGadgetText(1)     
      EndSelect
      
    Case #WM_LBUTTONDOWN
      act = GetActiveGadget()
      Select  act
        Case 1
          HideWindow(10,0)
          HideWindow(1,0)
          
        Case 2
          HideWindow(10,1)
          HideWindow(1,1)                
          text$ = GetGadgetItemText(2,GetGadgetState(2),1)
          SetGadgetText(1,text$)                
          
      EndSelect
      
    Case #WM_LBUTTONUP
      
  EndSelect 
  
Until Quit = 1
End
Egypt my love
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by breeze4me »

Based on Axolotl's code, I modified the code to look like the image shown.

Code: Select all

; ComboBox OwnerDraw in PureBasic 
EnableExplicit 

Enumeration EWindow 1 
  #WND_Main 
EndEnumeration 

Enumeration EGadget 1 
  #GDT_strInfo 
  #GDT_cbbValues 
EndEnumeration 


#CB_GETCOMBOBOXINFO = 356

Structure COMBOBOXINFO Align #PB_Structure_AlignC
  cbSize.l
  rcItem.RECT
  rcButton.RECT
  stateButton.l
  hwndCombo.i
  hwndItem.i
  hwndList.i
EndStructure

#COMBOBOX_COLUMN_0 = 60

Global NewList CodeDesc.s()

AddElement(CodeDesc()) : CodeDesc() = "PINEAPPLE"
AddElement(CodeDesc()) : CodeDesc() = "BANANA"
AddElement(CodeDesc()) : CodeDesc() = "DRAGON FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "STAR FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "MANGO"
AddElement(CodeDesc()) : CodeDesc() = "RAMBUTAN"
AddElement(CodeDesc()) : CodeDesc() = "WATERMELON"
AddElement(CodeDesc()) : CodeDesc() = "POMEGRANATE"
AddElement(CodeDesc()) : CodeDesc() = "LYCHEE"
AddElement(CodeDesc()) : CodeDesc() = "MANGOSTEEN"
AddElement(CodeDesc()) : CodeDesc() = "TAMARIND"
AddElement(CodeDesc()) : CodeDesc() = "PASSION FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "PAPAYA"


Procedure WindowCallback(hWnd, Msg, WParam, LParam) 
  Protected result, hBrush, *lpdis.DRAWITEMSTRUCT
  Protected text$, t$, tlen, dtFlags  
  
  Select Msg
    Case #WM_DRAWITEM
      *lpdis = lParam  ; typecast to DRAWITEMSTRUCT 
      If *lpdis\CtlType = #ODT_COMBOBOX 
        
        Protected cbinfo.COMBOBOXINFO, rt.RECT
        
        ; Change the listbox size of the combobox.
        If IsGadget(WParam) And GadgetType(WParam) = #PB_GadgetType_ComboBox
          cbinfo\cbSize = SizeOf(COMBOBOXINFO)
          SendMessage_(GadgetID(WParam), #CB_GETCOMBOBOXINFO, 0, @cbinfo)
          
          If cbinfo\hwndList
            GetWindowRect_(cbinfo\hwndList, rt)
            If rt\right - rt\left <> DesktopScaledX(200)
              MoveWindow_(cbinfo\hwndList, rt\left, rt\top, DesktopScaledX(200), rt\bottom - rt\top, 0)
            EndIf
          EndIf
        EndIf
        
        SetBkMode_(*lpdis\hDC, #TRANSPARENT)    ; Text is rendered transparent 
        
        If *lpdis\ItemState & #ODS_FOCUS 
          hBrush = GetSysColorBrush_(#COLOR_HIGHLIGHT)  ; returns a cached brush instead of allocating a new one 
          FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush) 
          
          SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_HIGHLIGHTTEXT)) 
          DrawFocusRect_(*lpdis\hDC, *lpdis\rcItem)
        Else 
          ; Fill the background color differently for odd and even lines.
          If *lpdis\itemID % 2 And *lpdis\itemState & #ODS_COMBOBOXEDIT = 0
            hBrush = CreateSolidBrush_($DDDDDD)
          Else
            hBrush = GetSysColorBrush_(#COLOR_WINDOW)
          EndIf
          If hBrush
            FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush) 
            DeleteObject_(hBrush)
          EndIf
          
          SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_WINDOWTEXT)) 
        EndIf 

        If *lpdis\itemID <> -1 
          tlen = SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXTLEN, *lpdis\itemID, 0) ; lParam .. not used 
          If tlen > 0 
            text$ = Space(tlen)
            SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXT, *lpdis\itemID, @text$) 
          EndIf 
          
          dtFlags = #DT_LEFT | #DT_VCENTER | #DT_SINGLELINE
          If Asc(text$) 
            ; column 0
            t$ = StringField(text$, 1, #LF$) 
            If Asc(t$) 
              *lpdis\rcItem\left + 5
              DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags) 
            EndIf 

            ; column 1 
            t$ = StringField(text$, 2, #LF$) 
            If Asc(t$) 
              *lpdis\rcItem\left + DesktopScaledX(#COMBOBOX_COLUMN_0)
              
              If *lpdis\itemState & #ODS_COMBOBOXEDIT = 0
                Protected hPen, oldPen
                
                ; Draw a vertical divider for the column.
                hPen = CreatePen_(#PS_SOLID, 1, $BBBBBB)
                If hPen
                  oldPen = SelectObject_(*lpdis\hdc, hPen)
                  MoveToEx_(*lpdis\hdc, *lpdis\rcItem\left - 5, *lpdis\rcItem\top, 0)
                  LineTo_(*lpdis\hdc, *lpdis\rcItem\left - 5, *lpdis\rcItem\bottom)
                  SelectObject_(*lpdis\hdc, oldPen)
                  DeleteObject_(hPen)
                EndIf
                
                DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags) 
              EndIf
            EndIf 
          EndIf 
        EndIf 
      
      EndIf 
    ; ProcedureReturn result 
  
  EndSelect 
  
  ; default means handling by PB internally 
  ;
  ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure 


Procedure OpenMainWindow(WndW=400, WndH=100) 
  If OpenWindow(#WND_Main, 0, 0, WndW, WndH, "ComboBox OwnerDraw", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_TitleBar ) 
    StringGadget(#GDT_strInfo, 8, 8, WndW-16, 20, "Selected Combobox item is .... ")  
    
    ComboBoxGadget(#GDT_cbbValues, 8, 40, 100, 21, #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED) 
    SendMessage_(GadgetID(#GDT_cbbValues), #CB_SETITEMHEIGHT, 0, DesktopScaledY(GadgetHeight(#GDT_cbbValues) - 2))
    
    SetWindowCallback(@WindowCallback(), #WND_Main) 
    
    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False
EndProcedure 

Procedure Main() 
  Protected index, text$  

  If OpenMainWindow() 

    ForEach(CodeDesc())
      index + 1
      ; **
      ; **  Insert delimiter #LF$ to enable ComboBoxGadget value to be extracted without its
      ; **  associated description
      ; **
      AddGadgetItem(#GDT_cbbValues, -1, "STK" + RSet(Str(index), 3, "0") + #LF$ + CodeDesc()) 
    Next  

  Repeat 
    Select WaitWindowEvent() 
      Case #PB_Event_CloseWindow 
        Break ; bye 
      Case #PB_Event_Gadget 
        Select EventGadget() 
          Case #GDT_cbbValues 
            text$ = GetGadgetText(#GDT_cbbValues) 
            SetGadgetText(#GDT_strInfo, "Stock code == " + StringField(text$, 1, #LF$)) 
        EndSelect 
    EndSelect 
  ForEver 

  EndIf 
  ProcedureReturn 0 
EndProcedure 

End Main() 
Last edited by breeze4me on Tue Nov 12, 2024 10:53 am, edited 1 time in total.
PBJim
Enthusiast
Enthusiast
Posts: 296
Joined: Fri Jan 19, 2024 11:56 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by PBJim »

Thanks very much to all for the suggested answers to this post, I appreciate the code that's been put together on this.

I wish the solution was a bit easier. The only thing that I feel might be a downside with this, is that the callbacks are perhaps paired with each ComboBoxGadget, so where we have a window containing a lot of ComboBoxGadgets, the callbacks might become difficult for others to maintain in future. But, I haven't started to put that into use yet, so I may be overthinking.

With your example Rashad, you mention it's for fun, but actually it works well. We already have pop-up search windows for some input fields and I hadn't thought of positioning them so that they appear to be a combobox :D

A question aimed at PB's developers — are there plans to extend the abilities of GUI gadgets? I don't know, as I'm not familiar with the history of the GUI functions or whether they have been the subject of enhancements in the past.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4991
Joined: Sun Apr 12, 2009 6:27 am

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by RASHAD »

Hi PBJim :D
Next snippet is more simple and more close
Have fun

Code: Select all


LoadFont(0,"Arial",11)

OpenWindow(0,0,0,400,250,"Window",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ComboBoxGadget(1,10,10,180,24) 
dLh = FindWindow_("ComboLBox",0)   
SetGadgetFont(1,FontID(0))
ListIconGadget(2,10,GadgetY(1)+GadgetHeight(1),360,200,"",70,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#LVS_NOCOLUMNHEADER)
SetGadgetColor(2,#PB_Gadget_BackColor,$D5F3FE)
SetGadgetColor(2,#PB_Gadget_LineColor,$0)
AddGadgetColumn(2,1,"",100)
AddGadgetColumn(2,2,"",180)
For i = 0 To 10
  AddGadgetItem(1,i,"Item "+Str(i))
  AddGadgetItem(2,i,"Item "+Str(i)+#LF$+"Text "+Str(i)+#LF$+"another text "+Str(i))
Next
MoveWindow_(dlh,0,0,1,1,1)
HideGadget(2,1)
SetGadgetState(1,8)
Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_CloseWindow
      Quit = 1      
      
    Case #WM_LBUTTONDOWN
      act = GetActiveGadget()
      Select  act
        Case 1
          ;ShowWindow_(dlh,#SW_HIDE	)
          HideGadget(2,0)
          SetActiveGadget(2)
          
        Case 2               
          text$ = GetGadgetItemText(1,GetGadgetState(2),0)
          SetGadgetText(1,text$) 
          HideGadget(2,1)               
          
      EndSelect
      
  EndSelect 
  
Until Quit = 1
End

Egypt my love
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4991
Joined: Sun Apr 12, 2009 6:27 am

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by RASHAD »

Code: Select all


LoadFont(0,"Arial",11)

OpenWindow(0,0,0,400,250,"Window",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ComboBoxGadget(1,10,10,180,24) 
dLh = FindWindow_("ComboLBox",0)   
SetGadgetFont(1,FontID(0))
ListIconGadget(2,10,GadgetY(1)+GadgetHeight(1),360,200,"",70,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#LVS_NOCOLUMNHEADER)
SetGadgetColor(2,#PB_Gadget_BackColor,$D5F3FE)
SetGadgetColor(2,#PB_Gadget_LineColor,$0)
AddGadgetColumn(2,1,"",100)
AddGadgetColumn(2,2,"",180)
For i = 0 To 10
  AddGadgetItem(1,i,"Item "+Str(i))
  AddGadgetItem(2,i,"Item "+Str(i)+#LF$+"Text "+Str(i)+#LF$+"another text "+Str(i))
Next
SendMessage_(GadgetID(1), #CB_SETMINVISIBLE, 1, 0)
HideGadget(2,1)
SetGadgetState(1,8)
Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_CloseWindow
      Quit = 1      
      
    Case #WM_LBUTTONDOWN,#WM_LBUTTONDBLCLK
      act = GetActiveGadget()
      Select  act
        Case 1
          HideGadget(2,0)
          SetActiveGadget(2)
          
        Case 2               
          text$ = GetGadgetItemText(1,GetGadgetState(2),0)
          SetGadgetText(1,text$) 
          HideGadget(2,1)               
          
      EndSelect
      
  EndSelect 
  
Until Quit = 1
End

EDIT : Bug fixed
Last edited by RASHAD on Tue Nov 12, 2024 11:47 am, edited 1 time in total.
Egypt my love
PBJim
Enthusiast
Enthusiast
Posts: 296
Joined: Fri Jan 19, 2024 11:56 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by PBJim »

RASHAD wrote: Tue Nov 12, 2024 11:00 am Hi PBJim :D
Next snippet is more simple and more close Have fun
Yes, that's easier, thanks.

It just needs to check or disable double-click on the ComboBoxGadget, as it opens its own drop-down list :

Image
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4991
Joined: Sun Apr 12, 2009 6:27 am

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by RASHAD »

Previous post updated
Egypt my love
User avatar
ChrisR
Addict
Addict
Posts: 1484
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by ChrisR »

Nice solution RASHAD :)
A small addition to close the Combobox by clicking on it again

Code: Select all

LoadFont(0,"Arial",11)

Define IsListIconVisible

OpenWindow(0,0,0,400,250,"Window",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ComboBoxGadget(1,10,10,180,24) 
;dLh = FindWindow_("ComboLBox",0)   
SetGadgetFont(1,FontID(0))
ListIconGadget(2,GadgetX(1),GadgetY(1)+GadgetHeight(1),360,200,"",70,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#LVS_NOCOLUMNHEADER)
SetGadgetColor(2,#PB_Gadget_BackColor,$D5F3FE)
SetGadgetColor(2,#PB_Gadget_LineColor,$0)
AddGadgetColumn(2,1,"",100)
AddGadgetColumn(2,2,"",180)
For i = 0 To 10
  AddGadgetItem(1,i,"Item "+Str(i))
  AddGadgetItem(2,i,"Item "+Str(i)+#LF$+"Text "+Str(i)+#LF$+"another text "+Str(i))
Next
SendMessage_(GadgetID(1), #CB_SETMINVISIBLE, 1, 0)

HideGadget(2,1)
SetGadgetState(1,8)
Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_CloseWindow
      Quit = 1      
      
    Case #WM_LBUTTONDOWN,#WM_LBUTTONDBLCLK
      act = GetActiveGadget()
      Select  act
        Case 1
          If IsListIconVisible
            SetActiveGadget(2)
            HideGadget(2,1)
            IsListIconVisible = #False
          Else
            HideGadget(2,0)
            SetActiveGadget(2)
            IsListIconVisible = #True
          EndIf
          
        Case 2
          text$ = GetGadgetItemText(1,GetGadgetState(2),0)
          SetGadgetText(1,text$) 
          HideGadget(2,1)              
          IsListIconVisible = #False
          
      EndSelect
      
  EndSelect 
  
Until Quit = 1
End

PBJim
Enthusiast
Enthusiast
Posts: 296
Joined: Fri Jan 19, 2024 11:56 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by PBJim »

RASHAD wrote: Tue Nov 12, 2024 11:47 am Previous post updated
Thanks Rashad, it works well.

Incidentally, was it important to use a specific font? I wasn't sure if it was done so that you could be certain other possible default Windows fonts didn't ruin the result.

Code: Select all

; LoadFont(0,"Arial",11)
; SetGadgetFont(1,FontID(0))
I'm not even sure where Windows' default font settings are defined these days, it's been so long since I've looked at them.
Axolotl
Addict
Addict
Posts: 872
Joined: Wed Dec 31, 2008 3:36 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by Axolotl »

PBJim wrote: Tue Nov 12, 2024 10:36 am ..... I wish the solution was a bit easier. The only thing that I feel might be a downside with this, is that the callbacks are perhaps paired with each ComboBoxGadget, so where we have a window containing a lot of ComboBoxGadgets, the callbacks might become difficult for others to maintain in future. But, I haven't started to put that into use yet, so I may be overthinking. .....
You should not be so scared about the callback solution, because the callback function relates to the parent window.
What you have to do is, you need some configuration. I did that in a small structure and a map.
See the updated example based on breeze4me's update of my first example.
Thanks to breeze4me for his good addition.
BTW: I think this is very good solution, because it works with mouse and keyboard. (IMHO)

Code: Select all

;
; File : ComboBox_AdvancedExample.pb 
;
; ComboBox OwnerDraw in PureBasic 
; 
EnableExplicit 

Enumeration EWindow 1 
  #WND_Main 
EndEnumeration 

Enumeration EGadget 1 
  #GDT_strInfo 
  #GDT_cbbValues 
  #GDT_cbbZipCodes   ; of Florida ... why not!  
EndEnumeration 


#CB_GETCOMBOBOXINFO = 356

Structure COMBOBOXINFO Align #PB_Structure_AlignC
  cbSize.l
  rcItem.RECT
  rcButton.RECT
  stateButton.l
  hwndCombo.i
  hwndItem.i
  hwndList.i
EndStructure

#COMBOBOX_COLUMN_0 =  60  
#COMBOBOX_COLUMN_1 = 120  
#COMBOBOX_COLUMN_2 = 120 


; new user defined structure 
;
Structure TComboBoxData 
  Separator$          ; i.e.  #LF$, |, etc. 
  ListWndWidth.i      ; listbox of the combobox 
  Array Columns.i(0)  ; 
  ; tbc. 
EndStructure 


; --- 

Global NewMap AdvComboBoxData.TComboBoxData()   ; Key is Str(Gadget) 


Global NewList CodeDesc.s()

AddElement(CodeDesc()) : CodeDesc() = "PINEAPPLE"
AddElement(CodeDesc()) : CodeDesc() = "BANANA"
AddElement(CodeDesc()) : CodeDesc() = "DRAGON FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "STAR FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "MANGO"
AddElement(CodeDesc()) : CodeDesc() = "RAMBUTAN"
AddElement(CodeDesc()) : CodeDesc() = "WATERMELON"
AddElement(CodeDesc()) : CodeDesc() = "POMEGRANATE"
AddElement(CodeDesc()) : CodeDesc() = "LYCHEE"
AddElement(CodeDesc()) : CodeDesc() = "MANGOSTEEN"
AddElement(CodeDesc()) : CodeDesc() = "TAMARIND"
AddElement(CodeDesc()) : CodeDesc() = "PASSION FRUIT"
AddElement(CodeDesc()) : CodeDesc() = "PAPAYA"

; --- 
; --- 

Procedure WindowCallback(hWnd, Msg, WParam, LParam) 
  Protected result, hBrush, *lpdis.DRAWITEMSTRUCT
  Protected text$, t$, sep$, tlen, nn, width, dtFlags  
  Protected cbinfo.COMBOBOXINFO, rt.RECT  ; resize the list of the combobox 
  
  Select Msg
    Case #WM_DRAWITEM
      *lpdis = lParam  ; typecast to DRAWITEMSTRUCT 
      If *lpdis\CtlType = #ODT_COMBOBOX 
        
        ; Change the listbox size of the combobox.
        ;
        If IsGadget(WParam) And GadgetType(WParam) = #PB_GadgetType_ComboBox
          cbinfo\cbSize = SizeOf(COMBOBOXINFO)
          SendMessage_(GadgetID(WParam), #CB_GETCOMBOBOXINFO, 0, @cbinfo)
          width = AdvComboBoxData(Str(WParam))\ListWndWidth

          If cbinfo\hwndList
            GetWindowRect_(cbinfo\hwndList, rt)
            MoveWindow_(cbinfo\hwndList, rt\left, rt\top, DesktopScaledX(width), rt\bottom - rt\top, 0)
          EndIf
        EndIf
        
        SetBkMode_(*lpdis\hDC, #TRANSPARENT)    ; Text is rendered transparent 
        
        If *lpdis\ItemState & #ODS_FOCUS 
          hBrush = GetSysColorBrush_(#COLOR_HIGHLIGHT)  ; returns a cached brush instead of allocating a new one 
          FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush) 
          
          SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_HIGHLIGHTTEXT)) 
;         DrawFocusRect_(*lpdis\hDC, *lpdis\rcItem)   ; <-- It's up to you. (I like it better this way) 
        Else 
          ; Fill the background color differently for odd and even lines.
          ;
          If *lpdis\itemID & 1 ; % 2 ; .. even or odd ?? 
            hBrush = GetSysColorBrush_(#COLOR_WINDOW) ; this is borrowed from system, destroying is not needed. 
            FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush) 
          Else
            hBrush = CreateSolidBrush_($FFF8F0) ; AliceBlue <-- my fav color. 
            FillRect_(*lpdis\hDC, *lpdis\rcItem, hBrush) 
            DeleteObject_(hBrush) ; here we need to destroy the created brush 
          EndIf
          
          SetTextColor_(*lpdis\hDC, GetSysColor_(#COLOR_WINDOWTEXT)) 
        EndIf 

        If *lpdis\itemID <> -1 
          tlen = SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXTLEN, *lpdis\itemID, 0) ; lParam .. not used 
          If tlen > 0 
            text$ = Space(tlen)
            SendMessage_(*lpdis\hwndItem, #CB_GETLBTEXT, *lpdis\itemID, @text$) 
          EndIf 
          
          dtFlags = #DT_LEFT | #DT_VCENTER | #DT_END_ELLIPSIS 
          If Asc(text$) 
            sep$ = AdvComboBoxData(Str(WParam))\Separator$ 

            ; column 0
            ;
            t$ = StringField(text$, 1, sep$) ; #LF$) 
            If Asc(t$) 
              *lpdis\rcItem\left + 5
              DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags) 
            EndIf 

            ; column 1 to N 
            ;
            For nn = 0 To ArraySize(AdvComboBoxData()\Columns()) - 1 
              t$ = StringField(text$, nn + 2, sep$) ; #LF$) 
              If Asc(t$) 
                *lpdis\rcItem\left + DesktopScaledX(AdvComboBoxData()\Columns(nn)) 
              
                If *lpdis\itemState & #ODS_COMBOBOXEDIT = 0 
                  Protected hPen, oldPen

                  ; Draw a vertical divider for the column.
                  ;
                  hPen = CreatePen_(#PS_SOLID, 1, $BBBBBB) 
                  If hPen
                    oldPen = SelectObject_(*lpdis\hdc, hPen)
                    MoveToEx_(*lpdis\hdc, *lpdis\rcItem\left - 5, *lpdis\rcItem\top, 0)
                    LineTo_(*lpdis\hdc, *lpdis\rcItem\left - 5, *lpdis\rcItem\bottom)
                    SelectObject_(*lpdis\hdc, oldPen)
                    DeleteObject_(hPen)
                  EndIf

                  DrawText_(*lpdis\hdc, t$, Len(t$), *lpdis\rcItem, dtFlags) 
                EndIf
              EndIf 
            Next nn 
          EndIf 
        EndIf 

      EndIf 
    ; ProcedureReturn result 
  
  EndSelect 
  
  ; default means handling by PB internally 
  ;
  ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure 


Procedure OpenMainWindow(WndW=400, WndH=100) 
  If OpenWindow(#WND_Main, 0, 0, WndW, WndH, "ComboBox OwnerDraw", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_TitleBar ) 
    StringGadget(#GDT_strInfo, 8, 8, WndW-16, 20, "Selected Combobox item is .... ")  
    
    ComboBoxGadget(#GDT_cbbValues, 8, 40, 100, 21, #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED) 
    SendMessage_(GadgetID(#GDT_cbbValues), #CB_SETITEMHEIGHT, 0, DesktopScaledY(GadgetHeight(#GDT_cbbValues) - 2))
    AdvComboBoxData(Str(#GDT_cbbValues))\Separator$ = #LF$ 
    AdvComboBoxData()\ListWndWidth = 200  ; not scaled pixels 
    Dim AdvComboBoxData()\Columns(1) 
    AdvComboBoxData()\Columns(0) = #COMBOBOX_COLUMN_0 

    ; NEW ComboBox useing the same callback .....
    ;
    ComboBoxGadget(#GDT_cbbZipCodes, 8, 64, 100, 21, #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED) 
    SendMessage_(GadgetID(#GDT_cbbZipCodes), #CB_SETITEMHEIGHT, 0, DesktopScaledY(GadgetHeight(#GDT_cbbZipCodes) - 2))  ; ?? breezeme stuff! 
    AdvComboBoxData(Str(#GDT_cbbZipCodes))\Separator$ = #LF$ ;"|" 
    AdvComboBoxData()\ListWndWidth = 400  ; not scaled pixels 
    Dim AdvComboBoxData()\Columns(3) 
    AdvComboBoxData()\Columns(0) = #COMBOBOX_COLUMN_0 
    AdvComboBoxData()\Columns(1) = #COMBOBOX_COLUMN_1 
    AdvComboBoxData()\Columns(2) = #COMBOBOX_COLUMN_2 
    
    SetWindowCallback(@WindowCallback(), #WND_Main) 
    
    ProcedureReturn #True 
  EndIf 
  ProcedureReturn #False
EndProcedure 

Procedure Main() 
  Protected index, text$  

  If OpenMainWindow() 

    ForEach(CodeDesc())
      index + 1
      ; **
      ; **  Insert delimiter #LF$ to enable ComboBoxGadget value to be extracted without its
      ; **  associated description
      ; **
      AddGadgetItem(#GDT_cbbValues, -1, "STK" + RSet(Str(index), 3, "0") + #LF$ + CodeDesc()) 
    Next  

    ; Copied from https://www.zip-codes.com/state/fl.asp 
    ; ZIP Code LF City LF County LF Type 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32003" + #LF$ + "Fleming Island" + #LF$ + "Clay" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32004" + #LF$ + "Ponte Vedra Beach" + #LF$ + "Saint Johns" + #LF$ + "P.O. Box") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32006" + #LF$ + "Fleming Island" + #LF$ + "Clay" + #LF$ + "P.O. Box") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32007" + #LF$ + "Bostwick" + #LF$ + "Putnam" + #LF$ + "P.O. Box") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32008" + #LF$ + "Branford" + #LF$ + "Suwannee" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32009" + #LF$ + "Bryceville" + #LF$ + "Nassau" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32011" + #LF$ + "Callahan" + #LF$ + "Nassau" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32013" + #LF$ + "Day" + #LF$ + "Lafayette" + #LF$ + "P.O. Box") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32024" + #LF$ + "Lake City" + #LF$ + "Columbia" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32025" + #LF$ + "Lake City" + #LF$ + "Columbia" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32030" + #LF$ + "Doctors Inlet" + #LF$ + "Clay" + #LF$ + "P.O. Box") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32033" + #LF$ + "Elkton" + #LF$ + "Saint Johns" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32034" + #LF$ + "Fernandina Beach" + #LF$ + "Nassau" + #LF$ + "Standard") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32035" + #LF$ + "Fernandina Beach" + #LF$ + "Nassau" + #LF$ + "P.O. Box") 
    AddGadgetItem(#GDT_cbbZipCodes, -1, "32038" + #LF$ + "Fort White" + #LF$ + "Columbia" + #LF$ + "Standard") 


    Repeat 
      Select WaitWindowEvent() 
        Case #PB_Event_CloseWindow 
          Break ; bye 
        Case #PB_Event_Gadget 
          Select EventGadget() 
            Case #GDT_cbbValues 
              text$ = GetGadgetText(#GDT_cbbValues) 
              SetGadgetText(#GDT_strInfo, "Stock code == " + StringField(text$, 1, #LF$)) 

            Case #GDT_cbbZipCodes 
              text$ = GetGadgetText(#GDT_cbbZipCodes) 
              Debug text$ 
            
          EndSelect 
      EndSelect 
    ForEver 
  EndIf 
  ProcedureReturn 0 
EndProcedure 

End Main() 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
PBJim
Enthusiast
Enthusiast
Posts: 296
Joined: Fri Jan 19, 2024 11:56 pm

Re: Multi-column ComboBoxGadget and use of separating delimiter

Post by PBJim »

ChrisR wrote: Tue Nov 12, 2024 1:38 pm Nice solution RASHAD :)
A small addition to close the Combobox by clicking on it again
Thanks for that ChirisR, I'd noticed that also and put a toggle into it.

The difficulty with these workarounds, once we diverge from PB's built-in gadgets, is that there are often several unintended consequences, such as this one below :

Image

I realised that if the special combobox overlayed other fields, I could tab to them anyway. So we have to make sure to close the special combobox in several cases, tab, lost focus etc.

The thing is, for the solution we're working on, we could just resolve the requirement with SetGadgetItemData() and store the length of the key field in there, because it allows us to later extract the key — of otherwise unknown length and format — from the complete ComboBoxGadget line. Anyway, it's been interesting to see other options.
Post Reply