[gnome-builder/wip/chergert/gutter] document gutter renderer and cleanup
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/gutter] document gutter renderer and cleanup
- Date: Mon, 18 Sep 2017 09:11:56 +0000 (UTC)
commit b6cab7b8418b0aa1fcbf407dbd9cf01a4358bedc
Author: Christian Hergert <chergert redhat com>
Date: Mon Sep 18 02:11:45 2017 -0700
document gutter renderer and cleanup
libide/sourceview/ide-omni-gutter-renderer.c | 452 +++++++++++++++++++-------
1 files changed, 335 insertions(+), 117 deletions(-)
---
diff --git a/libide/sourceview/ide-omni-gutter-renderer.c b/libide/sourceview/ide-omni-gutter-renderer.c
index 2d8d112..6486888 100644
--- a/libide/sourceview/ide-omni-gutter-renderer.c
+++ b/libide/sourceview/ide-omni-gutter-renderer.c
@@ -33,6 +33,24 @@
#include "sourceview/ide-omni-gutter-renderer.h"
#include "sourceview/ide-source-view.h"
+/**
+ * SECTION:ide-omni-gutter-renderer
+ * @title: IdeOmniGutterRenderer
+ * @short_description: A featureful gutter renderer for the code editor
+ *
+ * This is a #GtkSourceGutterRenderer that knows how to render many of
+ * our components necessary for Builder. Because of the complexity of
+ * Builder, using traditional gutter renderers takes up a great deal
+ * of horizontal space.
+ *
+ * By overlapping some of our components, we can take up less space and
+ * be easier for the user with increased hit-targets.
+ *
+ * Additionally, we can render faster because we can coalesce work.
+ *
+ * Since: 3.28
+ */
+
#define DIAGNOSTICS_SIZE 16
#define ARROW_WIDTH 5
#define CHANGE_WIDTH 2
@@ -51,6 +69,10 @@ struct _IdeOmniGutterRenderer
DzlSignalGroup *view_signals;
DzlSignalGroup *buffer_signals;
+ /* TODO: It would be nice to use some basic caching here
+ * so we don't waste 6Kb-12Kb of data on these surfaces.
+ * But that can be done later after this patch set merges.
+ */
cairo_surface_t *note_surface;
cairo_surface_t *warning_surface;
cairo_surface_t *error_surface;
@@ -58,6 +80,12 @@ struct _IdeOmniGutterRenderer
cairo_surface_t *warning_selected_surface;
cairo_surface_t *error_selected_surface;
+ /*
+ * We cache various colors we need from the style scheme to avoid
+ * looking them up very often, as it is CPU time consuming. We also
+ * use these colors to prime the symbolic colors for the icon surfaces
+ * to they look appropriate for the style scheme.
+ */
struct {
GdkRGBA fg;
GdkRGBA bg;
@@ -70,15 +98,59 @@ struct _IdeOmniGutterRenderer
GdkRGBA change;
} changes;
- PangoLayout *layout;
+ /*
+ * We need to reuse a single pango layout while drawing all the lines
+ * to keep the overhead low. We don't have pixel caching on the gutter
+ * data so keeping this stuff fast is critical.
+ */
+ PangoLayout *layout;
+
+ /*
+ * We reuse a simple bold attr list for the current line number
+ * information. This way we don't have to do any pango markup
+ * parsing.
+ */
PangoAttrList *bold_attrs;
+ /* We stash a copy of how long the line numbers could be. 1000 => 4. */
guint n_chars;
+
+ /* While processing the lines, we track what our first line number is
+ * so that differential calculation for each line is cheap by avoiding
+ * accessing GtkTextIter information.
+ */
guint begin_line;
- gint stopped_line;
+
+ /*
+ * While starting a render, we check to see what the current
+ * breakpoint line is (so we can draw the proper background.
+ *
+ * TODO: Add a callback to the debug manager to avoid querying this
+ * information on every draw cycle.
+ */
+ gint stopped_line;
+
+ /*
+ * To avoid doing multiple line recalculations inline, we defer our
+ * changed handler until we've re-entered teh main loop. Otherwise
+ * we could handle lots of small changes during automated processing
+ * of the underlying buffer.
+ */
guint resize_source;
- gint number_width;
+ /*
+ * The number_width field contains the maximum width of the text as
+ * sized by Pango. It is in pixel units in the scale of the widget
+ * as the underlying components will automatically deal with scaling
+ * for us (as necessary).
+ */
+ gint number_width;
+
+ /*
+ * Some users might want to toggle off individual features of the
+ * omni gutter, and these boolean properties provide that. Other
+ * components map them to GSettings values to be toggled.
+ */
guint show_line_changes : 1;
guint show_line_numbers : 1;
guint show_line_diagnostics : 1;
@@ -99,32 +171,55 @@ enum {
typedef struct
{
+ /* The line contains a regular breakpoint */
guint is_breakpoint : 1;
+
+ /* The line contains a countpoint styl breakpoint */
guint is_countpoint : 1;
+
+ /* The line contains a watchpoint style breakpoint */
guint is_watchpoint : 1;
+
+ /* The line is an addition to the buffer */
guint is_add : 1;
+
+ /* The line has changed in the buffer */
guint is_change : 1;
+
+ /* The line is part of a deleted range in the buffer */
guint is_delete : 1;
+
+ /* The line contains a diagnostic error */
+ guint is_error : 1;
+
+ /* The line contains a diagnostic warning */
guint is_warning : 1;
+
+ /* The line contains a diagnostic note */
guint is_note : 1;
- guint is_error : 1;
} LineInfo;
G_DEFINE_TYPE (IdeOmniGutterRenderer, ide_omni_gutter_renderer, GTK_SOURCE_TYPE_GUTTER_RENDERER)
static GParamSpec *properties [N_PROPS];
+/*
+ * style_get_is_bold:
+ *
+ * This helper is used to extract the "bold" field from a GtkSourceStyle
+ * within a GtkSourceStyleScheme.
+ *
+ * Returns; %TRUE if @val was set to a trusted value.
+ */
static gboolean
-get_style_boolean (GtkSourceStyleScheme *scheme,
+style_get_is_bold (GtkSourceStyleScheme *scheme,
const gchar *style_name,
- const gchar *property,
gboolean *val)
{
GtkSourceStyle *style;
g_assert (!scheme || GTK_SOURCE_IS_STYLE_SCHEME (scheme));
g_assert (style_name != NULL);
- g_assert (property != NULL);
g_assert (val != NULL);
*val = FALSE;
@@ -132,24 +227,31 @@ get_style_boolean (GtkSourceStyleScheme *scheme,
if (scheme == NULL)
return FALSE;
- style = gtk_source_style_scheme_get_style (scheme, style_name);
-
- if (style != NULL)
+ if (NULL != (style = gtk_source_style_scheme_get_style (scheme, style_name)))
{
- g_autofree gchar *property_set = g_strdup_printf ("%s-set", property);
- gboolean set = FALSE;
-
+ gboolean bold_set = FALSE;
g_object_get (style,
- property, val,
- property_set, &set,
+ "bold-set", &bold_set,
+ "bold", val,
NULL);
-
- return set;
+ return bold_set;
}
return FALSE;
}
+/*
+ * get_style_rgba:
+ *
+ * Gets a #GdkRGBA for a particular field of a style within @scheme.
+ *
+ * @type should be set to BACKGROUND or FOREGROUND.
+ *
+ * If we fail to locate the style, @rgba is set to transparent black.
+ * such as #rgba(0,0,0,0).
+ *
+ * Returns: %TRUE if the value placed into @rgba can be trusted.
+ */
static gboolean
get_style_rgba (GtkSourceStyleScheme *scheme,
const gchar *style_name,
@@ -168,9 +270,7 @@ get_style_rgba (GtkSourceStyleScheme *scheme,
if (scheme == NULL)
return FALSE;
- style = gtk_source_style_scheme_get_style (scheme, style_name);
-
- if (style != NULL)
+ if (NULL != (style = gtk_source_style_scheme_get_style (scheme, style_name)))
{
g_autofree gchar *str = NULL;
gboolean set = FALSE;
@@ -180,11 +280,10 @@ get_style_rgba (GtkSourceStyleScheme *scheme,
type ? "background-set" : "foreground-set", &set,
NULL);
- if (str != NULL && set)
- {
- gdk_rgba_parse (rgba, str);
- return TRUE;
- }
+ if (str != NULL)
+ gdk_rgba_parse (rgba, str);
+
+ return set;
}
return FALSE;
@@ -214,13 +313,14 @@ reload_style_colors (IdeOmniGutterRenderer *self,
gtk_style_context_get_background_color (context, state, &bg);
G_GNUC_END_IGNORE_DEPRECATIONS;
+ /* Extract common values from style schemes. */
if (!get_style_rgba (scheme, "line-numbers", FOREGROUND, &self->text.fg))
self->text.fg = fg;
if (!get_style_rgba (scheme, "line-numbers", BACKGROUND, &self->text.bg))
self->text.bg = bg;
- if (!get_style_boolean (scheme, "line-numbers", "bold", &self->text.bold))
+ if (!style_get_is_bold (scheme, "line-numbers", &self->text.bold))
self->text.bold = FALSE;
if (!get_style_rgba (scheme, "current-line-number", FOREGROUND, &self->current.fg))
@@ -229,9 +329,14 @@ reload_style_colors (IdeOmniGutterRenderer *self,
if (!get_style_rgba (scheme, "current-line-number", BACKGROUND, &self->current.bg))
self->current.bg = bg;
- if (!get_style_boolean (scheme, "current-line-number", "bold", &self->current.bold))
+ if (!style_get_is_bold (scheme, "current-line-number", &self->current.bold))
self->current.bold = TRUE;
+ /*
+ * These debugger:: prefix values come from Builder's style-scheme xml
+ * as well as in the IdeBuffer class. Other style schemes may also
+ * support them, though.
+ */
if (!get_style_rgba (scheme, "debugger::current-breakpoint", BACKGROUND, &self->stopped_bg))
gdk_rgba_parse (&self->stopped_bg, "#fcaf3e");
@@ -241,9 +346,12 @@ reload_style_colors (IdeOmniGutterRenderer *self,
if (!get_style_rgba (scheme, "debugger::breakpoint", BACKGROUND, &self->bkpt.bg))
get_style_rgba (scheme, "selection", BACKGROUND, &self->bkpt.bg);
- if (!get_style_boolean (scheme, "debugger::breakpoint", "bold", &self->bkpt.bold))
+ if (!style_get_is_bold (scheme, "debugger::breakpoint", &self->bkpt.bold))
self->bkpt.bold = FALSE;
+ /* These gutter:: prefix values come from Builder's style-scheme xml
+ * files, but other style schemes may also support them now too.
+ */
if (!get_style_rgba (scheme, "gutter::added-line", FOREGROUND, &self->changes.add))
gdk_rgba_parse (&self->changes.add, "#8ae234");
@@ -265,6 +373,10 @@ collect_breakpoint_info (IdeDebuggerBreakpoint *breakpoint,
} *bkpt_info = user_data;
guint line;
+ g_assert (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
+ g_assert (bkpt_info != NULL);
+
+ /* Debugger breakpoints are 1-based line numbers */
if (!(line = ide_debugger_breakpoint_get_line (breakpoint)))
return;
@@ -287,25 +399,27 @@ ide_omni_gutter_renderer_load_breakpoints (IdeOmniGutterRenderer *self,
GtkTextIter *end,
GArray *lines)
{
- struct {
- GArray *lines;
- guint begin;
- guint end;
- } bkpt_info;
-
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
g_assert (begin != NULL);
g_assert (lines != NULL);
g_assert (lines->len > 0);
- bkpt_info.lines = lines;
- bkpt_info.begin = gtk_text_iter_get_line (begin);
- bkpt_info.end = gtk_text_iter_get_line (end);
-
if (self->breakpoints != NULL)
- ide_debugger_breakpoints_foreach (self->breakpoints,
- (GFunc)collect_breakpoint_info,
- &bkpt_info);
+ {
+ struct {
+ GArray *lines;
+ guint begin;
+ guint end;
+ } info;
+
+ info.lines = lines;
+ info.begin = gtk_text_iter_get_line (begin);
+ info.end = gtk_text_iter_get_line (end);
+
+ ide_debugger_breakpoints_foreach (self->breakpoints,
+ (GFunc)collect_breakpoint_info,
+ &info);
+ }
}
static void
@@ -313,7 +427,7 @@ ide_omni_gutter_renderer_load_basic (IdeOmniGutterRenderer *self,
GtkTextIter *begin,
GArray *lines)
{
- IdeBuffer *buffer;
+ GtkTextBuffer *buffer;
guint line;
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
@@ -321,27 +435,23 @@ ide_omni_gutter_renderer_load_basic (IdeOmniGutterRenderer *self,
g_assert (lines != NULL);
g_assert (lines->len > 0);
- buffer = (IdeBuffer *)gtk_text_iter_get_buffer (begin);
-
+ buffer = gtk_text_iter_get_buffer (begin);
if (!IDE_IS_BUFFER (buffer))
return;
line = gtk_text_iter_get_line (begin);
- if (buffer != NULL)
+ for (guint i = 0; i < lines->len; i++)
{
- for (guint i = 0; i < lines->len; i++)
- {
- LineInfo *info = &g_array_index (lines, LineInfo, i);
- IdeBufferLineFlags flags = ide_buffer_get_line_flags (buffer, line + i);
-
- info->is_add = !!(flags & IDE_BUFFER_LINE_FLAGS_ADDED);
- info->is_change = !!(flags & IDE_BUFFER_LINE_FLAGS_CHANGED);
- info->is_delete = !!(flags & IDE_BUFFER_LINE_FLAGS_DELETED);
- info->is_warning = !!(flags & IDE_BUFFER_LINE_FLAGS_WARNING);
- info->is_note = !!(flags & IDE_BUFFER_LINE_FLAGS_NOTE);
- info->is_error = !!(flags & IDE_BUFFER_LINE_FLAGS_ERROR);
- }
+ LineInfo *info = &g_array_index (lines, LineInfo, i);
+ IdeBufferLineFlags flags = ide_buffer_get_line_flags (IDE_BUFFER (buffer), line + i);
+
+ info->is_add = !!(flags & IDE_BUFFER_LINE_FLAGS_ADDED);
+ info->is_change = !!(flags & IDE_BUFFER_LINE_FLAGS_CHANGED);
+ info->is_delete = !!(flags & IDE_BUFFER_LINE_FLAGS_DELETED);
+ info->is_warning = !!(flags & IDE_BUFFER_LINE_FLAGS_WARNING);
+ info->is_note = !!(flags & IDE_BUFFER_LINE_FLAGS_NOTE);
+ info->is_error = !!(flags & IDE_BUFFER_LINE_FLAGS_ERROR);
}
}
@@ -368,7 +478,7 @@ ide_omni_gutter_renderer_recalculate_size (IdeOmniGutterRenderer *self)
const PangoFontDescription *font_desc;
g_autofree gchar *numbers = NULL;
GtkTextBuffer *buffer;
- IdeSourceView *view;
+ GtkTextView *view;
PangoLayout *layout;
GtkTextIter end;
guint line;
@@ -377,35 +487,44 @@ ide_omni_gutter_renderer_recalculate_size (IdeOmniGutterRenderer *self)
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
- view = (IdeSourceView *)gtk_source_gutter_renderer_get_view (GTK_SOURCE_GUTTER_RENDERER (self));
+ /* There is nothing we can do until a view has been attached. */
+ view = gtk_source_gutter_renderer_get_view (GTK_SOURCE_GUTTER_RENDERER (self));
if (!IDE_IS_SOURCE_VIEW (view))
return;
/*
* First, we need to get the size of the text for the last line of the
- * buffer, which will be the longest. We size the font with '9' since it
- * will generally be the widest of the numbers. Although, we only support
- * monospace anyway, so it shouldn't matter.
+ * buffer (which will be the longest). We size the font with '9' since it
+ * will generally be one of the widest of the numbers. Although, we only
+ * "support" * monospace anyway, so it shouldn't be drastic if we're off.
*/
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+ buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_end_iter (buffer, &end);
line = gtk_text_iter_get_line (&end) + 1;
self->n_chars = count_num_digits (line);
numbers = g_strnfill (self->n_chars, '9');
- font_desc = ide_source_view_get_font_desc (view);
+ /*
+ * Get the font description used by the IdeSourceView so we can
+ * match the font styling as much as possible.
+ */
+ font_desc = ide_source_view_get_font_desc (IDE_SOURCE_VIEW (view));
layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), numbers);
pango_layout_set_font_description (layout, font_desc);
+
+ /*
+ * Now cache the width of the text layout so we can simplify our
+ * positioning later. We simply size everything the same and then
+ * align to the right to reduce the draw overhead.
+ */
pango_layout_get_pixel_size (layout, &self->number_width, &height);
- g_clear_object (&layout);
+ /* Now calculate the size based on enabled features */
size = 2;
-
if (self->show_line_diagnostics)
size += DIAGNOSTICS_SIZE + 2;
-
if (self->show_line_numbers)
size += self->number_width + 2;
@@ -417,9 +536,11 @@ ide_omni_gutter_renderer_recalculate_size (IdeOmniGutterRenderer *self)
else if (self->show_line_changes)
size += CHANGE_WIDTH + 2;
+ /* Update the size and ensure we are re-drawn */
gtk_source_gutter_renderer_set_size (GTK_SOURCE_GUTTER_RENDERER (self), size );
-
gtk_source_gutter_renderer_queue_draw (GTK_SOURCE_GUTTER_RENDERER (self));
+
+ g_clear_object (&layout);
}
static void
@@ -466,16 +587,20 @@ ide_omni_gutter_renderer_begin (GtkSourceGutterRenderer *renderer,
g_assert (begin != NULL);
g_assert (end != NULL);
+ /*
+ * This is the start of our draw process. The first thing we want to
+ * do is collect as much information as we'll need when doing the
+ * actual draw. That helps us coalesce similar work together, which is
+ * good for the CPU usage. We are *very* sensitive to CPU usage here
+ * as the GtkTextView does not pixel cache the gutter.
+ */
+
self->stopped_line = -1;
+ /* Locate the current stopped breakpoint if any. */
buffer = gtk_text_iter_get_buffer (begin);
table = gtk_text_buffer_get_tag_table (buffer);
tag = gtk_text_tag_table_lookup (table, "debugger::current-breakpoint");
-
- /*
- * Locate the current stopped breakpoint if any.
- */
-
if (tag != NULL)
{
bkpt = *begin;
@@ -496,17 +621,22 @@ ide_omni_gutter_renderer_begin (GtkSourceGutterRenderer *renderer,
self->begin_line = gtk_text_iter_get_line (begin);
end_line = gtk_text_iter_get_line (end);
+ /* Give ourselves a fresh array to stash our line info */
g_array_set_size (self->lines, end_line - self->begin_line + 1);
memset (self->lines->data, 0, self->lines->len * sizeof (LineInfo));
+ /* Now load breakpoints, diagnostics, and line changes */
ide_omni_gutter_renderer_load_basic (self, begin, self->lines);
ide_omni_gutter_renderer_load_breakpoints (self, begin, end, self->lines);
+ /* Create a new layout for rendering lines to */
self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), "");
pango_layout_set_alignment (self->layout, PANGO_ALIGN_RIGHT);
- pango_layout_set_font_description (self->layout,
- ide_source_view_get_font_desc (view));
+ pango_layout_set_font_description (self->layout, ide_source_view_get_font_desc (view));
+ /* Tweak the sizing (for proper alignment) based on the if we are
+ * going to be rendering the breakpoints arrow or not.
+ */
if (self->breakpoints != NULL)
pango_layout_set_width (self->layout, (cell_area->width - ARROW_WIDTH - 4) * PANGO_SCALE);
else
@@ -519,14 +649,14 @@ ide_omni_gutter_renderer_query_activatable (GtkSourceGutterRenderer *renderer,
GdkRectangle *area,
GdkEvent *event)
{
- IdeOmniGutterRenderer *self = (IdeOmniGutterRenderer *)renderer;
-
- g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
+ g_assert (IDE_IS_OMNI_GUTTER_RENDERER (renderer));
g_assert (begin != NULL);
g_assert (area != NULL);
g_assert (event != NULL);
- return (self->breakpoints != NULL);
+ /* Clicking will move the cursor, so always TRUE */
+
+ return TRUE;
}
static void
@@ -541,6 +671,8 @@ animate_at_iter (IdeOmniGutterRenderer *self,
g_assert (area != NULL);
g_assert (iter != NULL);
+ /* Show a little bullet animation shooting right */
+
view = gtk_source_gutter_renderer_get_view (GTK_SOURCE_GUTTER_RENDERER (self));
theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
@@ -590,6 +722,11 @@ ide_omni_gutter_renderer_activate (GtkSourceGutterRenderer *renderer,
g_assert (event != NULL);
g_assert (self->breakpoints != NULL);
+ /* TODO: We could check for event->button.button to see if we
+ * can display a popover with information such as
+ * diagnostics, or breakpoints, or git blame.
+ */
+
buffer = gtk_text_iter_get_buffer (iter);
context = ide_buffer_get_context (IDE_BUFFER (buffer));
debug_manager = ide_context_get_debug_manager (context);
@@ -665,6 +802,11 @@ draw_breakpoint_bg (IdeOmniGutterRenderer *self,
g_assert (cr != NULL);
g_assert (bg_area != NULL);
+ /*
+ * This draws a little arrow starting from the left and pointing
+ * over the line changes portion of the gutter.
+ */
+
area.x = bg_area->x;
area.y = bg_area->y;
area.height = bg_area->height;
@@ -685,6 +827,11 @@ draw_breakpoint_bg (IdeOmniGutterRenderer *self,
rgba = self->bkpt.bg;
+ /*
+ * Tweak the brightness based on if we are in pre-light and
+ * if we are also still an active breakpoint.
+ */
+
if ((state & GTK_SOURCE_GUTTER_RENDERER_STATE_PRELIT) != 0)
{
if (IS_BREAKPOINT (info))
@@ -693,6 +840,8 @@ draw_breakpoint_bg (IdeOmniGutterRenderer *self,
rgba.alpha *= 0.4;
}
+ /* And draw... */
+
gdk_cairo_set_source_rgba (cr, &rgba);
cairo_fill (cr);
}
@@ -708,18 +857,30 @@ draw_add_change (IdeOmniGutterRenderer *self,
g_assert (cr != NULL);
g_assert (area != NULL);
- cairo_rectangle (cr,
- area->x + area->width - 2 - CHANGE_WIDTH,
- area->y,
- CHANGE_WIDTH,
- area->y + area->height);
+ /*
+ * Draw a simple line with the appropriate color from the style scheme
+ * based on the type of change we have.
+ */
- if (info->is_add)
- gdk_cairo_set_source_rgba (cr, &self->changes.add);
- else
- gdk_cairo_set_source_rgba (cr, &self->changes.change);
+ if (info->is_add || info->is_change)
+ {
+ cairo_rectangle (cr,
+ area->x + area->width - 2 - CHANGE_WIDTH,
+ area->y,
+ CHANGE_WIDTH,
+ area->y + area->height);
- cairo_fill (cr);
+ if (info->is_add)
+ gdk_cairo_set_source_rgba (cr, &self->changes.add);
+ else
+ gdk_cairo_set_source_rgba (cr, &self->changes.change);
+
+ cairo_fill (cr);
+ }
+
+ /*
+ * TODO: Draw a red deletion mark if necessary.
+ */
}
static void
@@ -790,6 +951,13 @@ ide_omni_gutter_renderer_draw (GtkSourceGutterRenderer *renderer,
g_assert (begin != NULL);
g_assert (end != NULL);
+ /*
+ * This is our primary draw routine. It is called for every line that
+ * is visible. We are incredibly sensitive to performance churn here
+ * so it is important that we be as minimal as possible while
+ * retaining the features we need.
+ */
+
view = gtk_source_gutter_renderer_get_view (GTK_SOURCE_GUTTER_RENDERER (self));
highlight_line = gtk_source_view_get_highlight_current_line (GTK_SOURCE_VIEW (view));
has_focus = gtk_widget_has_focus (GTK_WIDGET (view));
@@ -805,6 +973,12 @@ ide_omni_gutter_renderer_draw (GtkSourceGutterRenderer *renderer,
gchar linestr[16];
gint len;
+ /*
+ * Draw some background for the line so that it looks like the
+ * breakpoint arrow draws over it. Debugger break line takes
+ * precidence over the current highlight line. Also, ensure that
+ * the view is drawing the highlight line first.
+ */
if (line == self->stopped_line)
{
gdk_cairo_rectangle (cr, bg_area);
@@ -818,9 +992,15 @@ ide_omni_gutter_renderer_draw (GtkSourceGutterRenderer *renderer,
cairo_fill (cr);
}
+ /* Draw line changes next so it will show up underneath the
+ * breakpoint arrows.
+ */
if (self->show_line_changes && (info->is_add || info->is_change))
draw_add_change (self, cr, cell_area, info, state);
+ /* Draw breakpoint arrows if we have any breakpoints that could
+ * potentially match.
+ */
if (self->breakpoints != NULL)
{
has_breakpoint = IS_BREAKPOINT (info);
@@ -828,11 +1008,24 @@ ide_omni_gutter_renderer_draw (GtkSourceGutterRenderer *renderer,
draw_breakpoint_bg (self, cr, cell_area, info, state);
}
+ /* Now that we might have an altered background for the line,
+ * we can draw the diagnostic icon (with possibly altered
+ * color for symbolic icon).
+ */
if (self->show_line_diagnostics && IS_DIAGNOSTIC (info))
draw_diagnostic (self, cr, cell_area, info, state);
+ /*
+ * Now draw the line numbers if we are showing them. Ensure
+ * we tweak the style to match closely to how the default
+ * gtksourceview lines gutter renderer does it.
+ */
if (self->show_line_numbers)
{
+ /* TODO: Easy performance win here is to use an array of
+ * strings containing the first 1000 or so line numbers
+ * with direct index offsets.
+ */
len = g_snprintf (linestr, sizeof linestr, "%u", line + 1);
pango_layout_set_text (self->layout, linestr, len);
@@ -854,6 +1047,7 @@ ide_omni_gutter_renderer_draw (GtkSourceGutterRenderer *renderer,
bold = self->text.bold;
}
+ /* Current line is always bold */
if (state & GTK_SOURCE_GUTTER_RENDERER_STATE_CURSOR)
bold |= self->current.bold;
@@ -880,6 +1074,16 @@ get_icon_surface (IdeOmniGutterRenderer *self,
g_assert (GTK_IS_WIDGET (widget));
g_assert (icon_name != NULL);
+ /*
+ * This deals with loading a given icon by icon name and trying to
+ * apply our current style as the symbolic colors. We do not support
+ * error/warning/etc for symbolic icons so they are all replaced with
+ * the proper foreground color.
+ *
+ * If selected is set, we alter the color to make sure it will look
+ * good on top of a breakpoint arrow.
+ */
+
screen = gtk_widget_get_screen (widget);
icon_theme = gtk_icon_theme_get_for_screen (screen);
@@ -920,6 +1124,15 @@ ide_omni_gutter_renderer_reload_icons (IdeOmniGutterRenderer *self)
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
+ /*
+ * This isn't ideal (we should find a better way to cache icons that
+ * is safe with scale and foreground color changes we need).
+ *
+ * TODO: Create something similar to pixbuf helpers that allow for
+ * more control over the cache key so it can be shared between
+ * multiple instances.
+ */
+
g_clear_pointer (&self->note_surface, cairo_surface_destroy);
g_clear_pointer (&self->warning_surface, cairo_surface_destroy);
g_clear_pointer (&self->error_surface, cairo_surface_destroy);
@@ -954,6 +1167,12 @@ ide_omni_gutter_renderer_reload (IdeOmniGutterRenderer *self)
view = gtk_source_gutter_renderer_get_view (GTK_SOURCE_GUTTER_RENDERER (self));
buffer = gtk_text_view_get_buffer (view);
+ /*
+ * Use the Language ID to determine if it makes sense to show
+ * breakpoints. We don't want to show them for things like
+ * markdown files and such.
+ */
+
if (NULL != (language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (buffer))))
id = gtk_source_language_get_id (language);
@@ -971,49 +1190,44 @@ ide_omni_gutter_renderer_reload (IdeOmniGutterRenderer *self)
}
}
+ /* Replace our previous breakpoints */
g_set_object (&self->breakpoints, breakpoints);
+ /* Reload icons and then recalcuate our physical size */
ide_omni_gutter_renderer_reload_icons (self);
ide_omni_gutter_renderer_recalculate_size (self);
}
static void
-ide_omni_gutter_renderer_bind_view (IdeOmniGutterRenderer *self,
- IdeSourceView *view,
- DzlSignalGroup *view_signals)
+ide_omni_gutter_renderer_notify_buffer (IdeOmniGutterRenderer *self,
+ GParamSpec *pspec,
+ IdeSourceView *view)
{
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
g_assert (IDE_IS_SOURCE_VIEW (view));
- g_assert (DZL_IS_SIGNAL_GROUP (view_signals));
if (self->buffer_signals != NULL)
{
- GtkTextBuffer *buffer;
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
- if (IDE_IS_BUFFER (buffer))
- dzl_signal_group_set_target (self->buffer_signals, buffer);
+ if (!IDE_IS_BUFFER (buffer))
+ buffer = NULL;
+ dzl_signal_group_set_target (self->buffer_signals, buffer);
ide_omni_gutter_renderer_reload (self);
}
}
static void
-ide_omni_gutter_renderer_notify_buffer (IdeOmniGutterRenderer *self,
- GParamSpec *pspec,
- IdeSourceView *view)
+ide_omni_gutter_renderer_bind_view (IdeOmniGutterRenderer *self,
+ IdeSourceView *view,
+ DzlSignalGroup *view_signals)
{
- GtkTextBuffer *buffer;
-
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
- g_assert (pspec != NULL);
g_assert (IDE_IS_SOURCE_VIEW (view));
+ g_assert (DZL_IS_SIGNAL_GROUP (view_signals));
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
- if (IDE_IS_BUFFER (buffer))
- dzl_signal_group_set_target (self->buffer_signals, buffer);
-
- ide_omni_gutter_renderer_reload (self);
+ ide_omni_gutter_renderer_notify_buffer (self, NULL, view);
}
static void
@@ -1036,6 +1250,9 @@ ide_omni_gutter_renderer_notify_view (IdeOmniGutterRenderer *self)
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
view = gtk_source_gutter_renderer_get_view (GTK_SOURCE_GUTTER_RENDERER (self));
+ if (!IDE_IS_SOURCE_VIEW (view))
+ view = NULL;
+
dzl_signal_group_set_target (self->view_signals, view);
}
@@ -1047,9 +1264,7 @@ ide_omni_gutter_renderer_do_recalc (gpointer data)
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
self->resize_source = 0;
-
ide_omni_gutter_renderer_recalculate_size (self);
-
return G_SOURCE_REMOVE;
}
@@ -1060,6 +1275,7 @@ ide_omni_gutter_renderer_buffer_changed (IdeOmniGutterRenderer *self,
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
g_assert (IDE_IS_BUFFER (buffer));
+ /* Run immediately at the end of this main loop iteration */
if (self->resize_source == 0)
self->resize_source = gdk_threads_add_idle_full (G_PRIORITY_HIGH,
ide_omni_gutter_renderer_do_recalc,
@@ -1077,8 +1293,11 @@ ide_omni_gutter_renderer_notify_style_scheme (IdeOmniGutterRenderer *self,
g_assert (IDE_IS_OMNI_GUTTER_RENDERER (self));
g_assert (IDE_IS_BUFFER (buffer));
+ /* Update our cached rgba colors */
scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer));
reload_style_colors (self, scheme);
+
+ /* Regenerate icons matching the scheme colors */
ide_omni_gutter_renderer_reload_icons (self);
}
@@ -1115,6 +1334,12 @@ ide_omni_gutter_renderer_dispose (GObject *object)
ide_clear_source (&self->resize_source);
+ g_clear_object (&self->breakpoints);
+ g_clear_pointer (&self->lines, g_array_unref);
+
+ g_clear_object (&self->view_signals);
+ g_clear_object (&self->buffer_signals);
+
g_clear_pointer (&self->note_surface, cairo_surface_destroy);
g_clear_pointer (&self->warning_surface, cairo_surface_destroy);
g_clear_pointer (&self->error_surface, cairo_surface_destroy);
@@ -1122,16 +1347,9 @@ ide_omni_gutter_renderer_dispose (GObject *object)
g_clear_pointer (&self->warning_selected_surface, cairo_surface_destroy);
g_clear_pointer (&self->error_selected_surface, cairo_surface_destroy);
- g_clear_pointer (&self->lines, g_array_unref);
-
g_clear_object (&self->layout);
g_clear_pointer (&self->bold_attrs, pango_attr_list_unref);
- g_clear_object (&self->breakpoints);
-
- g_clear_object (&self->buffer_signals);
- g_clear_object (&self->view_signals);
-
G_OBJECT_CLASS (ide_omni_gutter_renderer_parent_class)->dispose (object);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]