Chromium Code Reviews| 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..10fe725687ee8fb40204e00b002721383c11ab11 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 TouchHandlers(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_.enable = 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_.enable = 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_.isZooming()) { |
| 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_.isZooming()) { |
| 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_.enable = 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_.enable = 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_.enable = false; |
| } else { |
| this.editor_.getPrompt().hide(); |
| this.editor_.leaveModeGently(); |
| + this.touchHandlers_.enable = 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,202 @@ 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). |
| - * @constructor |
| - * @implements {ImageBuffer.Overlay} |
| + * Apply the change of viewport. |
| */ |
| -function SwipeOverlay(callback) { |
| - this.callback_ = callback; |
| +SlideMode.prototype.applyViewportChange = function() { |
| + this.imageView_.applyViewportChange(); |
| +}; |
| + |
| +/** |
|
mtomasz
2014/07/23 08:15:08
@param, @constructor missing
hirono
2014/07/23 09:27:23
Done.
|
| + * Touch handlers of the slide mode. |
| + */ |
| +function TouchHandlers(targetElement, slideMode) { |
|
mtomasz
2014/07/23 08:15:08
I'm wondering if the plural naming is fine. How ab
hirono
2014/07/23 09:27:23
Done.
|
| + /** |
| + * Event source. |
| + * @type {DOMElement} |
| + */ |
| + this.targetElement_ = targetElement; |
| + |
| + /** |
| + * Target of touch operations. |
| + * @type {SlideMode} |
| + * @private |
| + */ |
| + this.slideMode_ = slideMode; |
| + |
| + /** |
| + * Flag to enable/disable touch operation. |
| + */ |
| + this.enable_ = true; |
|
mtomasz
2014/07/23 08:15:08
nit: enabled?
hirono
2014/07/23 09:27:23
Done.
|
| + |
| + /** |
| + * Whether it is in a touch operation that is started from targetElement or |
| + * not. |
| + * @type {boolean} |
| + * @private |
| + */ |
| + this.inTouch_ = false; |
|
mtomasz
2014/07/23 08:15:08
nit: How about simply touchStarted?
hirono
2014/07/23 09:27:23
Done.
|
| + |
| + /** |
| + * 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; |
| + |
| + var onTouchEventBound = this.onTouchEvent_.bind(this); |
| + targetElement.addEventListener('touchstart', onTouchEventBound); |
| + 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; |
| +TouchHandlers.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); |
| +TouchHandlers.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); |
| +}; |
| + |
| +TouchHandlers.prototype = { |
| + /** |
| + * @param {boolean} flag New value. |
| + */ |
| + set enable(flag) { |
| + this.enable_ = flag; |
| + if (!this.enable_) |
| + 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. |
| + */ |
| +TouchHandlers.prototype.stopOperation = function() { |
| + this.inTouch_ = false; |
| + this.done_ = false; |
| + this.gestureStartEvent_ = null; |
| + this.lastEvent_ = null; |
| + this.lastZoom_ = 1.0; |
| +}; |
| + |
| +/** |
| + * @param {event} event Touch event. |
| */ |
| -SwipeOverlay.SWIPE_THRESHOLD = 100; |
| +TouchHandlers.prototype.onTouchEvent_ = function(event) { |
| + // Check if the current touch operation started from the target element or |
| + // not. |
| + if (!this.inTouch_) { |
| + // Check if a touch operation starts with the event. |
| + if (this.enable_ && |
| + event.type === 'touchstart' && |
|
mtomasz
2014/07/23 08:15:08
nit: Why not splitting this to a separate method?
hirono
2014/07/23 09:27:23
Done.
|
| + event.touches.length === 1) { |
| + this.inTouch_ = true; |
| + } else { |
| + return; |
| + } |
| + } else { |
| + // Check if the current touch operaiton ends with the event. |
|
mtomasz
2014/07/23 08:15:08
typo: operation
hirono
2014/07/23 09:27:23
Done.
|
| + 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.isZooming()) { |
| + // 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 > TouchHandlers.SWIPE_THRESHOLD) { |
| + this.slideMode_.advanceManually(-1); |
| + this.done_ = true; |
| + } else if (dx < -TouchHandlers.SWIPE_THRESHOLD) { |
| + this.slideMode_.advanceManually(1); |
| + this.done_ = true; |
| + } |
| + } |
| + break; |
| + |
| + case 2: |
| + // Pinch zoom. |
| + var distance1 = TouchHandlers.getDistance(this.lastEvent_); |
| + var distance2 = TouchHandlers.getDistance(event); |
| + if (distance1 == 0.0) |
|
mtomasz
2014/07/23 08:15:08
nit: Simply === 0?
hirono
2014/07/23 09:27:23
Done.
|
| + 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(); |
| +}; |