summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2016-05-10 16:21:17 +0100
committerCaolán McNamara <caolanm@redhat.com>2016-05-11 12:09:11 +0100
commit43896bcbea44aa92ee80ee7ba6cb39f5f514c751 (patch)
tree785f0c32bd73cd91a560ba0b5802687bd760fc1f
parentfac124afd23b23c648900d14c41881dc246f5e0e (diff)
Resolves: tdf#99709 native gtk3 menubar isn't accessible with F6
Change-Id: If772231e824e71c327103e147e3eef69e82339f6
-rw-r--r--include/vcl/taskpanelist.hxx1
-rw-r--r--vcl/inc/salmenu.hxx1
-rw-r--r--vcl/inc/unx/gtk/gtkframe.hxx1
-rw-r--r--vcl/inc/unx/gtk/gtksalmenu.hxx6
-rw-r--r--vcl/source/window/menu.cxx21
-rw-r--r--vcl/source/window/menubarwindow.cxx4
-rw-r--r--vcl/source/window/taskpanelist.cxx7
-rw-r--r--vcl/unx/gtk/gtksalmenu.cxx130
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
}