wip: Add more information to crossing events

Add fields for direct descendents to GtkCrossingData,
and populate them when emitting focus change events.

Also add accessors for these fields to GtkEventControllerKey,
and verify that they are set properly in the focus test.

Not done yet: Do the same for pointer crossing events.
This commit is contained in:
Matthias Clasen 2020-02-18 23:30:16 -05:00
parent 7bb6abb1d4
commit 89c3a7ab24
4 changed files with 117 additions and 8 deletions

View File

@ -48,10 +48,15 @@ typedef struct _GtkCrossingData GtkCrossingData;
* @direction: whether this is a focus-in or focus-out event
* @mode: the crossing mode
* @old_target: the old target
* @old_descendent: the direct child of the receiving widget that
* is an ancestor of @old_target, or %NULL if @old_target is not
* a descendent of the receiving widget
* @new_target: the new target
* @new_descendent: the direct child of the receiving widget that
* is an ancestor of @new_target, or %NULL if @new_target is not
* a descendent of the receiving widget
*
* The struct that is passed to gtk_event_controller_handle_crossing()
* and is also passed to #GtkEventControllerKey::focus-change.
* The struct that is passed to gtk_event_controller_handle_crossing().
*
* The @old_target and @new_target fields are set to the old or new
* focus or hover location.
@ -61,7 +66,9 @@ struct _GtkCrossingData {
GtkCrossingDirection direction;
GdkCrossingMode mode;
GtkWidget *old_target;
GtkWidget *old_descendent;
GtkWidget *new_target;
GtkWidget *new_descendent;
};
GDK_AVAILABLE_IN_ALL

View File

@ -548,6 +548,24 @@ gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller)
return controller->current_crossing->new_target;
}
GtkWidget *
gtk_event_controller_key_get_old_focus_child (GtkEventControllerKey *controller)
{
g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
g_return_val_if_fail (controller->current_crossing != NULL, NULL);
return controller->current_crossing->old_descendent;
}
GtkWidget *
gtk_event_controller_key_get_new_focus_child (GtkEventControllerKey *controller)
{
g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
g_return_val_if_fail (controller->current_crossing != NULL, NULL);
return controller->current_crossing->new_descendent;
}
/**
* gtk_event_controller_key_contains_focus:
* @self: a #GtkEventControllerKey

View File

@ -62,6 +62,10 @@ 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);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_event_controller_key_get_old_focus_child (GtkEventControllerKey *controller);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_event_controller_key_get_new_focus_child (GtkEventControllerKey *controller);
GDK_AVAILABLE_IN_ALL
gboolean gtk_event_controller_key_contains_focus (GtkEventControllerKey *self);

View File

@ -6346,15 +6346,49 @@ gtk_window_move_focus (GtkWidget *widget,
gtk_window_set_focus (GTK_WINDOW (widget), NULL);
}
static void
check_crossing_invariants (GtkWidget *widget,
GtkCrossingData *crossing)
{
if (crossing->old_target == NULL)
g_assert (crossing->old_descendent == NULL);
else if (crossing->old_descendent == NULL)
g_assert (crossing->old_target == widget || !gtk_widget_is_ancestor (crossing->old_target, widget));
else
{
g_assert (gtk_widget_get_parent (crossing->old_descendent) == widget);
g_assert (gtk_widget_is_ancestor (crossing->old_descendent, widget));
g_assert (crossing->old_target == crossing->old_descendent || gtk_widget_is_ancestor (crossing->old_target, crossing->old_descendent));
}
if (crossing->new_target == NULL)
g_assert (crossing->new_descendent == NULL);
else if (crossing->new_descendent == NULL)
g_assert (crossing->new_target == widget || !gtk_widget_is_ancestor (crossing->new_target, widget));
else
{
g_assert (gtk_widget_get_parent (crossing->new_descendent) == widget);
g_assert (gtk_widget_is_ancestor (crossing->new_descendent, widget));
g_assert (crossing->new_target == crossing->new_descendent || gtk_widget_is_ancestor (crossing->new_target, crossing->new_descendent));
}
}
static void
synthesize_focus_change_events (GtkWindow *window,
GtkWidget *old_focus,
GtkWidget *new_focus)
{
GtkCrossingData crossing;
GtkWidget *ancestor;
GtkWidget *widget, *focus_child;
GList *list, *l;
GtkStateFlags flags;
GtkWidget *prev;
gboolean seen_ancestor;
if (old_focus && new_focus)
ancestor = gtk_widget_common_ancestor (old_focus, new_focus);
else
ancestor = NULL;
flags = GTK_STATE_FLAG_FOCUSED;
if (gtk_window_get_focus_visible (GTK_WINDOW (window)))
@ -6363,29 +6397,52 @@ synthesize_focus_change_events (GtkWindow *window,
crossing.type = GTK_CROSSING_FOCUS;
crossing.mode = GDK_CROSSING_NORMAL;
crossing.old_target = old_focus;
crossing.old_descendent = NULL;
crossing.new_target = new_focus;
crossing.new_descendent = NULL;
crossing.direction = GTK_CROSSING_OUT;
prev = NULL;
seen_ancestor = FALSE;
widget = old_focus;
while (widget)
{
crossing.old_descendent = prev;
if (seen_ancestor)
{
crossing.new_descendent = new_focus ? prev : NULL;
}
else if (widget == ancestor)
{
GtkWidget *w;
crossing.new_descendent = NULL;
for (w = new_focus; w != ancestor; w = gtk_widget_get_parent (w))
crossing.new_descendent = w;
seen_ancestor = TRUE;
}
else
{
crossing.new_descendent = NULL;
}
check_crossing_invariants (widget, &crossing);
gtk_widget_handle_crossing (widget, &crossing, 0, 0);
gtk_widget_unset_state_flags (widget, flags);
gtk_widget_set_focus_child (widget, NULL);
prev = widget;
widget = gtk_widget_get_parent (widget);
}
list = NULL;
widget = new_focus;
while (widget)
{
list = g_list_prepend (list, widget);
widget = gtk_widget_get_parent (widget);
}
for (widget = new_focus; widget; widget = gtk_widget_get_parent (widget))
list = g_list_prepend (list, widget);
crossing.direction = GTK_CROSSING_IN;
seen_ancestor = FALSE;
for (l = list; l; l = l->next)
{
widget = l->data;
@ -6393,6 +6450,29 @@ synthesize_focus_change_events (GtkWindow *window,
focus_child = l->next->data;
else
focus_child = NULL;
crossing.new_descendent = focus_child;
if (seen_ancestor)
{
crossing.old_descendent = NULL;
}
else if (widget == ancestor)
{
GtkWidget *w;
crossing.old_descendent = NULL;
for (w = old_focus; w != ancestor; w = gtk_widget_get_parent (w))
{
crossing.old_descendent = w;
}
seen_ancestor = TRUE;
}
else
{
crossing.old_descendent = old_focus ? focus_child : NULL;
}
check_crossing_invariants (widget, &crossing);
gtk_widget_handle_crossing (widget, &crossing, 0, 0);
gtk_widget_set_state_flags (widget, flags, FALSE);
gtk_widget_set_focus_child (widget, focus_child);