a11y: Redo TextView cursor/selection signal handling
As part of the removal of idles, redo how we emit signals on the accessible. Should work as good or better than before, but with a lot less code.
This commit is contained in:
		@ -40,13 +40,10 @@ static void       delete_range_cb      (GtkTextBuffer    *buffer,
 | 
			
		||||
                                                        GtkTextIter      *arg1,
 | 
			
		||||
                                                        GtkTextIter      *arg2,
 | 
			
		||||
                                                        gpointer         user_data);
 | 
			
		||||
static void       changed_cb           (GtkTextBuffer    *buffer,
 | 
			
		||||
                                                        gpointer         user_data);
 | 
			
		||||
static void       mark_set_cb          (GtkTextBuffer    *buffer,
 | 
			
		||||
                                                        GtkTextIter      *arg1,
 | 
			
		||||
                                                        GtkTextMark      *arg2,
 | 
			
		||||
                                                        gpointer         user_data);
 | 
			
		||||
static gint             insert_idle_handler            (gpointer         data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void atk_editable_text_interface_init      (AtkEditableTextIface      *iface);
 | 
			
		||||
@ -70,17 +67,6 @@ gtk_text_view_accessible_initialize (AtkObject *obj,
 | 
			
		||||
  obj->role = ATK_ROLE_TEXT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
gtk_text_view_accessible_finalize (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
  GtkTextViewAccessible *text_view = GTK_TEXT_VIEW_ACCESSIBLE (object);
 | 
			
		||||
 | 
			
		||||
  if (text_view->insert_notify_handler)
 | 
			
		||||
    g_source_remove (text_view->insert_notify_handler);
 | 
			
		||||
 | 
			
		||||
  G_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->finalize (object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
gtk_text_view_accessible_notify_gtk (GObject    *obj,
 | 
			
		||||
                                     GParamSpec *pspec)
 | 
			
		||||
@ -126,12 +112,9 @@ gtk_text_view_accessible_ref_state_set (AtkObject *accessible)
 | 
			
		||||
static void
 | 
			
		||||
gtk_text_view_accessible_class_init (GtkTextViewAccessibleClass *klass)
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | 
			
		||||
  AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
 | 
			
		||||
  GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
 | 
			
		||||
 | 
			
		||||
  gobject_class->finalize = gtk_text_view_accessible_finalize;
 | 
			
		||||
 | 
			
		||||
  class->ref_state_set = gtk_text_view_accessible_ref_state_set;
 | 
			
		||||
  class->initialize = gtk_text_view_accessible_initialize;
 | 
			
		||||
 | 
			
		||||
@ -141,10 +124,6 @@ gtk_text_view_accessible_class_init (GtkTextViewAccessibleClass *klass)
 | 
			
		||||
static void
 | 
			
		||||
gtk_text_view_accessible_init (GtkTextViewAccessible *accessible)
 | 
			
		||||
{
 | 
			
		||||
  accessible->signal_name = NULL;
 | 
			
		||||
  accessible->previous_insert_offset = -1;
 | 
			
		||||
  accessible->previous_selection_bound = -1;
 | 
			
		||||
  accessible->insert_notify_handler = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -156,10 +135,9 @@ setup_buffer (GtkTextView           *view,
 | 
			
		||||
  buffer = gtk_text_view_get_buffer (view);
 | 
			
		||||
 | 
			
		||||
  /* Set up signal callbacks */
 | 
			
		||||
  g_signal_connect (buffer, "insert-text", G_CALLBACK (insert_text_cb), view);
 | 
			
		||||
  g_signal_connect_after (buffer, "insert-text", G_CALLBACK (insert_text_cb), view);
 | 
			
		||||
  g_signal_connect (buffer, "delete-range", G_CALLBACK (delete_range_cb), view);
 | 
			
		||||
  g_signal_connect (buffer, "mark-set", G_CALLBACK (mark_set_cb), view);
 | 
			
		||||
  g_signal_connect (buffer, "changed", G_CALLBACK (changed_cb), view);
 | 
			
		||||
  g_signal_connect_after (buffer, "mark-set", G_CALLBACK (mark_set_cb), view);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gchar *
 | 
			
		||||
@ -1162,6 +1140,35 @@ atk_editable_text_interface_init (AtkEditableTextIface *iface)
 | 
			
		||||
 | 
			
		||||
/* Callbacks */
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
gtk_text_view_accessible_update_cursor (GtkTextViewAccessible *accessible,
 | 
			
		||||
                                        GtkTextBuffer *        buffer)
 | 
			
		||||
{
 | 
			
		||||
  int prev_insert_offset, prev_selection_bound;
 | 
			
		||||
  int insert_offset, selection_bound;
 | 
			
		||||
  GtkTextIter iter;
 | 
			
		||||
 | 
			
		||||
  prev_insert_offset = accessible->insert_offset;
 | 
			
		||||
  prev_selection_bound = accessible->selection_bound;
 | 
			
		||||
 | 
			
		||||
  gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
 | 
			
		||||
  insert_offset = gtk_text_iter_get_offset (&iter);
 | 
			
		||||
  gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_selection_bound (buffer));
 | 
			
		||||
  selection_bound = gtk_text_iter_get_offset (&iter);
 | 
			
		||||
 | 
			
		||||
  if (prev_insert_offset == insert_offset && prev_selection_bound == selection_bound)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  accessible->insert_offset = insert_offset;
 | 
			
		||||
  accessible->selection_bound = selection_bound;
 | 
			
		||||
 | 
			
		||||
  if (prev_insert_offset != insert_offset)
 | 
			
		||||
    g_signal_emit_by_name (accessible, "text-caret-moved", insert_offset);
 | 
			
		||||
 | 
			
		||||
  if (prev_insert_offset != prev_selection_bound || insert_offset != selection_bound)
 | 
			
		||||
    g_signal_emit_by_name (accessible, "text-selection-changed");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
insert_text_cb (GtkTextBuffer *buffer,
 | 
			
		||||
                GtkTextIter   *iter,
 | 
			
		||||
@ -1176,33 +1183,12 @@ insert_text_cb (GtkTextBuffer *buffer,
 | 
			
		||||
 | 
			
		||||
  accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (view)));
 | 
			
		||||
 | 
			
		||||
  accessible->signal_name = "text_changed::insert";
 | 
			
		||||
  position = gtk_text_iter_get_offset (iter);
 | 
			
		||||
  length = g_utf8_strlen (text, len);
 | 
			
		||||
 
 | 
			
		||||
  if (accessible->length == 0)
 | 
			
		||||
    {
 | 
			
		||||
      accessible->position = position;
 | 
			
		||||
      accessible->length = length;
 | 
			
		||||
    }
 | 
			
		||||
  else if (accessible->position + accessible->length == position)
 | 
			
		||||
    {
 | 
			
		||||
      accessible->length += length;
 | 
			
		||||
    }
 | 
			
		||||
  else
 | 
			
		||||
    {
 | 
			
		||||
      /*
 | 
			
		||||
       * We have a non-contiguous insert so report what we have
 | 
			
		||||
       */
 | 
			
		||||
      if (accessible->insert_notify_handler)
 | 
			
		||||
        {
 | 
			
		||||
          g_source_remove (accessible->insert_notify_handler);
 | 
			
		||||
        }
 | 
			
		||||
      accessible->insert_notify_handler = 0;
 | 
			
		||||
      insert_idle_handler (accessible);
 | 
			
		||||
      accessible->position = position;
 | 
			
		||||
      accessible->length = length;
 | 
			
		||||
    }
 | 
			
		||||
  g_signal_emit_by_name (accessible, "text-changed::insert", position - length, length);
 | 
			
		||||
 | 
			
		||||
  gtk_text_view_accessible_update_cursor (accessible, buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -1211,73 +1197,21 @@ delete_range_cb (GtkTextBuffer *buffer,
 | 
			
		||||
                 GtkTextIter   *end,
 | 
			
		||||
                 gpointer       data)
 | 
			
		||||
{
 | 
			
		||||
  GtkTextView *text = data;
 | 
			
		||||
  GtkTextView *view = data;
 | 
			
		||||
  GtkTextViewAccessible *accessible;
 | 
			
		||||
  gint offset;
 | 
			
		||||
  gint length;
 | 
			
		||||
  gint offset, length;
 | 
			
		||||
 | 
			
		||||
  accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (view)));
 | 
			
		||||
 | 
			
		||||
  offset = gtk_text_iter_get_offset (start);
 | 
			
		||||
  length = gtk_text_iter_get_offset (end) - offset;
 | 
			
		||||
 | 
			
		||||
  accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (text)));
 | 
			
		||||
  if (accessible->insert_notify_handler)
 | 
			
		||||
    {
 | 
			
		||||
      g_source_remove (accessible->insert_notify_handler);
 | 
			
		||||
      accessible->insert_notify_handler = 0;
 | 
			
		||||
      if (accessible->position == offset &&
 | 
			
		||||
          accessible->length == length)
 | 
			
		||||
        {
 | 
			
		||||
        /*
 | 
			
		||||
         * Do not bother with insert and delete notifications
 | 
			
		||||
         */
 | 
			
		||||
          accessible->signal_name = NULL;
 | 
			
		||||
          accessible->position = 0;
 | 
			
		||||
          accessible->length = 0;
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
  g_signal_emit_by_name (accessible,
 | 
			
		||||
                         "text_changed::delete",
 | 
			
		||||
                         offset,
 | 
			
		||||
                         length);
 | 
			
		||||
 | 
			
		||||
      insert_idle_handler (accessible);
 | 
			
		||||
    }
 | 
			
		||||
  g_signal_emit_by_name (accessible, "text_changed::delete", offset, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gint
 | 
			
		||||
get_selection_bound (GtkTextBuffer *buffer)
 | 
			
		||||
{
 | 
			
		||||
  GtkTextMark *bound;
 | 
			
		||||
  GtkTextIter iter;
 | 
			
		||||
 | 
			
		||||
  bound = gtk_text_buffer_get_selection_bound (buffer);
 | 
			
		||||
  gtk_text_buffer_get_iter_at_mark (buffer, &iter, bound);
 | 
			
		||||
  return gtk_text_iter_get_offset (&iter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
emit_text_caret_moved (GtkTextViewAccessible *accessible,
 | 
			
		||||
                       gint                   offset)
 | 
			
		||||
{
 | 
			
		||||
  /*
 | 
			
		||||
   * If we have text which has been inserted notify the user
 | 
			
		||||
   */
 | 
			
		||||
  if (accessible->insert_notify_handler)
 | 
			
		||||
    {
 | 
			
		||||
      g_source_remove (accessible->insert_notify_handler);
 | 
			
		||||
      accessible->insert_notify_handler = 0;
 | 
			
		||||
      insert_idle_handler (accessible);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (offset != accessible->previous_insert_offset)
 | 
			
		||||
    {
 | 
			
		||||
      /*
 | 
			
		||||
       * If the caret position has not changed then don't bother notifying
 | 
			
		||||
       *
 | 
			
		||||
       * When mouse click is used to change caret position, notification
 | 
			
		||||
       * is received on button down and button up.
 | 
			
		||||
       */
 | 
			
		||||
      g_signal_emit_by_name (accessible, "text_caret_moved", offset);
 | 
			
		||||
      accessible->previous_insert_offset = offset;
 | 
			
		||||
    }
 | 
			
		||||
  gtk_text_view_accessible_update_cursor (accessible, buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -1297,84 +1231,12 @@ mark_set_cb (GtkTextBuffer *buffer,
 | 
			
		||||
   */
 | 
			
		||||
  if (mark == gtk_text_buffer_get_insert (buffer))
 | 
			
		||||
    {
 | 
			
		||||
      gint insert_offset, selection_bound;
 | 
			
		||||
      gboolean selection_changed;
 | 
			
		||||
 | 
			
		||||
      insert_offset = gtk_text_iter_get_offset (location);
 | 
			
		||||
 | 
			
		||||
      selection_bound = get_selection_bound (buffer);
 | 
			
		||||
      if (selection_bound != insert_offset)
 | 
			
		||||
        {
 | 
			
		||||
          if (selection_bound != accessible->previous_selection_bound ||
 | 
			
		||||
              insert_offset != accessible->previous_insert_offset)
 | 
			
		||||
            selection_changed = TRUE;
 | 
			
		||||
          else
 | 
			
		||||
            selection_changed = FALSE;
 | 
			
		||||
        }
 | 
			
		||||
      else if (accessible->previous_selection_bound != accessible->previous_insert_offset)
 | 
			
		||||
        selection_changed = TRUE;
 | 
			
		||||
      else
 | 
			
		||||
        selection_changed = FALSE;
 | 
			
		||||
 | 
			
		||||
      emit_text_caret_moved (accessible, insert_offset);
 | 
			
		||||
      /*
 | 
			
		||||
       * insert and selection_bound marks are different to a selection
 | 
			
		||||
       * has changed
 | 
			
		||||
       */
 | 
			
		||||
      if (selection_changed)
 | 
			
		||||
        g_signal_emit_by_name (accessible, "text_selection_changed");
 | 
			
		||||
      accessible->previous_selection_bound = selection_bound;
 | 
			
		||||
      gtk_text_view_accessible_update_cursor (accessible, buffer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
changed_cb (GtkTextBuffer *buffer,
 | 
			
		||||
            gpointer       data)
 | 
			
		||||
{
 | 
			
		||||
  GtkTextView *text = data;
 | 
			
		||||
  GtkTextViewAccessible *accessible;
 | 
			
		||||
 | 
			
		||||
  accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (text)));
 | 
			
		||||
  if (accessible->signal_name)
 | 
			
		||||
  else if (mark == gtk_text_buffer_get_selection_bound (buffer))
 | 
			
		||||
    {
 | 
			
		||||
      if (!accessible->insert_notify_handler)
 | 
			
		||||
        {
 | 
			
		||||
          accessible->insert_notify_handler = gdk_threads_add_idle (insert_idle_handler, accessible);
 | 
			
		||||
        }
 | 
			
		||||
      return;
 | 
			
		||||
      gtk_text_view_accessible_update_cursor (accessible, buffer);
 | 
			
		||||
    }
 | 
			
		||||
  emit_text_caret_moved (accessible, get_insert_offset (buffer));
 | 
			
		||||
  accessible->previous_selection_bound = get_selection_bound (buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gint
 | 
			
		||||
insert_idle_handler (gpointer data)
 | 
			
		||||
{
 | 
			
		||||
  GtkTextViewAccessible *accessible = data;
 | 
			
		||||
  GtkWidget *widget;
 | 
			
		||||
  GtkTextBuffer *buffer;
 | 
			
		||||
 | 
			
		||||
  g_signal_emit_by_name (data,
 | 
			
		||||
                         accessible->signal_name,
 | 
			
		||||
                         accessible->position,
 | 
			
		||||
                         accessible->length);
 | 
			
		||||
  accessible->signal_name = NULL;
 | 
			
		||||
  accessible->position = 0;
 | 
			
		||||
  accessible->length = 0;
 | 
			
		||||
 | 
			
		||||
  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 | 
			
		||||
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
 | 
			
		||||
  if (accessible->insert_notify_handler)
 | 
			
		||||
    {
 | 
			
		||||
    /*
 | 
			
		||||
     * If called from idle handler notify caret moved
 | 
			
		||||
     */
 | 
			
		||||
      accessible->insert_notify_handler = 0;
 | 
			
		||||
      emit_text_caret_moved (accessible, get_insert_offset (buffer));
 | 
			
		||||
      accessible->previous_selection_bound = get_selection_bound (buffer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gint
 | 
			
		||||
 | 
			
		||||
@ -38,16 +38,8 @@ struct _GtkTextViewAccessible
 | 
			
		||||
{
 | 
			
		||||
  GtkContainerAccessible parent;
 | 
			
		||||
 | 
			
		||||
  gint           previous_insert_offset;
 | 
			
		||||
  gint           previous_selection_bound;
 | 
			
		||||
  /*
 | 
			
		||||
   * These fields store information about text changed
 | 
			
		||||
   */
 | 
			
		||||
  gchar          *signal_name;
 | 
			
		||||
  gint           position;
 | 
			
		||||
  gint           length;
 | 
			
		||||
 | 
			
		||||
  guint          insert_notify_handler;
 | 
			
		||||
  gint           insert_offset;
 | 
			
		||||
  gint           selection_bound;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _GtkTextViewAccessibleClass
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user