From 3a099754653acc09cd2b5560e92cdf8820bd6fc8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 7 Jan 2020 16:33:20 +0100 Subject: [PATCH 1/3] Create WXSendContextMenuEvent() helper function Put common code from all the different ports into it. This is not very useful right now, but it will allow to change this function once, instead of applying the same change to all ports, in the upcoming commit. --- include/wx/window.h | 3 +++ src/common/wincmn.cpp | 7 +++++++ src/gtk/window.cpp | 8 ++------ src/gtk1/window.cpp | 8 ++------ src/msw/window.cpp | 5 +---- src/osx/window_osx.cpp | 15 +-------------- src/qt/window.cpp | 10 +++------- 7 files changed, 19 insertions(+), 37 deletions(-) diff --git a/include/wx/window.h b/include/wx/window.h index c08ffa7712..f07c92d05d 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -1513,6 +1513,9 @@ public: // Returns true if more idle time is requested. virtual bool SendIdleEvents(wxIdleEvent& event); + // Send wxContextMenuEvent and return true if it was processed. + bool WXSendContextMenuEvent(const wxPoint& posInScreenCoords); + // get the handle of the window for the underlying window system: this // is only used for wxWin itself or for user code which wants to call // platform-specific APIs diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 9d0ef0a1b8..828b805ff4 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -3069,6 +3069,13 @@ wxWindowBase::DoGetPopupMenuSelectionFromUser(wxMenu& menu, int x, int y) #endif // wxUSE_MENUS +bool wxWindowBase::WXSendContextMenuEvent(const wxPoint& posInScreenCoords) +{ + wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), posInScreenCoords); + evtCtx.SetEventObject(this); + return HandleWindowEvent(evtCtx); +} + // methods for drawing the sizers in a visible way: this is currently only // enabled for "full debug" builds with wxDEBUG_LEVEL==2 as it doesn't work // that well and also because we don't want to leave it enabled in default diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index c211ece237..ee6e7db6d6 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -1706,12 +1706,8 @@ gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget), // (a) it's a command event and so is propagated to the parent // (b) under some ports it can be generated from kbd too // (c) it uses screen coords (because of (a)) - wxContextMenuEvent evtCtx( - wxEVT_CONTEXT_MENU, - win->GetId(), - win->ClientToScreen(event.GetPosition())); - evtCtx.SetEventObject(win); - return win->GTKProcessEvent(evtCtx); + const wxPoint pos = win->ClientToScreen(event.GetPosition()); + return win->WXSendContextMenuEvent(pos); } return FALSE; diff --git a/src/gtk1/window.cpp b/src/gtk1/window.cpp index fdc2e37a2c..98224b25fb 100644 --- a/src/gtk1/window.cpp +++ b/src/gtk1/window.cpp @@ -1629,12 +1629,8 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, // (a) it's a command event and so is propagated to the parent // (b) under some ports it can be generated from kbd too // (c) it uses screen coords (because of (a)) - wxContextMenuEvent evtCtx( - wxEVT_CONTEXT_MENU, - win->GetId(), - win->ClientToScreen(event.GetPosition())); - evtCtx.SetEventObject(win); - return win->HandleWindowEvent(evtCtx); + const wxPoint pos = win->ClientToScreen(event.GetPosition()); + return win->WXSendContextMenuEvent(pos); } return FALSE; diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 23c965885c..957b185bd7 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3636,8 +3636,6 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, // the event may be handled by a parent window wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); - // we could have got an event from our child, reflect it back // to it if this is the case wxWindowMSW *win = NULL; @@ -3650,8 +3648,7 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, if ( !win ) win = this; - evtCtx.SetEventObject(win); - processed = win->HandleWindowEvent(evtCtx); + processed = win->WXSendContextMenuEvent(pt); if ( !processed ) { diff --git a/src/osx/window_osx.cpp b/src/osx/window_osx.cpp index f3c4260ac7..51686ffd13 100644 --- a/src/osx/window_osx.cpp +++ b/src/osx/window_osx.cpp @@ -2338,20 +2338,7 @@ void wxWindowMac::OnMouseEvent( wxMouseEvent &event ) { if ( event.GetEventType() == wxEVT_RIGHT_DOWN ) { - // copied from wxGTK : CS - // VZ: shouldn't we move this to base class then? - - // generate a "context menu" event: this is similar to wxEVT_RIGHT_DOWN - // except that: - // - // (a) it's a command event and so is propagated to the parent - // (b) under MSW it can be generated from kbd too - // (c) it uses screen coords (because of (a)) - wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, - this->GetId(), - this->ClientToScreen(event.GetPosition())); - evtCtx.SetEventObject(this); - if ( ! HandleWindowEvent(evtCtx) ) + if ( !WXSendContextMenuEvent(ClientToScreen(event.GetPosition())) ) event.Skip() ; } else diff --git a/src/qt/window.cpp b/src/qt/window.cpp index 7a31b7a928..5e1ac194ef 100644 --- a/src/qt/window.cpp +++ b/src/qt/window.cpp @@ -1619,15 +1619,11 @@ bool wxWindowQt::QtHandleCloseEvent ( QWidget *handler, QCloseEvent *WXUNUSED( e bool wxWindowQt::QtHandleContextMenuEvent ( QWidget *WXUNUSED( handler ), QContextMenuEvent *event ) { - wxContextMenuEvent e( wxEVT_CONTEXT_MENU, GetId() ); - e.SetPosition( + const wxPoint pos = event->reason() == QContextMenuEvent::Keyboard ? wxDefaultPosition - : wxQtConvertPoint( event->globalPos() ) - ); - e.SetEventObject(this); - - return ProcessWindowEvent( e ); + : wxQtConvertPoint( event->globalPos() ); + return WXSendContextMenuEvent(pos); } bool wxWindowQt::QtHandleFocusEvent ( QWidget *WXUNUSED( handler ), QFocusEvent *event ) From 52416931a4c682d5de92ec165b99fa09d56d6980 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 7 Jan 2020 16:44:00 +0100 Subject: [PATCH 2/3] Send wxEVT_CONTEXT_MENU to the main window of composite controls This is important to allow catching the context menu events from the composite control children at the main window level using the main window ID: previously, these events used the (typically auto-generated internally) ID of the child window, which was an implementation detail and prevented the code binding to these events using the ID of e.g. wxListCtrl itself from working under the other platforms, where wxListCtrl is a generic composite window, even if it worked under MSW, where wxListCtrl is native. --- include/wx/window.h | 3 +++ src/common/wincmn.cpp | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/wx/window.h b/include/wx/window.h index f07c92d05d..972ef41d58 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -1514,6 +1514,9 @@ public: virtual bool SendIdleEvents(wxIdleEvent& event); // Send wxContextMenuEvent and return true if it was processed. + // + // Note that the event may end up being sent to a different window, if this + // window is part of a composite control. bool WXSendContextMenuEvent(const wxPoint& posInScreenCoords); // get the handle of the window for the underlying window system: this diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 828b805ff4..0b744892fd 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -3071,9 +3071,16 @@ wxWindowBase::DoGetPopupMenuSelectionFromUser(wxMenu& menu, int x, int y) bool wxWindowBase::WXSendContextMenuEvent(const wxPoint& posInScreenCoords) { - wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), posInScreenCoords); - evtCtx.SetEventObject(this); - return HandleWindowEvent(evtCtx); + // When the mouse click happens in a subwindow of a composite control, + // the user-visible event should seem to originate from the main window + // and, notably, use its ID and not the (usually auto-generated and so + // not very useful) ID of the subwindow. + wxWindow* const mainWin = GetMainWindowOfCompositeControl(); + + wxContextMenuEvent + evtCtx(wxEVT_CONTEXT_MENU, mainWin->GetId(), posInScreenCoords); + evtCtx.SetEventObject(mainWin); + return mainWin->HandleWindowEvent(evtCtx); } // methods for drawing the sizers in a visible way: this is currently only From 9ec0511924b56b75cc51b5462c7535fd9c94683f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 7 Jan 2020 16:52:04 +0100 Subject: [PATCH 3/3] Add a test of catching wxEVT_CONTEXT_MENU to the grid sample Verify that these events are seen as coming from the grid itself after the changes of the previous commit. --- samples/grid/griddemo.cpp | 19 +++++++++++++++++++ samples/grid/griddemo.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index bc68dae5dc..df7810c7d6 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -614,6 +614,8 @@ GridFrame::GridFrame() grid->SetAttr(11, 11, new wxGridCellAttr); grid->SetAttr(11, 11, NULL); + grid->Bind(wxEVT_CONTEXT_MENU, &GridFrame::OnGridContextMenu, this, grid->GetId()); + wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL ); topSizer->Add( grid, 1, @@ -797,6 +799,23 @@ void GridFrame::SetTabCustomHandler(wxCommandEvent&) grid->Bind(wxEVT_GRID_TABBING, &GridFrame::OnGridCustomTab, this); } +void GridFrame::OnGridContextMenu(wxContextMenuEvent& event) +{ + // This is not supposed to happen: even if the grid consists of different + // subwindows internally, all context menu events should be seen as coming + // from the grid itself. + if ( event.GetEventObject() != grid ) + { + wxLogError("Context menu unexpectedly sent from non-grid window."); + } + else + { + wxLogMessage("wxEVT_CONTEXT_MENU in the grid at at (%d, %d)", + event.GetPosition().x, event.GetPosition().y); + } + + event.Skip(); +} void GridFrame::ToggleGridLines( wxCommandEvent& WXUNUSED(ev) ) { diff --git a/samples/grid/griddemo.h b/samples/grid/griddemo.h index 9c7ab3e8f3..a80c366b7b 100644 --- a/samples/grid/griddemo.h +++ b/samples/grid/griddemo.h @@ -122,6 +122,8 @@ class GridFrame : public wxFrame void OnGridCustomTab(wxGridEvent& event); + void OnGridContextMenu(wxContextMenuEvent& event); + public: GridFrame(); ~GridFrame();