[gtk+/touch-text-selection: 3/6] Implement touch text selection in GtkTextView



commit 7bcf624a459a652a98ccb886c82025cae9fae7ee
Author: Carlos Garnacho <carlos lanedo com>
Date:   Wed Jul 11 16:43:51 2012 +0200

    Implement touch text selection in GtkTextView
    
    GtkTextHandle is used to indicate both the cursor position
    and the selection bound, dragging the handles will modify
    the selection and scroll if necessary.

 gtk/gtktextview.c |  186 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 183 insertions(+), 3 deletions(-)
---
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 6611673..acd0f7a 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -50,6 +50,7 @@
 #include "gtkwindow.h"
 #include "gtkscrollable.h"
 #include "gtktypebuiltins.h"
+#include "gtktexthandleprivate.h"
 
 #include "a11y/gtktextviewaccessible.h"
 
@@ -132,6 +133,7 @@ struct _GtkTextViewPrivate
   GdkDevice *dnd_device;
 
   gulong selection_drag_handler;
+  GtkTextHandle *text_handle;
 
   GtkTextWindow *text_window;
   GtkTextWindow *left_window;
@@ -229,6 +231,9 @@ struct _GtkTextViewPrivate
    * driving the scrollable adjustment values */
   guint hscroll_policy : 1;
   guint vscroll_policy : 1;
+
+  guint dragging_handle : 1;
+  guint dragged_handle_pos : 1;
 };
 
 struct _GtkTextPendingScroll
@@ -495,6 +500,19 @@ static void gtk_text_view_forall (GtkContainer *container,
                                   GtkCallback   callback,
                                   gpointer      callback_data);
 
+/* GtkTextHandle handlers */
+static void gtk_text_view_handle_cursor_changed          (GtkTextHandle *handle,
+                                                          gint           x,
+                                                          gint           y,
+                                                          GtkTextView   *text_view);
+static void gtk_text_view_handle_selection_bound_changed (GtkTextHandle *handle,
+                                                          gint           x,
+                                                          gint           y,
+                                                          GtkTextView   *text_view);
+static void gtk_text_view_handle_drag_finished           (GtkTextHandle *handle,
+                                                          GtkTextView   *text_view);
+
+
 /* FIXME probably need the focus methods. */
 
 typedef struct _GtkTextViewChild GtkTextViewChild;
@@ -1418,6 +1436,14 @@ gtk_text_view_init (GtkTextView *text_view)
 
   /* We handle all our own redrawing */
   gtk_widget_set_redraw_on_allocate (widget, FALSE);
+
+  priv->text_handle = _gtk_text_handle_new (widget);
+  g_signal_connect (priv->text_handle, "cursor-changed",
+                    G_CALLBACK (gtk_text_view_handle_cursor_changed), text_view);
+  g_signal_connect (priv->text_handle, "selection-bound-changed",
+                    G_CALLBACK (gtk_text_view_handle_selection_bound_changed), text_view);
+  g_signal_connect (priv->text_handle, "drag-finished",
+                    G_CALLBACK (gtk_text_view_handle_drag_finished), text_view);
 }
 
 /**
@@ -3068,6 +3094,7 @@ gtk_text_view_finalize (GObject *object)
   if (priv->bottom_window)
     text_window_free (priv->bottom_window);
 
+  g_object_unref (priv->text_handle);
   g_object_unref (priv->im_context);
 
   g_free (priv->im_module);
@@ -4053,6 +4080,8 @@ gtk_text_view_realize (GtkWidget *widget)
 
   /* Ensure updating the spot location. */
   gtk_text_view_update_im_spot_location (text_view);
+
+  _gtk_text_handle_set_relative_to (priv->text_handle, priv->text_window->window);
 }
 
 static void
@@ -4093,6 +4122,8 @@ gtk_text_view_unrealize (GtkWidget *widget)
   if (priv->bottom_window)
     text_window_unrealize (priv->bottom_window);
 
+  _gtk_text_handle_set_relative_to (priv->text_handle, NULL);
+
   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget);
 }
 
@@ -4360,6 +4391,140 @@ emit_event_on_tags (GtkWidget   *widget,
   return retval;
 }
 
+static void
+_gtk_text_view_set_handle_position (GtkTextView           *text_view,
+                                    GtkTextIter           *iter,
+                                    GtkTextHandlePosition  pos)
+{
+  GtkTextViewPrivate *priv;
+  GdkRectangle rect;
+  gint x, y;
+
+  priv = text_view->priv;
+  gtk_text_view_get_cursor_locations (text_view, iter, &rect, NULL);
+
+  x = rect.x - priv->xoffset;
+  y = rect.y + rect.height - priv->yoffset;
+
+  if ((!priv->dragging_handle ||
+       priv->dragged_handle_pos != pos) &&
+      (x < 0 || x > SCREEN_WIDTH (text_view) ||
+       y < 0 || y > SCREEN_HEIGHT (text_view)))
+    _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
+  else
+    {
+      _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
+
+      x = CLAMP (x, 0, SCREEN_WIDTH (text_view));
+      y = CLAMP (y, 0, SCREEN_HEIGHT (text_view));
+      _gtk_text_handle_set_position (priv->text_handle, pos, x, y);
+    }
+}
+
+static void
+gtk_text_view_handle_cursor_changed (GtkTextHandle *handle,
+                                     gint           x,
+                                     gint           y,
+                                     GtkTextView   *text_view)
+{
+  GtkTextViewPrivate *priv;
+  GtkTextIter iter, bound;
+  GtkTextHandleMode mode;
+  GtkTextBuffer *buffer;
+
+  priv = text_view->priv;
+  priv->dragging_handle = TRUE;
+  priv->dragged_handle_pos = GTK_TEXT_HANDLE_POSITION_CURSOR;
+
+  buffer = get_buffer (text_view);
+  mode = _gtk_text_handle_get_mode (handle);
+
+  gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
+                                     x + priv->xoffset,
+                                     y + priv->yoffset);
+  _gtk_text_view_set_handle_position (text_view, &iter,
+                                      GTK_TEXT_HANDLE_POSITION_CURSOR);
+
+  if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
+    gtk_text_buffer_place_cursor (buffer, &iter);
+  else
+    {
+      gtk_text_buffer_get_iter_at_mark (buffer, &bound,
+                                        gtk_text_buffer_get_selection_bound (buffer));
+      gtk_text_buffer_select_range (buffer, &iter, &bound);
+    }
+
+  gtk_text_view_scroll_mark_onscreen (text_view,
+                                      gtk_text_buffer_get_insert (buffer));
+}
+
+static void
+gtk_text_view_handle_selection_bound_changed (GtkTextHandle *handle,
+                                              gint           x,
+                                              gint           y,
+                                              GtkTextView   *text_view)
+{
+  GtkTextViewPrivate *priv;
+  GtkTextBuffer *buffer;
+  GtkTextIter iter, ins;
+
+  priv = text_view->priv;
+  priv->dragging_handle = TRUE;
+  priv->dragged_handle_pos = GTK_TEXT_HANDLE_POSITION_SELECTION_BOUND;
+
+  gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
+                                     x + priv->xoffset,
+                                     y + priv->yoffset);
+  _gtk_text_view_set_handle_position (text_view, &iter,
+                                      GTK_TEXT_HANDLE_POSITION_SELECTION_BOUND);
+
+  buffer = get_buffer (text_view);
+  gtk_text_buffer_get_iter_at_mark (buffer, &ins,
+                                    gtk_text_buffer_get_insert (buffer));
+  gtk_text_buffer_select_range (buffer, &ins, &iter);
+
+  gtk_text_view_scroll_mark_onscreen (text_view,
+                                      gtk_text_buffer_get_selection_bound (buffer));
+}
+
+static void
+gtk_text_view_handle_drag_finished (GtkTextHandle *handle,
+                                    GtkTextView   *text_view)
+{
+  GtkTextViewPrivate *priv;
+
+  priv = text_view->priv;
+  priv->dragging_handle = FALSE;
+}
+
+static void
+_gtk_text_view_update_handles (GtkTextView       *text_view,
+                               GtkTextHandleMode  mode)
+{
+  GtkTextViewPrivate *priv = text_view->priv;
+  GtkTextBuffer *buffer;
+  GtkTextIter iter;
+
+  _gtk_text_handle_set_mode (priv->text_handle, mode);
+  buffer = get_buffer (text_view);
+
+  if (mode != GTK_TEXT_HANDLE_MODE_NONE)
+    {
+      gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+                                        gtk_text_buffer_get_insert (buffer));
+      _gtk_text_view_set_handle_position (text_view, &iter,
+                                          GTK_TEXT_HANDLE_POSITION_CURSOR);
+    }
+
+  if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
+    {
+      gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+                                        gtk_text_buffer_get_selection_bound (buffer));
+      _gtk_text_view_set_handle_position (text_view, &iter,
+                                          GTK_TEXT_HANDLE_POSITION_SELECTION_BOUND);
+    }
+}
+
 static gint
 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
 {
@@ -4491,6 +4656,9 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
   gtk_text_view_reset_blink_time (text_view);
   gtk_text_view_pend_cursor_blink (text_view);
 
+  _gtk_text_handle_set_mode (priv->text_handle,
+                             GTK_TEXT_HANDLE_MODE_NONE);
+
   return retval;
 }
 
@@ -4585,6 +4753,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
           else
             {
               gtk_text_view_start_selection_drag (text_view, &iter, event);
+              _gtk_text_view_update_handles (text_view,
+                                             GTK_TEXT_HANDLE_MODE_CURSOR);
             }
 
           return TRUE;
@@ -4667,9 +4837,12 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
 
 	  gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
 	  gtk_text_view_check_cursor_blink (text_view);
-	  
+
+          _gtk_text_view_update_handles (text_view,
+                                         GTK_TEXT_HANDLE_MODE_CURSOR);
+
           priv->pending_place_cursor_button = 0;
-          
+
           return FALSE;
         }
     }
@@ -4743,6 +4916,8 @@ gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
   g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
 					keymap_direction_changed,
 					text_view);
+  _gtk_text_handle_set_mode (priv->text_handle,
+                             GTK_TEXT_HANDLE_MODE_NONE);
 
   if (priv->editable)
     {
@@ -6452,6 +6627,8 @@ selection_motion_event_handler (GtkTextView    *text_view,
   text_view->priv->scroll_timeout =
     gdk_threads_add_timeout (50, selection_scan_timeout, text_view);
 
+  _gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION);
+
   return TRUE;
 }
 
@@ -7703,7 +7880,10 @@ gtk_text_view_value_changed (GtkAdjustment *adjustment,
    * changes made by the validation are pushed through.
    */
   gtk_text_view_update_im_spot_location (text_view);
-  
+
+  _gtk_text_view_update_handles (text_view,
+                                 _gtk_text_handle_get_mode (priv->text_handle));
+
   DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
 }
 



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