[gtk/wip/textview: 24/24] textview: port GtkTextView to GskPangoRenderer



commit 2db9ec5bbd25606029728576e55e40e991566d4c
Author: Christian Hergert <chergert redhat com>
Date:   Sat Jul 20 18:25:51 2019 -0700

    textview: port GtkTextView to GskPangoRenderer
    
    This removes the use of GtkTextDisplay (a PangoRenderer) to use
    the GskPangoRender which generates render nodes. Part of this means
    improving the GskPangoRenderer to support the necessary features for
    displaying a GtkTextView.
    
    Primarily, this is a merging of GtkTextDisplay features into
    GskPangoRender. Additionally, GtkTextDisplay was removed to allow for
    gtk_text_layout_snapshot() to be implemented elsewhere.

 gtk/gskpango.c              | 186 +++++++--
 gtk/gskpango.h              |  54 ++-
 gtk/gtktextdisplay.c        | 932 --------------------------------------------
 gtk/gtktextdisplayprivate.h | 101 -----
 gtk/gtktextlayout.c         | 331 ++++++++++++++++
 gtk/gtktextlayoutprivate.h  |  15 +-
 gtk/gtktextutil.c           |   2 +-
 gtk/gtktextview.c           |   1 -
 gtk/meson.build             |   1 -
 9 files changed, 542 insertions(+), 1081 deletions(-)
---
diff --git a/gtk/gskpango.c b/gtk/gskpango.c
index 8e1c2e3f70..d34d4057a3 100644
--- a/gtk/gskpango.c
+++ b/gtk/gskpango.c
@@ -23,40 +23,25 @@
 #include "gsk/gskrendernodeprivate.h"
 #include "gskpango.h"
 #include "gtksnapshotprivate.h"
+#include "gtkstylecontextprivate.h"
+#include "gtktextlayoutprivate.h"
+#include "gtktextviewprivate.h"
 
 #include <math.h>
 
 #include <pango/pango.h>
 #include <cairo.h>
 
-#define GSK_PANGO_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_PANGO_RENDERER, 
GskPangoRendererClass))
-#define GSK_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_PANGO_RENDERER))
-#define GSK_PANGO_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_PANGO_RENDERER, 
GskPangoRendererClass))
-
-/*
- * This is a PangoRenderer implementation that translates all the draw calls to
- * gsk render nodes, using the GtkSnapshot helper class. Glyphs are translated
- * to text nodes, all other draw calls fall back to cairo nodes.
- */
-
-struct _GskPangoRenderer
-{
-  PangoRenderer parent_instance;
-
-  GtkSnapshot *snapshot;
-  GdkRGBA fg_color;
-  graphene_rect_t bounds;
-
-  /* house-keeping options */
-  gboolean is_cached_renderer;
-};
+G_DEFINE_TYPE (GskPangoRenderer, gsk_pango_renderer, PANGO_TYPE_RENDERER)
 
-struct _GskPangoRendererClass
+void
+gsk_pango_renderer_set_state (GskPangoRenderer      *crenderer,
+                              GskPangoRendererState  state)
 {
-  PangoRendererClass parent_class;
-};
+  g_return_if_fail (GSK_IS_PANGO_RENDERER (crenderer));
 
-G_DEFINE_TYPE (GskPangoRenderer, gsk_pango_renderer, PANGO_TYPE_RENDERER)
+  crenderer->state = state;
+}
 
 static void
 get_color (GskPangoRenderer *crenderer,
@@ -158,15 +143,14 @@ gsk_pango_renderer_draw_rectangle (PangoRenderer     *renderer,
 {
   GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
   GdkRGBA rgba;
-  graphene_rect_t bounds;
 
   get_color (crenderer, part, &rgba);
-
-  graphene_rect_init (&bounds,
-                      (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
-                      (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
-
-  gtk_snapshot_append_color (crenderer->snapshot, &rgba, &bounds);
+  gtk_snapshot_append_color (crenderer->snapshot,
+                             &rgba,
+                             &GRAPHENE_RECT_INIT ((double)x / PANGO_SCALE,
+                                                  (double)y / PANGO_SCALE,
+                                                  (double)width / PANGO_SCALE,
+                                                  (double)height / PANGO_SCALE));
 }
 
 static void
@@ -336,6 +320,124 @@ gsk_pango_renderer_draw_shape (PangoRenderer  *renderer,
   cairo_destroy (cr);
 }
 
+static void
+text_renderer_set_rgba (GskPangoRenderer *crenderer,
+                        PangoRenderPart   part,
+                        const GdkRGBA    *rgba)
+{
+  PangoRenderer *renderer = PANGO_RENDERER (crenderer);
+  PangoColor color = { 0, };
+  guint16 alpha;
+
+  if (rgba)
+    {
+      color.red = (guint16)(rgba->red * 65535);
+      color.green = (guint16)(rgba->green * 65535);
+      color.blue = (guint16)(rgba->blue * 65535);
+      alpha = (guint16)(rgba->alpha * 65535);
+      pango_renderer_set_color (renderer, part, &color);
+      pango_renderer_set_alpha (renderer, part, alpha);
+    }
+  else
+    {
+      pango_renderer_set_color (renderer, part, NULL);
+      pango_renderer_set_alpha (renderer, part, 0);
+    }
+}
+
+static GtkTextAppearance *
+get_item_appearance (PangoItem *item)
+{
+  GSList *tmp_list = item->analysis.extra_attrs;
+
+  while (tmp_list)
+    {
+      PangoAttribute *attr = tmp_list->data;
+
+      if (attr->klass->type == gtk_text_attr_appearance_type)
+        return &((GtkTextAttrAppearance *)attr)->appearance;
+
+      tmp_list = tmp_list->next;
+    }
+
+  return NULL;
+}
+
+static void
+gsk_pango_renderer_prepare_run (PangoRenderer  *renderer,
+                                PangoLayoutRun *run)
+{
+  GtkStyleContext *context;
+  GskPangoRenderer *crenderer = GSK_PANGO_RENDERER (renderer);
+  GdkRGBA *bg_rgba = NULL;
+  GdkRGBA *fg_rgba = NULL;
+  GtkTextAppearance *appearance;
+
+  PANGO_RENDERER_CLASS (gsk_pango_renderer_parent_class)->prepare_run (renderer, run);
+
+  appearance = get_item_appearance (run->item);
+
+  if (appearance == NULL)
+    return;
+
+  context = gtk_widget_get_style_context (crenderer->widget);
+
+  if (appearance->draw_bg && crenderer->state == GSK_PANGO_RENDERER_NORMAL)
+    bg_rgba = appearance->bg_rgba;
+  else
+    bg_rgba = NULL;
+
+  text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_BACKGROUND, bg_rgba);
+
+  if (crenderer->state == GSK_PANGO_RENDERER_SELECTED &&
+      GTK_IS_TEXT_VIEW (crenderer->widget))
+    {
+      GtkCssNode *selection_node;
+
+      selection_node = gtk_text_view_get_selection_node ((GtkTextView *)crenderer->widget);
+      gtk_style_context_save_to_node (context, selection_node);
+
+      gtk_style_context_get (context,
+                             "color", &fg_rgba,
+                             NULL);
+
+      gtk_style_context_restore (context);
+    }
+  else if (crenderer->state == GSK_PANGO_RENDERER_CURSOR && gtk_widget_has_focus (crenderer->widget))
+    {
+      gtk_style_context_get (context,
+                             "background-color", &fg_rgba,
+                             NULL);
+    }
+  else
+    fg_rgba = appearance->fg_rgba;
+
+  text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_FOREGROUND, fg_rgba);
+
+  if (appearance->strikethrough_rgba)
+    text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_STRIKETHROUGH, appearance->strikethrough_rgba);
+  else
+    text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_rgba);
+
+  if (appearance->underline_rgba)
+    text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_UNDERLINE, appearance->underline_rgba);
+  else if (appearance->underline == PANGO_UNDERLINE_ERROR)
+    {
+      if (!crenderer->error_color)
+        {
+          static const GdkRGBA red = { 1, 0, 0, 1 };
+          crenderer->error_color = gdk_rgba_copy (&red);
+        }
+
+      text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_UNDERLINE, crenderer->error_color);
+    }
+  else
+    text_renderer_set_rgba (crenderer, PANGO_RENDER_PART_UNDERLINE, fg_rgba);
+
+  if (fg_rgba != appearance->fg_rgba)
+    gdk_rgba_free (fg_rgba);
+}
+
 static void
 gsk_pango_renderer_init (GskPangoRenderer *renderer G_GNUC_UNUSED)
 {
@@ -352,13 +454,14 @@ gsk_pango_renderer_class_init (GskPangoRendererClass *klass)
   renderer_class->draw_trapezoid = gsk_pango_renderer_draw_trapezoid;
   renderer_class->draw_error_underline = gsk_pango_renderer_draw_error_underline;
   renderer_class->draw_shape = gsk_pango_renderer_draw_shape;
+  renderer_class->prepare_run = gsk_pango_renderer_prepare_run;
 }
 
 static GskPangoRenderer *cached_renderer = NULL; /* MT-safe */
 G_LOCK_DEFINE_STATIC (cached_renderer);
 
-static GskPangoRenderer *
-acquire_renderer (void)
+GskPangoRenderer *
+gsk_pango_renderer_acquire (void)
 {
   GskPangoRenderer *renderer;
 
@@ -380,13 +483,20 @@ acquire_renderer (void)
   return renderer;
 }
 
-static void
-release_renderer (GskPangoRenderer *renderer)
+void
+gsk_pango_renderer_release (GskPangoRenderer *renderer)
 {
   if (G_LIKELY (renderer->is_cached_renderer))
     {
+      renderer->widget = NULL;
       renderer->snapshot = NULL;
 
+      if (renderer->error_color)
+        {
+          gdk_rgba_free (renderer->error_color);
+          renderer->error_color = NULL;
+        }
+
       G_UNLOCK (cached_renderer);
     }
   else
@@ -414,7 +524,7 @@ gtk_snapshot_append_layout (GtkSnapshot   *snapshot,
   g_return_if_fail (snapshot != NULL);
   g_return_if_fail (PANGO_IS_LAYOUT (layout));
 
-  crenderer = acquire_renderer ();
+  crenderer = gsk_pango_renderer_acquire ();
 
   crenderer->snapshot = snapshot;
   crenderer->fg_color = *color;
@@ -424,5 +534,5 @@ gtk_snapshot_append_layout (GtkSnapshot   *snapshot,
 
   pango_renderer_draw_layout (PANGO_RENDERER (crenderer), layout, 0, 0);
 
-  release_renderer (crenderer);
+  gsk_pango_renderer_release (crenderer);
 }
diff --git a/gtk/gskpango.h b/gtk/gskpango.h
index a97fdebea4..33ebff422a 100644
--- a/gtk/gskpango.h
+++ b/gtk/gskpango.h
@@ -24,15 +24,57 @@
 
 G_BEGIN_DECLS
 
-#define GSK_TYPE_PANGO_RENDERER (gsk_pango_renderer_get_type ())
+#define GSK_TYPE_PANGO_RENDERER            (gsk_pango_renderer_get_type ())
+#define GSK_PANGO_RENDERER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_PANGO_RENDERER, 
GskPangoRenderer))
+#define GSK_IS_PANGO_RENDERER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_PANGO_RENDERER))
+#define GSK_PANGO_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_PANGO_RENDERER, 
GskPangoRendererClass))
+#define GSK_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_PANGO_RENDERER))
+#define GSK_PANGO_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_PANGO_RENDERER, 
GskPangoRendererClass))
 
-#define GSK_PANGO_RENDERER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_PANGO_RENDERER, 
GskPangoRenderer))
-#define GSK_IS_PANGO_RENDERER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_PANGO_RENDERER))
+typedef struct _GskPangoRenderer      GskPangoRenderer;
+typedef struct _GskPangoRendererClass GskPangoRendererClass;
 
-typedef struct _GskPangoRenderer        GskPangoRenderer;
-typedef struct _GskPangoRendererClass   GskPangoRendererClass;
+typedef enum
+{
+  GSK_PANGO_RENDERER_NORMAL,
+  GSK_PANGO_RENDERER_SELECTED,
+  GSK_PANGO_RENDERER_CURSOR
+} GskPangoRendererState;
 
-GType gsk_pango_renderer_get_type (void) G_GNUC_CONST;
+/*
+ * This is a PangoRenderer implementation that translates all the draw calls to
+ * gsk render nodes, using the GtkSnapshot helper class. Glyphs are translated
+ * to text nodes, all other draw calls fall back to cairo nodes.
+ */
+
+struct _GskPangoRenderer
+{
+  PangoRenderer          parent_instance;
+
+  GtkWidget             *widget;
+  GtkSnapshot           *snapshot;
+  GdkRGBA                fg_color;
+  graphene_rect_t        bounds;
+
+  /* Error underline color for this widget */
+  GdkRGBA               *error_color;
+
+  GskPangoRendererState  state;
+
+  /* house-keeping options */
+  guint                  is_cached_renderer : 1;
+};
+
+struct _GskPangoRendererClass
+{
+  PangoRendererClass parent_class;
+};
+
+GType             gsk_pango_renderer_get_type  (void) G_GNUC_CONST;
+void              gsk_pango_renderer_set_state (GskPangoRenderer      *crenderer,
+                                                GskPangoRendererState  state);
+GskPangoRenderer *gsk_pango_renderer_acquire   (void);
+void              gsk_pango_renderer_release   (GskPangoRenderer      *crenderer);
 
 G_END_DECLS
 
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c
index 79785773d1..5564fd815f 100644
--- a/gtk/gtktextlayout.c
+++ b/gtk/gtktextlayout.c
@@ -77,12 +77,16 @@
 
 #include "config.h"
 #include "gtkmarshalers.h"
+#include "gtkstylecontextprivate.h"
 #include "gtktextlayoutprivate.h"
 #include "gtktextbtree.h"
 #include "gtktextbufferprivate.h"
 #include "gtktextiterprivate.h"
 #include "gtktextutil.h"
+#include "gskpango.h"
 #include "gtkintl.h"
+#include "gtkwidgetprivate.h"
+#include "gtktextviewprivate.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -3785,3 +3789,330 @@ gtk_text_layout_buffer_delete_range (GtkTextBuffer *textbuffer,
 
   gtk_text_layout_update_cursor_line (layout);
 }
+
+static void
+render_para (GskPangoRenderer   *crenderer,
+             int                 offset_y,
+             GtkTextLineDisplay *line_display,
+             int                 selection_start_index,
+             int                 selection_end_index)
+{
+  GtkStyleContext *context;
+  PangoLayout *layout = line_display->layout;
+  int byte_offset = 0;
+  PangoLayoutIter *iter;
+  int screen_width;
+  GdkRGBA *selection;
+  gboolean first = TRUE;
+  GtkCssNode *selection_node;
+  graphene_point_t point = { 0, offset_y };
+
+  g_return_if_fail (GTK_IS_TEXT_VIEW (crenderer->widget));
+
+  iter = pango_layout_get_iter (layout);
+  screen_width = line_display->total_width;
+
+  context = gtk_widget_get_style_context (crenderer->widget);
+  selection_node = gtk_text_view_get_selection_node ((GtkTextView*)crenderer->widget);
+  gtk_style_context_save_to_node (context, selection_node);
+
+  gtk_style_context_get (context, "background-color", &selection, NULL);
+
+  gtk_style_context_restore (context);
+
+  gtk_snapshot_save (crenderer->snapshot);
+  gtk_snapshot_translate (crenderer->snapshot, &point);
+
+  do
+    {
+      PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
+      int selection_y, selection_height;
+      int first_y, last_y;
+      PangoRectangle line_rect;
+      int baseline;
+      gboolean at_last_line;
+
+      pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
+      baseline = pango_layout_iter_get_baseline (iter);
+      pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
+
+      /* Adjust for margins */
+
+      line_rect.x += line_display->x_offset * PANGO_SCALE;
+      line_rect.y += line_display->top_margin * PANGO_SCALE;
+      baseline += line_display->top_margin * PANGO_SCALE;
+
+      /* Selection is the height of the line, plus top/bottom
+       * margin if we're the first/last line
+       */
+      selection_y = PANGO_PIXELS (first_y) + line_display->top_margin;
+      selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
+
+      if (first)
+        {
+          selection_y -= line_display->top_margin;
+          selection_height += line_display->top_margin;
+        }
+
+      at_last_line = pango_layout_iter_at_last_line (iter);
+      if (at_last_line)
+        selection_height += line_display->bottom_margin;
+
+      first = FALSE;
+
+      if (selection_start_index < byte_offset &&
+          selection_end_index > line->length + byte_offset) /* All selected */
+        {
+          gtk_snapshot_append_color (crenderer->snapshot,
+                                     selection,
+                                     &GRAPHENE_RECT_INIT (line_display->left_margin,
+                                                          selection_y,
+                                                          screen_width,
+                                                          selection_height));
+          gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_SELECTED);
+          pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
+                                           line,
+                                           line_rect.x,
+                                           baseline);
+        }
+      else
+        {
+          if (line_display->pg_bg_rgba_set)
+            gtk_snapshot_append_color (crenderer->snapshot,
+                                       &line_display->pg_bg_rgba,
+                                       &GRAPHENE_RECT_INIT (line_display->left_margin,
+                                                            selection_y,
+                                                            screen_width,
+                                                            selection_height));
+
+          gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_NORMAL);
+          pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
+                                           line,
+                                           line_rect.x,
+                                           baseline);
+
+          /* Check if some part of the line is selected; the newline
+           * that is after line->length for the last line of the
+           * paragraph counts as part of the line for this
+           */
+          if ((selection_start_index < byte_offset + line->length ||
+               (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line 
(iter))) &&
+              selection_end_index > byte_offset)
+            {
+              gint *ranges = NULL;
+              gint n_ranges, i;
+
+              pango_layout_line_get_x_ranges (line, selection_start_index, selection_end_index, &ranges, 
&n_ranges);
+
+              gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_SELECTED);
+
+              for (i = 0; i < n_ranges; i++)
+                {
+                  graphene_rect_t bounds;
+
+                  bounds.origin.x = line_display->x_offset + PANGO_PIXELS (ranges[2*i]);
+                  bounds.origin.y = selection_y;
+                  bounds.size.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
+                  bounds.size.height = selection_height;
+
+                  gtk_snapshot_append_color (crenderer->snapshot, selection, &bounds);
+                  gtk_snapshot_push_clip (crenderer->snapshot, &bounds);
+                  pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
+                                                   line,
+                                                   line_rect.x,
+                                                   baseline);
+                  gtk_snapshot_pop (crenderer->snapshot);
+                }
+
+              g_free (ranges);
+
+              /* Paint in the ends of the line */
+              if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
+                  ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
+                   (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + 
line->length)))
+                gtk_snapshot_append_color (crenderer->snapshot,
+                                           selection,
+                                           &GRAPHENE_RECT_INIT (line_display->left_margin,
+                                                                selection_y,
+                                                                PANGO_PIXELS (line_rect.x) - 
line_display->left_margin,
+                                                                selection_height));
+
+              if (line_rect.x + line_rect.width <
+                  (screen_width + line_display->left_margin) * PANGO_SCALE &&
+                  ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + 
line->length) ||
+                   (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
+                {
+                  int nonlayout_width = line_display->left_margin
+                                      + screen_width
+                                      - PANGO_PIXELS (line_rect.x)
+                                      - PANGO_PIXELS (line_rect.width);
+                  gtk_snapshot_append_color (crenderer->snapshot,
+                                             selection,
+                                             &GRAPHENE_RECT_INIT (PANGO_PIXELS (line_rect.x) + PANGO_PIXELS 
(line_rect.width),
+                                                                  selection_y,
+                                                                  nonlayout_width,
+                                                                  selection_height));
+                }
+            }
+          else if (line_display->has_block_cursor &&
+                   gtk_widget_has_focus (crenderer->widget) &&
+                   byte_offset <= line_display->insert_index &&
+                   (line_display->insert_index < byte_offset + line->length ||
+                    (at_last_line && line_display->insert_index == byte_offset + line->length)))
+            {
+              GdkRGBA cursor_color;
+              graphene_rect_t bounds = {
+                .origin.x = line_display->x_offset + line_display->block_cursor.x,
+                .origin.y = line_display->block_cursor.y + line_display->top_margin,
+                .size.width = line_display->block_cursor.width,
+                .size.height = line_display->block_cursor.height,
+              };
+
+              /* we draw text using base color on filled cursor rectangle of cursor color
+               * (normally white on black) */
+              _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
+
+              gtk_snapshot_append_color (crenderer->snapshot, &cursor_color, &bounds);
+
+              /* draw text under the cursor if any */
+              if (!line_display->cursor_at_line_end)
+                {
+                  gsk_pango_renderer_set_state (crenderer, GSK_PANGO_RENDERER_CURSOR);
+                  gtk_snapshot_push_clip (crenderer->snapshot, &bounds);
+                  pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
+                                                   line,
+                                                   line_rect.x,
+                                                   baseline);
+                  gtk_snapshot_pop (crenderer->snapshot);
+                }
+            }
+        }
+
+      byte_offset += line->length;
+    }
+  while (pango_layout_iter_next_line (iter));
+
+  gtk_snapshot_restore (crenderer->snapshot);
+
+  gdk_rgba_free (selection);
+  pango_layout_iter_free (iter);
+}
+
+void
+gtk_text_layout_snapshot (GtkTextLayout      *layout,
+                          GtkWidget          *widget,
+                          GtkSnapshot        *snapshot,
+                          const GdkRectangle *clip)
+{
+  GskPangoRenderer *crenderer;
+  GtkStyleContext *context;
+  gint offset_y;
+  GtkTextIter selection_start, selection_end;
+  gboolean have_selection;
+  GSList *line_list;
+  GSList *tmp_list;
+  GdkRGBA color;
+
+  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+  g_return_if_fail (layout->default_style != NULL);
+  g_return_if_fail (layout->buffer != NULL);
+  g_return_if_fail (snapshot != NULL);
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_get_color (context, &color);
+
+  line_list = gtk_text_layout_get_lines (layout, clip->y, clip->y + clip->height, &offset_y);
+
+  if (line_list == NULL)
+    return; /* nothing on the screen */
+
+  crenderer = gsk_pango_renderer_acquire ();
+
+  crenderer->widget = widget;
+  crenderer->snapshot = snapshot;
+  crenderer->fg_color = color;
+
+  gtk_text_layout_wrap_loop_start (layout);
+
+  have_selection = gtk_text_buffer_get_selection_bounds (layout->buffer,
+                                                         &selection_start,
+                                                         &selection_end);
+
+  tmp_list = line_list;
+  while (tmp_list != NULL)
+    {
+      GtkTextLine *line = tmp_list->data;
+      GtkTextLineDisplay *line_display;
+      gint selection_start_index = -1;
+      gint selection_end_index = -1;
+
+      line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
+
+      if (line_display->height > 0)
+        {
+          g_assert (line_display->layout != NULL);
+
+          if (have_selection)
+            {
+              GtkTextIter line_start, line_end;
+              gint byte_count;
+
+              gtk_text_layout_get_iter_at_line (layout, &line_start, line, 0);
+              line_end = line_start;
+              if (!gtk_text_iter_ends_line (&line_end))
+                gtk_text_iter_forward_to_line_end (&line_end);
+              byte_count = gtk_text_iter_get_visible_line_index (&line_end);
+
+              if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
+                  gtk_text_iter_compare (&selection_end, &line_start) >= 0)
+                {
+                  if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
+                    selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
+                  else
+                    selection_start_index = -1;
+
+                  if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
+                    selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
+                  else
+                    selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
+                }
+            }
+
+          render_para (crenderer, offset_y, line_display,
+                       selection_start_index, selection_end_index);
+
+          /* We paint the cursors last, because they overlap another chunk
+           * and need to appear on top.
+           */
+          if (line_display->cursors != NULL)
+            {
+              int i;
+
+              for (i = 0; i < line_display->cursors->len; i++)
+                {
+                  int index;
+                  PangoDirection dir;
+
+                  index = g_array_index(line_display->cursors, int, i);
+                  dir = (line_display->direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : 
PANGO_DIRECTION_LTR;
+
+                  gtk_snapshot_render_insertion_cursor (crenderer->snapshot, context,
+                                                        line_display->x_offset, offset_y + 
line_display->top_margin,
+                                                        line_display->layout, index, dir);
+                }
+            }
+        } /* line_display->height > 0 */
+
+      offset_y += line_display->height;
+
+      gtk_text_layout_free_line_display (layout, line_display);
+
+      tmp_list = tmp_list->next;
+    }
+
+  gtk_text_layout_wrap_loop_end (layout);
+
+  g_slist_free (line_list);
+
+  gsk_pango_renderer_release (crenderer);
+}
diff --git a/gtk/gtktextlayoutprivate.h b/gtk/gtktextlayoutprivate.h
index 4a44731373..7ba6627b04 100644
--- a/gtk/gtktextlayoutprivate.h
+++ b/gtk/gtktextlayoutprivate.h
@@ -113,6 +113,14 @@ struct _GtkTextLayout
   gint width;
   gint height;
 
+  /* Pixel offsets from the left and from the top to be used when we
+   * draw; these allow us to create left/top margins. We don't need
+   * anything special for bottom/right margins, because those don't
+   * affect drawing.
+   */
+  /* gint left_edge; */
+  /* gint top_edge; */
+
   GtkTextBuffer *buffer;
 
   gint left_padding;
@@ -137,7 +145,7 @@ struct _GtkTextLayout
 
   /* Whether we are allowed to wrap right now */
   gint wrap_loop_count;
-
+  
   /* Whether to show the insertion cursor */
   guint cursor_visible : 1;
 
@@ -389,6 +397,11 @@ void gtk_text_anchored_child_set_layout     (GtkWidget          *child,
 
 void gtk_text_layout_spew (GtkTextLayout *layout);
 
+void gtk_text_layout_snapshot (GtkTextLayout        *layout,
+                               GtkWidget            *widget,
+                               GtkSnapshot          *snapshot,
+                               const GdkRectangle   *clip);
+
 G_END_DECLS
 
 #endif  /* __GTK_TEXT_LAYOUT_PRIVATE_H__ */
diff --git a/gtk/gtktextutil.c b/gtk/gtktextutil.c
index 19c2f7982d..e806c6a205 100644
--- a/gtk/gtktextutil.c
+++ b/gtk/gtktextutil.c
@@ -27,8 +27,8 @@
 #include "gtktextview.h"
 #include "gtktextutil.h"
 
-#include "gtktextdisplayprivate.h"
 #include "gtktextbuffer.h"
+#include "gtktextlayoutprivate.h"
 #include "gtkmenuitem.h"
 #include "gtkintl.h"
 
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index baaf8e4bff..73c8521455 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -41,7 +41,6 @@
 #include "gtkrenderbackgroundprivate.h"
 #include "gtkseparatormenuitem.h"
 #include "gtksettings.h"
-#include "gtktextdisplayprivate.h"
 #include "gtktextiterprivate.h"
 #include "gtkimmulticontext.h"
 #include "gtkprivate.h"
diff --git a/gtk/meson.build b/gtk/meson.build
index 428e1e49ab..22fe73077f 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -366,7 +366,6 @@ gtk_public_sources = files([
   'gtktextattributes.c',
   'gtktextbuffer.c',
   'gtktextchild.c',
-  'gtktextdisplay.c',
   'gtktexthandle.c',
   'gtktextiter.c',
   'gtktextlayout.c',


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