Merge branch 'matthiasc/atspi-child' into 'master'

Emit ChildrenChanged signal on hierarchy updates

See merge request GNOME/gtk!2752
This commit is contained in:
Matthias Clasen 2020-10-27 18:24:43 +00:00
commit 8df883ed71
12 changed files with 376 additions and 24 deletions

View File

@ -34,7 +34,6 @@
#include "gtkatspiutilsprivate.h"
#include "gtkatspivalueprivate.h"
#include "gtkatspicomponentprivate.h"
#include "a11y/atspi/atspi-accessible.h"
#include "a11y/atspi/atspi-action.h"
#include "a11y/atspi/atspi-editabletext.h"
@ -348,12 +347,12 @@ get_index_in_parent (GtkWidget *widget)
child;
child = gtk_widget_get_next_sibling (child))
{
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
continue;
if (child == widget)
return idx;
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
continue;
idx++;
}
@ -375,12 +374,12 @@ get_index_in_toplevels (GtkWidget *widget)
g_object_unref (window);
if (!gtk_widget_get_visible (window))
continue;
if (window == widget)
return idx;
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (window)))
continue;
idx += 1;
}
@ -810,6 +809,23 @@ emit_bounds_changed (GtkAtSpiContext *self,
NULL);
}
static void
emit_children_changed (GtkAtSpiContext *self,
GtkAtSpiContext *child_context,
int idx,
GtkAccessibleChildState state)
{
GVariant *child_ref = gtk_at_spi_context_to_ref (child_context);
GVariant *context_ref = gtk_at_spi_context_to_ref (self);
gtk_at_spi_emit_children_changed (self->connection,
self->context_path,
state,
idx,
child_ref,
context_ref);
}
static void
gtk_at_spi_context_state_change (GtkATContext *ctx,
GtkAccessibleStateChange changed_states,
@ -821,15 +837,40 @@ gtk_at_spi_context_state_change (GtkATContext *ctx,
{
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx);
GtkAccessible *accessible = gtk_at_context_get_accessible (ctx);
GtkWidget *widget;
GtkAccessibleValue *value;
if (!GTK_IS_WIDGET (accessible))
if (GTK_IS_WIDGET (accessible) && !gtk_widget_get_realized (GTK_WIDGET (accessible)))
return;
widget = GTK_WIDGET (accessible);
if (!gtk_widget_get_realized (widget))
return;
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN)
{
GtkWidget *parent;
GtkATContext *context;
GtkAccessibleChildChange change;
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_HIDDEN);
if (gtk_boolean_accessible_value_get (value))
change = GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED;
else
change = GTK_ACCESSIBLE_CHILD_CHANGE_ADDED;
if (GTK_IS_ROOT (accessible))
{
gtk_at_spi_root_child_changed (self->root, change, accessible);
}
else
{
if (GTK_IS_WIDGET (accessible))
parent = gtk_widget_get_parent (GTK_WIDGET (accessible));
else if (GTK_IS_STACK_PAGE (accessible))
parent = gtk_widget_get_parent (gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
else
g_assert_not_reached ();
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent));
gtk_at_context_child_changed (context, change, accessible);
}
}
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_BUSY)
{
@ -1040,6 +1081,120 @@ gtk_at_spi_context_bounds_change (GtkATContext *ctx)
emit_bounds_changed (self, (int)x, (int)y, width, height);
}
static void
gtk_at_spi_context_child_change (GtkATContext *ctx,
GtkAccessibleChildChange change,
GtkAccessible *child)
{
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx);
GtkAccessible *accessible = gtk_at_context_get_accessible (ctx);
GtkATContext *child_context = gtk_accessible_get_at_context (child);
GtkWidget *parent_widget;
GtkWidget *child_widget;
int idx = 0;
if (!GTK_IS_WIDGET (accessible))
return;
if (child_context == NULL)
return;
/* handle the stack page special case */
if (GTK_IS_WIDGET (child) &&
GTK_IS_STACK (gtk_widget_get_parent (GTK_WIDGET (child))))
{
GtkWidget *stack;
GtkStackPage *page;
GListModel *pages;
stack = gtk_widget_get_parent (GTK_WIDGET (child));
page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (child));
pages = G_LIST_MODEL (gtk_stack_get_pages (GTK_STACK (stack)));
idx = 0;
for (guint i = 0; i < g_list_model_get_n_items (pages); i++)
{
GtkStackPage *item = g_list_model_get_item (pages, i);
g_object_unref (item);
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (item)))
continue;
if (item == page)
break;
idx++;
}
g_object_unref (pages);
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
{
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (stack))),
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
idx,
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (child)),
0,
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
}
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED)
{
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (child)),
0,
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (stack))),
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
idx,
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
}
return;
}
parent_widget = GTK_WIDGET (accessible);
if (GTK_IS_STACK_PAGE (child))
child_widget = gtk_stack_page_get_child (GTK_STACK_PAGE (child));
else
child_widget = GTK_WIDGET (child);
if (gtk_widget_get_parent (child_widget) != parent_widget)
{
idx = 0;
}
else
{
for (GtkWidget *iter = gtk_widget_get_first_child (parent_widget);
iter != NULL;
iter = gtk_widget_get_next_sibling (iter))
{
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (iter)))
continue;
if (iter == child_widget)
break;
idx += 1;
}
}
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
emit_children_changed (self,
GTK_AT_SPI_CONTEXT (child_context),
idx,
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
else if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED)
emit_children_changed (self,
GTK_AT_SPI_CONTEXT (child_context),
idx,
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
}
/* }}} */
/* {{{ D-Bus Registration */
static void
@ -1326,6 +1481,7 @@ gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass)
context_class->state_change = gtk_at_spi_context_state_change;
context_class->platform_change = gtk_at_spi_context_platform_change;
context_class->bounds_change = gtk_at_spi_context_bounds_change;
context_class->child_change = gtk_at_spi_context_child_change;
obj_props[PROP_BUS_ADDRESS] =
g_param_spec_string ("bus-address", NULL, NULL,

View File

@ -405,6 +405,65 @@ static const GDBusInterfaceVTable root_accessible_vtable = {
NULL,
};
void
gtk_at_spi_root_child_changed (GtkAtSpiRoot *self,
GtkAccessibleChildChange change,
GtkAccessible *child)
{
guint n, i;
int idx = 0;
GVariant *window_ref;
GtkAccessibleChildState state;
if (!self->toplevels)
return;
for (i = 0, n = g_list_model_get_n_items (self->toplevels); i < n; i++)
{
GtkAccessible *item = g_list_model_get_item (self->toplevels, i);
g_object_unref (item);
if (item == child)
break;
if (!gtk_accessible_should_present (item))
continue;
idx++;
}
if (child == NULL)
{
window_ref = gtk_at_spi_null_ref ();
}
else
{
GtkATContext *context = gtk_accessible_get_at_context (child);
window_ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
}
switch (change)
{
case GTK_ACCESSIBLE_CHILD_CHANGE_ADDED:
state = GTK_ACCESSIBLE_CHILD_STATE_ADDED;
break;
case GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED:
state = GTK_ACCESSIBLE_CHILD_STATE_REMOVED;
break;
default:
g_assert_not_reached ();
}
gtk_at_spi_emit_children_changed (self->connection,
self->root_path,
state,
idx,
gtk_at_spi_root_to_ref (self),
window_ref);
}
static void
on_registration_reply (GObject *gobject,
GAsyncResult *result,
@ -437,7 +496,6 @@ on_registration_reply (GObject *gobject,
/* Register the cache object */
self->cache = gtk_at_spi_cache_new (self->connection, ATSPI_CACHE_PATH);
/* Monitor the top levels */
self->toplevels = gtk_window_get_toplevels ();
}

View File

@ -42,4 +42,9 @@ gtk_at_spi_root_get_cache (GtkAtSpiRoot *self);
GVariant *
gtk_at_spi_root_to_ref (GtkAtSpiRoot *self);
void
gtk_at_spi_root_child_changed (GtkAtSpiRoot *self,
GtkAccessibleChildChange change,
GtkAccessible *child);
G_END_DECLS

View File

@ -305,3 +305,37 @@ gtk_at_spi_null_ref (void)
{
return g_variant_new ("(so)", "", "/org/a11y/atspi/null");
}
void
gtk_at_spi_emit_children_changed (GDBusConnection *connection,
const char *path,
GtkAccessibleChildState state,
int idx,
GVariant *child_ref,
GVariant *sender_ref)
{
const char *change;
switch (state)
{
case GTK_ACCESSIBLE_CHILD_STATE_ADDED:
change = "add";
break;
case GTK_ACCESSIBLE_CHILD_STATE_REMOVED:
change = "remove";
break;
default:
g_assert_not_reached ();
return;
}
g_dbus_connection_emit_signal (connection,
NULL,
path,
"org.a11y.atspi.Event.Object",
"ChildrenChanged",
g_variant_new ("(siiv@(so))", change, idx, 0, child_ref, sender_ref),
NULL);
}

View File

@ -31,4 +31,12 @@ gtk_atspi_role_for_context (GtkATContext *context);
GVariant *
gtk_at_spi_null_ref (void);
void
gtk_at_spi_emit_children_changed (GDBusConnection *connection,
const char *path,
GtkAccessibleChildState state,
int idx,
GVariant *child_ref,
GVariant *sender_ref);
G_END_DECLS

View File

@ -742,6 +742,8 @@ gtk_accessible_bounds_changed (GtkAccessible *self)
gboolean
gtk_accessible_should_present (GtkAccessible *self)
{
GtkATContext *context;
if (GTK_IS_WIDGET (self) &&
!gtk_widget_get_visible (GTK_WIDGET (self)))
return FALSE;
@ -750,5 +752,39 @@ gtk_accessible_should_present (GtkAccessible *self)
gtk_accessible_get_accessible_role (self) == GTK_ACCESSIBLE_ROLE_PRESENTATION)
return FALSE;
context = gtk_accessible_get_at_context (self);
if (gtk_at_context_has_accessible_state (context, GTK_ACCESSIBLE_STATE_HIDDEN))
{
GtkAccessibleValue *value;
value = gtk_at_context_get_accessible_state (context, GTK_ACCESSIBLE_STATE_HIDDEN);
if (gtk_boolean_accessible_value_get (value))
return FALSE;
}
return TRUE;
}
void
gtk_accessible_update_children (GtkAccessible *self,
GtkAccessible *child,
GtkAccessibleChildState state)
{
GtkATContext *context;
if (GTK_IS_WIDGET (self) &&
gtk_widget_get_root (GTK_WIDGET (self)) == NULL)
return;
context = gtk_accessible_get_at_context (self);
/* propagate changes up from ignored widgets */
if (gtk_accessible_get_accessible_role (self) == GTK_ACCESSIBLE_ROLE_NONE)
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (gtk_widget_get_parent (GTK_WIDGET (self))));
if (context == NULL)
return;
gtk_at_context_child_changed (context, 1 << state, child);
gtk_at_context_update (context);
}

View File

@ -49,4 +49,8 @@ gboolean gtk_accessible_get_platform_state (GtkAccessible *s
void gtk_accessible_bounds_changed (GtkAccessible *self);
void gtk_accessible_update_children (GtkAccessible *self,
GtkAccessible *child,
GtkAccessibleChildState state);
G_END_DECLS

View File

@ -155,6 +155,13 @@ gtk_at_context_real_bounds_change (GtkATContext *self)
{
}
static void
gtk_at_context_real_child_change (GtkATContext *self,
GtkAccessibleChildChange change,
GtkAccessible *child)
{
}
static void
gtk_at_context_class_init (GtkATContextClass *klass)
{
@ -167,6 +174,7 @@ gtk_at_context_class_init (GtkATContextClass *klass)
klass->state_change = gtk_at_context_real_state_change;
klass->platform_change = gtk_at_context_real_platform_change;
klass->bounds_change = gtk_at_context_real_bounds_change;
klass->child_change = gtk_at_context_real_child_change;
/**
* GtkATContext:accessible-role:
@ -979,3 +987,11 @@ gtk_at_context_bounds_changed (GtkATContext *self)
{
GTK_AT_CONTEXT_GET_CLASS (self)->bounds_change (self);
}
void
gtk_at_context_child_changed (GtkATContext *self,
GtkAccessibleChildChange change,
GtkAccessible *child)
{
GTK_AT_CONTEXT_GET_CLASS (self)->child_change (self, change, child);
}

View File

@ -90,6 +90,16 @@ typedef enum {
GTK_ACCESSIBLE_PLATFORM_CHANGE_FOCUSED = 1 << GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED,
} GtkAccessiblePlatformChange;
typedef enum {
GTK_ACCESSIBLE_CHILD_STATE_ADDED,
GTK_ACCESSIBLE_CHILD_STATE_REMOVED
} GtkAccessibleChildState;
typedef enum {
GTK_ACCESSIBLE_CHILD_CHANGE_ADDED = 1 << GTK_ACCESSIBLE_CHILD_STATE_ADDED,
GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED = 1 << GTK_ACCESSIBLE_CHILD_STATE_REMOVED
} GtkAccessibleChildChange;
struct _GtkATContext
{
GObject parent_instance;
@ -124,6 +134,10 @@ struct _GtkATContextClass
GtkAccessiblePlatformChange changed_platform);
void (* bounds_change) (GtkATContext *self);
void (* child_change) (GtkATContext *self,
GtkAccessibleChildChange changed_child,
GtkAccessible *child);
};
GdkDisplay * gtk_at_context_get_display (GtkATContext *self);
@ -158,6 +172,9 @@ char * gtk_at_context_get_description (GtkATContext
void gtk_at_context_platform_changed (GtkATContext *self,
GtkAccessiblePlatformChange change);
void gtk_at_context_bounds_changed (GtkATContext *self);
void gtk_at_context_child_changed (GtkATContext *self,
GtkAccessibleChildChange change,
GtkAccessible *child);
const char * gtk_accessible_property_get_attribute_name (GtkAccessibleProperty property);
const char * gtk_accessible_relation_get_attribute_name (GtkAccessibleRelation relation);

View File

@ -1304,6 +1304,10 @@ update_child_visible (GtkStack *stack,
gtk_widget_set_child_visible (priv->last_visible_child->widget, FALSE);
priv->last_visible_child = NULL;
}
gtk_accessible_update_state (GTK_ACCESSIBLE (child_info),
GTK_ACCESSIBLE_STATE_HIDDEN, !visible,
-1);
}
static void
@ -1467,29 +1471,24 @@ stack_remove (GtkStack *stack,
if (child_info == NULL)
return;
priv->children = g_list_remove (priv->children, child_info);
g_signal_handlers_disconnect_by_func (child,
stack_child_visibility_notify_cb,
stack);
was_visible = gtk_widget_get_visible (child);
g_clear_object (&child_info->widget);
if (priv->visible_child == child_info)
{
if (in_dispose)
priv->visible_child = NULL;
else
set_visible_child (stack, NULL, priv->transition_type, priv->transition_duration);
}
priv->visible_child = NULL;
if (priv->last_visible_child == child_info)
priv->last_visible_child = NULL;
gtk_widget_unparent (child);
g_clear_object (&child_info->widget);
priv->children = g_list_remove (priv->children, child_info);
g_object_unref (child_info);
if (!in_dispose &&

View File

@ -2413,6 +2413,10 @@ gtk_widget_unparent (GtkWidget *widget)
g_object_freeze_notify (G_OBJECT (widget));
gtk_accessible_update_children (GTK_ACCESSIBLE (priv->parent),
GTK_ACCESSIBLE (widget),
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
root = _gtk_widget_get_root (widget);
if (GTK_IS_WINDOW (root))
_gtk_window_unset_focus_and_default (GTK_WINDOW (root), widget);
@ -5796,6 +5800,11 @@ gtk_widget_reposition_after (GtkWidget *widget,
gtk_widget_queue_compute_expand (parent);
}
if (prev_parent == NULL)
gtk_accessible_update_children (GTK_ACCESSIBLE (parent),
GTK_ACCESSIBLE (widget),
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
gtk_widget_pop_verify_invariants (widget);
}

View File

@ -1554,6 +1554,11 @@ gtk_window_constructed (GObject *object)
gtk_widget_add_controller (GTK_WIDGET (object), GTK_EVENT_CONTROLLER (priv->click_gesture));
g_list_store_append (toplevel_list, window);
gtk_accessible_update_state (GTK_ACCESSIBLE (window),
GTK_ACCESSIBLE_STATE_HIDDEN, TRUE,
-1);
g_object_unref (window);
}
@ -7195,11 +7200,16 @@ gtk_window_destroy (GtkWindow *window)
return;
g_object_ref (window);
gtk_tooltip_unset_surface (GTK_NATIVE (window));
gtk_window_hide (GTK_WIDGET (window));
gtk_accessible_update_state (GTK_ACCESSIBLE (window),
GTK_ACCESSIBLE_STATE_HIDDEN, TRUE,
-1);
g_list_store_remove (toplevel_list, i);
gtk_window_hide (GTK_WIDGET (window));
gtk_widget_unrealize (GTK_WIDGET (window));
g_object_unref (window);