Page 1 of 1

Right-click on a ComboBoxGadget

Posted: Sun Sep 14, 2025 10:27 pm
by RobertRioja
I hope someone can answer this. If I click on a ComboBoxGadget, its list will drop down so I can click on any item. That will trigger the #PB_EventType_Change event. However, I need to right-click on an item and I don't see a way of capturing that event.

Any ideas?

Thanks,
Rob

Re: Right-click on a ComboBoxGadget

Posted: Mon Sep 15, 2025 1:45 pm
by Axolotl
Which OS?

On windows maybe by doing the following:
1. Get the ListBox handle (GetComboBoxInfo),
2. subclass it (SetWindowSubclass)
3. look at WM_RBUTTONDOWN and LB_ITEMFROMPOINT

Re: Right-click on a ComboBoxGadget

Posted: Mon Sep 15, 2025 2:32 pm
by Axolotl
Here is an example of right-click on the dropdown-listbox ..... (Windows Only)

Code: Select all

EnableExplicit 

Import "Comctl32.lib"  ;{ <<< from (latest) Comctl32.dll  >>>
  ; use the PureBasic Syntax (Windows API procedures using trailing underscore) 
  ; 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    SetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, dwRefData)  As "SetWindowSubclass" 
    GetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, *dwRefData) As "GetWindowSubclass"
    RemoveWindowSubclass_(hWnd, *fnSubclass, uIdSubclass)          As "RemoveWindowSubclass"
    DefSubclassProc_(hWnd, uMsg, wParam, lParam)                   As "DefSubclassProc"
  CompilerElse
    SetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, dwRefData)  As "_SetWindowSubclass@16" 
    GetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, *dwRefData) As "_GetWindowSubclass@16"
    RemoveWindowSubclass_(hWnd, *fnSubclass, uIdSubclass)          As "_RemoveWindowSubclass@12"
    DefSubclassProc_(hWnd, uMsg, wParam, lParam)                   As "_DefSubclassProc@16" 
  CompilerEndIf
EndImport 

; for simplicity's sake, constants (enums are even better)  
#WND_Main = 1 
#GDT_cbbTest = 2 

Structure COMBOBOXINFO 
  cbSize.l        ; DWORD 
  rcItem.RECT     ; RECT  
  rcButton.RECT   ; RECT  
  stateButton.l   ; DWORD 
  hwndCombo.i     ; HWND  
  hwndItem.i      ; HWND  
  hwndList.i      ; HWND  
EndStructure 

#LB_ITEMFROMPOINT = 425 

Procedure SubclassComboboxList(hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) 
  Protected index, pt.POINT 

  Select uMsg 
    Case #WM_NCDESTROY                                                         : Debug "WM_NCDESTROY" 
      RemoveWindowSubclass_(hWnd, @SubclassComboboxList(), uIdSubclass) 

    Case #WM_LBUTTONDOWN                                                       : Debug "WM_LBUTTONDOWN" 

    Case #WM_RBUTTONDOWN                                                       : Debug "WM_RBUTTONDOWN" 
      GetCursorPos_(@pt) 
      ScreenToClient_(hWnd, @pt) 
      index = ((pt\y & $FFFF) << 16) + (pt\x + $FFFF) 
      index = SendMessage_(hWnd, #LB_ITEMFROMPOINT, 0, index) 
      If (index >> 16) & $FFFF  ; inside the listbox 
        index & $FFFF                                   : Debug "  index == " + index + "   outside " 
      Else 
        index & $FFFF                                   : Debug "  index == " + index + "   inside " 
      EndIf 

  EndSelect 
  ProcedureReturn DefSubclassProc_(hWnd, uMsg, wParam, lParam) 
EndProcedure

Procedure Main() 
  Protected ii, hList, cbi.COMBOBOXINFO 

  If OpenWindow(#WND_Main, 0, 0, 256, 200, "Something with Combobox ... ", #PB_Window_ScreenCentered|#PB_Window_SystemMenu) 
    StickyWindow(#WND_Main, 1) ; always on top :) 

    ComboBoxGadget(#GDT_cbbTest, 8, 8, 240, 24) 
      For ii = 1 To 9 
        AddGadgetItem(#GDT_cbbTest, -1, "Test Text Item "+ii) 
      Next 
      SetGadgetState(#GDT_cbbTest, 1) 


    cbi\cbSize = SizeOf(COMBOBOXINFO) 
    If GetComboBoxInfo_(GadgetID(#GDT_cbbTest), @cbi)
      hList = cbi\hwndList                                  : Debug "Combobox List hwnd == " + hList 
      SetWindowSubclass_(hList, @SubclassComboboxList(), #GDT_cbbTest, 0)  ; 
    EndIf 

    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow 
          Break ; say good bye. 
      EndSelect
    ForEver
  EndIf 
EndProcedure 

End Main() 

Re: Right-click on a ComboBoxGadget

Posted: Mon Sep 15, 2025 11:42 pm
by RobertRioja
Hello Axolotl,

Thank you very much. This is exactly what I needed.

Rob

Re: Right-click on a ComboBoxGadget

Posted: Tue Sep 16, 2025 3:05 am
by BarryG

Code: Select all

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

  ComboBoxGadget(0, 10, 10, 250, 21)
  For a = 1 To 5
    AddGadgetItem(0, -1,"ComboBox item " + Str(a))
  Next
  
  Repeat
  
    ev=WaitWindowEvent()
    
    If ev=#WM_RBUTTONDOWN
      Debug "Right-clicked on "+Str(GetGadgetState(0))
    EndIf
    
  Until ev = #PB_Event_CloseWindow
  
EndIf

Re: Right-click on a ComboBoxGadget

Posted: Tue Sep 16, 2025 2:00 pm
by Axolotl
Works well, with one limitation: If the mouse is outside the list box when clicked, it is not recognized.

Re: Right-click on a ComboBoxGadget

Posted: Wed Sep 17, 2025 8:31 am
by BarryG
How is that a limitation? The request is for when the gadget is right-clicked, so if the gadget is not in focus, then no big deal?

Re: Right-click on a ComboBoxGadget

Posted: Wed Sep 17, 2025 8:05 pm
by RobertRioja
The real problem is that right-clicking anywhere will trigger the event. I need it to trigger ONLY when an item in the ComboBoxGadget is right-clicked.

Rob

Re: Right-click on a ComboBoxGadget

Posted: Thu Sep 18, 2025 12:57 am
by mk-soft

Code: Select all

;-TOP by mk-soft

Procedure IsCursorOnGadget(Gadget)
  Protected r1, hWnd, cursor.q, rect.RECT
  hWnd = GadgetID(Gadget)
  GetCursorPos_(@cursor)
  GetClientRect_(hWnd, @rect)
  ClientToScreen_(hWnd, @rect)
  rect\right + rect\left
  rect\bottom + rect\top
  r1 = PtInRect_(@rect, cursor)
  ProcedureReturn r1
EndProcedure

If OpenWindow(0, 0, 0, 270, 80, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  ComboBoxGadget(0, 10, 10, 250, 21)
  For a = 1 To 5
    AddGadgetItem(0, -1,"ComboBox item " + Str(a))
  Next
  
  Repeat
    
    ev=WaitWindowEvent()
    
    If ev=#WM_RBUTTONDOWN
      
      If IsCursorOnGadget(0)
        Debug "Right-clicked on "+Str(GetGadgetState(0))
      EndIf
      
    EndIf
    
  Until ev = #PB_Event_CloseWindow
  
EndIf

Re: Right-click on a ComboBoxGadget

Posted: Thu Sep 18, 2025 2:03 am
by RobertRioja
The problem with the last reply, is that it only works if you right-click on the gadget itself. I need to be able to right-click on an ITEM that is pulled down from the ComboBoxGadget. The IsCursorOnGadget function only looks at the boundaries of the gadget, not the pull down list.

Rob

Re: Right-click on a ComboBoxGadget

Posted: Thu Sep 18, 2025 2:34 am
by BarryG
My code does let you right-click on an item from the drop-down list? It even shows which item number was right-clicked.

Re: Right-click on a ComboBoxGadget

Posted: Thu Sep 18, 2025 3:27 pm
by Axolotl
okay, new try based on my code from above.
Now, after open the Dropdownlist with LeftClick you can use either Left or Right Mouse Click to do the selection.
Easier to show than to describe.
Anyway the result is in the main loop. (I borrowed the #PB_Eventtype_RightClick for that)
Please bear in mind it's a concept only.

Code: Select all

EnableExplicit 

Import "Comctl32.lib"  ;{ <<< from (latest) Comctl32.dll  >>>
  ; use the PureBasic Syntax (Windows API procedures using trailing underscore) 
  ; 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    SetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, dwRefData)  As "SetWindowSubclass" 
    GetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, *dwRefData) As "GetWindowSubclass"
    RemoveWindowSubclass_(hWnd, *fnSubclass, uIdSubclass)          As "RemoveWindowSubclass"
    DefSubclassProc_(hWnd, uMsg, wParam, lParam)                   As "DefSubclassProc"
  CompilerElse
    SetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, dwRefData)  As "_SetWindowSubclass@16" 
    GetWindowSubclass_(hWnd, *fnSubclass, uIdSubclass, *dwRefData) As "_GetWindowSubclass@16"
    RemoveWindowSubclass_(hWnd, *fnSubclass, uIdSubclass)          As "_RemoveWindowSubclass@12"
    DefSubclassProc_(hWnd, uMsg, wParam, lParam)                   As "_DefSubclassProc@16" 
  CompilerEndIf
EndImport 

; for simplicity's sake, constants (enums are even better)  
#WND_Main = 1 
#GDT_cbbTest = 2 


Structure COMBOBOXINFO 
  cbSize.l        ; DWORD 
  rcItem.RECT     ; RECT  
  rcButton.RECT   ; RECT  
  stateButton.l   ; DWORD 
  hwndCombo.i     ; HWND  
  hwndItem.i      ; HWND  
  hwndList.i      ; HWND  
EndStructure 

#LB_ITEMFROMPOINT = 425 

Procedure SubclassComboboxList(hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) 
  Protected index, pt.POINT 

  Select uMsg 
    Case #WM_NCDESTROY                                                         : Debug "WM_NCDESTROY" 
      RemoveWindowSubclass_(hWnd, @SubclassComboboxList(), uIdSubclass) 

;   Case #WM_LBUTTONDOWN                                                       : Debug "WM_LBUTTONDOWN" 

    Case #WM_RBUTTONDOWN                                                       ;: Debug "WM_RBUTTONDOWN" 
      GetCursorPos_(@pt) 
      ScreenToClient_(hWnd, @pt) 
      index = ((pt\y & $FFFF) << 16) + (pt\x + $FFFF) 
      index = SendMessage_(hWnd, #LB_ITEMFROMPOINT, 0, index) 
      If (index >> 16) & $FFFF  ; inside the listbox or out 
;       index & $FFFF                                   : Debug "  index == " + index + "   outside " 
        ProcedureReturn 0 
      Else 
        index & $FFFF                                   : Debug "WM_RBUTTONDOWN -> index == " + index + "   inside " 

        SetGadgetText(uIdSubclass, GetGadgetItemText(uIdSubclass, index))   ; <= set selected text 
        SendMessage_(GadgetID(uIdSubclass), #CB_SHOWDROPDOWN, #False, 0)    ; <= close the drop down 
        PostEvent(#PB_Event_Gadget, #WND_Main, uIdSubclass, #PB_EventType_RightClick, index) ; <= send a PB_ Style Event, Index as Data not needed.  
      EndIf 
  EndSelect 
  ProcedureReturn DefSubclassProc_(hWnd, uMsg, wParam, lParam) 
EndProcedure


Procedure Main() 
  Protected ii, d, state, hList, cbi.COMBOBOXINFO 

  If OpenWindow(#WND_Main, 0, 0, 456, 200, "Something with Combobox ... ", #PB_Window_ScreenCentered|#PB_Window_SystemMenu) 
    StickyWindow(#WND_Main, 1) ; always on top :) 

    ComboBoxGadget(#GDT_cbbTest, 8, 8, 240, 24) 
      For ii = 0 To 9 
        AddGadgetItem(#GDT_cbbTest, -1, "Test Text Item "+ii) 
      Next 
      SetGadgetState(#GDT_cbbTest, 1) 


    cbi\cbSize = SizeOf(COMBOBOXINFO) 
    If GetComboBoxInfo_(GadgetID(#GDT_cbbTest), @cbi)
      hList = cbi\hwndList                                  : Debug "MAIN: Combobox List hwnd == " + hList 
      SetWindowSubclass_(hList, @SubclassComboboxList(), #GDT_cbbTest, 0)  ; 
    EndIf 

    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow 
          Break ; say good bye. 

        Case #PB_Event_Gadget 
          Select EventGadget() 
            Case #GDT_cbbTest   
              Select EventType() 
                Case #PB_EventType_Change 
                  state = GetGadgetState(#GDT_cbbTest) 
                  Debug "MAIN: EventType_Change " + state 

                Case #PB_EventType_RightClick ; NEW .. NOT supported by PB as default 
                  state = GetGadgetState(#GDT_cbbTest) 
                  d = EventData() ; <= not really needed, because the state is correct already. 
                  Debug "MAIN: EventType_RightClick " + state + ", Data = " + d 

              EndSelect 
          EndSelect 

      EndSelect
    ForEver
  EndIf 
EndProcedure 

End Main() 

Re: Right-click on a ComboBoxGadget

Posted: Sat Sep 20, 2025 12:06 am
by RobertRioja
To Axolotl: Your first code does exactly what I need. Your second code is "better" but more than what I need. Thank you.

To all else: Thank you for your efforts. But I now have what I needed.

Rob

Re: Right-click on a ComboBoxGadget

Posted: Sat Sep 20, 2025 4:01 am
by Randy Walker
Axolotl wrote: Mon Sep 15, 2025 2:32 pm Here is an example of right-click on the dropdown-listbox ..... (Windows Only)
That is a really great code sample there Axolotl. Thanks for sharing!!!