include/vcl/taskpanelist.hxx | 1 vcl/inc/salmenu.hxx | 3 vcl/inc/unx/gtk/gloactiongroup.h | 2 vcl/inc/unx/gtk/gtkframe.hxx | 1 vcl/inc/unx/gtk/gtksalmenu.hxx | 6 + vcl/source/window/menu.cxx | 21 ++++- vcl/source/window/menubarwindow.cxx | 9 ++ vcl/source/window/taskpanelist.cxx | 7 + vcl/unx/gtk/gloactiongroup.cxx | 6 - vcl/unx/gtk/gtksalframe.cxx | 2 vcl/unx/gtk/gtksalmenu.cxx | 132 ++++++++++++++++++++++++++++-------- vcl/unx/gtk3/gtk3gtkframe.cxx | 2 12 files changed, 150 insertions(+), 42 deletions(-)
New commits: commit 43896bcbea44aa92ee80ee7ba6cb39f5f514c751 Author: Caolán McNamara <caol...@redhat.com> Date: Tue May 10 16:21:17 2016 +0100 Resolves: tdf#99709 native gtk3 menubar isn't accessible with F6 Change-Id: If772231e824e71c327103e147e3eef69e82339f6 diff --git a/include/vcl/taskpanelist.hxx b/include/vcl/taskpanelist.hxx index fd04365..e1b908d 100644 --- a/include/vcl/taskpanelist.hxx +++ b/include/vcl/taskpanelist.hxx @@ -41,6 +41,7 @@ public: void AddWindow( vcl::Window *pWindow ); void RemoveWindow( vcl::Window *pWindow ); bool HandleKeyEvent(const KeyEvent& rKeyEvent); + static bool IsCycleKey(const vcl::KeyCode& rKeyCode); }; #endif diff --git a/vcl/inc/salmenu.hxx b/vcl/inc/salmenu.hxx index 6457d9a..a792356 100644 --- a/vcl/inc/salmenu.hxx +++ b/vcl/inc/salmenu.hxx @@ -81,6 +81,7 @@ public: virtual void Update() {} virtual bool CanGetFocus() const { return false; } + virtual bool TakeFocus() { return false; } // TODO: implement show/hide for the Win/Mac VCL native backends virtual void ShowItem( unsigned nPos, bool bShow ) { EnableItem( nPos, bShow ); } diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx index 38b6c1b..7c0803b 100644 --- a/vcl/inc/unx/gtk/gtkframe.hxx +++ b/vcl/inc/unx/gtk/gtkframe.hxx @@ -370,6 +370,7 @@ public: static GdkDisplay* getGdkDisplay(); GtkWidget* getWindow() const { return m_pWindow; } GtkFixed* getFixedContainer() const { return m_pFixedContainer; } + GtkEventBox* getEventBox() const { return m_pEventBox; } GtkWidget* getMouseEventWidget() const; #if GTK_CHECK_VERSION(3,0,0) GtkGrid* getTopLevelGridWidget() const { return m_pTopLevelGrid; } diff --git a/vcl/inc/unx/gtk/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx index 966b7f8..8469659 100644 --- a/vcl/inc/unx/gtk/gtksalmenu.hxx +++ b/vcl/inc/unx/gtk/gtksalmenu.hxx @@ -47,6 +47,8 @@ private: bool mbMenuBar; bool mbNeedsUpdate; + bool mbReturnFocusToDocument; + GtkWidget* mpMenuBarContainerWidget; GtkWidget* mpMenuBarWidget; GtkWidget* mpCloseButton; Menu* mpVCLMenu; @@ -115,9 +117,13 @@ public: void CreateMenuBarWidget(); void DestroyMenuBarWidget(); + gboolean SignalKey(GdkEventKey* pEvent); + void ReturnFocus(); virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, FloatWinPopupFlags nFlags) override; virtual void ShowCloseButton(bool bShow) override; + virtual bool CanGetFocus() const override; + virtual bool TakeFocus() override; }; class GtkSalMenuItem : public SalMenuItem diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx index 810525b..096faee 100644 --- a/vcl/source/window/menu.cxx +++ b/vcl/source/window/menu.cxx @@ -2597,13 +2597,24 @@ void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete ) bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent ) { - bool bDone = false; + // No keyboard processing when our menubar is invisible + if (!IsDisplayable()) + return false; - // No keyboard processing when system handles the menu or our menubar is invisible - if( !IsDisplayable() || - ( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) ) - return bDone; + // No keyboard processing when system handles the menu. + SalMenu *pNativeMenu = ImplGetSalMenu(); + if (pNativeMenu && pNativeMenu->VisibleMenuBar()) + { + // Except when the event is the F6 cycle pane event and we can put our + // focus into it (i.e. the gtk3 menubar case but not the mac/unity case + // where its not part of the application window) + if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode())) + return false; + if (!pNativeMenu->CanGetFocus()) + return false; + } + bool bDone = false; // check for enabled, if this method is called from another window... vcl::Window* pWin = ImplGetWindow(); if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode()) diff --git a/vcl/source/window/menubarwindow.cxx b/vcl/source/window/menubarwindow.cxx index ac84120..16d3af5 100644 --- a/vcl/source/window/menubarwindow.cxx +++ b/vcl/source/window/menubarwindow.cxx @@ -1078,6 +1078,10 @@ void MenuBarWindow::LoseFocus() void MenuBarWindow::GetFocus() { + SalMenu *pNativeMenu = pMenu ? pMenu->ImplGetSalMenu() : nullptr; + if (pNativeMenu && pNativeMenu->TakeFocus()) + return; + if ( nHighlightedItem == ITEMPOS_INVALID ) { mbAutoPopup = false; // do not open menu when activated by focus handling like taskpane cycling diff --git a/vcl/source/window/taskpanelist.cxx b/vcl/source/window/taskpanelist.cxx index 3b9a3d9..4e995a5 100644 --- a/vcl/source/window/taskpanelist.cxx +++ b/vcl/source/window/taskpanelist.cxx @@ -146,6 +146,11 @@ bool TaskPaneList::IsInList( vcl::Window *pWindow ) return false; } +bool TaskPaneList::IsCycleKey(const vcl::KeyCode& rKeyCode) +{ + return rKeyCode.GetCode() == KEY_F6 && !rKeyCode.IsMod2(); // F6 +} + bool TaskPaneList::HandleKeyEvent(const KeyEvent& rKeyEvent) { @@ -160,7 +165,7 @@ bool TaskPaneList::HandleKeyEvent(const KeyEvent& rKeyEvent) // and the shortcut conflicts with tab-control shortcut ), it is no more supported vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode(); bool bForward = !aKeyCode.IsShift(); - if( aKeyCode.GetCode() == KEY_F6 && ! aKeyCode.IsMod2() ) // F6 + if (TaskPaneList::IsCycleKey(aKeyCode)) { bool bSplitterOnly = aKeyCode.IsMod1() && aKeyCode.IsShift(); diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx index 3f1a7fb..4145afa 100644 --- a/vcl/unx/gtk/gtksalmenu.cxx +++ b/vcl/unx/gtk/gtksalmenu.cxx @@ -417,6 +417,8 @@ bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const Rectangle& rRec GtkSalMenu::GtkSalMenu( bool bMenuBar ) : mbMenuBar( bMenuBar ), mbNeedsUpdate( false ), + mbReturnFocusToDocument( false ), + mpMenuBarContainerWidget( nullptr ), mpMenuBarWidget( nullptr ), mpCloseButton( nullptr ), mpVCLMenu( nullptr ), @@ -576,63 +578,139 @@ void GtkSalMenu::ShowCloseButton(bool bShow) gtk_widget_set_valign(mpCloseButton, GTK_ALIGN_CENTER); gtk_container_add(GTK_CONTAINER(mpCloseButton), image); - gtk_grid_attach(GTK_GRID(mpMenuBarWidget), GTK_WIDGET(mpCloseButton), 1, 0, 1, 1); + gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), GTK_WIDGET(mpCloseButton), 1, 0, 1, 1); gtk_widget_show_all(mpCloseButton); #else (void)bShow; - (void)mpMenuBarWidget; + (void)mpMenuBarContainerWidget; (void)mpCloseButton; #endif } +//Typically when the menubar is deactivated we want the focus to return +//to where it came from. If the menubar was activated because of F6 +//moving focus into the associated VCL menubar then on pressing ESC +//or any other normal reason for deactivation we want focus to return +//to the document, defininitely not still stuck in the associated +//VCL menubar. But if F6 is pressed while the menubar is activated +//we want to pass that F6 back to the VCL menubar which will move +//focus to the next pane by itself. +void GtkSalMenu::ReturnFocus() +{ + if (!mbReturnFocusToDocument) + gtk_widget_grab_focus(GTK_WIDGET(mpFrame->getEventBox())); + else + mpFrame->GetWindow()->GrabFocusToDocument(); + mbReturnFocusToDocument = false; +} + +gboolean GtkSalMenu::SignalKey(GdkEventKey* pEvent) +{ + if (pEvent->keyval == GDK_F6) + { + mbReturnFocusToDocument = false; + gtk_menu_shell_cancel(GTK_MENU_SHELL(mpMenuBarWidget)); + //because we return false here, the keypress will continue + //to propogate and in the case that vcl focus is in + //the vcl menubar then that will also process F6 and move + //to the next pane + } + return false; +} + +//The GtkSalMenu is owner by a Vcl Menu/MenuBar. In the menubar +//case the vcl menubar is present and "visible", but with a 0 height +//so it not apparent. Normally it acts as though it is not there when +//a Native menubar is active. If we return true here, then for keyboard +//activation and traversal with F6 through panes then the vcl menubar +//acts as though it *is* present and we translate its take focus and F6 +//traversal key events into the gtk menubar equivalents. +bool GtkSalMenu::CanGetFocus() const +{ + return mpMenuBarWidget != nullptr; +} + +bool GtkSalMenu::TakeFocus() +{ + if (!mpMenuBarWidget) + return false; + + //Send a keyboard event to the gtk menubar to let it know it has been + //activated via the keyboard. Doesn't do anything except cause the gtk + //menubar "keyboard_mode" member to get set to true, so typically mnemonics + //are shown which will serve as indication that the menubar has focus + //(given that we wnt to show it with no menus popped down) + GdkEvent *event = gdk_event_new(GDK_KEY_PRESS); + event->key.window = GDK_WINDOW(g_object_ref(gtk_widget_get_window(mpMenuBarWidget))); + event->key.send_event = TRUE; + event->key.time = gtk_get_current_event_time(); + event->key.state = 0; + event->key.keyval = 0; + event->key.length = 0; + event->key.string = nullptr; + event->key.hardware_keycode = 0; + event->key.group = 0; + event->key.is_modifier = false; + gtk_widget_event(mpMenuBarWidget, event); + gdk_event_free(event); + + //this pairing results in a menubar with keyboard focus with no menus + //auto-popped down + gtk_menu_shell_select_first(GTK_MENU_SHELL(mpMenuBarWidget), false); + gtk_menu_shell_deselect(GTK_MENU_SHELL(mpMenuBarWidget)); + mbReturnFocusToDocument = true; + return true; +} + #if GTK_CHECK_VERSION(3,0,0) -//hack-around https://bugzilla.gnome.org/show_bug.cgi?id=762756 -static void ReturnFocus(GtkMenuShell *, gpointer pWidget) + +static void MenuBarReturnFocus(GtkMenuShell*, gpointer menu) { - GtkWidget* pTopLevel = static_cast<GtkWidget*>(pWidget); - GdkWindow *window = gtk_widget_get_window(pTopLevel); - GdkEvent *fevent = gdk_event_new(GDK_FOCUS_CHANGE); + GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu); + pMenu->ReturnFocus(); +} - fevent->focus_change.type = GDK_FOCUS_CHANGE; - fevent->focus_change.window = GDK_WINDOW(g_object_ref(window)); - fevent->focus_change.in = static_cast<gint16>(TRUE); - gtk_widget_send_focus_change(pTopLevel, fevent); - gdk_event_free(fevent); +static gboolean MenuBarSignalKey(GtkWidget*, GdkEventKey* pEvent, gpointer menu) +{ + GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu); + return pMenu->SignalKey(pEvent); } + #endif void GtkSalMenu::CreateMenuBarWidget() { #if GTK_CHECK_VERSION(3,0,0) GtkGrid* pGrid = mpFrame->getTopLevelGridWidget(); - mpMenuBarWidget = gtk_grid_new(); + mpMenuBarContainerWidget = gtk_grid_new(); - gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true); + gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarContainerWidget), true); gtk_grid_insert_row(pGrid, 0); - gtk_grid_attach(pGrid, mpMenuBarWidget, 0, 0, 1, 1); + gtk_grid_attach(pGrid, mpMenuBarContainerWidget, 0, 0, 1, 1); - GtkWidget *pMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel); - gtk_widget_insert_action_group(pMenuBarWidget, "win", mpActionGroup); - gtk_widget_set_hexpand(GTK_WIDGET(pMenuBarWidget), true); - gtk_grid_attach(GTK_GRID(mpMenuBarWidget), pMenuBarWidget, 0, 0, 1, 1); - g_signal_connect(G_OBJECT(pMenuBarWidget), "deactivate", G_CALLBACK(ReturnFocus), mpFrame->getWindow()); + mpMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel); + gtk_widget_insert_action_group(mpMenuBarWidget, "win", mpActionGroup); + gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true); + gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), mpMenuBarWidget, 0, 0, 1, 1); + g_signal_connect(G_OBJECT(mpMenuBarWidget), "deactivate", G_CALLBACK(MenuBarReturnFocus), this); + g_signal_connect(G_OBJECT(mpMenuBarWidget), "key-press-event", G_CALLBACK(MenuBarSignalKey), this); - gtk_widget_show_all(mpMenuBarWidget); + gtk_widget_show_all(mpMenuBarContainerWidget); #else - (void)mpMenuBarWidget; + (void)mpMenuBarContainerWidget; #endif } void GtkSalMenu::DestroyMenuBarWidget() { #if GTK_CHECK_VERSION(3,0,0) - if (mpMenuBarWidget) + if (mpMenuBarContainerWidget) { - gtk_widget_destroy(mpMenuBarWidget); - mpMenuBarWidget = nullptr; + gtk_widget_destroy(mpMenuBarContainerWidget); + mpMenuBarContainerWidget = nullptr; } #else - (void)mpMenuBarWidget; + (void)mpMenuBarContainerWidget; #endif } commit fac124afd23b23c648900d14c41881dc246f5e0e Author: Caolán McNamara <caol...@redhat.com> Date: Tue May 10 14:56:54 2016 +0100 make this cunning more explicit Change-Id: Ie461646e076da4aa4b144c68e0e08c0424474ad8 diff --git a/vcl/inc/salmenu.hxx b/vcl/inc/salmenu.hxx index 0145547..6457d9a 100644 --- a/vcl/inc/salmenu.hxx +++ b/vcl/inc/salmenu.hxx @@ -80,6 +80,8 @@ public: virtual void RemoveMenuBarButton( sal_uInt16 nId ); virtual void Update() {} + virtual bool CanGetFocus() const { return false; } + // TODO: implement show/hide for the Win/Mac VCL native backends virtual void ShowItem( unsigned nPos, bool bShow ) { EnableItem( nPos, bShow ); } diff --git a/vcl/source/window/menubarwindow.cxx b/vcl/source/window/menubarwindow.cxx index 4b72e0b..ac84120 100644 --- a/vcl/source/window/menubarwindow.cxx +++ b/vcl/source/window/menubarwindow.cxx @@ -1180,10 +1180,13 @@ bool MenuBarWindow::HandleMenuButtonEvent( sal_uInt16 i_nButtonId ) bool MenuBarWindow::CanGetFocus() const { - /* #i83908# do not use the menubar if it is native and invisible + /* #i83908# do not use the menubar if it is native or invisible this relies on MenuBar::ImplCreate setting the height of the menubar to 0 in this case */ + SalMenu *pNativeMenu = pMenu ? pMenu->ImplGetSalMenu() : nullptr; + if (pNativeMenu && pNativeMenu->VisibleMenuBar()) + return pNativeMenu->CanGetFocus(); return GetSizePixel().Height() > 0; } commit ab30f3367686f37c18a5c11e3621e29e37b11409 Author: Caolán McNamara <caol...@redhat.com> Date: Wed May 11 10:41:31 2016 +0100 gtk[3]: the frame argument is now unused Change-Id: Ib9989545495a8682d7cac97c02ab73d7c622aecf diff --git a/vcl/inc/unx/gtk/gloactiongroup.h b/vcl/inc/unx/gtk/gloactiongroup.h index 4fbab01..97d9db8 100644 --- a/vcl/inc/unx/gtk/gloactiongroup.h +++ b/vcl/inc/unx/gtk/gloactiongroup.h @@ -42,7 +42,7 @@ struct GLOActionGroupClass GType g_lo_action_group_get_type (void) G_GNUC_CONST; -GLOActionGroup * g_lo_action_group_new (gpointer frame); +GLOActionGroup * g_lo_action_group_new (void); void g_lo_action_group_set_top_menu (GLOActionGroup *group, gpointer top_menu); diff --git a/vcl/unx/gtk/gloactiongroup.cxx b/vcl/unx/gtk/gloactiongroup.cxx index 00b0ccc..fe946f0 100644 --- a/vcl/unx/gtk/gloactiongroup.cxx +++ b/vcl/unx/gtk/gloactiongroup.cxx @@ -100,7 +100,6 @@ g_lo_action_class_init (GLOActionClass *klass) struct GLOActionGroupPrivate { GHashTable *table; /* string -> GLOAction */ - GtkSalFrame *frame; /* Frame to which GActionGroup is associated. */ }; static void g_lo_action_group_iface_init (GActionGroupInterface *); @@ -318,7 +317,6 @@ g_lo_action_group_init (GLOActionGroup *group) GLOActionGroupPrivate); group->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - group->priv->frame = nullptr; } static void @@ -341,11 +339,9 @@ g_lo_action_group_iface_init (GActionGroupInterface *iface) } GLOActionGroup * -g_lo_action_group_new (gpointer frame) +g_lo_action_group_new() { GLOActionGroup* group = G_LO_ACTION_GROUP (g_object_new (G_TYPE_LO_ACTION_GROUP, nullptr)); - group->priv->frame = static_cast< GtkSalFrame* > (frame); - return group; } diff --git a/vcl/unx/gtk/gtksalframe.cxx b/vcl/unx/gtk/gtksalframe.cxx index 7e118f0..83e8836 100644 --- a/vcl/unx/gtk/gtksalframe.cxx +++ b/vcl/unx/gtk/gtksalframe.cxx @@ -583,7 +583,7 @@ gboolean ensure_dbus_setup( gpointer data ) // Create menu model and action group attached to this frame. GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); - GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new( static_cast< gpointer >( pSalFrame ) )); + GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new()); // Generate menu paths. ::Window windowId = GDK_WINDOW_XID( gdkWindow ); diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx index b6deecf..3f1a7fb 100644 --- a/vcl/unx/gtk/gtksalmenu.cxx +++ b/vcl/unx/gtk/gtksalmenu.cxx @@ -368,7 +368,7 @@ bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const Rectangle& rRec Point aPos = FloatingWindow::ImplCalcPos(pWin, rRect, nFlags, nArrangeIndex); aPos = FloatingWindow::ImplConvertToAbsPos(xParent, aPos); - GLOActionGroup* pActionGroup = g_lo_action_group_new(static_cast<gpointer>(mpFrame)); + GLOActionGroup* pActionGroup = g_lo_action_group_new(); mpActionGroup = G_ACTION_GROUP(pActionGroup); mpMenuModel = G_MENU_MODEL(g_lo_menu_new()); // Generate the main menu structure, populates mpMenuModel diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx index ada90e7..36754f9 100644 --- a/vcl/unx/gtk3/gtk3gtkframe.cxx +++ b/vcl/unx/gtk3/gtk3gtkframe.cxx @@ -587,7 +587,7 @@ gboolean ensure_dbus_setup( gpointer data ) // Create menu model and action group attached to this frame. GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); - GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new( static_cast< gpointer >( pSalFrame ) )); + GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new()); // Generate menu paths. sal_uIntPtr windowId = pSalFrame->GetNativeWindowHandle(pSalFrame->getWindow());
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits