[gnome-photos/wip/rishi/zoom: 1/3] add zoom bar
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/zoom: 1/3] add zoom bar
- Date: Wed, 12 Apr 2017 07:01:16 +0000 (UTC)
commit 8e43a39c7ebf382217699f8e8659cc285a0aa99b
Author: Debarshi Ray <debarshir gnome org>
Date: Wed Jan 11 11:44:13 2017 +0100
add zoom bar
src/photos-zoom-bar.c | 1078 +++++++++++++++++++++++++++++++++++++++++++++++++
src/photos-zoom-bar.h | 39 ++
2 files changed, 1117 insertions(+), 0 deletions(-)
---
diff --git a/src/photos-zoom-bar.c b/src/photos-zoom-bar.c
new file mode 100644
index 0000000..913e8a2
--- /dev/null
+++ b/src/photos-zoom-bar.c
@@ -0,0 +1,1078 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/* Based on code from:
+ * + Documents
+ */
+
+
+#include "config.h"
+
+#include "photos-zoom-bar.h"
+
+
+struct _PhotosZoomBar
+{
+ GtkWidget *button_area;
+ GtkWidget *scale;
+ GtkWidget *page_label;
+ GtkWidget *preview_window;
+ GtkWidget *preview_image;
+ GtkWidget *preview_label;
+ gboolean scrubbing;
+ gboolean hover;
+};
+
+enum
+{
+ PROP_0,
+ PROP_HOVER
+};
+
+
+G_DEFINE_TYPE (PhotosZoomBar, photos_zoom_bar, GTK_TYPE_BOX);
+
+
+/* Thumbnails dimensions cache */
+#define PREVIEW_SIZE_CACHE_KEY "gd-preview-size-cache"
+#define PREVIEW_WIDTH 144
+#define PRELOAD_RANGE 5
+
+static void previews_update_range (PhotosZoomBar *self, int page);
+
+static void
+get_preview_size_for_page (EvDocument *document,
+ guint page,
+ gint *width,
+ gint *height)
+{
+ gdouble scale;
+ gdouble w, h;
+
+ ev_document_get_page_size (document, page, &w, &h);
+ scale = (gdouble)PREVIEW_WIDTH / w;
+
+ *width = MAX ((gint)(w * scale + 0.5), 1);
+ *height = MAX ((gint)(h * scale + 0.5), 1);
+}
+
+static GdPreviewSizeCache *
+gd_preview_size_cache_new (EvDocument *document)
+{
+ GdPreviewSizeCache *cache;
+ gint i, n_pages;
+ GtkRequisition *thumb_size;
+
+ cache = g_new0 (GdPreviewSizeCache, 1);
+
+ if (ev_document_is_page_size_uniform (document)) {
+ cache->uniform = TRUE;
+ get_preview_size_for_page (document, 0,
+ &cache->uniform_width,
+ &cache->uniform_height);
+ return cache;
+ }
+
+ n_pages = ev_document_get_n_pages (document);
+ cache->sizes = g_new0 (GtkRequisition, n_pages);
+
+ for (i = 0; i < n_pages; i++) {
+ thumb_size = &(cache->sizes[i]);
+ get_preview_size_for_page (document, i,
+ &thumb_size->width,
+ &thumb_size->height);
+ }
+
+ return cache;
+}
+
+static void
+gd_preview_size_cache_get_size (GdPreviewSizeCache *cache,
+ gint page,
+ gint rotation,
+ gint *width,
+ gint *height)
+{
+ gint w, h;
+
+ if (cache->uniform) {
+ w = cache->uniform_width;
+ h = cache->uniform_height;
+ } else {
+ GtkRequisition *thumb_size;
+
+ thumb_size = &(cache->sizes[page]);
+
+ w = thumb_size->width;
+ h = thumb_size->height;
+ }
+
+ if (rotation == 0 || rotation == 180) {
+ if (width) {
+ *width = w;
+ }
+ if (height) {
+ *height = h;
+ }
+ } else {
+ if (width) {
+ *width = h;
+ }
+ if (height) {
+ *height = w;
+ }
+ }
+}
+
+static void
+gd_preview_size_cache_free (GdPreviewSizeCache *cache)
+{
+ if (cache->sizes) {
+ g_free (cache->sizes);
+ cache->sizes = NULL;
+ }
+
+ g_free (cache);
+}
+
+static GdPreviewSizeCache *
+gd_preview_size_cache_get (EvDocument *document)
+{
+ GdPreviewSizeCache *cache;
+
+ cache = g_object_get_data (G_OBJECT (document), PREVIEW_SIZE_CACHE_KEY);
+ if (!cache) {
+ cache = gd_preview_size_cache_new (document);
+ g_object_set_data_full (G_OBJECT (document),
+ PREVIEW_SIZE_CACHE_KEY,
+ cache,
+ (GDestroyNotify)gd_preview_size_cache_free);
+ }
+
+ return cache;
+}
+
+static cairo_surface_t *
+preview_get_loading_icon (PhotosZoomBar *self,
+ int width,
+ int height)
+{
+ PhotosZoomBarPrivate *priv = self->priv;
+ cairo_surface_t *icon;
+ char *key;
+
+ key = g_strdup_printf ("%dx%d", width, height);
+ icon = g_hash_table_lookup (priv->loading_icons, key);
+ if (icon == NULL) {
+ gboolean inverted_colors;
+ gint device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+
+ inverted_colors = ev_document_model_get_inverted_colors (priv->model);
+ icon = ev_document_misc_render_loading_thumbnail_surface (GTK_WIDGET (self),
+ width * device_scale,
+ height * device_scale,
+ inverted_colors);
+ g_hash_table_insert (priv->loading_icons, key, icon);
+ } else {
+ g_free (key);
+ }
+
+ return cairo_surface_reference (icon);
+}
+
+static void
+update_page_label (PhotosZoomBar *self)
+{
+ char *text;
+
+ text = g_strdup_printf (_("Page %u of %u"), self->priv->current_page + 1, self->priv->n_pages);
+ gtk_label_set_text (GTK_LABEL (self->priv->page_label), text);
+ g_free (text);
+}
+
+static void
+update_scale (PhotosZoomBar *self)
+{
+ gtk_range_set_value (GTK_RANGE (self->priv->scale), self->priv->current_page);
+}
+
+static void
+update_page (PhotosZoomBar *self)
+{
+ self->priv->current_page = ev_document_model_get_page (self->priv->model);
+ update_page_label (self);
+ update_scale (self);
+ previews_update_range (self, self->priv->current_page);
+}
+
+static void
+thumbnail_job_completed_cb (EvJobThumbnail *job,
+ PhotosZoomBar *self)
+{
+ PhotosZoomBarPrivate *priv = self->priv;
+ cairo_surface_t *surface;
+ PreviewItem *item;
+
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+ gint device_scale;
+ device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ cairo_surface_set_device_scale (job->thumbnail_surface, device_scale, device_scale);
+#endif
+ surface = ev_document_misc_render_thumbnail_surface_with_frame (GTK_WIDGET (self),
+ job->thumbnail_surface,
+ -1, -1);
+
+ if (priv->inverted_colors) {
+ ev_document_misc_invert_surface (surface);
+ }
+
+ item = &self->priv->previews[job->page];
+ g_clear_pointer (&item->surface, (GDestroyNotify) cairo_surface_destroy);
+ item->surface = surface;
+ item->loaded = TRUE;
+ g_clear_object (&item->job);
+
+ /* check to see if preview needs updating */
+ if (self->priv->preview_page == job->page) {
+ gtk_image_set_from_surface (GTK_IMAGE (self->priv->preview_image), item->surface);
+ }
+}
+
+static void
+preview_item_clear_thumbnail_job (PhotosZoomBar *self,
+ PreviewItem *item)
+{
+ if (item->job != NULL) {
+ g_signal_handlers_disconnect_by_func (item->job, thumbnail_job_completed_cb, self);
+ ev_job_cancel (item->job);
+ }
+
+ g_clear_object (&item->job);
+}
+
+static void
+previews_clear_range (PhotosZoomBar *self,
+ int start_page,
+ int end_page)
+{
+ int i;
+
+ g_assert (start_page <= end_page);
+
+ for (i = start_page; i < end_page; i++) {
+ PreviewItem *item = &self->priv->previews[i];
+
+ if (item != NULL) {
+ preview_item_clear_thumbnail_job (self, item);
+ }
+ }
+}
+static void
+get_size_for_page (PhotosZoomBar *self,
+ int page,
+ gint *width_return,
+ gint *height_return)
+{
+ gdouble width, height;
+ gint preview_height;
+ gint device_scale;
+
+ device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ ev_document_get_page_size (self->priv->document, page, &width, &height);
+ preview_height = (int) (PREVIEW_WIDTH * height / width + 0.5);
+
+ if (self->priv->rotation == 90 || self->priv->rotation == 270) {
+ *width_return = preview_height * device_scale;
+ *height_return = PREVIEW_WIDTH * device_scale;
+ } else {
+ *width_return = PREVIEW_WIDTH * device_scale;
+ *height_return = preview_height * device_scale;
+ }
+}
+
+static void
+previews_load_range (PhotosZoomBar *self,
+ int start_page,
+ int end_page)
+{
+ int i;
+
+ g_assert (start_page <= end_page);
+
+ for (i = start_page; i < end_page; i++) {
+ PreviewItem *item = &self->priv->previews[i];
+
+ if (item != NULL && !item->loaded && item->job == NULL) {
+ gint preview_width, preview_height;
+ get_size_for_page (self, i, &preview_width, &preview_height);
+ item->job = ev_job_thumbnail_new_with_target_size (self->priv->document,
+ i, self->priv->rotation,
+ preview_width, preview_height);
+ ev_job_thumbnail_set_has_frame (EV_JOB_THUMBNAIL (item->job), FALSE);
+ ev_job_thumbnail_set_output_format (EV_JOB_THUMBNAIL (item->job),
EV_JOB_THUMBNAIL_SURFACE);
+ ev_job_scheduler_push_job (EV_JOB (item->job), EV_JOB_PRIORITY_HIGH);
+
+ g_signal_connect (item->job, "finished",
+ G_CALLBACK (thumbnail_job_completed_cb),
+ self);
+ }
+ }
+}
+
+static void
+previews_update_range (PhotosZoomBar *self,
+ int page)
+{
+ int old_start_page;
+ int old_end_page;
+
+ old_start_page = self->priv->page_start;
+ old_end_page = self->priv->page_end;
+
+ self->priv->page_start = MAX (page - PRELOAD_RANGE / 2, 0);
+ self->priv->page_end = MIN (page + PRELOAD_RANGE / 2, self->priv->n_pages);
+
+ if (self->priv->page_start == old_start_page &&
+ self->priv->page_end == old_end_page) {
+ return;
+ }
+
+ /* Clear the areas we no longer display */
+ if (old_start_page >= 0 && old_start_page < self->priv->page_start) {
+ previews_clear_range (self, old_start_page, MIN (self->priv->page_start - 1, old_end_page));
+ }
+
+ if (old_end_page > 0 && old_end_page > self->priv->page_end) {
+ previews_clear_range (self, MAX (self->priv->page_end + 1, old_start_page), old_end_page);
+ }
+
+ previews_load_range (self, self->priv->page_start, self->priv->page_end);
+}
+
+static void
+previews_create (PhotosZoomBar *self)
+{
+ int i;
+
+ self->priv->previews = g_new0 (PreviewItem, self->priv->n_pages);
+
+ for (i = 0; i < self->priv->n_pages; i++) {
+ PreviewItem *item = &self->priv->previews[i];
+ char *label;
+ int width;
+ int height;
+
+ label = ev_document_get_page_label (self->priv->document, i);
+
+ gd_preview_size_cache_get_size (self->priv->size_cache,
+ i,
+ self->priv->rotation,
+ &width, &height);
+ item->page = i;
+ item->label = g_markup_printf_escaped ("%s", label);
+ item->surface = preview_get_loading_icon (self, width, height);
+ item->loaded = FALSE;
+ item->job = NULL;
+
+ g_free (label);
+ }
+}
+
+static void
+previews_clear (PhotosZoomBar *self)
+{
+ int i;
+
+ if (self->priv->previews == NULL) {
+ return;
+ }
+
+ for (i = 0; i < self->priv->n_pages; i++) {
+ PreviewItem *item = &self->priv->previews[i];
+
+ preview_item_clear_thumbnail_job (self, item);
+
+ g_clear_pointer (&item->surface, (GDestroyNotify) cairo_surface_destroy);
+ g_free (item->label);
+ item->label = NULL;
+ }
+
+ g_free (self->priv->previews);
+ self->priv->previews = NULL;
+}
+
+static void
+previews_reload (PhotosZoomBar *self)
+{
+ if (self->priv->document == NULL ||
+ self->priv->n_pages <= 0) {
+ return;
+ }
+
+ previews_clear (self);
+ previews_create (self);
+}
+
+static void
+rotation_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ PhotosZoomBar *self)
+{
+ self->priv->rotation = ev_document_model_get_rotation (model);
+ previews_reload (self);
+}
+
+static void
+inverted_colors_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ PhotosZoomBar *self)
+{
+ self->priv->inverted_colors = ev_document_model_get_inverted_colors (model);
+ previews_reload (self);
+}
+
+static void
+photos_zoom_bar_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ PhotosZoomBar *self)
+{
+ PhotosZoomBarPrivate *priv = self->priv;
+ EvDocument *document;
+
+ document = ev_document_model_get_document (model);
+ if (document == self->priv->document) {
+ return;
+ }
+
+ previews_clear (self);
+ priv->n_pages = 0;
+ priv->page_start = -1;
+ priv->page_end = -1;
+
+ g_clear_object (&priv->document);
+ priv->document = document;
+ if (priv->document != NULL) {
+ g_object_ref (priv->document);
+
+ priv->size_cache = gd_preview_size_cache_get (document);
+ priv->n_pages = ev_document_get_n_pages (document);
+
+ previews_create (self);
+
+ gtk_widget_set_sensitive (priv->scale, (priv->n_pages > 1));
+ gtk_range_set_range (GTK_RANGE (priv->scale), 0.0, priv->n_pages - 1);
+
+ update_page (self);
+ }
+}
+
+static void
+page_changed_cb (EvDocumentModel *model,
+ gint old_page,
+ gint new_page,
+ PhotosZoomBar *self)
+{
+ if (self->priv->current_page != new_page) {
+ update_page (self);
+ }
+}
+
+static void
+photos_zoom_bar_set_document_model (PhotosZoomBar *self,
+ EvDocumentModel *model)
+{
+ PhotosZoomBarPrivate *priv = self->priv;
+
+ if (priv->model == model) {
+ return;
+ }
+
+ if (priv->model != NULL) {
+ g_signal_handlers_disconnect_by_data (priv->model, self);
+ g_object_unref (priv->model);
+ }
+
+ priv->model = model;
+
+ if (model != NULL) {
+ g_object_ref (model);
+ }
+
+ priv->rotation = ev_document_model_get_rotation (model);
+ priv->inverted_colors = ev_document_model_get_inverted_colors (model);
+
+ photos_zoom_bar_document_changed_cb (model, NULL, self);
+
+ g_signal_connect (priv->model,
+ "notify::document",
+ G_CALLBACK (photos_zoom_bar_document_changed_cb),
+ self);
+ g_signal_connect (priv->model,
+ "notify::rotation",
+ G_CALLBACK (rotation_changed_cb),
+ self);
+ g_signal_connect (priv->model,
+ "notify::inverted-colors",
+ G_CALLBACK (inverted_colors_changed_cb),
+ self);
+ g_signal_connect (priv->model,
+ "page-changed",
+ G_CALLBACK (page_changed_cb),
+ self);
+
+}
+
+static void
+photos_zoom_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (object);
+
+ switch (prop_id) {
+ case PROP_DOCUMENT_MODEL:
+ g_value_set_object (value, self->priv->model);
+ break;
+ case PROP_HOVER:
+ g_value_set_boolean (value, photos_zoom_bar_get_hover (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+photos_zoom_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (object);
+
+ switch (prop_id) {
+ case PROP_DOCUMENT_MODEL:
+ photos_zoom_bar_set_document_model (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+photos_zoom_bar_dispose (GObject *object)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (object);
+
+ if (self->priv->update_id != 0) {
+ g_source_remove (self->priv->update_id);
+ self->priv->update_id = 0;
+ }
+
+ if (self->priv->show_id != 0) {
+ g_source_remove (self->priv->show_id);
+ self->priv->show_id = 0;
+ }
+
+ g_clear_object (&self->priv->model);
+ g_clear_object (&self->priv->document);
+
+ if (self->priv->loading_icons != NULL) {
+ g_hash_table_destroy (self->priv->loading_icons);
+ self->priv->loading_icons = NULL;
+ }
+
+ previews_clear (self);
+
+ G_OBJECT_CLASS (photos_zoom_bar_parent_class)->dispose (object);
+}
+
+static void
+photos_zoom_bar_set_hover (PhotosZoomBar *self,
+ gboolean hover)
+{
+ if (self->priv->hover == hover) {
+ return;
+ }
+
+ self->priv->hover = hover;
+ g_object_notify (G_OBJECT (self), "hover");
+}
+
+static gboolean
+photos_zoom_bar_enter_notify (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (widget);
+ PhotosZoomBarPrivate *priv = self->priv;
+
+ if (event->detail != GDK_NOTIFY_INFERIOR) {
+ photos_zoom_bar_set_hover (self, TRUE);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+photos_zoom_bar_leave_notify (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (widget);
+ PhotosZoomBarPrivate *priv = self->priv;
+
+ if (event->detail != GDK_NOTIFY_INFERIOR) {
+ photos_zoom_bar_set_hover (self, FALSE);
+ }
+
+ return FALSE;
+}
+
+static void
+photos_zoom_bar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (widget);
+ PhotosZoomBarPrivate *priv = self->priv;
+
+ GTK_WIDGET_CLASS (photos_zoom_bar_parent_class)->size_allocate (widget, allocation);
+
+ if (gtk_widget_get_realized (widget)) {
+ gdk_window_move_resize (gtk_widget_get_window (widget),
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+ }
+}
+
+static void
+photos_zoom_bar_realize (GtkWidget *widget)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (widget);
+ PhotosZoomBarPrivate *priv = self->priv;
+ GtkAllocation allocation;
+ GdkWindow *window;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_widget_set_realized (widget, TRUE);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_TOUCH_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+
+ window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes, attributes_mask);
+ gtk_widget_set_window (widget, window);
+ gtk_widget_register_window (widget, window);
+
+ gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
+}
+
+static gboolean
+photos_zoom_bar_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (widget);
+
+ gtk_render_background (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
+
+ gtk_render_frame (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
+
+ return GTK_WIDGET_CLASS (photos_zoom_bar_parent_class)->draw (widget, cr);
+}
+
+static void
+create_preview_window (PhotosZoomBar *self)
+{
+ GtkStyleContext *context;
+ GtkWidget *box, *toplevel;
+ GdkScreen *screen;
+ GdkVisual *visual;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ if (!toplevel) {
+ return;
+ }
+
+ self->priv->preview_window = gtk_window_new (GTK_WINDOW_POPUP);
+ screen = gtk_widget_get_screen (self->priv->preview_window);
+ visual = gdk_screen_get_rgba_visual (screen);
+
+ if (visual != NULL) {
+ gtk_widget_set_visual (self->priv->preview_window, visual);
+ }
+
+ gtk_window_set_type_hint (GTK_WINDOW (self->priv->preview_window), GDK_WINDOW_TYPE_HINT_TOOLTIP);
+ gtk_window_set_resizable (GTK_WINDOW (self->priv->preview_window), FALSE);
+ gtk_window_set_transient_for (GTK_WINDOW (self->priv->preview_window),
+ GTK_WINDOW (toplevel));
+
+ context = gtk_widget_get_style_context (self->priv->preview_window);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOOLTIP);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_widget_set_margin_start (box, 6);
+ gtk_widget_set_margin_end (box, 6);
+ gtk_widget_set_margin_top (box, 6);
+ gtk_widget_set_margin_bottom (box, 6);
+ gtk_container_add (GTK_CONTAINER (self->priv->preview_window), box);
+ gtk_widget_show (box);
+
+ self->priv->preview_image = gtk_image_new ();
+ gtk_widget_set_size_request (self->priv->preview_image, PREVIEW_WIDTH, -1);
+ gtk_box_pack_start (GTK_BOX (box), self->priv->preview_image, FALSE, FALSE, 0);
+
+ self->priv->preview_label = gtk_label_new ("");
+ gtk_label_set_line_wrap (GTK_LABEL (self->priv->preview_label), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), self->priv->preview_label, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (box);
+}
+
+static void
+photos_zoom_bar_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *previous_toplevel)
+{
+ PhotosZoomBar *self = PHOTOS_ZOOM_BAR (widget);
+
+ /* Initialize preview window when in a toplevel */
+ if (previous_toplevel == NULL) {
+ create_preview_window (self);
+ }
+}
+
+static void
+photos_zoom_bar_class_init (PhotosZoomBarClass *class)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (class);
+ GtkWidgetClass *wclass = GTK_WIDGET_CLASS (class);
+
+ oclass->dispose = photos_zoom_bar_dispose;
+ oclass->get_property = photos_zoom_bar_get_property;
+ oclass->set_property = photos_zoom_bar_set_property;
+
+ wclass->draw = photos_zoom_bar_draw;
+ wclass->realize = photos_zoom_bar_realize;
+ wclass->enter_notify_event = photos_zoom_bar_enter_notify;
+ wclass->leave_notify_event = photos_zoom_bar_leave_notify;
+ wclass->size_allocate = photos_zoom_bar_size_allocate;
+ wclass->hierarchy_changed = photos_zoom_bar_hierarchy_changed;
+
+ g_object_class_install_property (oclass,
+ PROP_HOVER,
+ g_param_spec_boolean ("hover",
+ "Hover",
+ "Whether the widget is hovered",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (oclass, sizeof (PhotosZoomBarPrivate));
+ gtk_widget_class_set_css_name (wclass, "toolbar");
+}
+
+static void
+update_current_page (PhotosZoomBar *self)
+{
+ gdouble page;
+
+ page = round (gtk_range_get_value (GTK_RANGE (self->priv->scale)));
+ ev_document_model_set_page (self->priv->model, page);
+}
+
+static void
+hide_preview (PhotosZoomBar *self)
+{
+ if (self->priv->update_id != 0) {
+ g_source_remove (self->priv->update_id);
+ self->priv->update_id = 0;
+ }
+
+ if (self->priv->show_id != 0) {
+ g_source_remove (self->priv->show_id);
+ self->priv->show_id = 0;
+ }
+
+ gtk_widget_hide (self->priv->preview_window);
+}
+
+static void
+show_preview (PhotosZoomBar *self)
+{
+ GdkWindow *window;
+ int x, y;
+ int width, height;
+ int bx, by;
+
+ gtk_widget_realize (self->priv->preview_window);
+
+ width = gtk_widget_get_allocated_width (GTK_WIDGET (self->priv->preview_window));
+ height = gtk_widget_get_allocated_height (GTK_WIDGET (self->priv->preview_window));
+
+ window = gtk_widget_get_window (GTK_WIDGET (self));
+ gdk_window_get_origin (window, &bx, &by);
+
+ x = bx + (gdk_window_get_width (window) - width) / 2;
+ y = by - height - 10;
+
+ gtk_window_move (GTK_WINDOW (self->priv->preview_window), x, y);
+ gtk_window_present (GTK_WINDOW (self->priv->preview_window));
+}
+
+static gboolean
+update_jobs_timeout (PhotosZoomBar *self)
+{
+ PreviewItem *item;
+
+ previews_update_range (self, self->priv->preview_page);
+
+ item = &self->priv->previews[self->priv->preview_page];
+ if (item->job != NULL) {
+ ev_job_scheduler_update_job (item->job, EV_JOB_PRIORITY_URGENT);
+ }
+
+ self->priv->update_id = 0;
+ return FALSE;
+}
+
+static gboolean
+show_preview_timeout (PhotosZoomBar *self)
+{
+ show_preview (self);
+
+ self->priv->show_id = 0;
+ return FALSE;
+}
+
+static void
+update_preview (PhotosZoomBar *self)
+{
+ PreviewItem *item;
+
+ item = &self->priv->previews[self->priv->preview_page];
+
+ if (item->surface != NULL) {
+ gtk_image_set_from_surface (GTK_IMAGE (self->priv->preview_image), item->surface);
+ }
+
+ gtk_label_set_text (GTK_LABEL (self->priv->preview_label), item->label);
+
+ if (self->priv->update_id == 0) {
+ self->priv->update_id = g_timeout_add (300, (GSourceFunc)update_jobs_timeout, self);
+ }
+}
+
+static void
+scale_value_changed_cb (GtkRange *range,
+ PhotosZoomBar *self)
+{
+ int page;
+
+ page = round (gtk_range_get_value (GTK_RANGE (self->priv->scale)));
+ if (page == self->priv->preview_page) {
+ return;
+ }
+
+ self->priv->preview_page = page;
+ if (self->priv->scrubbing) {
+ update_preview (self);
+ if (self->priv->show_id == 0) {
+ self->priv->show_id = g_timeout_add (300, (GSourceFunc)show_preview_timeout, self);
+ }
+ } else {
+ hide_preview (self);
+ update_current_page (self);
+ }
+}
+
+static gboolean
+scale_button_press_cb (GtkWidget *widget,
+ GdkEvent *event,
+ PhotosZoomBar *self)
+{
+ self->priv->scrubbing = TRUE;
+ update_preview (self);
+
+ /* delay the show slightly to avoid flashing if the release is
+ coming soon */
+ if (self->priv->show_id == 0) {
+ self->priv->show_id = g_timeout_add (300, (GSourceFunc)show_preview_timeout, self);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+scale_button_release_cb (GtkWidget *widget,
+ GdkEvent *event,
+ PhotosZoomBar *self)
+{
+ self->priv->scrubbing = FALSE;
+ hide_preview (self);
+ update_current_page (self);
+
+ return FALSE;
+}
+
+static gboolean
+scale_grab_broken_cb (GtkWidget *widget,
+ GdkEvent *event,
+ PhotosZoomBar *self)
+{
+ self->priv->scrubbing = FALSE;
+ hide_preview (self);
+
+ return FALSE;
+}
+
+static gboolean
+scale_motion_notify_cb (GtkWidget *widget,
+ GdkEvent *event,
+ PhotosZoomBar *self)
+{
+ /* show the preview immediately if we're scrubbing */
+ if (self->priv->scrubbing) {
+ if (self->priv->show_id != 0) {
+ g_source_remove (self->priv->show_id);
+ self->priv->show_id = 0;
+ }
+ show_preview (self);
+ }
+
+ return FALSE;
+}
+
+/**
+ * photos_zoom_bar_get_button_area:
+ * @bar: a #PhotosZoomBar
+ *
+ * Returns the button area of @bar.
+ *
+ * Returns: (transfer none): the button area #GtkBox.
+ **/
+GtkWidget *
+photos_zoom_bar_get_button_area (PhotosZoomBar *bar)
+{
+ g_return_val_if_fail (GD_IS_NAV_BAR (bar), NULL);
+
+ return bar->priv->button_area;
+}
+
+static void
+photos_zoom_bar_init (PhotosZoomBar *self)
+{
+ PhotosZoomBarPrivate *priv;
+ GtkWidget *inner_box;
+
+ self->priv = PHOTOS_ZOOM_BAR_GET_PRIVATE (self);
+
+ priv = self->priv;
+
+ gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
+
+ priv->loading_icons = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)cairo_surface_destroy);
+
+ inner_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_box_set_spacing (GTK_BOX (inner_box), 10);
+ gtk_widget_show (inner_box);
+ gtk_widget_set_hexpand (GTK_WIDGET (inner_box), TRUE);
+ gtk_container_add (GTK_CONTAINER (self), inner_box);
+
+ priv->button_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (priv->button_area)),
+ GTK_STYLE_CLASS_LINKED);
+ gtk_widget_show (priv->button_area);
+ gtk_box_pack_start (GTK_BOX (inner_box), priv->button_area, FALSE, FALSE, 0);
+
+ priv->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
+ gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE);
+ gtk_scale_set_has_origin (GTK_SCALE (priv->scale), TRUE);
+ gtk_range_set_increments (GTK_RANGE (priv->scale), 1.0, 1.0);
+ gtk_range_set_range (GTK_RANGE (priv->scale), 0.0, 1.0);
+ gtk_widget_show (priv->scale);
+ gtk_box_pack_start (GTK_BOX (inner_box), priv->scale, TRUE, TRUE, 0);
+
+ priv->page_label = gtk_label_new (NULL);
+ gtk_widget_show (priv->page_label);
+ gtk_box_pack_end (GTK_BOX (inner_box), priv->page_label, FALSE, FALSE, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (self), 0);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)),
+ GTK_STYLE_CLASS_TOOLBAR);
+ g_signal_connect (self, "notify::scale-factor",
+ G_CALLBACK (previews_reload), NULL);
+
+ g_signal_connect (priv->scale, "value-changed",
+ G_CALLBACK (scale_value_changed_cb),
+ self);
+ g_signal_connect (priv->scale, "button-press-event",
+ G_CALLBACK (scale_button_press_cb),
+ self);
+ g_signal_connect (priv->scale, "button-release-event",
+ G_CALLBACK (scale_button_release_cb),
+ self);
+ g_signal_connect (priv->scale, "grab-broken-event",
+ G_CALLBACK (scale_grab_broken_cb),
+ self);
+ g_signal_connect (priv->scale, "motion-notify-event",
+ G_CALLBACK (scale_motion_notify_cb),
+ self);
+}
+
+
+GtkWidget *
+photos_zoom_bar_new (void)
+{
+ return g_object_new (PHOTOS_TYPE_ZOOM_BAR, NULL);
+}
+
+
+gboolean
+photos_zoom_bar_get_hover (PhotosZoomBar *self)
+{
+ return self->hover;
+}
diff --git a/src/photos-zoom-bar.h b/src/photos-zoom-bar.h
new file mode 100644
index 0000000..c3a7346
--- /dev/null
+++ b/src/photos-zoom-bar.h
@@ -0,0 +1,39 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/* Based on code from:
+ * + Documents
+ */
+
+#ifndef PHOTOS_ZOOM_BAR_H
+#define PHOTOS_ZOOM_BAR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PHOTOS_TYPE_ZOOM_BAR (photos_zoom_bar_get_type ())
+G_DECLARE_FINAL_TYPE (PhotosZoomBar, photos_zoom_bar, PHOTOS, ZOOM_BAR, GtkBox);
+
+GtkWidget *photos_zoom_bar_new (void);
+
+G_END_DECLS
+
+#endif /* PHOTOS_ZOOM_BAR_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]