Fix popup menu position on non-primary displays in wxGTK

If the popup menu position was explicitly specified, it wasn't taken
into account correctly in wxGTK code as it always clamped it to the
primary display boundaries, even if the window showing the menu wasn't
on this monitor at all, typically resulting in the menu shown at the
left or right edge of the monitor instead of the correct position.

Fix this by using the display containing the window instead. This
requires passing the window, or rather the menu from which it can be
retrieved, to wxPopupMenuPositionCallback too, so add a helper struct to
pass both the position and the menu to it.

Closes https://github.com/wxWidgets/wxWidgets/pull/1621
This commit is contained in:
Vadim Zeitlin
2019-10-27 17:55:51 +01:00
parent c0db9fe01c
commit e790f03950

View File

@@ -26,6 +26,7 @@
#include "wx/math.h" #include "wx/math.h"
#endif #endif
#include "wx/display.h"
#include "wx/dnd.h" #include "wx/dnd.h"
#include "wx/tooltip.h" #include "wx/tooltip.h"
#include "wx/caret.h" #include "wx/caret.h"
@@ -5678,6 +5679,12 @@ void wxWindowGTK::GTKFindWindow(GtkWidget* widget, wxArrayGdkWindows& windows)
#if wxUSE_MENUS_NATIVE #if wxUSE_MENUS_NATIVE
struct wxPopupMenuPositionCallbackData
{
wxPoint pos;
wxMenu *menu;
};
extern "C" { extern "C" {
static static
void wxPopupMenuPositionCallback( GtkMenu *menu, void wxPopupMenuPositionCallback( GtkMenu *menu,
@@ -5685,7 +5692,7 @@ void wxPopupMenuPositionCallback( GtkMenu *menu,
gboolean * WXUNUSED(whatever), gboolean * WXUNUSED(whatever),
gpointer user_data ) gpointer user_data )
{ {
// ensure that the menu appears entirely on screen // ensure that the menu appears entirely on the same display as the window
GtkRequisition req; GtkRequisition req;
#ifdef __WXGTK3__ #ifdef __WXGTK3__
gtk_widget_get_preferred_size(GTK_WIDGET(menu), &req, NULL); gtk_widget_get_preferred_size(GTK_WIDGET(menu), &req, NULL);
@@ -5693,14 +5700,24 @@ void wxPopupMenuPositionCallback( GtkMenu *menu,
gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req); gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
#endif #endif
wxSize sizeScreen = wxGetDisplaySize(); const wxPopupMenuPositionCallbackData&
wxPoint *pos = (wxPoint*)user_data; data = *static_cast<wxPopupMenuPositionCallbackData*>(user_data);
gint xmax = sizeScreen.x - req.width, const wxRect
ymax = sizeScreen.y - req.height; rect = wxDisplay(data.menu->GetInvokingWindow()).GetClientArea();
*x = pos->x < xmax ? pos->x : xmax; wxPoint pos = data.pos;
*y = pos->y < ymax ? pos->y : ymax; if ( pos.x < rect.x )
pos.x = rect.x;
if ( pos.y < rect.y )
pos.y = rect.y;
if ( pos.x + req.width > rect.GetRight() )
pos.x = rect.GetRight() - req.width;
if ( pos.y + req.height > rect.GetBottom() )
pos.y = rect.GetBottom() - req.height;
*x = pos.x;
*y = pos.y;
} }
} }
@@ -5708,7 +5725,7 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
{ {
wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") ); wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
wxPoint pos; wxPopupMenuPositionCallbackData data;
gpointer userdata; gpointer userdata;
GtkMenuPositionFunc posfunc; GtkMenuPositionFunc posfunc;
if ( x == -1 && y == -1 ) if ( x == -1 && y == -1 )
@@ -5719,8 +5736,9 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
} }
else else
{ {
pos = ClientToScreen(wxPoint(x, y)); data.pos = ClientToScreen(wxPoint(x, y));
userdata = &pos; data.menu = menu;
userdata = &data;
posfunc = wxPopupMenuPositionCallback; posfunc = wxPopupMenuPositionCallback;
} }