summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-08-05 02:07:43 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-08-05 02:07:43 +0000
commit053bd0cd317a56751b5ed85e9468eda4a285bb42 (patch)
treed48502ce178c1eb50eff123a8de368f8b131b49c
parentca547b87168f2a1354af099464a76f670b5973e9 (diff)
parenta3ce5741930c8599ee939ee0d656fbb21bf7e41a (diff)
downloadgtk+-053bd0cd317a56751b5ed85e9468eda4a285bb42.tar.gz
Merge branch 'fix-tab-not-captured-in-popover' into 'master'
popovermenu: Cycle around focus also with (Shift+)Tab Closes #3915 See merge request GNOME/gtk!3732
-rw-r--r--gtk/gtkpopover.c58
-rw-r--r--gtk/gtkpopovermenu.c17
2 files changed, 69 insertions, 6 deletions
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index 4a6bd4074e..706afc9108 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -972,6 +972,59 @@ gtk_popover_unrealize (GtkWidget *widget)
g_clear_object (&priv->surface);
}
+static gboolean
+gtk_popover_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ if (!gtk_widget_get_visible (widget))
+ return FALSE;
+
+ /* This code initially comes from gtkpopovermenu.c */
+ if (gtk_widget_get_first_child (widget) == NULL)
+ {
+ /* Empty popover, so nothing to Tab through. */
+ return FALSE;
+ }
+ else
+ {
+ /* Move focus normally, but when nothing can be focused in this direction then we cycle around. */
+ if (gtk_widget_focus_move (widget, direction))
+ return TRUE;
+
+ if (gtk_popover_get_autohide (GTK_POPOVER (widget)))
+ {
+ GtkWidget *p = gtk_root_get_focus (gtk_widget_get_root (widget));
+
+ /* In the case where the popover doesn't have any focusable child (like
+ * the GtkTreePopover for combo boxes) then the focus will end up out of
+ * the popover, hence creating an infinite loop below. To avoid this, just
+ * say we had focus and stop here.
+ */
+ if (!gtk_widget_is_ancestor (p, widget) && p != widget)
+ return TRUE;
+
+ /* Cycle around with (Shift+)Tab */
+ if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD)
+ {
+ for (;
+ p != widget;
+ p = gtk_widget_get_parent (p))
+ {
+ /* Unfocus everything in the popover. */
+ gtk_widget_set_focus_child (p, NULL);
+ }
+ }
+ /* Focus again from scratch */
+ gtk_widget_focus_move (widget, direction);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+}
+
static void
gtk_popover_show (GtkWidget *widget)
{
@@ -1734,6 +1787,7 @@ gtk_popover_class_init (GtkPopoverClass *klass)
widget_class->unrealize = gtk_popover_unrealize;
widget_class->map = gtk_popover_map;
widget_class->unmap = gtk_popover_unmap;
+ widget_class->focus = gtk_popover_focus;
widget_class->show = gtk_popover_show;
widget_class->hide = gtk_popover_hide;
widget_class->measure = gtk_popover_measure;
@@ -2175,8 +2229,8 @@ gtk_popover_get_position (GtkPopover *popover)
* Sets whether @popover is modal.
*
* A modal popover will grab the keyboard focus on it when being
- * displayed. Clicking outside the popover area or pressing Esc
- * will dismiss the popover.
+ * displayed. Focus will wrap around within the popover. Clicking
+ * outside the popover area or pressing Esc will dismiss the popover.
*
* Called this function on an already showing popup with a new
* autohide value different from the current one, will cause the
diff --git a/gtk/gtkpopovermenu.c b/gtk/gtkpopovermenu.c
index 3f999fa90f..9a3458584f 100644
--- a/gtk/gtkpopovermenu.c
+++ b/gtk/gtkpopovermenu.c
@@ -480,12 +480,21 @@ gtk_popover_menu_focus (GtkWidget *widget,
else
return TRUE;
}
- else if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
+ /* Cycle around with up/down arrows and (Shift+)Tab when modal */
+ else if (gtk_popover_get_autohide (GTK_POPOVER (menu)))
{
- GtkWidget *p;
+ GtkWidget *p = gtk_root_get_focus (gtk_widget_get_root (widget));
+
+ /* In the case where the popover doesn't have any focusable child, if
+ * the menu doesn't have any item for example, then the focus will end
+ * up out of the popover, hence creating an infinite loop below. To
+ * avoid this, just say we had focus and stop here.
+ */
+ if (!gtk_widget_is_ancestor (p, widget) && p != widget)
+ return TRUE;
/* cycle around */
- for (p = gtk_root_get_focus (gtk_widget_get_root (widget));
+ for (;
p != widget;
p = gtk_widget_get_parent (p))
{
@@ -493,7 +502,7 @@ gtk_popover_menu_focus (GtkWidget *widget,
}
if (gtk_widget_focus_move (widget, direction))
return TRUE;
- }
+ }
}
return FALSE;