Commit 4ff9163c authored by Christian Hergert's avatar Christian Hergert

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.
parent 45ebe47d
......@@ -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);
}
......@@ -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
......
This diff is collapsed.
/* gtktextdisplay.c - display layed-out text
*
* Copyright (c) 1992-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
* Copyright (c) 2000 Red Hat, Inc.
* Tk->Gtk port by Havoc Pennington
*
* This file can be used under your choice of two licenses, the LGPL
* and the original Tk license.
*
* LGPL:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that 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 library. If not, see <http://www.gnu.org/licenses/>.
*
* Original Tk license:
*
* This software is copyrighted by the Regents of the University of
* California, Sun Microsystems, Inc., and other parties. The
* following terms apply to all files associated with the software
* unless explicitly disclaimed in individual files.
*
* The authors hereby grant permission to use, copy, modify,
* distribute, and license this software and its documentation for any
* purpose, provided that existing copyright notices are retained in
* all copies and that this notice is included verbatim in any
* distributions. No written agreement, license, or royalty fee is
* required for any of the authorized uses. Modifications to this
* software may be copyrighted by their authors and need not follow
* the licensing terms described here, provided that the new terms are
* clearly indicated on the first page of each file where they apply.
*
* IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
* DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
* OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
* NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* GOVERNMENT USE: If you are acquiring this software on behalf of the
* U.S. government, the Government shall have only "Restricted Rights"
* in the software and related documentation as defined in the Federal
* Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
* are acquiring the software on behalf of the Department of Defense,
* the software shall be classified as "Commercial Computer Software"
* and the Government shall have only "Restricted Rights" as defined
* in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
* foregoing, the authors grant the U.S. Government and others acting
* in its behalf permission to use and distribute the software in
* accordance with the terms specified in this license.
*
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __GTK_TEXT_DISPLAY_PRIVATE_H__
#define __GTK_TEXT_DISPLAY_PRIVATE_H__
#include "gtktextlayoutprivate.h"
G_BEGIN_DECLS
/* A semi-public header intended for use by code that also
* uses GtkTextLayout
*/
/* The drawable should be pre-initialized to your preferred background.
* widget - Widget to grab some style info from
* snapshot - Snapshot to render to, matrix set so that (0, 0)
* is the top left of the layout
* clip - visible area
*/
void gtk_text_layout_snapshot (GtkTextLayout *layout,
GtkWidget *widget,
GtkSnapshot *snapshot,
const GdkRectangle *clip);
G_END_DECLS
#endif /* __GTK_TEXT_DISPLAY_PRIVATE_H__ */
......@@ -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,336 @@ 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;
graphene_rect_init (&crenderer->bounds,
clip->x,
clip->y,
clip->width,
clip->height);
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)