diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-03-17 02:42:46 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-03-17 02:42:46 +0000 |
commit | fc2b412c0c6428a3ae971e59c7950bccf7a6e0dc (patch) | |
tree | 3711e56c82c5ec8acbfc65b9e5467384cb2ad2f0 | |
parent | 64b9114d98cf9bf985e93b71e0f4ed8f67547be7 (diff) | |
parent | 1ce5327058071f4c29261898037e546e1956e90a (diff) | |
download | gtk+-fc2b412c0c6428a3ae971e59c7950bccf7a6e0dc.tar.gz |
Merge branch 'wip/matthiasc/focus3' into 'master'
Move focus to GtkRoot
See merge request GNOME/gtk!640
67 files changed, 901 insertions, 834 deletions
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 9d3e68f683..e0b068bcdc 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -6702,6 +6702,12 @@ gtk_event_controller_motion_get_type <TITLE>GtkEventControllerKey</TITLE> GtkEventControllerKey gtk_event_controller_key_new +gtk_event_controller_key_set_im_context +gtk_event_controller_key_get_im_context +gtk_event_controller_key_forward +gtk_event_controller_key_get_group +gtk_event_controller_key_get_focus_origin +gtk_event_controller_key_get_focus_target <SUBSECTION Standard> GTK_TYPE_EVENT_CONTROLLER_KEY diff --git a/gdk/gdk-private.h b/gdk/gdk-private.h index 9344ac2e49..b5551b750c 100644 --- a/gdk/gdk-private.h +++ b/gdk/gdk-private.h @@ -20,8 +20,6 @@ void gdk_surface_thaw_toplevel_updates (GdkSurface *surface); gboolean gdk_surface_supports_edge_constraints (GdkSurface *surface); -GObject * gdk_event_get_user_data (const GdkEvent *event); - guint32 gdk_display_get_last_seen_time (GdkDisplay *display); void gdk_display_set_double_click_time (GdkDisplay *display, diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 3b0944d3cc..d9e9af6eef 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -564,8 +564,8 @@ gdk_event_copy (const GdkEvent *event) g_object_ref (new_event->any.device); if (new_event->any.source_device) g_object_ref (new_event->any.source_device); - if (new_event->any.user_data) - g_object_ref (new_event->any.user_data); + if (new_event->any.target) + g_object_ref (new_event->any.target); switch ((guint) event->any.type) { @@ -573,6 +573,13 @@ gdk_event_copy (const GdkEvent *event) case GDK_LEAVE_NOTIFY: if (event->crossing.child_surface != NULL) g_object_ref (event->crossing.child_surface); + if (event->crossing.related_target) + g_object_ref (event->crossing.related_target); + break; + + case GDK_FOCUS_CHANGE: + if (event->focus_change.related_target) + g_object_ref (event->focus_change.related_target); break; case GDK_DRAG_ENTER: @@ -634,6 +641,11 @@ gdk_event_finalize (GObject *object) case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: g_clear_object (&event->crossing.child_surface); + g_clear_object (&event->crossing.related_target); + break; + + case GDK_FOCUS_CHANGE: + g_clear_object (&event->focus_change.related_target); break; case GDK_DRAG_ENTER: @@ -675,7 +687,7 @@ gdk_event_finalize (GObject *object) g_clear_object (&event->any.device); g_clear_object (&event->any.source_device); - g_clear_object (&event->any.user_data); + g_clear_object (&event->any.target); G_OBJECT_CLASS (gdk_event_parent_class)->finalize (object); } @@ -1904,16 +1916,39 @@ gdk_event_get_scancode (GdkEvent *event) } void -gdk_event_set_user_data (GdkEvent *event, - GObject *user_data) +gdk_event_set_target (GdkEvent *event, + GObject *target) +{ + g_set_object (&event->any.target, target); +} + +GObject * +gdk_event_get_target (const GdkEvent *event) +{ + return event->any.target; +} + +void +gdk_event_set_related_target (GdkEvent *event, + GObject *target) { - g_set_object (&event->any.user_data, user_data); + if (event->any.type == GDK_ENTER_NOTIFY || + event->any.type == GDK_LEAVE_NOTIFY) + g_set_object (&event->crossing.related_target, target); + else if (event->any.type == GDK_FOCUS_CHANGE) + g_set_object (&event->focus_change.related_target, target); } GObject * -gdk_event_get_user_data (const GdkEvent *event) +gdk_event_get_related_target (const GdkEvent *event) { - return event->any.user_data; + if (event->any.type == GDK_ENTER_NOTIFY || + event->any.type == GDK_LEAVE_NOTIFY) + return event->crossing.related_target; + else if (event->any.type == GDK_FOCUS_CHANGE) + return event->focus_change.related_target; + + return NULL; } /** @@ -1980,6 +2015,11 @@ gdk_event_get_crossing_mode (const GdkEvent *event, *mode = event->crossing.mode; return TRUE; } + else if (event->any.type == GDK_FOCUS_CHANGE) + { + *mode = event->focus_change.mode; + return TRUE; + } return FALSE; } @@ -2006,6 +2046,11 @@ gdk_event_get_crossing_detail (const GdkEvent *event, *detail = event->crossing.detail; return TRUE; } + else if (event->any.type == GDK_FOCUS_CHANGE) + { + *detail = event->focus_change.detail; + return TRUE; + } return FALSE; } diff --git a/gdk/gdkeventsprivate.h b/gdk/gdkeventsprivate.h index b81faba589..2db43d9a82 100644 --- a/gdk/gdkeventsprivate.h +++ b/gdk/gdkeventsprivate.h @@ -61,7 +61,7 @@ struct _GdkEventAny GdkDevice *device; GdkDevice *source_device; GdkDisplay *display; - GObject *user_data; + GObject *target; }; /* @@ -303,6 +303,7 @@ struct _GdkEventCrossing GdkNotifyType detail; gboolean focus; guint state; + GObject *related_target; }; /* @@ -312,6 +313,8 @@ struct _GdkEventCrossing * @send_event: %TRUE if the event was sent explicitly. * @in: %TRUE if the surface has gained the keyboard focus, %FALSE if * it has lost the focus. + * @mode: the crossing mode + * @detail: the kind of crossing that happened * * Describes a change of keyboard focus. */ @@ -319,6 +322,9 @@ struct _GdkEventFocus { GdkEventAny any; gint16 in; + GdkCrossingMode mode; + GdkNotifyType detail; + GObject *related_target; }; /* @@ -632,8 +638,12 @@ union _GdkEvent GdkEventPadGroupMode pad_group_mode; }; -void gdk_event_set_user_data (GdkEvent *event, - GObject *user_data); +void gdk_event_set_target (GdkEvent *event, + GObject *user_data); +GObject * gdk_event_get_target (const GdkEvent *event); +void gdk_event_set_related_target (GdkEvent *event, + GObject *user_data); +GObject * gdk_event_get_related_target (const GdkEvent *event); #endif /* __GDK_EVENTS_PRIVATE_H__ */ diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 6b941ced3e..32df686852 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -295,6 +295,8 @@ static gboolean gtk_calendar_key_controller_key_pressed (GtkEventControllerKey * GdkModifierType state, GtkWidget *widget); static void gtk_calendar_key_controller_focus (GtkEventControllerKey *controller, + GdkCrossingMode mode, + GdkNotifyType detail, GtkWidget *widget); static void gtk_calendar_grab_notify (GtkWidget *widget, gboolean was_grabbed); @@ -2854,6 +2856,8 @@ gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller, static void gtk_calendar_key_controller_focus (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkWidget *widget) { GtkCalendar *calendar = GTK_CALENDAR (widget); diff --git a/gtk/gtkcoloreditor.c b/gtk/gtkcoloreditor.c index 34f5991ca2..8670027631 100644 --- a/gtk/gtkcoloreditor.c +++ b/gtk/gtkcoloreditor.c @@ -36,6 +36,7 @@ #include "gtkspinbutton.h" #include "gtkstylecontext.h" #include "gtkeventcontrollerkey.h" +#include "gtkroot.h" #include <math.h> @@ -224,7 +225,7 @@ popup_edit (GtkWidget *widget, { dismiss_current_popup (editor); toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editor)); - g_set_object (&editor->priv->popdown_focus, gtk_window_get_focus (GTK_WINDOW (toplevel))); + g_set_object (&editor->priv->popdown_focus, gtk_root_get_focus (GTK_ROOT (toplevel))); editor->priv->current_popup = popup; editor->priv->popup_position = position; gtk_widget_show (popup); diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index f1b28d999b..f7f3fe1017 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -36,6 +36,7 @@ #include "gtkeventcontrollerkey.h" #include "gtkbindings.h" #include "gtkenums.h" +#include "gtkmain.h" #include <gdk/gdk.h> @@ -46,6 +47,9 @@ struct _GtkEventControllerKey GHashTable *pressed_keys; const GdkEvent *current_event; + + guint is_focus : 1; + guint contains_focus : 1; }; struct _GtkEventControllerKeyClass @@ -65,11 +69,19 @@ enum { static guint signals[N_SIGNALS] = { 0 }; +enum { + PROP_IS_FOCUS = 1, + PROP_CONTAINS_FOCUS, + NUM_PROPERTIES +}; + +static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; + G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key, GTK_TYPE_EVENT_CONTROLLER) static void -gtk_event_controller_finalize (GObject *object) +gtk_event_controller_key_finalize (GObject *object) { GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object); @@ -79,6 +91,50 @@ gtk_event_controller_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 = FALSE; + break; + case GDK_NOTIFY_INFERIOR: + is_focus = focus_in; + contains_focus = !focus_in; + 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->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) @@ -93,11 +149,23 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, 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); - if (gdk_event_get_focus_in (event, &focus_in) && focus_in) - g_signal_emit (controller, signals[FOCUS_IN], 0); + 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); + g_signal_emit (controller, signals[FOCUS_OUT], 0, mode, detail); + + key->current_event = NULL; return FALSE; } @@ -159,15 +227,76 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, } static void +gtk_event_controller_key_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkEventControllerKey *controller = GTK_EVENT_CONTROLLER_KEY (object); + + switch (prop_id) + { + case PROP_IS_FOCUS: + g_value_set_boolean (value, controller->is_focus); + break; + + case PROP_CONTAINS_FOCUS: + g_value_set_boolean (value, controller->contains_focus); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) { GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = gtk_event_controller_finalize; + 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; /** + * GtkEventControllerKey:is-focus: + * + * Whether focus is in the controllers widget itself, + * as opposed to in a descendent widget. See + * #GtkEventControllerKey:contains-focus. + * + * When handling focus events, this property is updated + * before #GtkEventControllerKey::focus-in or + * #GtkEventControllerKey::focus-out are emitted. + */ + props[PROP_IS_FOCUS] = + g_param_spec_boolean ("is-focus", + P_("Is Focus"), + P_("Whether the focus is in the controllers widget"), + FALSE, + G_PARAM_READABLE); + + /** + * GtkEventControllerKey:contains-focus: + * + * Whether focus is in a descendant of the controllers widget. + * See #GtkEventControllerKey:is-focus. + * + * When handling focus events, this property is updated + * before #GtkEventControllerKey::focus-in or + * #GtkEventControllerKey::focus-out are emitted. + */ + props[PROP_CONTAINS_FOCUS] = + g_param_spec_boolean ("contains-focus", + P_("Contains Focus"), + P_("Whether the focus is in a descendant of the controllers widget"), + FALSE, + G_PARAM_READABLE); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, props); + + /** * GtkEventControllerKey::key-pressed: * @controller: the object which received the signal. * @keyval: the pressed key. @@ -233,38 +362,50 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GTK_TYPE_EVENT_CONTROLLER_KEY, G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + 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 #GtkEventController:widget controlled - * by the @controller is given the keyboard focus. + * 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, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + 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 #GtkEventController:widget controlled - * by the @controller loses the keyboard focus. + * 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"), GTK_TYPE_EVENT_CONTROLLER_KEY, G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + NULL, + G_TYPE_NONE, + 2, + GDK_TYPE_CROSSING_MODE, + GDK_TYPE_NOTIFY_TYPE); } static void @@ -283,8 +424,7 @@ gtk_event_controller_key_init (GtkEventControllerKey *controller) GtkEventController * gtk_event_controller_key_new (void) { - return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, - NULL); + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, NULL); } /** @@ -330,6 +470,13 @@ gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller) * * Forwards the current event of this @controller to a @widget. * + * This function can only be used in handlers for the + * #GtkEventControllerKey::key-pressed, + * #GtkEventControllerKey::key-released + * or + * #GtkEventControllerKey::modifiers + * signals. + * * Returns: whether the @widget handled the event **/ gboolean @@ -339,6 +486,8 @@ gtk_event_controller_key_forward (GtkEventControllerKey *controller, g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); g_return_val_if_fail (controller->current_event != NULL, FALSE); + g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_KEY_PRESS || + gdk_event_get_event_type (controller->current_event) == GDK_KEY_RELEASE, FALSE); if (!gtk_widget_get_realized (widget)) gtk_widget_realize (widget); @@ -377,3 +526,64 @@ gtk_event_controller_key_get_group (GtkEventControllerKey *controller) return group; } + +/** + * gtk_event_controller_key_get_focus_origin: + * @controller: a #GtkEventControllerKey + * + * Returns the widget that was holding focus before. + * + * This function can only be used in handlers for the + * #GtkEventControllerKey::focus-in and + * #GtkEventControllerKey::focus-out signals. + * + * Returns: (transfer none): the previous focus + */ +GtkWidget * +gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller) +{ + gboolean focus_in; + GtkWidget *origin; + + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); + g_return_val_if_fail (controller->current_event != NULL, NULL); + g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_FOCUS_CHANGE, NULL); + + gdk_event_get_focus_in (controller->current_event, &focus_in); + + if (focus_in) + origin = (GtkWidget *)gdk_event_get_related_target (controller->current_event); + else + origin = (GtkWidget *)gdk_event_get_target (controller->current_event); + + return origin; +} + +/** + * gtk_event_controller_key_get_focus_target: + * @controller: a #GtkEventControllerKey + * + * Returns the widget that will be holding focus afterwards. + * + * This function can only be used in handlers for the + * #GtkEventControllerKey::focus-in and + * #GtkEventControllerKey::focus-out signals. + * + * Returns: (transfer none): the next focus + */ +GtkWidget * +gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller) +{ + gboolean focus_in; + + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); + g_return_val_if_fail (controller->current_event != NULL, NULL); + g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_FOCUS_CHANGE, NULL); + + gdk_event_get_focus_in (controller->current_event, &focus_in); + + if (focus_in) + return (GtkWidget *)gdk_event_get_target (controller->current_event); + else + return (GtkWidget *)gdk_event_get_related_target (controller->current_event); +} diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h index c8a579773a..b036bde7ec 100644 --- a/gtk/gtkeventcontrollerkey.h +++ b/gtk/gtkeventcontrollerkey.h @@ -58,6 +58,11 @@ gboolean gtk_event_controller_key_forward (GtkEventControllerK GDK_AVAILABLE_IN_ALL guint gtk_event_controller_key_get_group (GtkEventControllerKey *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller); + G_END_DECLS #endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */ diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 4d9ab00e05..3227a84e1f 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -82,6 +82,7 @@ #include "gtkdebug.h" #include "gtkfilechoosererrorstackprivate.h" #include "gtkentryprivate.h" +#include "gtkroot.h" #include <cairo-gobject.h> @@ -340,6 +341,7 @@ struct _GtkFileChooserWidgetPrivate { GSource *focus_entry_idle; gulong toplevel_set_focus_id; + GtkWidget *toplevel_current_focus_widget; GtkWidget *toplevel_last_focus_widget; gint sort_column; @@ -1361,7 +1363,7 @@ key_press_cb (GtkEventController *controller, GtkWidget *default_widget, *focus_widget; default_widget = gtk_window_get_default_widget (window); - focus_widget = gtk_window_get_focus (window); + focus_widget = gtk_root_get_focus (GTK_ROOT (window)); if (widget != default_widget && !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget)))) @@ -2709,7 +2711,7 @@ location_mode_set (GtkFileChooserWidget *impl, switch_to_file_list = FALSE; if (toplevel) { - current_focus = gtk_window_get_focus (toplevel); + current_focus = gtk_root_get_focus (GTK_ROOT (toplevel)); if (!current_focus || current_focus == priv->location_entry) switch_to_file_list = TRUE; } @@ -3560,13 +3562,14 @@ gtk_file_chooser_widget_dispose (GObject *object) * widget on our toplevel. See gtk_file_chooser_widget_hierarchy_changed() */ static void -toplevel_set_focus_cb (GtkWindow *window, - GtkWidget *focus, +toplevel_set_focus_cb (GtkWindow *window, + GParamSpec *pspec, GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; - priv->toplevel_last_focus_widget = gtk_window_get_focus (window); + priv->toplevel_last_focus_widget = priv->toplevel_current_focus_widget; + priv->toplevel_current_focus_widget = gtk_root_get_focus (GTK_ROOT (window)); } /* We monitor the focus widget on our toplevel to be able to know which widget @@ -3584,9 +3587,10 @@ gtk_file_chooser_widget_root (GtkWidget *widget) toplevel = gtk_widget_get_toplevel (widget); g_assert (priv->toplevel_set_focus_id == 0); - priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus", + priv->toplevel_set_focus_id = g_signal_connect (toplevel, "notify::focus-widget", G_CALLBACK (toplevel_set_focus_cb), impl); - priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel)); + priv->toplevel_last_focus_widget = NULL; + priv->toplevel_current_focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel)); } static void @@ -3602,6 +3606,7 @@ gtk_file_chooser_widget_unroot (GtkWidget *widget) g_signal_handler_disconnect (toplevel, priv->toplevel_set_focus_id); priv->toplevel_set_focus_id = 0; priv->toplevel_last_focus_widget = NULL; + priv->toplevel_current_focus_widget = NULL; } GTK_WIDGET_CLASS (gtk_file_chooser_widget_parent_class)->unroot (widget); @@ -5806,7 +5811,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser) toplevel = get_toplevel (GTK_WIDGET (impl)); if (toplevel) - current_focus = gtk_window_get_focus (toplevel); + current_focus = gtk_root_get_focus (GTK_ROOT (toplevel)); else current_focus = NULL; @@ -6655,7 +6660,7 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserEmbed *chooser_embed) retval = FALSE; - current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); + current_focus = gtk_root_get_focus (GTK_ROOT (toplevel)); if (current_focus == priv->browse_files_tree_view) { diff --git a/gtk/gtkimcontextxim.c b/gtk/gtkimcontextxim.c index 98f6e530a6..c203f502df 100644 --- a/gtk/gtkimcontextxim.c +++ b/gtk/gtkimcontextxim.c @@ -1580,40 +1580,6 @@ on_status_toplevel_notify_display (GtkWindow *toplevel, gtk_widget_get_display (GTK_WIDGET (toplevel))); } -/* Called when the toplevel window is moved; updates the position of - * the status window to follow it. - */ -static gboolean -on_status_toplevel_configure (GtkWidget *toplevel, - GdkEvent *event, - StatusWindow *status_window) -{ - if (gdk_event_get_event_type (event) == GDK_CONFIGURE) - { - GdkRectangle rect; - GtkRequisition requisition; - gint y; - gint height; - - if (status_window->window) - { - height = DisplayHeight(GDK_SURFACE_XDISPLAY (gtk_widget_get_surface (toplevel)), 0); - - gdk_surface_get_frame_extents (gtk_widget_get_surface (toplevel), &rect); - gtk_widget_get_preferred_size ( (status_window->window), &requisition, NULL); - - if (rect.y + rect.height + requisition.height < height) - y = rect.y + rect.height; - else - y = height - requisition.height; - - gtk_window_move (GTK_WINDOW (status_window->window), rect.x, y); - } - } - - return GDK_EVENT_PROPAGATE; -} - /* Frees a status window and removes its link from the status_windows list */ static void @@ -1630,9 +1596,6 @@ status_window_free (StatusWindow *status_window) g_signal_handlers_disconnect_by_func (status_window->toplevel, G_CALLBACK (on_status_toplevel_notify_display), status_window); - g_signal_handlers_disconnect_by_func (status_window->toplevel, - G_CALLBACK (on_status_toplevel_configure), - status_window); if (status_window->window) gtk_widget_destroy (status_window->window); @@ -1661,9 +1624,6 @@ status_window_get (GtkWidget *toplevel) g_signal_connect (toplevel, "destroy", G_CALLBACK (on_status_toplevel_destroy), status_window); - g_signal_connect (toplevel, "event", - G_CALLBACK (on_status_toplevel_configure), - status_window); g_signal_connect (toplevel, "notify::display", G_CALLBACK (on_status_toplevel_notify_display), status_window); @@ -1695,8 +1655,6 @@ status_window_make_window (StatusWindow *status_window) gtk_window_set_display (GTK_WINDOW (status_window->window), gtk_widget_get_display (status_window->toplevel)); - - on_status_toplevel_configure (status_window->toplevel, NULL, status_window); } /* Updates the text in the status window, hiding or diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 86442d16a1..e3be68e02e 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -6222,7 +6222,7 @@ gtk_label_activate_current_link (GtkLabel *label) if (window) { default_widget = gtk_window_get_default_widget (window); - focus_widget = gtk_window_get_focus (window); + focus_widget = gtk_root_get_focus (GTK_ROOT (window)); if (default_widget != widget && !(widget == focus_widget && (!default_widget || !gtk_widget_is_sensitive (default_widget)))) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 8aa278e9eb..b435c489a3 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1407,63 +1407,84 @@ static void synth_crossing (GtkWidget *widget, GtkWidget *toplevel, gboolean enter, - GtkWidget *other_widget, + GtkWidget *target, + GtkWidget *related_target, GdkEvent *source, GdkNotifyType notify_type, GdkCrossingMode crossing_mode) { GdkEvent *event; - gdouble x, y; + GtkStateFlags flags; - event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY); - gdk_event_set_user_data (event, G_OBJECT (widget)); + if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE) + { + event = gdk_event_new (GDK_FOCUS_CHANGE); + event->focus_change.in = enter; + event->focus_change.mode = crossing_mode; + event->focus_change.detail = notify_type; + + flags = GTK_STATE_FLAG_FOCUSED; + if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel))) + flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; + } + else + { + gdouble x, y; + event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY); + if (related_target) + event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (related_target)); + gdk_event_get_coords (source, &x, &y); + event->crossing.x = x; + event->crossing.y = y; + event->crossing.mode = crossing_mode; + event->crossing.detail = notify_type; + + flags = GTK_STATE_FLAG_PRELIGHT; + } + + gdk_event_set_target (event, G_OBJECT (target)); + gdk_event_set_related_target (event, G_OBJECT (related_target)); gdk_event_set_device (event, gdk_event_get_device (source)); gdk_event_set_source_device (event, gdk_event_get_source_device (source)); - event->any.surface = g_object_ref (gtk_widget_get_surface (toplevel)); - if (other_widget) - event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (other_widget)); + event->any.surface = gtk_widget_get_surface (toplevel); + if (event->any.surface) + g_object_ref (event->any.surface); if (enter) - gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE); + gtk_widget_set_state_flags (widget, flags, FALSE); else - gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT); + gtk_widget_unset_state_flags (widget, flags); - gdk_event_get_coords (source, &x, &y); - event->crossing.x = x; - event->crossing.y = y; - event->crossing.mode = crossing_mode; - event->crossing.detail = notify_type; + if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE) + { + /* maintain focus chain */ + if (enter || notify_type == GDK_NOTIFY_INFERIOR) + { + GtkWidget *parent = gtk_widget_get_parent (widget); + if (parent) + gtk_widget_set_focus_child (parent, widget); + } + else if (!enter && notify_type != GDK_NOTIFY_INFERIOR) + { + GtkWidget *parent = gtk_widget_get_parent (widget); + if (parent) + gtk_widget_set_focus_child (parent, NULL); + } + /* maintain widget state */ + if (notify_type == GDK_NOTIFY_ANCESTOR || + notify_type == GDK_NOTIFY_INFERIOR || + notify_type == GDK_NOTIFY_NONLINEAR) + gtk_widget_set_has_focus (widget, enter); + } + gtk_widget_event (widget, event); g_object_unref (event); } -static GtkWidget * -update_pointer_focus_state (GtkWindow *toplevel, - GdkEvent *event, - GtkWidget *new_target) -{ - GtkWidget *old_target = NULL; - GdkEventSequence *sequence; - GdkDevice *device; - gdouble x, y; - - device = gdk_event_get_device (event); - sequence = gdk_event_get_event_sequence (event); - old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence); - if (old_target == new_target) - return old_target; - - gdk_event_get_coords (event, &x, &y); - gtk_window_update_pointer_focus (toplevel, device, sequence, - new_target, x, y); - - return old_target; -} - -static void -gtk_synthesize_crossing_events (GtkWindow *toplevel, +void +gtk_synthesize_crossing_events (GtkRoot *toplevel, GtkWidget *old_target, GtkWidget *new_target, GdkEvent *event, @@ -1492,13 +1513,16 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, { widget = old_target; - while (widget != ancestor) + while (widget) { notify_type = (widget == old_target) ? leave_type : get_virtual_notify_type (leave_type); - synth_crossing (widget, GTK_WIDGET (toplevel), FALSE, - new_target, event, notify_type, mode); + if (widget != ancestor || widget == old_target) + synth_crossing (widget, GTK_WIDGET (toplevel), FALSE, + old_target, new_target, event, notify_type, mode); + if (widget == ancestor) + break; widget = gtk_widget_get_parent (widget); } } @@ -1509,9 +1533,11 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, widget = new_target; - while (widget != ancestor) + while (widget) { widgets = g_slist_prepend (widgets, widget); + if (widget == ancestor) + break; widget = gtk_widget_get_parent (widget); } @@ -1522,12 +1548,37 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel, notify_type = (widget == new_target) ? enter_type : get_virtual_notify_type (enter_type); - synth_crossing (widget, GTK_WIDGET (toplevel), TRUE, - old_target, event, notify_type, mode); + if (widget != ancestor || widget == new_target) + synth_crossing (widget, GTK_WIDGET (toplevel), TRUE, + new_target, old_target, event, notify_type, mode); } } } + +static GtkWidget * +update_pointer_focus_state (GtkWindow *toplevel, + GdkEvent *event, + GtkWidget *new_target) +{ + GtkWidget *old_target = NULL; + GdkEventSequence *sequence; + GdkDevice *device; + gdouble x, y; + + device = gdk_event_get_device (event); + sequence = gdk_event_get_event_sequence (event); + old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence); + if (old_target == new_target) + return old_target; + + gdk_event_get_coords (event, &x, &y); + gtk_window_update_pointer_focus (toplevel, device, sequence, + new_target, x, y); + + return old_target; +} + static gboolean is_pointing_event (GdkEvent *event) { @@ -1604,7 +1655,7 @@ handle_pointing_event (GdkEvent *event) old_target = update_pointer_focus_state (toplevel, event, NULL); if (event->any.type == GDK_LEAVE_NOTIFY) - gtk_synthesize_crossing_events (toplevel, old_target, NULL, + gtk_synthesize_crossing_events (GTK_ROOT (toplevel), old_target, NULL, event, event->crossing.mode); break; case GDK_ENTER_NOTIFY: @@ -1629,7 +1680,7 @@ handle_pointing_event (GdkEvent *event) if (!gtk_window_lookup_pointer_focus_implicit_grab (toplevel, device, sequence)) { - gtk_synthesize_crossing_events (toplevel, old_target, target, + gtk_synthesize_crossing_events (GTK_ROOT (toplevel), old_target, target, event, GDK_CROSSING_NORMAL); } @@ -1659,7 +1710,7 @@ handle_pointing_event (GdkEvent *event) new_target = gtk_widget_pick (GTK_WIDGET (toplevel), x, y); if (new_target == NULL) new_target = GTK_WIDGET (toplevel); - gtk_synthesize_crossing_events (toplevel, target, new_target, event, + gtk_synthesize_crossing_events (GTK_ROOT (toplevel), target, new_target, event, GDK_CROSSING_UNGRAB); gtk_window_maybe_update_cursor (toplevel, NULL, device); } @@ -1761,7 +1812,7 @@ gtk_main_do_event (GdkEvent *event) if (is_pointing_event (event)) target_widget = handle_pointing_event (event); - else if (GTK_IS_WINDOW (target_widget) && + else if (GTK_IS_ROOT (target_widget) && (event->any.type == GDK_KEY_PRESS || event->any.type == GDK_KEY_RELEASE)) { @@ -1771,7 +1822,7 @@ gtk_main_do_event (GdkEvent *event) gtk_window_activate_key (GTK_WINDOW (target_widget), (GdkEventKey *) event)) goto cleanup; - focus_widget = gtk_window_get_focus (GTK_WINDOW (target_widget)); + focus_widget = gtk_root_get_focus (GTK_ROOT (target_widget)); if (focus_widget) target_widget = focus_widget; } @@ -1779,7 +1830,7 @@ gtk_main_do_event (GdkEvent *event) if (!target_widget) goto cleanup; - gdk_event_set_user_data (event, G_OBJECT (target_widget)); + gdk_event_set_target (event, G_OBJECT (target_widget)); window_group = gtk_main_get_window_group (target_widget); device = gdk_event_get_device (event); @@ -2398,7 +2449,7 @@ gtk_get_event_widget (const GdkEvent *event) GtkWidget * gtk_get_event_target (const GdkEvent *event) { - return GTK_WIDGET (gdk_event_get_user_data (event)); + return GTK_WIDGET (gdk_event_get_target (event)); } /** diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index d11ee42584..a23f9c41b7 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -5426,7 +5426,13 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook, child_has_focus = priv->child_has_focus; if (priv->cur_page) - gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED); + { + GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (notebook)); + GtkWidget *focus = gtk_root_get_focus (root); + if (focus) + child_has_focus = gtk_widget_is_ancestor (focus, priv->cur_page->child); + gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED); + } priv->cur_page = page; gtk_widget_set_state_flags (page->tab_widget, GTK_STATE_FLAG_CHECKED, FALSE); diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c index 9fc18c9667..c09b3d9142 100644 --- a/gtk/gtkpaned.c +++ b/gtk/gtkpaned.c @@ -2256,10 +2256,8 @@ gtk_paned_restore_focus (GtkPaned *paned) if (!gtk_widget_child_focus (GTK_WIDGET (paned), GTK_DIR_TAB_FORWARD)) { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned)); - - if (GTK_IS_WINDOW (toplevel)) - gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); + GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (paned)); + gtk_root_set_focus (root, NULL); } } @@ -2364,7 +2362,6 @@ gtk_paned_cycle_handle_focus (GtkPaned *paned, { GtkPaned *focus; GtkPaned *first; - GtkWidget *toplevel; GtkWidget *focus_child; gtk_paned_find_neighbours (paned, &next, &prev); @@ -2410,10 +2407,7 @@ gtk_paned_cycle_handle_focus (GtkPaned *paned, first = next; } - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned)); - - if (GTK_IS_WINDOW (toplevel)) - gtk_paned_set_saved_focus (focus, gtk_window_get_focus (GTK_WINDOW (toplevel))); + gtk_paned_set_saved_focus (focus, gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (paned)))); gtk_paned_set_first_paned (focus, first); priv->original_position = gtk_paned_get_position (focus); diff --git a/gtk/gtkplacesview.c b/gtk/gtkplacesview.c index bb03ef9d01..dabb486046 100644 --- a/gtk/gtkplacesview.c +++ b/gtk/gtkplacesview.c @@ -1766,7 +1766,7 @@ on_key_press_event (GtkEventController *controller, if (!toplevel) return FALSE; - focus_widget = gtk_window_get_focus (toplevel); + focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel)); if (!GTK_IS_PLACES_VIEW_ROW (focus_widget)) return FALSE; diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index cfd697a234..c77ddc3f25 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -614,10 +614,11 @@ window_active_changed (GtkWindow *window, static void window_set_focus (GtkWindow *window, - GtkWidget *widget, + GParamSpec *pspec, GtkPopover *popover) { GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); + GtkWidget *widget = gtk_root_get_focus (GTK_ROOT (window)); if (!priv->modal || !widget || !gtk_widget_is_drawable (GTK_WIDGET (popover))) return; @@ -673,7 +674,7 @@ gtk_popover_apply_modality (GtkPopover *popover, g_signal_connect (priv->window, "notify::is-active", G_CALLBACK (window_active_changed), popover); - g_signal_connect (priv->window, "set-focus", + g_signal_connect (priv->window, "notify::focus-widget", G_CALLBACK (window_set_focus), popover); } else diff --git a/gtk/gtkroot.c b/gtk/gtkroot.c index ad93f8791a..249d7405b1 100644 --- a/gtk/gtkroot.c +++ b/gtk/gtkroot.c @@ -21,6 +21,8 @@ #include "gtkrootprivate.h" #include "gdk/gdk-private.h" +#include "gtkprivate.h" +#include "gtkintl.h" /** * SECTION:gtkroot @@ -65,6 +67,13 @@ gtk_root_default_init (GtkRootInterface *iface) iface->get_display = gtk_root_default_get_display; iface->get_renderer = gtk_root_default_get_renderer; iface->get_surface_transform = gtk_root_default_get_surface_transform; + + g_object_interface_install_property (iface, + g_param_spec_object ("focus-widget", + P_("Focus widget"), + P_("The focus widget"), + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); } GdkDisplay * @@ -124,3 +133,63 @@ gtk_root_get_for_surface (GdkSurface *surface) return NULL; } + +/** + * gtk_root_set_focus: + * @self: a #GtkRoot + * @focus: (allow-none): widget to be the new focus widget, or %NULL + * to unset the focus widget + * + * If @focus is not the current focus widget, and is focusable, sets + * it as the focus widget for the root. If @focus is %NULL, unsets + * the focus widget for the root. + * + * To set the focus to a particular widget in the root, it is usually + * more convenient to use gtk_widget_grab_focus() instead of this function. + */ +void +gtk_root_set_focus (GtkRoot *self, + GtkWidget *focus) +{ + g_return_if_fail (GTK_IS_ROOT (self)); + g_return_if_fail (focus == NULL || GTK_IS_WIDGET (focus)); + + g_object_set (self, "focus-widget", focus, NULL); +} + +/** + * gtk_root_get_focus: + * @self: a #GtkRoot + * + * Retrieves the current focused widget within the root. + * + * Note that this is the widget that would have the focus + * if the root is active; if the root is not focused then + * `gtk_widget_has_focus (widget)` will be %FALSE for the + * widget. + * + * Returns: (nullable) (transfer none): the currently focused widget, + * or %NULL if there is none. + */ +GtkWidget * +gtk_root_get_focus (GtkRoot *self) +{ + GtkWidget *focus; + + g_return_val_if_fail (GTK_IS_ROOT (self), NULL); + + g_object_get (self, "focus-widget", &focus, NULL); + + if (focus) + g_object_unref (focus); + + return focus; +} + +guint +gtk_root_install_properties (GObjectClass *object_class, + guint first_prop) +{ + g_object_class_override_property (object_class, first_prop + GTK_ROOT_PROP_FOCUS_WIDGET, "focus-widget"); + return GTK_ROOT_NUM_PROPERTIES; +} diff --git a/gtk/gtkroot.h b/gtk/gtkroot.h index 45e777ba68..4c7ae89e37 100644 --- a/gtk/gtkroot.h +++ b/gtk/gtkroot.h @@ -56,6 +56,12 @@ struct _GtkRootInterface GDK_AVAILABLE_IN_ALL GtkWidget * gtk_root_get_for_surface (GdkSurface *surface); +GDK_AVAILABLE_IN_ALL +void gtk_root_set_focus (GtkRoot *self, + GtkWidget *focus); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_root_get_focus (GtkRoot *self); + G_END_DECLS #endif /* __GTK_ROOT_H__ */ diff --git a/gtk/gtkrootprivate.h b/gtk/gtkrootprivate.h index 07ddc380e5..357bc6441f 100644 --- a/gtk/gtkrootprivate.h +++ b/gtk/gtkrootprivate.h @@ -11,6 +11,14 @@ GskRenderer * gtk_root_get_renderer (GtkRoot void gtk_root_get_surface_transform (GtkRoot *self, int *x, int *y); +enum { + GTK_ROOT_PROP_FOCUS_WIDGET, + GTK_ROOT_NUM_PROPERTIES +} GtkRootProperties; + +guint gtk_root_install_properties (GObjectClass *object_class, + guint first_prop); + G_END_DECLS #endif /* __GTK_ROOT_PRIVATE_H__ */ diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c index 8e16faa837..b273b2e30d 100644 --- a/gtk/gtkspinbutton.c +++ b/gtk/gtkspinbutton.c @@ -824,6 +824,8 @@ key_controller_key_released (GtkEventControllerKey *key, static void key_controller_focus_out (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkSpinButton *spin_button) { GtkSpinButtonPrivate *priv = gtk_spin_button_get_instance_private (spin_button); diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index f9a7723448..d7222f9e5e 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -1123,7 +1123,6 @@ set_visible_child (GtkStack *stack, GtkStackPage *info; GtkWidget *widget = GTK_WIDGET (stack); GList *l; - GtkWidget *toplevel; GtkWidget *focus; gboolean contains_focus = FALSE; guint old_pos = GTK_INVALID_LIST_POSITION; @@ -1165,24 +1164,23 @@ set_visible_child (GtkStack *stack, } } - toplevel = gtk_widget_get_toplevel (widget); - if (GTK_IS_WINDOW (toplevel)) + if (gtk_widget_get_root (widget)) + focus = gtk_root_get_focus (gtk_widget_get_root (widget)); + else + focus = NULL; + if (focus && + priv->visible_child && + priv->visible_child->widget && + gtk_widget_is_ancestor (focus, priv->visible_child->widget)) { - focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - if (focus && - priv->visible_child && - priv->visible_child->widget && - gtk_widget_is_ancestor (focus, priv->visible_child->widget)) - { - contains_focus = TRUE; - - if (priv->visible_child->last_focus) - g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus), - (gpointer *)&priv->visible_child->last_focus); - priv->visible_child->last_focus = focus; - g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus), - (gpointer *)&priv->visible_child->last_focus); - } + contains_focus = TRUE; + + if (priv->visible_child->last_focus) + g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus), + (gpointer *)&priv->visible_child->last_focus); + priv->visible_child->last_focus = focus; + g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus), + (gpointer *)&priv->visible_child->last_focus); } if (priv->last_visible_child) diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 99a5afba37..7a4d6cc2b9 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -3810,7 +3810,7 @@ gtk_text_real_activate (GtkText *self) if (window) { default_widget = gtk_window_get_default_widget (window); - focus_widget = gtk_window_get_focus (window); + focus_widget = gtk_root_get_focus (GTK_ROOT (window)); if (widget != default_widget && !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget)))) gtk_window_activate_default (window); diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 1a4ed0488a..6a45c7893d 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -614,6 +614,8 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey GdkModifierType state, GtkTreeView *tree_view); static void gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key, + GdkCrossingMode mode, + GdkNotifyType detail, GtkTreeView *tree_view); static gint gtk_tree_view_focus (GtkWidget *widget, @@ -5475,6 +5477,8 @@ 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_widget_queue_draw (GTK_WIDGET (tree_view)); @@ -10151,7 +10155,8 @@ send_focus_change (GtkWidget *widget, fevent->focus_change.in = in; gdk_event_set_device (fevent, device); - gtk_widget_send_focus_change (widget, fevent); + gtk_widget_set_has_focus (widget, in); + gtk_widget_event (widget, fevent); g_object_unref (fevent); } diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index 5817a5f2f8..78e22c5ef2 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -814,6 +814,8 @@ gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout) static void focus_in (GtkEventControllerKey *controller, + GdkCrossingMode mode, + GdkNotifyType detail, GtkTreeViewColumn *column) { _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column); @@ -1016,11 +1018,8 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column) gtk_widget_set_can_focus (priv->button, FALSE); if (gtk_widget_has_focus (priv->button)) { - GtkWidget *toplevel = gtk_widget_get_toplevel (priv->tree_view); - if (gtk_widget_is_toplevel (toplevel)) - { - gtk_window_set_focus (GTK_WINDOW (toplevel), NULL); - } + GtkRoot *root = gtk_widget_get_root (priv->tree_view); + gtk_root_set_focus (root, NULL); } } /* Queue a resize on the assumption that we always want to catch all changes diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index cb37b01ff2..ca206fc6a4 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -484,7 +484,6 @@ enum { GRAB_NOTIFY, CHILD_NOTIFY, MNEMONIC_ACTIVATE, - FOCUS, MOVE_FOCUS, KEYNAV_FAILED, DRAG_BEGIN, @@ -1671,23 +1670,6 @@ gtk_widget_class_init (GtkWidgetClass *klass) G_TYPE_BOOLEAN); /** - * GtkWidget::focus: - * @widget: the object which received the signal. - * @direction: - * - * Returns: %TRUE to stop other handlers from being invoked for the event. %FALSE to propagate the event further. - */ - widget_signals[FOCUS] = - g_signal_new (I_("focus"), - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkWidgetClass, focus), - _gtk_boolean_handled_accumulator, NULL, - _gtk_marshal_BOOLEAN__ENUM, - G_TYPE_BOOLEAN, 1, - GTK_TYPE_DIRECTION_TYPE); - - /** * GtkWidget::move-focus: * @widget: the object which received the signal. * @direction: @@ -5321,107 +5303,25 @@ _gtk_widget_grab_notify (GtkWidget *widget, * gtk_widget_grab_focus: * @widget: a #GtkWidget * - * Causes @widget to have the keyboard focus for the #GtkWindow it's - * inside. @widget must be a focusable widget, such as a #GtkEntry; - * something like #GtkFrame won’t work. - * - * More precisely, it must have the %GTK_CAN_FOCUS flag set. Use - * gtk_widget_set_can_focus() to modify that flag. + * Causes @widget (or one of its descendents) to have the keyboard focus + * for the #GtkWindow it's inside. * - * The widget also needs to be realized and mapped. This is indicated by the - * related signals. Grabbing the focus immediately after creating the widget - * will likely fail and cause critical warnings. + * @widget must be focusable, or have a ::grab_focus implementation that + * transfers the focus to a descendant of @widget that is focusable. **/ void gtk_widget_grab_focus (GtkWidget *widget) { g_return_if_fail (GTK_IS_WIDGET (widget)); - if (!gtk_widget_is_sensitive (widget)) - return; - - g_object_ref (widget); GTK_WIDGET_GET_CLASS (widget)->grab_focus (widget); - g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]); - g_object_unref (widget); -} - -static void -reset_focus_recurse (GtkWidget *widget, - gpointer data) -{ - gtk_widget_set_focus_child (widget, NULL); - - gtk_widget_forall (widget, - reset_focus_recurse, - NULL); } static void gtk_widget_real_grab_focus (GtkWidget *focus_widget) { - GtkWidget *toplevel; - GtkWidget *widget; - - /* clear the current focus setting, break if the current widget - * is the focus widget's parent, since containers above that will - * be set by the next loop. - */ - toplevel = _gtk_widget_get_toplevel (focus_widget); - if (_gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel)) - { - widget = gtk_window_get_focus (GTK_WINDOW (toplevel)); - - if (widget == focus_widget) - { - /* We call _gtk_window_internal_set_focus() here so that the - * toplevel window can request the focus if necessary. - * This is needed when the toplevel is a GtkPlug - */ - if (!gtk_widget_has_focus (widget)) - _gtk_window_internal_set_focus (GTK_WINDOW (toplevel), focus_widget); - - return; - } - - if (widget) - { - GtkWidget *common_ancestor = gtk_widget_common_ancestor (widget, focus_widget); - - if (widget != common_ancestor) - { - while (widget->priv->parent) - { - widget = widget->priv->parent; - gtk_widget_set_focus_child (widget, NULL); - if (widget == common_ancestor) - break; - } - } - } - } - else if (toplevel != focus_widget) - { - /* gtk_widget_grab_focus() operates on a tree without window... - * actually, this is very questionable behavior. - */ - - gtk_widget_forall (toplevel, - reset_focus_recurse, - NULL); - } - - /* now propagate the new focus up the widget tree and finally - * set it on the window - */ - widget = focus_widget; - while (widget->priv->parent) - { - gtk_widget_set_focus_child (widget->priv->parent, widget); - widget = widget->priv->parent; - } - if (GTK_IS_WINDOW (widget)) - _gtk_window_internal_set_focus (GTK_WINDOW (widget), focus_widget); + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (focus_widget); + gtk_root_set_focus (priv->root, focus_widget); } static gboolean @@ -5536,17 +5436,8 @@ gtk_widget_real_focus (GtkWidget *widget, } else { - GPtrArray *focus_order = g_ptr_array_new (); - gboolean ret = FALSE; - - /* Try focusing any of the child widgets, depending on the given @direction */ - - gtk_widget_focus_sort (widget, direction, focus_order); - ret = gtk_widget_focus_move (widget, direction, focus_order); - - g_ptr_array_unref (focus_order); - - if (ret) + /* Try focusing any of the child widgets, depending on the given direction */ + if (gtk_widget_focus_move (widget, direction)) return TRUE; } @@ -5560,10 +5451,7 @@ gtk_widget_real_move_focus (GtkWidget *widget, GtkWidget *toplevel = _gtk_widget_get_toplevel (widget); if (widget != toplevel && GTK_IS_WINDOW (toplevel)) - { - g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0, - direction); - } + g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0, direction); } static gboolean @@ -5594,9 +5482,15 @@ gtk_widget_real_keynav_failed (GtkWidget *widget, * @widget: a #GtkWidget * @can_focus: whether or not @widget can own the input focus. * - * Specifies whether @widget can own the input focus. See - * gtk_widget_grab_focus() for actually setting the input focus on a - * widget. + * Specifies whether @widget can own the input focus. + * + * Note that having @can_focus be %TRUE is only one of the + * necessary conditions for being focusable. A widget must + * also be sensitive and not have a ancestor that is marked + * as not child-focusable in order to receive input focus. + * + * See gtk_widget_grab_focus() for actually setting the input + * focus on a widget. **/ void gtk_widget_set_can_focus (GtkWidget *widget, @@ -5709,16 +5603,14 @@ gtk_widget_has_visible_focus (GtkWidget *widget) gboolean gtk_widget_is_focus (GtkWidget *widget) { - GtkWidget *toplevel; + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - toplevel = _gtk_widget_get_toplevel (widget); + if (priv->root) + return widget == gtk_root_get_focus (priv->root); - if (GTK_IS_WINDOW (toplevel)) - return widget == gtk_window_get_focus (GTK_WINDOW (toplevel)); - else - return FALSE; + return FALSE; } /** @@ -7580,8 +7472,6 @@ gboolean gtk_widget_child_focus (GtkWidget *widget, GtkDirectionType direction) { - gboolean return_val; - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); if (!_gtk_widget_get_visible (widget) || @@ -7593,12 +7483,7 @@ gtk_widget_child_focus (GtkWidget *widget, * focus */ - g_signal_emit (widget, - widget_signals[FOCUS], - 0, - direction, &return_val); - - return return_val; + return GTK_WIDGET_GET_CLASS (widget)->focus (widget, direction); } /** @@ -8924,14 +8809,7 @@ gtk_widget_propagate_state (GtkWidget *widget, priv->state_flags |= GTK_STATE_FLAG_INSENSITIVE; if (gtk_widget_is_focus (widget) && !gtk_widget_is_sensitive (widget)) - { - GtkWidget *window; - - window = _gtk_widget_get_toplevel (widget); - - if (window && _gtk_widget_is_toplevel (window)) - gtk_window_set_focus (GTK_WINDOW (window), NULL); - } + gtk_root_set_focus (priv->root, NULL); new_flags = priv->state_flags; @@ -11696,59 +11574,17 @@ gtk_widget_get_overflow (GtkWidget *widget) return priv->overflow; } -/** - * gtk_widget_send_focus_change: - * @widget: a #GtkWidget - * @event: a #GdkEvent of type GDK_FOCUS_CHANGE - * - * Sends the focus change @event to @widget - * - * This function is not meant to be used by applications. The only time it - * should be used is when it is necessary for a #GtkWidget to assign focus - * to a widget that is semantically owned by the first widget even though - * it’s not a direct child - for instance, a search entry in a floating - * window similar to the quick search in #GtkTreeView. - * - * An example of its usage is: - * - * |[<!-- language="C" --> - * GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE); - * - * fevent->focus_change.type = GDK_FOCUS_CHANGE; - * fevent->focus_change.in = TRUE; - * fevent->focus_change.surface = _gtk_widget_get_surface (widget); - * if (fevent->focus_change.surface != NULL) - * g_object_ref (fevent->focus_change.surface); - * - * gtk_widget_send_focus_change (widget, fevent); - * - * g_object_unref (event); - * ]| - * - * Returns: the return value from the event signal emission: %TRUE - * if the event was handled, and %FALSE otherwise - */ -gboolean -gtk_widget_send_focus_change (GtkWidget *widget, - GdkEvent *event) +void +gtk_widget_set_has_focus (GtkWidget *widget, + gboolean has_focus) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - gboolean res; - - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - g_return_val_if_fail (event != NULL && event->any.type == GDK_FOCUS_CHANGE, FALSE); - - g_object_ref (widget); - - priv->has_focus = event->focus_change.in; - res = gtk_widget_event (widget, event); + if (priv->has_focus == has_focus) + return; + priv->has_focus = has_focus; g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]); - - g_object_unref (widget); - - return res; } /** @@ -13470,26 +13306,7 @@ gtk_widget_set_focus_child (GtkWidget *widget, g_return_if_fail (gtk_widget_get_parent (child) == widget); } - if (priv->focus_child) - gtk_widget_unset_state_flags (priv->focus_child, - GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_FOCUS_VISIBLE); - - if (child) - { - GtkWidget *toplevel; - GtkStateFlags flags = GTK_STATE_FLAG_FOCUSED; - - toplevel = _gtk_widget_get_toplevel (widget); - if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel))) - flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; - - gtk_widget_set_state_flags (child, flags, FALSE); - } - g_set_object (&priv->focus_child, child); - - if (GTK_IS_CONTAINER (widget)) - gtk_container_set_focus_child (GTK_CONTAINER (widget), child); } /** diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index f3012b07a4..97848c698e 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -451,9 +451,6 @@ gboolean gtk_widget_mnemonic_activate (GtkWidget *widget, GDK_AVAILABLE_IN_ALL gboolean gtk_widget_event (GtkWidget *widget, const GdkEvent *event); -GDK_AVAILABLE_IN_ALL -gboolean gtk_widget_send_focus_change (GtkWidget *widget, - GdkEvent *event); GDK_AVAILABLE_IN_ALL gboolean gtk_widget_activate (GtkWidget *widget); @@ -1063,6 +1060,9 @@ GDK_AVAILABLE_IN_ALL void gtk_widget_set_focus_child (GtkWidget *widget, GtkWidget *child); GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_widget_get_focus_child (GtkWidget *widget); + +GDK_AVAILABLE_IN_ALL void gtk_widget_snapshot_child (GtkWidget *widget, GtkWidget *child, GtkSnapshot *snapshot); diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c index af68bd7a56..26f7f8ae24 100644 --- a/gtk/gtkwidgetfocus.c +++ b/gtk/gtkwidgetfocus.c @@ -155,15 +155,11 @@ static gboolean old_focus_coords (GtkWidget *widget, graphene_rect_t *old_focus_bounds) { - GtkWidget *toplevel = _gtk_widget_get_toplevel (widget); GtkWidget *old_focus; - if (GTK_IS_WINDOW (toplevel)) - { - old_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - if (old_focus) - return gtk_widget_compute_bounds (old_focus, widget, old_focus_bounds); - } + old_focus = gtk_root_get_focus (gtk_widget_get_root (widget)); + if (old_focus) + return gtk_widget_compute_bounds (old_focus, widget, old_focus_bounds); return FALSE; } @@ -426,7 +422,9 @@ gtk_widget_focus_sort (GtkWidget *widget, child != NULL; child = _gtk_widget_get_next_sibling (child)) { - if (_gtk_widget_get_realized (child)) + if (_gtk_widget_get_realized (child) && + _gtk_widget_is_drawable (child) && + gtk_widget_get_sensitive (child)) g_ptr_array_add (focus_order, child); } } @@ -454,13 +452,17 @@ gtk_widget_focus_sort (GtkWidget *widget, gboolean gtk_widget_focus_move (GtkWidget *widget, - GtkDirectionType direction, - GPtrArray *focus_order) + GtkDirectionType direction) { + GPtrArray *focus_order; GtkWidget *focus_child = gtk_widget_get_focus_child (widget); int i; + gboolean ret = FALSE; - for (i = 0; i < focus_order->len; i ++) + focus_order = g_ptr_array_new (); + gtk_widget_focus_sort (widget, direction, focus_order); + + for (i = 0; i < focus_order->len && !ret; i++) { GtkWidget *child = g_ptr_array_index (focus_order, i); @@ -469,18 +471,17 @@ gtk_widget_focus_move (GtkWidget *widget, if (focus_child == child) { focus_child = NULL; - - if (gtk_widget_child_focus (child, direction)) - return TRUE; + ret = gtk_widget_child_focus (child, direction); } } else if (_gtk_widget_is_drawable (child) && gtk_widget_is_ancestor (child, widget)) { - if (gtk_widget_child_focus (child, direction)) - return TRUE; + ret = gtk_widget_child_focus (child, direction); } } - return FALSE; + g_ptr_array_unref (focus_order); + + return ret; } diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index ae8595e4dd..9ede6b8b79 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -242,6 +242,12 @@ GdkSurface * _gtk_widget_get_device_surface (GtkWidget *widget, GdkDevice *device); GList * _gtk_widget_list_devices (GtkWidget *widget); +void gtk_synthesize_crossing_events (GtkRoot *toplevel, + GtkWidget *from, + GtkWidget *to, + GdkEvent *event, + GdkCrossingMode mode); + void _gtk_widget_synthesize_crossing (GtkWidget *from, GtkWidget *to, GdkDevice *device, @@ -311,14 +317,13 @@ void gtk_widget_forall (GtkWidget GtkCallback callback, gpointer user_data); -GtkWidget *gtk_widget_get_focus_child (GtkWidget *widget); - void gtk_widget_focus_sort (GtkWidget *widget, GtkDirectionType direction, GPtrArray *focus_order); gboolean gtk_widget_focus_move (GtkWidget *widget, - GtkDirectionType direction, - GPtrArray *focus_order); + GtkDirectionType direction); +void gtk_widget_set_has_focus (GtkWidget *widget, + gboolean has_focus); void gtk_widget_get_surface_allocation (GtkWidget *widget, GtkAllocation *allocation); @@ -335,6 +340,7 @@ gboolean gtk_widget_run_controllers (GtkWidget const GdkEvent *event, GtkPropagationPhase phase); + /* inline getters */ static inline GtkWidget * diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 600e96e515..bc2a53cff2 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -122,16 +122,12 @@ * elements representing the #GtkAccelGroup objects you want to add to * your window (synonymous with gtk_window_add_accel_group(). * - * It also supports the <initial-focus> element, whose name property names - * the widget to receive the focus when the window is mapped. - * * An example of a UI definition fragment with accel groups: * |[ * <object class="GtkWindow"> * <accel-groups> * <group name="accelgroup1"/> * </accel-groups> - * <initial-focus name="thunderclap"/> * </object> * * ... @@ -440,8 +436,6 @@ static gint gtk_window_focus (GtkWidget *widget, GtkDirectionType direction); static void gtk_window_move_focus (GtkWidget *widget, GtkDirectionType dir); -static void gtk_window_real_set_focus (GtkWindow *window, - GtkWidget *focus); static void gtk_window_real_activate_default (GtkWindow *window); static void gtk_window_real_activate_focus (GtkWindow *window); @@ -816,8 +810,6 @@ gtk_window_class_init (GtkWindowClass *klass) container_class->remove = gtk_window_remove; container_class->forall = gtk_window_forall; - klass->set_focus = gtk_window_real_set_focus; - klass->activate_default = gtk_window_real_activate_default; klass->activate_focus = gtk_window_real_activate_focus; klass->keys_changed = gtk_window_keys_changed; @@ -1118,24 +1110,7 @@ gtk_window_class_init (GtkWindowClass *klass) GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_properties (gobject_class, LAST_ARG, window_props); - - /** - * GtkWindow:set-focus: - * @window: the window which received the signal - * @widget: (nullable): the newly focused widget (or %NULL for no focus) - * - * This signal is emitted whenever the currently focused widget in - * this window changes. - */ - window_signals[SET_FOCUS] = - g_signal_new (I_("set-focus"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkWindowClass, set_focus), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GTK_TYPE_WIDGET); + gtk_root_install_properties (gobject_class, LAST_ARG); /** * GtkWindow::activate-focus: @@ -2095,6 +2070,9 @@ gtk_window_set_property (GObject *object, case PROP_FOCUS_VISIBLE: gtk_window_set_focus_visible (window, g_value_get_boolean (value)); break; + case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET: + gtk_window_set_focus (window, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2210,6 +2188,9 @@ gtk_window_get_property (GObject *object, case PROP_IS_MAXIMIZED: g_value_set_boolean (value, gtk_window_is_maximized (window)); break; + case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET: + g_value_set_object (value, gtk_window_get_focus (window)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2365,55 +2346,6 @@ static const GMarkupParser window_parser = window_start_element }; -typedef struct { - GObject *object; - GtkBuilder *builder; - gchar *name; - gint line; - gint col; -} NameSubParserData; - -static void -focus_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **names, - const gchar **values, - gpointer user_data, - GError **error) -{ - NameSubParserData *data = (NameSubParserData*)user_data; - - if (strcmp (element_name, "initial-focus") == 0) - { - const gchar *name; - - if (!_gtk_builder_check_parent (data->builder, context, "object", error)) - return; - - if (!g_markup_collect_attributes (element_name, names, values, error, - G_MARKUP_COLLECT_STRING, "name", &name, - G_MARKUP_COLLECT_INVALID)) - { - _gtk_builder_prefix_error (data->builder, context, error); - return; - } - - data->name = g_strdup (name); - g_markup_parse_context_get_position (context, &data->line, &data->col); - } - else - { - _gtk_builder_error_unhandled_tag (data->builder, context, - "GtkWindow", element_name, - error); - } -} - -static const GMarkupParser focus_parser = -{ - focus_start_element -}; - static gboolean gtk_window_buildable_custom_tag_start (GtkBuildable *buildable, GtkBuilder *builder, @@ -2441,21 +2373,6 @@ gtk_window_buildable_custom_tag_start (GtkBuildable *buildable, return TRUE; } - if (strcmp (tagname, "initial-focus") == 0) - { - NameSubParserData *data; - - data = g_slice_new0 (NameSubParserData); - data->name = NULL; - data->object = G_OBJECT (buildable); - data->builder = builder; - - *parser = focus_parser; - *parser_data = data; - - return TRUE; - } - return FALSE; } @@ -2478,23 +2395,6 @@ gtk_window_buildable_custom_finished (GtkBuildable *buildable, g_slice_free (GSListSubParserData, data); } - - if (strcmp (tagname, "initial-focus") == 0) - { - NameSubParserData *data = (NameSubParserData*)user_data; - - if (data->name) - { - GObject *object; - - object = _gtk_builder_lookup_object (builder, data->name, data->line, data->col); - if (object) - gtk_window_set_focus (GTK_WINDOW (buildable), GTK_WIDGET (object)); - g_free (data->name); - } - - g_slice_free (NameSubParserData, data); - } } static GdkDisplay * @@ -2772,70 +2672,6 @@ gtk_window_get_role (GtkWindow *window) } /** - * gtk_window_set_focus: - * @window: a #GtkWindow - * @focus: (allow-none): widget to be the new focus widget, or %NULL to unset - * any focus widget for the toplevel window. - * - * If @focus is not the current focus widget, and is focusable, sets - * it as the focus widget for the window. If @focus is %NULL, unsets - * the focus widget for this window. To set the focus to a particular - * widget in the toplevel, it is usually more convenient to use - * gtk_widget_grab_focus() instead of this function. - **/ -void -gtk_window_set_focus (GtkWindow *window, - GtkWidget *focus) -{ - GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - GtkWidget *parent; - - g_return_if_fail (GTK_IS_WINDOW (window)); - - if (focus) - { - g_return_if_fail (GTK_IS_WIDGET (focus)); - g_return_if_fail (gtk_widget_get_can_focus (focus)); - - if (!gtk_widget_get_visible (GTK_WIDGET (window))) - priv->initial_focus = focus; - else - gtk_widget_grab_focus (focus); - } - else - { - /* Clear the existing focus chain, so that when we focus into - * the window again, we start at the beginnning. - */ - GtkWidget *widget = priv->focus_widget; - if (widget) - { - while ((parent = _gtk_widget_get_parent (widget))) - { - widget = parent; - gtk_widget_set_focus_child (widget, NULL); - } - } - - _gtk_window_internal_set_focus (window, NULL); - } -} - -void -_gtk_window_internal_set_focus (GtkWindow *window, - GtkWidget *focus) -{ - GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - - g_return_if_fail (GTK_IS_WINDOW (window)); - - priv->initial_focus = NULL; - if ((priv->focus_widget != focus) || - (focus && !gtk_widget_has_focus (focus))) - g_signal_emit (window, window_signals[SET_FOCUS], 0, focus); -} - -/** * gtk_window_set_default: * @window: a #GtkWindow * @default_widget: (allow-none): widget to be the default, or %NULL @@ -7072,42 +6908,31 @@ gtk_window_real_activate_focus (GtkWindow *window) static void do_focus_change (GtkWidget *widget, - gboolean in) + gboolean in) { GdkSeat *seat; - GList *devices, *d; - - g_object_ref (widget); + GdkDevice *device; + GdkEvent *event; seat = gdk_display_get_default_seat (gtk_widget_get_display (widget)); - devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_KEYBOARD); - devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); + device = gdk_seat_get_keyboard (seat); - for (d = devices; d; d = d->next) - { - GdkDevice *dev = d->data; - GdkEvent *fevent; - GdkSurface *surface; + event = gdk_event_new (GDK_FOCUS_CHANGE); + gdk_event_set_display (event, gtk_widget_get_display (widget)); + gdk_event_set_device (event, device); - surface = _gtk_widget_get_surface (widget); - - fevent = gdk_event_new (GDK_FOCUS_CHANGE); - gdk_event_set_display (fevent, gtk_widget_get_display (widget)); - - fevent->any.type = GDK_FOCUS_CHANGE; - fevent->any.surface = surface; - if (surface) - g_object_ref (surface); - fevent->focus_change.in = in; - gdk_event_set_device (fevent, dev); + event->any.type = GDK_FOCUS_CHANGE; + event->any.surface = _gtk_widget_get_surface (widget); + if (event->any.surface) + g_object_ref (event->any.surface); + event->focus_change.in = in; + event->focus_change.mode = GDK_CROSSING_STATE_CHANGED; + event->focus_change.detail = GDK_NOTIFY_ANCESTOR; - gtk_widget_send_focus_change (widget, fevent); - - g_object_unref (fevent); - } + gtk_widget_set_has_focus (widget, in); + gtk_widget_event (widget, event); - g_list_free (devices); - g_object_unref (widget); + g_object_unref (event); } static gboolean @@ -7325,80 +7150,56 @@ gtk_window_move_focus (GtkWidget *widget, gtk_window_set_focus (GTK_WINDOW (widget), NULL); } -static void -gtk_window_real_set_focus (GtkWindow *window, - GtkWidget *focus) +/** + * gtk_window_set_focus: + * @window: a #GtkWindow + * @focus: (allow-none): widget to be the new focus widget, or %NULL to unset + * any focus widget for the toplevel window. + * + * If @focus is not the current focus widget, and is focusable, sets + * it as the focus widget for the window. If @focus is %NULL, unsets + * the focus widget for this window. To set the focus to a particular + * widget in the toplevel, it is usually more convenient to use + * gtk_widget_grab_focus() instead of this function. + **/ +void +gtk_window_set_focus (GtkWindow *window, + GtkWidget *focus) { GtkWindowPrivate *priv = gtk_window_get_instance_private (window); - GtkWidget *old_focus = priv->focus_widget; - - if (old_focus) - { - g_object_ref (old_focus); - g_object_freeze_notify (G_OBJECT (old_focus)); - } - if (focus) - { - g_object_ref (focus); - g_object_freeze_notify (G_OBJECT (focus)); - } - - if (priv->focus_widget) - { - if (gtk_widget_get_receives_default (priv->focus_widget) && - (priv->focus_widget != priv->default_widget)) - { - _gtk_widget_set_has_default (priv->focus_widget, FALSE); + GtkWidget *old_focus = NULL; + GdkSeat *seat; + GdkDevice *device; + GdkEvent *event; - if (priv->default_widget) - _gtk_widget_set_has_default (priv->default_widget, TRUE); - } + g_return_if_fail (GTK_IS_WINDOW (window)); - priv->focus_widget = NULL; + if (focus && !gtk_widget_is_sensitive (focus)) + return; - if (priv->is_active) - do_focus_change (old_focus, FALSE); + if (priv->focus_widget) + old_focus = g_object_ref (priv->focus_widget); + g_set_object (&priv->focus_widget, NULL); - g_object_notify (G_OBJECT (old_focus), "is-focus"); - } + seat = gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (window))); + device = gdk_seat_get_keyboard (seat); - /* The above notifications may have set a new focus widget, - * if so, we don't want to override it. - */ - if (focus && !priv->focus_widget) - { - priv->focus_widget = focus; + 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 = _gtk_widget_get_surface (GTK_WIDGET (window)); + if (event->any.surface) + g_object_ref (event->any.surface); - if (gtk_widget_get_receives_default (priv->focus_widget) && - (priv->focus_widget != priv->default_widget)) - { - if (gtk_widget_get_can_default (priv->focus_widget)) - _gtk_widget_set_has_default (priv->focus_widget, TRUE); + gtk_synthesize_crossing_events (GTK_ROOT (window), old_focus, focus, event, GDK_CROSSING_NORMAL); - if (priv->default_widget) - _gtk_widget_set_has_default (priv->default_widget, FALSE); - } + g_object_unref (event); - if (priv->is_active) - do_focus_change (priv->focus_widget, TRUE); + g_set_object (&priv->focus_widget, focus); - /* It's possible for do_focus_change() above to have callbacks - * that clear priv->focus_widget here. - */ - if (priv->focus_widget) - g_object_notify (G_OBJECT (priv->focus_widget), "is-focus"); - } + g_clear_object (&old_focus); - if (old_focus) - { - g_object_thaw_notify (G_OBJECT (old_focus)); - g_object_unref (old_focus); - } - if (focus) - { - g_object_thaw_notify (G_OBJECT (focus)); - g_object_unref (focus); - } + g_object_notify (G_OBJECT (window), "focus-widget"); } static void diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index fb30ab99b8..2b7a0078f5 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -57,7 +57,6 @@ struct _GtkWindow /** * GtkWindowClass: * @parent_class: The parent class. - * @set_focus: Sets child as the focus widget for the window. * @activate_focus: Activates the current focused widget within the window. * @activate_default: Activates the default widget for the window. * @keys_changed: Signal gets emitted when the set of accelerators or @@ -72,9 +71,6 @@ struct _GtkWindowClass /*< public >*/ - void (* set_focus) (GtkWindow *window, - GtkWidget *focus); - /* G_SIGNAL_ACTION signals for keybindings */ void (* activate_focus) (GtkWindow *window); diff --git a/gtk/inspector/misc-info.c b/gtk/inspector/misc-info.c index 9c3b3ec05f..1d9d053d7d 100644 --- a/gtk/inspector/misc-info.c +++ b/gtk/inspector/misc-info.c @@ -46,9 +46,6 @@ struct _GtkInspectorMiscInfoPrivate { GtkWidget *default_widget_row; GtkWidget *default_widget; GtkWidget *default_widget_button; - GtkWidget *focus_widget_row; - GtkWidget *focus_widget; - GtkWidget *focus_widget_button; GtkWidget *mnemonic_label_row; GtkWidget *mnemonic_label; GtkWidget *request_mode_row; @@ -218,43 +215,6 @@ show_default_widget (GtkWidget *button, GtkInspectorMiscInfo *sl) } static void -update_focus_widget (GtkInspectorMiscInfo *sl) -{ - GtkWidget *widget; - - widget = gtk_window_get_focus (GTK_WINDOW (sl->priv->object)); - if (widget) - { - gchar *tmp; - tmp = g_strdup_printf ("%p", widget); - gtk_label_set_label (GTK_LABEL (sl->priv->focus_widget), tmp); - g_free (tmp); - gtk_widget_set_sensitive (sl->priv->focus_widget_button, TRUE); - } - else - { - gtk_label_set_label (GTK_LABEL (sl->priv->focus_widget), "NULL"); - gtk_widget_set_sensitive (sl->priv->focus_widget_button, FALSE); - } -} - -static void -set_focus_cb (GtkWindow *window, GtkWidget *focus, GtkInspectorMiscInfo *sl) -{ - update_focus_widget (sl); -} - -static void -show_focus_widget (GtkWidget *button, GtkInspectorMiscInfo *sl) -{ - GtkWidget *widget; - - widget = gtk_window_get_focus (GTK_WINDOW (sl->priv->object)); - if (widget) - show_object (sl, G_OBJECT (widget), "properties"); -} - -static void show_mnemonic_label (GtkWidget *button, GtkInspectorMiscInfo *sl) { GtkWidget *widget; @@ -358,7 +318,6 @@ update_info (gpointer data) if (GTK_IS_WINDOW (sl->priv->object)) { update_default_widget (sl); - update_focus_widget (sl); } if (GDK_IS_FRAME_CLOCK (sl->priv->object)) @@ -408,7 +367,6 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, if (sl->priv->object) { g_signal_handlers_disconnect_by_func (sl->priv->object, state_flags_changed, sl); - g_signal_handlers_disconnect_by_func (sl->priv->object, set_focus_cb, sl); g_signal_handlers_disconnect_by_func (sl->priv->object, allocation_changed, sl); disconnect_each_other (sl->priv->object, G_OBJECT (sl)); disconnect_each_other (sl, sl->priv->object); @@ -475,14 +433,10 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl, if (GTK_IS_WINDOW (object)) { gtk_widget_show (sl->priv->default_widget_row); - gtk_widget_show (sl->priv->focus_widget_row); - - g_signal_connect_object (object, "set-focus", G_CALLBACK (set_focus_cb), sl, G_CONNECT_AFTER); } else { gtk_widget_hide (sl->priv->default_widget_row); - gtk_widget_hide (sl->priv->focus_widget_row); } if (GDK_IS_FRAME_CLOCK (object)) @@ -595,9 +549,6 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget_row); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget_button); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget_row); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget_button); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, mnemonic_label_row); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, mnemonic_label); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, request_mode_row); @@ -631,7 +582,6 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, child_visible); gtk_widget_class_bind_template_callback (widget_class, show_default_widget); - gtk_widget_class_bind_template_callback (widget_class, show_focus_widget); gtk_widget_class_bind_template_callback (widget_class, show_frame_clock); } diff --git a/gtk/inspector/misc-info.ui b/gtk/inspector/misc-info.ui index 0ee5082e24..cbb3ef65b4 100644 --- a/gtk/inspector/misc-info.ui +++ b/gtk/inspector/misc-info.ui @@ -158,42 +158,6 @@ </object> </child> <child> - <object class="GtkListBoxRow" id="focus_widget_row"> - <property name="activatable">0</property> - <child> - <object class="GtkBox"> - <property name="margin">10</property> - <property name="spacing">40</property> - <child> - <object class="GtkLabel" id="focus_widget_label"> - <property name="label" translatable="yes">Focus Widget</property> - <property name="halign">start</property> - <property name="valign">baseline</property> - <property name="xalign">0.0</property> - <property name="hexpand">1</property> - </object> - </child> - <child> - <object class="GtkLabel" id="focus_widget"> - <property name="selectable">1</property> - <property name="halign">end</property> - <property name="valign">baseline</property> - <property name="ellipsize">end</property> - </object> - </child> - <child> - <object class="GtkButton" id="focus_widget_button"> - <property name="halign">end</property> - <property name="valign">baseline</property> - <property name="label" translatable="yes">Properties</property> - <signal name="clicked" handler="show_focus_widget"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> <object class="GtkListBoxRow" id="mnemonic_label_row"> <property name="activatable">0</property> <child> @@ -616,7 +580,6 @@ <widget name="state_label"/> <widget name="buildable_id_label"/> <widget name="default_widget_label"/> - <widget name="focus_widget_label"/> <widget name="frame_clock_label"/> </widgets> </object> diff --git a/tests/dialog.ui b/tests/dialog.ui index 93aa2015bc..baa032ec48 100644 --- a/tests/dialog.ui +++ b/tests/dialog.ui @@ -239,6 +239,5 @@ <action-widget response="-6">cancel_button</action-widget> <action-widget response="apply">confirm_button</action-widget> </action-widgets> - <initial-focus name="name_entry"/> </object> </interface> diff --git a/tests/testtoolbar.c b/tests/testtoolbar.c index 57c7ceb1b3..12160c8115 100644 --- a/tests/testtoolbar.c +++ b/tests/testtoolbar.c @@ -344,7 +344,7 @@ popup_context_menu (GtkToolbar *toolbar, gint x, gint y, gint button_number) GtkWidget *widget; window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (toolbar))); - widget = gtk_window_get_focus (window); + widget = gtk_root_get_focus (GTK_ROOT (window)); if (!widget) widget = GTK_WIDGET (toolbar); diff --git a/testsuite/a11y/about.txt b/testsuite/a11y/about.txt index 165234e97a..5add6017ca 100644 --- a/testsuite/a11y/about.txt +++ b/testsuite/a11y/about.txt @@ -2,7 +2,7 @@ window1 "dialog" index: 0 name: About FancyPants - state: enabled sensitive showing visible + state: active enabled sensitive showing visible toolkit: gtk window-type: dialog <AtkComponent> @@ -374,7 +374,7 @@ See the GNU General Public License, version 3 or later for details. parent: stack_switcher index: 0 name: About - state: checked enabled focusable sensitive showing visible + state: checked enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/accessible-name.txt b/testsuite/a11y/accessible-name.txt index 1c5be98fb8..f2274db1e5 100644 --- a/testsuite/a11y/accessible-name.txt +++ b/testsuite/a11y/accessible-name.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -12,7 +12,7 @@ window1 parent: window1 index: 0 name: Accessible name - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/actionbar.txt b/testsuite/a11y/actionbar.txt index c5323a4f29..abd273cb1c 100644 --- a/testsuite/a11y/actionbar.txt +++ b/testsuite/a11y/actionbar.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -21,7 +21,7 @@ window1 parent: unnamed-GtkContainerAccessible-0 index: 0 name: Start 1 - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/assistant.txt b/testsuite/a11y/assistant.txt index 6de0d4f1c3..022637ed4a 100644 --- a/testsuite/a11y/assistant.txt +++ b/testsuite/a11y/assistant.txt @@ -13,7 +13,7 @@ window1 parent: content index: 0 name: Page 1 - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/buttons.txt b/testsuite/a11y/buttons.txt index 0a43e143c6..30d0a3ff12 100644 --- a/testsuite/a11y/buttons.txt +++ b/testsuite/a11y/buttons.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -21,7 +21,7 @@ window1 parent: grid1 index: 0 name: Hello World! - state: checked enabled focusable sensitive showing visible + state: checked enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/calendar.txt b/testsuite/a11y/calendar.txt index 768d6c479d..f9e6a18c3b 100644 --- a/testsuite/a11y/calendar.txt +++ b/testsuite/a11y/calendar.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -11,7 +11,7 @@ window1 "calendar" parent: window1 index: 0 - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/colorchooser.txt b/testsuite/a11y/colorchooser.txt index d1dc6adce6..b509b61519 100644 --- a/testsuite/a11y/colorchooser.txt +++ b/testsuite/a11y/colorchooser.txt @@ -2,7 +2,7 @@ window1 "dialog" index: 0 name: Select a Color - state: enabled sensitive showing visible + state: active enabled sensitive showing visible toolkit: gtk window-type: dialog <AtkComponent> @@ -58,7 +58,7 @@ window1 parent: unnamed-GtkContainerAccessible-1 index: 0 name: Light Scarlet Red - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/combos.txt b/testsuite/a11y/combos.txt index 490ecd390b..3d635149dc 100644 --- a/testsuite/a11y/combos.txt +++ b/testsuite/a11y/combos.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> diff --git a/testsuite/a11y/entries.txt b/testsuite/a11y/entries.txt index 3fc7c7ee99..342b557ec8 100644 --- a/testsuite/a11y/entries.txt +++ b/testsuite/a11y/entries.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> diff --git a/testsuite/a11y/expander.txt b/testsuite/a11y/expander.txt index 6a66719dde..5f6ac1ed38 100644 --- a/testsuite/a11y/expander.txt +++ b/testsuite/a11y/expander.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -12,7 +12,7 @@ window1 parent: window1 index: 0 name: Reveal this - state: enabled expandable focusable sensitive showing visible + state: enabled expandable focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/headerbar.txt b/testsuite/a11y/headerbar.txt index 1905c05131..6a933d0110 100644 --- a/testsuite/a11y/headerbar.txt +++ b/testsuite/a11y/headerbar.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -21,7 +21,7 @@ window1 parent: headerbar1 index: 0 name: Yes - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/hello-world.txt b/testsuite/a11y/hello-world.txt index 76119e2099..a3816eda2e 100644 --- a/testsuite/a11y/hello-world.txt +++ b/testsuite/a11y/hello-world.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -12,7 +12,7 @@ window1 parent: window1 index: 0 name: Hello World! - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/iconview.txt b/testsuite/a11y/iconview.txt index eebac50f42..760937982f 100644 --- a/testsuite/a11y/iconview.txt +++ b/testsuite/a11y/iconview.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -11,7 +11,7 @@ window1 "layered pane" parent: window1 index: 0 - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/label-static.txt b/testsuite/a11y/label-static.txt index eff7a8e47c..885522437c 100644 --- a/testsuite/a11y/label-static.txt +++ b/testsuite/a11y/label-static.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -12,7 +12,7 @@ window1 parent: window1 index: 0 name: Go to the GTK+ website or >google it - state: enabled focusable multi-line sensitive showing visible has-tooltip + state: enabled focusable focused multi-line sensitive showing visible has-tooltip toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/label.txt b/testsuite/a11y/label.txt index cf01c56f2f..2875ee2d14 100644 --- a/testsuite/a11y/label.txt +++ b/testsuite/a11y/label.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -12,7 +12,7 @@ window1 parent: window1 index: 0 name: Go to the GTK+ website or >google it - state: enabled focusable multi-line sensitive showing visible has-tooltip + state: enabled focusable focused multi-line sensitive showing visible has-tooltip toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/link.txt b/testsuite/a11y/link.txt index dcd795b596..4d8ff1ad7e 100644 --- a/testsuite/a11y/link.txt +++ b/testsuite/a11y/link.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -12,7 +12,7 @@ window1 parent: window1 index: 0 name: Hello World! - state: enabled focusable sensitive showing visible visited has-tooltip + state: enabled focusable focused sensitive showing visible visited has-tooltip toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/listbox.txt b/testsuite/a11y/listbox.txt index 366d3341e6..46c5abce48 100644 --- a/testsuite/a11y/listbox.txt +++ b/testsuite/a11y/listbox.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -30,7 +30,7 @@ window1 "list item" parent: listbox1 index: 0 - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/lockbutton.txt b/testsuite/a11y/lockbutton.txt index fef97f64a6..b8968cd386 100644 --- a/testsuite/a11y/lockbutton.txt +++ b/testsuite/a11y/lockbutton.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -14,7 +14,7 @@ window1 name: Lock description: Dialog is unlocked. Click to prevent further changes - state: enabled focusable sensitive showing visible has-tooltip + state: enabled focusable focused sensitive showing visible has-tooltip toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/menubutton.txt b/testsuite/a11y/menubutton.txt index d9893ab868..ecfe438f70 100644 --- a/testsuite/a11y/menubutton.txt +++ b/testsuite/a11y/menubutton.txt @@ -1,7 +1,7 @@ window1 "frame" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -20,7 +20,7 @@ window1 parent: window1 index: 0 name: Menu - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/menubutton2.txt b/testsuite/a11y/menubutton2.txt index e5d8cbb537..5dafe5000c 100644 --- a/testsuite/a11y/menubutton2.txt +++ b/testsuite/a11y/menubutton2.txt @@ -1,7 +1,7 @@ window1 "frame" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -89,7 +89,7 @@ window1 parent: window1 index: 0 name: Menu - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/menubutton3.txt b/testsuite/a11y/menubutton3.txt index e5d8cbb537..5dafe5000c 100644 --- a/testsuite/a11y/menubutton3.txt +++ b/testsuite/a11y/menubutton3.txt @@ -1,7 +1,7 @@ window1 "frame" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -89,7 +89,7 @@ window1 parent: window1 index: 0 name: Menu - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/menus.txt b/testsuite/a11y/menus.txt index 44ca3ddab9..ab89b99250 100644 --- a/testsuite/a11y/menus.txt +++ b/testsuite/a11y/menus.txt @@ -7,6 +7,14 @@ window1 <AtkComponent> layer: window alpha: 1 + unnamed-GtkContainerAccessible-0 + "panel" + parent: window1 + state: enabled sensitive showing visible + toolkit: gtk + <AtkComponent> + layer: widget + alpha: 1 menubar1 "menu bar" parent: window1 diff --git a/testsuite/a11y/mnemonic.txt b/testsuite/a11y/mnemonic.txt index 362afca174..cd95e32062 100644 --- a/testsuite/a11y/mnemonic.txt +++ b/testsuite/a11y/mnemonic.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> diff --git a/testsuite/a11y/notebook.txt b/testsuite/a11y/notebook.txt index 7cdd28ec0c..d2e50bf062 100644 --- a/testsuite/a11y/notebook.txt +++ b/testsuite/a11y/notebook.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -11,7 +11,7 @@ window1 "page tab list" parent: window1 index: 0 - state: enabled focusable sensitive showing visible + state: enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/pickers.txt b/testsuite/a11y/pickers.txt index 7f457e374a..f6971e5bf2 100644 --- a/testsuite/a11y/pickers.txt +++ b/testsuite/a11y/pickers.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> diff --git a/testsuite/a11y/placeholder-text.txt b/testsuite/a11y/placeholder-text.txt index 8079c61f03..b8d9cd2f3e 100644 --- a/testsuite/a11y/placeholder-text.txt +++ b/testsuite/a11y/placeholder-text.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -20,7 +20,7 @@ window1 "text" parent: box1 index: 0 - state: editable enabled focusable sensitive showing single-line visible + state: editable enabled sensitive showing single-line visible toolkit: gtk placeholder-text: Subject or Addresses contain <AtkComponent> @@ -62,7 +62,7 @@ window1 "text" parent: box1 index: 1 - state: editable enabled focusable sensitive showing single-line visible + state: editable enabled sensitive showing single-line visible toolkit: gtk placeholder-text: Message contains <AtkComponent> @@ -104,7 +104,7 @@ window1 "text" parent: box1 index: 2 - state: editable enabled focusable sensitive showing single-line visible + state: editable enabled sensitive showing single-line visible toolkit: gtk placeholder-text: Body contains <AtkComponent> diff --git a/testsuite/a11y/range.txt b/testsuite/a11y/range.txt index be18a34fd2..e044e08dc8 100644 --- a/testsuite/a11y/range.txt +++ b/testsuite/a11y/range.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -21,7 +21,7 @@ window1 parent: grid1 index: 0 description: 45.5 - state: enabled focusable horizontal sensitive showing visible + state: enabled focusable focused horizontal sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/scale-drawvalue.txt b/testsuite/a11y/scale-drawvalue.txt index 327b819ffa..c0f9915470 100644 --- a/testsuite/a11y/scale-drawvalue.txt +++ b/testsuite/a11y/scale-drawvalue.txt @@ -1,7 +1,7 @@ window1 "frame" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -20,7 +20,7 @@ window1 parent: window1 index: 0 description: -42.0 - state: enabled focusable horizontal sensitive showing visible + state: enabled focusable focused horizontal sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/stack.txt b/testsuite/a11y/stack.txt index bb07325586..5d41385ccd 100644 --- a/testsuite/a11y/stack.txt +++ b/testsuite/a11y/stack.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -30,7 +30,7 @@ window1 parent: stackswitcher1 index: 0 name: Page 1 - state: checked enabled focusable sensitive showing visible + state: checked enabled focusable focused sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/text.txt b/testsuite/a11y/text.txt index f69dfca29c..f873df4a99 100644 --- a/testsuite/a11y/text.txt +++ b/testsuite/a11y/text.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -11,7 +11,7 @@ window1 "text" parent: window1 index: 0 - state: editable enabled focusable multi-line sensitive showing visible + state: editable enabled focusable focused multi-line sensitive showing visible toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/tooltips.txt b/testsuite/a11y/tooltips.txt index e2a0f3ed99..d351486336 100644 --- a/testsuite/a11y/tooltips.txt +++ b/testsuite/a11y/tooltips.txt @@ -1,7 +1,7 @@ window1 "frame" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -29,7 +29,7 @@ window1 parent: box1 index: 0 description: Tooltip1 - state: enabled focusable sensitive showing visible has-tooltip + state: enabled focusable focused sensitive showing visible has-tooltip toolkit: gtk <AtkComponent> layer: widget diff --git a/testsuite/a11y/tree.txt b/testsuite/a11y/tree.txt index c26c1a9c84..1ed8066958 100644 --- a/testsuite/a11y/tree.txt +++ b/testsuite/a11y/tree.txt @@ -1,7 +1,7 @@ window1 "window" index: 0 - state: enabled resizable sensitive showing visible + state: active enabled resizable sensitive showing visible toolkit: gtk window-type: normal <AtkComponent> @@ -11,7 +11,7 @@ window1 "table" parent: window1 index: 0 - state: enabled focusable sensitive showing visible manages-descendants + state: enabled focusable focused sensitive showing visible manages-descendants toolkit: gtk <AtkComponent> layer: widget @@ -48,7 +48,7 @@ window1 "table cell" parent: tv index: 1 - state: enabled focusable selectable selected sensitive showing transient visible + state: active enabled focusable focused selectable selected sensitive showing transient visible <AtkComponent> layer: widget alpha: 1 @@ -69,7 +69,7 @@ window1 parent: tv index: 0 name: One - state: enabled focusable selectable selected sensitive showing single-line transient visible + state: active enabled focusable focused selectable selected sensitive showing single-line transient visible <AtkComponent> layer: widget alpha: 1 @@ -118,7 +118,7 @@ window1 parent: tv index: 1 name: 234 - state: enabled focusable selectable selected sensitive showing single-line transient visible + state: active enabled focusable focused selectable selected sensitive showing single-line transient visible <AtkComponent> layer: widget alpha: 1 diff --git a/testsuite/gtk/focus.c b/testsuite/gtk/focus.c index 0605c8eb53..a6ffeccbad 100644 --- a/testsuite/gtk/focus.c +++ b/testsuite/gtk/focus.c @@ -1,41 +1,186 @@ #include <gtk/gtk.h> +const char * +widget_name (GtkWidget *widget) +{ + if (gtk_widget_get_name (widget)) + return gtk_widget_get_name (widget); + else if (GTK_IS_LABEL (widget)) + return gtk_label_get_label (GTK_LABEL (widget)); + else if (GTK_IS_EDITABLE (widget)) + return gtk_editable_get_text (GTK_EDITABLE (widget)); + else + return G_OBJECT_TYPE_NAME (widget); +} + +static char * +mode_to_string (GdkCrossingMode mode) +{ + return g_enum_to_string (GDK_TYPE_CROSSING_MODE, mode); +} + +static char * +detail_to_string (GdkNotifyType detail) +{ + return g_enum_to_string (GDK_TYPE_NOTIFY_TYPE, detail); +} + +static void +focus_in (GtkEventController *controller, + GdkCrossingMode mode, + GdkNotifyType detail, + GString *s) +{ + GtkWidget *widget = gtk_event_controller_get_widget (controller); + g_string_append_printf (s, "%s: focus-in %s %s\n", + widget_name (widget), + mode_to_string (mode), + detail_to_string (detail)); +} + +static void +focus_out (GtkEventController *controller, + GdkCrossingMode mode, + GdkNotifyType detail, + GString *s) +{ + GtkWidget *widget = gtk_event_controller_get_widget (controller); + g_string_append_printf (s, "%s: focus-out %s %s\n", + widget_name (widget), + mode_to_string (mode), + detail_to_string (detail)); +} + +static void +add_controller (GtkWidget *widget, GString *s) +{ + GtkEventController *controller; + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in), s); + g_signal_connect (controller, "focus-out", G_CALLBACK (focus_out), s); + gtk_widget_add_controller (widget, controller); +} + static void test_window_focus (void) { GtkWidget *window; GtkWidget *box; + GtkWidget *box1; + GtkWidget *box2; + GtkWidget *label1; + GtkWidget *label2; GtkWidget *entry1; GtkWidget *entry2; + GString *s = g_string_new (""); + + /* + * The tree look like this, with [] indicating + * focus locations: + * + * window + * | + * +----[box]-----+ + * | | | + * label1 box1 box2------+ + * | | | + * [entry1] label2 [entry2] + */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_name (window, "window"); + add_controller (window, s); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_can_focus (box, TRUE); + gtk_widget_set_name (box, "box"); + add_controller (box, s); gtk_container_add (GTK_CONTAINER (window), box); - gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("label1")); + label1 = gtk_label_new ("label1"); + gtk_widget_set_name (label1, "label1"); + add_controller (label1, s); + gtk_container_add (GTK_CONTAINER (box), label1); + box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_name (box1, "box1"); + add_controller (box1, s); + gtk_container_add (GTK_CONTAINER (box), box1); entry1 = gtk_text_new (); - gtk_container_add (GTK_CONTAINER (box), entry1); - gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("label2")); + gtk_widget_set_name (entry1, "entry1"); + add_controller (entry1, s); + gtk_container_add (GTK_CONTAINER (box1), entry1); + box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_name (box2, "box2"); + add_controller (box2, s); + gtk_container_add (GTK_CONTAINER (box), box2); + label2 = gtk_label_new ("label2"); + gtk_widget_set_name (label2, "label2"); + add_controller (label2, s); + gtk_container_add (GTK_CONTAINER (box2), label2); entry2 = gtk_text_new (); - gtk_container_add (GTK_CONTAINER (box), entry2); + gtk_widget_set_name (entry2, "entry2"); + add_controller (entry2, s); + gtk_container_add (GTK_CONTAINER (box2), entry2); g_assert_null (gtk_window_get_focus (GTK_WINDOW (window))); - gtk_window_set_focus (GTK_WINDOW (window), entry1); + gtk_widget_show (window); - g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); + /* show puts the initial focus on box */ + g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box); - gtk_widget_show (window); + if (g_test_verbose ()) + g_print ("-> box\n%s\n", s->str); + + g_assert_cmpstr (s->str, ==, +"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n"); + g_string_truncate (s, 0); + + gtk_widget_grab_focus (entry1); + + if (g_test_verbose ()) + g_print ("box -> entry1\n%s\n", s->str); + + g_assert_cmpstr (s->str, ==, +"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR\n" +"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" +"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n"); + + g_string_truncate (s, 0); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); gtk_widget_grab_focus (entry2); - g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2); + if (g_test_verbose ()) + g_print ("entry1 -> entry2\n%s\n", s->str); - gtk_widget_hide (window); + g_assert_cmpstr (s->str, ==, +"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR\n" +"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL\n" +"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL\n" +"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR\n"); + + g_string_truncate (s, 0); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2); + gtk_widget_grab_focus (box); + + if (g_test_verbose ()) + g_print ("entry2 -> box\n%s", s->str); + + g_assert_cmpstr (s->str, ==, +"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR\n" +"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL\n" +"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR\n"); + + g_string_truncate (s, 0); + + gtk_widget_hide (window); + + g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box); + gtk_window_set_focus (GTK_WINDOW (window), entry1); g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1); |