[gtk/wip/readonly-events: 5/5] New focus change handling
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/readonly-events: 5/5] New focus change handling
- Date: Thu, 13 Feb 2020 19:45:30 +0000 (UTC)
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]