Compare commits
3 Commits
matthiasc/
...
wip/cherge
Author | SHA1 | Date | |
---|---|---|---|
|
908b708d33 | ||
|
b2f4e9b898 | ||
|
dcb81155db |
108
gtk/gskpango.c
108
gtk/gskpango.c
@@ -43,6 +43,32 @@ gsk_pango_renderer_set_state (GskPangoRenderer *crenderer,
|
||||
crenderer->state = state;
|
||||
}
|
||||
|
||||
static GtkSnapshot *
|
||||
get_snapshot (GskPangoRenderer *crenderer,
|
||||
gboolean foreground)
|
||||
{
|
||||
switch (crenderer->state)
|
||||
{
|
||||
case GSK_PANGO_RENDERER_CURSOR:
|
||||
return crenderer->cursors_snapshot;
|
||||
|
||||
case GSK_PANGO_RENDERER_NORMAL:
|
||||
if (foreground)
|
||||
return crenderer->fg_snapshot;
|
||||
else
|
||||
return crenderer->bg_snapshot;
|
||||
|
||||
case GSK_PANGO_RENDERER_SELECTED:
|
||||
if (foreground)
|
||||
return crenderer->selection_fg_snapshot;
|
||||
else
|
||||
return crenderer->selection_bg_snapshot;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_color (GskPangoRenderer *crenderer,
|
||||
PangoRenderPart part,
|
||||
@@ -97,7 +123,7 @@ gsk_pango_renderer_draw_glyph_item (PangoRenderer *renderer,
|
||||
|
||||
get_color (crenderer, PANGO_RENDER_PART_FOREGROUND, &color);
|
||||
|
||||
gtk_snapshot_append_text (crenderer->snapshot,
|
||||
gtk_snapshot_append_text (get_snapshot (crenderer, TRUE),
|
||||
glyph_item->item->analysis.font,
|
||||
glyph_item->glyphs,
|
||||
&color,
|
||||
@@ -117,7 +143,7 @@ gsk_pango_renderer_draw_rectangle (PangoRenderer *renderer,
|
||||
GdkRGBA rgba;
|
||||
|
||||
get_color (crenderer, part, &rgba);
|
||||
gtk_snapshot_append_color (crenderer->snapshot,
|
||||
gtk_snapshot_append_color (get_snapshot (crenderer, FALSE),
|
||||
&rgba,
|
||||
&GRAPHENE_RECT_INIT ((double)x / PANGO_SCALE,
|
||||
(double)y / PANGO_SCALE,
|
||||
@@ -139,7 +165,8 @@ gsk_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
|
||||
cairo_t *cr;
|
||||
gdouble x, y;
|
||||
|
||||
cr = gtk_snapshot_append_cairo (crenderer->snapshot, &crenderer->bounds);
|
||||
cr = gtk_snapshot_append_cairo (get_snapshot (crenderer, TRUE),
|
||||
&crenderer->bounds);
|
||||
|
||||
set_color (crenderer, part, cr);
|
||||
|
||||
@@ -168,12 +195,14 @@ gsk_pango_renderer_draw_error_underline (PangoRenderer *renderer,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
|
||||
GtkSnapshot *snapshot;
|
||||
GdkRGBA rgba;
|
||||
double xx, yy, ww, hh;
|
||||
double hs;
|
||||
double e, o;
|
||||
|
||||
GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
|
||||
snapshot = get_snapshot (crenderer, TRUE);
|
||||
|
||||
xx = (double)x / PANGO_SCALE;
|
||||
yy = (double)y / PANGO_SCALE;
|
||||
@@ -185,18 +214,18 @@ gsk_pango_renderer_draw_error_underline (PangoRenderer *renderer,
|
||||
|
||||
#if 0
|
||||
gdk_rgba_parse (&rgba, "yellow");
|
||||
gtk_snapshot_append_color (crenderer->snapshot, &rgba,
|
||||
gtk_snapshot_append_color (snapshot, &rgba,
|
||||
&GRAPHENE_RECT_INIT (xx, yy, ww, hh));
|
||||
#endif
|
||||
|
||||
|
||||
get_color (crenderer, PANGO_RENDER_PART_UNDERLINE, &rgba);
|
||||
gtk_snapshot_save (crenderer->snapshot);
|
||||
gtk_snapshot_translate (crenderer->snapshot,
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_translate (snapshot,
|
||||
&GRAPHENE_POINT_INIT (xx, yy));
|
||||
|
||||
gtk_snapshot_rotate (crenderer->snapshot, 45);
|
||||
gtk_snapshot_translate (crenderer->snapshot,
|
||||
gtk_snapshot_rotate (snapshot, 45);
|
||||
gtk_snapshot_translate (snapshot,
|
||||
&GRAPHENE_POINT_INIT (e / 2 + hs * HEIGHT_RATIO,
|
||||
- hs * HEIGHT_RATIO));
|
||||
|
||||
@@ -206,7 +235,7 @@ gsk_pango_renderer_draw_error_underline (PangoRenderer *renderer,
|
||||
if (o + hs * (1 + HEIGHT_RATIO) >= ww)
|
||||
break;
|
||||
|
||||
gtk_snapshot_append_color (crenderer->snapshot, &rgba,
|
||||
gtk_snapshot_append_color (snapshot, &rgba,
|
||||
&GRAPHENE_RECT_INIT (xx, yy, hh, hh * HEIGHT_RATIO));
|
||||
|
||||
xx += hh * (1 - HEIGHT_RATIO);
|
||||
@@ -216,13 +245,13 @@ gsk_pango_renderer_draw_error_underline (PangoRenderer *renderer,
|
||||
if (o + hs * (1 + HEIGHT_RATIO) >= ww)
|
||||
break;
|
||||
|
||||
gtk_snapshot_append_color (crenderer->snapshot, &rgba,
|
||||
gtk_snapshot_append_color (snapshot, &rgba,
|
||||
&GRAPHENE_RECT_INIT (xx, yy, hh * HEIGHT_RATIO, hh));
|
||||
|
||||
o += hs * (1 - HEIGHT_RATIO);
|
||||
}
|
||||
|
||||
gtk_snapshot_restore (crenderer->snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -232,6 +261,7 @@ gsk_pango_renderer_draw_shape (PangoRenderer *renderer,
|
||||
int y)
|
||||
{
|
||||
GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
|
||||
GtkSnapshot *snapshot;
|
||||
cairo_t *cr;
|
||||
PangoLayout *layout;
|
||||
PangoCairoShapeRendererFunc shape_renderer;
|
||||
@@ -239,7 +269,8 @@ gsk_pango_renderer_draw_shape (PangoRenderer *renderer,
|
||||
double base_x = (double)x / PANGO_SCALE;
|
||||
double base_y = (double)y / PANGO_SCALE;
|
||||
|
||||
cr = gtk_snapshot_append_cairo (crenderer->snapshot, &crenderer->bounds);
|
||||
snapshot = get_snapshot (crenderer, TRUE);
|
||||
cr = gtk_snapshot_append_cairo (snapshot, &crenderer->bounds);
|
||||
|
||||
layout = pango_renderer_get_layout (renderer);
|
||||
if (!layout)
|
||||
@@ -419,6 +450,12 @@ gsk_pango_renderer_acquire (void)
|
||||
renderer = g_object_new (GSK_TYPE_PANGO_RENDERER, NULL);
|
||||
}
|
||||
|
||||
renderer->fg_snapshot = gtk_snapshot_new ();
|
||||
renderer->bg_snapshot = gtk_snapshot_new ();
|
||||
renderer->selection_fg_snapshot = gtk_snapshot_new ();
|
||||
renderer->selection_bg_snapshot = gtk_snapshot_new ();
|
||||
renderer->cursors_snapshot = gtk_snapshot_new ();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@@ -428,7 +465,6 @@ gsk_pango_renderer_release (GskPangoRenderer *renderer)
|
||||
if (G_LIKELY (renderer->is_cached_renderer))
|
||||
{
|
||||
renderer->widget = NULL;
|
||||
renderer->snapshot = NULL;
|
||||
|
||||
if (renderer->error_color)
|
||||
{
|
||||
@@ -436,12 +472,54 @@ gsk_pango_renderer_release (GskPangoRenderer *renderer)
|
||||
renderer->error_color = NULL;
|
||||
}
|
||||
|
||||
g_clear_object (&renderer->fg_snapshot);
|
||||
g_clear_object (&renderer->bg_snapshot);
|
||||
g_clear_object (&renderer->selection_fg_snapshot);
|
||||
g_clear_object (&renderer->selection_bg_snapshot);
|
||||
g_clear_object (&renderer->cursors_snapshot);
|
||||
|
||||
G_UNLOCK (cached_renderer);
|
||||
}
|
||||
else
|
||||
g_object_unref (renderer);
|
||||
}
|
||||
|
||||
static void
|
||||
append_to_snapshot (GtkSnapshot *snapshot,
|
||||
GtkSnapshot **source)
|
||||
{
|
||||
g_assert (GTK_IS_SNAPSHOT (snapshot));
|
||||
g_assert (source != NULL);
|
||||
|
||||
if (*source != NULL)
|
||||
{
|
||||
GskRenderNode *node;
|
||||
|
||||
node = gtk_snapshot_free_to_node (*source);
|
||||
*source = NULL;
|
||||
|
||||
if (node != NULL)
|
||||
{
|
||||
gtk_snapshot_append_node (snapshot, node);
|
||||
gsk_render_node_unref (node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_pango_renderer_apply (GskPangoRenderer *crenderer,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_PANGO_RENDERER (crenderer));
|
||||
g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
|
||||
|
||||
append_to_snapshot (snapshot, &crenderer->bg_snapshot);
|
||||
append_to_snapshot (snapshot, &crenderer->fg_snapshot);
|
||||
append_to_snapshot (snapshot, &crenderer->selection_bg_snapshot);
|
||||
append_to_snapshot (snapshot, &crenderer->selection_fg_snapshot);
|
||||
append_to_snapshot (snapshot, &crenderer->cursors_snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_snapshot_append_layout:
|
||||
* @snapshot: a #GtkSnapshot
|
||||
@@ -465,7 +543,6 @@ gtk_snapshot_append_layout (GtkSnapshot *snapshot,
|
||||
|
||||
crenderer = gsk_pango_renderer_acquire ();
|
||||
|
||||
crenderer->snapshot = snapshot;
|
||||
crenderer->fg_color = *color;
|
||||
|
||||
pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
|
||||
@@ -473,5 +550,6 @@ gtk_snapshot_append_layout (GtkSnapshot *snapshot,
|
||||
|
||||
pango_renderer_draw_layout (PANGO_RENDERER (crenderer), layout, 0, 0);
|
||||
|
||||
gsk_pango_renderer_apply (crenderer, snapshot);
|
||||
gsk_pango_renderer_release (crenderer);
|
||||
}
|
||||
|
@@ -52,7 +52,11 @@ struct _GskPangoRenderer
|
||||
PangoRenderer parent_instance;
|
||||
|
||||
GtkWidget *widget;
|
||||
GtkSnapshot *snapshot;
|
||||
GtkSnapshot *fg_snapshot;
|
||||
GtkSnapshot *bg_snapshot;
|
||||
GtkSnapshot *selection_fg_snapshot;
|
||||
GtkSnapshot *selection_bg_snapshot;
|
||||
GtkSnapshot *cursors_snapshot;
|
||||
GdkRGBA fg_color;
|
||||
graphene_rect_t bounds;
|
||||
|
||||
@@ -75,6 +79,8 @@ void gsk_pango_renderer_set_state (GskPangoRenderer *crenderer
|
||||
GskPangoRendererState state);
|
||||
GskPangoRenderer *gsk_pango_renderer_acquire (void);
|
||||
void gsk_pango_renderer_release (GskPangoRenderer *crenderer);
|
||||
void gsk_pango_renderer_apply (GskPangoRenderer *crenderer,
|
||||
GtkSnapshot *snapshot);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -5358,6 +5358,25 @@ gtk_text_iter_equal (const GtkTextIter *lhs,
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_text_iter_same_line (const GtkTextIter *lhs,
|
||||
const GtkTextIter *rhs)
|
||||
{
|
||||
GtkTextRealIter *real_lhs;
|
||||
GtkTextRealIter *real_rhs;
|
||||
|
||||
real_lhs = gtk_text_iter_make_surreal (lhs);
|
||||
real_rhs = gtk_text_iter_make_surreal (rhs);
|
||||
|
||||
if (real_lhs == NULL || real_rhs == NULL)
|
||||
return FALSE;
|
||||
|
||||
check_invariants (lhs);
|
||||
check_invariants (rhs);
|
||||
|
||||
return real_lhs->line == real_rhs->line;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_text_iter_compare:
|
||||
* @lhs: a #GtkTextIter
|
||||
|
@@ -41,6 +41,8 @@ gboolean _gtk_text_iter_forward_indexable_segment (GtkTextIter
|
||||
gboolean _gtk_text_iter_backward_indexable_segment (GtkTextIter *iter);
|
||||
gint _gtk_text_iter_get_segment_byte (const GtkTextIter *iter);
|
||||
gint _gtk_text_iter_get_segment_char (const GtkTextIter *iter);
|
||||
gboolean _gtk_text_iter_same_line (const GtkTextIter *lhs,
|
||||
const GtkTextIter *rhs);
|
||||
|
||||
gboolean gtk_text_iter_get_attributes (const GtkTextIter *iter,
|
||||
GtkTextAttributes *values);
|
||||
|
@@ -82,6 +82,7 @@
|
||||
#include "gtktextbtree.h"
|
||||
#include "gtktextbufferprivate.h"
|
||||
#include "gtktextiterprivate.h"
|
||||
#include "gtktextlinedisplaycacheprivate.h"
|
||||
#include "gtktextutil.h"
|
||||
#include "gskpango.h"
|
||||
#include "gtkintl.h"
|
||||
@@ -101,6 +102,9 @@ struct _GtkTextLayoutPrivate
|
||||
direction only influences the direction of the cursor line.
|
||||
*/
|
||||
GtkTextLine *cursor_line;
|
||||
|
||||
/* Cache for GtkTextLineDisplay to reduce overhead creating layouts */
|
||||
GtkTextLineDisplayCache *cache;
|
||||
};
|
||||
|
||||
static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
|
||||
@@ -133,19 +137,33 @@ static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
|
||||
|
||||
static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
|
||||
|
||||
static void gtk_text_layout_mark_set_handler (GtkTextBuffer *buffer,
|
||||
const GtkTextIter *location,
|
||||
GtkTextMark *mark,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *iter,
|
||||
gchar *str,
|
||||
gint len,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_buffer_delete_range (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *start,
|
||||
GtkTextIter *end,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_after_mark_set_handler (GtkTextBuffer *buffer,
|
||||
const GtkTextIter *location,
|
||||
GtkTextMark *mark,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_after_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *iter,
|
||||
gchar *str,
|
||||
gint len,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_after_buffer_delete_range (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *start,
|
||||
GtkTextIter *end,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_before_mark_set_handler (GtkTextBuffer *buffer,
|
||||
const GtkTextIter *location,
|
||||
GtkTextMark *mark,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_before_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *iter,
|
||||
gchar *str,
|
||||
gint len,
|
||||
gpointer data);
|
||||
static void gtk_text_layout_before_buffer_delete_range (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *start,
|
||||
GtkTextIter *end,
|
||||
gpointer data);
|
||||
|
||||
|
||||
static void gtk_text_layout_update_cursor_line (GtkTextLayout *layout);
|
||||
|
||||
@@ -182,9 +200,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (GtkTextLayout, gtk_text_layout, G_TYPE_OBJECT)
|
||||
static void
|
||||
gtk_text_layout_dispose (GObject *object)
|
||||
{
|
||||
GtkTextLayout *layout;
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (object);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
layout = GTK_TEXT_LAYOUT (object);
|
||||
g_clear_pointer (&priv->cache, gtk_text_line_display_cache_free);
|
||||
|
||||
gtk_text_layout_set_buffer (layout, NULL);
|
||||
|
||||
@@ -197,8 +216,6 @@ gtk_text_layout_dispose (GObject *object)
|
||||
g_clear_object (&layout->ltr_context);
|
||||
g_clear_object (&layout->rtl_context);
|
||||
|
||||
g_clear_pointer (&layout->one_display_cache, gtk_text_line_display_unref);
|
||||
|
||||
if (layout->preedit_attrs != NULL)
|
||||
{
|
||||
pango_attr_list_unref (layout->preedit_attrs);
|
||||
@@ -275,7 +292,10 @@ gtk_text_layout_class_init (GtkTextLayoutClass *klass)
|
||||
static void
|
||||
gtk_text_layout_init (GtkTextLayout *text_layout)
|
||||
{
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (text_layout);
|
||||
|
||||
text_layout->cursor_visible = TRUE;
|
||||
priv->cache = gtk_text_line_display_cache_new ();
|
||||
}
|
||||
|
||||
GtkTextLayout*
|
||||
@@ -315,14 +335,24 @@ gtk_text_layout_set_buffer (GtkTextLayout *layout,
|
||||
_gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
layout);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_mark_set_handler),
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_after_mark_set_handler),
|
||||
layout);
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_buffer_insert_text),
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_after_buffer_insert_text),
|
||||
layout);
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_buffer_delete_range),
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_after_buffer_delete_range),
|
||||
layout);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_before_mark_set_handler),
|
||||
layout);
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_before_buffer_insert_text),
|
||||
layout);
|
||||
g_signal_handlers_disconnect_by_func (layout->buffer,
|
||||
G_CALLBACK (gtk_text_layout_before_buffer_delete_range),
|
||||
layout);
|
||||
|
||||
g_object_unref (layout->buffer);
|
||||
@@ -339,11 +369,18 @@ gtk_text_layout_set_buffer (GtkTextLayout *layout,
|
||||
|
||||
/* Bind to all signals that move the insert mark. */
|
||||
g_signal_connect_after (layout->buffer, "mark-set",
|
||||
G_CALLBACK (gtk_text_layout_mark_set_handler), layout);
|
||||
G_CALLBACK (gtk_text_layout_after_mark_set_handler), layout);
|
||||
g_signal_connect_after (layout->buffer, "insert-text",
|
||||
G_CALLBACK (gtk_text_layout_buffer_insert_text), layout);
|
||||
G_CALLBACK (gtk_text_layout_after_buffer_insert_text), layout);
|
||||
g_signal_connect_after (layout->buffer, "delete-range",
|
||||
G_CALLBACK (gtk_text_layout_buffer_delete_range), layout);
|
||||
G_CALLBACK (gtk_text_layout_after_buffer_delete_range), layout);
|
||||
|
||||
g_signal_connect (layout->buffer, "mark-set",
|
||||
G_CALLBACK (gtk_text_layout_before_mark_set_handler), layout);
|
||||
g_signal_connect (layout->buffer, "insert-text",
|
||||
G_CALLBACK (gtk_text_layout_before_buffer_insert_text), layout);
|
||||
g_signal_connect (layout->buffer, "delete-range",
|
||||
G_CALLBACK (gtk_text_layout_before_buffer_delete_range), layout);
|
||||
|
||||
gtk_text_layout_update_cursor_line (layout);
|
||||
}
|
||||
@@ -627,46 +664,26 @@ gtk_text_layout_emit_changed (GtkTextLayout *layout,
|
||||
g_signal_emit (layout, signals[CHANGED], 0, y, old_height, new_height);
|
||||
}
|
||||
|
||||
static void
|
||||
text_layout_changed (GtkTextLayout *layout,
|
||||
gint y,
|
||||
gint old_height,
|
||||
gint new_height,
|
||||
gboolean cursors_only)
|
||||
{
|
||||
/* Check if the range intersects our cached line display,
|
||||
* and invalidate the cached line if so.
|
||||
*/
|
||||
if (layout->one_display_cache)
|
||||
{
|
||||
GtkTextLine *line = layout->one_display_cache->line;
|
||||
gint cache_y = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
line, layout);
|
||||
gint cache_height = layout->one_display_cache->height;
|
||||
|
||||
if (cache_y + cache_height > y && cache_y < y + old_height)
|
||||
gtk_text_layout_invalidate_cache (layout, line, cursors_only);
|
||||
}
|
||||
|
||||
gtk_text_layout_emit_changed (layout, y, old_height, new_height);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_layout_changed (GtkTextLayout *layout,
|
||||
gint y,
|
||||
gint old_height,
|
||||
gint new_height)
|
||||
{
|
||||
text_layout_changed (layout, y, old_height, new_height, FALSE);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
gtk_text_line_display_cache_invalidate_y_range (priv->cache, layout, y, old_height, FALSE);
|
||||
gtk_text_layout_emit_changed (layout, y, old_height, new_height);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_layout_cursors_changed (GtkTextLayout *layout,
|
||||
gint y,
|
||||
gint old_height,
|
||||
gint new_height)
|
||||
gint old_height,
|
||||
gint new_height)
|
||||
{
|
||||
text_layout_changed (layout, y, old_height, new_height, TRUE);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
gtk_text_line_display_cache_invalidate_y_range (priv->cache, layout, y, old_height, TRUE);
|
||||
gtk_text_layout_emit_changed (layout, y, old_height, new_height);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -819,20 +836,16 @@ gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean cursors_only)
|
||||
{
|
||||
if (layout->one_display_cache && line == layout->one_display_cache->line)
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
g_assert (GTK_IS_TEXT_LAYOUT (layout));
|
||||
|
||||
if (priv->cache != NULL)
|
||||
{
|
||||
if (cursors_only)
|
||||
{
|
||||
GtkTextLineDisplay *display = layout->one_display_cache;
|
||||
|
||||
g_clear_pointer (&display->cursors, g_array_unref);
|
||||
display->cursors_invalid = TRUE;
|
||||
display->has_block_cursor = FALSE;
|
||||
}
|
||||
gtk_text_line_display_cache_invalidate_cursors (priv->cache, line);
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&layout->one_display_cache, gtk_text_line_display_unref);
|
||||
}
|
||||
gtk_text_line_display_cache_invalidate_line (priv->cache, line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,7 +853,7 @@ gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
|
||||
*/
|
||||
static void
|
||||
gtk_text_layout_invalidate_cursor_line (GtkTextLayout *layout,
|
||||
gboolean cursors_only)
|
||||
gboolean cursors_only)
|
||||
{
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
GtkTextLineData *line_data;
|
||||
@@ -849,22 +862,20 @@ gtk_text_layout_invalidate_cursor_line (GtkTextLayout *layout,
|
||||
return;
|
||||
|
||||
line_data = _gtk_text_line_get_data (priv->cursor_line, layout);
|
||||
if (line_data)
|
||||
|
||||
if (line_data != NULL)
|
||||
{
|
||||
if (cursors_only)
|
||||
gtk_text_layout_invalidate_cache (layout, priv->cursor_line, TRUE);
|
||||
else
|
||||
{
|
||||
gtk_text_layout_invalidate_cache (layout, priv->cursor_line, FALSE);
|
||||
_gtk_text_line_invalidate_wrap (priv->cursor_line, line_data);
|
||||
}
|
||||
gtk_text_layout_invalidate_cache (layout, priv->cursor_line, cursors_only);
|
||||
|
||||
if (!cursors_only)
|
||||
_gtk_text_line_invalidate_wrap (priv->cursor_line, line_data);
|
||||
|
||||
gtk_text_layout_invalidated (layout);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_layout_update_cursor_line(GtkTextLayout *layout)
|
||||
gtk_text_layout_update_cursor_line (GtkTextLayout *layout)
|
||||
{
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
GtkTextIter iter;
|
||||
@@ -873,6 +884,8 @@ gtk_text_layout_update_cursor_line(GtkTextLayout *layout)
|
||||
gtk_text_buffer_get_insert (layout->buffer));
|
||||
|
||||
priv->cursor_line = _gtk_text_iter_get_text_line (&iter);
|
||||
|
||||
gtk_text_line_display_cache_set_cursor_line (priv->cache, priv->cursor_line);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -924,34 +937,8 @@ gtk_text_layout_real_invalidate_cursors (GtkTextLayout *layout,
|
||||
const GtkTextIter *start,
|
||||
const GtkTextIter *end)
|
||||
{
|
||||
/* Check if the range intersects our cached line display,
|
||||
* and invalidate the cached line if so.
|
||||
*/
|
||||
if (layout->one_display_cache)
|
||||
{
|
||||
GtkTextIter line_start, line_end;
|
||||
GtkTextLine *line = layout->one_display_cache->line;
|
||||
|
||||
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);
|
||||
|
||||
if (gtk_text_iter_compare (start, end) > 0)
|
||||
{
|
||||
const GtkTextIter *tmp = start;
|
||||
start = end;
|
||||
end = tmp;
|
||||
}
|
||||
|
||||
if (gtk_text_iter_compare (&line_start, end) <= 0 &&
|
||||
gtk_text_iter_compare (start, &line_end) <= 0)
|
||||
{
|
||||
gtk_text_layout_invalidate_cache (layout, line, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
gtk_text_line_display_cache_invalidate_range (priv->cache, layout, start, end, TRUE);
|
||||
gtk_text_layout_invalidated (layout);
|
||||
}
|
||||
|
||||
@@ -2122,10 +2109,10 @@ add_preedit_attrs (GtkTextLayout *layout,
|
||||
|
||||
/* Iterate over the line and fill in display->cursors.
|
||||
* It’s a stripped copy of gtk_text_layout_get_line_display() */
|
||||
static void
|
||||
update_text_display_cursors (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
GtkTextLineDisplay *display)
|
||||
void
|
||||
gtk_text_layout_update_display_cursors (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
GtkTextLineDisplay *display)
|
||||
{
|
||||
GtkTextLineSegment *seg;
|
||||
GtkTextIter iter;
|
||||
@@ -2275,9 +2262,9 @@ tags_array_toggle_tag (GPtrArray *array,
|
||||
}
|
||||
|
||||
GtkTextLineDisplay *
|
||||
gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only)
|
||||
gtk_text_layout_create_display (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only)
|
||||
{
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
GtkTextLineDisplay *display;
|
||||
@@ -2305,26 +2292,10 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
|
||||
g_return_val_if_fail (line != NULL, NULL);
|
||||
|
||||
if (layout->one_display_cache)
|
||||
{
|
||||
if (line == layout->one_display_cache->line &&
|
||||
(size_only || !layout->one_display_cache->size_only))
|
||||
{
|
||||
if (!size_only)
|
||||
update_text_display_cursors (layout, line, layout->one_display_cache);
|
||||
return gtk_text_line_display_ref (layout->one_display_cache);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&layout->one_display_cache, gtk_text_line_display_unref);
|
||||
}
|
||||
}
|
||||
|
||||
DV (g_print ("creating one line display cache (%s)\n", G_STRLOC));
|
||||
|
||||
display = g_rc_box_new0 (GtkTextLineDisplay);
|
||||
|
||||
display->size_only = size_only;
|
||||
display->mru_link.data = display;
|
||||
display->size_only = !!size_only;
|
||||
display->line = line;
|
||||
display->insert_index = -1;
|
||||
|
||||
@@ -2335,7 +2306,7 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
if (totally_invisible_line (layout, line, &iter))
|
||||
{
|
||||
display->layout = pango_layout_new (layout->ltr_context);
|
||||
return display;
|
||||
return g_steal_pointer (&display);
|
||||
}
|
||||
|
||||
/* Find the bidi base direction */
|
||||
@@ -2347,7 +2318,7 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
line->dir_strong == PANGO_DIRECTION_NEUTRAL)
|
||||
{
|
||||
base_dir = (layout->keyboard_direction == GTK_TEXT_DIR_LTR) ?
|
||||
PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
|
||||
PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
/* Allocate space for flat text for buffer
|
||||
@@ -2371,7 +2342,7 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
seg->type == >k_text_child_type)
|
||||
{
|
||||
style = get_style (layout, tags);
|
||||
initial_toggle_segments = FALSE;
|
||||
initial_toggle_segments = FALSE;
|
||||
|
||||
/* We have to delay setting the paragraph values until we
|
||||
* hit the first texture or text segment because toggles at
|
||||
@@ -2401,9 +2372,9 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
*/
|
||||
|
||||
gint bytes = 0;
|
||||
GtkTextLineSegment *prev_seg = NULL;
|
||||
GtkTextLineSegment *prev_seg = NULL;
|
||||
|
||||
while (seg)
|
||||
while (seg)
|
||||
{
|
||||
if (seg->type == >k_text_char_type)
|
||||
{
|
||||
@@ -2412,35 +2383,35 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
buffer_byte_offset += seg->byte_count;
|
||||
bytes += seg->byte_count;
|
||||
}
|
||||
else if (seg->type == >k_text_right_mark_type ||
|
||||
seg->type == >k_text_left_mark_type)
|
||||
else if (seg->type == >k_text_right_mark_type ||
|
||||
seg->type == >k_text_left_mark_type)
|
||||
{
|
||||
/* If we have preedit string, break out of this loop - we'll almost
|
||||
* certainly have different attributes on the preedit string
|
||||
*/
|
||||
/* If we have preedit string, break out of this loop - we'll almost
|
||||
* certainly have different attributes on the preedit string
|
||||
*/
|
||||
|
||||
if (layout->preedit_len > 0 &&
|
||||
_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
seg->body.mark.obj))
|
||||
break;
|
||||
if (layout->preedit_len > 0 &&
|
||||
_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
seg->body.mark.obj))
|
||||
break;
|
||||
|
||||
if (seg->body.mark.visible)
|
||||
{
|
||||
cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset));
|
||||
cursor_segs = g_slist_prepend (cursor_segs, seg);
|
||||
if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
seg->body.mark.obj))
|
||||
display->insert_index = layout_byte_offset;
|
||||
}
|
||||
if (seg->body.mark.visible)
|
||||
{
|
||||
cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset));
|
||||
cursor_segs = g_slist_prepend (cursor_segs, seg);
|
||||
if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
seg->body.mark.obj))
|
||||
display->insert_index = layout_byte_offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
else
|
||||
break;
|
||||
|
||||
prev_seg = seg;
|
||||
prev_seg = seg;
|
||||
seg = seg->next;
|
||||
}
|
||||
|
||||
seg = prev_seg; /* Back up one */
|
||||
seg = prev_seg; /* Back up one */
|
||||
add_generic_attrs (layout, &style->appearance,
|
||||
bytes,
|
||||
attrs, layout_byte_offset - bytes,
|
||||
@@ -2503,42 +2474,42 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
/* Style may have changed, drop our
|
||||
current cached style */
|
||||
invalidate_cached_style (layout);
|
||||
/* Add the tag only after we have seen some non-toggle non-mark segment,
|
||||
* otherwise the tag is already accounted for by _gtk_text_btree_get_tags(). */
|
||||
if (!initial_toggle_segments)
|
||||
tags = tags_array_toggle_tag (tags, seg->body.toggle.info->tag);
|
||||
/* Add the tag only after we have seen some non-toggle non-mark segment,
|
||||
* otherwise the tag is already accounted for by _gtk_text_btree_get_tags(). */
|
||||
if (!initial_toggle_segments)
|
||||
tags = tags_array_toggle_tag (tags, seg->body.toggle.info->tag);
|
||||
}
|
||||
|
||||
/* Marks */
|
||||
else if (seg->type == >k_text_right_mark_type ||
|
||||
seg->type == >k_text_left_mark_type)
|
||||
{
|
||||
gint cursor_offset = 0;
|
||||
|
||||
/* At the insertion point, add the preedit string, if any */
|
||||
|
||||
if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
seg->body.mark.obj))
|
||||
{
|
||||
display->insert_index = layout_byte_offset;
|
||||
|
||||
if (layout->preedit_len > 0)
|
||||
{
|
||||
text_allocated += layout->preedit_len;
|
||||
text = g_realloc (text, text_allocated);
|
||||
gint cursor_offset = 0;
|
||||
|
||||
/* At the insertion point, add the preedit string, if any */
|
||||
|
||||
if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
|
||||
seg->body.mark.obj))
|
||||
{
|
||||
display->insert_index = layout_byte_offset;
|
||||
|
||||
if (layout->preedit_len > 0)
|
||||
{
|
||||
text_allocated += layout->preedit_len;
|
||||
text = g_realloc (text, text_allocated);
|
||||
|
||||
style = get_style (layout, tags);
|
||||
add_preedit_attrs (layout, style, attrs, layout_byte_offset, size_only);
|
||||
release_style (layout, style);
|
||||
style = get_style (layout, tags);
|
||||
add_preedit_attrs (layout, style, attrs, layout_byte_offset, size_only);
|
||||
release_style (layout, style);
|
||||
|
||||
memcpy (text + layout_byte_offset, layout->preedit_string, layout->preedit_len);
|
||||
layout_byte_offset += layout->preedit_len;
|
||||
memcpy (text + layout_byte_offset, layout->preedit_string, layout->preedit_len);
|
||||
layout_byte_offset += layout->preedit_len;
|
||||
/* DO NOT increment the buffer byte offset for preedit */
|
||||
|
||||
cursor_offset = layout->preedit_cursor - layout->preedit_len;
|
||||
}
|
||||
}
|
||||
|
||||
cursor_offset = layout->preedit_cursor - layout->preedit_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Display visible marks */
|
||||
|
||||
@@ -2623,17 +2594,17 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
gint excess = display->total_width - text_pixel_width;
|
||||
|
||||
switch (pango_layout_get_alignment (display->layout))
|
||||
{
|
||||
case PANGO_ALIGN_LEFT:
|
||||
{
|
||||
case PANGO_ALIGN_LEFT:
|
||||
default:
|
||||
break;
|
||||
case PANGO_ALIGN_CENTER:
|
||||
display->x_offset += excess / 2;
|
||||
break;
|
||||
case PANGO_ALIGN_RIGHT:
|
||||
display->x_offset += excess;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PANGO_ALIGN_CENTER:
|
||||
display->x_offset += excess / 2;
|
||||
break;
|
||||
case PANGO_ALIGN_RIGHT:
|
||||
display->x_offset += excess;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free this if we aren't in a loop */
|
||||
@@ -2645,19 +2616,31 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
if (tags != NULL)
|
||||
g_ptr_array_free (tags, TRUE);
|
||||
|
||||
g_assert (layout->one_display_cache == NULL);
|
||||
|
||||
layout->one_display_cache = gtk_text_line_display_ref (display);
|
||||
|
||||
if (saw_widget)
|
||||
allocate_child_widgets (layout, display);
|
||||
|
||||
return display;
|
||||
return g_steal_pointer (&display);
|
||||
}
|
||||
|
||||
GtkTextLineDisplay *
|
||||
gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only)
|
||||
{
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
return gtk_text_line_display_cache_get (priv->cache, layout, line, size_only);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_line_display_finalize (GtkTextLineDisplay *display)
|
||||
{
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->cache_iter == NULL);
|
||||
g_assert (display->mru_link.prev == NULL);
|
||||
g_assert (display->mru_link.next == NULL);
|
||||
g_assert (display->mru_link.data == display);
|
||||
|
||||
g_clear_object (&display->layout);
|
||||
g_clear_pointer (&display->cursors, g_array_unref);
|
||||
}
|
||||
@@ -3784,26 +3767,57 @@ gtk_text_layout_spew (GtkTextLayout *layout)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_layout_before_mark_set_handler (GtkTextBuffer *buffer,
|
||||
const GtkTextIter *location,
|
||||
GtkTextMark *mark,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
if (mark == gtk_text_buffer_get_insert (buffer))
|
||||
gtk_text_line_display_cache_set_cursor_line (priv->cache, NULL);
|
||||
}
|
||||
|
||||
/* Catch all situations that move the insertion point.
|
||||
*/
|
||||
static void
|
||||
gtk_text_layout_mark_set_handler (GtkTextBuffer *buffer,
|
||||
const GtkTextIter *location,
|
||||
GtkTextMark *mark,
|
||||
gpointer data)
|
||||
gtk_text_layout_after_mark_set_handler (GtkTextBuffer *buffer,
|
||||
const GtkTextIter *location,
|
||||
GtkTextMark *mark,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
if (mark == gtk_text_buffer_get_insert (buffer))
|
||||
gtk_text_layout_update_cursor_line (layout);
|
||||
{
|
||||
gtk_text_layout_update_cursor_line (layout);
|
||||
gtk_text_line_display_cache_set_cursor_line (priv->cache, priv->cursor_line);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_layout_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *iter,
|
||||
gchar *str,
|
||||
gint len,
|
||||
gpointer data)
|
||||
gtk_text_layout_before_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *iter,
|
||||
gchar *str,
|
||||
gint len,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
|
||||
|
||||
gtk_text_line_display_cache_invalidate_line (priv->cache, line);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_layout_after_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *iter,
|
||||
gchar *str,
|
||||
gint len,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
|
||||
|
||||
@@ -3811,16 +3825,55 @@ gtk_text_layout_buffer_insert_text (GtkTextBuffer *textbuffer,
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_layout_buffer_delete_range (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *start,
|
||||
GtkTextIter *end,
|
||||
gpointer data)
|
||||
gtk_text_layout_before_buffer_delete_range (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *start,
|
||||
GtkTextIter *end,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
|
||||
GtkTextLayoutPrivate *priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
gtk_text_line_display_cache_invalidate_range (priv->cache, layout, start, end, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_layout_after_buffer_delete_range (GtkTextBuffer *textbuffer,
|
||||
GtkTextIter *start,
|
||||
GtkTextIter *end,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextLayout *layout = GTK_TEXT_LAYOUT (data);
|
||||
|
||||
gtk_text_layout_update_cursor_line (layout);
|
||||
}
|
||||
|
||||
static void
|
||||
push_translate (GskPangoRenderer *crenderer,
|
||||
const graphene_point_t *point)
|
||||
{
|
||||
gtk_snapshot_save (crenderer->fg_snapshot);
|
||||
gtk_snapshot_save (crenderer->bg_snapshot);
|
||||
gtk_snapshot_save (crenderer->selection_fg_snapshot);
|
||||
gtk_snapshot_save (crenderer->selection_bg_snapshot);
|
||||
gtk_snapshot_save (crenderer->cursors_snapshot);
|
||||
|
||||
gtk_snapshot_translate (crenderer->fg_snapshot, point);
|
||||
gtk_snapshot_translate (crenderer->bg_snapshot, point);
|
||||
gtk_snapshot_translate (crenderer->selection_fg_snapshot, point);
|
||||
gtk_snapshot_translate (crenderer->selection_bg_snapshot, point);
|
||||
gtk_snapshot_translate (crenderer->cursors_snapshot, point);
|
||||
}
|
||||
|
||||
static void
|
||||
pop_translate (GskPangoRenderer *crenderer)
|
||||
{
|
||||
gtk_snapshot_restore (crenderer->fg_snapshot);
|
||||
gtk_snapshot_restore (crenderer->bg_snapshot);
|
||||
gtk_snapshot_restore (crenderer->selection_fg_snapshot);
|
||||
gtk_snapshot_restore (crenderer->selection_bg_snapshot);
|
||||
gtk_snapshot_restore (crenderer->cursors_snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
render_para (GskPangoRenderer *crenderer,
|
||||
int offset_y,
|
||||
@@ -3854,8 +3907,7 @@ render_para (GskPangoRenderer *crenderer,
|
||||
gtk_style_context_restore (context);
|
||||
}
|
||||
|
||||
gtk_snapshot_save (crenderer->snapshot);
|
||||
gtk_snapshot_translate (crenderer->snapshot, &point);
|
||||
push_translate (crenderer, &point);
|
||||
|
||||
do
|
||||
{
|
||||
@@ -3897,7 +3949,7 @@ render_para (GskPangoRenderer *crenderer,
|
||||
if (selection_start_index < byte_offset &&
|
||||
selection_end_index > line->length + byte_offset) /* All selected */
|
||||
{
|
||||
gtk_snapshot_append_color (crenderer->snapshot,
|
||||
gtk_snapshot_append_color (crenderer->selection_bg_snapshot,
|
||||
selection,
|
||||
&GRAPHENE_RECT_INIT (line_display->left_margin,
|
||||
selection_y,
|
||||
@@ -3912,7 +3964,7 @@ render_para (GskPangoRenderer *crenderer,
|
||||
else
|
||||
{
|
||||
if (line_display->pg_bg_rgba_set)
|
||||
gtk_snapshot_append_color (crenderer->snapshot,
|
||||
gtk_snapshot_append_color (crenderer->selection_bg_snapshot,
|
||||
&line_display->pg_bg_rgba,
|
||||
&GRAPHENE_RECT_INIT (line_display->left_margin,
|
||||
selection_y,
|
||||
@@ -3949,13 +4001,13 @@ render_para (GskPangoRenderer *crenderer,
|
||||
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);
|
||||
gtk_snapshot_append_color (crenderer->selection_bg_snapshot, selection, &bounds);
|
||||
gtk_snapshot_push_clip (crenderer->selection_fg_snapshot, &bounds);
|
||||
pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
|
||||
line,
|
||||
line_rect.x,
|
||||
baseline);
|
||||
gtk_snapshot_pop (crenderer->snapshot);
|
||||
gtk_snapshot_pop (crenderer->selection_fg_snapshot);
|
||||
}
|
||||
|
||||
g_free (ranges);
|
||||
@@ -3964,7 +4016,7 @@ render_para (GskPangoRenderer *crenderer,
|
||||
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,
|
||||
gtk_snapshot_append_color (crenderer->selection_bg_snapshot,
|
||||
selection,
|
||||
&GRAPHENE_RECT_INIT (line_display->left_margin,
|
||||
selection_y,
|
||||
@@ -3980,7 +4032,7 @@ render_para (GskPangoRenderer *crenderer,
|
||||
+ screen_width
|
||||
- PANGO_PIXELS (line_rect.x)
|
||||
- PANGO_PIXELS (line_rect.width);
|
||||
gtk_snapshot_append_color (crenderer->snapshot,
|
||||
gtk_snapshot_append_color (crenderer->selection_bg_snapshot,
|
||||
selection,
|
||||
&GRAPHENE_RECT_INIT (PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
|
||||
selection_y,
|
||||
@@ -4006,21 +4058,21 @@ render_para (GskPangoRenderer *crenderer,
|
||||
* (normally white on black) */
|
||||
_gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
|
||||
|
||||
gtk_snapshot_push_opacity (crenderer->snapshot, cursor_alpha);
|
||||
gtk_snapshot_append_color (crenderer->snapshot, &cursor_color, &bounds);
|
||||
gtk_snapshot_push_opacity (crenderer->cursors_snapshot, cursor_alpha);
|
||||
gtk_snapshot_append_color (crenderer->cursors_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);
|
||||
gtk_snapshot_push_clip (crenderer->cursors_snapshot, &bounds);
|
||||
pango_renderer_draw_layout_line (PANGO_RENDERER (crenderer),
|
||||
line,
|
||||
line_rect.x,
|
||||
baseline);
|
||||
gtk_snapshot_pop (crenderer->snapshot);
|
||||
gtk_snapshot_pop (crenderer->cursors_snapshot);
|
||||
}
|
||||
gtk_snapshot_pop (crenderer->snapshot);
|
||||
gtk_snapshot_pop (crenderer->cursors_snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4028,7 +4080,7 @@ render_para (GskPangoRenderer *crenderer,
|
||||
}
|
||||
while (pango_layout_iter_next_line (iter));
|
||||
|
||||
gtk_snapshot_restore (crenderer->snapshot);
|
||||
pop_translate (crenderer);
|
||||
|
||||
gdk_rgba_free (selection);
|
||||
pango_layout_iter_free (iter);
|
||||
@@ -4041,20 +4093,23 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||
const GdkRectangle *clip,
|
||||
float cursor_alpha)
|
||||
{
|
||||
GtkTextLayoutPrivate *priv;
|
||||
GskPangoRenderer *crenderer;
|
||||
GtkStyleContext *context;
|
||||
gint offset_y;
|
||||
GtkTextIter selection_start, selection_end;
|
||||
gboolean have_selection;
|
||||
GSList *line_list;
|
||||
GSList *tmp_list;
|
||||
GdkRGBA color;
|
||||
gint offset_y;
|
||||
|
||||
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);
|
||||
|
||||
priv = GTK_TEXT_LAYOUT_GET_PRIVATE (layout);
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
gtk_style_context_get_color (context, &color);
|
||||
|
||||
@@ -4066,7 +4121,6 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||
crenderer = gsk_pango_renderer_acquire ();
|
||||
|
||||
crenderer->widget = widget;
|
||||
crenderer->snapshot = snapshot;
|
||||
crenderer->fg_color = color;
|
||||
|
||||
graphene_rect_init (&crenderer->bounds, 0, 0, clip->width, clip->height);
|
||||
@@ -4128,7 +4182,7 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||
{
|
||||
int i;
|
||||
|
||||
gtk_snapshot_push_opacity (crenderer->snapshot, cursor_alpha);
|
||||
gtk_snapshot_push_opacity (crenderer->cursors_snapshot, cursor_alpha);
|
||||
for (i = 0; i < line_display->cursors->len; i++)
|
||||
{
|
||||
int index;
|
||||
@@ -4137,12 +4191,12 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||
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,
|
||||
gtk_snapshot_render_insertion_cursor (crenderer->cursors_snapshot, context,
|
||||
line_display->x_offset, offset_y + line_display->top_margin,
|
||||
line_display->layout, index, dir);
|
||||
}
|
||||
|
||||
gtk_snapshot_pop (crenderer->snapshot);
|
||||
gtk_snapshot_pop (crenderer->cursors_snapshot);
|
||||
}
|
||||
} /* line_display->height > 0 */
|
||||
|
||||
@@ -4157,5 +4211,24 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||
|
||||
g_slist_free (line_list);
|
||||
|
||||
gsk_pango_renderer_apply (crenderer, snapshot);
|
||||
|
||||
/* Only update eviction source once per snapshot */
|
||||
gtk_text_line_display_cache_delay_eviction (priv->cache);
|
||||
|
||||
gsk_pango_renderer_release (crenderer);
|
||||
}
|
||||
|
||||
gint
|
||||
gtk_text_line_display_compare (const GtkTextLineDisplay *display1,
|
||||
const GtkTextLineDisplay *display2,
|
||||
GtkTextLayout *layout)
|
||||
{
|
||||
GtkTextIter iter1;
|
||||
GtkTextIter iter2;
|
||||
|
||||
gtk_text_layout_get_iter_at_line (layout, &iter1, display1->line, 0);
|
||||
gtk_text_layout_get_iter_at_line (layout, &iter2, display2->line, 0);
|
||||
|
||||
return gtk_text_iter_compare (&iter1, &iter2);
|
||||
}
|
||||
|
@@ -138,11 +138,6 @@ struct _GtkTextLayout
|
||||
* over long runs with the same style. */
|
||||
GtkTextAttributes *one_style_cache;
|
||||
|
||||
/* A cache of one line display. Getting the same line
|
||||
* many times in a row is the most common case.
|
||||
*/
|
||||
GtkTextLineDisplay *one_display_cache;
|
||||
|
||||
/* Whether we are allowed to wrap right now */
|
||||
gint wrap_loop_count;
|
||||
|
||||
@@ -223,6 +218,12 @@ struct _GtkTextLineDisplay
|
||||
PangoLayout *layout;
|
||||
GArray *cursors; /* indexes of cursors in the PangoLayout */
|
||||
|
||||
/* GSequenceIter backpointer for use within cache */
|
||||
GSequenceIter *cache_iter;
|
||||
|
||||
/* GQueue link for use in MRU to help cull cache */
|
||||
GList mru_link;
|
||||
|
||||
GtkTextDirection direction;
|
||||
|
||||
gint width; /* Width of layout */
|
||||
@@ -298,8 +299,12 @@ void gtk_text_layout_wrap_loop_end (GtkTextLayout *layout);
|
||||
GtkTextLineDisplay* gtk_text_layout_get_line_display (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only);
|
||||
GtkTextLineDisplay *gtk_text_line_display_ref (GtkTextLineDisplay *display);
|
||||
void gtk_text_line_display_unref (GtkTextLineDisplay *display);
|
||||
|
||||
GtkTextLineDisplay *gtk_text_line_display_ref (GtkTextLineDisplay *display);
|
||||
void gtk_text_line_display_unref (GtkTextLineDisplay *display);
|
||||
gint gtk_text_line_display_compare (const GtkTextLineDisplay *display1,
|
||||
const GtkTextLineDisplay *display2,
|
||||
GtkTextLayout *layout);
|
||||
|
||||
void gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
|
||||
GtkTextIter *target_iter,
|
||||
@@ -354,6 +359,12 @@ void gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
|
||||
GtkTextIter *iter,
|
||||
GdkRectangle *strong_pos,
|
||||
GdkRectangle *weak_pos);
|
||||
GtkTextLineDisplay *gtk_text_layout_create_display (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only);
|
||||
void gtk_text_layout_update_display_cursors (GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
GtkTextLineDisplay *display);
|
||||
gboolean _gtk_text_layout_get_block_cursor (GtkTextLayout *layout,
|
||||
GdkRectangle *pos);
|
||||
gboolean gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
|
||||
|
716
gtk/gtktextlinedisplaycache.c
Normal file
716
gtk/gtktextlinedisplaycache.c
Normal file
@@ -0,0 +1,716 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
* Copyright (C) 2019 Red Hat, Inc.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtktextbtree.h"
|
||||
#include "gtktextbufferprivate.h"
|
||||
#include "gtktextiterprivate.h"
|
||||
#include "gtktextlinedisplaycacheprivate.h"
|
||||
|
||||
#define MRU_MAX_SIZE 250
|
||||
#define BLOW_CACHE_TIMEOUT_SEC 20
|
||||
#define DEBUG_LINE_DISPLAY_CACHE 0
|
||||
|
||||
struct _GtkTextLineDisplayCache
|
||||
{
|
||||
GSequence *sorted_by_line;
|
||||
GHashTable *line_to_display;
|
||||
GtkTextLine *cursor_line;
|
||||
GQueue mru;
|
||||
GSource *evict_source;
|
||||
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
guint log_source;
|
||||
gint hits;
|
||||
gint misses;
|
||||
gint inval;
|
||||
gint inval_cursors;
|
||||
gint inval_by_line;
|
||||
gint inval_by_range;
|
||||
gint inval_by_y_range;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
# define STAT_ADD(val,n) ((val) += n)
|
||||
# define STAT_INC(val) STAT_ADD(val,1)
|
||||
static gboolean
|
||||
dump_stats (gpointer data)
|
||||
{
|
||||
GtkTextLineDisplayCache *cache = data;
|
||||
g_printerr ("%p: size=%u hits=%d misses=%d inval_total=%d "
|
||||
"inval_cursors=%d inval_by_line=%d "
|
||||
"inval_by_range=%d inval_by_y_range=%d\n",
|
||||
cache, g_hash_table_size (cache->line_to_display),
|
||||
cache->hits, cache->misses,
|
||||
cache->inval, cache->inval_cursors,
|
||||
cache->inval_by_line, cache->inval_by_range,
|
||||
cache->inval_by_y_range);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
#else
|
||||
# define STAT_ADD(val,n)
|
||||
# define STAT_INC(val)
|
||||
#endif
|
||||
|
||||
GtkTextLineDisplayCache *
|
||||
gtk_text_line_display_cache_new (void)
|
||||
{
|
||||
GtkTextLineDisplayCache *ret;
|
||||
|
||||
ret = g_slice_new0 (GtkTextLineDisplayCache);
|
||||
ret->sorted_by_line = g_sequence_new ((GDestroyNotify)gtk_text_line_display_unref);
|
||||
ret->line_to_display = g_hash_table_new (NULL, NULL);
|
||||
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
ret->log_source = g_timeout_add_seconds (1, dump_stats, ret);
|
||||
#endif
|
||||
|
||||
return g_steal_pointer (&ret);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_line_display_cache_free (GtkTextLineDisplayCache *cache)
|
||||
{
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
g_clear_handle_id (&cache->log_source, g_source_remove);
|
||||
#endif
|
||||
|
||||
g_clear_pointer (&cache->evict_source, g_source_destroy);
|
||||
g_clear_pointer (&cache->sorted_by_line, g_sequence_free);
|
||||
g_clear_pointer (&cache->line_to_display, g_hash_table_unref);
|
||||
g_slice_free (GtkTextLineDisplayCache, cache);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_text_line_display_cache_blow_cb (gpointer data)
|
||||
{
|
||||
GtkTextLineDisplayCache *cache = data;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
g_printerr ("Evicting GtkTextLineDisplayCache\n");
|
||||
#endif
|
||||
|
||||
cache->evict_source = NULL;
|
||||
|
||||
gtk_text_line_display_cache_invalidate (cache);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_line_display_cache_delay_eviction (GtkTextLineDisplayCache *cache)
|
||||
{
|
||||
g_assert (cache != NULL);
|
||||
|
||||
if (cache->evict_source != NULL)
|
||||
{
|
||||
gint64 deadline;
|
||||
|
||||
deadline = g_get_monotonic_time () + (BLOW_CACHE_TIMEOUT_SEC * G_USEC_PER_SEC);
|
||||
g_source_set_ready_time (cache->evict_source, deadline);
|
||||
}
|
||||
else
|
||||
{
|
||||
guint tag;
|
||||
|
||||
tag = g_timeout_add_seconds (BLOW_CACHE_TIMEOUT_SEC,
|
||||
gtk_text_line_display_cache_blow_cb,
|
||||
cache);
|
||||
cache->evict_source = g_main_context_find_source_by_id (NULL, tag);
|
||||
g_source_set_name (cache->evict_source, "[gtk+] gtk_text_line_display_cache_blow_cb");
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
static void
|
||||
check_disposition (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
gint last = G_MAXUINT;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (cache->sorted_by_line != NULL);
|
||||
g_assert (cache->line_to_display != NULL);
|
||||
|
||||
for (iter = g_sequence_get_begin_iter (cache->sorted_by_line);
|
||||
!g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
GtkTextLineDisplay *display = g_sequence_get (iter);
|
||||
GtkTextIter text_iter;
|
||||
guint line;
|
||||
|
||||
gtk_text_layout_get_iter_at_line (layout, &text_iter, display->line, 0);
|
||||
line = gtk_text_iter_get_line (&text_iter);
|
||||
|
||||
g_assert_cmpint (line, >, last);
|
||||
|
||||
last = line;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
gtk_text_line_display_cache_take_display (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLineDisplay *display,
|
||||
GtkTextLayout *layout)
|
||||
{
|
||||
g_assert (cache != NULL);
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->line != NULL);
|
||||
g_assert (display->cache_iter == NULL);
|
||||
g_assert (display->mru_link.data == display);
|
||||
g_assert (display->mru_link.prev == NULL);
|
||||
g_assert (display->mru_link.next == NULL);
|
||||
g_assert (g_hash_table_lookup (cache->line_to_display, display->line) == NULL);
|
||||
|
||||
#if DEBUG_LINE_DISPLAY_CACHE
|
||||
check_disposition (cache, layout);
|
||||
#endif
|
||||
|
||||
display->cache_iter =
|
||||
g_sequence_insert_sorted (cache->sorted_by_line,
|
||||
display,
|
||||
(GCompareDataFunc) gtk_text_line_display_compare,
|
||||
layout);
|
||||
g_hash_table_insert (cache->line_to_display, display->line, display);
|
||||
g_queue_push_head_link (&cache->mru, &display->mru_link);
|
||||
|
||||
/* Cull the cache if we're at capacity */
|
||||
while (cache->mru.length > MRU_MAX_SIZE)
|
||||
{
|
||||
display = g_queue_peek_tail (&cache->mru);
|
||||
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_text_line_display_cache_invalidate_display:
|
||||
* @cache: a GtkTextLineDisplayCache
|
||||
* @display: a GtkTextLineDisplay
|
||||
* @cursors_only: if only the cursor positions should be invalidated
|
||||
*
|
||||
* If @cursors_only is TRUE, then only the cursors are invalidated. Otherwise,
|
||||
* @display is removed from the cache.
|
||||
*
|
||||
* Use this function when you already have access to a display as it reduces
|
||||
* some overhead.
|
||||
*/
|
||||
void
|
||||
gtk_text_line_display_cache_invalidate_display (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLineDisplay *display,
|
||||
gboolean cursors_only)
|
||||
{
|
||||
g_assert (cache != NULL);
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->line != NULL);
|
||||
|
||||
if (cursors_only)
|
||||
{
|
||||
g_clear_pointer (&display->cursors, g_array_unref);
|
||||
display->cursors_invalid = TRUE;
|
||||
display->has_block_cursor = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
GSequenceIter *iter = g_steal_pointer (&display->cache_iter);
|
||||
|
||||
if (cache->cursor_line == display->line)
|
||||
cache->cursor_line = NULL;
|
||||
|
||||
g_hash_table_remove (cache->line_to_display, display->line);
|
||||
g_queue_unlink (&cache->mru, &display->mru_link);
|
||||
|
||||
if (iter != NULL)
|
||||
g_sequence_remove (iter);
|
||||
}
|
||||
|
||||
STAT_INC (cache->inval);
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_text_line_display_cache_get:
|
||||
* @cache: a #GtkTextLineDisplayCache
|
||||
* @layout: a GtkTextLayout
|
||||
* @line: a GtkTextLine
|
||||
* @size_only: if only line sizing is needed
|
||||
*
|
||||
* Gets a GtkTextLineDisplay for @line.
|
||||
*
|
||||
* If no cached display exists, a new display will be created.
|
||||
*
|
||||
* It's possible that calling this function will cause some existing
|
||||
* cached displays to be released and destroyed.
|
||||
*
|
||||
* Returns: (transfer full) (not nullable): a #GtkTextLineDisplay
|
||||
*/
|
||||
GtkTextLineDisplay *
|
||||
gtk_text_line_display_cache_get (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only)
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (layout != NULL);
|
||||
g_assert (line != NULL);
|
||||
|
||||
display = g_hash_table_lookup (cache->line_to_display, line);
|
||||
|
||||
if (display != NULL)
|
||||
{
|
||||
if (size_only || !display->size_only)
|
||||
{
|
||||
STAT_INC (cache->hits);
|
||||
|
||||
if (!size_only && display->line == cache->cursor_line)
|
||||
gtk_text_layout_update_display_cursors (layout, display->line, display);
|
||||
|
||||
/* Move to front of MRU */
|
||||
g_queue_unlink (&cache->mru, &display->mru_link);
|
||||
g_queue_push_head_link (&cache->mru, &display->mru_link);
|
||||
|
||||
return gtk_text_line_display_ref (display);
|
||||
}
|
||||
|
||||
/* We need an updated display that includes more than just
|
||||
* sizing, so we need to drop this entry and force the layout
|
||||
* to create a new one.
|
||||
*/
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, FALSE);
|
||||
}
|
||||
|
||||
STAT_INC (cache->misses);
|
||||
|
||||
g_assert (!g_hash_table_lookup (cache->line_to_display, line));
|
||||
|
||||
display = gtk_text_layout_create_display (layout, line, size_only);
|
||||
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->line == line);
|
||||
|
||||
if (!size_only)
|
||||
{
|
||||
if (line == cache->cursor_line)
|
||||
gtk_text_layout_update_display_cursors (layout, line, display);
|
||||
|
||||
gtk_text_line_display_cache_take_display (cache,
|
||||
gtk_text_line_display_ref (display),
|
||||
layout);
|
||||
}
|
||||
|
||||
return g_steal_pointer (&display);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_line_display_cache_invalidate (GtkTextLineDisplayCache *cache)
|
||||
{
|
||||
g_assert (cache != NULL);
|
||||
g_assert (cache->sorted_by_line != NULL);
|
||||
g_assert (cache->line_to_display != NULL);
|
||||
|
||||
STAT_ADD (cache->inval, g_hash_table_size (cache->line_to_display));
|
||||
|
||||
cache->cursor_line = NULL;
|
||||
|
||||
while (cache->mru.head != NULL)
|
||||
{
|
||||
GtkTextLineDisplay *display = g_queue_peek_head (&cache->mru);
|
||||
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, FALSE);
|
||||
}
|
||||
|
||||
g_assert (g_hash_table_size (cache->line_to_display) == 0);
|
||||
g_assert (g_sequence_get_length (cache->sorted_by_line) == 0);
|
||||
g_assert (cache->mru.length == 0);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_line_display_cache_invalidate_cursors (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLine *line)
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (line != NULL);
|
||||
|
||||
STAT_INC (cache->inval_cursors);
|
||||
|
||||
display = g_hash_table_lookup (cache->line_to_display, line);
|
||||
|
||||
if (display != NULL)
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_text_line_display_cache_invalidate_line:
|
||||
* @self: a GtkTextLineDisplayCache
|
||||
* @line: a GtkTextLine
|
||||
*
|
||||
* Removes a cached display for @line.
|
||||
*
|
||||
* Compare to gtk_text_line_display_cache_invalidate_cursors() which
|
||||
* only invalidates the cursors for this row.
|
||||
*/
|
||||
void
|
||||
gtk_text_line_display_cache_invalidate_line (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLine *line)
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (line != NULL);
|
||||
|
||||
display = g_hash_table_lookup (cache->line_to_display, line);
|
||||
|
||||
if (display != NULL)
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, FALSE);
|
||||
|
||||
STAT_INC (cache->inval_by_line);
|
||||
}
|
||||
|
||||
static GSequenceIter *
|
||||
find_iter_at_text_iter (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
const GtkTextIter *iter)
|
||||
{
|
||||
GSequenceIter *left;
|
||||
GSequenceIter *right;
|
||||
GSequenceIter *mid;
|
||||
GSequenceIter *end;
|
||||
GtkTextLine *target;
|
||||
guint target_lineno;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (iter != NULL);
|
||||
|
||||
if (g_sequence_is_empty (cache->sorted_by_line))
|
||||
return NULL;
|
||||
|
||||
/* gtk_text_iter_get_line() will have cached value */
|
||||
target_lineno = gtk_text_iter_get_line (iter);
|
||||
target = _gtk_text_iter_get_text_line (iter);
|
||||
|
||||
/* Get some iters so we can work with pointer compare */
|
||||
end = g_sequence_get_end_iter (cache->sorted_by_line);
|
||||
left = g_sequence_get_begin_iter (cache->sorted_by_line);
|
||||
right = g_sequence_iter_prev (end);
|
||||
|
||||
/* We already checked for empty above */
|
||||
g_assert (!g_sequence_iter_is_end (left));
|
||||
g_assert (!g_sequence_iter_is_end (right));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
guint lineno;
|
||||
|
||||
if (left == right)
|
||||
mid = left;
|
||||
else
|
||||
mid = g_sequence_range_get_midpoint (left, right);
|
||||
|
||||
g_assert (mid != NULL);
|
||||
g_assert (!g_sequence_iter_is_end (mid));
|
||||
|
||||
if (mid == end)
|
||||
break;
|
||||
|
||||
display = g_sequence_get (mid);
|
||||
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->line != NULL);
|
||||
g_assert (display->cache_iter != NULL);
|
||||
|
||||
if (target == display->line)
|
||||
return mid;
|
||||
|
||||
if (right == left)
|
||||
break;
|
||||
|
||||
lineno = _gtk_text_line_get_number (display->line);
|
||||
|
||||
if (target_lineno < lineno)
|
||||
right = mid;
|
||||
else if (target_lineno > lineno)
|
||||
left = g_sequence_iter_next (mid);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gtk_text_line_display_cache_invalidate_range:
|
||||
* @cache: a GtkTextLineDisplayCache
|
||||
* @begin: the starting text iter
|
||||
* @end: the ending text iter
|
||||
*
|
||||
* Removes all GtkTextLineDisplay that fall between or including
|
||||
* @begin and @end.
|
||||
*/
|
||||
void
|
||||
gtk_text_line_display_cache_invalidate_range (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
const GtkTextIter *begin,
|
||||
const GtkTextIter *end,
|
||||
gboolean cursors_only)
|
||||
{
|
||||
GSequenceIter *begin_iter;
|
||||
GSequenceIter *end_iter;
|
||||
GSequenceIter *iter;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (layout != NULL);
|
||||
g_assert (begin != NULL);
|
||||
g_assert (end != NULL);
|
||||
|
||||
STAT_INC (cache->inval_by_range);
|
||||
|
||||
/* Short-circuit, is_empty() is O(1) */
|
||||
if (g_sequence_is_empty (cache->sorted_by_line))
|
||||
return;
|
||||
|
||||
/* gtk_text_iter_order() preserving const */
|
||||
if (gtk_text_iter_compare (begin, end) > 0)
|
||||
{
|
||||
const GtkTextIter *tmp = begin;
|
||||
end = begin;
|
||||
begin = tmp;
|
||||
}
|
||||
|
||||
/* Common case, begin/end on same line. Just try to find the line by
|
||||
* line number and invalidate it alone.
|
||||
*/
|
||||
if G_LIKELY (_gtk_text_iter_same_line (begin, end))
|
||||
{
|
||||
begin_iter = find_iter_at_text_iter (cache, layout, begin);
|
||||
|
||||
if (begin_iter != NULL)
|
||||
{
|
||||
GtkTextLineDisplay *display = g_sequence_get (begin_iter);
|
||||
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->line != NULL);
|
||||
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, cursors_only);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find GSequenceIter containing GtkTextLineDisplay that correspond
|
||||
* to each of the text positions.
|
||||
*/
|
||||
begin_iter = find_iter_at_text_iter (cache, layout, begin);
|
||||
end_iter = find_iter_at_text_iter (cache, layout, end);
|
||||
|
||||
/* Short-circuit if we found nothing */
|
||||
if (begin_iter == NULL && end_iter == NULL)
|
||||
return;
|
||||
|
||||
/* If nothing matches the end, we need to walk to the end of our
|
||||
* cached displays. We know there is a non-zero number of items
|
||||
* in the sequence at this point, so we can iter_prev() safely.
|
||||
*/
|
||||
if (end_iter == NULL)
|
||||
end_iter = g_sequence_iter_prev (g_sequence_get_end_iter (cache->sorted_by_line));
|
||||
|
||||
/* If nothing matched the begin, we need to walk starting from
|
||||
* the first display we have cached.
|
||||
*/
|
||||
if (begin_iter == NULL)
|
||||
begin_iter = g_sequence_get_begin_iter (cache->sorted_by_line);
|
||||
|
||||
iter = begin_iter;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
GtkTextLineDisplay *display = g_sequence_get (iter);
|
||||
GSequenceIter *next = g_sequence_iter_next (iter);
|
||||
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, cursors_only);
|
||||
|
||||
if (iter == end_iter)
|
||||
break;
|
||||
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
static GSequenceIter *
|
||||
find_iter_at_at_y (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
gint y)
|
||||
{
|
||||
GtkTextBTree *btree;
|
||||
GSequenceIter *left;
|
||||
GSequenceIter *right;
|
||||
GSequenceIter *mid;
|
||||
GSequenceIter *end;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (layout != NULL);
|
||||
|
||||
if (g_sequence_is_empty (cache->sorted_by_line))
|
||||
return NULL;
|
||||
|
||||
btree = _gtk_text_buffer_get_btree (layout->buffer);
|
||||
|
||||
/* Get some iters so we can work with pointer compare */
|
||||
end = g_sequence_get_end_iter (cache->sorted_by_line);
|
||||
left = g_sequence_get_begin_iter (cache->sorted_by_line);
|
||||
right = g_sequence_iter_prev (end);
|
||||
|
||||
/* We already checked for empty above */
|
||||
g_assert (!g_sequence_iter_is_end (left));
|
||||
g_assert (!g_sequence_iter_is_end (right));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
gint cache_y;
|
||||
gint cache_height;
|
||||
|
||||
if (left == right)
|
||||
mid = left;
|
||||
else
|
||||
mid = g_sequence_range_get_midpoint (left, right);
|
||||
|
||||
g_assert (mid != NULL);
|
||||
g_assert (!g_sequence_iter_is_end (mid));
|
||||
|
||||
if (mid == end)
|
||||
break;
|
||||
|
||||
display = g_sequence_get (mid);
|
||||
|
||||
g_assert (display != NULL);
|
||||
g_assert (display->line != NULL);
|
||||
|
||||
cache_y = _gtk_text_btree_find_line_top (btree, display->line, layout);
|
||||
cache_height = display->height;
|
||||
|
||||
if (y >= cache_y && y <= (cache_y + cache_height))
|
||||
return mid;
|
||||
|
||||
if (left == right)
|
||||
break;
|
||||
|
||||
if (y < cache_y)
|
||||
right = mid;
|
||||
else if (y > (cache_y + cache_height))
|
||||
left = g_sequence_iter_next (mid);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_text_line_display_cache_invalidate_y_range:
|
||||
* @cache: a GtkTextLineDisplayCache
|
||||
* @y: the starting Y position
|
||||
* @old_height: the height to invalidate
|
||||
* @cursors_only: if only cursors should be invalidated
|
||||
*
|
||||
* Remove all GtkTextLineDisplay that fall into the range starting
|
||||
* from the Y position to Y+Height.
|
||||
*/
|
||||
void
|
||||
gtk_text_line_display_cache_invalidate_y_range (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
gint y,
|
||||
gint old_height,
|
||||
gboolean cursors_only)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
GtkTextBTree *btree;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
g_assert (layout != NULL);
|
||||
|
||||
STAT_INC (cache->inval_by_y_range);
|
||||
|
||||
btree = _gtk_text_buffer_get_btree (layout->buffer);
|
||||
iter = find_iter_at_at_y (cache, layout, y);
|
||||
|
||||
if (iter == NULL)
|
||||
return;
|
||||
|
||||
while (!g_sequence_iter_is_end (iter))
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
gint cache_y;
|
||||
gint cache_height;
|
||||
|
||||
display = g_sequence_get (iter);
|
||||
iter = g_sequence_iter_next (iter);
|
||||
|
||||
cache_y = _gtk_text_btree_find_line_top (btree, display->line, layout);
|
||||
cache_height = display->height;
|
||||
|
||||
if (cache_y + cache_height > y && cache_y < y + old_height)
|
||||
{
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, cursors_only);
|
||||
|
||||
y += cache_height;
|
||||
old_height -= cache_height;
|
||||
|
||||
if (old_height > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_text_line_display_cache_set_cursor_line (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLine *cursor_line)
|
||||
{
|
||||
GtkTextLineDisplay *display;
|
||||
|
||||
g_assert (cache != NULL);
|
||||
|
||||
if (cursor_line == cache->cursor_line)
|
||||
return;
|
||||
|
||||
display = g_hash_table_lookup (cache->line_to_display, cache->cursor_line);
|
||||
|
||||
if (display != NULL)
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, FALSE);
|
||||
|
||||
cache->cursor_line = cursor_line;
|
||||
|
||||
display = g_hash_table_lookup (cache->line_to_display, cache->cursor_line);
|
||||
|
||||
if (display != NULL)
|
||||
gtk_text_line_display_cache_invalidate_display (cache, display, FALSE);
|
||||
}
|
59
gtk/gtktextlinedisplaycacheprivate.h
Normal file
59
gtk/gtktextlinedisplaycacheprivate.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
* Copyright (C) 2019 Red Hat, Inc.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __GTK_TEXT_LINE_DISPLAY_CACHE_PRIVATE_H__
|
||||
#define __GTK_TEXT_LINE_DISPLAY_CACHE_PRIVATE_H__
|
||||
|
||||
#include "gtktextlayoutprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkTextLineDisplayCache GtkTextLineDisplayCache;
|
||||
|
||||
GtkTextLineDisplayCache *gtk_text_line_display_cache_new (void);
|
||||
void gtk_text_line_display_cache_free (GtkTextLineDisplayCache *cache);
|
||||
GtkTextLineDisplay *gtk_text_line_display_cache_get (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
GtkTextLine *line,
|
||||
gboolean size_only);
|
||||
void gtk_text_line_display_cache_delay_eviction (GtkTextLineDisplayCache *cache);
|
||||
void gtk_text_line_display_cache_set_cursor_line (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLine *line);
|
||||
void gtk_text_line_display_cache_invalidate (GtkTextLineDisplayCache *cache);
|
||||
void gtk_text_line_display_cache_invalidate_cursors (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLine *line);
|
||||
void gtk_text_line_display_cache_invalidate_display (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLineDisplay *display,
|
||||
gboolean cursors_only);
|
||||
void gtk_text_line_display_cache_invalidate_line (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLine *line);
|
||||
void gtk_text_line_display_cache_invalidate_range (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
const GtkTextIter *begin,
|
||||
const GtkTextIter *end,
|
||||
gboolean cursors_only);
|
||||
void gtk_text_line_display_cache_invalidate_y_range (GtkTextLineDisplayCache *cache,
|
||||
GtkTextLayout *layout,
|
||||
gint y,
|
||||
gint height,
|
||||
gboolean cursors_only);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_TEXT_LINE_DISPLAY_CACHE_PRIVATE_H__ */
|
@@ -369,6 +369,7 @@ gtk_public_sources = files([
|
||||
'gtktexthandle.c',
|
||||
'gtktextiter.c',
|
||||
'gtktextlayout.c',
|
||||
'gtktextlinedisplaycache.c',
|
||||
'gtktextmark.c',
|
||||
'gtktextsegment.c',
|
||||
'gtktexttag.c',
|
||||
|
Reference in New Issue
Block a user