[recipes] Use a multi-image viewer
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes] Use a multi-image viewer
- Date: Thu, 15 Dec 2016 19:02:39 +0000 (UTC)
commit 8497586a6d25c820170579dce3f4aed9533217b6
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Dec 15 13:08:00 2016 -0500
Use a multi-image viewer
This widget shows multiple images, and lets the user cycle through
them.
po/POTFILES.in | 2 +
src/Makefile.am | 2 +
src/gr-details-page.c | 16 +--
src/gr-details-page.ui | 2 +-
src/gr-image-viewer.c | 301 +++++++++++++++++++++++++++++++++++++++++++++
src/gr-image-viewer.h | 36 ++++++
src/gr-image-viewer.ui | 101 +++++++++++++++
src/main.c | 2 +
src/recipes.css | 4 +
src/recipes.gresource.xml | 1 +
10 files changed, 452 insertions(+), 15 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7b05dd5..bc9b7c2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -9,6 +9,7 @@ src/gr-cuisines-page.ui
src/gr-details-page.ui
src/gr-edit-page.ui
src/gr-image-editor.ui
+src/gr-image-viewer.ui
src/gr-ingredient-search-tile.ui
src/gr-ingredient-tile.ui
src/gr-ingredients-page.ui
@@ -35,6 +36,7 @@ src/gr-diet.c
src/gr-diet-row.c
src/gr-edit-page.c
src/gr-image-editor.c
+src/gr-image-viewer.c
src/gr-ingredient.c
src/gr-ingredient-search-tile.c
src/gr-ingredient-tile.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 49c8470..1d9e9dc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,6 +46,8 @@ recipes_SOURCES = \
gr-edit-page.c \
gr-image-editor.h \
gr-image-editor.c \
+ gr-image-viewer.h \
+ gr-image-viewer.c \
gr-ingredient.h \
gr-ingredient.c \
gr-ingredient-row.h \
diff --git a/src/gr-details-page.c b/src/gr-details-page.c
index 5259a75..5f8d328 100644
--- a/src/gr-details-page.c
+++ b/src/gr-details-page.c
@@ -31,6 +31,7 @@
#include "gr-window.h"
#include "gr-utils.h"
#include "gr-image-editor.h"
+#include "gr-image-viewer.h"
#include "gr-ingredients-list.h"
#include "gr-timer.h"
#include "gr-recipe-printer.h"
@@ -689,20 +690,7 @@ gr_details_page_set_recipe (GrDetailsPage *page,
instructions = gr_recipe_get_instructions (recipe);
g_object_get (recipe, "images", &images, NULL);
- if (images->len > 0) {
- g_autofree char *css = NULL;
- GrRotatedImage *ri = &g_array_index (images, GrRotatedImage, 0);
- g_autoptr(GdkPixbuf) pb = gdk_pixbuf_new_from_file (ri->path, NULL);
-
- css = g_strdup_printf (" background: url('%s');\n"
- " background-size: 100%%;\n"
- " background-repeat: no-repeat;\n", ri->path);
- gr_utils_widget_set_css_simple (page->recipe_image, css);
- g_object_set (page->recipe_image,
- "width-request", 360,
- "height-request", (int)((360.0 / gdk_pixbuf_get_width (pb)) *
gdk_pixbuf_get_height (pb)),
- NULL);
- }
+ gr_image_viewer_set_images (GR_IMAGE_VIEWER (page->recipe_image), images);
ing = gr_ingredients_list_new (ingredients);
g_set_object (&page->ingredients, ing);
diff --git a/src/gr-details-page.ui b/src/gr-details-page.ui
index 03aa0aa..bf0a1a5 100644
--- a/src/gr-details-page.ui
+++ b/src/gr-details-page.ui
@@ -118,7 +118,7 @@ followed</property>
<property name="spacing">20</property>
<property name="valign">start</property>
<child>
- <object class="GtkImage" id="recipe_image">
+ <object class="GrImageViewer" id="recipe_image">
<property name="visible">1</property>
<property name="margin-bottom">30</property>
</object>
diff --git a/src/gr-image-viewer.c b/src/gr-image-viewer.c
new file mode 100644
index 0000000..e521921
--- /dev/null
+++ b/src/gr-image-viewer.c
@@ -0,0 +1,301 @@
+/* gr-image-viewer.c:
+ *
+ * Copyright (C) 2016 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 3.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gr-image-viewer.h"
+#include "gr-image-editor.h"
+#include "gr-utils.h"
+
+struct _GrImageViewer
+{
+ GtkEventBox parent_instance;
+
+ GtkWidget *overlay;
+ GtkWidget *image;
+ GtkWidget *event_box;
+ GtkWidget *next_revealer;
+ GtkWidget *prev_revealer;
+ GtkWidget *preview_revealer;
+ GtkWidget *preview_list;
+
+ GArray *images;
+ int index;
+
+ guint hide_timeout;
+
+ GtkGesture *gesture;
+};
+
+
+G_DEFINE_TYPE (GrImageViewer, gr_image_viewer, GTK_TYPE_BOX)
+
+GrImageViewer *
+gr_image_viewer_new (void)
+{
+ return g_object_new (GR_TYPE_IMAGE_VIEWER, NULL);
+}
+
+static void
+remove_hide_timeout (GrImageViewer *viewer)
+{
+ if (viewer->hide_timeout != 0) {
+ g_source_remove (viewer->hide_timeout);
+ viewer->hide_timeout = 0;
+ }
+}
+
+static void
+gr_image_viewer_finalize (GObject *object)
+{
+ GrImageViewer *viewer = GR_IMAGE_VIEWER (object);
+
+ g_array_unref (viewer->images);
+ remove_hide_timeout (viewer);
+ g_clear_object (&viewer->gesture);
+
+ G_OBJECT_CLASS (gr_image_viewer_parent_class)->finalize (object);
+}
+
+static void
+set_current_image (GrImageViewer *viewer)
+{
+ GtkFlowBoxChild *child;
+
+ if (!viewer->images)
+ return;
+
+ if (viewer->images->len > viewer->index) {
+ GrRotatedImage *ri = &g_array_index (viewer->images, GrRotatedImage, viewer->index);
+ g_autoptr(GdkPixbuf) pb = load_pixbuf_fill_size (ri->path, ri->angle, 360, 240);
+ gtk_image_set_from_pixbuf (GTK_IMAGE (viewer->image), pb);
+ }
+
+ child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (viewer->preview_list), viewer->index);
+ gtk_flow_box_select_child (GTK_FLOW_BOX (viewer->preview_list), child);
+}
+
+static void
+populate_preview (GrImageViewer *viewer)
+{
+ int i;
+
+ container_remove_all (GTK_CONTAINER (viewer->preview_list));
+
+ for (i = 0; i < viewer->images->len; i++) {
+ GrRotatedImage *ri = &g_array_index (viewer->images, GrRotatedImage, i);
+ g_autoptr(GdkPixbuf) pb = load_pixbuf_fill_size (ri->path, ri->angle, 60, 40);
+ GtkWidget *image;
+
+ image = gtk_image_new_from_pixbuf (pb);
+ gtk_widget_show (image);
+ gtk_container_add (GTK_CONTAINER (viewer->preview_list), image);
+ }
+}
+
+static void
+show_buttons (GrImageViewer *viewer)
+{
+ if (!viewer->images || viewer->images->len < 2)
+ return;
+
+ if (!gtk_revealer_get_child_revealed (GTK_REVEALER (viewer->next_revealer)))
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->next_revealer), TRUE);
+
+ if (!gtk_revealer_get_child_revealed (GTK_REVEALER (viewer->prev_revealer)))
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->prev_revealer), TRUE);
+}
+
+static void
+hide_buttons (GrImageViewer *viewer)
+{
+ if (gtk_revealer_get_child_revealed (GTK_REVEALER (viewer->next_revealer)))
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->next_revealer), FALSE);
+
+ if (gtk_revealer_get_child_revealed (GTK_REVEALER (viewer->prev_revealer)))
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->prev_revealer), FALSE);
+}
+
+static void
+hide_preview (GrImageViewer *viewer)
+{
+ if (gtk_revealer_get_child_revealed (GTK_REVEALER (viewer->preview_revealer)))
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->preview_revealer), FALSE);
+}
+
+static void
+toggle_preview (GrImageViewer *viewer)
+{
+ if (!viewer->images || viewer->images->len < 2)
+ return;
+
+ if (gtk_revealer_get_child_revealed (GTK_REVEALER (viewer->preview_revealer)))
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->preview_revealer), FALSE);
+ else
+ gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->preview_revealer), TRUE);
+}
+
+static void
+hide_controls (GrImageViewer *viewer)
+{
+ hide_buttons (viewer);
+ hide_preview (viewer);
+}
+
+static gboolean
+hide_timeout (gpointer data)
+{
+ GrImageViewer *viewer = data;
+
+ hide_controls (viewer);
+
+ viewer->hide_timeout = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+reset_hide_timeout (GrImageViewer *viewer)
+{
+ remove_hide_timeout (viewer);
+ viewer->hide_timeout = g_timeout_add (5000, hide_timeout, viewer);
+}
+
+static gboolean
+enter_leave_notify (GtkWidget *widget,
+ GdkEvent *event,
+ GrImageViewer *viewer)
+{
+ if (((GdkEventCrossing *)event)->detail != GDK_NOTIFY_VIRTUAL) // FIXME
+ return FALSE;
+
+ if (event->type == GDK_ENTER_NOTIFY) {
+ show_buttons (viewer);
+ reset_hide_timeout (viewer);
+ }
+ else {
+ hide_controls (viewer);
+ remove_hide_timeout (viewer);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+motion_notify (GtkWidget *widget,
+ GdkEvent *event,
+ GrImageViewer *viewer)
+{
+ show_buttons (viewer);
+ reset_hide_timeout (viewer);
+
+ return FALSE;
+}
+
+static void
+button_press (GrImageViewer *viewer)
+{
+ toggle_preview (viewer);
+}
+
+static void
+prev_image (GrImageViewer *viewer)
+{
+ viewer->index = (viewer->index + viewer->images->len - 1) % viewer->images->len;
+ set_current_image (viewer);
+}
+
+static void
+next_image (GrImageViewer *viewer)
+{
+ viewer->index = (viewer->index + 1) % viewer->images->len;
+ set_current_image (viewer);
+}
+
+static void
+preview_selected (GrImageViewer *viewer)
+{
+ GList *l;
+ GtkFlowBoxChild *child;
+
+ l = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (viewer->preview_list));
+ if (!l)
+ return;
+
+ child = l->data;
+ g_list_free (l);
+ viewer->index = gtk_flow_box_child_get_index (child);
+ set_current_image (viewer);
+}
+
+static void
+gr_image_viewer_init (GrImageViewer *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+ gtk_widget_add_events (GTK_WIDGET (self->event_box), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
GDK_POINTER_MOTION_MASK);
+ gtk_widget_add_events (GTK_WIDGET (self->event_box), GDK_BUTTON_PRESS_MASK);
+
+ g_signal_connect (self->event_box, "enter-notify-event", G_CALLBACK (enter_leave_notify), self);
+ g_signal_connect (self->event_box, "leave-notify-event", G_CALLBACK (enter_leave_notify), self);
+ g_signal_connect (self->event_box, "motion-notify-event", G_CALLBACK (motion_notify), self);
+
+#if 1
+ self->gesture = gtk_gesture_multi_press_new (self->event_box);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->gesture), GTK_PHASE_BUBBLE);
+ g_signal_connect_swapped (self->gesture, "pressed", G_CALLBACK (button_press), self);
+#endif
+}
+
+static void
+gr_image_viewer_class_init (GrImageViewerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gr_image_viewer_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Recipes/gr-image-viewer.ui");
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, image);
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, event_box);
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, overlay);
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, prev_revealer);
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, next_revealer);
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, preview_revealer);
+ gtk_widget_class_bind_template_child (widget_class, GrImageViewer, preview_list);
+ gtk_widget_class_bind_template_callback (widget_class, prev_image);
+ gtk_widget_class_bind_template_callback (widget_class, next_image);
+ gtk_widget_class_bind_template_callback (widget_class, preview_selected);
+}
+
+void
+gr_image_viewer_set_images (GrImageViewer *viewer,
+ GArray *images)
+{
+ if (viewer->images)
+ g_array_unref (viewer->images);
+ viewer->images = images;
+ if (viewer->images)
+ g_array_ref (viewer->images);
+
+ populate_preview (viewer);
+
+ viewer->index = 0;
+ set_current_image (viewer);
+}
diff --git a/src/gr-image-viewer.h b/src/gr-image-viewer.h
new file mode 100644
index 0000000..21f40f0
--- /dev/null
+++ b/src/gr-image-viewer.h
@@ -0,0 +1,36 @@
+/* gr-image-viewer.h
+ *
+ * Copyright (C) 2016 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 3.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GR_TYPE_IMAGE_VIEWER (gr_image_viewer_get_type())
+
+G_DECLARE_FINAL_TYPE (GrImageViewer, gr_image_viewer, GR, IMAGE_VIEWER, GtkBox)
+
+GrImageViewer *gr_image_viewer_new (void);
+void gr_image_viewer_set_images (GrImageViewer *viewer,
+ GArray *images);
+
+G_END_DECLS
+
diff --git a/src/gr-image-viewer.ui b/src/gr-image-viewer.ui
new file mode 100644
index 0000000..0b76930
--- /dev/null
+++ b/src/gr-image-viewer.ui
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="recipes">
+ <!-- interface-requires gtk+ 3.10 -->
+ <template class="GrImageViewer" parent="GtkBox">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkOverlay" id="overlay">
+ <property name="visible">1</property>
+ <child type="overlay">
+ <object class="GtkRevealer" id="prev_revealer">
+ <property name="visible">1</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="margin">10</property>
+ <property name="transition-type">crossfade</property>
+ <style> <class name="osd"/> </style>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">1</property>
+ <signal name="clicked" handler="prev_image" swapped="yes"/>
+ <style> <class name="image-button"/> </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">1</property>
+ <property name="icon-name">pan-start-symbolic</property>
+ <property name="icon-size">2</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkRevealer" id="next_revealer">
+ <property name="visible">1</property>
+ 2<property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin">10</property>
+ <property name="transition-type">crossfade</property>
+ <style> <class name="osd"/> </style>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">1</property>
+ <style> <class name="image-button"/> </style>
+ <signal name="clicked" handler="next_image" swapped="yes"/>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">1</property>
+ <property name="icon-name">pan-end-symbolic</property>
+ <property name="icon-size">2</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkRevealer" id="preview_revealer">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="valign">end</property>
+ <property name="margin">0</property>
+ <property name="transition-type">slide-up</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">1</property>
+ <property name="orientation">horizontal</property>
+ <property name="halign">fill</property>
+ <property name="valign">fill</property>
+ <style> <class name="preview-list"/> </style>
+ <child>
+ <object class="GtkFlowBox" id="preview_list">
+ <property name="visible">1</property>
+ <property name="halign">center</property>
+ <property name="selection-mode">single</property>
+ <property name="min-children-per-line">5</property>
+ <signal name="selected-children-changed" handler="preview_selected" swapped="yes"/>
+ </object>
+ <packing>
+ <property name="expand">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEventBox" id="event_box">
+ <property name="visible">1</property>
+ <property name="above-child">1</property>
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="visible">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/main.c b/src/main.c
index d7ca331..ecb39f9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -36,6 +36,7 @@
#include "gr-timer-widget.h"
#include "gr-toggle-button.h"
#include "gr-image-editor.h"
+#include "gr-image-viewer.h"
int
@@ -49,6 +50,7 @@ main (int argc, char *argv[])
g_type_ensure (GR_TYPE_DETAILS_PAGE);
g_type_ensure (GR_TYPE_EDIT_PAGE);
g_type_ensure (GR_TYPE_IMAGE_EDITOR);
+ g_type_ensure (GR_TYPE_IMAGE_VIEWER);
g_type_ensure (GR_TYPE_INGREDIENTS_PAGE);
g_type_ensure (GR_TYPE_LIST_PAGE);
g_type_ensure (GR_TYPE_QUERY_EDITOR);
diff --git a/src/recipes.css b/src/recipes.css
index 16e420f..765db83 100644
--- a/src/recipes.css
+++ b/src/recipes.css
@@ -167,3 +167,7 @@ image.chef.circular {
image.very-spicy {
color: red;
}
+
+.preview-list {
+ background: black;
+}
diff --git a/src/recipes.gresource.xml b/src/recipes.gresource.xml
index 94a4889..a746b7e 100644
--- a/src/recipes.gresource.xml
+++ b/src/recipes.gresource.xml
@@ -10,6 +10,7 @@
<file preprocess="xml-stripblanks">gr-diet-row.ui</file>
<file preprocess="xml-stripblanks">gr-edit-page.ui</file>
<file preprocess="xml-stripblanks">gr-image-editor.ui</file>
+ <file preprocess="xml-stripblanks">gr-image-viewer.ui</file>
<file preprocess="xml-stripblanks">gr-ingredient-row.ui</file>
<file preprocess="xml-stripblanks">gr-ingredient-tile.ui</file>
<file preprocess="xml-stripblanks">gr-ingredients-page.ui</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]