[gnome-shell] StWidgetAccessible: accessibility support for StWidget



commit 125adb4d3215b29bbeea84cecbf1842f0423d063
Author: Alejandro Piñeiro <apinheiro igalia com>
Date:   Tue Jan 4 13:14:15 2011 +0100

    StWidgetAccessible: accessibility support for StWidget
    
    It includes:
    
     * Expose a proper focusable state, instead of the default one from
       cally, using StWidget::can_focus, and also notifying the state change
    
     * Management of the selected stated, using the current pseudo_class.
    
     * Defines a new virtual method on StWidget: get_accessible_type. In
       this way it is not required to reimplement get_accessible just for
       a accessible type change. get_accessible is reimplemented using this.
       You can see that as a substitute of the atk object factory
    
    https://bugzilla.gnome.org/show_bug.cgi?id=636716

 src/Makefile-st.am            |    1 +
 src/st/st-widget-accessible.h |   76 +++++++++++++++
 src/st/st-widget.c            |  205 +++++++++++++++++++++++++++++++++++++++++
 src/st/st-widget.h            |    2 +
 4 files changed, 284 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index e952a4a..225ff5b 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -98,6 +98,7 @@ st_source_h =					\
 	st/st-tooltip.h				\
 	st/st-types.h				\
 	st/st-widget.h				\
+	st/st-widget-accessible.h		\
 	$(NULL)
 
 st.h: stamp-st.h
diff --git a/src/st/st-widget-accessible.h b/src/st/st-widget-accessible.h
new file mode 100644
index 0000000..c60f778
--- /dev/null
+++ b/src/st/st-widget-accessible.h
@@ -0,0 +1,76 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-widget-accessible.h: Accessible object for StWidget
+ *
+ * Copyright 2010 Igalia, S.L.
+ * Author: Alejandro Piñeiro Iglesias <apinheiro igalia com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
+#error "Only <st/st.h> can be included directly.h"
+#endif
+
+#ifndef __ST_WIDGET_ACCESSIBLE_H__
+#define __ST_WIDGET_ACCESSIBLE_H__
+
+G_BEGIN_DECLS
+
+#include <st/st-widget.h>
+#include <cally/cally.h>
+
+#define ST_TYPE_WIDGET_ACCESSIBLE st_widget_accessible_get_type ()
+
+#define ST_WIDGET_ACCESSIBLE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+  ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessible))
+
+#define ST_IS_WIDGET_ACCESSIBLE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+  ST_TYPE_WIDGET_ACCESSIBLE))
+
+#define ST_WIDGET_ACCESSIBLE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+  ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessibleClass))
+
+#define ST_IS_WIDGET_ACCESSIBLE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+  ST_TYPE_WIDGET_ACCESSIBLE))
+
+#define ST_WIDGET_ACCESSIBLE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+  ST_TYPE_WIDGET_ACCESSIBLE, StWidgetAccessibleClass))
+
+typedef struct _StWidgetAccessible  StWidgetAccessible;
+typedef struct _StWidgetAccessibleClass  StWidgetAccessibleClass;
+typedef struct _StWidgetAccessiblePrivate  StWidgetAccessiblePrivate;
+
+struct _StWidgetAccessible
+{
+  CallyActor parent;
+
+  /*< private >*/
+  StWidgetAccessiblePrivate *priv;
+};
+
+struct _StWidgetAccessibleClass
+{
+  CallyActorClass parent_class;
+};
+
+GType st_widget_accessible_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __ST_WIDGET_ACCESSIBLE_H__ */
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 23b591d..8ebe1dd 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -42,6 +42,8 @@
 #include "st-tooltip.h"
 #include "st-theme-node-transition.h"
 
+#include "st-widget-accessible.h"
+
 /*
  * Forward declaration for sake of StWidgetChild
  */
@@ -67,6 +69,8 @@ struct _StWidgetPrivate
   StTooltip    *tooltip;
 
   StTextDirection   direction;
+
+  AtkObject *accessible;
 };
 
 /**
@@ -117,6 +121,8 @@ static gboolean st_widget_real_navigate_focus (StWidget         *widget,
                                                ClutterActor     *from,
                                                GtkDirectionType  direction);
 
+static AtkObject * st_widget_get_accessible (ClutterActor *actor);
+
 static void
 st_widget_set_property (GObject      *gobject,
                         guint         prop_id,
@@ -281,6 +287,12 @@ st_widget_dispose (GObject *gobject)
       priv->tooltip = NULL;
     }
 
+  /* The real dispose of this accessible is done on
+   * AtkGObjectAccessible weak ref callback
+   */
+  if (priv->accessible)
+    priv->accessible = NULL;
+
   G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject);
 }
 
@@ -725,8 +737,11 @@ st_widget_class_init (StWidgetClass *klass)
   actor_class->key_focus_out = st_widget_key_focus_out;
   actor_class->hide = st_widget_hide;
 
+  actor_class->get_accessible = st_widget_get_accessible;
+
   klass->style_changed = st_widget_real_style_changed;
   klass->navigate_focus = st_widget_real_navigate_focus;
+  klass->get_accessible_type = st_widget_accessible_get_type;
 
   /**
    * StWidget:pseudo-class:
@@ -1948,3 +1963,193 @@ st_get_slow_down_factor ()
 {
   return st_slow_down_factor;
 }
+
+/******************************************************************************/
+/*************************** ACCESSIBILITY SUPPORT ****************************/
+/******************************************************************************/
+
+/* GObject */
+
+static void st_widget_accessible_class_init (StWidgetAccessibleClass *klass);
+static void st_widget_accessible_init       (StWidgetAccessible *widget);
+
+/* AtkObject */
+static AtkStateSet *st_widget_accessible_ref_state_set (AtkObject *obj);
+static void         st_widget_accessible_initialize    (AtkObject *obj,
+                                                        gpointer   data);
+
+/* Private methods */
+static void on_pseudo_class_notify (GObject    *gobject,
+                                    GParamSpec *pspec,
+                                    gpointer    data);
+static void on_can_focus_notify    (GObject    *gobject,
+                                    GParamSpec *pspec,
+                                    gpointer    data);
+static void check_selected         (StWidgetAccessible *self,
+                                    StWidget *widget);
+
+G_DEFINE_TYPE (StWidgetAccessible, st_widget_accessible, CALLY_TYPE_ACTOR)
+
+#define ST_WIDGET_ACCESSIBLE_GET_PRIVATE(obj) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_WIDGET_ACCESSIBLE, \
+                                StWidgetAccessiblePrivate))
+
+struct _StWidgetAccessiblePrivate
+{
+  /* Cached values (used to avoid extra notifications) */
+  gboolean selected;
+};
+
+
+static AtkObject *
+st_widget_get_accessible (ClutterActor *actor)
+{
+  StWidget *widget = NULL;
+
+  g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
+
+  widget = ST_WIDGET (actor);
+
+  if (widget->priv->accessible == NULL)
+    {
+      widget->priv->accessible =
+        g_object_new (ST_WIDGET_GET_CLASS (widget)->get_accessible_type (),
+                      NULL);
+
+      atk_object_initialize (widget->priv->accessible, actor);
+    }
+
+  return widget->priv->accessible;
+}
+
+
+static void
+st_widget_accessible_class_init (StWidgetAccessibleClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+  atk_class->ref_state_set = st_widget_accessible_ref_state_set;
+  atk_class->initialize = st_widget_accessible_initialize;
+
+  g_type_class_add_private (gobject_class, sizeof (StWidgetAccessiblePrivate));
+}
+
+static void
+st_widget_accessible_init (StWidgetAccessible *self)
+{
+  StWidgetAccessiblePrivate *priv = ST_WIDGET_ACCESSIBLE_GET_PRIVATE (self);
+
+  self->priv = priv;
+}
+
+static void
+st_widget_accessible_initialize (AtkObject *obj,
+                                 gpointer   data)
+{
+  ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->initialize (obj, data);
+
+  g_signal_connect (data, "notify::pseudo-class",
+                    G_CALLBACK (on_pseudo_class_notify),
+                    obj);
+
+  g_signal_connect (data, "notify::can-focus",
+                    G_CALLBACK (on_can_focus_notify),
+                    obj);
+
+  /* Check the cached selected state and notify the first selection.
+   * Ie: it is required to ensure a first notification when Alt+Tab
+   * popup appears
+   */
+  check_selected (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data));
+}
+
+static AtkStateSet *
+st_widget_accessible_ref_state_set (AtkObject *obj)
+{
+  AtkStateSet *result = NULL;
+  ClutterActor *actor = NULL;
+  StWidget *widget = NULL;
+  StWidgetAccessible *self = NULL;
+
+  result = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->ref_state_set (obj);
+
+  actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
+
+  if (actor == NULL) /* State is defunct */
+    return result;
+
+  widget = ST_WIDGET (actor);
+  self = ST_WIDGET_ACCESSIBLE (obj);
+
+  /* priv->selected should be properly updated on the
+   * ATK_STATE_SELECTED notification callbacks
+   */
+  if (self->priv->selected)
+    atk_state_set_add_state (result, ATK_STATE_SELECTED);
+
+  /* On clutter there isn't any tip to know if a actor is focusable or
+   * not, anyone can receive the key_focus. For this reason
+   * cally_actor sets any actor as FOCUSABLE. This is not the case on
+   * St, where we have can_focus. But this means that we need to
+   * remove the state FOCUSABLE if it is not focusable
+   */
+  if (st_widget_get_can_focus (widget))
+    atk_state_set_add_state (result, ATK_STATE_FOCUSABLE);
+  else
+    atk_state_set_remove_state (result, ATK_STATE_FOCUSABLE);
+
+  return result;
+}
+
+static void
+on_pseudo_class_notify (GObject    *gobject,
+                        GParamSpec *pspec,
+                        gpointer    data)
+{
+  check_selected (ST_WIDGET_ACCESSIBLE (data),
+                  ST_WIDGET (gobject));
+}
+
+/*
+ * This method checks if the widget is selected, and notify a atk
+ * state change if required
+ *
+ * In order to decide if there was a selection, we use the current
+ * pseudo-class of the widget searching for "selected", the current
+ * homogeneus way to check if a item is selected (see bug 637830)
+ *
+ * In a ideal world we would have a more standard way to check if the
+ * item is selected or not, like the widget-context (as in the case of
+ * gtktreeview-cells), or something like the property "can-focus". But
+ * for the moment this is enough, and we can update that in the future
+ * if required.
+ */
+static void
+check_selected (StWidgetAccessible *self,
+                StWidget *widget)
+{
+  gboolean found = FALSE;
+
+  found = st_widget_has_style_pseudo_class (widget,
+                                            "selected");
+
+  if (found != self->priv->selected)
+    {
+      self->priv->selected = found;
+      atk_object_notify_state_change (ATK_OBJECT (self),
+                                      ATK_STATE_SELECTED,
+                                      found);
+    }
+}
+
+static void
+on_can_focus_notify (GObject    *gobject,
+                     GParamSpec *pspec,
+                     gpointer    data)
+{
+  gboolean can_focus = st_widget_get_can_focus (ST_WIDGET (gobject));
+
+  atk_object_notify_state_change (ATK_OBJECT (data),
+                                  ATK_STATE_FOCUSABLE, can_focus);
+}
diff --git a/src/st/st-widget.h b/src/st/st-widget.h
index cc3b47c..8945436 100644
--- a/src/st/st-widget.h
+++ b/src/st/st-widget.h
@@ -82,6 +82,8 @@ struct _StWidgetClass
   gboolean (* navigate_focus)  (StWidget         *self,
                                 ClutterActor     *from,
                                 GtkDirectionType  direction);
+
+  GType    (*get_accessible_type)   (void);
 };
 
 GType st_widget_get_type (void) G_GNUC_CONST;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]