[gnome-documents/wip/gepub] preview: split out a common class for navigation controls



commit 6d477f316f5f048d4a1500928cd3d529bc36396c
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Sun Jun 12 11:59:11 2016 -0700

    preview: split out a common class for navigation controls
    
    Let's share this code between all the preview backends.

 src/epubview.js                           |  192 +++---------------------
 src/evinceview.js                         |  230 +++++------------------------
 src/lokview.js                            |  217 +++++-----------------------
 src/org.gnome.Books.src.gresource.xml     |    1 +
 src/org.gnome.Documents.src.gresource.xml |    1 +
 src/preview.js                            |  206 ++++++++++++++++++++++++++
 6 files changed, 302 insertions(+), 545 deletions(-)
---
diff --git a/src/epubview.js b/src/epubview.js
index 94f6be3..f91a2cb 100644
--- a/src/epubview.js
+++ b/src/epubview.js
@@ -20,7 +20,6 @@
  */
 
 const GLib = imports.gi.GLib;
-const Gdk = imports.gi.Gdk;
 const Gepub = imports.gi.Gepub;
 const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
@@ -32,13 +31,12 @@ const Application = imports.application;
 const Documents = imports.documents;
 const ErrorBox = imports.errorBox;
 const MainToolbar = imports.mainToolbar;
+const Preview = imports.preview;
 const Searchbar = imports.searchbar;
 const WindowMode = imports.windowMode;
 
 const Lang = imports.lang;
-const Mainloop = imports.mainloop;
 const Signals = imports.signals;
-const Tweener = imports.tweener.tweener;
 
 function isEpub(mimeType) {
     return (mimeType == 'application/epub+zip');
@@ -53,7 +51,7 @@ const EPUBView = new Lang.Class({
                       transition_type: Gtk.StackTransitionType.CROSSFADE });
 
         this._overlay = overlay;
-        this.page = 1;
+        this._page = 1;
 
         this._errorBox = new ErrorBox.ErrorBox();
         this.add_named(this._errorBox, 'error');
@@ -126,7 +124,7 @@ const EPUBView = new Lang.Class({
             return;
 
         this.set_visible_child_full('view', Gtk.StackTransitionType.NONE);
-        this.page = 1;
+        this._page = 1;
         this._navControls.show();
     },
 
@@ -138,7 +136,7 @@ const EPUBView = new Lang.Class({
         this.add_named(this.view, 'view');
         this.view.show();
 
-        this._navControls = new EPUBViewNavControls(this, this._overlay);
+        this._navControls = new Preview.PreviewNavControls(this, this._overlay);
         this.set_visible_child_full('view', Gtk.StackTransitionType.NONE);
     },
 
@@ -153,18 +151,30 @@ const EPUBView = new Lang.Class({
         this.view.load_bytes(new GLib.Bytes(current), mime, 'UTF-8', null);
     },
 
-    goNext: function() {
-        if (this._epubdoc.go_next()) {
-            this.page++;
+    goPrev: function() {
+        if (this._epubdoc.go_prev()) {
+            this._page--;
             this._loadCurrent();
         }
     },
 
-    goPrev: function() {
-        if (this._epubdoc.go_prev()) {
-            this.page--;
+    goNext: function() {
+        if (this._epubdoc.go_next()) {
+            this._page++;
             this._loadCurrent();
         }
+    },
+
+    get hasPages() {
+        return true;
+    },
+
+    get page() {
+        return this._page;
+    },
+
+    get numPages() {
+        return this._epubSpine ? this._epubSpine.length : 0;
     }
 });
 
@@ -287,161 +297,3 @@ const EPUBViewToolbar = new Lang.Class({
         this.toolbar.set_title(primary);
     },
 });
-
-const _PREVIEW_NAVBAR_MARGIN = 30;
-const _AUTO_HIDE_TIMEOUT = 2;
-
-const EPUBViewNavControls = new Lang.Class({
-    Name: 'EPUBViewNavControls',
-
-    _init: function(epubView, overlay) {
-        this._epubView = epubView;
-        this._overlay = overlay;
-
-        this._visible = false;
-        this._visibleInternal = false;
-        this._pageChangedId = 0;
-        this._autoHideId = 0;
-        this._motionId = 0;
-
-        this.prev_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-previous-symbolic',
-                                                                    pixel_size: 16 }),
-                                            margin: _PREVIEW_NAVBAR_MARGIN,
-                                            halign: Gtk.Align.START,
-                                            valign: Gtk.Align.CENTER });
-        this.prev_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.prev_widget);
-        this.prev_widget.connect('clicked', Lang.bind(this, this._onPrevClicked));
-        this.prev_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
-        this.prev_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
-
-        this.next_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-next-symbolic',
-                                                                    pixel_size: 16 }),
-                                            margin: _PREVIEW_NAVBAR_MARGIN,
-                                            halign: Gtk.Align.END,
-                                            valign: Gtk.Align.CENTER });
-        this.next_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.next_widget);
-        this.next_widget.connect('clicked', Lang.bind(this, this._onNextClicked));
-        this.next_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
-        this.next_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
-        this._overlay.connect('motion-notify-event', Lang.bind(this, this._onMotion));
-        this._visible = true;
-
-    },
-
-    _onEnterNotify: function() {
-        this._unqueueAutoHide();
-        return false;
-    },
-
-    _onLeaveNotify: function() {
-        this._queueAutoHide();
-        return false;
-    },
-
-    _motionTimeout: function() {
-        this._motionId = 0;
-        this._visibleInternal = true;
-        this._updateVisibility();
-        this._queueAutoHide();
-        return false;
-    },
-
-    _onMotion: function(widget, event) {
-        if (this._motionId != 0)
-            return false;
-
-        let device = event.get_source_device();
-        if (device.input_source == Gdk.InputSource.TOUCHSCREEN)
-            return false;
-
-        this._motionId = Mainloop.idle_add(Lang.bind(this, this._motionTimeout));
-        return false;
-    },
-
-    _onPrevClicked: function() {
-        this._epubView.goPrev();
-    },
-
-    _onNextClicked: function() {
-        this._epubView.goNext();
-    },
-
-    _autoHide: function() {
-        this._autoHideId = 0;
-        this._visibleInternal = false;
-        this._updateVisibility();
-        return false;
-    },
-
-    _unqueueAutoHide: function() {
-        if (this._autoHideId == 0)
-            return;
-
-        Mainloop.source_remove(this._autoHideId);
-        this._autoHideId = 0;
-    },
-
-    _queueAutoHide: function() {
-        this._unqueueAutoHide();
-        this._autoHideId = Mainloop.timeout_add_seconds(_AUTO_HIDE_TIMEOUT, Lang.bind(this, this._autoHide));
-    },
-
-    _updateVisibility: function() {
-        if (!this._epubView)
-            return;
-
-        if (!this._visible || !this._visibleInternal) {
-            this._fadeOutButton(this.prev_widget);
-            this._fadeOutButton(this.next_widget);
-            return;
-        }
-
-        if (this._epubView.page == 1)
-            this._fadeOutButton(this.prev_widget);
-        else
-            this._fadeInButton(this.prev_widget);
-
-        var l = this._epubView._epubSpine.length;
-        if (this._epubView.page >= l)
-            this._fadeOutButton(this.next_widget);
-        else
-            this._fadeInButton(this.next_widget);
-    },
-
-    _fadeInButton: function(widget) {
-        widget.show_all();
-        Tweener.addTween(widget, { opacity: 1,
-                                   time: 0.30,
-                                   transition: 'easeOutQuad' });
-    },
-
-    _fadeOutButton: function(widget) {
-        Tweener.addTween(widget, { opacity: 0,
-                                   time: 0.30,
-                                   transition: 'easeOutQuad',
-                                   onComplete: function() {
-                                       widget.hide();
-                                   },
-                                   onCompleteScope: this });
-    },
-
-    show: function() {
-        this._visible = true;
-        this._visibleInternal = true;
-        this._updateVisibility();
-        this._queueAutoHide();
-    },
-
-    hide: function() {
-        this._visible = false;
-        this._visibleInternal = false;
-        this._updateVisibility();
-    },
-
-    destroy: function() {
-        this.prev_widget.destroy();
-        this.next_widget.destroy();
-    }
-});
diff --git a/src/evinceview.js b/src/evinceview.js
index 07b4de8..6f3f4b2 100644
--- a/src/evinceview.js
+++ b/src/evinceview.js
@@ -22,7 +22,6 @@
 const EvDocument = imports.gi.EvinceDocument;
 const EvView = imports.gi.EvinceView;
 const GdPrivate = imports.gi.GdPrivate;
-const Gdk = imports.gi.Gdk;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
@@ -30,12 +29,12 @@ const _ = imports.gettext.gettext;
 
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
-const Tweener = imports.tweener.tweener;
 
 const Application = imports.application;
 const ErrorBox = imports.errorBox;
 const MainToolbar = imports.mainToolbar;
 const Places = imports.places;
+const Preview = imports.preview;
 const Searchbar = imports.searchbar;
 const Utils = imports.utils;
 const WindowMode = imports.windowMode;
@@ -605,41 +604,50 @@ const EvinceView = new Lang.Class({
 
     get lastSearch() {
         return this._lastSearch;
+    },
+
+    goPrev: function() {
+        this.view.previous_page();
+    },
+
+    goNext: function() {
+        this.view.next_page();
+    },
+
+    get hasPages() {
+        return this._model ? (this._model.document.get_n_pages() > 0) : false;
+    },
+
+    get page() {
+        return this._model ? this._model.page : 0;
+    },
+
+    get numPages() {
+        return this._model ? this._model.document.get_n_pages() : 0;
     }
 });
 Utils.addJSSignalMethods(EvinceView.prototype);
 
-const _PREVIEW_NAVBAR_MARGIN = 30;
-const _AUTO_HIDE_TIMEOUT = 2;
-
 const EvinceViewNavControls = new Lang.Class({
     Name: 'EvinceViewNavControls',
+    Extends: Preview.PreviewNavControls,
 
     _init: function(previewView, overlay) {
+        this._pageChangedId = 0;
+
         this._previewView = previewView;
         this._model = previewView.getModel();
-        this._overlay = overlay;
 
-        this._visible = false;
-        this._visibleInternal = false;
-        this._pageChangedId = 0;
-        this._autoHideId = 0;
-        this._motionId = 0;
-
-        this.bar_widget = new GdPrivate.NavBar({ document_model: this._model,
-                                                 margin: _PREVIEW_NAVBAR_MARGIN,
-                                                 valign: Gtk.Align.END,
-                                                 opacity: 0 });
-        this.bar_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.bar_widget);
-        this.bar_widget.connect('notify::hover', Lang.bind(this, function() {
-            if (this.bar_widget.hover)
-                this._onEnterNotify();
-            else
-                this._onLeaveNotify();
-        }));
+        this.parent(previewView, overlay);
+    },
 
-        let buttonArea = this.bar_widget.get_button_area();
+    createBarWidget: function() {
+        let barWidget = new GdPrivate.NavBar({ document_model: this._model,
+                                               margin: Preview.PREVIEW_NAVBAR_MARGIN,
+                                               valign: Gtk.Align.END,
+                                               opacity: 0 });
+
+        let buttonArea = barWidget.get_button_area();
 
         let button = new Gtk.Button({ action_name: 'app.places',
                                       image: new Gtk.Image({ icon_name: 'view-list-symbolic',
@@ -657,131 +665,7 @@ const EvinceViewNavControls = new Lang.Class({
                                       });
         buttonArea.pack_start(button, false, false, 0);
 
-        this.prev_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-previous-symbolic',
-                                                                    pixel_size: 16 }),
-                                            margin_start: _PREVIEW_NAVBAR_MARGIN,
-                                            margin_end: _PREVIEW_NAVBAR_MARGIN,
-                                            halign: Gtk.Align.START,
-                                            valign: Gtk.Align.CENTER });
-        this.prev_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.prev_widget);
-        this.prev_widget.connect('clicked', Lang.bind(this, this._onPrevClicked));
-        this.prev_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
-        this.prev_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
-
-        this.next_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-next-symbolic',
-                                                                    pixel_size: 16 }),
-                                            margin_start: _PREVIEW_NAVBAR_MARGIN,
-                                            margin_end: _PREVIEW_NAVBAR_MARGIN,
-                                            halign: Gtk.Align.END,
-                                            valign: Gtk.Align.CENTER });
-        this.next_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.next_widget);
-        this.next_widget.connect('clicked', Lang.bind(this, this._onNextClicked));
-        this.next_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
-        this.next_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
-
-        this._overlay.connect('motion-notify-event', Lang.bind(this, this._onMotion));
-
-        this._tapGesture = new Gtk.GestureMultiPress({ propagation_phase: Gtk.PropagationPhase.CAPTURE,
-                                                       touch_only: true,
-                                                       widget: this._previewView.view });
-        this._tapGesture.connect('released', Lang.bind(this, this._onMultiPressReleased));
-        this._tapGesture.connect('stopped', Lang.bind(this, this._onMultiPressStopped));
-    },
-
-    _onEnterNotify: function() {
-        this._unqueueAutoHide();
-        return false;
-    },
-
-    _onLeaveNotify: function() {
-        this._queueAutoHide();
-        return false;
-    },
-
-    _motionTimeout: function() {
-        this._motionId = 0;
-        this._visibleInternal = true;
-        this._updateVisibility();
-        if (!this.bar_widget.hover)
-            this._queueAutoHide();
-        return false;
-    },
-
-    _onMotion: function(widget, event) {
-        if (this._motionId != 0) {
-            return false;
-        }
-
-        let device = event.get_source_device();
-        if (device.input_source == Gdk.InputSource.TOUCHSCREEN) {
-            return false;
-        }
-
-        this._motionId = Mainloop.idle_add(Lang.bind(this, this._motionTimeout));
-        return false;
-    },
-
-    _onMultiPressReleased: function() {
-        this._tapGesture.set_state(Gtk.EventSequenceState.CLAIMED);
-        this._visibleInternal = !this._visibleInternal;
-        this._unqueueAutoHide();
-        this._updateVisibility();
-    },
-
-    _onMultiPressStopped: function() {
-        this._tapGesture.set_state(Gtk.EventSequenceState.DENIED);
-    },
-
-    _onPrevClicked: function() {
-        this._previewView.view.previous_page();
-    },
-
-    _onNextClicked: function() {
-        this._previewView.view.next_page();
-    },
-
-    _autoHide: function() {
-        this._autoHideId = 0;
-        this._visibleInternal = false;
-        this._updateVisibility();
-        return false;
-    },
-
-    _unqueueAutoHide: function() {
-        if (this._autoHideId == 0)
-            return;
-
-        Mainloop.source_remove(this._autoHideId);
-        this._autoHideId = 0;
-    },
-
-    _queueAutoHide: function() {
-        this._unqueueAutoHide();
-        this._autoHideId = Mainloop.timeout_add_seconds(_AUTO_HIDE_TIMEOUT, Lang.bind(this, this._autoHide));
-    },
-
-    _updateVisibility: function() {
-        if (!this._model || !this._visible || !this._visibleInternal) {
-            this._fadeOutButton(this.bar_widget);
-            this._fadeOutButton(this.prev_widget);
-            this._fadeOutButton(this.next_widget);
-            return;
-        }
-
-        this._fadeInButton(this.bar_widget);
-
-        if (this._model.page > 0)
-            this._fadeInButton(this.prev_widget);
-        else
-            this._fadeOutButton(this.prev_widget);
-
-        let doc = this._model.document;
-        if (doc.get_n_pages() > this._model.page + 1)
-            this._fadeInButton(this.next_widget);
-        else
-            this._fadeOutButton(this.next_widget);
+        return barWidget;
     },
 
     setModel: function(model) {
@@ -795,45 +679,6 @@ const EvinceViewNavControls = new Lang.Class({
 
         if (this._model)
             this._pageChangedId = this._model.connect('page-changed', Lang.bind(this, 
this._updateVisibility));
-    },
-
-    _fadeInButton: function(widget) {
-        if (!this._model)
-            return;
-        widget.show_all();
-        Tweener.addTween(widget, { opacity: 1,
-                                   time: 0.30,
-                                   transition: 'easeOutQuad' });
-    },
-
-    _fadeOutButton: function(widget) {
-        Tweener.addTween(widget, { opacity: 0,
-                                   time: 0.30,
-                                   transition: 'easeOutQuad',
-                                   onComplete: function() {
-                                       widget.hide();
-                                   },
-                                   onCompleteScope: this });
-    },
-
-    show: function() {
-        this._visible = true;
-        this._visibleInternal = true;
-        this._updateVisibility();
-        this._queueAutoHide();
-    },
-
-    hide: function() {
-        this._visible = false;
-        this._visibleInternal = false;
-        this._updateVisibility();
-    },
-
-    destroy: function() {
-        this.bar_widget.destroy();
-        this.prev_widget.destroy();
-        this.next_widget.destroy();
-        this._tapGesture = null;
     }
 });
 
@@ -892,22 +737,21 @@ const EvinceViewToolbar = new Lang.Class({
         if (!this._model)
             return;
 
-        let evDoc = this._model.get_document();
-        let hasPages = (evDoc.get_n_pages() > 0);
         let isFind = true;
 
         try {
             // This is a hack to find out if evDoc implements the
             // EvDocument.DocumentFind interface or not. We don't expect
             // the following invocation to work.
+            let evDoc = this._model.get_document();
             evDoc.find_text();
         } catch (e if e instanceof TypeError) {
             isFind = false;
         } catch (e) {
         }
 
-        this._handleEvent = (hasPages && isFind);
-        this._searchAction.enabled = (hasPages && isFind);
+        this._handleEvent = (this.hasPages && isFind);
+        this._searchAction.enabled = (this.hasPages && isFind);
     },
 
     _getEvinceViewMenu: function() {
diff --git a/src/lokview.js b/src/lokview.js
index 2c97d4b..85a4119 100644
--- a/src/lokview.js
+++ b/src/lokview.js
@@ -31,13 +31,12 @@ const Gtk = imports.gi.Gtk;
 const _ = imports.gettext.gettext;
 
 const Lang = imports.lang;
-const Mainloop = imports.mainloop;
 const Signals = imports.signals;
-const Tweener = imports.tweener.tweener;
 
 const Application = imports.application;
 const ErrorBox = imports.errorBox;
 const MainToolbar = imports.mainToolbar;
+const Preview = imports.preview;
 const Documents = imports.documents;
 
 const ZOOM_IN_FACTOR = 1.2;
@@ -199,13 +198,6 @@ const LOKView = new Lang.Class({
 
     open_document_cb: function(res, doc) {
         // TODO: Call _finish and check failure
-        if (isOpenDocumentPartDocument(this._doc.mimeType)) {
-            this.hasParts = true;
-            this.totalParts = this.view.get_parts();
-            this.currentPart = this.view.get_part();
-        } else
-            this.hasParts = false;
-
         this._progressBar.hide();
         this.set_visible_child_name('view');
         this.view.set_edit(false);
@@ -234,7 +226,7 @@ const LOKView = new Lang.Class({
             this.view.connect('notify::can-zoom-out', Lang.bind(this, this._onCanZoomOutChanged));
         }
 
-        this._navControls = new LOKViewNavControls(this, this._overlay);
+        this._navControls = new Preview.PreviewNavControls(this, this._overlay);
         this.set_visible_child_full('view', Gtk.StackTransitionType.NONE);
     },
 
@@ -268,6 +260,39 @@ const LOKView = new Lang.Class({
         this._errorBox.update(primary, secondary);
         this.set_visible_child_name('error');
     },
+
+    goPrev: function() {
+        let currentPart = this.view.get_part();
+        currentPart -= 1;
+        if (currentPart < 0)
+            return;
+        this.view.set_part(currentPart);
+        // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97236
+        this.view.reset_view();
+    },
+
+    goNext: function() {
+        let totalParts  = this.view.get_parts();
+        let currentPart = this.view.get_part();
+        currentPart += 1;
+        if (currentPart > totalParts)
+            return;
+        this.view.set_part(currentPart);
+        // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97236
+        this.view.reset_view();
+    },
+
+    get hasPages() {
+        return isOpenDocumentPartDocument(this._doc.mimeType);
+    },
+
+    get page() {
+        return this.view.get_part();
+    },
+
+    get numPages() {
+        return this.view.get_parts();
+    }
 });
 Signals.addSignalMethods(LOKView.prototype);
 
@@ -353,175 +378,3 @@ const LOKViewToolbar = new Lang.Class({
         this.toolbar.set_title(primary);
     }
 });
-
-const _LOKVIEW_NAVBAR_MARGIN = 30;
-const _AUTO_HIDE_TIMEOUT = 2;
-
-const LOKViewNavControls = new Lang.Class({
-    Name: 'LOKViewNavControls',
-
-    _init: function(lokView, overlay) {
-        this._lokView = lokView;
-        this._overlay = overlay;
-
-        this._visible = false;
-        this._visibleInternal = false;
-        this._pageChangedId = 0;
-        this._autoHideId = 0;
-        this._motionId = 0;
-
-        this.prev_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-previous-symbolic',
-                                                                    pixel_size: 16 }),
-                                            margin_start: _LOKVIEW_NAVBAR_MARGIN,
-                                            margin_end: _LOKVIEW_NAVBAR_MARGIN,
-                                            halign: Gtk.Align.START,
-                                            valign: Gtk.Align.CENTER });
-        this.prev_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.prev_widget);
-        this.prev_widget.connect('clicked', Lang.bind(this, this._onPrevClicked));
-        this.prev_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
-        this.prev_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
-
-        this.next_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-next-symbolic',
-                                                                    pixel_size: 16 }),
-                                            margin_start: _LOKVIEW_NAVBAR_MARGIN,
-                                            margin_end: _LOKVIEW_NAVBAR_MARGIN,
-                                            halign: Gtk.Align.END,
-                                            valign: Gtk.Align.CENTER });
-        this.next_widget.get_style_context().add_class('osd');
-        this._overlay.add_overlay(this.next_widget);
-        this.next_widget.connect('clicked', Lang.bind(this, this._onNextClicked));
-        this.next_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
-        this.next_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
-        this._overlay.connect('motion-notify-event', Lang.bind(this, this._onMotion));
-    },
-
-    _onEnterNotify: function() {
-        this._unqueueAutoHide();
-        return false;
-    },
-
-    _onLeaveNotify: function() {
-        this._queueAutoHide();
-        return false;
-    },
-
-    _motionTimeout: function() {
-        this._motionId = 0;
-        this._visibleInternal = true;
-        this._updateVisibility();
-        this._queueAutoHide();
-        return false;
-    },
-
-    _onMotion: function(widget, event) {
-        if (this._motionId != 0) {
-            return false;
-        }
-
-        let device = event.get_source_device();
-        if (device.input_source == Gdk.InputSource.TOUCHSCREEN) {
-            return false;
-        }
-
-        this._motionId = Mainloop.idle_add(Lang.bind(this, this._motionTimeout));
-        return false;
-    },
-
-    _onPrevClicked: function() {
-        let currentPart = this._lokView.view.get_part();
-        currentPart -= 1;
-        if (currentPart < 0)
-            return;
-        this._lokView.view.set_part(currentPart);
-        // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97236
-        this._lokView.view.reset_view();
-        this._lokView.currentPart = currentPart;
-    },
-
-    _onNextClicked: function() {
-        let totalParts  = this._lokView.view.get_parts();
-        let currentPart = this._lokView.view.get_part();
-        currentPart += 1;
-        if (currentPart > totalParts)
-            return;
-        this._lokView.view.set_part(currentPart);
-        // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97236
-        this._lokView.view.reset_view();
-        this._lokView.currentPart = currentPart;
-    },
-
-    _autoHide: function() {
-        this._autoHideId = 0;
-        this._visibleInternal = false;
-        this._updateVisibility();
-        return false;
-    },
-
-    _unqueueAutoHide: function() {
-        if (this._autoHideId == 0)
-            return;
-
-        Mainloop.source_remove(this._autoHideId);
-        this._autoHideId = 0;
-    },
-
-    _queueAutoHide: function() {
-        this._unqueueAutoHide();
-        //FIXME: disable this temporarily till motion-notify-event works
-        //this._autoHideId = Mainloop.timeout_add_seconds(_AUTO_HIDE_TIMEOUT, Lang.bind(this, 
this._autoHide));
-    },
-
-    _updateVisibility: function() {
-        if (!this._lokView.hasParts) {
-            this._fadeOutButton(this.prev_widget);
-            this._fadeOutButton(this.next_widget);
-            return;
-        }
-
-        if (this._lokView.currentPart > 0)
-            this._fadeInButton(this.prev_widget);
-        else
-            this._fadeOutButton(this.prev_widget);
-
-        if (this._lokView.currentPart < this._lokView.totalParts)
-            this._fadeInButton(this.next_widget);
-        else
-            this._fadeOutButton(this.next_widget);
-    },
-
-    _fadeInButton: function(widget) {
-        widget.show_all();
-        Tweener.addTween(widget, { opacity: 1,
-                                   time: 0.30,
-                                   transition: 'easeOutQuad' });
-    },
-
-    _fadeOutButton: function(widget) {
-        Tweener.addTween(widget, { opacity: 0,
-                                   time: 0.30,
-                                   transition: 'easeOutQuad',
-                                   onComplete: function() {
-                                       widget.hide();
-                                   },
-                                   onCompleteScope: this });
-    },
-
-    show: function() {
-        this._visible = true;
-        this._visibleInternal = true;
-        this._updateVisibility();
-        this._queueAutoHide();
-    },
-
-    hide: function() {
-        this._visible = false;
-        this._visibleInternal = false;
-        this._updateVisibility();
-    },
-
-    destroy: function() {
-        this.prev_widget.destroy();
-        this.next_widget.destroy();
-    }
-});
diff --git a/src/org.gnome.Books.src.gresource.xml b/src/org.gnome.Books.src.gresource.xml
index ec04c5f..e76b30e 100644
--- a/src/org.gnome.Books.src.gresource.xml
+++ b/src/org.gnome.Books.src.gresource.xml
@@ -18,6 +18,7 @@
     <file>password.js</file>
     <file>places.js</file>
     <file>presentation.js</file>
+    <file>preview.js</file>
     <file>evinceview.js</file>
     <file>properties.js</file>
     <file>query.js</file>
diff --git a/src/org.gnome.Documents.src.gresource.xml b/src/org.gnome.Documents.src.gresource.xml
index ea5ff97..44c00e0 100644
--- a/src/org.gnome.Documents.src.gresource.xml
+++ b/src/org.gnome.Documents.src.gresource.xml
@@ -17,6 +17,7 @@
     <file>notifications.js</file>
     <file>password.js</file>
     <file>places.js</file>
+    <file>preview.js</file>
     <file>presentation.js</file>
     <file>evinceview.js</file>
     <file>properties.js</file>
diff --git a/src/preview.js b/src/preview.js
new file mode 100644
index 0000000..3fd5553
--- /dev/null
+++ b/src/preview.js
@@ -0,0 +1,206 @@
+const GdPrivate = imports.gi.GdPrivate;
+const Gdk = imports.gi.Gdk;
+const Gtk = imports.gi.Gtk;
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Tweener = imports.tweener.tweener;
+
+const _AUTO_HIDE_TIMEOUT = 2;
+const PREVIEW_NAVBAR_MARGIN = 30;
+
+const PreviewNavControls = new Lang.Class({
+    Name: 'PreviewNavControls',
+
+    _init: function(preview, overlay) {
+        this._preview = preview;
+        this._overlay = overlay;
+
+        this._visible = false;
+        this._visibleInternal = false;
+        this._pageChangedId = 0;
+        this._autoHideId = 0;
+        this._motionId = 0;
+
+        this.bar_widget = this.createBarWidget();
+        if (this.bar_widget) {
+            this.bar_widget.get_style_context().add_class('osd');
+            this._overlay.add_overlay(this.bar_widget);
+            this.bar_widget.connect('notify::hover', Lang.bind(this, function() {
+                if (this.bar_widget.hover)
+                    this._onEnterNotify();
+                else
+                    this._onLeaveNotify();
+            }));
+        }
+
+        this.prev_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-previous-symbolic',
+                                                                    pixel_size: 16 }),
+                                            margin_start: PREVIEW_NAVBAR_MARGIN,
+                                            margin_end: PREVIEW_NAVBAR_MARGIN,
+                                            halign: Gtk.Align.START,
+                                            valign: Gtk.Align.CENTER });
+        this.prev_widget.get_style_context().add_class('osd');
+        this._overlay.add_overlay(this.prev_widget);
+        this.prev_widget.connect('clicked', Lang.bind(this, this._onPrevClicked));
+        this.prev_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
+        this.prev_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
+
+        this.next_widget = new Gtk.Button({ image: new Gtk.Image ({ icon_name: 'go-next-symbolic',
+                                                                    pixel_size: 16 }),
+                                            margin_start: PREVIEW_NAVBAR_MARGIN,
+                                            margin_end: PREVIEW_NAVBAR_MARGIN,
+                                            halign: Gtk.Align.END,
+                                            valign: Gtk.Align.CENTER });
+        this.next_widget.get_style_context().add_class('osd');
+        this._overlay.add_overlay(this.next_widget);
+        this.next_widget.connect('clicked', Lang.bind(this, this._onNextClicked));
+        this.next_widget.connect('enter-notify-event', Lang.bind(this, this._onEnterNotify));
+        this.next_widget.connect('leave-notify-event', Lang.bind(this, this._onLeaveNotify));
+
+        this._overlay.connect('motion-notify-event', Lang.bind(this, this._onMotion));
+
+        this._tapGesture = new Gtk.GestureMultiPress({ propagation_phase: Gtk.PropagationPhase.CAPTURE,
+                                                       touch_only: true,
+                                                       widget: this._preview.view });
+        this._tapGesture.connect('released', Lang.bind(this, this._onMultiPressReleased));
+        this._tapGesture.connect('stopped', Lang.bind(this, this._onMultiPressStopped));
+    },
+
+    createBarWidget: function() {
+        return null;
+    },
+
+    _onEnterNotify: function() {
+        this._unqueueAutoHide();
+        return false;
+    },
+
+    _onLeaveNotify: function() {
+        this._queueAutoHide();
+        return false;
+    },
+
+    _motionTimeout: function() {
+        this._motionId = 0;
+        this._visibleInternal = true;
+        this._updateVisibility();
+        if (this.bar_widget && !this.bar_widget.hover)
+            this._queueAutoHide();
+        return false;
+    },
+
+    _onMotion: function(widget, event) {
+        if (this._motionId != 0)
+            return false;
+
+        let device = event.get_source_device();
+        if (device.input_source == Gdk.InputSource.TOUCHSCREEN)
+            return false;
+
+        this._motionId = Mainloop.idle_add(Lang.bind(this, this._motionTimeout));
+        return false;
+    },
+
+    _onMultiPressReleased: function() {
+        this._tapGesture.set_state(Gtk.EventSequenceState.CLAIMED);
+        this._visibleInternal = !this._visibleInternal;
+        this._unqueueAutoHide();
+        this._updateVisibility();
+    },
+
+    _onMultiPressStopped: function() {
+        this._tapGesture.set_state(Gtk.EventSequenceState.DENIED);
+    },
+
+    _onPrevClicked: function() {
+        this._preview.goPrev();
+    },
+
+    _onNextClicked: function() {
+        this._preview.goNext();
+    },
+
+    _autoHide: function() {
+        this._autoHideId = 0;
+        this._visibleInternal = false;
+        this._updateVisibility();
+        return false;
+    },
+
+    _unqueueAutoHide: function() {
+        if (this._autoHideId == 0)
+            return;
+
+        Mainloop.source_remove(this._autoHideId);
+        this._autoHideId = 0;
+    },
+
+    _queueAutoHide: function() {
+        this._unqueueAutoHide();
+        this._autoHideId = Mainloop.timeout_add_seconds(_AUTO_HIDE_TIMEOUT, Lang.bind(this, this._autoHide));
+    },
+
+    _updateVisibility: function() {
+        let currentPage = this._preview.page;
+        let numPages = this._preview.numPages;
+
+        if (!this._visible || !this._visibleInternal || !this._preview.hasPages) {
+            if (this.bar_widget)
+                this._fadeOutButton(this.bar_widget);
+            this._fadeOutButton(this.prev_widget);
+            this._fadeOutButton(this.next_widget);
+            return;
+        }
+
+        if (this.bar_widget)
+            this._fadeInButton(this.bar_widget);
+
+        if (currentPage > 0)
+            this._fadeInButton(this.prev_widget);
+        else
+            this._fadeOutButton(this.prev_widget);
+
+        if (numPages > currentPage + 1)
+            this._fadeInButton(this.next_widget);
+        else
+            this._fadeOutButton(this.next_widget);
+    },
+
+    _fadeInButton: function(widget) {
+        widget.show_all();
+        Tweener.addTween(widget, { opacity: 1,
+                                   time: 0.30,
+                                   transition: 'easeOutQuad' });
+    },
+
+    _fadeOutButton: function(widget) {
+        Tweener.addTween(widget, { opacity: 0,
+                                   time: 0.30,
+                                   transition: 'easeOutQuad',
+                                   onComplete: function() {
+                                       widget.hide();
+                                   }});
+    },
+
+    show: function() {
+        this._visible = true;
+        this._visibleInternal = true;
+        this._updateVisibility();
+        this._queueAutoHide();
+    },
+
+    hide: function() {
+        this._visible = false;
+        this._visibleInternal = false;
+        this._updateVisibility();
+    },
+
+    destroy: function() {
+        if (this.bar_widget)
+            this.bar_widget.destroy();
+        this.prev_widget.destroy();
+        this.next_widget.destroy();
+        this._tapGesture = null;
+    }
+});


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]