diff --git a/gtk/a11y/gtkatspicontext.c b/gtk/a11y/gtkatspicontext.c index d694163a5d..9c61b86526 100644 --- a/gtk/a11y/gtkatspicontext.c +++ b/gtk/a11y/gtkatspicontext.c @@ -1531,6 +1531,42 @@ gtk_at_spi_context_unrealize (GtkATContext *context) g_clear_object (&self->root); } +static void +gtk_at_spi_context_announce (GtkATContext *context, + const char *message, + GtkAccessibleAnnouncementPriority priority) +{ + GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context); + AtspiLive live; + + if (self->connection == NULL) + return; + + switch (priority) + { + case GTK_ACCESSIBLE_ANNOUNCEMENT_PRIORITY_LOW: + case GTK_ACCESSIBLE_ANNOUNCEMENT_PRIORITY_MEDIUM: + live = ATSPI_LIVE_POLITE; + break; + case GTK_ACCESSIBLE_ANNOUNCEMENT_PRIORITY_HIGH: + live = ATSPI_LIVE_ASSERTIVE; + break; + default: + g_assert_not_reached (); + } + + g_dbus_connection_emit_signal (self->connection, + NULL, + self->context_path, + "org.a11y.atspi.Event.Object", + "Announcement", + g_variant_new ("(siiva{sv})", + "", live, 0, + g_variant_new_string (message), + NULL), + NULL); +} + static void gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass) { @@ -1545,6 +1581,7 @@ gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass) 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; + context_class->announce = gtk_at_spi_context_announce; } static void diff --git a/gtk/a11y/gtkatspiprivate.h b/gtk/a11y/gtkatspiprivate.h index cdde64dd83..2e869ca4d1 100644 --- a/gtk/a11y/gtkatspiprivate.h +++ b/gtk/a11y/gtkatspiprivate.h @@ -277,6 +277,11 @@ typedef enum { ATSPI_SCROLL_ANYWHERE } AtspiScrollType; +typedef enum { + ATSPI_LIVE_POLITE = 1, + ATSPI_LIVE_ASSERTIVE = 2 +} AtspiLive; + typedef struct _GtkAtSpiRoot GtkAtSpiRoot; typedef struct _GtkAtSpiCache GtkAtSpiCache; typedef struct _GtkAtSpiContext GtkAtSpiContext; diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index 4c58c69032..2bac2ce960 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -751,6 +751,41 @@ gtk_accessible_reset_relation (GtkAccessible *self, g_object_unref (context); } +/** + * gtk_accessible_announce: + * @self: a `GtkAccessible` + * @message: the string to announce + * @priority: the priority of the announcement + * + * Requests the user's screen reader to announce the given message. + * + * This kind of notification is useful for messages that + * either have only a visual representation or that are not + * exposed visually at all, e.g. a notification about a + * successful operation. + * + * Also, by using this API, you can ensure that the message + * does not interrupts the user's current screen reader output. + * + * Since: 4.14 + */ +void +gtk_accessible_announce (GtkAccessible *self, + const char *message, + GtkAccessibleAnnouncementPriority priority) +{ + GtkATContext *context; + + g_return_if_fail (GTK_IS_ACCESSIBLE (self)); + + context = gtk_accessible_get_at_context (self); + if (context == NULL) + return; + + gtk_at_context_announce (context, message, priority); + g_object_unref (context); +} + static const char *role_names[] = { [GTK_ACCESSIBLE_ROLE_ALERT] = NC_("accessibility", "alert"), [GTK_ACCESSIBLE_ROLE_ALERT_DIALOG] = NC_("accessibility", "alert dialog"), diff --git a/gtk/gtkaccessible.h b/gtk/gtkaccessible.h index 5327f48303..9826e95f11 100644 --- a/gtk/gtkaccessible.h +++ b/gtk/gtkaccessible.h @@ -260,4 +260,9 @@ GDK_AVAILABLE_IN_4_14 GtkAccessibleList * gtk_accessible_list_new_from_array (GtkAccessible **accessibles, gsize n_accessibles); +GDK_AVAILABLE_IN_4_14 +void gtk_accessible_announce (GtkAccessible *self, + const char *message, + GtkAccessibleAnnouncementPriority priority); + G_END_DECLS diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c index b32ef858e9..f16e7490e4 100644 --- a/gtk/gtkatcontext.c +++ b/gtk/gtkatcontext.c @@ -1473,3 +1473,14 @@ gtk_at_context_child_changed (GtkATContext *self, GTK_AT_CONTEXT_GET_CLASS (self)->child_change (self, change, child); } + +void +gtk_at_context_announce (GtkATContext *self, + const char *message, + GtkAccessibleAnnouncementPriority priority) +{ + if (!self->realized) + return; + + GTK_AT_CONTEXT_GET_CLASS (self)->announce (self, message, priority); +} diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h index d9489fa3c9..e65b5f8279 100644 --- a/gtk/gtkatcontextprivate.h +++ b/gtk/gtkatcontextprivate.h @@ -127,6 +127,10 @@ struct _GtkATContextClass void (* realize) (GtkATContext *self); void (* unrealize) (GtkATContext *self); + + void (* announce) (GtkATContext *self, + const char *message, + GtkAccessibleAnnouncementPriority priority); }; GtkATContext * gtk_at_context_clone (GtkATContext *self, @@ -193,4 +197,8 @@ void gtk_at_context_set_next_accessible_sibling (GtkATContext *self, GtkAccessible *sibling); +void gtk_at_context_announce (GtkATContext *self, + const char *message, + GtkAccessibleAnnouncementPriority priority); + G_END_DECLS diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 52a734572e..2fe04619d0 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -1801,6 +1801,29 @@ typedef enum { /*< prefix=GTK_ACCESSIBLE_SORT >*/ GTK_ACCESSIBLE_SORT_OTHER } GtkAccessibleSort; +/** + * GTK_ANNOUNCEMENT_PRIORITY: + * @GTK_ANNOUNCEMENT_PRIORITY_LOW: The announcement is low priority, and might be read only + * on the user's request. + * @GTK_ANNOUNCEMENT_PRIORITY_MEDIUM: The announcement is of medium priority, and is usually + * spoken at the next opportunity, such as at the end of speaking the current sentence + * or when the user pauses typing. + * @GTK_ANNOUNCEMENT_PRIORITY_HIGH: The announcement is of high priority, and is usually + * spoken immediately. Because an interruption might disorient users or cause them to + * not complete their current task, authors SHOULD NOT use high priority announcements + * unless the interruption is imperative. An example would be a notification about a + * critical battery power level. + * + * The priority of an accessibility announcement. + * + * Since: 4.14 + */ +typedef enum { + GTK_ACCESSIBLE_ANNOUNCEMENT_PRIORITY_LOW, + GTK_ACCESSIBLE_ANNOUNCEMENT_PRIORITY_MEDIUM, + GTK_ACCESSIBLE_ANNOUNCEMENT_PRIORITY_HIGH +} GtkAccessibleAnnouncementPriority; + /** * GtkPopoverMenuFlags: * @GTK_POPOVER_MENU_SLIDING: Submenus are presented as sliding submenus that