Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Unified Diff: ui/file_manager/gallery/js/slide_mode.js

Issue 411853002: Gallery.app: Add touch handlers for the zoom/scroll feature. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();
+};

Powered by Google App Engine
This is Rietveld 408576698