My app (a physics simulator) consists of a frame with a wxGLCanvas in it.
The frame has a menu bar with menu items having different shortcuts - some are plain alphanumerical characters (e.g. with label "Test1\tA") while others have modifiers (e.g. with label "Test3\tCTRL+A").
The canvas is supposed to get mouse events, and thus I bind mouse event handlers to it, and allow it to get focus.
The app works fine on MSW - the canvas gets mouse events, and hitting for example "A" invokes the target of the respective menu handler. However, on GTK3 (Ubuntu 18.04, gcc 8.4.0) the plain alphanumerical shortcuts don't work anymore, while the ones with modifiers work. Referring to the example above, hitting "A" is a nop, while hitting CTRL+A invokes the respective menu handler. Removing the GLCanvas creation allows the simple alphanumerical shortcuts to work again.
Here's a minimal repro, builds and reproduces on both MSW and Linux:
Code: Select all
#include <wx/wx.h>
#include <wx/glcanvas.h>
#include <memory>
class MyApp : public wxApp
{
public:
MyApp();
virtual bool OnInit() wxOVERRIDE;
};
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnTest1(wxCommandEvent & event);
void OnTest2(wxCommandEvent & event);
void OnTest3(wxCommandEvent & event);
private:
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
wxEND_EVENT_TABLE()
long const ID_MAIN_CANVAS = wxNewId();
long const ID_TEST1_MENUITEM = wxNewId();
long const ID_TEST2_MENUITEM = wxNewId();
long const ID_TEST3_MENUITEM = wxNewId();
// ============================================================================
// implementation
// ============================================================================
wxIMPLEMENT_APP(MyApp);
#if defined(__linux__) || defined(linux) || defined(__linux)
#include <X11/Xlib.h>
#endif
MyApp::MyApp()
{
}
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
wxInitAllImageHandlers();
MyFrame *frame = new MyFrame("Minimal wxWidgets App");
SetTopWindow(frame);
return true;
}
// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------
MyFrame::MyFrame(const wxString& title)
: wxFrame(
nullptr,
wxID_ANY,
title,
wxDefaultPosition,
wxSize(640, 480),
wxDEFAULT_FRAME_STYLE,
_T("Main Frame"))
{
{
wxMenuBar * menuBar = new wxMenuBar();
{
wxMenu * fileMenu = new wxMenu;
fileMenu->Append(wxID_EXIT, "E&xit\tAlt-X", "Quit this program");
menuBar->Append(fileMenu, "&File");
}
{
wxMenu * testMenu = new wxMenu;
{
wxMenuItem * menuItem = new wxMenuItem(testMenu, ID_TEST1_MENUITEM, "Test1\tA", wxEmptyString, wxITEM_NORMAL);
testMenu->Append(menuItem);
Connect(ID_TEST1_MENUITEM, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&MyFrame::OnTest1);
}
{
wxMenuItem * menuItem = new wxMenuItem(testMenu, ID_TEST2_MENUITEM, "Test2\tB", wxEmptyString, wxITEM_NORMAL);
testMenu->Append(menuItem);
Connect(ID_TEST2_MENUITEM, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&MyFrame::OnTest2);
}
{
wxMenuItem * menuItem = new wxMenuItem(testMenu, ID_TEST3_MENUITEM, "Test3\tCTRL+A", wxEmptyString, wxITEM_NORMAL);
testMenu->Append(menuItem);
Connect(ID_TEST3_MENUITEM, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&MyFrame::OnTest3);
}
testMenu->Append(new wxMenuItem(testMenu, wxID_SEPARATOR));
menuBar->Append(testMenu, "&Test");
}
SetMenuBar(menuBar);
}
// Comment this block out to verify "A" and "B" work
{
auto * sizer = new wxBoxSizer(wxVERTICAL);
static int glCanvasAttributes[] =
{
WX_GL_RGBA,
WX_GL_DOUBLEBUFFER,
WX_GL_DEPTH_SIZE, 16,
0, 0
};
auto * glCanvas = new wxGLCanvas(this, ID_MAIN_CANVAS, glCanvasAttributes, wxDefaultPosition, wxDefaultSize, 0L);
sizer->Add(glCanvas, 1, wxEXPAND, 0);
SetSizer(sizer);
}
SetCursor(wxCursor(wxCURSOR_CROSS));
Show();
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void MyFrame::OnTest1(wxCommandEvent & event)
{
wxMessageBox("Test 1!");
}
void MyFrame::OnTest2(wxCommandEvent & event)
{
wxMessageBox("Test 2!");
}
void MyFrame::OnTest3(wxCommandEvent & event)
{
wxMessageBox("Test 3!");
}
I've got two questions here. First, which of the two platforms is "at fault" here - in other words, is the canvas' stealing of the keys expected, or is it not?
Secondly, can anyone think of a workaround? Would it be as simple as registering key handlers with the canvas and explicitly propagating them up to the parent? Is there instead something that sounds less hacky?
Many thanks in advance for any pointers!