| Index: ui/file_manager/gallery/js/slide_mode.js
|
| diff --git a/ui/file_manager/gallery/js/slide_mode.js b/ui/file_manager/gallery/js/slide_mode.js
|
| index fdc77f222e6956d1cfaf8f4ff30f92525918213e..4c480f4fdd3f122edf5ebf8864ced53100cb5edb 100644
|
| --- a/ui/file_manager/gallery/js/slide_mode.js
|
| +++ b/ui/file_manager/gallery/js/slide_mode.js
|
| @@ -91,6 +91,11 @@ SlideMode.prototype.getName = function() { return 'slide'; };
|
| SlideMode.prototype.getTitle = function() { return 'GALLERY_SLIDE'; };
|
|
|
| /**
|
| + * @return {Viewport} Viewport.
|
| + */
|
| +SlideMode.prototype.getViewport = function() { return this.viewport_; };
|
| +
|
| +/**
|
| * Initialize the listeners.
|
| * @private
|
| */
|
| @@ -232,8 +237,7 @@ SlideMode.prototype.initDom_ = function() {
|
| this.displayStringFunction_,
|
| this.onToolsVisibilityChanged_.bind(this));
|
|
|
| - this.editor_.getBuffer().addOverlay(
|
| - new SwipeOverlay(this.advanceManually.bind(this)));
|
| + this.touchHandlers_ = new TouchHandler(this.imageContainer_, this);
|
| };
|
|
|
| /**
|
| @@ -311,6 +315,7 @@ SlideMode.prototype.enter = function(
|
| this.selectionModel_.addEventListener('change', this.onSelectionBound_);
|
| this.dataModel_.addEventListener('splice', this.onSpliceBound_);
|
| this.dataModel_.addEventListener('content', this.onContentBound_);
|
| + this.touchHandlers_.enabled = true;
|
|
|
| // Wait 1000ms after the animation is done, then prefetch the next image.
|
| this.requestPrefetch(1, delay + 1000);
|
| @@ -357,6 +362,9 @@ SlideMode.prototype.leave = function(zoomToRect, callback) {
|
| // Disable the slide-mode only buttons when leaving.
|
| this.editButton_.setAttribute('disabled', '');
|
| this.printButton_.setAttribute('disabled', '');
|
| +
|
| + // Disable touch operation.
|
| + this.touchHandlers_.enabled = false;
|
| };
|
|
|
|
|
| @@ -860,8 +868,9 @@ SlideMode.prototype.onKeyDown = function(event) {
|
| case 'U+001B': // Escape
|
| if (this.isEditing()) {
|
| this.toggleEditor(event);
|
| - } else if (this.viewport_.getZoomIndex() !== 0) {
|
| + } else if (this.viewport_.isZoomed()) {
|
| this.viewport_.resetView();
|
| + this.touchHandlers_.stopOperation();
|
| this.imageView_.applyViewportChange();
|
| } else {
|
| return false; // Not handled.
|
| @@ -878,14 +887,14 @@ SlideMode.prototype.onKeyDown = function(event) {
|
| case 'Down':
|
| case 'Left':
|
| case 'Right':
|
| - if (!this.isEditing() && this.viewport_.getZoomIndex() !== 0) {
|
| + if (!this.isEditing() && this.viewport_.isZoomed()) {
|
| var delta = SlideMode.KEY_OFFSET_MAP[keyID];
|
| this.viewport_.setOffset(
|
| ~~(this.viewport_.getOffsetX() +
|
| delta[0] * this.viewport_.getZoom()),
|
| ~~(this.viewport_.getOffsetY() +
|
| - delta[1] * this.viewport_.getZoom()),
|
| - true);
|
| + delta[1] * this.viewport_.getZoom()));
|
| + this.touchHandlers_.stopOperation();
|
| this.imageView_.applyViewportChange();
|
| } else {
|
| this.advanceWithKeyboard(keyID);
|
| @@ -898,21 +907,24 @@ SlideMode.prototype.onKeyDown = function(event) {
|
|
|
| case 'Ctrl-U+00BB': // Ctrl+'=' zoom in.
|
| if (!this.isEditing()) {
|
| - this.viewport_.setZoomIndex(this.viewport_.getZoomIndex() + 1);
|
| + this.viewport_.zoomIn();
|
| + this.touchHandlers_.stopOperation();
|
| this.imageView_.applyViewportChange();
|
| }
|
| break;
|
|
|
| case 'Ctrl-U+00BD': // Ctrl+'-' zoom out.
|
| if (!this.isEditing()) {
|
| - this.viewport_.setZoomIndex(this.viewport_.getZoomIndex() - 1);
|
| + this.viewport_.zoomOut();
|
| + this.touchHandlers_.stopOperation();
|
| this.imageView_.applyViewportChange();
|
| }
|
| break;
|
|
|
| case 'Ctrl-U+0030': // Ctrl+'0' zoom reset.
|
| if (!this.isEditing()) {
|
| - this.viewport_.resetView();
|
| + this.viewport_.setZoom(1.0);
|
| + this.touchHandlers_.stopOperation();
|
| this.imageView_.applyViewportChange();
|
| }
|
| break;
|
| @@ -928,6 +940,7 @@ SlideMode.prototype.onKeyDown = function(event) {
|
| SlideMode.prototype.onResize_ = function() {
|
| this.viewport_.setScreenSize(
|
| this.container_.clientWidth, this.container_.clientHeight);
|
| + this.touchHandlers_.stopOperation();
|
| this.editor_.getBuffer().draw();
|
| };
|
|
|
| @@ -1077,7 +1090,7 @@ SlideMode.prototype.isSlideshowOn_ = function() {
|
| };
|
|
|
| /**
|
| - * Start the slideshow.
|
| + * Starts the slideshow.
|
| * @param {number=} opt_interval First interval in ms.
|
| * @param {Event=} opt_event Event.
|
| */
|
| @@ -1086,6 +1099,9 @@ SlideMode.prototype.startSlideshow = function(opt_interval, opt_event) {
|
| this.viewport_.resetView();
|
| this.imageView_.applyViewportChange();
|
|
|
| + // Disable touch operation.
|
| + this.touchHandlers_.enabled = false;
|
| +
|
| // Set the attribute early to prevent the toolbar from flashing when
|
| // the slideshow is being started from the mosaic view.
|
| this.container_.setAttribute('slideshow', 'playing');
|
| @@ -1116,7 +1132,7 @@ SlideMode.prototype.startSlideshow = function(opt_interval, opt_event) {
|
| };
|
|
|
| /**
|
| - * Stop the slideshow.
|
| + * Stops the slideshow.
|
| * @param {Event=} opt_event Event.
|
| * @private
|
| */
|
| @@ -1141,6 +1157,9 @@ SlideMode.prototype.stopSlideshow_ = function(opt_event) {
|
| this.leaveAfterSlideshow_ = false;
|
| setTimeout(this.toggleMode_.bind(this), toggleModeDelay);
|
| }
|
| +
|
| + // Re-enable touch operation.
|
| + this.touchHandlers_.enabled = true;
|
| };
|
|
|
| /**
|
| @@ -1152,7 +1171,7 @@ SlideMode.prototype.isSlideshowPlaying_ = function() {
|
| };
|
|
|
| /**
|
| - * Pause/resume the slideshow.
|
| + * Pauses/resumes the slideshow.
|
| * @private
|
| */
|
| SlideMode.prototype.toggleSlideshowPause_ = function() {
|
| @@ -1182,7 +1201,7 @@ SlideMode.prototype.scheduleNextSlide_ = function(opt_interval) {
|
| };
|
|
|
| /**
|
| - * Resume the slideshow.
|
| + * Resumes the slideshow.
|
| * @param {number=} opt_interval Slideshow interval in ms.
|
| * @private
|
| */
|
| @@ -1192,7 +1211,7 @@ SlideMode.prototype.resumeSlideshow_ = function(opt_interval) {
|
| };
|
|
|
| /**
|
| - * Pause the slideshow.
|
| + * Pauses the slideshow.
|
| * @private
|
| */
|
| SlideMode.prototype.pauseSlideshow_ = function() {
|
| @@ -1211,7 +1230,7 @@ SlideMode.prototype.isEditing = function() {
|
| };
|
|
|
| /**
|
| - * Stop editing.
|
| + * Stops editing.
|
| * @private
|
| */
|
| SlideMode.prototype.stopEditing_ = function() {
|
| @@ -1244,9 +1263,11 @@ SlideMode.prototype.toggleEditor = function(opt_event) {
|
| this.editor_.getPrompt().showAt(
|
| 'top', 'GALLERY_READONLY_WARNING', 0, this.context_.readonlyDirName);
|
| }
|
| + this.touchHandlers_.enabled = false;
|
| } else {
|
| this.editor_.getPrompt().hide();
|
| this.editor_.leaveModeGently();
|
| + this.touchHandlers_.enabled = true;
|
| }
|
| };
|
|
|
| @@ -1260,7 +1281,7 @@ SlideMode.prototype.print_ = function() {
|
| };
|
|
|
| /**
|
| - * Display the error banner.
|
| + * Displays the error banner.
|
| * @param {string} message Message.
|
| * @private
|
| */
|
| @@ -1272,7 +1293,7 @@ SlideMode.prototype.showErrorBanner_ = function(message) {
|
| };
|
|
|
| /**
|
| - * Show/hide the busy spinner.
|
| + * Shows/hides the busy spinner.
|
| *
|
| * @param {boolean} on True if show, false if hide.
|
| * @private
|
| @@ -1294,45 +1315,203 @@ SlideMode.prototype.showSpinner_ = function(on) {
|
| };
|
|
|
| /**
|
| - * Overlay that handles swipe gestures. Changes to the next or previous file.
|
| - * @param {function(number)} callback A callback accepting the swipe direction
|
| - * (1 means left, -1 right).
|
| + * Apply the change of viewport.
|
| + */
|
| +SlideMode.prototype.applyViewportChange = function() {
|
| + this.imageView_.applyViewportChange();
|
| +};
|
| +
|
| +/**
|
| + * Touch handlers of the slide mode.
|
| + * @param {DOMElement} targetElement Event source.
|
| + * @param {SlideMode} slideMode Slide mode to be operated by the handler.
|
| * @constructor
|
| - * @implements {ImageBuffer.Overlay}
|
| */
|
| -function SwipeOverlay(callback) {
|
| - this.callback_ = callback;
|
| +function TouchHandler(targetElement, slideMode) {
|
| + /**
|
| + * Event source.
|
| + * @type {DOMElement}
|
| + */
|
| + this.targetElement_ = targetElement;
|
| +
|
| + /**
|
| + * Target of touch operations.
|
| + * @type {SlideMode}
|
| + * @private
|
| + */
|
| + this.slideMode_ = slideMode;
|
| +
|
| + /**
|
| + * Flag to enable/disable touch operation.
|
| + * @type {boolean}
|
| + */
|
| + this.enabled_ = true;
|
| +
|
| + /**
|
| + * Whether it is in a touch operation that is started from targetElement or
|
| + * not.
|
| + * @type {boolean}
|
| + * @private
|
| + */
|
| + this.touchStarted_ = false;
|
| +
|
| + /**
|
| + * The swipe action that should happen only once in an operation is already
|
| + * done or not.
|
| + * @type {boolean}
|
| + * @private
|
| + */
|
| + this.done_ = false;
|
| +
|
| + /**
|
| + * Event on beginning of the current gesture.
|
| + * The variable is updated when the number of touch finger changed.
|
| + * @type {TouchEvent}
|
| + * @private
|
| + */
|
| + this.gestureStartEvent_ = null;
|
| +
|
| + /**
|
| + * Last touch event.
|
| + * @type {TouchEvent}
|
| + * @private
|
| + */
|
| + this.lastEvent_ = null;
|
| +
|
| + /**
|
| + * Zoom value just after last touch event.
|
| + * @type {number}
|
| + * @private
|
| + */
|
| + this.lastZoom_ = 1.0;
|
| +
|
| + targetElement.addEventListener('touchstart', this.onTouchStart_.bind(this));
|
| + var onTouchEventBound = this.onTouchEvent_.bind(this);
|
| + targetElement.ownerDocument.addEventListener('touchmove', onTouchEventBound);
|
| + targetElement.ownerDocument.addEventListener('touchend', onTouchEventBound);
|
| }
|
|
|
| /**
|
| - * Inherit ImageBuffer.Overlay.
|
| + * If the user touched the image and moved the finger more than SWIPE_THRESHOLD
|
| + * horizontally it's considered as a swipe gesture (change the current image).
|
| + * @type {number}
|
| + * @const
|
| */
|
| -SwipeOverlay.prototype.__proto__ = ImageBuffer.Overlay.prototype;
|
| +TouchHandler.SWIPE_THRESHOLD = 100;
|
|
|
| /**
|
| - * @param {number} x X pointer position.
|
| - * @param {number} y Y pointer position.
|
| - * @param {boolean} touch True if dragging caused by touch.
|
| - * @return {function} The closure to call on drag.
|
| + * Obtains distance between fingers.
|
| + * @param {TouchEvent} event Touch event. It should include more than two
|
| + * touches.
|
| + * @return {boolean} Distance between touch[0] and touch[1].
|
| */
|
| -SwipeOverlay.prototype.getDragHandler = function(x, y, touch) {
|
| - if (!touch)
|
| - return null;
|
| - var origin = x;
|
| - var done = false;
|
| - return function(x, y) {
|
| - if (!done && origin - x > SwipeOverlay.SWIPE_THRESHOLD) {
|
| - this.callback_(1);
|
| - done = true;
|
| - } else if (!done && x - origin > SwipeOverlay.SWIPE_THRESHOLD) {
|
| - this.callback_(-1);
|
| - done = true;
|
| - }
|
| - }.bind(this);
|
| +TouchHandler.getDistance = function(event) {
|
| + var touch1 = event.touches[0];
|
| + var touch2 = event.touches[1];
|
| + var dx = touch1.clientX - touch2.clientX;
|
| + var dy = touch1.clientY - touch2.clientY;
|
| + return Math.sqrt(dx * dx + dy * dy);
|
| +};
|
| +
|
| +TouchHandler.prototype = {
|
| + /**
|
| + * @param {boolean} flag New value.
|
| + */
|
| + set enabled(flag) {
|
| + this.enabled_ = flag;
|
| + if (!this.enabled_)
|
| + this.stopOperation();
|
| + }
|
| };
|
|
|
| /**
|
| - * If the user touched the image and moved the finger more than SWIPE_THRESHOLD
|
| - * horizontally it's considered as a swipe gesture (change the current image).
|
| + * Stops the current touch operation.
|
| */
|
| -SwipeOverlay.SWIPE_THRESHOLD = 100;
|
| +TouchHandler.prototype.stopOperation = function() {
|
| + this.touchStarted_ = false;
|
| + this.done_ = false;
|
| + this.gestureStartEvent_ = null;
|
| + this.lastEvent_ = null;
|
| + this.lastZoom_ = 1.0;
|
| +};
|
| +
|
| +TouchHandler.prototype.onTouchStart_ = function(event) {
|
| + if (this.enabled_ && event.touches.length === 1)
|
| + this.touchStarted_ = true;
|
| +};
|
| +
|
| +/**
|
| + * @param {event} event Touch event.
|
| + */
|
| +TouchHandler.prototype.onTouchEvent_ = function(event) {
|
| + // Check if the current touch operation started from the target element or
|
| + // not.
|
| + if (!this.touchStarted_)
|
| + return;
|
| +
|
| + // Check if the current touch operation ends with the event.
|
| + if (event.touches.length === 0) {
|
| + this.stopOperation();
|
| + return;
|
| + }
|
| +
|
| + // Check if a new gesture started or not.
|
| + if (!this.lastEvent_ ||
|
| + this.lastEvent_.touches.length !== event.touches.length) {
|
| + if (event.touches.length === 2 ||
|
| + event.touches.length === 1) {
|
| + this.gestureStartEvent_ = event;
|
| + this.lastEvent_ = event;
|
| + this.lastZoom_ = this.slideMode_.getViewport().getZoom();
|
| + } else {
|
| + this.gestureStartEvent_ = null;
|
| + this.lastEvent_ = null;
|
| + this.lastZoom_ = 1.0;
|
| + }
|
| + return;
|
| + }
|
| +
|
| + // Handle the gesture movement.
|
| + var viewport = this.slideMode_.getViewport();
|
| + switch (event.touches.length) {
|
| + case 1:
|
| + if (viewport.isZoomed()) {
|
| + // Scrolling an image by swipe.
|
| + var dx = event.touches[0].screenX - this.lastEvent_.touches[0].screenX;
|
| + var dy = event.touches[0].screenY - this.lastEvent_.touches[0].screenY;
|
| + viewport.setOffset(
|
| + viewport.getOffsetX() + dx, viewport.getOffsetY() + dy);
|
| + this.slideMode_.applyViewportChange();
|
| + } else {
|
| + // Traversing images by swipe.
|
| + if (this.done_)
|
| + break;
|
| + var dx =
|
| + event.touches[0].clientX -
|
| + this.gestureStartEvent_.touches[0].clientX;
|
| + if (dx > TouchHandler.SWIPE_THRESHOLD) {
|
| + this.slideMode_.advanceManually(-1);
|
| + this.done_ = true;
|
| + } else if (dx < -TouchHandler.SWIPE_THRESHOLD) {
|
| + this.slideMode_.advanceManually(1);
|
| + this.done_ = true;
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 2:
|
| + // Pinch zoom.
|
| + var distance1 = TouchHandler.getDistance(this.lastEvent_);
|
| + var distance2 = TouchHandler.getDistance(event);
|
| + if (distance1 === 0)
|
| + break;
|
| + var zoom = distance2 / distance1 * this.lastZoom_;
|
| + viewport.setZoom(zoom);
|
| + this.slideMode_.applyViewportChange();
|
| + break;
|
| + }
|
| +
|
| + // Update the last event.
|
| + this.lastEvent_ = event;
|
| + this.lastZoom_ = viewport.getZoom();
|
| +};
|
|
|