Popup Menus

BlitzMax Modules Forums/Brucey's Modules/Popup Menus

Looking at the Menu sample program i can see where the popup menu is displayed.

Function OnRightUp(event:wxEvent)
Local x:Int, y:Int
wxMouseEvent(event).GetPosition(x, y)
MyFrame(event.parent).ShowContextMenu(x, y)
End Function

what i would like to do is recognise the control under the mouse. They will be listctrl's but the right click might occur in a section that doesn't have an item to respond to OnListItemRightClick. If i click on the header then i can respond with OnListColumnRightClick.

or should i be using wxWindow.wxMouseEvent.OnRightUp on the specific controls?

Cheers
Glenn

well just tried wxWindow.wxMouseEvent.OnRightUp and that wasn't what i was after :)

There's a HitTest() method that's part of wxListCtrl which can give you information as to what is under a specific point on the control - like an item, or a header, etc.

Haven't used it myself yet tho.

ah yes, i remember reading about that a while ago.
I don't see it in wxCodeGen though in the events, so i forgot about it...

Will try tomorrow.

Cheers

It's not an event.

You would catch a mouse event (say right-click), and then call HitTest() passing in the coords, and getting back the flags param, as well as the item number for that coord.

probably :-)

I'm not sure the flags param works? Last I tried to use it, it always returned 0.

The event I'd connect to is wxEVT_RIGHT_DOWN. HitTest() will return the item (as in list row) under mouse of the gadget you received the event from. But I'm not sure it is necessary?

Just connect up the right click mouse event to every gadget you want to check. If you don't want the gadget itself to respond to the event then remove event.Skip()

[edited to correct an error re HitTest()]

Sample code:
Trying to establish which Listbox had the right click.

Main:
'
' Example application stub generated by wxCodeGen v0.96 : 25 Feb 2008 20:01:20
'
SuperStrict

Framework wx.wxApp

Import "TestPopups.bmx"
Import brl.filesystem
Import brl.system

New MyApp.run()

Type MyApp Extends wxApp

	Field frame:MyFrame2
	
	Method OnInit:Int()

?macos
		' make the default Mac font for controls not so big
		wxSystemOptions.SetOption(wxWINDOW_DEFAULT_VARIANT, wxWINDOW_VARIANT_SMALL)
?
		wxInitAllImageHandlers()

		wxImage.AddHandler( New wxXPMHandler ) ' for Mac/Win32 (it is default on Linux)

		Local frame:MyFrame2 = MyFrame2(New MyFrame2.Create(, , "Test Popup Menu Selection"))

		frame.show()
		
		SetTopWindow(frame)

		Return True
	End Method

End Type



Type MyFrame2 Extends MyFrame2Base

	Method OnInit()
		Super.OnInit()

		' Add own initialisation code here

	End Method

	Method RightClickForPopup(event:wxMouseEvent)
		' TODO : Implement me
		DebugLog "Right Click for Popup"
		
		
		
	End Method

End Type


Gui:
'
' BlitzMax code generated with wxCodeGen v0.96 : 25 Feb 2008 20:01:20
' 
' 
' PLEASE DO "NOT" EDIT THIS FILE!
' 
SuperStrict

Import wx.wxFrame
Import wx.wxListBox
Import wx.wxPanel
Import wx.wxWindow
Import wx.wxListCtrl

Type MyFrame2Base Extends wxFrame

	Field m_panel5:wxPanel
	Field m_listBoxPopup1:wxListBox
	Field m_listBoxPopup2:wxListBox
	Field m_listBoxNoPopup:wxListBox


	Method Create:MyFrame2Base(parent:wxWindow = Null,id:Int = wxID_ANY, title:String = "", x:Int = -1, y:Int = -1, w:Int = 500, h:Int = 300, style:Int = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
		Return MyFrame2Base(Super.Create(parent, id, title, x, y, w, h, style))
	End Method

	Method OnInit()

		Local bSizer11:wxBoxSizer
		bSizer11 = New wxBoxSizer.Create(wxVERTICAL)
		m_panel5 = New wxPanel.Create(Self, wxID_ANY,,,,, wxTAB_TRAVERSAL)

		Local bSizer12:wxBoxSizer
		bSizer12 = New wxBoxSizer.Create(wxVERTICAL)
		m_listBoxPopup1 = New wxListBox.Create(m_panel5, wxID_ANY, Null)

		bSizer12.Add(m_listBoxPopup1, 0, wxALL, 5)

		m_listBoxPopup2 = New wxListBox.Create(m_panel5, wxID_ANY, Null)

		bSizer12.Add(m_listBoxPopup2, 0, wxALL, 5)

		m_listBoxNoPopup = New wxListBox.Create(m_panel5, wxID_ANY, Null)

		bSizer12.Add(m_listBoxNoPopup, 0, wxALL, 5)

		m_panel5.SetSizer(bSizer12)
		m_panel5.Layout()
		bSizer11.Add(m_panel5, 1, wxEXPAND | wxALL, 5)

		SetSizer(bSizer11)
		Layout()

		m_listBoxPopup1.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
		m_listBoxPopup2.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
	End Method

	Function _RightClickForPopup(event:wxEvent)
		MyFrame2Base(event.sink).RightClickForPopup(wxMouseEvent(event))
		DebugStop
		Local s:Int = event.getid()'sink).RightClickForPopup.parent()
		DebugLog s
		Local a:Object = event.GetEventObject()
		DebugLog ""
	End Function

	Method RightClickForPopup(event:wxMouseEvent)
		DebugLog "Please override MyFrame2.RightClickForPopup()"
		event.Skip()
	End Method

End Type


The debug shows the first listbox as -203 (getid) and the second as -204. The third listbox is not responding to the right click (Intentional)

Stepping through the debug variables i can't identify these numbers.
Also object "a" doesn't mean much to me...

So each listbox is being identified uniquely but i don't know what the code means.

Cheers
Glenn

I haven't tried HitTest() yet because i am expecting the right click to occur on empty listboxes...

I haven't tried HitTest() yet

In which case HitTest() probably returns -1 for nothing?

The debug shows the first listbox as -203 (getid) and the second as -204.

For every control you make, you can assign a unique Id. You can use this in the event system to refer to a specific control by its id. This is especially useful when a number of events use the same callback function. (otherwise, you might not care about assigning a unique id to a control that uses its own callback function).

If you don't assign an id to a control, one is assigned for you, for internal use, but your application can just assume that value is wxID_ANY.

In wxFormBuilder, you can set your Id in the properties for the control, by giving it a name, which will be made into a Const variable by wxCodeGen.

For example, you make the id something like ID_BORIS, and you'll have that generated as a Const with a unique int value.
You could then, in a shared callback method have something like :
Select event.GetId()

    Case ID_BORIS
        Debuglog "You clicked on Boris!"

    Case ID_FRED
        Debuglog "You clicked on Fred!"

End Select


Cool.
That does the trick for me.
Many Thanks again

Working Example:

TestPopupsMain.bmx
'
' Example application stub generated by wxCodeGen v0.96 : 25 Feb 2008 20:01:20
'
SuperStrict

Framework wx.wxApp

Import "TestPopups.bmx"
Import brl.filesystem
Import brl.system

New MyApp.run()

Type MyApp Extends wxApp

	Field frame:MyFrame2
	
	Method OnInit:Int()

?macos
		' make the default Mac font for controls not so big
		wxSystemOptions.SetOption(wxWINDOW_DEFAULT_VARIANT, wxWINDOW_VARIANT_SMALL)
?
		wxInitAllImageHandlers()

		wxImage.AddHandler( New wxXPMHandler ) ' for Mac/Win32 (it is default on Linux)

		Local frame:MyFrame2 = MyFrame2(New MyFrame2.Create(, , "Test Popup Menu Selection"))

		frame.show()
		
		SetTopWindow(frame)

		Return True
	End Method

End Type



Type MyFrame2 Extends MyFrame2Base

	Method OnInit()
		Super.OnInit()

		' Add own initialisation code here

	End Method

	Method RightClickForPopup(event:wxMouseEvent)

		Select event.GetId()

		    Case ID_lb1
        	DebugLog "You clicked on Boris!"

    		Case ID_lb2
        	DebugLog "You clicked on Fred!"

		End Select		
		
	End Method

End Type


TestPopups.bmx
'
' BlitzMax code generated with wxCodeGen v0.96 : 25 Feb 2008 23:22:05
' 
' 
' PLEASE DO "NOT" EDIT THIS FILE!
' 
SuperStrict

Import wx.wxFrame
Import wx.wxListBox
Import wx.wxPanel
Import wx.wxWindow
Import wx.wxMouseEvent

Type MyFrame2Base Extends wxFrame

	Field m_panel5:wxPanel
	Field m_listBoxPopup1:wxListBox
	Field m_listBoxPopup2:wxListBox
	Field m_listBoxNoPopup:wxListBox

	Const ID_lb1:Int = 1000
	Const ID_lb2:Int = 1001
	
	Method Create:MyFrame2Base(parent:wxWindow = Null,id:Int = wxID_ANY, title:String = "", x:Int = -1, y:Int = -1, w:Int = 500, h:Int = 300, style:Int = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
		Return MyFrame2Base(Super.Create(parent, id, title, x, y, w, h, style))
	End Method

	Method OnInit()

		Local bSizer11:wxBoxSizer
		bSizer11 = New wxBoxSizer.Create(wxVERTICAL)
		m_panel5 = New wxPanel.Create(Self, wxID_ANY,,,,, wxTAB_TRAVERSAL)

		Local bSizer12:wxBoxSizer
		bSizer12 = New wxBoxSizer.Create(wxVERTICAL)
		m_listBoxPopup1 = New wxListBox.Create(m_panel5, ID_lb1, Null)

		bSizer12.Add(m_listBoxPopup1, 0, wxALL, 5)

		m_listBoxPopup2 = New wxListBox.Create(m_panel5, ID_lb2, Null)

		bSizer12.Add(m_listBoxPopup2, 0, wxALL, 5)

		m_listBoxNoPopup = New wxListBox.Create(m_panel5, wxID_ANY, Null)

		bSizer12.Add(m_listBoxNoPopup, 0, wxALL, 5)

		m_panel5.SetSizer(bSizer12)
		m_panel5.Layout()
		bSizer11.Add(m_panel5, 1, wxEXPAND | wxALL, 5)

		SetSizer(bSizer11)
		Layout()

		Connect(ID_lb1, wxEVT_RIGHT_UP, _RightClickForPopup)
		Connect(ID_lb2, wxEVT_RIGHT_UP, _RightClickForPopup)
		
		m_listBoxPopup1.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
		m_listBoxPopup2.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
	
	End Method

	Function _RightClickForPopup(event:wxEvent)
		MyFrame2Base(event.sink).RightClickForPopup(wxMouseEvent(event))

	End Function

	Method RightClickForPopup(event:wxMouseEvent)
		DebugLog "Please override MyFrame2.RightClickForPopup()"

		event.Skip()
	End Method

End Type


Thanks to Brucey and David for the help.

Glad you got it sorted!

Could you send me the fbp please?
I'm wondering why it's generating this :
Connect(ID_lb1, wxEVT_RIGHT_UP, _RightClickForPopup)
Connect(ID_lb2, wxEVT_RIGHT_UP, _RightClickForPopup)
		
m_listBoxPopup1.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
m_listBoxPopup2.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)


Thanks :-)

Sent.
What is wrong with it?

Nothing, it appears :-)

Thanks for clearing things up. I wondered why the code was generating essentially duplicate Connects, but it doesn't with mine.

Nothing to see here, move along, move along!

:o)

sorry to resurrect this older thread.

TestPopupsGUI.bmx
'
' BlitzMax code generated with wxCodeGen v0.96 : 25 Feb 2008 23:22:05
' 
' 
' PLEASE DO "NOT" EDIT THIS FILE!
' 
SuperStrict

Import wx.wxFrame
Import wx.wxListBox
Import wx.wxPanel
Import wx.wxWindow
Import wx.wxMouseEvent

Type MyFrame2Base Extends wxFrame

	Field m_panel5:wxPanel
	Field m_listBoxPopup1:wxListBox
	Field m_listBoxPopup2:wxListBox
	Field m_listBoxNoPopup:wxListBox

	Const ID_lb1:Int = 1000
	Const ID_lb2:Int = 1001
	
	Method Create:MyFrame2Base(parent:wxWindow = Null,id:Int = wxID_ANY, title:String = "", x:Int = -1, y:Int = -1, w:Int = 500, h:Int = 300, style:Int = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
		Return MyFrame2Base(Super.Create(parent, id, title, x, y, w, h, style))
	End Method

	Method OnInit()

		Local bSizer11:wxBoxSizer
		bSizer11 = New wxBoxSizer.Create(wxVERTICAL)
		m_panel5 = New wxPanel.Create(Self, wxID_ANY,,,,, wxTAB_TRAVERSAL)

		Local bSizer12:wxBoxSizer
		bSizer12 = New wxBoxSizer.Create(wxVERTICAL)
		m_listBoxPopup1 = New wxListBox.Create(m_panel5, ID_lb1, Null)

		bSizer12.Add(m_listBoxPopup1, 0, wxALL, 5)

		m_listBoxPopup2 = New wxListBox.Create(m_panel5, ID_lb2, Null)

		bSizer12.Add(m_listBoxPopup2, 0, wxALL, 5)

		m_listBoxNoPopup = New wxListBox.Create(m_panel5, wxID_ANY, Null)

		bSizer12.Add(m_listBoxNoPopup, 0, wxALL, 5)

		m_panel5.SetSizer(bSizer12)
		m_panel5.Layout()
		bSizer11.Add(m_panel5, 1, wxEXPAND | wxALL, 5)

		SetSizer(bSizer11)
		Layout()

'		Connect(ID_lb1, wxEVT_RIGHT_UP, _RightClickForPopup)
'		Connect(ID_lb2, wxEVT_RIGHT_UP, _RightClickForPopup)
		
		m_listBoxPopup1.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
		m_listBoxPopup2.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
	
	End Method

	Function _RightClickForPopup(event:wxEvent)
		MyFrame2Base(event.sink).RightClickForPopup(wxMouseEvent(event))

	End Function

	Method RightClickForPopup(event:wxMouseEvent)
		DebugLog "Please override MyFrame2.RightClickForPopup()"

		event.Skip()
	End Method

End Type



and TestPopups.bmx
'
' Example application stub generated by wxCodeGen v0.96 : 25 Feb 2008 20:01:20
'
SuperStrict

Framework wx.wxApp

Import "TestPopupsGUI.bmx"
Import brl.filesystem
Import brl.system

New MyApp.run()

Type MyApp Extends wxApp

	Field frame:MyFrame2
	
	Method OnInit:Int()

?macos
		' make the default Mac font for controls not so big
		wxSystemOptions.SetOption(wxWINDOW_DEFAULT_VARIANT, wxWINDOW_VARIANT_SMALL)
?
		wxInitAllImageHandlers()

		wxImage.AddHandler( New wxXPMHandler ) ' for Mac/Win32 (it is default on Linux)

		Local frame:MyFrame2 = MyFrame2(New MyFrame2.Create(, , "Test Popup Menu Selection"))

		frame.show()
		
		SetTopWindow(frame)

		Return True
	End Method

End Type



Type MyFrame2 Extends MyFrame2Base

	Method OnInit()
		Super.OnInit()

		' Add own initialisation code here

	End Method

	Method RightClickForPopup(event:wxMouseEvent)

		Select event.GetId()

		    Case ID_lb1
        	DebugLog "You clicked on Boris!"

    		Case ID_lb2
        	DebugLog "You clicked on Fred!"

		End Select		
		
	End Method

End Type



The current version of wxCodeGen seems to generate the 2 connect lines which i have commented out.
' Connect(ID_lb1, wxEVT_RIGHT_UP, _RightClickForPopup)
' Connect(ID_lb2, wxEVT_RIGHT_UP, _RightClickForPopup)

but these two are the ones that work in this example for the listboxes.

m_listBoxPopup1.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)
m_listBoxPopup2.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)

also the Function _RightClickForPopup(event:wxEvent) has MyFrame2Base(event.sink) whereas the current wxCodeGen gives me MyFrame2Base(event.parent) whcih my listbox is not responding to.

am i doing something wrong?

Cheers
Glenn

Hi Bruce,
Just checking what wxCodeGen should be doing.
Connect(ID_lb1, wxEVT_RIGHT_UP, _RightClickForPopup)
or
m_listBoxPopup1.ConnectAny(wxEVT_RIGHT_UP, _RightClickForPopup, Null, Self)

Currently it does the first but that doesn't appear to trigger the event for me in the sample above

Cheers
Glenn

I have this working
	Function OnContextMenu(event:wxEvent)
		Local frame:MyFrame = MyFrame(event.parent)
		Local x:Int, y:Int
		
		wxContextMenuEvent(event).GetPosition(x, y)
		' This line makes sure the popup menu is positioned where the mouse is.
		frame.ScreenToClient(x, y)
		' This line displays the popup menu
		frame.ShowContextMenu(x, y)
	End Function
	
	Method ShowContextMenu(x:Int, y:Int)
		Local menu:wxMenu = New wxMenu.Create()
		
		menu.Append(Menu_Add_SelectorType, "Add a NEW Selector Type")
		menu.Append(Menu_Edit_SelectorType, "Edit the highlighted Selector Type")
		menu.AppendSeparator()
		menu.Append(Menu_Delete_SelectorType, "Delete the highlighted Selector Type")
		
		PopupMenu(menu, x, y)
		
		menu.Free()
	End Method


I can display the menu.
I can click the menu.

I need to do something on each item.
I think something like this:
	Method ClickedpopupItem(event:wxMenuItem)
		DebugLog "Popup Menu item Chosen"
		Select event.GetId()

		    Case Menu_Add_SelectorType
        	DebugLog "You clicked on ADD"

		End Select		
	End Method


The Select/Case/End Select will give me an individual action.
What do i need to do to link this method with the items from the menu?

Cheers
Glenn

got it.
' intercept all menu events And Log them in this custom event handler
PushEventHandler(New MyEvtHandler.Create(Self))


is needed in the Frame OnInit method and
' A small helper type which intercepts all menu events And logs them
Type MyEvtHandler Extends wxEvtHandler

	Field frame:MyFrame
	
	Method Create:MyEvtHandler(_frame:MyFrame)
		frame = _frame
		Return MyEvtHandler(CreateHandler())
	End Method
	
	Method OnInit()
	    Connect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, OnMenuEvent)
	End Method
		
	Function OnMenuEvent(event:wxEvent)
		DebugLog "Popup Menu item Chosen"
		Select event.GetId()

		    Case Menu_Add_SelectorType
        	DebugLog "You clicked on ADD"

'    		Case ID_lb2
 '       	DebugLog "You clicked on Fred!"

		End Select	
'		MyEvtHandler(event.parent).frame.LogMenuEvent(event)
	
		event.Skip()
	End Function

End Type

is needed to do the actual work.

I've made mouse events be raised against the actual control, rather than via Connect() on the parent, which doesn't always work.
(In rev 382).

Many thanks
I will resync right away.

Cheers
Glenn