[shotwell/wip/phako/nicer-screen: 1/2] split-up CheckerboardLayout.vala
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell/wip/phako/nicer-screen: 1/2] split-up CheckerboardLayout.vala
- Date: Fri, 8 Feb 2019 21:20:19 +0000 (UTC)
commit 037f2cfc33b1e95bb6fe770fd70c39804b6e27a3
Author: Jens Georg <mail jensge org>
Date: Mon Jan 28 16:00:37 2019 +0100
split-up CheckerboardLayout.vala
src/CheckerboardItem.vala | 734 +++++++++++++++++++++++++++++++++++++
src/CheckerboardItemText.vala | 98 +++++
src/CheckerboardLayout.vala | 818 ------------------------------------------
src/meson.build | 2 +
4 files changed, 834 insertions(+), 818 deletions(-)
---
diff --git a/src/CheckerboardItem.vala b/src/CheckerboardItem.vala
new file mode 100644
index 00000000..c423cad4
--- /dev/null
+++ b/src/CheckerboardItem.vala
@@ -0,0 +1,734 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ *
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ */
+
+public abstract class CheckerboardItem : ThumbnailView {
+ // Collection properties CheckerboardItem understands
+ // SHOW_TITLES (bool)
+ public const string PROP_SHOW_TITLES = "show-titles";
+ // SHOW_COMMENTS (bool)
+ public const string PROP_SHOW_COMMENTS = "show-comments";
+ // SHOW_SUBTITLES (bool)
+ public const string PROP_SHOW_SUBTITLES = "show-subtitles";
+
+ public const int FRAME_WIDTH = 8;
+ public const int LABEL_PADDING = 4;
+ public const int BORDER_WIDTH = 1;
+
+ public const int SHADOW_RADIUS = 4;
+ public const float SHADOW_INITIAL_ALPHA = 0.5f;
+
+ public const int TRINKET_SCALE = 12;
+ public const int TRINKET_PADDING = 1;
+
+ public const int BRIGHTEN_SHIFT = 0x18;
+
+ public Dimensions requisition = Dimensions();
+ public Gdk.Rectangle allocation = Gdk.Rectangle();
+
+ private bool exposure = false;
+ private CheckerboardItemText? title = null;
+ private bool title_visible = true;
+ private CheckerboardItemText? comment = null;
+ private bool comment_visible = true;
+ private CheckerboardItemText? subtitle = null;
+ private bool subtitle_visible = false;
+ private bool is_cursor = false;
+ private Pango.Alignment tag_alignment = Pango.Alignment.LEFT;
+ private Gee.List<Tag>? user_visible_tag_list = null;
+ private Gee.Collection<Tag> tags;
+ private Gdk.Pixbuf pixbuf = null;
+ private Gdk.Pixbuf display_pixbuf = null;
+ private Gdk.Pixbuf brightened = null;
+ private Dimensions pixbuf_dim = Dimensions();
+ private int col = -1;
+ private int row = -1;
+ private int horizontal_trinket_offset = 0;
+
+ public CheckerboardItem(ThumbnailSource source, Dimensions initial_pixbuf_dim, string title, string?
comment,
+ bool marked_up = false, Pango.Alignment alignment = Pango.Alignment.LEFT) {
+ base(source);
+
+ pixbuf_dim = initial_pixbuf_dim;
+ this.title = new CheckerboardItemText(title, alignment, marked_up);
+ // on the checkboard page we display the comment in
+ // one line, i.e., replacing all newlines with spaces.
+ // that means that the display will contain "..." if the comment
+ // is too long.
+ // warning: changes here have to be done in set_comment, too!
+ if (comment != null)
+ this.comment = new CheckerboardItemText(comment.replace("\n", " "), alignment,
+ marked_up);
+
+ // Don't calculate size here, wait for the item to be assigned to a ViewCollection
+ // (notify_membership_changed) and calculate when the collection's property settings
+ // are known
+ }
+
+ public bool has_tags { get; private set; }
+
+ public override string get_name() {
+ return (title != null) ? title.get_text() : base.get_name();
+ }
+
+ public string get_title() {
+ return (title != null) ? title.get_text() : "";
+ }
+
+ public string get_comment() {
+ return (comment != null) ? comment.get_text() : "";
+ }
+
+ public void set_title(string text, bool marked_up = false,
+ Pango.Alignment alignment = Pango.Alignment.LEFT) {
+ if (title != null && title.is_set_to(text, marked_up, alignment))
+ return;
+
+ title = new CheckerboardItemText(text, alignment, marked_up);
+
+ if (title_visible) {
+ recalc_size("set_title");
+ notify_view_altered();
+ }
+ }
+
+ public void translate_coordinates(ref int x, ref int y) {
+ x -= allocation.x + FRAME_WIDTH;
+ y -= allocation.y + FRAME_WIDTH;
+ }
+
+ public void clear_title() {
+ if (title == null)
+ return;
+
+ title = null;
+
+ if (title_visible) {
+ recalc_size("clear_title");
+ notify_view_altered();
+ }
+ }
+
+ private void set_title_visible(bool visible) {
+ if (title_visible == visible)
+ return;
+
+ title_visible = visible;
+
+ recalc_size("set_title_visible");
+ notify_view_altered();
+ }
+
+ public void set_comment(string text, bool marked_up = false,
+ Pango.Alignment alignment = Pango.Alignment.LEFT) {
+ if (comment != null && comment.is_set_to(text, marked_up, alignment))
+ return;
+
+ comment = new CheckerboardItemText(text.replace("\n", " "), alignment, marked_up);
+
+ if (comment_visible) {
+ recalc_size("set_comment");
+ notify_view_altered();
+ }
+ }
+
+ public void clear_comment() {
+ if (comment == null)
+ return;
+
+ comment = null;
+
+ if (comment_visible) {
+ recalc_size("clear_comment");
+ notify_view_altered();
+ }
+ }
+
+ private void set_comment_visible(bool visible) {
+ if (comment_visible == visible)
+ return;
+
+ comment_visible = visible;
+
+ recalc_size("set_comment_visible");
+ notify_view_altered();
+ }
+
+ public void set_tags(Gee.Collection<Tag>? tags,
+ Pango.Alignment alignment = Pango.Alignment.LEFT) {
+ has_tags = (tags != null && tags.size > 0);
+ tag_alignment = alignment;
+ string text;
+ if (has_tags) {
+ this.tags = tags;
+ user_visible_tag_list = Tag.make_user_visible_tag_list(tags);
+ text = Tag.make_tag_markup_string(user_visible_tag_list);
+ } else {
+ text = "<small>.</small>";
+ }
+
+ if (subtitle != null && subtitle.is_set_to(text, true, alignment))
+ return;
+ subtitle = new CheckerboardItemText(text, alignment, true);
+
+ if (subtitle_visible) {
+ recalc_size("set_subtitle");
+ notify_view_altered();
+ }
+ }
+
+ public void clear_tags() {
+ clear_subtitle();
+ has_tags = false;
+ user_visible_tag_list = null;
+ }
+
+ public void highlight_user_visible_tag(int index)
+ requires (user_visible_tag_list != null) {
+ string text = Tag.make_tag_markup_string(user_visible_tag_list, index);
+ subtitle = new CheckerboardItemText(text, tag_alignment, true);
+
+ if (subtitle_visible)
+ notify_view_altered();
+ }
+
+ public Tag get_user_visible_tag(int index)
+ requires (index >= 0 && index < user_visible_tag_list.size) {
+ return user_visible_tag_list.get(index);
+ }
+
+ public Pango.Layout? get_tag_list_layout() {
+ return has_tags ? subtitle.get_pango_layout() : null;
+ }
+
+ public Gdk.Rectangle get_subtitle_allocation() {
+ return subtitle.allocation;
+ }
+
+ public string get_subtitle() {
+ return (subtitle != null) ? subtitle.get_text() : "";
+ }
+
+ public void set_subtitle(string text, bool marked_up = false,
+ Pango.Alignment alignment = Pango.Alignment.LEFT) {
+ if (subtitle != null && subtitle.is_set_to(text, marked_up, alignment))
+ return;
+
+ subtitle = new CheckerboardItemText(text, alignment, marked_up);
+
+ if (subtitle_visible) {
+ recalc_size("set_subtitle");
+ notify_view_altered();
+ }
+ }
+
+ public void clear_subtitle() {
+ if (subtitle == null)
+ return;
+
+ subtitle = null;
+
+ if (subtitle_visible) {
+ recalc_size("clear_subtitle");
+ notify_view_altered();
+ }
+ }
+
+ private void set_subtitle_visible(bool visible) {
+ if (subtitle_visible == visible)
+ return;
+
+ subtitle_visible = visible;
+
+ recalc_size("set_subtitle_visible");
+ notify_view_altered();
+ }
+
+ public void set_is_cursor(bool is_cursor) {
+ this.is_cursor = is_cursor;
+ }
+
+ public bool get_is_cursor() {
+ return is_cursor;
+ }
+
+ public virtual void handle_mouse_motion(int x, int y, int height, int width) {
+
+ }
+
+ public virtual void handle_mouse_leave() {
+ unbrighten();
+ }
+
+ public virtual void handle_mouse_enter() {
+ brighten();
+ }
+
+ protected override void notify_membership_changed(DataCollection? collection) {
+ bool title_visible = (bool) get_collection_property(PROP_SHOW_TITLES, true);
+ bool comment_visible = (bool) get_collection_property(PROP_SHOW_COMMENTS, true);
+ bool subtitle_visible = (bool) get_collection_property(PROP_SHOW_SUBTITLES, false);
+
+ bool altered = false;
+ if (this.title_visible != title_visible) {
+ this.title_visible = title_visible;
+ altered = true;
+ }
+
+ if (this.comment_visible != comment_visible) {
+ this.comment_visible = comment_visible;
+ altered = true;
+ }
+
+ if (this.subtitle_visible != subtitle_visible) {
+ this.subtitle_visible = subtitle_visible;
+ altered = true;
+ }
+
+ if (altered || !requisition.has_area()) {
+ recalc_size("notify_membership_changed");
+ notify_view_altered();
+ }
+
+ base.notify_membership_changed(collection);
+ }
+
+ protected override void notify_collection_property_set(string name, Value? old, Value val) {
+ switch (name) {
+ case PROP_SHOW_TITLES:
+ set_title_visible((bool) val);
+ break;
+
+ case PROP_SHOW_COMMENTS:
+ set_comment_visible((bool) val);
+ break;
+
+ case PROP_SHOW_SUBTITLES:
+ set_subtitle_visible((bool) val);
+ break;
+ }
+
+ base.notify_collection_property_set(name, old, val);
+ }
+
+ // The alignment point is the coordinate on the y-axis (relative to the top of the
+ // CheckerboardItem) which this item should be aligned to. This allows for
+ // bottom-alignment along the bottom edge of the thumbnail.
+ public int get_alignment_point() {
+ return FRAME_WIDTH + BORDER_WIDTH + pixbuf_dim.height;
+ }
+
+ public virtual void exposed() {
+ exposure = true;
+ }
+
+ public virtual void unexposed() {
+ exposure = false;
+
+ if (title != null)
+ title.clear_pango_layout();
+
+ if (comment != null)
+ comment.clear_pango_layout();
+
+ if (subtitle != null)
+ subtitle.clear_pango_layout();
+ }
+
+ public virtual bool is_exposed() {
+ return exposure;
+ }
+
+ public bool has_image() {
+ return pixbuf != null;
+ }
+
+ public Gdk.Pixbuf? get_image() {
+ return pixbuf;
+ }
+
+ public void set_image(Gdk.Pixbuf pixbuf) {
+ this.pixbuf = pixbuf;
+ display_pixbuf = pixbuf;
+ pixbuf_dim = Dimensions.for_pixbuf(pixbuf);
+
+ recalc_size("set_image");
+ notify_view_altered();
+ }
+
+ public void clear_image(Dimensions dim) {
+ bool had_image = pixbuf != null;
+
+ pixbuf = null;
+ display_pixbuf = null;
+ pixbuf_dim = dim;
+
+ recalc_size("clear_image");
+
+ if (had_image)
+ notify_view_altered();
+ }
+
+ public static int get_max_width(int scale) {
+ // width is frame width (two sides) + frame padding (two sides) + width of pixbuf (text
+ // never wider)
+ return (FRAME_WIDTH * 2) + scale;
+ }
+
+ private void recalc_size(string reason) {
+ Dimensions old_requisition = requisition;
+
+ // only add in the text heights if they're displayed
+ int title_height = (title != null && title_visible)
+ ? title.get_height() + LABEL_PADDING : 0;
+ int comment_height = (comment != null && comment_visible)
+ ? comment.get_height() + LABEL_PADDING : 0;
+ int subtitle_height = (subtitle != null && subtitle_visible)
+ ? subtitle.get_height() + LABEL_PADDING : 0;
+
+ // width is frame width (two sides) + frame padding (two sides) + width of pixbuf
+ // (text never wider)
+ requisition.width = (FRAME_WIDTH * 2) + (BORDER_WIDTH * 2) + pixbuf_dim.width;
+
+ // height is frame width (two sides) + frame padding (two sides) + height of pixbuf
+ // + height of text + label padding (between pixbuf and text)
+ requisition.height = (FRAME_WIDTH * 2) + (BORDER_WIDTH * 2)
+ + pixbuf_dim.height + title_height + comment_height + subtitle_height;
+
+#if TRACE_REFLOW_ITEMS
+ debug("recalc_size %s: %s title_height=%d comment_height=%d subtitle_height=%d requisition=%s",
+ get_source().get_name(), reason, title_height, comment_height, subtitle_height,
+ requisition.to_string());
+#endif
+
+ if (!requisition.approx_equals(old_requisition)) {
+#if TRACE_REFLOW_ITEMS
+ debug("recalc_size %s: %s notifying geometry altered", get_source().get_name(), reason);
+#endif
+ notify_geometry_altered();
+ }
+ }
+
+ protected static Dimensions get_border_dimensions(Dimensions object_dim, int border_width) {
+ Dimensions dimensions = Dimensions();
+ dimensions.width = object_dim.width + (border_width * 2);
+ dimensions.height = object_dim.height + (border_width * 2);
+ return dimensions;
+ }
+
+ protected static Gdk.Point get_border_origin(Gdk.Point object_origin, int border_width) {
+ Gdk.Point origin = Gdk.Point();
+ origin.x = object_origin.x - border_width;
+ origin.y = object_origin.y - border_width;
+ return origin;
+ }
+
+ protected virtual void paint_shadow(Cairo.Context ctx, Dimensions dimensions, Gdk.Point origin,
+ int radius, float initial_alpha) {
+ double rgb_all = 0.0;
+
+ // top right corner
+ paint_shadow_in_corner(ctx, origin.x + dimensions.width, origin.y + radius, rgb_all, radius,
+ initial_alpha, -0.5 * Math.PI, 0);
+ // bottom right corner
+ paint_shadow_in_corner(ctx, origin.x + dimensions.width, origin.y + dimensions.height, rgb_all,
+ radius, initial_alpha, 0, 0.5 * Math.PI);
+ // bottom left corner
+ paint_shadow_in_corner(ctx, origin.x + radius, origin.y + dimensions.height, rgb_all, radius,
+ initial_alpha, 0.5 * Math.PI, Math.PI);
+
+ // left right
+ Cairo.Pattern lr = new Cairo.Pattern.linear(0, origin.y + dimensions.height,
+ 0, origin.y + dimensions.height + radius);
+ lr.add_color_stop_rgba(0.0, rgb_all, rgb_all, rgb_all, initial_alpha);
+ lr.add_color_stop_rgba(1.0, rgb_all, rgb_all, rgb_all, 0.0);
+ ctx.set_source(lr);
+ ctx.rectangle(origin.x + radius, origin.y + dimensions.height, dimensions.width - radius, radius);
+ ctx.fill();
+
+ // top down
+ Cairo.Pattern td = new Cairo.Pattern.linear(origin.x + dimensions.width,
+ 0, origin.x + dimensions.width + radius, 0);
+ td.add_color_stop_rgba(0.0, rgb_all, rgb_all, rgb_all, initial_alpha);
+ td.add_color_stop_rgba(1.0, rgb_all, rgb_all, rgb_all, 0.0);
+ ctx.set_source(td);
+ ctx.rectangle(origin.x + dimensions.width, origin.y + radius,
+ radius, dimensions.height - radius);
+ ctx.fill();
+ }
+
+ protected void paint_shadow_in_corner(Cairo.Context ctx, int x, int y,
+ double rgb_all, float radius, float initial_alpha, double arc1, double arc2) {
+ Cairo.Pattern p = new Cairo.Pattern.radial(x, y, 0, x, y, radius);
+ p.add_color_stop_rgba(0.0, rgb_all, rgb_all, rgb_all, initial_alpha);
+ p.add_color_stop_rgba(1.0, rgb_all, rgb_all, rgb_all, 0);
+ ctx.set_source(p);
+ ctx.move_to(x, y);
+ ctx.arc(x, y, radius, arc1, arc2);
+ ctx.close_path();
+ ctx.fill();
+ }
+
+ protected virtual void paint_border(Cairo.Context ctx, Dimensions object_dimensions,
+ Gdk.Point object_origin, int border_width) {
+ if (border_width == 1) {
+ ctx.rectangle(object_origin.x - border_width, object_origin.y - border_width,
+ object_dimensions.width + (border_width * 2),
+ object_dimensions.height + (border_width * 2));
+ ctx.fill();
+ } else {
+ Dimensions dimensions = get_border_dimensions(object_dimensions, border_width);
+ Gdk.Point origin = get_border_origin(object_origin, border_width);
+
+ // amount of rounding needed on corners varies by size of object
+ double scale = int.max(object_dimensions.width, object_dimensions.height);
+ draw_rounded_corners_filled(ctx, dimensions, origin, 0.25 * scale);
+ }
+ }
+
+ protected virtual void paint_image(Cairo.Context ctx, Gdk.Pixbuf pixbuf, Gdk.Point origin) {
+ paint_pixmap_with_background(ctx, pixbuf, origin.x, origin.y);
+ }
+
+ private int get_selection_border_width(int scale) {
+ return ((scale <= ((Thumbnail.MIN_SCALE + Thumbnail.MAX_SCALE) / 3)) ? 5 : 4)
+ + BORDER_WIDTH;
+ }
+
+ protected virtual Gdk.Pixbuf? get_top_left_trinket(int scale) {
+ return null;
+ }
+
+ protected virtual Gdk.Pixbuf? get_top_right_trinket(int scale) {
+ return null;
+ }
+
+ protected virtual Gdk.Pixbuf? get_bottom_left_trinket(int scale) {
+ return null;
+ }
+
+ protected virtual Gdk.Pixbuf? get_bottom_right_trinket(int scale) {
+ return null;
+ }
+
+ public void paint(Gtk.StyleContext style_context, Cairo.Context ctx, Gdk.RGBA bg_color, Gdk.RGBA
selected_color,
+ Gdk.RGBA? border_color, Gdk.RGBA? focus_color) {
+ ctx.save();
+ ctx.translate(allocation.x + FRAME_WIDTH,
+ allocation.y + FRAME_WIDTH);
+ // calc the top-left point of the pixbuf
+ Gdk.Point pixbuf_origin = Gdk.Point();
+ pixbuf_origin.x = BORDER_WIDTH;
+ pixbuf_origin.y = BORDER_WIDTH;
+
+ ctx.set_line_width(FRAME_WIDTH);
+ ctx.set_source_rgba(selected_color.red, selected_color.green, selected_color.blue,
+ selected_color.alpha);
+
+ // draw shadow
+ if (border_color != null) {
+ ctx.save();
+ Dimensions shadow_dim = Dimensions();
+ shadow_dim.width = pixbuf_dim.width + BORDER_WIDTH;
+ shadow_dim.height = pixbuf_dim.height + BORDER_WIDTH;
+ paint_shadow(ctx, shadow_dim, pixbuf_origin, SHADOW_RADIUS, SHADOW_INITIAL_ALPHA);
+ ctx.restore();
+ }
+
+ // draw a border for the cursor with the selection width and normal border color
+ if (is_cursor) {
+ ctx.save();
+ ctx.set_source_rgba(focus_color.red, focus_color.green, focus_color.blue,
+ focus_color.alpha);
+ paint_border(ctx, pixbuf_dim, pixbuf_origin,
+ get_selection_border_width(int.max(pixbuf_dim.width, pixbuf_dim.height)));
+ ctx.restore();
+ }
+
+ // draw selection border
+ if (is_selected()) {
+ // border thickness depends on the size of the thumbnail
+ ctx.save();
+ paint_border(ctx, pixbuf_dim, pixbuf_origin,
+ get_selection_border_width(int.max(pixbuf_dim.width, pixbuf_dim.height)));
+ ctx.restore();
+ }
+
+ if (display_pixbuf != null) {
+ ctx.save();
+ ctx.set_source_rgba(bg_color.red, bg_color.green, bg_color.blue, bg_color.alpha);
+ paint_image(ctx, display_pixbuf, pixbuf_origin);
+ ctx.restore();
+ }
+
+ // title and subtitles are LABEL_PADDING below bottom of pixbuf
+ int text_y = pixbuf_dim.height + FRAME_WIDTH + LABEL_PADDING;
+ if (title != null && title_visible) {
+ // get the layout sized so its width is no more than the pixbuf's
+ // resize the text width to be no more than the pixbuf's
+ title.allocation.x = 0;
+ title.allocation.y = text_y;
+ title.allocation.width = pixbuf_dim.width;
+ title.allocation.height = title.get_height();
+ style_context.render_layout(ctx, title.allocation.x, title.allocation.y,
+ title.get_pango_layout(pixbuf_dim.width));
+
+ text_y += title.get_height() + LABEL_PADDING;
+ }
+
+ if (comment != null && comment_visible) {
+ comment.allocation.x = 0;
+ comment.allocation.y = text_y;
+ comment.allocation.width = pixbuf_dim.width;
+ comment.allocation.height = comment.get_height();
+ style_context.render_layout(ctx, comment.allocation.x, comment.allocation.y,
+ comment.get_pango_layout(pixbuf_dim.width));
+
+ text_y += comment.get_height() + LABEL_PADDING;
+ }
+
+ if (subtitle != null && subtitle_visible) {
+ subtitle.allocation.x = 0;
+ subtitle.allocation.y = text_y;
+ subtitle.allocation.width = pixbuf_dim.width;
+ subtitle.allocation.height = subtitle.get_height();
+
+ style_context.render_layout(ctx, subtitle.allocation.x, subtitle.allocation.y,
+ subtitle.get_pango_layout(pixbuf_dim.width));
+
+ // increment text_y if more text lines follow
+ }
+
+ ctx.set_source_rgba(selected_color.red, selected_color.green, selected_color.blue,
+ selected_color.alpha);
+
+ // draw trinkets last
+ Gdk.Pixbuf? trinket = get_bottom_left_trinket(TRINKET_SCALE);
+ if (trinket != null) {
+ int x = pixbuf_origin.x + TRINKET_PADDING + get_horizontal_trinket_offset();
+ int y = pixbuf_origin.y + pixbuf_dim.height - trinket.get_height() -
+ TRINKET_PADDING;
+ Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
+ ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
+ ctx.fill();
+ }
+
+ trinket = get_top_left_trinket(TRINKET_SCALE);
+ if (trinket != null) {
+ int x = pixbuf_origin.x + TRINKET_PADDING + get_horizontal_trinket_offset();
+ int y = pixbuf_origin.y + TRINKET_PADDING;
+ Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
+ ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
+ ctx.fill();
+ }
+
+ trinket = get_top_right_trinket(TRINKET_SCALE);
+ if (trinket != null) {
+ int x = pixbuf_origin.x + pixbuf_dim.width - trinket.width -
+ get_horizontal_trinket_offset() - TRINKET_PADDING;
+ int y = pixbuf_origin.y + TRINKET_PADDING;
+ Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
+ ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
+ ctx.fill();
+ }
+
+ trinket = get_bottom_right_trinket(TRINKET_SCALE);
+ if (trinket != null) {
+ int x = pixbuf_origin.x + pixbuf_dim.width - trinket.width -
+ get_horizontal_trinket_offset() - TRINKET_PADDING;
+ int y = pixbuf_origin.y + pixbuf_dim.height - trinket.height -
+ TRINKET_PADDING;
+ Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
+ ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
+ ctx.fill();
+ }
+ ctx.restore();
+ }
+
+ protected void set_horizontal_trinket_offset(int horizontal_trinket_offset) {
+ assert(horizontal_trinket_offset >= 0);
+ this.horizontal_trinket_offset = horizontal_trinket_offset;
+ }
+
+ protected int get_horizontal_trinket_offset() {
+ return horizontal_trinket_offset;
+ }
+
+ public void set_grid_coordinates(int col, int row) {
+ this.col = col;
+ this.row = row;
+ }
+
+ public int get_column() {
+ return col;
+ }
+
+ public int get_row() {
+ return row;
+ }
+
+ public void brighten() {
+ // "should" implies "can" and "didn't already"
+ if (brightened != null || pixbuf == null)
+ return;
+
+ // create a new lightened pixbuf to display
+ brightened = pixbuf.copy();
+ shift_colors(brightened, BRIGHTEN_SHIFT, BRIGHTEN_SHIFT, BRIGHTEN_SHIFT, 0);
+
+ display_pixbuf = brightened;
+
+ notify_view_altered();
+ }
+
+
+ public void unbrighten() {
+ // "should", "can", "didn't already"
+ if (brightened == null || pixbuf == null)
+ return;
+
+ brightened = null;
+
+ // return to the normal image
+ display_pixbuf = pixbuf;
+
+ notify_view_altered();
+ }
+
+ public override void visibility_changed(bool visible) {
+ // if going from visible to hidden, unbrighten
+ if (!visible)
+ unbrighten();
+
+ base.visibility_changed(visible);
+ }
+
+ private bool query_tooltip_on_text(CheckerboardItemText text, Gtk.Tooltip tooltip) {
+ if (!text.get_pango_layout().is_ellipsized())
+ return false;
+
+ if (text.is_marked_up())
+ tooltip.set_markup(text.get_text());
+ else
+ tooltip.set_text(text.get_text());
+
+ return true;
+ }
+
+ public bool query_tooltip(int x, int y, Gtk.Tooltip tooltip) {
+ if (title != null && title_visible && coord_in_rectangle(x, y, title.allocation))
+ return query_tooltip_on_text(title, tooltip);
+
+ if (comment != null && comment_visible && coord_in_rectangle(x, y, comment.allocation))
+ return query_tooltip_on_text(comment, tooltip);
+
+ if (subtitle != null && subtitle_visible && coord_in_rectangle(x, y, subtitle.allocation))
+ return query_tooltip_on_text(subtitle, tooltip);
+
+ return false;
+ }
+}
+
+
diff --git a/src/CheckerboardItemText.vala b/src/CheckerboardItemText.vala
new file mode 100644
index 00000000..89249385
--- /dev/null
+++ b/src/CheckerboardItemText.vala
@@ -0,0 +1,98 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ *
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ */
+
+private class CheckerboardItemText {
+ private static int one_line_height = 0;
+
+ private string text;
+ private bool marked_up;
+ private Pango.Alignment alignment;
+ private Pango.Layout layout = null;
+ private bool single_line = true;
+ private int height = 0;
+
+ public Gdk.Rectangle allocation = Gdk.Rectangle();
+
+ public CheckerboardItemText(string text, Pango.Alignment alignment = Pango.Alignment.LEFT,
+ bool marked_up = false) {
+ this.text = text;
+ this.marked_up = marked_up;
+ this.alignment = alignment;
+
+ single_line = is_single_line();
+ }
+
+ private bool is_single_line() {
+ return !String.contains_char(text, '\n');
+ }
+
+ public bool is_marked_up() {
+ return marked_up;
+ }
+
+ public bool is_set_to(string text, bool marked_up, Pango.Alignment alignment) {
+ return (this.marked_up == marked_up && this.alignment == alignment && this.text == text);
+ }
+
+ public string get_text() {
+ return text;
+ }
+
+ public int get_height() {
+ if (height == 0)
+ update_height();
+
+ return height;
+ }
+
+ public Pango.Layout get_pango_layout(int max_width = 0) {
+ if (layout == null)
+ create_pango();
+
+ if (max_width > 0)
+ layout.set_width(max_width * Pango.SCALE);
+
+ return layout;
+ }
+
+ public void clear_pango_layout() {
+ layout = null;
+ }
+
+ private void update_height() {
+ if (one_line_height != 0 && single_line)
+ height = one_line_height;
+ else
+ create_pango();
+ }
+
+ private void create_pango() {
+ // create layout for this string and ellipsize so it never extends past its laid-down width
+ layout = AppWindow.get_instance().create_pango_layout(null);
+ if (!marked_up)
+ layout.set_text(text, -1);
+ else
+ layout.set_markup(text, -1);
+
+ layout.set_ellipsize(Pango.EllipsizeMode.END);
+ layout.set_alignment(alignment);
+
+ // getting pixel size is expensive, and we only need the height, so use cached values
+ // whenever possible
+ if (one_line_height != 0 && single_line) {
+ height = one_line_height;
+ } else {
+ int width;
+ layout.get_pixel_size(out width, out height);
+
+ // cache first one-line height discovered
+ if (one_line_height == 0 && single_line)
+ one_line_height = height;
+ }
+ }
+}
+
+
diff --git a/src/CheckerboardLayout.vala b/src/CheckerboardLayout.vala
index bf6c9151..20f0d14b 100644
--- a/src/CheckerboardLayout.vala
+++ b/src/CheckerboardLayout.vala
@@ -4,824 +4,6 @@
* See the COPYING file in this distribution.
*/
-private class CheckerboardItemText {
- private static int one_line_height = 0;
-
- private string text;
- private bool marked_up;
- private Pango.Alignment alignment;
- private Pango.Layout layout = null;
- private bool single_line = true;
- private int height = 0;
-
- public Gdk.Rectangle allocation = Gdk.Rectangle();
-
- public CheckerboardItemText(string text, Pango.Alignment alignment = Pango.Alignment.LEFT,
- bool marked_up = false) {
- this.text = text;
- this.marked_up = marked_up;
- this.alignment = alignment;
-
- single_line = is_single_line();
- }
-
- private bool is_single_line() {
- return !String.contains_char(text, '\n');
- }
-
- public bool is_marked_up() {
- return marked_up;
- }
-
- public bool is_set_to(string text, bool marked_up, Pango.Alignment alignment) {
- return (this.marked_up == marked_up && this.alignment == alignment && this.text == text);
- }
-
- public string get_text() {
- return text;
- }
-
- public int get_height() {
- if (height == 0)
- update_height();
-
- return height;
- }
-
- public Pango.Layout get_pango_layout(int max_width = 0) {
- if (layout == null)
- create_pango();
-
- if (max_width > 0)
- layout.set_width(max_width * Pango.SCALE);
-
- return layout;
- }
-
- public void clear_pango_layout() {
- layout = null;
- }
-
- private void update_height() {
- if (one_line_height != 0 && single_line)
- height = one_line_height;
- else
- create_pango();
- }
-
- private void create_pango() {
- // create layout for this string and ellipsize so it never extends past its laid-down width
- layout = AppWindow.get_instance().create_pango_layout(null);
- if (!marked_up)
- layout.set_text(text, -1);
- else
- layout.set_markup(text, -1);
-
- layout.set_ellipsize(Pango.EllipsizeMode.END);
- layout.set_alignment(alignment);
-
- // getting pixel size is expensive, and we only need the height, so use cached values
- // whenever possible
- if (one_line_height != 0 && single_line) {
- height = one_line_height;
- } else {
- int width;
- layout.get_pixel_size(out width, out height);
-
- // cache first one-line height discovered
- if (one_line_height == 0 && single_line)
- one_line_height = height;
- }
- }
-}
-
-public abstract class CheckerboardItem : ThumbnailView {
- // Collection properties CheckerboardItem understands
- // SHOW_TITLES (bool)
- public const string PROP_SHOW_TITLES = "show-titles";
- // SHOW_COMMENTS (bool)
- public const string PROP_SHOW_COMMENTS = "show-comments";
- // SHOW_SUBTITLES (bool)
- public const string PROP_SHOW_SUBTITLES = "show-subtitles";
-
- public const int FRAME_WIDTH = 8;
- public const int LABEL_PADDING = 4;
- public const int BORDER_WIDTH = 1;
-
- public const int SHADOW_RADIUS = 4;
- public const float SHADOW_INITIAL_ALPHA = 0.5f;
-
- public const int TRINKET_SCALE = 12;
- public const int TRINKET_PADDING = 1;
-
- public const int BRIGHTEN_SHIFT = 0x18;
-
- public Dimensions requisition = Dimensions();
- public Gdk.Rectangle allocation = Gdk.Rectangle();
-
- private bool exposure = false;
- private CheckerboardItemText? title = null;
- private bool title_visible = true;
- private CheckerboardItemText? comment = null;
- private bool comment_visible = true;
- private CheckerboardItemText? subtitle = null;
- private bool subtitle_visible = false;
- private bool is_cursor = false;
- private Pango.Alignment tag_alignment = Pango.Alignment.LEFT;
- private Gee.List<Tag>? user_visible_tag_list = null;
- private Gee.Collection<Tag> tags;
- private Gdk.Pixbuf pixbuf = null;
- private Gdk.Pixbuf display_pixbuf = null;
- private Gdk.Pixbuf brightened = null;
- private Dimensions pixbuf_dim = Dimensions();
- private int col = -1;
- private int row = -1;
- private int horizontal_trinket_offset = 0;
-
- public CheckerboardItem(ThumbnailSource source, Dimensions initial_pixbuf_dim, string title, string?
comment,
- bool marked_up = false, Pango.Alignment alignment = Pango.Alignment.LEFT) {
- base(source);
-
- pixbuf_dim = initial_pixbuf_dim;
- this.title = new CheckerboardItemText(title, alignment, marked_up);
- // on the checkboard page we display the comment in
- // one line, i.e., replacing all newlines with spaces.
- // that means that the display will contain "..." if the comment
- // is too long.
- // warning: changes here have to be done in set_comment, too!
- if (comment != null)
- this.comment = new CheckerboardItemText(comment.replace("\n", " "), alignment,
- marked_up);
-
- // Don't calculate size here, wait for the item to be assigned to a ViewCollection
- // (notify_membership_changed) and calculate when the collection's property settings
- // are known
- }
-
- public bool has_tags { get; private set; }
-
- public override string get_name() {
- return (title != null) ? title.get_text() : base.get_name();
- }
-
- public string get_title() {
- return (title != null) ? title.get_text() : "";
- }
-
- public string get_comment() {
- return (comment != null) ? comment.get_text() : "";
- }
-
- public void set_title(string text, bool marked_up = false,
- Pango.Alignment alignment = Pango.Alignment.LEFT) {
- if (title != null && title.is_set_to(text, marked_up, alignment))
- return;
-
- title = new CheckerboardItemText(text, alignment, marked_up);
-
- if (title_visible) {
- recalc_size("set_title");
- notify_view_altered();
- }
- }
-
- public void translate_coordinates(ref int x, ref int y) {
- x -= allocation.x + FRAME_WIDTH;
- y -= allocation.y + FRAME_WIDTH;
- }
-
- public void clear_title() {
- if (title == null)
- return;
-
- title = null;
-
- if (title_visible) {
- recalc_size("clear_title");
- notify_view_altered();
- }
- }
-
- private void set_title_visible(bool visible) {
- if (title_visible == visible)
- return;
-
- title_visible = visible;
-
- recalc_size("set_title_visible");
- notify_view_altered();
- }
-
- public void set_comment(string text, bool marked_up = false,
- Pango.Alignment alignment = Pango.Alignment.LEFT) {
- if (comment != null && comment.is_set_to(text, marked_up, alignment))
- return;
-
- comment = new CheckerboardItemText(text.replace("\n", " "), alignment, marked_up);
-
- if (comment_visible) {
- recalc_size("set_comment");
- notify_view_altered();
- }
- }
-
- public void clear_comment() {
- if (comment == null)
- return;
-
- comment = null;
-
- if (comment_visible) {
- recalc_size("clear_comment");
- notify_view_altered();
- }
- }
-
- private void set_comment_visible(bool visible) {
- if (comment_visible == visible)
- return;
-
- comment_visible = visible;
-
- recalc_size("set_comment_visible");
- notify_view_altered();
- }
-
- public void set_tags(Gee.Collection<Tag>? tags,
- Pango.Alignment alignment = Pango.Alignment.LEFT) {
- has_tags = (tags != null && tags.size > 0);
- tag_alignment = alignment;
- string text;
- if (has_tags) {
- this.tags = tags;
- user_visible_tag_list = Tag.make_user_visible_tag_list(tags);
- text = Tag.make_tag_markup_string(user_visible_tag_list);
- } else {
- text = "<small>.</small>";
- }
-
- if (subtitle != null && subtitle.is_set_to(text, true, alignment))
- return;
- subtitle = new CheckerboardItemText(text, alignment, true);
-
- if (subtitle_visible) {
- recalc_size("set_subtitle");
- notify_view_altered();
- }
- }
-
- public void clear_tags() {
- clear_subtitle();
- has_tags = false;
- user_visible_tag_list = null;
- }
-
- public void highlight_user_visible_tag(int index)
- requires (user_visible_tag_list != null) {
- string text = Tag.make_tag_markup_string(user_visible_tag_list, index);
- subtitle = new CheckerboardItemText(text, tag_alignment, true);
-
- if (subtitle_visible)
- notify_view_altered();
- }
-
- public Tag get_user_visible_tag(int index)
- requires (index >= 0 && index < user_visible_tag_list.size) {
- return user_visible_tag_list.get(index);
- }
-
- public Pango.Layout? get_tag_list_layout() {
- return has_tags ? subtitle.get_pango_layout() : null;
- }
-
- public Gdk.Rectangle get_subtitle_allocation() {
- return subtitle.allocation;
- }
-
- public string get_subtitle() {
- return (subtitle != null) ? subtitle.get_text() : "";
- }
-
- public void set_subtitle(string text, bool marked_up = false,
- Pango.Alignment alignment = Pango.Alignment.LEFT) {
- if (subtitle != null && subtitle.is_set_to(text, marked_up, alignment))
- return;
-
- subtitle = new CheckerboardItemText(text, alignment, marked_up);
-
- if (subtitle_visible) {
- recalc_size("set_subtitle");
- notify_view_altered();
- }
- }
-
- public void clear_subtitle() {
- if (subtitle == null)
- return;
-
- subtitle = null;
-
- if (subtitle_visible) {
- recalc_size("clear_subtitle");
- notify_view_altered();
- }
- }
-
- private void set_subtitle_visible(bool visible) {
- if (subtitle_visible == visible)
- return;
-
- subtitle_visible = visible;
-
- recalc_size("set_subtitle_visible");
- notify_view_altered();
- }
-
- public void set_is_cursor(bool is_cursor) {
- this.is_cursor = is_cursor;
- }
-
- public bool get_is_cursor() {
- return is_cursor;
- }
-
- public virtual void handle_mouse_motion(int x, int y, int height, int width) {
-
- }
-
- public virtual void handle_mouse_leave() {
- unbrighten();
- }
-
- public virtual void handle_mouse_enter() {
- brighten();
- }
-
- protected override void notify_membership_changed(DataCollection? collection) {
- bool title_visible = (bool) get_collection_property(PROP_SHOW_TITLES, true);
- bool comment_visible = (bool) get_collection_property(PROP_SHOW_COMMENTS, true);
- bool subtitle_visible = (bool) get_collection_property(PROP_SHOW_SUBTITLES, false);
-
- bool altered = false;
- if (this.title_visible != title_visible) {
- this.title_visible = title_visible;
- altered = true;
- }
-
- if (this.comment_visible != comment_visible) {
- this.comment_visible = comment_visible;
- altered = true;
- }
-
- if (this.subtitle_visible != subtitle_visible) {
- this.subtitle_visible = subtitle_visible;
- altered = true;
- }
-
- if (altered || !requisition.has_area()) {
- recalc_size("notify_membership_changed");
- notify_view_altered();
- }
-
- base.notify_membership_changed(collection);
- }
-
- protected override void notify_collection_property_set(string name, Value? old, Value val) {
- switch (name) {
- case PROP_SHOW_TITLES:
- set_title_visible((bool) val);
- break;
-
- case PROP_SHOW_COMMENTS:
- set_comment_visible((bool) val);
- break;
-
- case PROP_SHOW_SUBTITLES:
- set_subtitle_visible((bool) val);
- break;
- }
-
- base.notify_collection_property_set(name, old, val);
- }
-
- // The alignment point is the coordinate on the y-axis (relative to the top of the
- // CheckerboardItem) which this item should be aligned to. This allows for
- // bottom-alignment along the bottom edge of the thumbnail.
- public int get_alignment_point() {
- return FRAME_WIDTH + BORDER_WIDTH + pixbuf_dim.height;
- }
-
- public virtual void exposed() {
- exposure = true;
- }
-
- public virtual void unexposed() {
- exposure = false;
-
- if (title != null)
- title.clear_pango_layout();
-
- if (comment != null)
- comment.clear_pango_layout();
-
- if (subtitle != null)
- subtitle.clear_pango_layout();
- }
-
- public virtual bool is_exposed() {
- return exposure;
- }
-
- public bool has_image() {
- return pixbuf != null;
- }
-
- public Gdk.Pixbuf? get_image() {
- return pixbuf;
- }
-
- public void set_image(Gdk.Pixbuf pixbuf) {
- this.pixbuf = pixbuf;
- display_pixbuf = pixbuf;
- pixbuf_dim = Dimensions.for_pixbuf(pixbuf);
-
- recalc_size("set_image");
- notify_view_altered();
- }
-
- public void clear_image(Dimensions dim) {
- bool had_image = pixbuf != null;
-
- pixbuf = null;
- display_pixbuf = null;
- pixbuf_dim = dim;
-
- recalc_size("clear_image");
-
- if (had_image)
- notify_view_altered();
- }
-
- public static int get_max_width(int scale) {
- // width is frame width (two sides) + frame padding (two sides) + width of pixbuf (text
- // never wider)
- return (FRAME_WIDTH * 2) + scale;
- }
-
- private void recalc_size(string reason) {
- Dimensions old_requisition = requisition;
-
- // only add in the text heights if they're displayed
- int title_height = (title != null && title_visible)
- ? title.get_height() + LABEL_PADDING : 0;
- int comment_height = (comment != null && comment_visible)
- ? comment.get_height() + LABEL_PADDING : 0;
- int subtitle_height = (subtitle != null && subtitle_visible)
- ? subtitle.get_height() + LABEL_PADDING : 0;
-
- // width is frame width (two sides) + frame padding (two sides) + width of pixbuf
- // (text never wider)
- requisition.width = (FRAME_WIDTH * 2) + (BORDER_WIDTH * 2) + pixbuf_dim.width;
-
- // height is frame width (two sides) + frame padding (two sides) + height of pixbuf
- // + height of text + label padding (between pixbuf and text)
- requisition.height = (FRAME_WIDTH * 2) + (BORDER_WIDTH * 2)
- + pixbuf_dim.height + title_height + comment_height + subtitle_height;
-
-#if TRACE_REFLOW_ITEMS
- debug("recalc_size %s: %s title_height=%d comment_height=%d subtitle_height=%d requisition=%s",
- get_source().get_name(), reason, title_height, comment_height, subtitle_height,
- requisition.to_string());
-#endif
-
- if (!requisition.approx_equals(old_requisition)) {
-#if TRACE_REFLOW_ITEMS
- debug("recalc_size %s: %s notifying geometry altered", get_source().get_name(), reason);
-#endif
- notify_geometry_altered();
- }
- }
-
- protected static Dimensions get_border_dimensions(Dimensions object_dim, int border_width) {
- Dimensions dimensions = Dimensions();
- dimensions.width = object_dim.width + (border_width * 2);
- dimensions.height = object_dim.height + (border_width * 2);
- return dimensions;
- }
-
- protected static Gdk.Point get_border_origin(Gdk.Point object_origin, int border_width) {
- Gdk.Point origin = Gdk.Point();
- origin.x = object_origin.x - border_width;
- origin.y = object_origin.y - border_width;
- return origin;
- }
-
- protected virtual void paint_shadow(Cairo.Context ctx, Dimensions dimensions, Gdk.Point origin,
- int radius, float initial_alpha) {
- double rgb_all = 0.0;
-
- // top right corner
- paint_shadow_in_corner(ctx, origin.x + dimensions.width, origin.y + radius, rgb_all, radius,
- initial_alpha, -0.5 * Math.PI, 0);
- // bottom right corner
- paint_shadow_in_corner(ctx, origin.x + dimensions.width, origin.y + dimensions.height, rgb_all,
- radius, initial_alpha, 0, 0.5 * Math.PI);
- // bottom left corner
- paint_shadow_in_corner(ctx, origin.x + radius, origin.y + dimensions.height, rgb_all, radius,
- initial_alpha, 0.5 * Math.PI, Math.PI);
-
- // left right
- Cairo.Pattern lr = new Cairo.Pattern.linear(0, origin.y + dimensions.height,
- 0, origin.y + dimensions.height + radius);
- lr.add_color_stop_rgba(0.0, rgb_all, rgb_all, rgb_all, initial_alpha);
- lr.add_color_stop_rgba(1.0, rgb_all, rgb_all, rgb_all, 0.0);
- ctx.set_source(lr);
- ctx.rectangle(origin.x + radius, origin.y + dimensions.height, dimensions.width - radius, radius);
- ctx.fill();
-
- // top down
- Cairo.Pattern td = new Cairo.Pattern.linear(origin.x + dimensions.width,
- 0, origin.x + dimensions.width + radius, 0);
- td.add_color_stop_rgba(0.0, rgb_all, rgb_all, rgb_all, initial_alpha);
- td.add_color_stop_rgba(1.0, rgb_all, rgb_all, rgb_all, 0.0);
- ctx.set_source(td);
- ctx.rectangle(origin.x + dimensions.width, origin.y + radius,
- radius, dimensions.height - radius);
- ctx.fill();
- }
-
- protected void paint_shadow_in_corner(Cairo.Context ctx, int x, int y,
- double rgb_all, float radius, float initial_alpha, double arc1, double arc2) {
- Cairo.Pattern p = new Cairo.Pattern.radial(x, y, 0, x, y, radius);
- p.add_color_stop_rgba(0.0, rgb_all, rgb_all, rgb_all, initial_alpha);
- p.add_color_stop_rgba(1.0, rgb_all, rgb_all, rgb_all, 0);
- ctx.set_source(p);
- ctx.move_to(x, y);
- ctx.arc(x, y, radius, arc1, arc2);
- ctx.close_path();
- ctx.fill();
- }
-
- protected virtual void paint_border(Cairo.Context ctx, Dimensions object_dimensions,
- Gdk.Point object_origin, int border_width) {
- if (border_width == 1) {
- ctx.rectangle(object_origin.x - border_width, object_origin.y - border_width,
- object_dimensions.width + (border_width * 2),
- object_dimensions.height + (border_width * 2));
- ctx.fill();
- } else {
- Dimensions dimensions = get_border_dimensions(object_dimensions, border_width);
- Gdk.Point origin = get_border_origin(object_origin, border_width);
-
- // amount of rounding needed on corners varies by size of object
- double scale = int.max(object_dimensions.width, object_dimensions.height);
- draw_rounded_corners_filled(ctx, dimensions, origin, 0.25 * scale);
- }
- }
-
- protected virtual void paint_image(Cairo.Context ctx, Gdk.Pixbuf pixbuf, Gdk.Point origin) {
- paint_pixmap_with_background(ctx, pixbuf, origin.x, origin.y);
- }
-
- private int get_selection_border_width(int scale) {
- return ((scale <= ((Thumbnail.MIN_SCALE + Thumbnail.MAX_SCALE) / 3)) ? 5 : 4)
- + BORDER_WIDTH;
- }
-
- protected virtual Gdk.Pixbuf? get_top_left_trinket(int scale) {
- return null;
- }
-
- protected virtual Gdk.Pixbuf? get_top_right_trinket(int scale) {
- return null;
- }
-
- protected virtual Gdk.Pixbuf? get_bottom_left_trinket(int scale) {
- return null;
- }
-
- protected virtual Gdk.Pixbuf? get_bottom_right_trinket(int scale) {
- return null;
- }
-
- public void paint(Gtk.StyleContext style_context, Cairo.Context ctx, Gdk.RGBA bg_color, Gdk.RGBA
selected_color,
- Gdk.RGBA? border_color, Gdk.RGBA? focus_color) {
- ctx.save();
- ctx.translate(allocation.x + FRAME_WIDTH,
- allocation.y + FRAME_WIDTH);
- // calc the top-left point of the pixbuf
- Gdk.Point pixbuf_origin = Gdk.Point();
- pixbuf_origin.x = BORDER_WIDTH;
- pixbuf_origin.y = BORDER_WIDTH;
-
- ctx.set_line_width(FRAME_WIDTH);
- ctx.set_source_rgba(selected_color.red, selected_color.green, selected_color.blue,
- selected_color.alpha);
-
- // draw shadow
- if (border_color != null) {
- ctx.save();
- Dimensions shadow_dim = Dimensions();
- shadow_dim.width = pixbuf_dim.width + BORDER_WIDTH;
- shadow_dim.height = pixbuf_dim.height + BORDER_WIDTH;
- paint_shadow(ctx, shadow_dim, pixbuf_origin, SHADOW_RADIUS, SHADOW_INITIAL_ALPHA);
- ctx.restore();
- }
-
- // draw a border for the cursor with the selection width and normal border color
- if (is_cursor) {
- ctx.save();
- ctx.set_source_rgba(focus_color.red, focus_color.green, focus_color.blue,
- focus_color.alpha);
- paint_border(ctx, pixbuf_dim, pixbuf_origin,
- get_selection_border_width(int.max(pixbuf_dim.width, pixbuf_dim.height)));
- ctx.restore();
- }
-
- // draw selection border
- if (is_selected()) {
- // border thickness depends on the size of the thumbnail
- ctx.save();
- paint_border(ctx, pixbuf_dim, pixbuf_origin,
- get_selection_border_width(int.max(pixbuf_dim.width, pixbuf_dim.height)));
- ctx.restore();
- }
-
- if (display_pixbuf != null) {
- ctx.save();
- ctx.set_source_rgba(bg_color.red, bg_color.green, bg_color.blue, bg_color.alpha);
- paint_image(ctx, display_pixbuf, pixbuf_origin);
- ctx.restore();
- }
-
- // title and subtitles are LABEL_PADDING below bottom of pixbuf
- int text_y = pixbuf_dim.height + FRAME_WIDTH + LABEL_PADDING;
- if (title != null && title_visible) {
- // get the layout sized so its width is no more than the pixbuf's
- // resize the text width to be no more than the pixbuf's
- title.allocation.x = 0;
- title.allocation.y = text_y;
- title.allocation.width = pixbuf_dim.width;
- title.allocation.height = title.get_height();
- style_context.render_layout(ctx, title.allocation.x, title.allocation.y,
- title.get_pango_layout(pixbuf_dim.width));
-
- text_y += title.get_height() + LABEL_PADDING;
- }
-
- if (comment != null && comment_visible) {
- comment.allocation.x = 0;
- comment.allocation.y = text_y;
- comment.allocation.width = pixbuf_dim.width;
- comment.allocation.height = comment.get_height();
- style_context.render_layout(ctx, comment.allocation.x, comment.allocation.y,
- comment.get_pango_layout(pixbuf_dim.width));
-
- text_y += comment.get_height() + LABEL_PADDING;
- }
-
- if (subtitle != null && subtitle_visible) {
- subtitle.allocation.x = 0;
- subtitle.allocation.y = text_y;
- subtitle.allocation.width = pixbuf_dim.width;
- subtitle.allocation.height = subtitle.get_height();
-
- style_context.render_layout(ctx, subtitle.allocation.x, subtitle.allocation.y,
- subtitle.get_pango_layout(pixbuf_dim.width));
-
- // increment text_y if more text lines follow
- }
-
- ctx.set_source_rgba(selected_color.red, selected_color.green, selected_color.blue,
- selected_color.alpha);
-
- // draw trinkets last
- Gdk.Pixbuf? trinket = get_bottom_left_trinket(TRINKET_SCALE);
- if (trinket != null) {
- int x = pixbuf_origin.x + TRINKET_PADDING + get_horizontal_trinket_offset();
- int y = pixbuf_origin.y + pixbuf_dim.height - trinket.get_height() -
- TRINKET_PADDING;
- Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
- ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
- ctx.fill();
- }
-
- trinket = get_top_left_trinket(TRINKET_SCALE);
- if (trinket != null) {
- int x = pixbuf_origin.x + TRINKET_PADDING + get_horizontal_trinket_offset();
- int y = pixbuf_origin.y + TRINKET_PADDING;
- Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
- ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
- ctx.fill();
- }
-
- trinket = get_top_right_trinket(TRINKET_SCALE);
- if (trinket != null) {
- int x = pixbuf_origin.x + pixbuf_dim.width - trinket.width -
- get_horizontal_trinket_offset() - TRINKET_PADDING;
- int y = pixbuf_origin.y + TRINKET_PADDING;
- Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
- ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
- ctx.fill();
- }
-
- trinket = get_bottom_right_trinket(TRINKET_SCALE);
- if (trinket != null) {
- int x = pixbuf_origin.x + pixbuf_dim.width - trinket.width -
- get_horizontal_trinket_offset() - TRINKET_PADDING;
- int y = pixbuf_origin.y + pixbuf_dim.height - trinket.height -
- TRINKET_PADDING;
- Gdk.cairo_set_source_pixbuf(ctx, trinket, x, y);
- ctx.rectangle(x, y, trinket.get_width(), trinket.get_height());
- ctx.fill();
- }
- ctx.restore();
- }
-
- protected void set_horizontal_trinket_offset(int horizontal_trinket_offset) {
- assert(horizontal_trinket_offset >= 0);
- this.horizontal_trinket_offset = horizontal_trinket_offset;
- }
-
- protected int get_horizontal_trinket_offset() {
- return horizontal_trinket_offset;
- }
-
- public void set_grid_coordinates(int col, int row) {
- this.col = col;
- this.row = row;
- }
-
- public int get_column() {
- return col;
- }
-
- public int get_row() {
- return row;
- }
-
- public void brighten() {
- // "should" implies "can" and "didn't already"
- if (brightened != null || pixbuf == null)
- return;
-
- // create a new lightened pixbuf to display
- brightened = pixbuf.copy();
- shift_colors(brightened, BRIGHTEN_SHIFT, BRIGHTEN_SHIFT, BRIGHTEN_SHIFT, 0);
-
- display_pixbuf = brightened;
-
- notify_view_altered();
- }
-
-
- public void unbrighten() {
- // "should", "can", "didn't already"
- if (brightened == null || pixbuf == null)
- return;
-
- brightened = null;
-
- // return to the normal image
- display_pixbuf = pixbuf;
-
- notify_view_altered();
- }
-
- public override void visibility_changed(bool visible) {
- // if going from visible to hidden, unbrighten
- if (!visible)
- unbrighten();
-
- base.visibility_changed(visible);
- }
-
- private bool query_tooltip_on_text(CheckerboardItemText text, Gtk.Tooltip tooltip) {
- if (!text.get_pango_layout().is_ellipsized())
- return false;
-
- if (text.is_marked_up())
- tooltip.set_markup(text.get_text());
- else
- tooltip.set_text(text.get_text());
-
- return true;
- }
-
- public bool query_tooltip(int x, int y, Gtk.Tooltip tooltip) {
- if (title != null && title_visible && coord_in_rectangle(x, y, title.allocation))
- return query_tooltip_on_text(title, tooltip);
-
- if (comment != null && comment_visible && coord_in_rectangle(x, y, comment.allocation))
- return query_tooltip_on_text(comment, tooltip);
-
- if (subtitle != null && subtitle_visible && coord_in_rectangle(x, y, subtitle.allocation))
- return query_tooltip_on_text(subtitle, tooltip);
-
- return false;
- }
-}
-
public class CheckerboardLayout : Gtk.DrawingArea {
public const int TOP_PADDING = 16;
public const int BOTTOM_PADDING = 16;
diff --git a/src/meson.build b/src/meson.build
index 65197196..abea7b21 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -166,6 +166,8 @@ executable('shotwell',
'NaturalCollate.vala',
'Thumbnail.vala',
'ThumbnailCache.vala',
+ 'CheckerboardItem.vala',
+ 'CheckerboardItemText.vala',
'CheckerboardLayout.vala',
'PhotoPage.vala',
'Page.vala',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]