diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-11-12 12:35:23 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-11-12 12:35:23 +0000 |
commit | 5d9799d4e35d407ed151bae51f9295ffbf794476 (patch) | |
tree | 80b385423b3d69965537cf0fdbca721223b04a70 | |
parent | f95943a752679ffba36cd3387719f4388fb81810 (diff) | |
parent | 08d59d3f6820d41de8a2080e358eee2df77bcec3 (diff) | |
download | gtk+-5d9799d4e35d407ed151bae51f9295ffbf794476.tar.gz |
Merge branch 'ebassi/for-master' into 'master'
ATContext lifetime fixes
Closes #3341
See merge request GNOME/gtk!2811
-rw-r--r-- | gtk/a11y/gtkatspicomponent.c | 19 | ||||
-rw-r--r-- | gtk/a11y/gtkatspicontext.c | 56 | ||||
-rw-r--r-- | gtk/a11y/gtkatspiprivate.h | 12 | ||||
-rw-r--r-- | gtk/a11y/gtkatspiutils.c | 13 | ||||
-rw-r--r-- | gtk/gtkaccessible.c | 17 | ||||
-rw-r--r-- | gtk/gtkaspectframe.c | 1 | ||||
-rw-r--r-- | gtk/gtkatcontext.c | 88 | ||||
-rw-r--r-- | gtk/gtkatcontextprivate.h | 4 | ||||
-rw-r--r-- | gtk/gtkbox.c | 5 | ||||
-rw-r--r-- | gtk/gtkcenterbox.c | 5 | ||||
-rw-r--r-- | gtk/gtkenums.h | 3 | ||||
-rw-r--r-- | gtk/gtkgrid.c | 8 | ||||
-rw-r--r-- | gtk/gtkheaderbar.c | 13 | ||||
-rw-r--r-- | gtk/gtkmodelbutton.c | 57 | ||||
-rw-r--r-- | gtk/gtknotebook.c | 30 | ||||
-rw-r--r-- | gtk/gtkscrolledwindow.c | 5 | ||||
-rw-r--r-- | gtk/gtkstack.c | 2 | ||||
-rw-r--r-- | gtk/gtktestatcontext.c | 8 | ||||
-rw-r--r-- | gtk/gtkviewport.c | 7 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 117 | ||||
-rw-r--r-- | gtk/gtkwindow.c | 6 | ||||
-rw-r--r-- | gtk/gtkwindowcontrols.c | 5 | ||||
-rw-r--r-- | gtk/gtkwindowhandle.c | 7 |
23 files changed, 334 insertions, 154 deletions
diff --git a/gtk/a11y/gtkatspicomponent.c b/gtk/a11y/gtkatspicomponent.c index 2ab1ffc81c..7da968d1d1 100644 --- a/gtk/a11y/gtkatspicomponent.c +++ b/gtk/a11y/gtkatspicomponent.c @@ -26,7 +26,9 @@ #include "gtkatspiprivate.h" #include "gtkatspiutilsprivate.h" #include "gtkaccessibleprivate.h" +#include "gtkpopover.h" #include "gtkwidget.h" +#include "gtkwindow.h" #include "a11y/atspi/atspi-component.h" @@ -194,11 +196,20 @@ component_handle_method (GDBusConnection *connection, } else if (g_strcmp0 (method_name, "GetLayer") == 0) { - g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + AtspiComponentLayer layer; + + if (GTK_IS_WINDOW (widget)) + layer = ATSPI_COMPONENT_LAYER_WINDOW; + else if (GTK_IS_POPOVER (widget)) + layer = ATSPI_COMPONENT_LAYER_POPUP; + else + layer = ATSPI_COMPONENT_LAYER_WIDGET; + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", layer)); } else if (g_strcmp0 (method_name, "GetMDIZOrder") == 0) { - g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(n)", 0)); } else if (g_strcmp0 (method_name, "GrabFocus") == 0) { @@ -206,7 +217,9 @@ component_handle_method (GDBusConnection *connection, } else if (g_strcmp0 (method_name, "GetAlpha") == 0) { - g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + double opacity = gtk_widget_get_opacity (widget); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(d)", opacity)); } else if (g_strcmp0 (method_name, "SetExtents") == 0) { diff --git a/gtk/a11y/gtkatspicontext.c b/gtk/a11y/gtkatspicontext.c index 98bde0a0e7..4dad9863dd 100644 --- a/gtk/a11y/gtkatspicontext.c +++ b/gtk/a11y/gtkatspicontext.c @@ -46,9 +46,10 @@ #include "gtkeditable.h" #include "gtkentryprivate.h" #include "gtkroot.h" +#include "gtkstack.h" #include "gtktextview.h" +#include "gtktypebuiltins.h" #include "gtkwindow.h" -#include "gtkstack.h" #include <gio/gio.h> @@ -411,7 +412,11 @@ get_parent_context_ref (GtkAccessible *accessible) gtk_accessible_get_at_context (GTK_ACCESSIBLE (page)); if (parent_context != NULL) - res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context)); + { + gtk_at_context_realize (parent_context); + + res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context)); + } } else { @@ -1399,6 +1404,8 @@ gtk_at_spi_context_finalize (GObject *gobject) gtk_at_spi_context_unregister_object (self); + g_clear_object (&self->root); + g_free (self->bus_address); g_free (self->context_path); @@ -1447,8 +1454,8 @@ static void gtk_at_spi_context_constructed (GObject *gobject) { GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject); - GdkDisplay *display = gtk_at_context_get_display (GTK_AT_CONTEXT (self)); + /* Make sure that we were properly constructed */ g_assert (self->bus_address); /* We use the application's object path to build the path of each @@ -1490,10 +1497,19 @@ gtk_at_spi_context_constructed (GObject *gobject) g_free (base_path); g_free (uuid); + G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject); +} + +static void +gtk_at_spi_context_realize (GtkATContext *context) +{ + GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context); + GdkDisplay *display = gtk_at_context_get_display (context); + /* Every GTK application has a single root AT-SPI object, which * handles all the global state, including the cache of accessible * objects. We use the GdkDisplay to store it, so it's guaranteed - * to be unique per-display connection + * to be a unique per-display connection */ self->root = g_object_get_data (G_OBJECT (display), "-gtk-atspi-root"); @@ -1502,26 +1518,32 @@ gtk_at_spi_context_constructed (GObject *gobject) { self->root = gtk_at_spi_root_new (self->bus_address); g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-root", - self->root, + g_object_ref (self->root), g_object_unref); } - - G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject); -} - -static void -gtk_at_spi_context_realize (GtkATContext *context) -{ - GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context); + else + { + g_object_ref (self->root); + } self->connection = gtk_at_spi_root_get_connection (self->root); if (self->connection == NULL) return; GtkAccessible *accessible = gtk_at_context_get_accessible (context); - GTK_NOTE (A11Y, g_message ("Realizing ATSPI context at '%s' for accessible '%s'", - self->context_path, - G_OBJECT_TYPE_NAME (accessible))); + +#ifdef G_ENABLE_DEBUG + if (GTK_DEBUG_CHECK (A11Y)) + { + GtkAccessibleRole role = gtk_at_context_get_accessible_role (context); + char *role_name = g_enum_to_string (GTK_TYPE_ACCESSIBLE_ROLE, role); + g_message ("Realizing ATSPI context “%s” for accessible “%s”, with role: “%s”", + self->context_path, + G_OBJECT_TYPE_NAME (accessible), + role_name); + g_free (role_name); + } +#endif gtk_atspi_connect_text_signals (accessible, (GtkAtspiTextChangedCallback *)emit_text_changed, @@ -1551,6 +1573,8 @@ gtk_at_spi_context_unrealize (GtkATContext *context) gtk_atspi_disconnect_text_signals (accessible); gtk_atspi_disconnect_selection_signals (accessible); gtk_at_spi_context_unregister_object (self); + + g_clear_object (&self->root); } static void diff --git a/gtk/a11y/gtkatspiprivate.h b/gtk/a11y/gtkatspiprivate.h index 9d3d316354..036a41ef27 100644 --- a/gtk/a11y/gtkatspiprivate.h +++ b/gtk/a11y/gtkatspiprivate.h @@ -252,4 +252,16 @@ typedef enum { ATSPI_COORD_TYPE_PARENT, } AtspiCoordType; +typedef enum { + ATSPI_COMPONENT_LAYER_INVALID, + ATSPI_COMPONENT_LAYER_BACKGROUND, + ATSPI_COMPONENT_LAYER_CANVAS, + ATSPI_COMPONENT_LAYER_WIDGET, + ATSPI_COMPONENT_LAYER_MDI, + ATSPI_COMPONENT_LAYER_POPUP, + ATSPI_COMPONENT_LAYER_OVERLAY, + ATSPI_COMPONENT_LAYER_WINDOW +} AtspiComponentLayer; + + G_END_DECLS diff --git a/gtk/a11y/gtkatspiutils.c b/gtk/a11y/gtkatspiutils.c index 7ec3193b9b..87b1887fe8 100644 --- a/gtk/a11y/gtkatspiutils.c +++ b/gtk/a11y/gtkatspiutils.c @@ -80,7 +80,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role) break; case GTK_ACCESSIBLE_ROLE_FORM: - break; + return ATSPI_ROLE_FORM; case GTK_ACCESSIBLE_ROLE_GENERIC: break; @@ -92,10 +92,10 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role) return ATSPI_ROLE_TABLE_CELL; case GTK_ACCESSIBLE_ROLE_GROUP: - break; + return ATSPI_ROLE_PANEL; case GTK_ACCESSIBLE_ROLE_HEADING: - break; + return ATSPI_ROLE_HEADING; case GTK_ACCESSIBLE_ROLE_IMG: return ATSPI_ROLE_IMAGE; @@ -110,7 +110,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role) break; case GTK_ACCESSIBLE_ROLE_LEGEND: - break; + return ATSPI_ROLE_LABEL; case GTK_ACCESSIBLE_ROLE_LINK: return ATSPI_ROLE_LINK; @@ -134,7 +134,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role) return ATSPI_ROLE_MARQUEE; case GTK_ACCESSIBLE_ROLE_MATH: - return ATSPI_ROLE_MATH;; + return ATSPI_ROLE_MATH; case GTK_ACCESSIBLE_ROLE_METER: return ATSPI_ROLE_LEVEL_BAR; @@ -269,7 +269,7 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role) return ATSPI_ROLE_FILLER; case GTK_ACCESSIBLE_ROLE_WINDOW: - return ATSPI_ROLE_WINDOW; + return ATSPI_ROLE_FRAME; default: break; @@ -294,6 +294,7 @@ gtk_atspi_role_for_context (GtkATContext *context) GtkAccessible *accessible = gtk_at_context_get_accessible (context); GtkAccessibleRole role = gtk_at_context_get_accessible_role (context); + /* ARIA does not have a "password entry" role, so we need to fudge it here */ if (GTK_IS_PASSWORD_ENTRY (accessible)) return ATSPI_ROLE_PASSWORD_TEXT; diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index 13c4cb35ed..991bc02756 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -104,13 +104,17 @@ gtk_accessible_get_at_context (GtkAccessible *self) GtkAccessibleRole gtk_accessible_get_accessible_role (GtkAccessible *self) { + GtkAccessibleRole role; + g_return_val_if_fail (GTK_IS_ACCESSIBLE (self), GTK_ACCESSIBLE_ROLE_NONE); GtkATContext *context = gtk_accessible_get_at_context (self); - if (context == NULL) - return GTK_ACCESSIBLE_ROLE_NONE; + if (context != NULL && gtk_at_context_is_realized (context)) + return gtk_at_context_get_accessible_role (context); + + g_object_get (G_OBJECT (self), "accessible-role", &role, NULL); - return gtk_at_context_get_accessible_role (context); + return role; } /** @@ -678,9 +682,7 @@ gtk_accessible_platform_changed (GtkAccessible *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)))); - } + context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (gtk_widget_get_parent (GTK_WIDGET (self)))); if (context == NULL) return; @@ -765,6 +767,9 @@ gtk_accessible_should_present (GtkAccessible *self) return FALSE; context = gtk_accessible_get_at_context (self); + if (context == NULL) + return FALSE; + if (gtk_at_context_has_accessible_state (context, GTK_ACCESSIBLE_STATE_HIDDEN)) { GtkAccessibleValue *value; diff --git a/gtk/gtkaspectframe.c b/gtk/gtkaspectframe.c index ad6049ffe9..916f307daf 100644 --- a/gtk/gtkaspectframe.c +++ b/gtk/gtkaspectframe.c @@ -176,6 +176,7 @@ gtk_aspect_frame_class_init (GtkAspectFrameClass *class) GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), I_("aspectframe")); + gtk_widget_class_set_accessible_role (GTK_WIDGET_CLASS (class), GTK_ACCESSIBLE_ROLE_GROUP); } static void diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c index 661ac848b1..8c49c27118 100644 --- a/gtk/gtkatcontext.c +++ b/gtk/gtkatcontext.c @@ -111,7 +111,7 @@ gtk_at_context_set_property (GObject *gobject, break; case PROP_DISPLAY: - self->display = g_value_get_object (value); + gtk_at_context_set_display (self, g_value_get_object (value)); break; default: @@ -245,8 +245,8 @@ gtk_at_context_class_init (GtkATContextClass *klass) "The display connection", GDK_TYPE_DISPLAY, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); /** * GtkATContext::state-change: @@ -385,15 +385,15 @@ gtk_at_context_init (GtkATContext *self) self->accessible_role = GTK_ACCESSIBLE_ROLE_NONE; self->properties = - gtk_accessible_attribute_set_new (N_PROPERTIES, + gtk_accessible_attribute_set_new (G_N_ELEMENTS (property_attrs), property_attrs, (GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_property); self->relations = - gtk_accessible_attribute_set_new (N_RELATIONS, + gtk_accessible_attribute_set_new (G_N_ELEMENTS (relation_attrs), relation_attrs, (GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_relation); self->states = - gtk_accessible_attribute_set_new (N_STATES, + gtk_accessible_attribute_set_new (G_N_ELEMENTS (state_attrs), state_attrs, (GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_state); } @@ -414,6 +414,30 @@ gtk_at_context_get_accessible (GtkATContext *self) return self->accessible; } +/*< private > + * gtk_at_context_set_accessible_role: + * @self: a #GtkATContext + * @role: the accessible role for the context + * + * Sets the accessible role for the given #GtkATContext. + * + * This function can only be called if the #GtkATContext is unrealized. + */ +void +gtk_at_context_set_accessible_role (GtkATContext *self, + GtkAccessibleRole role) +{ + g_return_if_fail (GTK_IS_AT_CONTEXT (self)); + g_return_if_fail (!self->realized); + + if (self->accessible_role == role) + return; + + self->accessible_role = role; + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACCESSIBLE_ROLE]); +} + /** * gtk_at_context_get_accessible_role: * @self: a #GtkATContext @@ -431,6 +455,34 @@ gtk_at_context_get_accessible_role (GtkATContext *self) } /*< private > + * gtk_at_context_set_display: + * @self: a #GtkATContext + * @display: a #GdkDisplay + * + * Sets the #GdkDisplay used by the #GtkATContext. + * + * This function can only be called if the #GtkATContext is + * not realized. + */ +void +gtk_at_context_set_display (GtkATContext *self, + GdkDisplay *display) +{ + g_return_if_fail (GTK_IS_AT_CONTEXT (self)); + g_return_if_fail (display == NULL || GDK_IS_DISPLAY (display)); + + if (self->display == display) + return; + + if (self->realized) + return; + + self->display = display; + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DISPLAY]); +} + +/*< private > * gtk_at_context_get_display: * @self: a #GtkATContext * @@ -862,6 +914,27 @@ gtk_at_context_get_accessible_relation (GtkATContext *self, return gtk_accessible_attribute_set_get_value (self->relations, relation); } +static gboolean +is_structural_role (GtkAccessibleRole role) +{ + /* Keep the switch small while avoiding the compiler warning for + * unhandled enumeration values + */ + switch ((int) role) + { + case GTK_ACCESSIBLE_ROLE_FORM: + case GTK_ACCESSIBLE_ROLE_GROUP: + case GTK_ACCESSIBLE_ROLE_GENERIC: + case GTK_ACCESSIBLE_ROLE_REGION: + return TRUE; + + default: + break; + } + + return FALSE; +} + /* See the WAI-ARIA § 4.3, "Accessible Name and Description Computation" */ static void gtk_at_context_get_name_accumulate (GtkATContext *self, @@ -937,7 +1010,8 @@ gtk_at_context_get_name_accumulate (GtkATContext *self, if (names->len != 0) return; - if (self->accessible) + /* Ignore structural elements, namely: generic containers */ + if (self->accessible != NULL && !is_structural_role (role)) g_ptr_array_add (names, (char *)G_OBJECT_TYPE_NAME (self->accessible)); } diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h index f1b62f8cb8..d9678cbea5 100644 --- a/gtk/gtkatcontextprivate.h +++ b/gtk/gtkatcontextprivate.h @@ -150,7 +150,11 @@ GtkATContext * gtk_at_context_clone (GtkATContext GtkAccessible *accessible, GdkDisplay *display); +void gtk_at_context_set_display (GtkATContext *self, + GdkDisplay *display); GdkDisplay * gtk_at_context_get_display (GtkATContext *self); +void gtk_at_context_set_accessible_role (GtkATContext *self, + GtkAccessibleRole role); void gtk_at_context_realize (GtkATContext *self); void gtk_at_context_unrealize (GtkATContext *self); diff --git a/gtk/gtkbox.c b/gtk/gtkbox.c index b23d523ac2..957678a3c0 100644 --- a/gtk/gtkbox.c +++ b/gtk/gtkbox.c @@ -51,6 +51,10 @@ * # CSS nodes * * GtkBox uses a single CSS node with name box. + * + * # Accessibility + * + * GtkBox uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ #include "config.h" @@ -278,6 +282,7 @@ gtk_box_class_init (GtkBoxClass *class) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("box")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void gtk_box_init (GtkBox *box) diff --git a/gtk/gtkcenterbox.c b/gtk/gtkcenterbox.c index 76f07ad6ba..9ea5a241e0 100644 --- a/gtk/gtkcenterbox.c +++ b/gtk/gtkcenterbox.c @@ -50,6 +50,10 @@ * * In vertical orientation, the nodes of the children are arranged from top to * bottom. + * + * # Accessibility + * + * GtkCenterBox uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ #include "config.h" @@ -210,6 +214,7 @@ gtk_center_box_class_init (GtkCenterBoxClass *klass) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CENTER_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("box")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 8f0f7edc98..206fdbef12 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -1136,7 +1136,8 @@ typedef enum { * @GTK_ACCESSIBLE_ROLE_GENERIC: Unused * @GTK_ACCESSIBLE_ROLE_GRID: A grid of items. * @GTK_ACCESSIBLE_ROLE_GRID_CELL: An item in a grid or tree grid. - * @GTK_ACCESSIBLE_ROLE_GROUP: Unused + * @GTK_ACCESSIBLE_ROLE_GROUP: An element that groups multiple widgets. GTK uses + * this role for various containers, like #GtkBox, #GtkViewport, and #GtkHeaderBar. * @GTK_ACCESSIBLE_ROLE_HEADING: Unused * @GTK_ACCESSIBLE_ROLE_IMG: An image. * @GTK_ACCESSIBLE_ROLE_INPUT: Abstract role. diff --git a/gtk/gtkgrid.c b/gtk/gtkgrid.c index 3a5b6a7a95..4f8d35492e 100644 --- a/gtk/gtkgrid.c +++ b/gtk/gtkgrid.c @@ -50,7 +50,11 @@ * * # CSS nodes * - * GtkGrid uses a single CSS node with name grid. + * GtkGrid uses a single CSS node with name `grid`. + * + * # Accessibility + * + * GtkGrid uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ typedef struct @@ -395,8 +399,8 @@ gtk_grid_class_init (GtkGridClass *class) g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); gtk_widget_class_set_css_name (widget_class, I_("grid")); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_GRID_LAYOUT); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static GtkBuildableIface *parent_buildable_iface; diff --git a/gtk/gtkheaderbar.c b/gtk/gtkheaderbar.c index 8147a86496..9006bea9e0 100644 --- a/gtk/gtkheaderbar.c +++ b/gtk/gtkheaderbar.c @@ -96,13 +96,17 @@ * ╰── windowcontrols.end * ]| * - * A #GtkHeaderBar's CSS node is called headerbar. It contains a windowhandle - * subnode, which contains a box subnode, which contains two box subnodes at - * the start and end of the headerbar, as well as a center node that represents + * A #GtkHeaderBar's CSS node is called `headerbar`. It contains a `windowhandle` + * subnode, which contains a `box` subnode, which contains two `box` subnodes at + * the start and end of the header bar, as well as a center node that represents * the title. * - * Each of the boxes contains a windowcontrols subnode, see #GtkWindowControls + * Each of the boxes contains a `windowcontrols` subnode, see #GtkWindowControls * for details, as well as other children. + * + * # Accessibility + * + * GtkHeaderBar uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ #define MIN_TITLE_CHARS 5 @@ -600,6 +604,7 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("headerbar")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index 6bcf11eb9b..c6e3d04f10 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -176,8 +176,6 @@ struct _GtkModelButton guint open_timeout; GtkEventController *controller; - GtkATContext *at_context; - guint active : 1; guint centered : 1; guint iconic : 1; @@ -194,10 +192,7 @@ struct _GtkModelButtonClass static void gtk_model_button_actionable_iface_init (GtkActionableInterface *iface); -static void gtk_model_button_accessible_iface_init (GtkAccessibleInterface *iface); - G_DEFINE_TYPE_WITH_CODE (GtkModelButton, gtk_model_button, GTK_TYPE_WIDGET, - G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE, gtk_model_button_accessible_iface_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_model_button_actionable_iface_init)) GType @@ -302,12 +297,20 @@ gtk_model_button_actionable_iface_init (GtkActionableInterface *iface) iface->set_action_target_value = gtk_model_button_set_action_target_value; } -static GtkATContext * -create_at_context (GtkModelButton *button, - GtkATContext *old_context) +static void +update_at_context (GtkModelButton *button) { - GdkDisplay *display = _gtk_widget_get_display (GTK_WIDGET (button)); GtkAccessibleRole role; + GtkATContext *context; + gboolean was_realized; + + context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (button)); + if (context == NULL) + return; + + was_realized = gtk_at_context_is_realized (context); + + gtk_at_context_unrealize (context); switch (button->role) { @@ -324,30 +327,10 @@ create_at_context (GtkModelButton *button, break; } - if (old_context != NULL) - return gtk_at_context_clone (old_context, role, GTK_ACCESSIBLE (button), display); - - return gtk_at_context_create (role, GTK_ACCESSIBLE (button), display); -} - -static GtkATContext * -gtk_model_button_get_at_context (GtkAccessible *accessible) -{ - GtkModelButton *button = GTK_MODEL_BUTTON (accessible); - - if (button->at_context == NULL) - button->at_context = create_at_context (button, NULL); - - return button->at_context; -} - -static void -gtk_model_button_accessible_iface_init (GtkAccessibleInterface *iface) -{ - GtkAccessibleInterface *parent_iface = g_type_interface_peek_parent (iface); + gtk_at_context_set_accessible_role (context, role); - iface->get_at_context = gtk_model_button_get_at_context; - iface->get_platform_state = parent_iface->get_platform_state; + if (was_realized) + gtk_at_context_realize (context); } static void @@ -610,8 +593,6 @@ static void gtk_model_button_set_role (GtkModelButton *self, GtkButtonRole role) { - GtkATContext *old_context; - if (role == self->role) return; @@ -631,11 +612,7 @@ gtk_model_button_set_role (GtkModelButton *self, update_node_name (self); gtk_model_button_update_state (self); - /* Replace the old context, if any, with a new context */ - old_context = g_steal_pointer (&self->at_context); - self->at_context = create_at_context (self, old_context); - g_clear_object (&old_context); - + update_at_context (self); update_accessible_properties (self); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ROLE]); @@ -1033,8 +1010,6 @@ gtk_model_button_dispose (GObject *object) g_clear_pointer (&model_button->menu_name, g_free); - g_clear_object (&model_button->at_context); - G_OBJECT_CLASS (gtk_model_button_parent_class)->dispose (object); } diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 6f96faeee1..4daddb7b76 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -126,30 +126,33 @@ * ╰── <child> * ]| * - * GtkNotebook has a main CSS node with name notebook, a subnode - * with name header and below that a subnode with name tabs which - * contains one subnode per tab with name tab. + * GtkNotebook has a main CSS node with name `notebook`, a subnode + * with name `header` and below that a subnode with name `tabs` which + * contains one subnode per tab with name `tab`. * * If action widgets are present, their CSS nodes are placed next - * to the tabs node. If the notebook is scrollable, CSS nodes with - * name arrow are placed as first and last child of the tabs node. + * to the `tabs` node. If the notebook is scrollable, CSS nodes with + * name `arrow` are placed as first and last child of the `tabs` node. * - * The main node gets the .frame style class when the notebook + * The main node gets the `.frame` style class when the notebook * has a border (see gtk_notebook_set_show_border()). * - * The header node gets one of the style class .top, .bottom, - * .left or .right, depending on where the tabs are placed. For - * reorderable pages, the tab node gets the .reorderable-page class. + * The header node gets one of the style class `.top`, `.bottom`, + * `.left` or `.right`, depending on where the tabs are placed. For + * reorderable pages, the tab node gets the `.reorderable-page` class. * - * A tab node gets the .dnd style class while it is moved with drag-and-drop. + * A `tab` node gets the `.dnd` style class while it is moved with drag-and-drop. * * The nodes are always arranged from left-to-right, regardless of text direction. * * # Accessibility * - * GtkNotebook uses the #GTK_ACCESSIBLE_ROLE_TAB_LIST and - * #GTK_ACCESSIBLE_ROLE_TAB roles for its list of tabs and the - * #GTK_ACCESSIBLE_ROLE_TAB_PANEL for the pages. + * GtkNotebook uses the following roles: + * + * - %GTK_ACCESSIBLE_ROLE_GROUP for the notebook widget + * - %GTK_ACCESSIBLE_ROLE_TAB_LIST for the list of tabs + * - %GTK_ACCESSIBLE_ROLE_TAB role for each tab + * - %GTK_ACCESSIBLE_ROLE_TAB_PANEL for each page */ @@ -1369,6 +1372,7 @@ gtk_notebook_class_init (GtkNotebookClass *class) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("notebook")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index afa250c846..09624cdc02 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -142,6 +142,10 @@ * * If both scrollbars are visible, the area where they meet is drawn * with a subnode named junction. + * + * # Accessibility + * + * GtkScrolledWindow uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ @@ -868,6 +872,7 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class) add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD); gtk_widget_class_set_css_name (widget_class, I_("scrolledwindow")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static gboolean diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index bb6ecfeb12..3d70475ff9 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -274,6 +274,8 @@ gtk_stack_page_finalize (GObject *object) g_object_remove_weak_pointer (G_OBJECT (page->last_focus), (gpointer *)&page->last_focus); + g_clear_object (&page->at_context); + G_OBJECT_CLASS (gtk_stack_page_parent_class)->finalize (object); } diff --git a/gtk/gtktestatcontext.c b/gtk/gtktestatcontext.c index c8bd222d1a..27ad90da67 100644 --- a/gtk/gtktestatcontext.c +++ b/gtk/gtktestatcontext.c @@ -118,15 +118,9 @@ gboolean gtk_test_accessible_has_role (GtkAccessible *accessible, GtkAccessibleRole role) { - GtkATContext *context; - g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), FALSE); - context = gtk_accessible_get_at_context (accessible); - if (context == NULL) - return FALSE; - - return gtk_at_context_get_accessible_role (context) == role; + return gtk_accessible_get_accessible_role (accessible) == role; } gboolean diff --git a/gtk/gtkviewport.c b/gtk/gtkviewport.c index 43df2cddae..42bf850864 100644 --- a/gtk/gtkviewport.c +++ b/gtk/gtkviewport.c @@ -53,7 +53,11 @@ * * # CSS nodes * - * GtkViewport has a single CSS node with name viewport. + * GtkViewport has a single CSS node with name `viewport`. + * + * # Accessibility + * + * GtkViewport uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ typedef struct _GtkViewportPrivate GtkViewportPrivate; @@ -382,6 +386,7 @@ gtk_viewport_class_init (GtkViewportClass *class) GTK_PARAM_READWRITE)); gtk_widget_class_set_css_name (widget_class, I_("viewport")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 8d6e4067d5..861d87a0cf 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -771,8 +771,6 @@ gtk_widget_base_class_init (gpointer g_class) g_object_unref (shortcut); } } - - priv->accessible_role = GTK_ACCESSIBLE_ROLE_WIDGET; } static void @@ -1624,6 +1622,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__INT_INT_BOOLEAN_OBJECTv); gtk_widget_class_set_css_name (klass, I_("widget")); + gtk_widget_class_set_accessible_role (klass, GTK_ACCESSIBLE_ROLE_WIDGET); } static void @@ -2300,6 +2299,34 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class) gtk_event_controller_set_name (controller, "gtk-widget-class-shortcuts"); gtk_widget_add_controller (widget, controller); } + + priv->at_context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget)); +} + +static void +gtk_widget_realize_at_context (GtkWidget *self) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self); + GtkAccessibleRole role = priv->accessible_role; + + if (gtk_at_context_is_realized (priv->at_context)) + return; + + /* Realize the root ATContext first */ + if (!GTK_IS_ROOT (self)) + gtk_widget_realize_at_context (GTK_WIDGET (priv->root)); + + /* Reset the accessible role to its current value */ + if (role == GTK_ACCESSIBLE_ROLE_WIDGET) + { + GtkWidgetClassPrivate *class_priv = GTK_WIDGET_GET_CLASS (self)->priv; + + role = class_priv->accessible_role; + } + + gtk_at_context_set_accessible_role (priv->at_context, role); + gtk_at_context_set_display (priv->at_context, gtk_root_get_display (priv->root)); + gtk_at_context_realize (priv->at_context); } void @@ -2330,15 +2357,7 @@ gtk_widget_root (GtkWidget *widget) if (priv->layout_manager) gtk_layout_manager_set_root (priv->layout_manager, priv->root); - if (priv->at_context != NULL) - { - GtkATContext *root_context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (priv->root)); - - if (root_context) - gtk_at_context_realize (root_context); - - gtk_at_context_realize (priv->at_context); - } + gtk_widget_realize_at_context (widget); GTK_WIDGET_GET_CLASS (widget)->root (widget); @@ -2364,8 +2383,8 @@ gtk_widget_unroot (GtkWidget *widget) GTK_WIDGET_GET_CLASS (widget)->unroot (widget); - if (priv->at_context) - gtk_at_context_unrealize (priv->at_context); + gtk_at_context_set_display (priv->at_context, gdk_display_get_default ()); + gtk_at_context_unrealize (priv->at_context); if (priv->context) gtk_style_context_set_display (priv->context, gdk_display_get_default ()); @@ -7028,6 +7047,7 @@ gtk_widget_dispose (GObject *object) GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); GSList *sizegroups; GtkActionMuxer *muxer; + GtkATContext *at_context; muxer = g_object_get_qdata (G_OBJECT (widget), quark_action_muxer); if (muxer != NULL) @@ -7048,8 +7068,6 @@ gtk_widget_dispose (GObject *object) gtk_layout_manager_set_widget (priv->layout_manager, NULL); g_clear_object (&priv->layout_manager); - g_clear_object (&priv->at_context); - priv->visible = FALSE; if (_gtk_widget_get_realized (widget)) gtk_widget_unrealize (widget); @@ -7074,6 +7092,10 @@ gtk_widget_dispose (GObject *object) gtk_size_group_remove_widget (size_group, widget); } + at_context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget)); + if (at_context != NULL) + gtk_at_context_unrealize (at_context); + g_object_set_qdata (object, quark_action_muxer, NULL); G_OBJECT_CLASS (gtk_widget_parent_class)->dispose (object); @@ -7235,6 +7257,7 @@ gtk_widget_finalize (GObject *object) g_object_unref (priv->cssnode); g_clear_object (&priv->context); + g_clear_object (&priv->at_context); _gtk_size_request_cache_free (&priv->requests); @@ -8103,33 +8126,36 @@ gtk_widget_accessible_get_at_context (GtkAccessible *accessible) { GtkWidget *self = GTK_WIDGET (accessible); GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self); + GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (self); + GtkWidgetClassPrivate *class_priv = widget_class->priv; + GtkAccessibleRole role; if (priv->in_destruction) - return NULL; - - if (priv->at_context == NULL) { - GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (self); - GtkWidgetClassPrivate *class_priv = widget_class->priv; - GdkDisplay *display = _gtk_widget_get_display (self); - GtkAccessibleRole role; + GTK_NOTE (A11Y, g_message ("ATContext for widget “%s” [%p] accessed during destruction", + G_OBJECT_TYPE_NAME (self), + self)); + return NULL; + } - /* Widgets have two options to set the accessible role: either they - * define it in their class_init() function, and the role applies to - * all instances; or an instance is created with the :accessible-role - * property (from GtkAccessible) set to anything other than the default - * GTK_ACCESSIBLE_ROLE_WIDGET value. - * - * In either case, the accessible role cannot be set post-construction. - */ - if (priv->accessible_role != GTK_ACCESSIBLE_ROLE_WIDGET) - role = priv->accessible_role; - else - role = class_priv->accessible_role; + if (priv->at_context != NULL) + return priv->at_context; - priv->accessible_role = role; - priv->at_context = gtk_at_context_create (role, accessible, display); - } + /* Widgets have two options to set the accessible role: either they + * define it in their class_init() function, and the role applies to + * all instances; or an instance is created with the :accessible-role + * property (from GtkAccessible) set to anything other than the default + * GTK_ACCESSIBLE_ROLE_WIDGET value. + * + * In either case, the accessible role cannot be set post-construction. + */ + if (priv->accessible_role != GTK_ACCESSIBLE_ROLE_WIDGET) + role = priv->accessible_role; + else + role = class_priv->accessible_role; + + priv->accessible_role = role; + priv->at_context = gtk_at_context_create (role, accessible, gdk_display_get_default ()); return priv->at_context; } @@ -12559,7 +12585,7 @@ gtk_widget_set_accessible_role (GtkWidget *self, priv->accessible_role = role; if (priv->at_context != NULL) - g_object_set (priv->at_context, "accessible-role", priv->accessible_role, NULL); + gtk_at_context_set_accessible_role (priv->at_context, role); g_object_notify (G_OBJECT (self), "accessible-role"); } @@ -12579,16 +12605,17 @@ gtk_widget_get_accessible_role (GtkWidget *self) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self); GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (self)); + GtkWidgetClassPrivate *class_priv; - if (context == NULL || !gtk_at_context_is_realized (context)) - { - if (priv->accessible_role == GTK_ACCESSIBLE_ROLE_WIDGET) - return gtk_widget_class_get_accessible_role (GTK_WIDGET_GET_CLASS (self)); + if (context != NULL && gtk_at_context_is_realized (context)) + return gtk_at_context_get_accessible_role (context); - return priv->accessible_role; - } + if (priv->accessible_role != GTK_ACCESSIBLE_ROLE_WIDGET) + return priv->accessible_role; + + class_priv = GTK_WIDGET_GET_CLASS (self)->priv; - return gtk_at_context_get_accessible_role (context); + return class_priv->accessible_role; } /** diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 6e8f289d0b..085e54b788 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -142,7 +142,7 @@ * * # Accessibility * - * GtkWindow uses the #GTK_ACCESSIBLE_ROLE_WINDOW role. + * GtkWindow uses the %GTK_ACCESSIBLE_ROLE_WINDOW role. */ #define MENU_BAR_ACCEL GDK_KEY_F10 @@ -1965,6 +1965,10 @@ gtk_window_set_title (GtkWindow *window, if (_gtk_widget_get_realized (GTK_WIDGET (window))) gdk_toplevel_set_title (GDK_TOPLEVEL (priv->surface), new_title != NULL ? new_title : ""); + gtk_accessible_update_property (GTK_ACCESSIBLE (window), + GTK_ACCESSIBLE_PROPERTY_LABEL, priv->title, + -1); + g_object_notify_by_pspec (G_OBJECT (window), window_props[PROP_TITLE]); } diff --git a/gtk/gtkwindowcontrols.c b/gtk/gtkwindowcontrols.c index d2b3667555..553cbbdf0f 100644 --- a/gtk/gtkwindowcontrols.c +++ b/gtk/gtkwindowcontrols.c @@ -79,6 +79,10 @@ * and #GtkWindowControls:decoration-layout value. * * When #GtkWindowControls:empty is %TRUE, it gets the .empty style class. + * + * # Accessibility + * + * GtkWindowHandle uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ struct _GtkWindowControls { @@ -538,6 +542,7 @@ gtk_window_controls_class_init (GtkWindowControlsClass *klass) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("windowcontrols")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void diff --git a/gtk/gtkwindowhandle.c b/gtk/gtkwindowhandle.c index 18919becac..5a6beeb273 100644 --- a/gtk/gtkwindowhandle.c +++ b/gtk/gtkwindowhandle.c @@ -48,7 +48,11 @@ * * # CSS nodes * - * #GtkWindowHandle has a single CSS node with the name windowhandle. + * #GtkWindowHandle has a single CSS node with the name `windowhandle`. + * + * # Accessibility + * + * GtkWindowHandle uses the %GTK_ACCESSIBLE_ROLE_GROUP role. */ struct _GtkWindowHandle { @@ -550,6 +554,7 @@ gtk_window_handle_class_init (GtkWindowHandleClass *klass) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("windowhandle")); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP); } static void |