[gitg/vala] Added various classes for rendering lanes
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg/vala] Added various classes for rendering lanes
- Date: Sun, 22 Apr 2012 13:15:10 +0000 (UTC)
commit 0a8d1985ebbc1cbfd912eac962d034cb3bcb50ea
Author: Jesse van den Kieboom <jesse vandenkieboom epfl ch>
Date: Sun Apr 22 15:13:02 2012 +0200
Added various classes for rendering lanes
libgitg-gtk/Makefile.am | 8 +-
libgitg-gtk/gitg-gtk-cell-renderer-lanes.vala | 214 ++++++++++++++++
libgitg-gtk/gitg-gtk-commit-list-view.vala | 74 ++++++
libgitg-gtk/gitg-gtk-label-renderer.vala | 332 +++++++++++++++++++++++++
4 files changed, 625 insertions(+), 3 deletions(-)
---
diff --git a/libgitg-gtk/Makefile.am b/libgitg-gtk/Makefile.am
index 1972003..6aeafcc 100644
--- a/libgitg-gtk/Makefile.am
+++ b/libgitg-gtk/Makefile.am
@@ -33,9 +33,11 @@ VALAFLAGS = \
GitgGtk-1.0.gir: libgitg-gtk-1.0.la
-VALA_FILES = \
- gitg-gtk-commit-list-view.vala \
- gitg-gtk-commit-model.vala
+VALA_FILES = \
+ gitg-gtk-commit-model.vala \
+ gitg-gtk-label-renderer.vala \
+ gitg-gtk-cell-renderer-lanes.vala \
+ gitg-gtk-commit-list-view.vala
# Ignore all warnings for vala code...
libgitg_gtk_1_0_la_CFLAGS = \
diff --git a/libgitg-gtk/gitg-gtk-cell-renderer-lanes.vala b/libgitg-gtk/gitg-gtk-cell-renderer-lanes.vala
new file mode 100644
index 0000000..c2b080a
--- /dev/null
+++ b/libgitg-gtk/gitg-gtk-cell-renderer-lanes.vala
@@ -0,0 +1,214 @@
+namespace GitgGtk
+{
+ class CellRendererLanes : Gtk.CellRendererText
+ {
+ public Gitg.Commit? commit { get; set; }
+ public Gitg.Commit? next_commit { get; set; }
+ public uint lane_width { get; set; default = 16; }
+ public uint dot_width { get; set; default = 10; }
+ public unowned SList<Gitg.Ref> labels { get; set; }
+
+ private int d_last_height;
+
+ private uint num_lanes
+ {
+ get { return commit.lanes.length(); }
+ }
+
+ private uint total_width(Gtk.Widget widget)
+ {
+ return num_lanes * lane_width +
+ LabelRenderer.width(widget, font_desc, labels);
+ }
+
+ public override void get_size(Gtk.Widget widget,
+ Gdk.Rectangle? area,
+ out int xoffset,
+ out int yoffset,
+ out int width,
+ out int height)
+ {
+ xoffset = 0;
+ yoffset = 0;
+
+ width = (int)total_width(widget);
+ height = area != null ? area.height : 1;
+ }
+
+ private void draw_arrow(Cairo.Context context,
+ Gdk.Rectangle area,
+ uint laneidx,
+ bool top)
+ {
+ double cw = lane_width;
+ double xpos = area.x + laneidx * cw + cw / 2.0;
+ double df = (top ? -1 : 1) * 0.25 * area.height;
+ double ypos = area.y + area.height / 2.0 + df;
+ double q = cw / 4.0;
+
+ context.move_to(xpos - q, ypos + (top ? q : -q));
+ context.line_to(xpos, ypos);
+ context.line_to(xpos + q, ypos + (top ? q : -q));
+ context.stroke();
+
+ context.move_to(xpos, ypos);
+ context.line_to(xpos, ypos - df);
+ context.stroke();
+ }
+
+ private void draw_arrows(Cairo.Context context,
+ Gdk.Rectangle area)
+ {
+ uint to = 0;
+
+ foreach (Gitg.Lane lane in commit.lanes)
+ {
+ var color = lane.color;
+ context.set_source_rgb(color.r, color.g, color.b);
+
+ if (lane.tag == Gitg.LaneTag.START)
+ {
+ draw_arrow(context, area, to, true);
+ }
+ else if (lane.tag == Gitg.LaneTag.END)
+ {
+ draw_arrow(context, area, to, false);
+ }
+
+ ++to;
+ }
+ }
+
+ private void draw_paths_real(Cairo.Context context,
+ Gdk.Rectangle area,
+ Gitg.Commit? commit,
+ double yoffset)
+ {
+ if (commit == null)
+ {
+ return;
+ }
+
+ int to = 0;
+ double cw = lane_width;
+ double ch = area.height / 2.0;
+
+ foreach (var lane in commit.lanes)
+ {
+ var color = lane.color;
+ context.set_source_rgb(color.r, color.g, color.b);
+
+ foreach (var from in lane.from)
+ {
+ double x1 = area.x + from * cw + cw / 2.0;
+ double x2 = area.x + to * cw + cw / 2.0;
+ double y1 = area.y + yoffset * ch;
+ double y2 = area.y + (yoffset + 1) * ch;
+ double y3 = area.y + (yoffset + 2) * ch;
+
+ context.move_to(x1, y1);
+ context.curve_to(x1, y2, x2, y2, x2, y3);
+ context.stroke();
+ }
+
+ ++to;
+ }
+ }
+
+ private void draw_top_paths(Cairo.Context context,
+ Gdk.Rectangle area)
+ {
+ draw_paths_real(context, area, commit, -1);
+ }
+
+ private void draw_bottom_paths(Cairo.Context context,
+ Gdk.Rectangle area)
+ {
+ draw_paths_real(context, area, next_commit, 1);
+ }
+
+ private void draw_paths(Cairo.Context context,
+ Gdk.Rectangle area)
+ {
+ context.set_line_width(2.0);
+ context.set_line_cap(Cairo.LineCap.ROUND);
+
+ draw_top_paths(context, area);
+ draw_bottom_paths(context, area);
+ draw_arrows(context, area);
+ }
+
+ private void draw_indicator(Cairo.Context context,
+ Gdk.Rectangle area)
+ {
+ double offset;
+ double radius;
+
+ offset = commit.mylane * lane_width + (lane_width - dot_width) / 2.0;
+ radius = dot_width / 2.0;
+
+ context.set_line_width(2.0);
+
+ context.arc(area.x + offset + radius,
+ area.y + area.height / 2.0,
+ radius,
+ 0,
+ 2 * Math.PI);
+
+ context.set_source_rgb(0, 0, 0);
+ context.stroke_preserve();
+
+ if (commit.lane != null)
+ {
+ var color = commit.lane.color;
+ context.set_source_rgb(color.r, color.g, color.b);
+ }
+
+ context.fill();
+ }
+
+ private void draw_labels(Cairo.Context context,
+ Gdk.Rectangle area,
+ Gtk.Widget widget)
+ {
+ uint offset;
+
+ offset = num_lanes * lane_width;
+
+ context.translate(offset, 0);
+ LabelRenderer.draw(widget, font_desc, context, labels, area);
+ }
+
+ public override void render(Cairo.Context context,
+ Gtk.Widget widget,
+ Gdk.Rectangle area,
+ Gdk.Rectangle cell_area,
+ Gtk.CellRendererState flags)
+ {
+ d_last_height = area.height;
+
+ context.save();
+
+ Gdk.cairo_rectangle(context, area);
+ context.clip();
+
+ draw_paths(context, area);
+ draw_indicator(context, area);
+ draw_labels(context, area, widget);
+
+ var narea = area;
+ var ncell_area = cell_area;
+
+ var tw = total_width(widget);
+
+ narea.x += (int)tw;
+ ncell_area.x += (int)tw;
+
+ context.restore();
+
+ base.render(context, widget, narea, ncell_area, flags);
+ }
+ }
+}
+
+// vi:ts=4
diff --git a/libgitg-gtk/gitg-gtk-commit-list-view.vala b/libgitg-gtk/gitg-gtk-commit-list-view.vala
new file mode 100644
index 0000000..96224d6
--- /dev/null
+++ b/libgitg-gtk/gitg-gtk-commit-list-view.vala
@@ -0,0 +1,74 @@
+namespace GitgGtk
+{
+ public class CommitListView : Gtk.TreeView, Gtk.Buildable
+ {
+ public CommitListView(GitgGtk.CommitModel model)
+ {
+ Object(model: model);
+ }
+
+ public CommitListView.for_repository(Gitg.Repository repository)
+ {
+ this(new GitgGtk.CommitModel(repository));
+ }
+
+ private void lanes_data_func(Gtk.CellLayout layout,
+ Gtk.CellRenderer cell,
+ Gtk.TreeModel model,
+ Gtk.TreeIter iter)
+ {
+ CommitModel? m = model as CommitModel;
+
+ if (m == null)
+ {
+ return;
+ }
+
+ CellRendererLanes lanes = (CellRendererLanes)cell;
+ Gitg.Commit? commit = m.commit_from_iter(iter);
+
+ if (commit == null)
+ {
+ return;
+ }
+
+ var cp = iter;
+ Gitg.Commit? next_commit = null;
+
+ if (m.iter_next(ref cp))
+ {
+ next_commit = m.commit_from_iter(cp);
+ }
+
+ unowned SList<Gitg.Ref> labels = m.repository.refs_for_id(commit.get_id());
+
+ lanes.commit = commit;
+ lanes.next_commit = next_commit;
+ lanes.labels = labels;
+ }
+
+ private void parser_finished(Gtk.Builder builder)
+ {
+ base.parser_finished(builder);
+
+ // Check if there is a cell renderer
+ foreach (var column in get_columns())
+ {
+ foreach (var cell in column.get_cells())
+ {
+ CellRendererLanes? lanes = cell as CellRendererLanes;
+
+ if (lanes == null)
+ {
+ continue;
+ }
+
+ column.set_cell_data_func(lanes,
+ lanes_data_func);
+ }
+ }
+ }
+ }
+}
+
+// vi:ts=4
diff --git a/libgitg-gtk/gitg-gtk-label-renderer.vala b/libgitg-gtk/gitg-gtk-label-renderer.vala
new file mode 100644
index 0000000..4319acf
--- /dev/null
+++ b/libgitg-gtk/gitg-gtk-label-renderer.vala
@@ -0,0 +1,332 @@
+namespace GitgGtk
+{
+ public class LabelRenderer
+ {
+ private static const int margin = 3;
+ private static const int padding = 4;
+
+ private static string label_text(Gitg.Ref r)
+ {
+ var escaped = Markup.escape_text(r.parsed_name.shortname);
+ return "<span size='smaller'>%s</span>".printf(escaped);
+ }
+
+ private static int get_label_width(Pango.Layout layout,
+ Gitg.Ref r)
+ {
+ var smaller = label_text(r);
+
+ int w;
+
+ layout.set_markup(smaller, -1);
+ layout.get_pixel_size(out w, null);
+
+ return w + padding * 2;
+ }
+
+ public static int width(Gtk.Widget widget,
+ Pango.FontDescription *font,
+ SList<Gitg.Ref> labels)
+ {
+ if (labels == null)
+ {
+ return 0;
+ }
+
+ int ret = 0;
+
+ var ctx = widget.get_pango_context();
+ var layout = new Pango.Layout(ctx);
+
+ layout.set_font_description(font);
+
+ foreach (Gitg.Ref r in labels)
+ {
+ ret += get_label_width(layout, r) + margin;
+ }
+
+ return ret + margin;
+ }
+
+ private static void
+ rounded_rectangle(Cairo.Context context,
+ double x,
+ double y,
+ double width,
+ double height,
+ double radius)
+ {
+ context.move_to(x + radius, y);
+ context.rel_line_to(width - 2 * radius, 0);
+ context.arc(x + width - radius, y + radius, radius, 1.5 * Math.PI, 0.0);
+
+ context.rel_line_to(0, height - 2 * radius);
+ context.arc(x + width - radius, y + height - radius, radius, 0.0, 0.5 * Math.PI);
+
+ context.rel_line_to(-(width - radius * 2), 0);
+ context.arc(x + radius, y + height - radius, radius, 0.5 * Math.PI, Math.PI);
+
+ context.rel_line_to(0, -(height - radius * 2));
+ context.arc(x + radius, y + radius, radius, Math.PI, 1.5 * Math.PI);
+ }
+
+ private static void get_type_color(Gitg.RefType type,
+ out double r,
+ out double g,
+ out double b)
+ {
+ switch (type)
+ {
+ case Gitg.RefType.NONE:
+ r = 1;
+ g = 1;
+ b = 0.8;
+ break;
+ case Gitg.RefType.BRANCH:
+ r = 0.8;
+ g = 1;
+ b = 0.5;
+ break;
+ case Gitg.RefType.REMOTE:
+ r = 0.5;
+ g = 0.8;
+ b = 1;
+ break;
+ case Gitg.RefType.TAG:
+ r = 1;
+ g = 1;
+ b = 0;
+ break;
+ case Gitg.RefType.STASH:
+ r = 1;
+ g = 0.8;
+ b = 0.5;
+ break;
+ default:
+ r = 1;
+ g = 1;
+ b = 1;
+ break;
+ }
+ }
+
+ private static void get_ref_color(Gitg.Ref rref,
+ out double r,
+ out double g,
+ out double b)
+ {
+ if (rref.working)
+ {
+ r = 1;
+ g = 0.7;
+ b = 0;
+ }
+ else
+ {
+ get_type_color(rref.parsed_name.rtype, out r, out g, out b);
+ }
+ }
+
+ private static void set_source_for_ref_type(Cairo.Context context,
+ Gitg.Ref rref,
+ bool use_state)
+ {
+ if (use_state)
+ {
+ switch (rref.state)
+ {
+ case Gitg.RefState.SELECTED:
+ context.set_source_rgb(1, 1, 1);
+ return;
+ case Gitg.RefState.PRELIGHT:
+ {
+ double r, g, b;
+
+ get_ref_color(rref, out r, out g, out b);
+ context.set_source_rgba(r, g, b, 0.3);
+ return;
+ }
+ }
+ }
+
+ double r, g, b;
+ get_ref_color(rref, out r, out g, out b);
+
+ context.set_source_rgb(r, g, b);
+ }
+
+ private static int render_label(Cairo.Context context,
+ Pango.Layout layout,
+ Gitg.Ref r,
+ int x,
+ int y,
+ int height,
+ bool use_state)
+ {
+ var smaller = label_text(r);
+
+ layout.set_markup(smaller, -1);
+
+ int w;
+ int h;
+
+ layout.get_pixel_size(out w, out h);
+
+ rounded_rectangle(context,
+ x + 0.5,
+ y + margin + 0.5,
+ w + padding * 2,
+ height - margin * 2,
+ 5);
+
+ set_source_for_ref_type(context, r, use_state);
+ context.fill_preserve();
+
+ context.set_source_rgb(0, 0, 0);
+ context.stroke();
+
+ context.save();
+
+ context.translate(x + padding, y + (height - h) / 2.0 + 0.5);
+ Pango.cairo_show_layout(context, layout);
+
+ context.restore();
+ return w;
+ }
+
+ public static void draw(Gtk.Widget widget,
+ Pango.FontDescription font,
+ Cairo.Context context,
+ SList<Gitg.Ref> labels,
+ Gdk.Rectangle area)
+ {
+ double pos = margin + 0.5;
+
+ context.save();
+ context.set_line_width(1.0);
+
+ var ctx = widget.get_pango_context();
+ var layout = new Pango.Layout(ctx);
+
+ layout.set_font_description(font);
+
+ foreach (Gitg.Ref r in labels)
+ {
+ var w = render_label(context,
+ layout,
+ r,
+ (int)pos,
+ area.y,
+ area.height,
+ true);
+
+ pos += w + padding * 2 + margin;
+ }
+
+ context.restore();
+ }
+
+ public static Gitg.Ref? get_ref_at_pos(Gtk.Widget widget,
+ Pango.FontDescription font,
+ SList<Gitg.Ref> labels,
+ int x,
+ out int hot_x)
+ {
+ hot_x = 0;
+
+ if (labels == null)
+ {
+ return null;
+ }
+
+ var ctx = widget.get_pango_context();
+ var layout = new Pango.Layout(ctx);
+
+ layout.set_font_description(font);
+
+ int start = margin;
+ Gitg.Ref? ret = null;
+
+ foreach (Gitg.Ref r in labels)
+ {
+ int width = get_label_width(layout, r);
+
+ if (x >= start && x <= start + width)
+ {
+ ret = r;
+ hot_x = x - start;
+
+ break;
+ }
+
+ start += width + margin;
+ }
+
+ return ret;
+ }
+
+ private static uchar convert_color_channel(uchar color,
+ uchar alpha)
+ {
+ return (uchar)((alpha != 0) ? (color / (alpha / 255.0)) : 0);
+ }
+
+ private static void convert_bgra_to_rgba(uchar[] src,
+ uchar[] dst,
+ int width,
+ int height)
+ {
+ int i = 0;
+
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ dst[i] = convert_color_channel(src[i + 2], src[i + 3]);
+ dst[i + 1] = convert_color_channel(src[i + 1], src[i + 3]);
+ dst[i + 2] = convert_color_channel(src[i], src[i + 3]);
+ dst[i + 3] = src[i + 3];
+
+ i += 4;
+ }
+ }
+ }
+
+ public static Gdk.Pixbuf render_ref(Gtk.Widget widget,
+ Pango.FontDescription font,
+ Gitg.Ref r,
+ int height,
+ int minwidth)
+ {
+ var ctx = widget.get_pango_context();
+ var layout = new Pango.Layout(ctx);
+
+ layout.set_font_description(font);
+
+ int width = int.max(get_label_width(layout, r), minwidth);
+
+ var surface = new Cairo.ImageSurface(Cairo.Format.ARGB32,
+ width + 2,
+ height + 2);
+
+ var context = new Cairo.Context(surface);
+ context.set_line_width(1);
+
+ render_label(context, layout, r, 1, 1, height, false);
+ var data = surface.get_data();
+
+ Gdk.Pixbuf ret = new Gdk.Pixbuf(Gdk.Colorspace.RGB,
+ true,
+ 8,
+ width + 2,
+ height + 2);
+
+ var pixdata = ret.get_pixels();
+ convert_bgra_to_rgba(data, pixdata, width + 2, height + 2);
+
+ return ret;
+ }
+ }
+}
+
+// vi:ts=4
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]