[gtksourceview/wip/gutter-renderer-folding: 1/2] GutterRendererFolds: drawing



commit 2f8a41aa238f8261b8ba79157e093de0b3f8c8c9
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Fri Sep 6 18:38:27 2013 +0200

    GutterRendererFolds: drawing
    
    And add a test program.

 gtksourceview/gtksource.h                    |    1 +
 gtksourceview/gtksourcegutterrendererfolds.c |  282 ++++++++++++++++++++++++++
 gtksourceview/gtksourcegutterrendererfolds.h |    4 +-
 tests/Makefile.am                            |    7 +
 tests/test-folding.c                         |   60 ++++++
 5 files changed, 353 insertions(+), 1 deletions(-)
---
diff --git a/gtksourceview/gtksource.h b/gtksourceview/gtksource.h
index 1c7bd79..4d09fe9 100644
--- a/gtksourceview/gtksource.h
+++ b/gtksourceview/gtksource.h
@@ -30,6 +30,7 @@
 #include <gtksourceview/gtksourcecompletionprovider.h>
 #include <gtksourceview/gtksourcegutter.h>
 #include <gtksourceview/gtksourcegutterrenderer.h>
+#include <gtksourceview/gtksourcegutterrendererfolds.h>
 #include <gtksourceview/gtksourcegutterrenderertext.h>
 #include <gtksourceview/gtksourcegutterrendererpixbuf.h>
 #include <gtksourceview/gtksourcelanguage.h>
diff --git a/gtksourceview/gtksourcegutterrendererfolds.c b/gtksourceview/gtksourcegutterrendererfolds.c
index d82cc47..5420bd3 100644
--- a/gtksourceview/gtksourcegutterrendererfolds.c
+++ b/gtksourceview/gtksourcegutterrendererfolds.c
@@ -21,6 +21,26 @@
 
 #include "gtksourcegutterrendererfolds.h"
 
+#define LINE_WIDTH     1.0
+
+/* The square size for drawing the box around the minus and plus signs. If the
+ * line width is 1, the square size must be an odd number, to be able to draw
+ * the sign in the middle of the square.
+ */
+#define SQUARE_SIZE    9
+
+/* The folding state at a certain line in the text buffer.
+ * Some states can be combined. For example, END and CONTINUE.
+ */
+typedef enum
+{
+       FOLDING_NONE            = 0,
+       FOLDING_START_FOLDED    = 1 << 0,
+       FOLDING_START_OPENED    = 1 << 1,
+       FOLDING_CONTINUE        = 1 << 2,
+       FOLDING_END             = 1 << 3
+} FoldingState;
+
 struct _GtkSourceGutterRendererFoldsPrivate
 {
        gint something;
@@ -30,6 +50,256 @@ G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceGutterRendererFolds,
                            gtk_source_gutter_renderer_folds,
                            GTK_SOURCE_TYPE_GUTTER_RENDERER)
 
+/* Draw a minus or a plus surrounded by a square. */
+static void
+draw_sign (cairo_t      *cr,
+          GdkRectangle *cell_area,
+          gboolean      folded)
+{
+       gint sign_width = SQUARE_SIZE - 4;
+       gint left_margin;
+       gint top_margin;
+       gint x;
+       gint y;
+
+       /* Integer division */
+       left_margin = (cell_area->width - SQUARE_SIZE) / 2;
+       top_margin = (cell_area->height - SQUARE_SIZE) / 2;
+
+       x = cell_area->x + left_margin;
+       y = cell_area->y + top_margin;
+
+       cairo_rectangle (cr,
+                        x + 0.5,
+                        y + 0.5,
+                        SQUARE_SIZE - 1.0,
+                        SQUARE_SIZE - 1.0);
+
+       cairo_move_to (cr,
+                      x + 2.5,
+                      y + SQUARE_SIZE / 2.0);
+
+       cairo_rel_line_to (cr, sign_width - 1.0, 0);
+
+       if (folded)
+       {
+               cairo_move_to (cr,
+                              x + SQUARE_SIZE / 2.0,
+                              y + 2.5);
+
+               cairo_rel_line_to (cr, 0, sign_width - 1.0);
+       }
+}
+
+static void
+draw_vertical_line (cairo_t      *cr,
+                   GdkRectangle *cell_area)
+{
+       /* Integer division */
+       gint x = cell_area->x + cell_area->width / 2;
+
+       cairo_move_to (cr,
+                      x + 0.5,
+                      cell_area->y + 0.5);
+
+       cairo_rel_line_to (cr,
+                          0,
+                          cell_area->height - 1.0);
+}
+
+static void
+draw_end (cairo_t      *cr,
+         GdkRectangle *cell_area)
+{
+       /* Integer division */
+       gint x = cell_area->x + cell_area->width / 2;
+       gint height = cell_area->height / 2 + 1;
+
+       cairo_move_to (cr,
+                      x + 0.5,
+                      cell_area->y + 0.5);
+
+       cairo_rel_line_to (cr,
+                          0,
+                          height - 1.0);
+
+       cairo_line_to (cr,
+                      cell_area->x + cell_area->width - 0.5,
+                      cell_area->y + height - 0.5);
+}
+
+/* To draw the folding states (that can be combined), the cell_area is split in
+ * three parts. The middle_area can contain the minus or plus sign, surrounded
+ * by a square. It can also contain a vertical bar, or a small horizontal bar to
+ * mark a fold end, etc. The top_area and bottom_area can just contain a
+ * vertical bar.
+ */
+static void
+split_cell_area (const GdkRectangle *cell_area,
+                GdkRectangle       *top_area,
+                GdkRectangle       *middle_area,
+                GdkRectangle       *bottom_area)
+{
+       g_assert (SQUARE_SIZE <= cell_area->height);
+
+       top_area->x = cell_area->x;
+       top_area->y = cell_area->y;
+       top_area->width = cell_area->width;
+       top_area->height = (cell_area->height - SQUARE_SIZE) / 2;
+
+       middle_area->x = cell_area->x;
+       middle_area->y = top_area->y + top_area->height;
+       middle_area->width = cell_area->width;
+       middle_area->height = SQUARE_SIZE;
+
+       bottom_area->x = cell_area->x;
+       bottom_area->y = middle_area->y + middle_area->height;
+       bottom_area->width = cell_area->width;
+       bottom_area->height = cell_area->height - top_area->height - middle_area->height;
+}
+
+static void
+gutter_renderer_folds_draw (GtkSourceGutterRenderer      *renderer,
+                           cairo_t                      *cr,
+                           GdkRectangle                 *background_area,
+                           GdkRectangle                 *cell_area,
+                           GtkTextIter                  *start,
+                           GtkTextIter                  *end,
+                           GtkSourceGutterRendererState  state)
+{
+       gint line_num;
+       FoldingState folding_state;
+       GdkRectangle top_area;
+       GdkRectangle middle_area;
+       GdkRectangle bottom_area;
+
+       /* Chain up to draw background */
+       GTK_SOURCE_GUTTER_RENDERER_CLASS (
+               gtk_source_gutter_renderer_folds_parent_class)->draw (renderer,
+                                                                     cr,
+                                                                     background_area,
+                                                                     cell_area,
+                                                                     start,
+                                                                     end,
+                                                                     state);
+
+       line_num = gtk_text_iter_get_line (start);
+
+       if (line_num == 0)
+       {
+               folding_state = FOLDING_START_FOLDED;
+       }
+       else if (line_num == 1)
+       {
+               folding_state = FOLDING_START_OPENED;
+       }
+       else if (line_num < 5)
+       {
+               folding_state = FOLDING_CONTINUE;
+       }
+       else if (line_num == 5)
+       {
+               folding_state = FOLDING_END;
+       }
+       else if (line_num == 6)
+       {
+               folding_state = FOLDING_NONE;
+       }
+       else if (line_num == 7)
+       {
+               folding_state = FOLDING_START_OPENED;
+       }
+       else if (line_num == 8)
+       {
+               folding_state = FOLDING_CONTINUE;
+       }
+       else if (line_num == 9)
+       {
+               folding_state = FOLDING_CONTINUE | FOLDING_START_OPENED;
+       }
+       else if (line_num < 12)
+       {
+               folding_state = FOLDING_CONTINUE;
+       }
+       else if (line_num == 12)
+       {
+               folding_state = FOLDING_CONTINUE | FOLDING_END;
+       }
+       else if (line_num == 13)
+       {
+               folding_state = FOLDING_CONTINUE;
+       }
+       else if (line_num == 14)
+       {
+               folding_state = FOLDING_CONTINUE | FOLDING_START_FOLDED;
+       }
+       else if (line_num == 15)
+       {
+               folding_state = FOLDING_CONTINUE;
+       }
+       else if (line_num == 16)
+       {
+               folding_state = FOLDING_END;
+       }
+       else
+       {
+               folding_state = FOLDING_NONE;
+       }
+
+       split_cell_area (cell_area,
+                        &top_area,
+                        &middle_area,
+                        &bottom_area);
+
+       cairo_save (cr);
+
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+       cairo_set_line_width (cr, LINE_WIDTH);
+
+       /* Top area */
+
+       if (folding_state & FOLDING_CONTINUE ||
+           folding_state & FOLDING_END)
+       {
+               draw_vertical_line (cr, &top_area);
+       }
+
+       /* Middle area */
+
+       if (folding_state & FOLDING_START_FOLDED)
+       {
+               draw_sign (cr, &middle_area, TRUE);
+       }
+       else if (folding_state & FOLDING_START_OPENED)
+       {
+               draw_sign (cr, &middle_area, FALSE);
+       }
+       else
+       {
+               if (folding_state & FOLDING_CONTINUE)
+               {
+                       draw_vertical_line (cr, &middle_area);
+               }
+
+               if (folding_state & FOLDING_END)
+               {
+                       draw_end (cr, &middle_area);
+               }
+       }
+
+       /* Bottom area */
+
+       if (folding_state & FOLDING_START_OPENED ||
+           folding_state & FOLDING_CONTINUE)
+       {
+               draw_vertical_line (cr, &bottom_area);
+       }
+
+       cairo_stroke (cr);
+
+       cairo_restore (cr);
+}
+
 static void
 gtk_source_gutter_renderer_folds_finalize (GObject *object)
 {
@@ -41,8 +311,11 @@ static void
 gtk_source_gutter_renderer_folds_class_init (GtkSourceGutterRendererFoldsClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkSourceGutterRendererClass *renderer_class = GTK_SOURCE_GUTTER_RENDERER_CLASS (klass);
 
        object_class->finalize = gtk_source_gutter_renderer_folds_finalize;
+
+       renderer_class->draw = gutter_renderer_folds_draw;
 }
 
 static void
@@ -50,3 +323,12 @@ gtk_source_gutter_renderer_folds_init (GtkSourceGutterRendererFolds *self)
 {
        self->priv = gtk_source_gutter_renderer_folds_get_instance_private (self);
 }
+
+GtkSourceGutterRenderer *
+gtk_source_gutter_renderer_folds_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_GUTTER_RENDERER_FOLDS,
+                            "size", SQUARE_SIZE,
+                            "xpad", 2,
+                            NULL);
+}
diff --git a/gtksourceview/gtksourcegutterrendererfolds.h b/gtksourceview/gtksourcegutterrendererfolds.h
index 09953f6..22d47c5 100644
--- a/gtksourceview/gtksourcegutterrendererfolds.h
+++ b/gtksourceview/gtksourcegutterrendererfolds.h
@@ -51,7 +51,9 @@ struct _GtkSourceGutterRendererFoldsClass
        gpointer padding[10];
 };
 
-GType gtk_source_gutter_renderer_folds_get_type (void) G_GNUC_CONST;
+GType                   gtk_source_gutter_renderer_folds_get_type      (void) G_GNUC_CONST;
+
+GtkSourceGutterRenderer        *gtk_source_gutter_renderer_folds_new           (void);
 
 G_END_DECLS
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b5e6827..b410345 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -32,6 +32,13 @@ test_completion_LDADD =              \
        $(DEP_LIBS)                     \
        $(TESTS_LIBS)
 
+TEST_PROGS += test-folding
+test_folding_SOURCES = test-folding.c
+test_folding_LDADD =                                           \
+       $(top_builddir)/gtksourceview/libgtksourceview-3.0.la   \
+       $(DEP_LIBS)                                             \
+       $(TESTS_LIBS)
+
 TEST_PROGS += test-search
 test_search_SOURCES =          \
        test-search.c           \
diff --git a/tests/test-folding.c b/tests/test-folding.c
new file mode 100644
index 0000000..13c6a09
--- /dev/null
+++ b/tests/test-folding.c
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* test-folding.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView 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.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <gtksourceview/gtksource.h>
+
+static void
+add_folds_gutter (GtkSourceView *view)
+{
+       GtkSourceGutter *gutter;
+       GtkSourceGutterRenderer *renderer;
+
+       gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
+       renderer = gtk_source_gutter_renderer_folds_new ();
+       gtk_source_gutter_insert (gutter, renderer, 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+       GtkWidget *window;
+       GtkWidget *sw;
+       GtkWidget *source_view;
+
+       gtk_init (&argc, &argv);
+
+       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
+       g_signal_connect (window, "destroy", gtk_main_quit, NULL);
+
+       sw = gtk_scrolled_window_new (NULL, NULL);
+       gtk_container_add (GTK_CONTAINER (window), sw);
+
+       source_view = gtk_source_view_new ();
+       gtk_container_add (GTK_CONTAINER (sw), source_view);
+
+       add_folds_gutter (GTK_SOURCE_VIEW (source_view));
+
+       gtk_widget_show_all (window);
+
+       gtk_main ();
+       return 0;
+}


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