diff options
author | Caolán McNamara <caolanm@redhat.com> | 2016-05-10 16:21:17 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2016-05-11 12:09:11 +0100 |
commit | 43896bcbea44aa92ee80ee7ba6cb39f5f514c751 (patch) | |
tree | 785f0c32bd73cd91a560ba0b5802687bd760fc1f | |
parent | fac124afd23b23c648900d14c41881dc246f5e0e (diff) |
Resolves: tdf#99709 native gtk3 menubar isn't accessible with F6
Change-Id: If772231e824e71c327103e147e3eef69e82339f6
-rw-r--r-- | include/vcl/taskpanelist.hxx | 1 | ||||
-rw-r--r-- | vcl/inc/salmenu.hxx | 1 | ||||
-rw-r--r-- | vcl/inc/unx/gtk/gtkframe.hxx | 1 | ||||
-rw-r--r-- | vcl/inc/unx/gtk/gtksalmenu.hxx | 6 | ||||
-rw-r--r-- | vcl/source/window/menu.cxx | 21 | ||||
-rw-r--r-- | vcl/source/window/menubarwindow.cxx | 4 | ||||
-rw-r--r-- | vcl/source/window/taskpanelist.cxx | 7 | ||||
-rw-r--r-- | vcl/unx/gtk/gtksalmenu.cxx | 130 |
8 files changed, 139 insertions, 32 deletions
diff --git a/include/vcl/taskpanelist.hxx b/include/vcl/taskpanelist.hxx index fd043651a4f3..e1b908d25839 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 6457d9a80569..a792356ab92e 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 38b6c1b7ed71..7c0803bac568 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 966b7f8fd73d..84696597bbc6 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 810525ba1234..096faeeb54f9 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 ac84120c73de..16d3af5ac397 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 3b9a3d9e62a0..4e995a5b42dc 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 3f1a7fb3d706..4145afaf3667 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 } |