Insertion cursor drawing improvements



Just found the following patch on one of my computers - I've had
it sitting around for a few months. What it 
does is:

 - Make the insertion cursor draw with wider width for taller cursors
 - Adds small directional arrows when you have split
   cursors to make it clear which cursor is which.
 - Adds a "cursor_color" property to GtkTextView to match
   the cursor_color property in GtkEntry.

Possible questions:

 - Should _gtk_draw_insertion_cursor() be public? It's conceivable
   that custom widgets or modified versions of 
   GtkEntry/GtkLabel/GtkTextView will need access to this function.
   It's not really a normal gtk_draw_* function as it's not
   virtualized in the style, takes a GdkGC as input, and so forth.

 - Does the cursor_gc addition to gtk_text_layout_draw() make sense?
   The alternative would be to remove the "cursor_color" style
   properties and add a global "cursor_color" setting.

Regards,
                                        Owen

Index: gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.149
diff -u -p -r1.149 gtkentry.c
--- gtkentry.c	2001/09/19 00:49:51	1.149
+++ gtkentry.c	2001/09/24 16:08:20
@@ -2583,6 +2583,7 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
   if (GTK_WIDGET_DRAWABLE (entry))
     {
       GtkWidget *widget = GTK_WIDGET (entry);
+      GdkRectangle cursor_location;
       gboolean split_cursor;
 
       gint xoffset = INNER_BORDER - entry->scroll_offset;
@@ -2590,6 +2591,8 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
       gint text_area_height;
       GdkGC *gc1 = NULL;
       GdkGC *gc2 = NULL;
+      GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
+      GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
       gint x1 = 0;
       gint x2 = 0;
 
@@ -2608,6 +2611,9 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
 
 	  if (weak_x != strong_x)
 	    {
+	      dir1 = widget_direction;
+	      dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
+	      
 	      gc2 = widget->style->text_gc[GTK_STATE_NORMAL];
 	      x2 = weak_x;
 	    }
@@ -2621,15 +2627,21 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
 	  else
 	    x1 = weak_x;
 	}
+
+      cursor_location.x = xoffset + x1;
+      cursor_location.y = INNER_BORDER;
+      cursor_location.width = 0;
+      cursor_location.height = text_area_height - 2 * INNER_BORDER ;
       
-      gdk_draw_line (entry->text_area, gc1,
-		     xoffset + x1, INNER_BORDER,
-		     xoffset + x1, text_area_height - INNER_BORDER);
+      _gtk_draw_insertion_cursor (entry->text_area, gc1,
+				  &cursor_location, dir1);
       
       if (gc2)
-	gdk_draw_line (entry->text_area, gc2,
-		       xoffset + x2, INNER_BORDER,
-		       xoffset + x2, text_area_height - INNER_BORDER);
+	{
+	  cursor_location.x = xoffset + x2;
+	  _gtk_draw_insertion_cursor (entry->text_area, gc2,
+				      &cursor_location, dir2);
+	}
     }
 }
 
Index: gtklabel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.c,v
retrieving revision 1.100
diff -u -p -r1.100 gtklabel.c
--- gtklabel.c	2001/09/19 00:49:51	1.100
+++ gtklabel.c	2001/09/24 16:08:20
@@ -1595,13 +1595,16 @@ gtk_label_draw_cursor (GtkLabel  *label,
   if (GTK_WIDGET_DRAWABLE (label))
     {
       GtkWidget *widget = GTK_WIDGET (label);
-      
+
       GtkTextDirection keymap_direction;
       GtkTextDirection widget_direction;
       PangoRectangle strong_pos, weak_pos;
       gboolean split_cursor;
       PangoRectangle *cursor1 = NULL;
       PangoRectangle *cursor2 = NULL;
+      GdkRectangle cursor_location;
+      GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
+      GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
       GdkGC *gc1 = NULL;
       GdkGC *gc2 = NULL;
 
@@ -1628,6 +1631,9 @@ gtk_label_draw_cursor (GtkLabel  *label,
 	  if (strong_pos.x != weak_pos.x ||
 	      strong_pos.y != weak_pos.y)
 	    {
+	      dir1 = widget_direction;
+	      dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
+	      
 	      gc2 = widget->style->black_gc;
 	      cursor2 = &weak_pos;
 	    }
@@ -1641,15 +1647,25 @@ gtk_label_draw_cursor (GtkLabel  *label,
 	  else
 	    cursor1 = &weak_pos;
 	}
+      
+      cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
+      cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
+      cursor_location.width = 0;
+      cursor_location.height = PANGO_PIXELS (cursor1->height);
       
-      gdk_draw_line (widget->window, gc1,
-		     xoffset + PANGO_PIXELS (cursor1->x), yoffset + PANGO_PIXELS (cursor1->y),
-		     xoffset + PANGO_PIXELS (cursor1->x), yoffset + PANGO_PIXELS (cursor1->y + cursor1->height));
+      _gtk_draw_insertion_cursor (widget->window, gc1,
+				  &cursor_location, dir1);
       
       if (gc2)
-	gdk_draw_line (widget->window, gc2,
-		       xoffset + PANGO_PIXELS (cursor2->x), yoffset + PANGO_PIXELS (cursor2->y),
-		       xoffset + PANGO_PIXELS (cursor2->x), yoffset + PANGO_PIXELS (cursor2->y + cursor2->height));
+	{
+	  cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
+	  cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
+	  cursor_location.width = 0;
+	  cursor_location.height = PANGO_PIXELS (cursor2->height);
+	  
+	  _gtk_draw_insertion_cursor (widget->window, gc2,
+				      &cursor_location, dir2);
+	}
     }
 }
 
Index: gtkstyle.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.c,v
retrieving revision 1.80
diff -u -p -r1.80 gtkstyle.c
--- gtkstyle.c	2001/09/18 20:06:46	1.80
+++ gtkstyle.c	2001/09/24 16:08:20
@@ -5050,3 +5050,60 @@ gtk_style_set_font (GtkStyle *style,
       style->private_font_desc = NULL;
     }
 }
+
+/**
+ * _gtk_draw_insertion_cursor:
+ * @drawable: a #GdkDrawable
+ * @gc: a #GdkGC
+ * @location: location where to draw the cursor (@location->width is ignored)
+ * @dir: text direction for the cursor, used to decide whether to draw a
+ *       directional arrow on the cursor and in what direction. Unless both
+ *       strong and weak cursors are displayed, this should be %GTK_TEXT_DIR_NONE.
+ * 
+ * Draws a text caret on @drawable at @location. This is not a style function
+ * but merely a convenience function for drawing the standard cursor shape.
+ **/
+void
+_gtk_draw_insertion_cursor (GdkDrawable      *drawable,
+			    GdkGC            *gc,
+			    GdkRectangle     *location,
+			    GtkTextDirection  dir)
+{
+  gint stem_width = location->height / 30 + 1;
+  gint arrow_width = stem_width + 1;
+  gint x, y;
+  gint i;
+
+  for (i = 0; i < stem_width; i++)
+    gdk_draw_line (drawable, gc,
+		   location->x + i - stem_width / 2, location->y,
+		   location->x + i - stem_width / 2, location->y + location->height);
+
+  if (dir == GTK_TEXT_DIR_RTL)
+    {
+      x = location->x - stem_width / 2 - 1;
+      y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
+  
+      for (i = 0; i < arrow_width; i++)
+	{
+	  gdk_draw_line (drawable, gc,
+			 x, y + i + 1,
+			 x, y + 2 * arrow_width - i - 1);
+	  x --;
+	}
+    }
+  else if (dir == GTK_TEXT_DIR_LTR)
+    {
+      x = location->x + stem_width - stem_width / 2;
+      y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
+  
+      for (i = 0; i < arrow_width; i++) 
+	{
+	  gdk_draw_line (drawable, gc,
+			 x, y + i + 1,
+			 x, y + 2 * arrow_width - i - 1);
+	  x++;
+	}
+    }
+}
+
Index: gtkstyle.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.h,v
retrieving revision 1.31
diff -u -p -r1.31 gtkstyle.h
--- gtkstyle.h	2001/09/18 20:06:46	1.31
+++ gtkstyle.h	2001/09/24 16:08:20
@@ -868,6 +868,11 @@ void gtk_paint_string     (GtkStyle     
 			   const gchar     *string);
 #endif /* GTK_DISABLE_DEPRECATED */
 
+void _gtk_draw_insertion_cursor (GdkDrawable      *drawable,
+				 GdkGC            *gc,
+				 GdkRectangle     *location,
+				 GtkTextDirection  dir);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: gtktextdisplay.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextdisplay.c,v
retrieving revision 1.29
diff -u -p -r1.29 gtktextdisplay.c
--- gtktextdisplay.c	2001/09/19 00:49:52	1.29
+++ gtktextdisplay.c	2001/09/24 16:08:20
@@ -716,6 +716,7 @@ void
 gtk_text_layout_draw (GtkTextLayout *layout,
                       GtkWidget *widget,
                       GdkDrawable *drawable,
+		      GdkGC       *cursor_gc,
                       /* Location of the drawable
                          in layout coordinates */
                       gint x_offset,
@@ -775,6 +776,8 @@ gtk_text_layout_draw (GtkTextLayout *lay
       GtkTextLineDisplay *line_display;
       gint selection_start_index = -1;
       gint selection_end_index = -1;
+      gboolean have_strong;
+      gboolean have_weak;
 
       GtkTextLine *line = tmp_list->data;
 
@@ -823,23 +826,53 @@ gtk_text_layout_draw (GtkTextLayout *lay
           /* We paint the cursors last, because they overlap another chunk
          and need to appear on top. */
 
+ 	  have_strong = FALSE;
+ 	  have_weak = FALSE;
+	  
+	  cursor_list = line_display->cursors;
+	  while (cursor_list)
+	    {
+	      GtkTextCursorDisplay *cursor = cursor_list->data;
+ 	      if (cursor->is_strong)
+ 		have_strong = TRUE;
+ 	      else
+ 		have_weak = TRUE;
+	      
+	      cursor_list = cursor_list->next;
+ 	    }
+	  
           cursor_list = line_display->cursors;
           while (cursor_list)
             {
               GtkTextCursorDisplay *cursor = cursor_list->data;
+	      GtkTextDirection dir;
+ 	      GdkRectangle cursor_location;
+
               GdkGC *gc;
 
               if (cursor->is_strong)
-                gc = widget->style->base_gc[GTK_STATE_SELECTED];
+                gc = cursor_gc;
               else
                 gc = widget->style->text_gc[GTK_STATE_NORMAL];
 
-              gdk_gc_set_clip_rectangle (gc, &clip);
-              gdk_draw_line (drawable, gc,
-                             line_display->x_offset + cursor->x - x_offset,
-                             current_y + line_display->top_margin + cursor->y,
-                             line_display->x_offset + cursor->x - x_offset,
-                             current_y + line_display->top_margin + cursor->y + cursor->height - 1);
+ 	      if (have_strong && have_weak)
+ 		{
+ 		  dir = line_display->direction;
+ 		  if (!cursor->is_strong)
+ 		    dir = (dir == GTK_TEXT_DIR_RTL) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
+ 		}
+ 	      else
+ 		{
+ 		  dir = GTK_TEXT_DIR_NONE;
+ 		}
+ 
+ 	      cursor_location.x = line_display->x_offset + cursor->x - x_offset;
+ 	      cursor_location.y = current_y + line_display->top_margin + cursor->y;
+ 	      cursor_location.width = 0;
+ 	      cursor_location.height = cursor->height;
+ 
+	      gdk_gc_set_clip_rectangle(gc, &clip);
+ 	      _gtk_draw_insertion_cursor (drawable, gc, &cursor_location, dir);
               gdk_gc_set_clip_rectangle (gc, NULL);
 
               cursor_list = cursor_list->next;
Index: gtktextdisplay.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextdisplay.h,v
retrieving revision 1.6
diff -u -p -r1.6 gtktextdisplay.h
--- gtktextdisplay.h	2000/10/30 17:02:57	1.6
+++ gtktextdisplay.h	2001/09/24 16:08:20
@@ -90,6 +90,7 @@ extern "C" {
 /* The drawable should be pre-initialized to your preferred background.
  * widget            - Widget to grab some style info from
  * drawable          - Drawable to render to
+ * cursor_gc         - Graphics context to use for cursor
  * x_offset/y_offset - Position of the drawable in layout coordinates
  * x/y/width/height  - Region of the layout to render. x,y must be inside
  *                     the drawable.
@@ -97,6 +98,7 @@ extern "C" {
 void gtk_text_layout_draw (GtkTextLayout        *layout,
                            GtkWidget            *widget,
                            GdkDrawable          *drawable,
+			   GdkGC                *cursor_gc,
                            gint                  x_offset,
                            gint                  y_offset,
                            gint                  x,
Index: gtktextview.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextview.c,v
retrieving revision 1.112
diff -u -p -r1.112 gtktextview.c
--- gtktextview.c	2001/09/22 00:08:18	1.112
+++ gtktextview.c	2001/09/24 16:08:20
@@ -620,6 +620,18 @@ gtk_text_view_class_init (GtkTextViewCla
 
   
   /*
+   * Style properties
+   */
+
+  gtk_widget_class_install_style_property (widget_class,
+					   g_param_spec_boxed ("cursor_color",
+							       _("Cursor color"),
+							       _("Color with which to draw insertion cursor"),
+							       GDK_TYPE_COLOR,
+							       G_PARAM_READABLE));
+
+
+  /*
    * Signals
    */
 
@@ -2909,6 +2921,24 @@ changed_handler (GtkTextLayout *layout,
 }
 
 static void
+gtk_text_view_realize_cursor_gc (GtkTextView *text_view)
+{
+  GdkColor *cursor_color;
+  
+  if (text_view->cursor_gc)
+    gdk_gc_unref (text_view->cursor_gc);
+
+  gtk_widget_style_get (GTK_WIDGET (text_view), "cursor_color", &cursor_color, NULL);
+  if (cursor_color)
+    {
+      text_view->cursor_gc = gdk_gc_new (text_view->text_window->bin_window);
+      gdk_gc_set_rgb_fg_color (text_view->cursor_gc, cursor_color);
+    }
+  else
+    text_view->cursor_gc = gdk_gc_ref (GTK_WIDGET (text_view)->style->base_gc[GTK_STATE_SELECTED]);
+}
+
+static void
 gtk_text_view_realize (GtkWidget *widget)
 {
   GtkTextView *text_view;
@@ -2958,6 +2988,8 @@ gtk_text_view_realize (GtkWidget *widget
     text_window_realize (text_view->bottom_window,
                          widget->window);
 
+  gtk_text_view_realize_cursor_gc (text_view);
+
   gtk_text_view_ensure_layout (text_view);
 
   if (text_view->buffer)
@@ -2976,6 +3008,12 @@ gtk_text_view_unrealize (GtkWidget *widg
     gtk_text_buffer_remove_selection_clipboard (text_view->buffer,
 						gtk_clipboard_get (GDK_SELECTION_PRIMARY));
 
+  if (text_view->cursor_gc)
+    {
+      gdk_gc_unref (text_view->cursor_gc);
+      text_view->cursor_gc = NULL;
+    }
+
   if (text_view->first_validate_idle)
     {
       g_source_remove (text_view->first_validate_idle);
@@ -3041,6 +3079,8 @@ gtk_text_view_style_set (GtkWidget *widg
       if (text_view->bottom_window)
         gdk_window_set_background (text_view->bottom_window->bin_window,
                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
+      
+      gtk_text_view_realize_cursor_gc (text_view);
     }
 
   if (text_view->layout && previous_style)
@@ -3575,6 +3615,7 @@ gtk_text_view_paint (GtkWidget *widget, 
   gtk_text_layout_draw (text_view->layout,
                         widget,
                         text_view->text_window->bin_window,
+			text_view->cursor_gc,
                         text_view->xoffset,
                         text_view->yoffset,
                         area->x, area->y,
Index: gtktextview.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextview.h,v
retrieving revision 1.28
diff -u -p -r1.28 gtktextview.h
--- gtktextview.h	2001/06/24 16:08:15	1.28
+++ gtktextview.h	2001/09/24 16:08:20
@@ -142,6 +142,7 @@ struct _GtkTextView
   GSList *children;
 
   GtkTextPendingScroll *pending_scroll;
+  GdkGC *cursor_gc;
 };
 
 struct _GtkTextViewClass




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