[gtk/wip/readonly-events: 5/5] New focus change handling



commit ac36f75c8a5e96bd4454875dd4af8be50bdee9e0
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Feb 12 20:26:29 2020 -0500

    New focus change handling
    
    Instead of relying on gdk's antiquated crossing events,
    create a new GtkCrossingEvent struct that contains the
    actual widgets, and a new event controller vfunc that
    expects this struct. This also saves us from making sense
    of X's crossing modes and details, and makes for a
    generally simpler api.
    
    The ::focus-in and ::focus-out signals of GtkEventControllerKey
    have been replaced by a single ::focus-change signal that
    takes a GtkCrossingEvent as an argument. All callers have
    been updated.

 gtk/gtkcalendar.c               |  17 ++---
 gtk/gtkentrycompletion.c        |  13 ++--
 gtk/gtkeventcontroller.c        |  61 +++++++++++++++
 gtk/gtkeventcontroller.h        |  30 ++++++++
 gtk/gtkeventcontrollerkey.c     | 163 ++++++++++++----------------------------
 gtk/gtkeventcontrollerprivate.h |   3 +
 gtk/gtkfilechooserentry.c       |  12 +--
 gtk/gtkmain.c                   |   2 -
 gtk/gtkmodelbutton.c            |  20 ++---
 gtk/gtkpopover.c                |  13 ----
 gtk/gtkpopovermenu.c            |  12 +--
 gtk/gtkspinbutton.c             |  14 ++--
 gtk/gtktext.c                   |  74 +++++++++---------
 gtk/gtktextview.c               | 108 ++++++++++++--------------
 gtk/gtktreeview.c               |  37 ++++-----
 gtk/gtktreeviewcolumn.c         |  12 +--
 gtk/gtkwidget.c                 |  19 +++++
 gtk/gtkwidgetprivate.h          |   2 +
 gtk/gtkwindow.c                 | 124 ++++++++++++++++++------------
 19 files changed, 391 insertions(+), 345 deletions(-)
---
diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c
index 6a67548648..4f99e0d4c7 100644
--- a/gtk/gtkcalendar.c
+++ b/gtk/gtkcalendar.c
@@ -256,9 +256,8 @@ static gboolean gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *
                                                          guint                  keycode,
                                                          GdkModifierType        state,
                                                          GtkWidget             *widget);
-static void     gtk_calendar_key_controller_focus       (GtkEventControllerKey *controller,
-                                                         GdkCrossingMode        mode,
-                                                         GdkNotifyType          detail,
+static void     gtk_calendar_key_controller_focus       (GtkEventController    *controller,
+                                                         const GtkCrossingEvent *event,
                                                          GtkWidget             *widget);
 static void     gtk_calendar_state_flags_changed  (GtkWidget     *widget,
                                                    GtkStateFlags  previous_state);
@@ -570,10 +569,7 @@ gtk_calendar_init (GtkCalendar *calendar)
   g_signal_connect (controller, "key-pressed",
                     G_CALLBACK (gtk_calendar_key_controller_key_pressed),
                     calendar);
-  g_signal_connect (controller, "focus-in",
-                    G_CALLBACK (gtk_calendar_key_controller_focus),
-                    calendar);
-  g_signal_connect (controller, "focus-out",
+  g_signal_connect (controller, "focus-change",
                     G_CALLBACK (gtk_calendar_key_controller_focus),
                     calendar);
   gtk_widget_add_controller (GTK_WIDGET (calendar), controller);
@@ -1370,10 +1366,9 @@ gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller,
 }
 
 static void
-gtk_calendar_key_controller_focus (GtkEventControllerKey *key,
-                                   GdkCrossingMode        mode,
-                                   GdkNotifyType          detail,
-                                   GtkWidget             *widget)
+gtk_calendar_key_controller_focus (GtkEventController     *controller,
+                                   const GtkCrossingEvent *event,
+                                   GtkWidget              *widget)
 {
   GtkCalendar *calendar = GTK_CALENDAR (widget);
   GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index c5a95c9748..bab278cc5f 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -2307,13 +2307,12 @@ accept_completion_callback (GtkEntryCompletion *completion)
   return FALSE;
 }
 
-static gboolean
-text_focus_out (GtkEntryCompletion *completion)
+static void
+text_focus_change (GtkEntryCompletion     *completion,
+                   const GtkCrossingEvent *event)
 {
-  if (gtk_widget_get_mapped (completion->priv->popup_window))
-    return FALSE;
-
-  return accept_completion_callback (completion);
+  if (!event->in && !gtk_widget_get_mapped (completion->priv->popup_window))
+    accept_completion_callback (completion);
 }
 
 static void
@@ -2349,7 +2348,7 @@ connect_completion_signals (GtkEntryCompletion *completion)
   controller = priv->entry_key_controller = gtk_event_controller_key_new ();
   g_signal_connect (controller, "key-pressed",
                     G_CALLBACK (gtk_entry_completion_key_pressed), completion);
-  g_signal_connect_swapped (controller, "focus-out", G_CALLBACK (text_focus_out), completion);
+  g_signal_connect_swapped (controller, "focus-change", G_CALLBACK (text_focus_change), completion);
   gtk_widget_add_controller (GTK_WIDGET (text), controller);
 
   completion->priv->changed_id =
diff --git a/gtk/gtkeventcontroller.c b/gtk/gtkeventcontroller.c
index f67669d18d..2748a148b1 100644
--- a/gtk/gtkeventcontroller.c
+++ b/gtk/gtkeventcontroller.c
@@ -127,6 +127,12 @@ gtk_event_controller_handle_event_default (GtkEventController *self,
   return FALSE;
 }
 
+static void
+gtk_event_controller_handle_focus_change_default (GtkEventController     *self,
+                                                  const GtkCrossingEvent *event)
+{
+}
+
 static void
 gtk_event_controller_set_property (GObject      *object,
                                    guint         prop_id,
@@ -204,6 +210,7 @@ gtk_event_controller_class_init (GtkEventControllerClass *klass)
   klass->unset_widget = gtk_event_controller_unset_widget;
   klass->filter_event = gtk_event_controller_filter_event_default;
   klass->handle_event = gtk_event_controller_handle_event_default;
+  klass->handle_focus_change = gtk_event_controller_handle_focus_change_default;
 
   object_class->finalize = gtk_event_controller_finalize;
   object_class->set_property = gtk_event_controller_set_property;
@@ -307,6 +314,33 @@ gtk_event_controller_handle_event (GtkEventController *controller,
   return retval;
 }
 
+/**
+ * gtk_event_controller_handle_focus_change:
+ * @controller: a #GtkEventController
+ * @event: a #GtkCrossingEvent
+ *
+ * Feeds a focus change event into @controller, so it can be interpreted
+ * and the controller actions triggered.
+ *
+ * Returns: %TRUE if the event was potentially useful to trigger the
+ *          controller action
+ **/
+void
+gtk_event_controller_handle_focus_change (GtkEventController     *controller,
+                                          const GtkCrossingEvent *event)
+{
+  GtkEventControllerClass *controller_class;
+
+  g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller));
+  g_return_if_fail (event != NULL);
+
+  controller_class = GTK_EVENT_CONTROLLER_GET_CLASS (controller);
+
+  g_object_ref (controller);
+  controller_class->handle_focus_change (controller, event);
+  g_object_unref (controller);
+}
+
 /**
  * gtk_event_controller_get_widget:
  * @controller: a #GtkEventController
@@ -451,3 +485,30 @@ gtk_event_controller_set_name (GtkEventController *controller,
   g_free (priv->name);
   priv->name = g_strdup (name);
 }
+
+static GtkCrossingEvent *
+gtk_crossing_event_copy (GtkCrossingEvent *crossing)
+{
+  GtkCrossingEvent *copy;
+
+  copy = g_new (GtkCrossingEvent, 1);
+
+  if (crossing->old_location)
+    copy->old_location = g_object_ref (crossing->old_location);
+  if (crossing->new_location)
+    copy->new_location = g_object_ref (crossing->new_location);
+
+  return copy;
+}
+
+static void
+gtk_crossing_event_free (GtkCrossingEvent *crossing)
+{
+  g_clear_object (&crossing->old_location);
+  g_clear_object (&crossing->new_location);
+
+  g_free (crossing);
+}
+
+G_DEFINE_BOXED_TYPE (GtkCrossingEvent, gtk_crossing_event,
+                     gtk_crossing_event_copy, gtk_crossing_event_free)
diff --git a/gtk/gtkeventcontroller.h b/gtk/gtkeventcontroller.h
index 1f058fb11b..e9910ec996 100644
--- a/gtk/gtkeventcontroller.h
+++ b/gtk/gtkeventcontroller.h
@@ -40,6 +40,33 @@ G_BEGIN_DECLS
 #define GTK_EVENT_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER, 
GtkEventControllerClass))
 
 
+typedef struct _GtkCrossingEvent GtkCrossingEvent;
+
+/**
+ * GtkCrossingEvent:
+ * @in: whether this is a focus-in or focus-out event
+ * @old_location: The old focus location
+ * @new_location: The new focus location
+ *
+ * The struct that is passed to gtk_event_controller_handle_focus_change()
+ * and is also passed to #GtkEventController::focus-change.
+ *
+ * The @old_location and @new_location fields are set to the old or new
+ * location or to %NULL if the widgets are not descencents of the current
+ * widget.
+ */
+struct _GtkCrossingEvent {
+  gboolean in;
+  GtkWidget *old_location;
+  GtkWidget *new_location;
+};
+
+#define GTK_TYPE_CROSSING_EVENT (gtk_crossing_event_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+GType               gtk_crossing_event_get_type (void) G_GNUC_CONST;
+
+
 GDK_AVAILABLE_IN_ALL
 GType        gtk_event_controller_get_type       (void) G_GNUC_CONST;
 
@@ -52,6 +79,9 @@ gboolean     gtk_event_controller_handle_event   (GtkEventController *controller
                                                   double              x,
                                                   double              y);
 GDK_AVAILABLE_IN_ALL
+void         gtk_event_controller_handle_focus_change (GtkEventController     *controller,
+                                                       const GtkCrossingEvent *event);
+GDK_AVAILABLE_IN_ALL
 void         gtk_event_controller_reset          (GtkEventController *controller);
 
 GDK_AVAILABLE_IN_ALL
diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c
index 0085e636f9..0fe9bfe0e4 100644
--- a/gtk/gtkeventcontrollerkey.c
+++ b/gtk/gtkeventcontrollerkey.c
@@ -65,8 +65,7 @@ enum {
   KEY_RELEASED,
   MODIFIERS,
   IM_UPDATE,
-  FOCUS_IN,
-  FOCUS_OUT,
+  FOCUS_CHANGE,
   N_SIGNALS
 };
 
@@ -94,57 +93,6 @@ gtk_event_controller_key_finalize (GObject *object)
   G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object);
 }
 
-static void
-update_focus (GtkEventControllerKey *key,
-              gboolean               focus_in,
-              GdkNotifyType          detail)
-{
-  gboolean is_focus;
-  gboolean contains_focus;
-
-  switch (detail)
-    {
-    case GDK_NOTIFY_VIRTUAL:
-    case GDK_NOTIFY_NONLINEAR_VIRTUAL:
-      is_focus = FALSE;
-      contains_focus = focus_in;
-      break;
-    case GDK_NOTIFY_ANCESTOR:
-    case GDK_NOTIFY_NONLINEAR:
-      is_focus = focus_in;
-      contains_focus = is_focus;
-      break;
-    case GDK_NOTIFY_INFERIOR:
-      is_focus = focus_in;
-      contains_focus = TRUE;
-      break;
-    case GDK_NOTIFY_UNKNOWN:
-    default:
-      g_warning ("Unknown focus change detail");
-      return;
-    }
-
-  g_object_freeze_notify (G_OBJECT (key));
-  if (key->is_focus != is_focus)
-    {
-      key->is_focus = is_focus;
-      g_object_notify (G_OBJECT (key), "is-focus");
-      if (key->im_context)
-        {
-          if (focus_in)
-            gtk_im_context_focus_in (key->im_context);
-          else
-            gtk_im_context_focus_out (key->im_context);
-        }
-    }
-  if (key->contains_focus != contains_focus)
-    {
-      key->contains_focus = contains_focus;
-      g_object_notify (G_OBJECT (key), "contains-focus");
-    }
-  g_object_thaw_notify (G_OBJECT (key));
-}
-
 static gboolean
 gtk_event_controller_key_handle_event (GtkEventController *controller,
                                        const GdkEvent     *event,
@@ -158,30 +106,6 @@ gtk_event_controller_key_handle_event (GtkEventController *controller,
   guint keyval;
   gboolean handled = FALSE;
 
-  if (event_type == GDK_FOCUS_CHANGE)
-    {
-      gboolean focus_in;
-      GdkCrossingMode mode;
-      GdkNotifyType detail;
-
-      gdk_event_get_focus_in (event, &focus_in);
-      gdk_event_get_crossing_mode (event, &mode);
-      gdk_event_get_crossing_detail (event, &detail);
-
-      update_focus (key, focus_in, detail);
-
-      key->current_event = event;
-
-      if (focus_in)
-        g_signal_emit (controller, signals[FOCUS_IN], 0, mode, detail);
-      else
-        g_signal_emit (controller, signals[FOCUS_OUT], 0, mode, detail);
-
-      key->current_event = NULL;
-
-      return FALSE;
-    }
-
   if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE)
     return FALSE;
 
@@ -229,6 +153,50 @@ gtk_event_controller_key_handle_event (GtkEventController *controller,
   return handled;
 }
 
+static void
+update_focus (GtkEventController     *controller,
+              const GtkCrossingEvent *event)
+{
+  GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
+  GtkWidget *widget = gtk_event_controller_get_widget (controller);
+  gboolean is_focus = FALSE;
+  gboolean contains_focus = FALSE;
+
+  if (event->new_location == widget)
+    is_focus = TRUE;
+  else if (event->new_location != NULL)
+    contains_focus = TRUE;
+
+  g_object_freeze_notify (G_OBJECT (key));
+  if (key->is_focus != is_focus)
+    {
+      key->is_focus = is_focus;
+      g_object_notify (G_OBJECT (key), "is-focus");
+      if (key->im_context)
+        {
+          if (is_focus)
+            gtk_im_context_focus_in (key->im_context);
+          else
+            gtk_im_context_focus_out (key->im_context);
+        }
+    }
+  if (key->contains_focus != contains_focus)
+    {
+      key->contains_focus = contains_focus;
+      g_object_notify (G_OBJECT (key), "contains-focus");
+    }
+  g_object_thaw_notify (G_OBJECT (key));
+}
+
+static void
+gtk_event_controller_key_handle_focus_change (GtkEventController     *controller,
+                                              const GtkCrossingEvent *event)
+{
+  update_focus (controller, event);
+
+  g_signal_emit (controller, signals[FOCUS_CHANGE], 0, event);
+}
+
 static void
 gtk_event_controller_key_get_property (GObject    *object,
                                        guint       prop_id,
@@ -261,6 +229,7 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
   object_class->finalize = gtk_event_controller_key_finalize;
   object_class->get_property = gtk_event_controller_key_get_property;
   controller_class->handle_event = gtk_event_controller_key_handle_event;
+  controller_class->handle_focus_change = gtk_event_controller_key_handle_focus_change;
 
   /**
    * GtkEventControllerKey:is-focus:
@@ -381,47 +350,15 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
                   NULL,
                   G_TYPE_NONE, 0);
 
-  /**
-   * GtkEventControllerKey::focus-in:
-   * @controller: the object which received the signal.
-   * @mode: crossing mode indicating what caused this change
-   * @detail: detail indication where the focus is coming from
-   *
-   * This signal is emitted whenever the widget controlled
-   * by the @controller or one of its descendants) is given
-   * the keyboard focus.
-   */
-  signals[FOCUS_IN] =
-    g_signal_new (I_("focus-in"),
-                  GTK_TYPE_EVENT_CONTROLLER_KEY,
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL,
-                  NULL,
-                  G_TYPE_NONE,
-                  2,
-                  GDK_TYPE_CROSSING_MODE,
-                  GDK_TYPE_NOTIFY_TYPE);
-
-  /**
-   * GtkEventControllerKey::focus-out:
-   * @controller: the object which received the signal.
-   * @mode: crossing mode indicating what caused this change
-   * @detail: detail indication where the focus is going
-   *
-   * This signal is emitted whenever the widget controlled
-   * by the @controller (or one of its descendants) loses
-   * the keyboard focus.
-   */
-  signals[FOCUS_OUT] =
-    g_signal_new (I_("focus-out"),
+  signals[FOCUS_CHANGE] =
+    g_signal_new (I_("focus-change"),
                   GTK_TYPE_EVENT_CONTROLLER_KEY,
                   G_SIGNAL_RUN_LAST,
                   0, NULL, NULL,
                   NULL,
                   G_TYPE_NONE,
-                  2,
-                  GDK_TYPE_CROSSING_MODE,
-                  GDK_TYPE_NOTIFY_TYPE);
+                  1,
+                  GTK_TYPE_CROSSING_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 }
 
 static void
diff --git a/gtk/gtkeventcontrollerprivate.h b/gtk/gtkeventcontrollerprivate.h
index 354956f841..94fdc1141e 100644
--- a/gtk/gtkeventcontrollerprivate.h
+++ b/gtk/gtkeventcontrollerprivate.h
@@ -40,6 +40,9 @@ struct _GtkEventControllerClass
                              double              y);
   void     (* reset)        (GtkEventController *controller);
 
+  void     (* handle_focus_change) (GtkEventController     *controller,
+                                    const GtkCrossingEvent *event);
+
   /*<private>*/
 
   /* Tells whether the event is filtered out, %TRUE makes
diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c
index 2c8a73f0c8..30f9e28721 100644
--- a/gtk/gtkfilechooserentry.c
+++ b/gtk/gtkfilechooserentry.c
@@ -259,12 +259,12 @@ match_func (GtkEntryCompletion *compl,
 }
 
 static void
-chooser_entry_focus_out (GtkEventControllerKey *key_controller,
-                         GdkCrossingMode        mode,
-                         GdkNotifyType          detail,
-                         GtkFileChooserEntry   *chooser_entry)
+chooser_entry_focus_change (GtkEventController     *controller,
+                            const GtkCrossingEvent *event,
+                            GtkFileChooserEntry    *chooser_entry)
 {
-  set_complete_on_load (chooser_entry, FALSE);
+  if (!event->in)
+    set_complete_on_load (chooser_entry, FALSE);
 }
 
 static void
@@ -312,7 +312,7 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
                     G_CALLBACK (gtk_file_chooser_entry_tab_handler),
                     chooser_entry);
   g_signal_connect (controller,
-                   "focus-out", G_CALLBACK (chooser_entry_focus_out),
+                   "focus-change", G_CALLBACK (chooser_entry_focus_change),
                    chooser_entry);
   gtk_widget_add_controller (GTK_WIDGET (chooser_entry), controller);
 
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 08fd1b407c..8fb03f784a 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1323,8 +1323,6 @@ synth_crossing (GtkWidget       *widget,
       event->focus_change.mode = crossing_mode;
       event->focus_change.detail = notify_type;
 
-g_print ("%s: %s\n", G_OBJECT_TYPE_NAME (widget), enter ? "focus-in" : "focus-out");
-
       flags = GTK_STATE_FLAG_FOCUSED;
       if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel)))
         flags |= GTK_STATE_FLAG_FOCUS_VISIBLE;
diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c
index aea36907dd..45f2dbb43c 100644
--- a/gtk/gtkmodelbutton.c
+++ b/gtk/gtkmodelbutton.c
@@ -1352,19 +1352,21 @@ leave_cb (GtkEventController *controller,
 }
 
 static void
-focus_in_cb (GtkEventController *controller,
-             GdkCrossingMode     mode,
-             GdkNotifyType       type,
-             gpointer            data)
+focus_change_cb (GtkEventController *controller,
+                 const GtkCrossingEvent *event,
+                 gpointer            data)
 {
   GtkWidget *target;
   GtkWidget *popover;
 
-  target = gtk_event_controller_get_widget (controller);
-  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+  if (event->in)
+    {
+      target = gtk_event_controller_get_widget (controller);
+      popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
 
-  if (popover)
-    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
+      if (popover)
+        gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
+    }
 }
 
 static void
@@ -1393,7 +1395,7 @@ gtk_model_button_init (GtkModelButton *self)
   gtk_widget_add_controller (GTK_WIDGET (self), controller);
 
   controller = gtk_event_controller_key_new ();
-  g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in_cb), NULL);
+  g_signal_connect (controller, "focus-change", G_CALLBACK (focus_change_cb), NULL);
   gtk_widget_add_controller (GTK_WIDGET (self), controller);
 
   gesture = gtk_gesture_click_new ();
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index af1fee728b..7c8c83043c 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -383,17 +383,6 @@ gtk_popover_native_check_resize (GtkNative *native)
     }
 }
 
-
-static void
-gtk_popover_focus_in (GtkWidget *widget)
-{
-}
-
-static void
-gtk_popover_focus_out (GtkWidget *widget)
-{
-}
-
 static void
 close_menu (GtkPopover *popover)
 {
@@ -569,8 +558,6 @@ gtk_popover_init (GtkPopover *popover)
   priv->has_arrow = TRUE;
 
   controller = gtk_event_controller_key_new ();
-  g_signal_connect_swapped (controller, "focus-in", G_CALLBACK (gtk_popover_focus_in), popover);
-  g_signal_connect_swapped (controller, "focus-out", G_CALLBACK (gtk_popover_focus_out), popover);
   g_signal_connect_swapped (controller, "key-pressed", G_CALLBACK (gtk_popover_key_pressed), popover);
   gtk_widget_add_controller (GTK_WIDGET (popover), controller);
 
diff --git a/gtk/gtkpopovermenu.c b/gtk/gtkpopovermenu.c
index e310033f12..005c27185d 100644
--- a/gtk/gtkpopovermenu.c
+++ b/gtk/gtkpopovermenu.c
@@ -168,14 +168,14 @@ visible_submenu_changed (GObject        *object,
 }
 
 static void
-focus_out (GtkEventControllerKey *controller,
-           GdkCrossingMode        mode,
-           GdkNotifyType          detail,
-           GtkPopoverMenu        *menu)
+focus_change (GtkEventController     *controller,
+              const GtkCrossingEvent *event,
+              GtkPopoverMenu         *menu)
 {
   GtkWidget *new_focus = gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (menu)));
 
-  if (!gtk_event_controller_key_contains_focus (controller) &&
+  if (!event->in &&
+      !gtk_event_controller_key_contains_focus (GTK_EVENT_CONTROLLER_KEY (controller)) &&
       new_focus != NULL)
     {
       if (menu->parent_menu &&
@@ -216,7 +216,7 @@ gtk_popover_menu_init (GtkPopoverMenu *popover)
   gtk_widget_add_css_class (GTK_WIDGET (popover), "menu");
 
   controller = gtk_event_controller_key_new ();
-  g_signal_connect (controller, "focus-out", G_CALLBACK (focus_out), popover);
+  g_signal_connect (controller, "focus-change", G_CALLBACK (focus_change), popover);
   gtk_widget_add_controller (GTK_WIDGET (popover), controller);
 
   controller = gtk_event_controller_motion_new ();
diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c
index e199433db9..0aa3ef8a19 100644
--- a/gtk/gtkspinbutton.c
+++ b/gtk/gtkspinbutton.c
@@ -920,14 +920,14 @@ key_controller_key_released (GtkEventControllerKey *key,
 }
 
 static void
-key_controller_focus_out (GtkEventControllerKey *key,
-                          GdkCrossingMode        mode,
-                          GdkNotifyType          detail,
-                          GtkSpinButton         *spin_button)
+key_controller_focus_change (GtkEventController     *controller,
+                             const GtkCrossingEvent *event,  
+                             GtkSpinButton          *spin_button)
 {
   GtkSpinButtonPrivate *priv = gtk_spin_button_get_instance_private (spin_button);
 
-  if (gtk_editable_get_editable (GTK_EDITABLE (priv->entry)))
+  if (!event->in &&
+      gtk_editable_get_editable (GTK_EDITABLE (priv->entry)))
     gtk_spin_button_update (spin_button);
 }
 
@@ -1015,8 +1015,8 @@ gtk_spin_button_init (GtkSpinButton *spin_button)
   controller = gtk_event_controller_key_new ();
   g_signal_connect (controller, "key-released",
                     G_CALLBACK (key_controller_key_released), spin_button);
-  g_signal_connect (controller, "focus-out",
-                    G_CALLBACK (key_controller_focus_out), spin_button);
+  g_signal_connect (controller, "focus-change",
+                    G_CALLBACK (key_controller_focus_change), spin_button);
   gtk_widget_add_controller (GTK_WIDGET (spin_button), controller);
 }
 
diff --git a/gtk/gtktext.c b/gtk/gtktext.c
index b671c9a39f..b8e82e0d95 100644
--- a/gtk/gtktext.c
+++ b/gtk/gtktext.c
@@ -323,8 +323,8 @@ static void   gtk_text_size_allocate        (GtkWidget        *widget,
                                              int               baseline);
 static void   gtk_text_snapshot             (GtkWidget        *widget,
                                              GtkSnapshot      *snapshot);
-static void   gtk_text_focus_in             (GtkWidget        *widget);
-static void   gtk_text_focus_out            (GtkWidget        *widget);
+static void   gtk_text_focus_change         (GtkWidget              *widget,
+                                             const GtkCrossingEvent *event);
 static gboolean gtk_text_grab_focus         (GtkWidget        *widget);
 static void   gtk_text_css_changed          (GtkWidget        *widget,
                                              GtkCssStyleChange *change);
@@ -1783,10 +1783,8 @@ gtk_text_init (GtkText *self)
                     G_CALLBACK (gtk_text_key_controller_key_pressed), self);
   g_signal_connect_swapped (priv->key_controller, "im-update",
                             G_CALLBACK (gtk_text_schedule_im_reset), self);
-  g_signal_connect_swapped (priv->key_controller, "focus-in",
-                            G_CALLBACK (gtk_text_focus_in), self);
-  g_signal_connect_swapped (priv->key_controller, "focus-out",
-                            G_CALLBACK (gtk_text_focus_out), self);
+  g_signal_connect_swapped (priv->key_controller, "focus-change",
+                            G_CALLBACK (gtk_text_focus_change), self);
   gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller),
                                            priv->im_context);
   gtk_widget_add_controller (GTK_WIDGET (self), priv->key_controller);
@@ -3054,55 +3052,53 @@ gtk_text_key_controller_key_pressed (GtkEventControllerKey *controller,
 }
 
 static void
-gtk_text_focus_in (GtkWidget *widget)
+gtk_text_focus_change (GtkWidget              *widget,
+                       const GtkCrossingEvent *event)
 {
   GtkText *self = GTK_TEXT (widget);
   GtkTextPrivate *priv = gtk_text_get_instance_private (self);
   GdkKeymap *keymap;
 
-  gtk_widget_queue_draw (widget);
-
-  keymap = gdk_display_get_keymap (gtk_widget_get_display (widget));
-
-  if (priv->editable)
+  if (event->in)
     {
-      gtk_text_schedule_im_reset (self);
-      gtk_im_context_focus_in (priv->im_context);
-    }
+      gtk_widget_queue_draw (widget);
 
-  g_signal_connect (keymap, "direction-changed",
-                    G_CALLBACK (keymap_direction_changed), self);
+      keymap = gdk_display_get_keymap (gtk_widget_get_display (widget));
 
-  gtk_text_reset_blink_time (self);
-  gtk_text_check_cursor_blink (self);
-}
+      if (priv->editable)
+        {
+          gtk_text_schedule_im_reset (self);
+          gtk_im_context_focus_in (priv->im_context);
+        }
 
-static void
-gtk_text_focus_out (GtkWidget *widget)
-{
-  GtkText *self = GTK_TEXT (widget);
-  GtkTextPrivate *priv = gtk_text_get_instance_private (self);
-  GdkKeymap *keymap;
+      g_signal_connect (keymap, "direction-changed",
+                        G_CALLBACK (keymap_direction_changed), self);
 
-  gtk_text_selection_bubble_popup_unset (self);
+      gtk_text_reset_blink_time (self);
+      gtk_text_check_cursor_blink (self);
+    }
+  else
+    {
+      gtk_text_selection_bubble_popup_unset (self);
 
-  if (priv->text_handle)
-    _gtk_text_handle_set_mode (priv->text_handle,
-                               GTK_TEXT_HANDLE_MODE_NONE);
+      if (priv->text_handle)
+        _gtk_text_handle_set_mode (priv->text_handle,
+                                   GTK_TEXT_HANDLE_MODE_NONE);
 
-  gtk_widget_queue_draw (widget);
+      gtk_widget_queue_draw (widget);
 
-  keymap = gdk_display_get_keymap (gtk_widget_get_display (widget));
+      keymap = gdk_display_get_keymap (gtk_widget_get_display (widget));
 
-  if (priv->editable)
-    {
-      gtk_text_schedule_im_reset (self);
-      gtk_im_context_focus_out (priv->im_context);
-    }
+      if (priv->editable)
+        {
+          gtk_text_schedule_im_reset (self);
+          gtk_im_context_focus_out (priv->im_context);
+        }
 
-  gtk_text_check_cursor_blink (self);
+      gtk_text_check_cursor_blink (self);
 
-  g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self);
+      g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self);
+    }
 }
 
 static gboolean
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index ac190dc8b9..2e603bd462 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -404,8 +404,8 @@ static gboolean gtk_text_view_key_controller_key_pressed  (GtkEventControllerKey
 static void     gtk_text_view_key_controller_im_update    (GtkEventControllerKey *controller,
                                                            GtkTextView           *text_view);
 
-static void gtk_text_view_focus_in             (GtkWidget        *widget);
-static void gtk_text_view_focus_out            (GtkWidget        *widget);
+static void gtk_text_view_focus_change         (GtkWidget        *widget,
+                                                const GtkCrossingEvent *event);
 static void gtk_text_view_motion               (GtkEventController *controller,
                                                 double              x,
                                                 double              y,
@@ -1698,11 +1698,8 @@ gtk_text_view_init (GtkTextView *text_view)
   g_signal_connect (priv->key_controller, "im-update",
                     G_CALLBACK (gtk_text_view_key_controller_im_update),
                     widget);
-  g_signal_connect_swapped (priv->key_controller, "focus-in",
-                            G_CALLBACK (gtk_text_view_focus_in),
-                            widget);
-  g_signal_connect_swapped (priv->key_controller, "focus-out",
-                            G_CALLBACK (gtk_text_view_focus_out),
+  g_signal_connect_swapped (priv->key_controller, "focus-change",
+                            G_CALLBACK (gtk_text_view_focus_change),
                             widget);
   gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller),
                                            priv->im_context);
@@ -5321,72 +5318,65 @@ keymap_direction_changed (GdkKeymap   *keymap,
 }
 
 static void
-gtk_text_view_focus_in (GtkWidget *widget)
+gtk_text_view_focus_change (GtkWidget              *widget,
+                            const GtkCrossingEvent *event)
 {
-  GtkTextView *text_view;
-  GtkTextViewPrivate *priv;
-
-  text_view = GTK_TEXT_VIEW (widget);
-  priv = text_view->priv;
-
-  gtk_widget_queue_draw (widget);
-
-  DV(g_print (G_STRLOC": focus_in\n"));
-
-  gtk_text_view_reset_blink_time (text_view);
+  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+  GtkTextViewPrivate *priv = text_view->priv;
 
-  if (cursor_visible (text_view) && priv->layout)
+  if (event->in)
     {
-      gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
-      gtk_text_view_check_cursor_blink (text_view);
-    }
+      gtk_widget_queue_draw (widget);
 
-  g_signal_connect (gdk_display_get_keymap (gtk_widget_get_display (widget)),
-                   "direction-changed",
-                   G_CALLBACK (keymap_direction_changed), text_view);
-  gtk_text_view_check_keymap_direction (text_view);
+      DV(g_print (G_STRLOC": focus_in\n"));
 
-  if (priv->editable)
-    {
-      priv->need_im_reset = TRUE;
-      gtk_im_context_focus_in (priv->im_context);
-    }
-}
+      gtk_text_view_reset_blink_time (text_view);
 
-static void
-gtk_text_view_focus_out (GtkWidget *widget)
-{
-  GtkTextView *text_view;
-  GtkTextViewPrivate *priv;
+      if (cursor_visible (text_view) && priv->layout)
+        {
+          gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
+          gtk_text_view_check_cursor_blink (text_view);
+        }
 
-  text_view = GTK_TEXT_VIEW (widget);
-  priv = text_view->priv;
+      g_signal_connect (gdk_display_get_keymap (gtk_widget_get_display (widget)),
+                        "direction-changed",
+                        G_CALLBACK (keymap_direction_changed), text_view);
+      gtk_text_view_check_keymap_direction (text_view);
 
-  gtk_text_view_end_selection_drag (text_view);
+      if (priv->editable)
+        {
+          priv->need_im_reset = TRUE;
+          gtk_im_context_focus_in (priv->im_context);
+        }
+    }
+  else
+    { 
+      gtk_text_view_end_selection_drag (text_view);
 
-  gtk_widget_queue_draw (widget);
+      gtk_widget_queue_draw (widget);
 
-  DV(g_print (G_STRLOC": focus_out\n"));
+      DV(g_print (G_STRLOC": focus_out\n"));
 
-  if (cursor_visible (text_view) && priv->layout)
-    {
-      gtk_text_view_check_cursor_blink (text_view);
-      gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
-    }
+      if (cursor_visible (text_view) && priv->layout)
+        {
+          gtk_text_view_check_cursor_blink (text_view);
+          gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
+        }
 
-  g_signal_handlers_disconnect_by_func (gdk_display_get_keymap (gtk_widget_get_display (widget)),
-                                       keymap_direction_changed,
-                                       text_view);
-  gtk_text_view_selection_bubble_popup_unset (text_view);
+      g_signal_handlers_disconnect_by_func (gdk_display_get_keymap (gtk_widget_get_display (widget)),
+                                            keymap_direction_changed,
+                                            text_view);
+      gtk_text_view_selection_bubble_popup_unset (text_view);
 
-  if (priv->text_handle)
-    _gtk_text_handle_set_mode (priv->text_handle,
-                               GTK_TEXT_HANDLE_MODE_NONE);
+      if (priv->text_handle)
+        _gtk_text_handle_set_mode (priv->text_handle,
+                                   GTK_TEXT_HANDLE_MODE_NONE);
 
-  if (priv->editable)
-    {
-      priv->need_im_reset = TRUE;
-      gtk_im_context_focus_out (priv->im_context);
+      if (priv->editable)
+        {
+          priv->need_im_reset = TRUE;
+          gtk_im_context_focus_out (priv->im_context);
+        }
     }
 }
 
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 27b0576477..c6ed7840f5 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -667,10 +667,9 @@ static void     gtk_tree_view_key_controller_key_released (GtkEventControllerKey
                                                            guint                  keycode,
                                                            GdkModifierType        state,
                                                            GtkTreeView           *tree_view);
-static void     gtk_tree_view_key_controller_focus_out    (GtkEventControllerKey *key,
-                                                           GdkCrossingMode        mode,
-                                                           GdkNotifyType          detail,
-                                                           GtkTreeView           *tree_view);
+static void     gtk_tree_view_key_controller_focus_change (GtkEventController     *key,
+                                                           const GtkCrossingEvent *event,
+                                                           GtkTreeView            *tree_view);
 
 static gint     gtk_tree_view_focus                (GtkWidget        *widget,
                                                    GtkDirectionType  direction);
@@ -1839,8 +1838,8 @@ gtk_tree_view_init (GtkTreeView *tree_view)
                     G_CALLBACK (gtk_tree_view_key_controller_key_pressed), tree_view);
   g_signal_connect (controller, "key-released",
                     G_CALLBACK (gtk_tree_view_key_controller_key_released), tree_view);
-  g_signal_connect (controller, "focus-out",
-                    G_CALLBACK (gtk_tree_view_key_controller_focus_out), tree_view);
+  g_signal_connect (controller, "focus-change",
+                    G_CALLBACK (gtk_tree_view_key_controller_focus_change), tree_view);
   gtk_widget_add_controller (GTK_WIDGET (tree_view), controller);
 }
 
@@ -5546,23 +5545,19 @@ gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller,
 }
 
 static void
-gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key,
-                                        GdkCrossingMode        mode,
-                                        GdkNotifyType          detail,
-                                        GtkTreeView           *tree_view)
+gtk_tree_view_key_controller_focus_change (GtkEventController     *key,
+                                           const GtkCrossingEvent *event,
+                                           GtkTreeView            *tree_view)
 {
-  gboolean is_focus, contains_focus;
-
-  gtk_widget_queue_draw (GTK_WIDGET (tree_view));
-
-  g_object_get (key,
-                "is-focus", &is_focus,
-                "contains-focus", &contains_focus,
-                NULL);
+  if (!event->in)
+    {
+      gtk_widget_queue_draw (GTK_WIDGET (tree_view));
 
-  if (tree_view->search_popover && !gtk_event_controller_key_contains_focus (key))
-    gtk_tree_view_search_popover_hide (tree_view->search_popover, tree_view,
-                                       gtk_get_current_event_device ());
+      if (tree_view->search_popover &&
+          !gtk_event_controller_key_contains_focus (GTK_EVENT_CONTROLLER_KEY (key)))
+        gtk_tree_view_search_popover_hide (tree_view->search_popover, tree_view,
+                                           gtk_get_current_event_device ());
+    }
 }
 
 /* Incremental Reflow
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index aa39d9b9ba..a095dc058d 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -830,12 +830,12 @@ gtk_tree_view_column_cell_layout_get_area (GtkCellLayout   *cell_layout)
 }
 
 static void
-focus_in (GtkEventControllerKey *controller,
-          GdkCrossingMode        mode,
-          GdkNotifyType          detail,
-          GtkTreeViewColumn     *column)
+focus_in (GtkEventControllerKey  *controller,
+          const GtkCrossingEvent *event,
+          GtkTreeViewColumn      *column)
 {
-  _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column);
+  if (event->in)
+    _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column);
 }
 
 /* Button handling code
@@ -866,7 +866,7 @@ gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column)
   gtk_widget_add_controller (priv->button, controller);
 
   controller = gtk_event_controller_key_new ();
-  g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in), tree_column);
+  g_signal_connect (controller, "focus-change", G_CALLBACK (focus_in), tree_column);
   gtk_widget_add_controller (priv->button, controller);
 
   priv->frame = gtk_frame_new (NULL);
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 5bb040124c..6aa1c78ba3 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4818,6 +4818,25 @@ gtk_widget_run_controllers (GtkWidget           *widget,
   return handled;
 }
 
+void
+gtk_widget_handle_focus_change (GtkWidget              *widget,
+                                const GtkCrossingEvent *event)
+{
+  GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+  GList *l;
+
+  g_object_ref (widget);
+
+  for (l = priv->event_controllers; l; l = l->next)
+    {
+      GtkEventController *controller = l->data;
+
+      gtk_event_controller_handle_focus_change (controller, event);
+    }
+
+  g_object_unref (widget);
+}
+
 static gboolean
 translate_event_coordinates (GdkEvent  *event,
                              double    *x,
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 87d0eba9c0..7e953f71fd 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -347,6 +347,8 @@ gboolean          gtk_widget_run_controllers               (GtkWidget
                                                             double               x,
                                                             double               y,
                                                             GtkPropagationPhase  phase);
+void              gtk_widget_handle_focus_change           (GtkWidget              *widget,
+                                                            const GtkCrossingEvent *event);
 
 
 guint             gtk_widget_add_surface_transform_changed_callback (GtkWidget                          
*widget,
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index efcfa998cb..5a6faf5942 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -398,8 +398,8 @@ static void gtk_window_size_allocate      (GtkWidget         *widget,
                                            int                height,
                                            int                  baseline);
 static gboolean gtk_window_close_request  (GtkWindow         *window);
-static void gtk_window_focus_in           (GtkWidget         *widget);
-static void gtk_window_focus_out          (GtkWidget         *widget);
+static void gtk_window_focus_change       (GtkWidget              *widget,
+                                           const GtkCrossingEvent *event);
 static gboolean gtk_window_key_pressed    (GtkWidget         *widget,
                                            guint              keyval,
                                            guint              keycode,
@@ -1860,10 +1860,8 @@ gtk_window_init (GtkWindow *window)
 
   priv->key_controller = gtk_event_controller_key_new ();
   gtk_event_controller_set_propagation_phase (priv->key_controller, GTK_PHASE_CAPTURE);
-  g_signal_connect_swapped (priv->key_controller, "focus-in",
-                            G_CALLBACK (gtk_window_focus_in), window);
-  g_signal_connect_swapped (priv->key_controller, "focus-out",
-                            G_CALLBACK (gtk_window_focus_out), window);
+  g_signal_connect_swapped (priv->key_controller, "focus-change",
+                            G_CALLBACK (gtk_window_focus_change), window);
   g_signal_connect_swapped (priv->key_controller, "key-pressed",
                             G_CALLBACK (gtk_window_key_pressed), window);
   g_signal_connect_swapped (priv->key_controller, "key-released",
@@ -6197,33 +6195,33 @@ gtk_window_has_mnemonic_modifier_pressed (GtkWindow *window)
 }
 
 static void
-gtk_window_focus_in (GtkWidget *widget)
+gtk_window_focus_change (GtkWidget              *widget,
+                         const GtkCrossingEvent *event)
 {
   GtkWindow *window = GTK_WINDOW (widget);
 
-  /* It appears spurious focus in events can occur when
-   *  the window is hidden. So we'll just check to see if
-   *  the window is visible before actually handling the
-   *  event
-   */
-  if (gtk_widget_get_visible (widget))
+  if (event->in)
     {
-      _gtk_window_set_is_active (window, TRUE);
+      /* It appears spurious focus in events can occur when
+       * the window is hidden. So we'll just check to see if
+       * the window is visible before actually handling the
+       * event
+       */
+      if (gtk_widget_get_visible (widget))
+        {
+          _gtk_window_set_is_active (window, TRUE);
 
-      if (gtk_window_has_mnemonic_modifier_pressed (window))
-        _gtk_window_schedule_mnemonics_visible (window);
+          if (gtk_window_has_mnemonic_modifier_pressed (window))
+            _gtk_window_schedule_mnemonics_visible (window);
+        }
     }
-}
-
-static void
-gtk_window_focus_out (GtkWidget *widget)
-{
-  GtkWindow *window = GTK_WINDOW (widget);
-
-  _gtk_window_set_is_active (window, FALSE);
+  else
+    {
+      _gtk_window_set_is_active (window, FALSE);
 
-  /* set the mnemonic-visible property to false */
-  gtk_window_set_mnemonics_visible (window, FALSE);
+      /* set the mnemonic-visible property to false */
+      gtk_window_set_mnemonics_visible (window, FALSE);
+    }
 }
 
 static void
@@ -6423,6 +6421,59 @@ gtk_window_move_focus (GtkWidget        *widget,
     gtk_window_set_focus (GTK_WINDOW (widget), NULL);
 }
 
+static void
+synthesize_focus_change_events (GtkWindow *window,
+                                GtkWidget *old_focus,
+                                GtkWidget *new_focus)
+{
+  GtkCrossingEvent event;
+  GtkWidget *widget, *focus_child;
+  GList *list, *l;
+  GtkStateFlags flags;
+
+  flags = GTK_STATE_FLAG_FOCUSED;
+  if (gtk_window_get_focus_visible (GTK_WINDOW (window)))
+    flags |= GTK_STATE_FLAG_FOCUS_VISIBLE;
+
+  event.old_location = old_focus;
+  event.new_location = new_focus;
+
+  event.in = FALSE;
+
+  widget = old_focus;
+  while (widget)
+    {
+      gtk_widget_handle_focus_change (widget, &event);
+      gtk_widget_unset_state_flags (widget, flags);
+      gtk_widget_set_focus_child (widget, NULL);
+      widget = gtk_widget_get_parent (widget);
+    }
+
+  list = NULL;
+  widget = new_focus;
+  while (widget)
+    {
+      list = g_list_prepend (list, widget);
+      widget = gtk_widget_get_parent (widget);
+    }
+
+  event.in = TRUE;
+
+  for (l = list; l; l = l->next)
+    {
+      widget = l->data;
+      if (l->next)
+        focus_child = l->next->data;
+      else
+        focus_child = NULL;
+      gtk_widget_handle_focus_change (widget, &event);
+      gtk_widget_set_state_flags (widget, flags, FALSE);
+      gtk_widget_set_focus_child (widget, focus_child);
+    }
+
+  g_list_free (list);
+}
+
 /**
  * gtk_window_set_focus:
  * @window: a #GtkWindow
@@ -6441,9 +6492,6 @@ gtk_window_set_focus (GtkWindow *window,
 {
   GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
   GtkWidget *old_focus = NULL;
-  GdkSeat *seat;
-  GdkDevice *device;
-  GdkEvent *event;
 
   g_return_if_fail (GTK_IS_WINDOW (window));
 
@@ -6457,23 +6505,7 @@ gtk_window_set_focus (GtkWindow *window,
     old_focus = g_object_ref (priv->focus_widget);
   g_set_object (&priv->focus_widget, NULL);
 
-  seat = gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (window)));
-  device = gdk_seat_get_keyboard (seat);
-
-  event = gdk_event_new (GDK_FOCUS_CHANGE);
-  gdk_event_set_display (event, gtk_widget_get_display (GTK_WIDGET (window)));
-  gdk_event_set_device (event, device);
-  event->any.surface = priv->surface;
-  if (event->any.surface)
-    g_object_ref (event->any.surface);
-
-g_print ("window: focus %s -> %s\n",
-         old_focus ? G_OBJECT_TYPE_NAME (old_focus) : "",
-         focus ? G_OBJECT_TYPE_NAME (focus) : "");
- 
-  gtk_synthesize_crossing_events (GTK_ROOT (window), old_focus, focus, event, GDK_CROSSING_NORMAL);
-
-  g_object_unref (event);
+  synthesize_focus_change_events (window, old_focus, focus);
 
   g_set_object (&priv->focus_widget, focus);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]