[libgepub] Added pagination to the GepubWidget
- From: Daniel Garcia Moreno <danigm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgepub] Added pagination to the GepubWidget
- Date: Sat, 20 May 2017 19:13:06 +0000 (UTC)
commit 2616050444094776de73fcdd124eb1f9b5053176
Author: Daniel GarcĂa Moreno <danigm wadobo com>
Date: Sat May 20 20:59:16 2017 +0200
Added pagination to the GepubWidget
I've added a property to set pagination on/off so the webkitwebview will
be shown with or without scroll.
The pagination is done with the column-width css property, setting the
"body" with the widget width and height and the column-width with the
same widget width, so we change the scroll from vertical to horizontal.
Using this combined with "overflow" css property and with the "scrollTo"
javascript function we can control the page showed.
The position in the book we've the chapter number and the the position
inside this chapter, the position is a percentage that we can get and
set with "gepub_widget_(g/s)et_pos". We store a percentage to make it
valid if the widget size changes, the page is not the same, but the
position is always valid.
To make this work well we need to remove margin and padding from the
body tag, because this affects the column-width.
https://bugzilla.gnome.org/show_bug.cgi?id=768002
libgepub/gepub-widget.c | 387 ++++++++++++++++++++++++++++++++++++++++++++++-
libgepub/gepub-widget.h | 16 ++
tests/test-gepub.c | 57 ++++++-
3 files changed, 452 insertions(+), 8 deletions(-)
---
diff --git a/libgepub/gepub-widget.c b/libgepub/gepub-widget.c
index 149b3dc..b3dbb6a 100644
--- a/libgepub/gepub-widget.c
+++ b/libgepub/gepub-widget.c
@@ -19,6 +19,7 @@
#include <config.h>
#include <gtk/gtk.h>
+#include <JavaScriptCore/JSValueRef.h>
#include "gepub-widget.h"
@@ -26,6 +27,11 @@ struct _GepubWidget {
WebKitWebView parent;
GepubDoc *doc;
+ gboolean paginate;
+ gint chapter_length; // real chapter length
+ gint chapter_pos; // position in the chapter, a percentage based on chapter_length
+ gint length;
+ gint init_chapter_pos;
};
struct _GepubWidgetClass {
@@ -35,6 +41,10 @@ struct _GepubWidgetClass {
enum {
PROP_0,
PROP_DOC,
+ PROP_PAGINATE,
+ PROP_CHAPTER,
+ PROP_N_CHAPTERS,
+ PROP_CHAPTER_POS,
NUM_PROPS
};
@@ -43,6 +53,147 @@ static GParamSpec *properties[NUM_PROPS] = { NULL, };
G_DEFINE_TYPE (GepubWidget, gepub_widget, WEBKIT_TYPE_WEB_VIEW)
static void
+scroll_to_chapter_pos (GepubWidget *widget) {
+ gchar *script = g_strdup_printf("document.querySelector('body').scrollTo(%d, 0)", widget->chapter_pos);
+ webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (widget), script, NULL, NULL, NULL);
+ g_free(script);
+}
+
+static void
+adjust_chapter_pos (GepubWidget *widget)
+{
+ // integer division to make a page start
+ gint page = widget->chapter_pos / widget->length;
+ gint next = page + 1;
+ gint d1 = widget->chapter_pos - (widget->length * page);
+ gint d2 = (widget->length * next) - widget->chapter_pos;
+
+ if (d1 < d2) {
+ widget->chapter_pos = widget->length * page;
+ } else {
+ widget->chapter_pos = widget->length * next;
+ }
+ scroll_to_chapter_pos (widget);
+}
+
+static void
+pagination_initialize_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ WebKitJavascriptResult *js_result;
+ JSValueRef value;
+ JSGlobalContextRef context;
+ GError *error = NULL;
+ GepubWidget *widget = GEPUB_WIDGET (user_data);
+
+ js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
+ if (!js_result) {
+ g_warning ("Error running javascript: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ context = webkit_javascript_result_get_global_context (js_result);
+ value = webkit_javascript_result_get_value (js_result);
+ if (JSValueIsNumber (context, value)) {
+ double n;
+
+ n = JSValueToNumber (context, value, NULL);
+ widget->chapter_length = (int)n;
+
+ if (widget->init_chapter_pos) {
+ widget->chapter_pos = widget->init_chapter_pos * widget->chapter_length / 100;
+ if (widget->chapter_pos > (widget->chapter_length - widget->length)) {
+ widget->chapter_pos = (widget->chapter_length - widget->length);
+ }
+ widget->init_chapter_pos = 0;
+ }
+
+ if (widget->chapter_pos) {
+ adjust_chapter_pos (widget);
+ }
+
+ } else {
+ g_warning ("Error running javascript: unexpected return value");
+ }
+ webkit_javascript_result_unref (js_result);
+}
+
+static void
+get_length_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ WebKitJavascriptResult *js_result;
+ JSValueRef value;
+ JSGlobalContextRef context;
+ GError *error = NULL;
+ GepubWidget *widget = GEPUB_WIDGET (user_data);
+
+ js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
+ if (!js_result) {
+ g_warning ("Error running javascript: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ context = webkit_javascript_result_get_global_context (js_result);
+ value = webkit_javascript_result_get_value (js_result);
+ if (JSValueIsNumber (context, value)) {
+ double n;
+
+ n = JSValueToNumber (context, value, NULL);
+ widget->length = (int)n;
+ } else {
+ g_warning ("Error running javascript: unexpected return value");
+ }
+ webkit_javascript_result_unref (js_result);
+}
+
+static void
+reload_length_cb (GtkWidget *widget,
+ GdkRectangle *allocation,
+ gpointer user_data)
+{
+ GepubWidget *gwidget = GEPUB_WIDGET (widget);
+ WebKitWebView *web_view = WEBKIT_WEB_VIEW (widget);
+
+ webkit_web_view_run_javascript (web_view,
+ "window.innerWidth",
+ NULL, get_length_finished, (gpointer)widget);
+
+ if (gwidget->paginate) {
+ webkit_web_view_run_javascript (web_view,
+ // TODO: Adjusts to show a little margin at least
+ "document.querySelector('body').setAttribute('style', '"
+ "overflow: hidden;"
+ "column-gap: 0px;"
+ "margin-left: 0px;"
+ "margin-right: 0px;"
+ "padding-left: 0px;"
+ "padding-right: 0px;"
+ "');"
+ "document.querySelector('body').style.columnWidth = window.innerWidth+'px';"
+ "document.querySelector('body').style.height = window.innerHeight+'px';"
+ "document.querySelector('body').scrollWidth",
+ NULL, pagination_initialize_finished, (gpointer)widget);
+ }
+}
+
+static void
+docready_cb (WebKitWebView *web_view,
+ WebKitLoadEvent load_event,
+ gpointer user_data)
+{
+ GepubWidget *widget = GEPUB_WIDGET (web_view);
+
+ if (load_event == WEBKIT_LOAD_FINISHED) {
+ reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
+ }
+}
+
+static void
resource_callback (WebKitURISchemeRequest *request, gpointer user_data)
{
GInputStream *stream;
@@ -87,6 +238,15 @@ gepub_widget_set_property (GObject *object,
case PROP_DOC:
gepub_widget_set_doc (widget, g_value_get_object (value));
break;
+ case PROP_PAGINATE:
+ gepub_widget_set_pagination (widget, g_value_get_boolean (value));
+ break;
+ case PROP_CHAPTER:
+ gepub_doc_set_page (widget->doc, g_value_get_int (value));
+ break;
+ case PROP_CHAPTER_POS:
+ gepub_widget_set_pos (widget, g_value_get_float (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -105,6 +265,18 @@ gepub_widget_get_property (GObject *object,
case PROP_DOC:
g_value_set_object (value, gepub_widget_get_doc (widget));
break;
+ case PROP_PAGINATE:
+ g_value_set_boolean (value, widget->paginate);
+ break;
+ case PROP_CHAPTER:
+ g_value_set_int (value, gepub_doc_get_page (widget->doc));
+ break;
+ case PROP_N_CHAPTERS:
+ g_value_set_int (value, gepub_doc_get_n_pages (widget->doc));
+ break;
+ case PROP_CHAPTER_POS:
+ g_value_set_float (value, gepub_widget_get_pos (widget));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -124,6 +296,11 @@ gepub_widget_finalize (GObject *object)
static void
gepub_widget_init (GepubWidget *widget)
{
+ widget->chapter_length = 0;
+ widget->paginate = FALSE;
+ widget->chapter_pos = 0;
+ widget->length = 0;
+ widget->init_chapter_pos = 0;
}
static void
@@ -136,6 +313,8 @@ gepub_widget_constructed (GObject *object)
ctx = webkit_web_view_get_context (WEBKIT_WEB_VIEW (widget));
webkit_web_context_register_uri_scheme (ctx, "epub", resource_callback, widget, NULL);
+ g_signal_connect (widget, "load-changed", G_CALLBACK (docready_cb), NULL);
+ g_signal_connect (widget, "size-allocate", G_CALLBACK (reload_length_cb), NULL);
}
static void
@@ -156,6 +335,34 @@ gepub_widget_class_init (GepubWidgetClass *klass)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
+ properties[PROP_PAGINATE] =
+ g_param_spec_boolean ("paginate",
+ "paginate",
+ "If the widget should paginate",
+ FALSE,
+ G_PARAM_READWRITE);
+
+ properties[PROP_CHAPTER] =
+ g_param_spec_int ("chapter",
+ "Current chapter",
+ "Current chapter in the doc",
+ -1, G_MAXINT, 0,
+ G_PARAM_READWRITE);
+
+ properties[PROP_N_CHAPTERS] =
+ g_param_spec_int ("nchapters",
+ "Number of chapters in the doc",
+ "Number of chapters in the doc",
+ -1, G_MAXINT, 0,
+ G_PARAM_READABLE);
+
+ properties[PROP_CHAPTER_POS] =
+ g_param_spec_float ("chapter_pos",
+ "Current position in chapter",
+ "Current position in chapter",
+ 0, 100, 0,
+ G_PARAM_READWRITE);
+
g_object_class_install_properties (object_class, NUM_PROPS, properties);
}
@@ -190,12 +397,15 @@ reload_current_chapter (GepubWidget *widget)
{
GBytes *current;
+ widget->chapter_length = 0;
+ widget->chapter_pos = 0;
+ widget->length = 0;
+
current = gepub_doc_get_current_with_epub_uris (widget->doc);
webkit_web_view_load_bytes (WEBKIT_WEB_VIEW (widget),
current,
gepub_doc_get_current_mime (widget->doc),
"UTF-8", NULL);
-
g_bytes_unref (current);
}
@@ -232,3 +442,178 @@ gepub_widget_set_doc (GepubWidget *widget,
g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_DOC]);
}
+
+/**
+ * gepub_widget_set_pagination:
+ * @widget: a #GepubWidget
+ * @p: true if the widget should paginate
+ *
+ * Enable or disable pagination
+ */
+void
+gepub_widget_set_pagination (GepubWidget *widget,
+ gboolean p)
+{
+ widget->paginate = p;
+ reload_current_chapter (widget);
+}
+
+/**
+ * gepub_widget_get_n_chapters:
+ * @widget: a #GepubWidget
+ *
+ * Returns: the number of chapters in the document
+ */
+gint
+gepub_widget_get_n_chapters (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
+ return gepub_doc_get_n_pages (widget->doc);
+}
+
+/**
+ * gepub_widget_get_chapter:
+ * @widget: a #GepubWidget
+ *
+ * Returns: the current chapter in the document
+ */
+gint
+gepub_widget_get_chapter (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
+ return gepub_doc_get_page (widget->doc);
+}
+
+/**
+ * gepub_widget_get_chapter_length:
+ * @widget: a #GepubWidget
+ *
+ * Returns: the current chapter length
+ */
+gint
+gepub_widget_get_chapter_length (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
+ return widget->chapter_length;
+}
+
+/**
+ * gepub_widget_set_chapter:
+ * @widget: a #GepubWidget
+ *
+ * Sets the current chapter in the doc
+ */
+void
+gepub_widget_set_chapter (GepubWidget *widget,
+ gint index)
+{
+ g_return_if_fail (GEPUB_IS_DOC (widget->doc));
+ return gepub_doc_set_page (widget->doc, index);
+}
+
+/**
+ * gepub_widget_chapter_next:
+ * @widget: a #GepubWidget
+ *
+ * Returns: TRUE on success, FALSE if there's no next chapter
+ */
+gboolean
+gepub_widget_chapter_next (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
+ return gepub_doc_go_next (widget->doc);
+}
+
+/**
+ * gepub_widget_chapter_prev:
+ * @widget: a #GepubWidget
+ *
+ * Returns: TRUE on success, FALSE if there's no prev chapter
+ */
+gboolean
+gepub_widget_chapter_prev (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
+ return gepub_doc_go_prev (widget->doc);
+}
+
+/**
+ * gepub_widget_page_next:
+ * @widget: a #GepubWidget
+ *
+ * Returns: TRUE on success, FALSE if there's no next page
+ */
+gboolean
+gepub_widget_page_next (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
+ widget->chapter_pos = widget->chapter_pos + widget->length;
+
+ if (widget->chapter_pos > (widget->chapter_length - widget->length)) {
+ widget->chapter_pos = (widget->chapter_length - widget->length);
+ return gepub_doc_go_next (widget->doc);
+ }
+
+ scroll_to_chapter_pos (widget);
+
+ g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
+ return TRUE;
+}
+
+/**
+ * gepub_widget_page_prev:
+ * @widget: a #GepubWidget
+ *
+ * Returns: TRUE on success, FALSE if there's no next page
+ */
+gboolean
+gepub_widget_page_prev (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
+ widget->chapter_pos = widget->chapter_pos - widget->length;
+
+ if (widget->chapter_pos < 0) {
+ widget->init_chapter_pos = 100;
+ return gepub_doc_go_prev (widget->doc);
+ }
+
+ scroll_to_chapter_pos (widget);
+
+ g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
+ return TRUE;
+}
+
+/**
+ * gepub_widget_get_pos:
+ * @widget: a #GepubWidget
+ *
+ * Returns: the current position in the chapter
+ */
+gfloat
+gepub_widget_get_pos (GepubWidget *widget)
+{
+ g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
+
+ if (!widget->chapter_length) {
+ return 0;
+ }
+
+ return widget->chapter_pos * 100 / (float)(widget->chapter_length);
+}
+
+/**
+ * gepub_widget_set_pos:
+ * @widget: a #GepubWidget
+ *
+ * Sets the current position in the chapter
+ */
+void
+gepub_widget_set_pos (GepubWidget *widget,
+ gfloat index)
+{
+ g_return_if_fail (GEPUB_IS_DOC (widget->doc));
+ widget->chapter_pos = index * widget->chapter_length / 100;
+ adjust_chapter_pos (widget);
+
+ g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
+}
diff --git a/libgepub/gepub-widget.h b/libgepub/gepub-widget.h
index b77cc6a..67ed53a 100644
--- a/libgepub/gepub-widget.h
+++ b/libgepub/gepub-widget.h
@@ -46,6 +46,22 @@ GepubDoc *gepub_widget_get_doc (GepubWidget *wid
void gepub_widget_set_doc (GepubWidget *widget,
GepubDoc *doc);
+void gepub_widget_set_pagination (GepubWidget *widget, gboolean p);
+
+gint gepub_widget_get_n_chapters (GepubWidget *widget);
+gint gepub_widget_get_chapter (GepubWidget *widget);
+gint gepub_widget_get_chapter_length (GepubWidget *widget);
+void gepub_widget_set_chapter (GepubWidget *widget,
+ gint index);
+gboolean gepub_widget_chapter_next (GepubWidget *widget);
+gboolean gepub_widget_chapter_prev (GepubWidget *widget);
+
+gfloat gepub_widget_get_pos (GepubWidget *widget);
+void gepub_widget_set_pos (GepubWidget *widget,
+ gfloat index);
+gboolean gepub_widget_page_next (GepubWidget *widget);
+gboolean gepub_widget_page_prev (GepubWidget *widget);
+
G_END_DECLS
#endif /* __GEPUB_WIDGET_H__ */
diff --git a/tests/test-gepub.c b/tests/test-gepub.c
index 20048d4..8f6864b 100644
--- a/tests/test-gepub.c
+++ b/tests/test-gepub.c
@@ -8,6 +8,7 @@ gchar *buf2 = NULL;
gchar *tmpbuf;
GtkTextBuffer *page_buffer;
+GtkWidget *PAGE_LABEL;
#define PTEST1(...) printf (__VA_ARGS__)
#define PTEST2(...) buf = g_strdup_printf (__VA_ARGS__);\
@@ -19,6 +20,14 @@ GtkTextBuffer *page_buffer;
#define TEST(f,arg...) PTEST ("\n### TESTING " #f " ###\n\n"); f (arg); PTEST ("\n\n");
+static void
+reload_current_chapter (GepubWidget *widget)
+{
+ gchar *txt = g_strdup_printf ("%02.2f", gepub_widget_get_pos (widget));
+ gtk_label_set_text (GTK_LABEL (PAGE_LABEL), txt);
+ g_free (txt);
+}
+
void
update_text (GepubDoc *doc)
{
@@ -69,14 +78,22 @@ void
button_pressed (GtkButton *button, GepubWidget *widget)
{
GepubDoc *doc = gepub_widget_get_doc (widget);
+ printf("CLICKED %s\n", gtk_button_get_label (button));
- if (!strcmp (gtk_button_get_label (button), "prev")) {
+ if (!strcmp (gtk_button_get_label (button), "< chapter")) {
gepub_doc_go_prev (doc);
- } else {
+ } else if (!strcmp (gtk_button_get_label (button), "chapter >")) {
gepub_doc_go_next (doc);
+ } else if (!strcmp (gtk_button_get_label (button), "paginated")) {
+ gboolean b = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+ gepub_widget_set_pagination (widget, b);
+ } else if (!strcmp (gtk_button_get_label (button), "< page")) {
+ gepub_widget_page_prev (widget);
+ } else if (!strcmp (gtk_button_get_label (button), "page >")) {
+ gepub_widget_page_next (widget);
}
update_text (doc);
- print_replaced_text (doc);
+ //print_replaced_text (doc);
}
void
@@ -245,6 +262,11 @@ main (int argc, char **argv)
GtkWidget *b_next;
GtkWidget *b_prev;
+ GtkWidget *p_next;
+ GtkWidget *p_prev;
+
+ GtkWidget *paginate;
+
GtkTextBuffer *buffer;
GepubDoc *doc;
@@ -258,7 +280,7 @@ main (int argc, char **argv)
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", (GCallback)gtk_main_quit, NULL);
- gtk_widget_set_size_request (GTK_WIDGET (window), 800, 500);
+ gtk_widget_set_size_request (GTK_WIDGET (window), 1200, 800);
vpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
gtk_container_add (GTK_CONTAINER (window), vpaned);
@@ -285,12 +307,33 @@ main (int argc, char **argv)
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
- b_prev = gtk_button_new_with_label ("prev");
+ b_prev = gtk_button_new_with_label ("< chapter");
g_signal_connect (b_prev, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
- b_next = gtk_button_new_with_label ("next");
+ b_next = gtk_button_new_with_label ("chapter >");
g_signal_connect (b_next, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
+
+ p_prev = gtk_button_new_with_label ("< page");
+ g_signal_connect (p_prev, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
+ p_next = gtk_button_new_with_label ("page >");
+ g_signal_connect (p_next, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
+
+ PAGE_LABEL = gtk_label_new ("0");
+
+ g_signal_connect_swapped (widget, "notify",
+ G_CALLBACK (reload_current_chapter), widget);
+
+ paginate = gtk_check_button_new_with_label ("paginated");
+ g_signal_connect (paginate, "clicked", (GCallback)button_pressed, GEPUB_WIDGET (widget));
+
gtk_container_add (GTK_CONTAINER (hbox), b_prev);
gtk_container_add (GTK_CONTAINER (hbox), b_next);
+
+ gtk_container_add (GTK_CONTAINER (hbox), p_prev);
+ gtk_container_add (GTK_CONTAINER (hbox), p_next);
+
+ gtk_container_add (GTK_CONTAINER (hbox), PAGE_LABEL);
+ gtk_container_add (GTK_CONTAINER (hbox), paginate);
+
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 5);
gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5);
@@ -300,7 +343,7 @@ main (int argc, char **argv)
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5);
- gtk_widget_set_size_request (GTK_WIDGET (vbox), 400, 500);
+ gtk_widget_set_size_request (GTK_WIDGET (vbox), 600, 500);
gtk_paned_add1 (GTK_PANED (vpaned), vbox);
gtk_paned_add2 (GTK_PANED (vpaned), widget);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]