Catching key events globally
From WxWiki
Keyboard events go to the component that currently has focus and do not propagate to the parent; if you are trying to catch key events globally it can thus be a little tricky. Here are a few ways to solve this problem - keep in mind there are probably more than presented here.
Contents |
[edit] Filter events
Source: http://wxforum.shadonet.com/viewtopic.php?p=66017#66017
Override wxApp::FilterEvent. This function is called early in event-processing, so you can do things like this (for F1):
int MyApp::FilterEvent(wxEvent& event) { if ( event.GetEventType()==wxEVT_KEY_DOWN && ((wxKeyEvent&)event).GetKeyCode()==WXK_F1) { frame->OnHelpF1( (wxKeyEvent&)event ); return true; } return -1; }
This will work most of the time. It will fail if
- Your application is not in the foreground. If you wish to catch keys even when your app is in the background, you will need to use platform-specific code
- Your window-manager grabs the key for itself, in which case your app won't see it.
- (Using wxGtk---I don't know about other platforms) the key is pressed while a modal wxDialog is showing, and the dialog doesn't have keyboard focus. The solution is to call SetFocus() on one of the dialog's children before calling ShowModal().
[edit] Recursive connect
You can recursively ->Connect() all components in a frame, therefore you will catch events no matter where the focus is.
void MyDialog::createChilds(void) { // create the childs: panels, textctrls, buttons, ... // ... ///////////////////////////////////// // after creation: this->connectKeyDownEvent(this); } void MyDialog::connectKeyDownEvent(wxWindow* pclComponent) { if(pclComponent) { pclComponent->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(MyDialog::onKeyDown), (wxObject*) NULL, this); wxWindowListNode* pclNode = pclComponent->GetChildren().GetFirst(); while(pclNode) { wxWindow* pclChild = pclNode->GetData(); this->connectKeyDownEvent(pclChild); pclNode = pclNode->GetNext(); } } } void MyDialog::onKeyDown(wxKeyEvent& event) { // do your event-processing here }
Code author : clyde729 (Source : http://wxforum.shadonet.com/viewtopic.php?t=9361)
Also note that many components will only receive key events if they have the wxWANTS_CHARS style flag enabled; then, you need to catch EVT_CHAR rather than or in addition to EVT_KEY_DOWN. For catching Enter presses on text controls, use style flag wxTE_PROCESS_ENTER, and catch event EVT_TEXT_ENTER.
[edit] Make a custom event handler
This code is inspired from code by Rostfrei. With it, you can simply create a class like the CEventPropagator below, then call the 'registerFor' function when your frame/panel is created (note that in the code below the 'registerFor' function is not recursive, so will only affect direct children, though it could be modified to be)
// Inspired from code by 'rostfrei' #include "wx/wx.h" #include <iostream> // ------------------------------------------------------------------------------------ // The CEventPropagator cutom event handler : it forces key events to propagate up class CEventPropagator : public wxEvtHandler { public: CEventPropagator(); static void registerFor(wxWindow* win); private: void onKeyDown(wxKeyEvent& aEvent); void onKeyUp(wxKeyEvent& aEvent); }; // ------------------------------------------------------------------------------------ CEventPropagator::CEventPropagator() { // Event connections this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEventPropagator::onKeyDown)); this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(CEventPropagator::onKeyUp)); } void CEventPropagator::onKeyDown(wxKeyEvent& aEvent) { //printf("CEventPropagator::onKeyDown\n"); aEvent.ResumePropagation(1); aEvent.Skip(); } void CEventPropagator::onKeyUp(wxKeyEvent& aEvent) { //printf("CEventPropagator::onKeyUp\n"); aEvent.ResumePropagation(1); aEvent.Skip(); } void CEventPropagator::registerFor(wxWindow* win) { wxWindowListNode* childNode = win->GetChildren().GetFirst(); while (childNode) { childNode->GetData()->PushEventHandler(new CEventPropagator()); childNode = childNode->GetNext(); } } // ------------------------------------------------------------------------------------ // Example of use class MyFrame : public wxFrame { public: MyFrame() : wxFrame(NULL, wxID_ANY, wxT("Hello wxWidgets"), wxPoint(50,50), wxSize(800,600)) { wxPanel* panel = new wxPanel(this); wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); for (int n=0; n<15; n++) { sizer->Add(new wxTextCtrl(panel, wxID_ANY, wxT("Some Widget")), 0, wxALL, 5); } panel->SetSizer(sizer); CEventPropagator::registerFor(panel); panel->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MyFrame::onKeyDown), NULL, this); } void onKeyDown(wxKeyEvent& evt) { std::cout << "Pressed key {" << evt.GetKeyCode() << "}\n"; evt.Skip(); } }; class MyApp: public wxApp { wxFrame* m_frame; public: bool OnInit() { m_frame = new MyFrame(); m_frame->Show(); return true; } }; IMPLEMENT_APP(MyApp)
[edit] System-wide Hot Key
bool wxWindow::RegisterHotKey(int hotkeyId, int modifiers, int virtualKeyCode)
Note: This is currently (wxWidgets 2.8.11) Windows only
[edit] Menu Hot key
You can also make a hot key part of a menu item
editMenu->Append(MENU_EDIT_COPY, _("Copy\tCtrl-C"));
[edit] Accelerator table
MyFrame::MyFrame(...) : wxFrame { wxAcceleratorEntry entries[14]; entries[0].Set(wxACCEL_CTRL, (int) 'O', MENU_LOAD_FILE); ... entries[12].Set(wxACCEL_NORMAL, WXK_SPACE, KEY_SPACE); entries[13].Set(wxACCEL_NORMAL, WXK_ESCAPE, KEY_ESC); wxAcceleratorTable accel(14, entries); SetAcceleratorTable(accel); } ... EVT_MENU(KEY_ESC,MyFrame::CheckAbort) EVT_MENU(KEY_SPACE,MyFrame::CheckAbort)
Source : http://wxforum.shadonet.com/viewtopic.php?t=18140 Author : celstark
[edit] Regular Polling
This is helpful to get a game-like behaviour : you can simply poll for key states using wxGetKeyState() (for instance in a wxTimer, or in idle events, etc.)
