Merge branch 'ebassi/for-master' into 'master'

ATContext lifetime fixes

Closes #3341

See merge request GNOME/gtk!2811
This commit is contained in:
Matthias Clasen 2020-11-12 12:35:23 +00:00
commit 5d9799d4e3
23 changed files with 336 additions and 156 deletions

View File

@ -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)
{

View File

@ -46,9 +46,10 @@
#include "gtkeditable.h"
#include "gtkentryprivate.h"
#include "gtkroot.h"
#include "gtktextview.h"
#include "gtkwindow.h"
#include "gtkstack.h"
#include "gtktextview.h"
#include "gtktypebuiltins.h"
#include "gtkwindow.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

View File

@ -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

View File

@ -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;

View File

@ -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);
return gtk_at_context_get_accessible_role (context);
g_object_get (G_OBJECT (self), "accessible-role", &role, NULL);
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;

View File

@ -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

View File

@ -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
@ -430,6 +454,34 @@ gtk_at_context_get_accessible_role (GtkATContext *self)
return self->accessible_role;
}
/*< 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));
}

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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);
gtk_at_context_set_accessible_role (context, role);
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);
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);
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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,34 +8126,37 @@ 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;
/* 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, display);
GTK_NOTE (A11Y, g_message ("ATContext for widget “%s” [%p] accessed during destruction",
G_OBJECT_TYPE_NAME (self),
self));
return NULL;
}
if (priv->at_context != NULL)
return priv->at_context;
/* 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;
return gtk_at_context_get_accessible_role (context);
class_priv = GTK_WIDGET_GET_CLASS (self)->priv;
return class_priv->accessible_role;
}
/**

View File

@ -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]);
}

View File

@ -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

View File

@ -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