From c3e552ac25be001a623469c549ee8d0719b98133 Mon Sep 17 00:00:00 2001 From: Maxim Monastirsky Date: Tue, 12 Jun 2018 02:26:28 +0300 Subject: wayland: Make popup menus not show off-screen Depends on gtk 3.22 for gtk_menu_popup_at_rect. Had to rework the toolbar context menu to not execute async, as otherwise it just closes immediately. (Basically it's same as rhbz#1342823, except that I couldn't find a way to feed gtk_menu_popup_at_rect with the correct timestamp). Change-Id: I779d8803c80314d93a342f1294c7280f1adf4c98 Reviewed-on: https://gerrit.libreoffice.org/55772 Tested-by: Jenkins Reviewed-by: Maxim Monastirsky --- include/vcl/toolbox.hxx | 1 - vcl/inc/toolbox.h | 2 - vcl/source/window/toolbox.cxx | 4 -- vcl/source/window/toolbox2.cxx | 27 +++---------- vcl/unx/gtk/gtksalmenu.cxx | 87 +++++++++++++++++++++++++++++------------- 5 files changed, 65 insertions(+), 56 deletions(-) diff --git a/include/vcl/toolbox.hxx b/include/vcl/toolbox.hxx index 7944fd950a82..58bf32bb0e26 100644 --- a/include/vcl/toolbox.hxx +++ b/include/vcl/toolbox.hxx @@ -210,7 +210,6 @@ private: SAL_DLLPRIVATE bool ImplHasExternalMenubutton(); SAL_DLLPRIVATE void ImplDrawFloatwinBorder(vcl::RenderContext& rRenderContext, ImplToolItem const * pItem ); - DECL_DLLPRIVATE_LINK( ImplCallExecuteCustomMenu, void*, void ); DECL_DLLPRIVATE_LINK( ImplUpdateHdl, Timer*, void ); DECL_DLLPRIVATE_LINK( ImplCustomMenuListener, VclMenuEvent&, void ); DECL_DLLPRIVATE_LINK( ImplDropdownLongClickHdl, Timer*, void ); diff --git a/vcl/inc/toolbox.h b/vcl/inc/toolbox.h index 9d928ce6a1ba..17c5585ef490 100644 --- a/vcl/inc/toolbox.h +++ b/vcl/inc/toolbox.h @@ -129,9 +129,7 @@ struct ImplToolBoxPrivateData // the optional custom menu VclPtr mpMenu; - tools::Rectangle maMenuRect; ToolBoxMenuType maMenuType; - ImplSVEvent * mnEventId; // called when menu button is clicked and before the popup menu is executed Link maMenuButtonHdl; diff --git a/vcl/source/window/toolbox.cxx b/vcl/source/window/toolbox.cxx index 2df527a8cbd4..d1007d4badcd 100644 --- a/vcl/source/window/toolbox.cxx +++ b/vcl/source/window/toolbox.cxx @@ -1341,10 +1341,6 @@ ToolBox::~ToolBox() void ToolBox::dispose() { - // custom menu event still running? - if( mpData && mpData->mnEventId ) - Application::RemoveUserEvent( mpData->mnEventId ); - // #103005# make sure our activate/deactivate balance is right while( mnActivateCount > 0 ) Deactivate(); diff --git a/vcl/source/window/toolbox2.cxx b/vcl/source/window/toolbox2.cxx index cc86b0d9a0f9..ab4c90ece48f 100644 --- a/vcl/source/window/toolbox2.cxx +++ b/vcl/source/window/toolbox2.cxx @@ -48,7 +48,6 @@ ImplToolBoxPrivateData::ImplToolBoxPrivateData() : { meButtonSize = ToolBoxButtonSize::DontCare; mpMenu = VclPtr::Create(); - mnEventId = nullptr; maMenuType = ToolBoxMenuType::NONE; maMenubuttonItem.maItemSize = Size( TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET, TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET ); @@ -1621,9 +1620,6 @@ namespace void ToolBox::UpdateCustomMenu() { // fill clipped items into menu - if( !IsMenuEnabled() ) - return; - PopupMenu *pMenu = GetMenu(); pMenu->Clear(); @@ -1680,12 +1676,13 @@ IMPL_LINK( ToolBox, ImplCustomMenuListener, VclMenuEvent&, rEvent, void ) } } -IMPL_LINK_NOARG(ToolBox, ImplCallExecuteCustomMenu, void*, void) +void ToolBox::ExecuteCustomMenu( const tools::Rectangle& rRect ) { - mpData->mnEventId = nullptr; - if( !IsMenuEnabled() ) + if ( !IsMenuEnabled() || ImplIsInPopupMode() ) return; + UpdateCustomMenu(); + if( GetMenuType() & ToolBoxMenuType::Customize ) // call button handler to allow for menu customization mpData->maMenuButtonHdl.Call( this ); @@ -1700,8 +1697,7 @@ IMPL_LINK_NOARG(ToolBox, ImplCallExecuteCustomMenu, void*, void) bool bBorderDel = false; VclPtr pWin = this; - tools::Rectangle aMenuRect = mpData->maMenuRect; - mpData->maMenuRect.SetEmpty(); + tools::Rectangle aMenuRect = rRect; VclPtr pBorderWin; if( aMenuRect.IsEmpty() && IsFloatingMode() ) { @@ -1733,19 +1729,6 @@ IMPL_LINK_NOARG(ToolBox, ImplCallExecuteCustomMenu, void*, void) if( uId ) GrabFocusToDocument(); - -} - -void ToolBox::ExecuteCustomMenu( const tools::Rectangle& rRect ) -{ - if ( IsMenuEnabled() && !ImplIsInPopupMode() ) - { - UpdateCustomMenu(); - // handle custom menu asynchronously - // to avoid problems if the toolbox is closed during menu execute - mpData->maMenuRect = rRect; - mpData->mnEventId = Application::PostUserEvent( LINK( this, ToolBox, ImplCallExecuteCustomMenu ), nullptr, true ); - } } // checks override first, useful during calculation of sizes diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx index 8fd2ea29636a..84add236d0be 100644 --- a/vcl/unx/gtk/gtksalmenu.cxx +++ b/vcl/unx/gtk/gtksalmenu.cxx @@ -410,33 +410,9 @@ bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangl FloatWinPopupFlags nFlags) { #if GTK_CHECK_VERSION(3,0,0) - guint nButton; - guint32 nTime; - - //typically there is an event, and we can then distinguish if this was - //launched from the keyboard (gets auto-mnemoniced) or the mouse (which - //doesn't) - GdkEvent *pEvent = gtk_get_current_event(); - if (pEvent) - { - gdk_event_get_button(pEvent, &nButton); - nTime = gdk_event_get_time(pEvent); - } - else - { - nButton = 0; - nTime = GtkSalFrame::GetLastInputEventTime(); - } - VclPtr xParent = pWin->ImplGetWindowImpl()->mpRealParent; mpFrame = static_cast(xParent->ImplGetFrame()); - // do the same strange semantics as vcl popup windows to arrive at a frame geometry - // in mirrored UI case; best done by actually executing the same code - sal_uInt16 nArrangeIndex; - Point aPos = FloatingWindow::ImplCalcPos(pWin, rRect, nFlags, nArrangeIndex); - aPos = FloatingWindow::ImplConvertToAbsPos(xParent, aPos); - GLOActionGroup* pActionGroup = g_lo_action_group_new(); mpActionGroup = G_ACTION_GROUP(pActionGroup); mpMenuModel = G_MENU_MODEL(g_lo_menu_new()); @@ -445,7 +421,6 @@ bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangl GtkWidget *pWidget = gtk_menu_new_from_model(mpMenuModel); gtk_menu_attach_to_widget(GTK_MENU(pWidget), mpFrame->getMouseEventWidget(), nullptr); - gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", mpActionGroup); //run in a sub main loop because we need to keep vcl PopupMenu alive to use @@ -454,8 +429,66 @@ bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangl //until the gtk menu is destroyed GMainLoop* pLoop = g_main_loop_new(nullptr, true); g_signal_connect_swapped(G_OBJECT(pWidget), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop); - gtk_menu_popup(GTK_MENU(pWidget), nullptr, nullptr, MenuPositionFunc, - &aPos, nButton, nTime); + +#if GTK_CHECK_VERSION(3,22,0) + if (gtk_check_version(3, 22, 0) == nullptr) + { + GdkGravity rect_anchor = GDK_GRAVITY_SOUTH_WEST, menu_anchor = GDK_GRAVITY_NORTH_WEST; + + if (nFlags & FloatWinPopupFlags::Left) + { + rect_anchor = GDK_GRAVITY_NORTH_WEST; + menu_anchor = GDK_GRAVITY_NORTH_EAST; + } + else if (nFlags & FloatWinPopupFlags::Up) + { + rect_anchor = GDK_GRAVITY_NORTH_WEST; + menu_anchor = GDK_GRAVITY_SOUTH_WEST; + } + else if (nFlags & FloatWinPopupFlags::Right) + { + rect_anchor = GDK_GRAVITY_NORTH_EAST; + } + + tools::Rectangle aFloatRect = FloatingWindow::ImplConvertToAbsPos(xParent, rRect); + aFloatRect.Move(-mpFrame->maGeometry.nX, -mpFrame->maGeometry.nY); + GdkRectangle rect {static_cast(aFloatRect.Left()), static_cast(aFloatRect.Top()), + static_cast(aFloatRect.GetWidth()), static_cast(aFloatRect.GetHeight())}; + + GdkWindow* gdkWindow = widget_get_window(mpFrame->getMouseEventWidget()); + gtk_menu_popup_at_rect(GTK_MENU(pWidget), gdkWindow, &rect, rect_anchor, menu_anchor, nullptr); + } + else +#endif + { + guint nButton; + guint32 nTime; + + //typically there is an event, and we can then distinguish if this was + //launched from the keyboard (gets auto-mnemoniced) or the mouse (which + //doesn't) + GdkEvent *pEvent = gtk_get_current_event(); + if (pEvent) + { + gdk_event_get_button(pEvent, &nButton); + nTime = gdk_event_get_time(pEvent); + } + else + { + nButton = 0; + nTime = GtkSalFrame::GetLastInputEventTime(); + } + + // do the same strange semantics as vcl popup windows to arrive at a frame geometry + // in mirrored UI case; best done by actually executing the same code + sal_uInt16 nArrangeIndex; + Point aPos = FloatingWindow::ImplCalcPos(pWin, rRect, nFlags, nArrangeIndex); + aPos = FloatingWindow::ImplConvertToAbsPos(xParent, aPos); + + gtk_menu_popup(GTK_MENU(pWidget), nullptr, nullptr, MenuPositionFunc, + &aPos, nButton, nTime); + } + if (g_main_loop_is_running(pLoop)) { gdk_threads_leave(); -- cgit v1.2.3