Title: [98460] trunk/Source/WebKit2
Revision
98460
Author
abe...@webkit.org
Date
2011-10-26 02:49:21 -0700 (Wed, 26 Oct 2011)

Log Message

[Qt][WK2] Add the animations on the ViewportInteractionEngine
https://bugs.webkit.org/show_bug.cgi?id=66664

Reviewed by Kenneth Rohde Christiansen.

Introduce a QScroller based panning and a QVariantAnimation based scale animation.
QScroller also handles flick gestures and animates overshoot.
If a pinch gesture ends, the scale animation will scale and reposition the content
such that it is within valid boundaries. The content is by default animated such
that the content position as seen in the center of the visible viewport stays
in the center, unless the content would be out of valid bounds.
The patch also simplifies the ViewportUpdateGuard by using reference counting
instead of a boolean for defering update requests.

* UIProcess/qt/QtPanGestureRecognizer.cpp:
(WebKit::QtPanGestureRecognizer::recognize):
* UIProcess/qt/QtPanGestureRecognizer.h:
* UIProcess/qt/QtPinchGestureRecognizer.cpp:
(WebKit::QtPinchGestureRecognizer::recognize):
* UIProcess/qt/QtTouchWebPageProxy.cpp:
(QtTouchWebPageProxy::doneWithTouchEvent):
* UIProcess/qt/QtViewportInteractionEngine.cpp:
(WebKit::bindToScaleLimits):
(WebKit::ViewportUpdateGuard::ViewportUpdateGuard):
(WebKit::ViewportUpdateGuard::~ViewportUpdateGuard):
(WebKit::QtViewportInteractionEngine::QtViewportInteractionEngine):
(WebKit::QtViewportInteractionEngine::innerBoundedScale):
(WebKit::QtViewportInteractionEngine::outerBoundedScale):
(WebKit::QtViewportInteractionEngine::updateVisibleRect):
(WebKit::QtViewportInteractionEngine::scaleAnimationStateChanged):
(WebKit::QtViewportInteractionEngine::event):
(WebKit::QtViewportInteractionEngine::stopAnimations):
(WebKit::QtViewportInteractionEngine::calculateBoundariesForScale):
(WebKit::QtViewportInteractionEngine::animateContentIntoBoundariesIfNeeded):
(WebKit::QtViewportInteractionEngine::reset):
(WebKit::QtViewportInteractionEngine::setConstraints):
(WebKit::QtViewportInteractionEngine::panGestureStarted):
(WebKit::QtViewportInteractionEngine::panGestureRequestUpdate):
(WebKit::QtViewportInteractionEngine::panGestureCancelled):
(WebKit::QtViewportInteractionEngine::panGestureEnded):
(WebKit::QtViewportInteractionEngine::pinchGestureStarted):
(WebKit::QtViewportInteractionEngine::pinchGestureRequestUpdate):
(WebKit::QtViewportInteractionEngine::pinchGestureEnded):
(WebKit::QtViewportInteractionEngine::contentViewportChanged):
* UIProcess/qt/QtViewportInteractionEngine.h:
(WebKit::QtViewportInteractionEngine::scroller):
(WebKit::QtViewportInteractionEngine::ScaleAnimation::ScaleAnimation):
(WebKit::QtViewportInteractionEngine::ScaleAnimation::updateCurrentValue):

Modified Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (98459 => 98460)


--- trunk/Source/WebKit2/ChangeLog	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/ChangeLog	2011-10-26 09:49:21 UTC (rev 98460)
@@ -1,3 +1,54 @@
+2011-10-25  Andras Becsi  <andras.be...@nokia.com>
+
+        [Qt][WK2] Add the animations on the ViewportInteractionEngine
+        https://bugs.webkit.org/show_bug.cgi?id=66664
+
+        Reviewed by Kenneth Rohde Christiansen.
+
+        Introduce a QScroller based panning and a QVariantAnimation based scale animation.
+        QScroller also handles flick gestures and animates overshoot.
+        If a pinch gesture ends, the scale animation will scale and reposition the content
+        such that it is within valid boundaries. The content is by default animated such
+        that the content position as seen in the center of the visible viewport stays
+        in the center, unless the content would be out of valid bounds.
+        The patch also simplifies the ViewportUpdateGuard by using reference counting
+        instead of a boolean for defering update requests.
+
+        * UIProcess/qt/QtPanGestureRecognizer.cpp:
+        (WebKit::QtPanGestureRecognizer::recognize):
+        * UIProcess/qt/QtPanGestureRecognizer.h:
+        * UIProcess/qt/QtPinchGestureRecognizer.cpp:
+        (WebKit::QtPinchGestureRecognizer::recognize):
+        * UIProcess/qt/QtTouchWebPageProxy.cpp:
+        (QtTouchWebPageProxy::doneWithTouchEvent):
+        * UIProcess/qt/QtViewportInteractionEngine.cpp:
+        (WebKit::bindToScaleLimits):
+        (WebKit::ViewportUpdateGuard::ViewportUpdateGuard):
+        (WebKit::ViewportUpdateGuard::~ViewportUpdateGuard):
+        (WebKit::QtViewportInteractionEngine::QtViewportInteractionEngine):
+        (WebKit::QtViewportInteractionEngine::innerBoundedScale):
+        (WebKit::QtViewportInteractionEngine::outerBoundedScale):
+        (WebKit::QtViewportInteractionEngine::updateVisibleRect):
+        (WebKit::QtViewportInteractionEngine::scaleAnimationStateChanged):
+        (WebKit::QtViewportInteractionEngine::event):
+        (WebKit::QtViewportInteractionEngine::stopAnimations):
+        (WebKit::QtViewportInteractionEngine::calculateBoundariesForScale):
+        (WebKit::QtViewportInteractionEngine::animateContentIntoBoundariesIfNeeded):
+        (WebKit::QtViewportInteractionEngine::reset):
+        (WebKit::QtViewportInteractionEngine::setConstraints):
+        (WebKit::QtViewportInteractionEngine::panGestureStarted):
+        (WebKit::QtViewportInteractionEngine::panGestureRequestUpdate):
+        (WebKit::QtViewportInteractionEngine::panGestureCancelled):
+        (WebKit::QtViewportInteractionEngine::panGestureEnded):
+        (WebKit::QtViewportInteractionEngine::pinchGestureStarted):
+        (WebKit::QtViewportInteractionEngine::pinchGestureRequestUpdate):
+        (WebKit::QtViewportInteractionEngine::pinchGestureEnded):
+        (WebKit::QtViewportInteractionEngine::contentViewportChanged):
+        * UIProcess/qt/QtViewportInteractionEngine.h:
+        (WebKit::QtViewportInteractionEngine::scroller):
+        (WebKit::QtViewportInteractionEngine::ScaleAnimation::ScaleAnimation):
+        (WebKit::QtViewportInteractionEngine::ScaleAnimation::updateCurrentValue):
+
 2011-10-26  Zeno Albisser  <zeno.albis...@nokia.com>
 
         [Qt][WK2] Rewrite MiniBrowser in QML

Modified: trunk/Source/WebKit2/UIProcess/qt/QtPanGestureRecognizer.cpp (98459 => 98460)


--- trunk/Source/WebKit2/UIProcess/qt/QtPanGestureRecognizer.cpp	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/UIProcess/qt/QtPanGestureRecognizer.cpp	2011-10-26 09:49:21 UTC (rev 98460)
@@ -37,7 +37,7 @@
     reset();
 }
 
-bool QtPanGestureRecognizer::recognize(const QTouchEvent* event)
+bool QtPanGestureRecognizer::recognize(const QTouchEvent* event, qint64 eventTimestampMillis)
 {
     // Pan gesture always starts on TouchBegin unless the engine is suspended.
     if (m_state == NoGesture && event->type() != QEvent::TouchBegin)
@@ -58,6 +58,7 @@
         ASSERT(m_state == NoGesture);
         m_state = GestureRecognitionStarted;
         m_firstPosition = touchPoint.screenPos();
+        m_viewportInteractionEngine->stopAnimations();
         return false;
     case QEvent::TouchUpdate: {
         ASSERT(m_state != NoGesture);
@@ -69,18 +70,16 @@
                 return false;
 
             m_state = GestureRecognized;
-            m_viewportInteractionEngine->panGestureStarted();
+            m_viewportInteractionEngine->panGestureStarted(touchPoint.pos(), eventTimestampMillis);
         }
 
         ASSERT(m_state == GestureRecognized);
-        // Offset sent to the views must be in page view coordinate space.
-        QPointF offsetSinceLastEvent(touchPoint.pos() - touchPoint.lastPos());
-        m_viewportInteractionEngine->panGestureRequestUpdate(offsetSinceLastEvent.x(), offsetSinceLastEvent.y());
+        m_viewportInteractionEngine->panGestureRequestUpdate(touchPoint.pos(), eventTimestampMillis);
         return true;
     }
     case QEvent::TouchEnd:
         if (m_state == GestureRecognized) {
-            m_viewportInteractionEngine->panGestureEnded();
+            m_viewportInteractionEngine->panGestureEnded(touchPoint.pos(), eventTimestampMillis);
             reset();
             return true;
         }

Modified: trunk/Source/WebKit2/UIProcess/qt/QtPanGestureRecognizer.h (98459 => 98460)


--- trunk/Source/WebKit2/UIProcess/qt/QtPanGestureRecognizer.h	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/UIProcess/qt/QtPanGestureRecognizer.h	2011-10-26 09:49:21 UTC (rev 98460)
@@ -42,7 +42,7 @@
 class QtPanGestureRecognizer : private QtGestureRecognizer {
 public:
     QtPanGestureRecognizer(QtViewportInteractionEngine*);
-    bool recognize(const QTouchEvent*);
+    bool recognize(const QTouchEvent*, qint64 eventTimestampMillis);
     void reset();
 
 private:

Modified: trunk/Source/WebKit2/UIProcess/qt/QtPinchGestureRecognizer.cpp (98459 => 98460)


--- trunk/Source/WebKit2/UIProcess/qt/QtPinchGestureRecognizer.cpp	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/UIProcess/qt/QtPinchGestureRecognizer.cpp	2011-10-26 09:49:21 UTC (rev 98460)
@@ -96,7 +96,7 @@
                 if (pinchDistance < pinchInitialTriggerDistanceThreshold)
                     return false;
                 m_state = GestureRecognized;
-                m_viewportInteractionEngine->pinchGestureStarted();
+                m_viewportInteractionEngine->pinchGestureStarted(computeTouchCenter(point1, point2));
 
                 // We reset the initial position to the previous position in order to avoid the jump caused
                 // by skipping all the events between the beginning and when the threshold is hit.

Modified: trunk/Source/WebKit2/UIProcess/qt/QtTouchWebPageProxy.cpp (98459 => 98460)


--- trunk/Source/WebKit2/UIProcess/qt/QtTouchWebPageProxy.cpp	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/UIProcess/qt/QtTouchWebPageProxy.cpp	2011-10-26 09:49:21 UTC (rev 98460)
@@ -58,7 +58,9 @@
         m_panGestureRecognizer.reset();
         m_pinchGestureRecognizer.reset();
     } else {
-        m_panGestureRecognizer.recognize(event.nativeEvent());
+        // Convert the event timestamp from second to millisecond.
+        qint64 eventTimestampMillis = static_cast<qint64>(event.timestamp() * 1000);
+        m_panGestureRecognizer.recognize(event.nativeEvent(), eventTimestampMillis);
         m_pinchGestureRecognizer.recognize(event.nativeEvent());
     }
 }

Modified: trunk/Source/WebKit2/UIProcess/qt/QtViewportInteractionEngine.cpp (98459 => 98460)


--- trunk/Source/WebKit2/UIProcess/qt/QtViewportInteractionEngine.cpp	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/UIProcess/qt/QtViewportInteractionEngine.cpp	2011-10-26 09:49:21 UTC (rev 98460)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2011 Benjamin Poulain <benja...@webkit.org>
  *
  * This library is free software; you can redistribute it and/or
@@ -23,58 +24,64 @@
 
 #include "PassOwnPtr.h"
 #include <QPointF>
+#include <QScrollEvent>
+#include <QScrollPrepareEvent>
+#include <QScrollerProperties>
 #include <QtDeclarative/qsgitem.h>
 
 namespace WebKit {
 
-static inline QRectF visibleRectInContentCoordinate(const QSGItem* content, const QSGItem* viewport)
-{
-    const QRectF viewportInContentCoordinate = content->mapRectFromItem(viewport, viewport->boundingRect());
-    const QRectF visibleArea = content->boundingRect().intersected(viewportInContentCoordinate);
-    return visibleArea;
-}
+static const int kScaleAnimationDurationMillis = 400;
 
 static inline QRectF contentRectInViewportCoordinate(const QSGItem* content, const QSGItem* viewport)
 {
     return viewport->mapRectFromItem(content, content->boundingRect());
 }
 
+static inline qreal bindToScaleLimits(qreal scale)
+{
+    // Bounded by [0.1, 10.0] like the viewport meta code in WebCore.
+    return qBound(qreal(0.1), scale, qreal(10.0));
+}
+
 // Updating content properties cause the notify signals to be sent by the content item itself.
 // Since we manage those differently, we do not want to respond
 // to them when we are the one changing the content.
 //
-// The guard make sure the signal viewportUpdateRequested() is sent if necessary.
+// The guard makes sure that the signal viewportUpdateRequested() is sent if necessary.
 // When multiple guards are alive, their lifetime must be perfectly imbricated (e.g. if used ouside stack frames).
-// We rely on the first one to trigger the update at the end since it only uses one bool internally.
+// We rely on the first one to trigger the update at the end.
 //
 // The public methods should create the guard if they might update content.
 class ViewportUpdateGuard {
 public:
     ViewportUpdateGuard(QtViewportInteractionEngine* viewportInteractionEngine)
         : viewportInteractionEngine(viewportInteractionEngine)
-        , wasUpdatingContent(viewportInteractionEngine->m_isUpdatingContent)
         , previousPosition(viewportInteractionEngine->m_content->pos())
         , previousSize(viewportInteractionEngine->m_content->width(), viewportInteractionEngine->m_content->height())
         , previousScale(viewportInteractionEngine->m_content->scale())
+
     {
-        viewportInteractionEngine->m_isUpdatingContent = true;
+        ++(viewportInteractionEngine->m_pendingUpdates);
     }
 
     ~ViewportUpdateGuard()
     {
-        if (!wasUpdatingContent) {
+        --(viewportInteractionEngine->m_pendingUpdates);
+        if (!viewportInteractionEngine->m_pendingUpdates) {
+            // Reset the tiling look-ahead vector so that tiles all around the viewport will be requested.
+            emit viewportInteractionEngine->viewportTrajectoryVectorChanged(QPointF());
             if (previousPosition != viewportInteractionEngine->m_content->pos()
                     || previousSize.width() != viewportInteractionEngine->m_content->width()
                     || previousSize.height() != viewportInteractionEngine->m_content->height()
                     || previousScale != viewportInteractionEngine->m_content->scale())
+                // We must notify the change so the client can rely on us for all changes of geometry.
                 emit viewportInteractionEngine->viewportUpdateRequested();
         }
-        viewportInteractionEngine->m_isUpdatingContent = wasUpdatingContent;
     }
 
 private:
     QtViewportInteractionEngine* const viewportInteractionEngine;
-    const bool wasUpdatingContent;
     const QPointF previousPosition;
     const QSizeF previousSize;
     const qreal previousScale;
@@ -83,7 +90,8 @@
 QtViewportInteractionEngine::QtViewportInteractionEngine(const QSGItem* viewport, QSGItem* content)
     : m_viewport(viewport)
     , m_content(content)
-    , m_isUpdatingContent(false)
+    , m_pendingUpdates(0)
+    , m_scaleAnimation(new ScaleAnimation(this))
     , m_pinchStartScale(1.f)
 {
     reset();
@@ -92,16 +100,180 @@
     connect(m_content, SIGNAL(widthChanged()), this, SLOT(contentViewportChanged()), Qt::DirectConnection);
     connect(m_content, SIGNAL(heightChanged()), this, SLOT(contentViewportChanged()), Qt::DirectConnection);
     connect(m_content, SIGNAL(scaleChanged()), this, SLOT(contentViewportChanged()), Qt::DirectConnection);
+    connect(m_scaleAnimation, SIGNAL(valueChanged(QVariant)), SLOT(updateVisibleRect(QVariant)), Qt::DirectConnection);
+    connect(m_scaleAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))
+            , SLOT(scaleAnimationStateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), Qt::DirectConnection);
 }
 
 QtViewportInteractionEngine::~QtViewportInteractionEngine()
 {
 }
 
+qreal QtViewportInteractionEngine::innerBoundedScale(qreal scale)
+{
+    return qBound(m_constraints.minimumScale, scale, m_constraints.maximumScale);
+}
+
+qreal QtViewportInteractionEngine::outerBoundedScale(qreal scale)
+{
+    if (m_constraints.isUserScalable) {
+        qreal hardMin = qreal(0.5) * m_constraints.minimumScale;
+        qreal hardMax = qreal(2.0) * m_constraints.maximumScale;
+        return bindToScaleLimits(qBound(hardMin, scale, hardMax));
+    }
+    return innerBoundedScale(scale);
+}
+
+void QtViewportInteractionEngine::updateVisibleRect(QVariant rect)
+{
+    ViewportUpdateGuard guard(this);
+
+    QRectF visibleRect = rect.toRectF();
+    qreal currentScale = m_viewport->width() / visibleRect.width();
+
+    m_content->setScale(currentScale);
+
+    // We need to animate the content but the position represents the viewport hence we need to invert the position here.
+    // To animate the position together with the scale we multiply the position with the current scale;
+    m_content->setPos(-visibleRect.topLeft() * currentScale);
+}
+
+void QtViewportInteractionEngine::scaleAnimationStateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
+{
+    switch (newState) {
+    case QAbstractAnimation::Running:
+        m_pinchViewportUpdateDeferrer = adoptPtr(new ViewportUpdateGuard(this));
+        break;
+    case QAbstractAnimation::Stopped:
+        m_pinchViewportUpdateDeferrer.clear();
+        break;
+    default:
+        break;
+    }
+}
+
+bool QtViewportInteractionEngine::event(QEvent* event)
+{
+    switch (event->type()) {
+    case QEvent::ScrollPrepare: {
+        QScrollPrepareEvent* prepareEvent = static_cast<QScrollPrepareEvent*>(event);
+        const QRectF viewportRect = m_viewport->boundingRect();
+        const QRectF contentRect = contentRectInViewportCoordinate(m_content, m_viewport);
+        const QRectF contentPositionRange = calculateBoundariesForScale(contentRect.size(), viewportRect.size(), 1);
+        prepareEvent->setContentPosRange(contentPositionRange);
+        prepareEvent->setViewportSize(viewportRect.size());
+
+        // As we want to push the contents and not actually scroll it, we need to invert the positions here.
+        prepareEvent->setContentPos(-contentRect.topLeft());
+        prepareEvent->accept();
+        return true;
+    }
+    case QEvent::Scroll: {
+        QScrollEvent* scrollEvent = static_cast<QScrollEvent*>(event);
+        QPointF newPos = -scrollEvent->contentPos() - scrollEvent->overshootDistance();
+        if (m_content->pos() != newPos) {
+            ViewportUpdateGuard guard(this);
+
+            QPointF currentPosInContentCoordinates = m_content->mapToItem(m_content->parentItem(), m_content->pos());
+            QPointF newPosInContentCoordinates = m_content->mapToItem(m_content->parentItem(), newPos);
+
+            // This must be emitted before viewportUpdateRequested so that the web process knows where to look for tiles.
+            emit viewportTrajectoryVectorChanged(currentPosInContentCoordinates- newPosInContentCoordinates);
+            m_content->setPos(newPos);
+        }
+        return true;
+    }
+    default:
+        break;
+    }
+    return QObject::event(event);
+}
+
+void QtViewportInteractionEngine::stopAnimations()
+{
+    m_scaleAnimation->stop();
+    scroller()->stop();
+
+    // If the animations were stopped we need to scale and reposition the content into valid boundaries immediately.
+    m_userInteractionFlags |= UserHasStoppedAnimations;
+    animateContentIntoBoundariesIfNeeded();
+    m_userInteractionFlags &= ~UserHasStoppedAnimations;
+}
+
+const QRectF QtViewportInteractionEngine::calculateBoundariesForScale(const QSizeF contentSize, const QSizeF viewportSize, qreal scale)
+{
+    const QSizeF scaledViewportSize = viewportSize / scale;
+
+    const qreal horizontalOffset = contentSize.width() - scaledViewportSize.width();
+    const qreal verticalOffset = contentSize.height() - scaledViewportSize.height();
+
+    const QPointF contentPositionRangeTopLeft(qMin<qreal>(0, horizontalOffset / 2), 0);
+    const qreal contentPositionRangeWidth = qMax<qreal>(0, horizontalOffset);
+    const qreal contentPositionRangeHeight = qMax<qreal>(0, verticalOffset);
+
+    return QRectF(contentPositionRangeTopLeft, QSizeF(contentPositionRangeWidth, contentPositionRangeHeight));
+}
+
+void QtViewportInteractionEngine::animateContentIntoBoundariesIfNeeded()
+{
+    m_scaleAnimation->stop();
+
+    qreal currentScale = m_content->scale();
+    bool userHasScaledContent = m_userInteractionFlags & UserHasScaledContent;
+    bool userHasStoppedAnimations = m_userInteractionFlags & UserHasStoppedAnimations;
+
+    if (!userHasScaledContent)
+        currentScale = m_constraints.initialScale;
+
+    qreal boundedScale = innerBoundedScale(currentScale);
+
+    QRectF viewportRect = m_viewport->boundingRect();
+    QPointF viewportHotspot = viewportRect.center();
+    QPointF position = m_content->mapFromItem(m_viewport, viewportHotspot) - viewportHotspot / boundedScale;
+    QRectF positionBoundaries = calculateBoundariesForScale(m_content->boundingRect().size(), viewportRect.size(), boundedScale);
+    QPointF minOffset = positionBoundaries.topLeft();
+    QPointF maxOffset = positionBoundaries.bottomRight();
+    QPointF boundedPosition;
+    boundedPosition.setX(qBound(minOffset.x(), position.x(), maxOffset.x()));
+    boundedPosition.setY(qBound(minOffset.y(), position.y(), maxOffset.y()));
+
+    QRectF scaledRect(boundedPosition, viewportRect.size() / boundedScale);
+    QRectF currentRect = m_viewport->mapRectToItem(m_content, viewportRect);
+
+    if (scaledRect == currentRect)
+        return;
+
+    if (userHasScaledContent && !userHasStoppedAnimations) {
+        m_scaleAnimation->setDuration(kScaleAnimationDurationMillis);
+        m_scaleAnimation->setEasingCurve(QEasingCurve::OutCubic);
+        m_scaleAnimation->setStartValue(currentRect);
+        m_scaleAnimation->setEndValue(scaledRect);
+        m_scaleAnimation->start();
+    } else
+        updateVisibleRect(scaledRect);
+}
+
 void QtViewportInteractionEngine::reset()
 {
     ViewportUpdateGuard guard(this);
     m_userInteractionFlags = UserHasNotInteractedWithContent;
+
+    stopAnimations();
+
+    QScrollerProperties properties = scroller()->scrollerProperties();
+
+    // The QtPanGestureRecognizer is responsible for recognizing the gesture
+    // thus we need to disable the drag start distance.
+    properties.setScrollMetric(QScrollerProperties::DragStartDistance, 0.0);
+
+    // Set some default QScroller constrains to mimic the physics engine of the N9 browser.
+    properties.setScrollMetric(QScrollerProperties::AxisLockThreshold, 0.66);
+    properties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.20);
+    properties.setScrollMetric(QScrollerProperties::MaximumVelocity, 0.2);
+    properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.33);
+    properties.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.33);
+
+    scroller()->setScrollerProperties(properties);
     setConstraints(Constraints());
 }
 
@@ -110,60 +282,43 @@
     if (m_constraints == constraints)
         return;
 
-    // FIXME: if a pinch gesture is ongoing, we have to:
-    // -cancel the gesture if isUserScalable becomes true
-    // -update the page on the fly without modifying its position
-
-    // FIXME: if a pan gesture is ongoing, we have to
-    // -update the page without changing the position
-    // -animate the page back in viewport if necessary (if the page is fully in
-    //  viewport, it does not pan horizontally anymore).
-
     ViewportUpdateGuard guard(this);
     m_constraints = constraints;
-    updateContentIfNeeded();
+    animateContentIntoBoundariesIfNeeded();
 }
 
-void QtViewportInteractionEngine::panGestureStarted()
+void QtViewportInteractionEngine::panGestureStarted(const QPointF& touchPoint, qint64 eventTimestampMillis)
 {
     // FIXME: suspend the Web engine (stop animated GIF, etc).
-    // FIXME: initialize physics for panning (stop animation, etc).
     m_userInteractionFlags |= UserHasMovedContent;
+    scroller()->handleInput(QScroller::InputPress, m_viewport->mapFromItem(m_content, touchPoint), eventTimestampMillis);
 }
 
-void QtViewportInteractionEngine::panGestureRequestUpdate(qreal deltaX, qreal deltaY)
+void QtViewportInteractionEngine::panGestureRequestUpdate(const QPointF& touchPoint, qint64 eventTimestampMillis)
 {
     ViewportUpdateGuard guard(this);
-
-    QPointF itemPositionInItemCoords = m_content->mapFromItem(m_content->parentItem(), m_content->pos());
-    QPointF destInViewportCoords = m_viewport->mapFromItem(m_content, itemPositionInItemCoords + QPointF(deltaX, deltaY));
-
-    m_content->setPos(destInViewportCoords);
-    // This must be emitted before viewportUpdateRequested so that the web process knows where to look for tiles.
-    emit viewportTrajectoryVectorChanged(QPointF(-deltaX, -deltaY));
+    scroller()->handleInput(QScroller::InputMove, m_viewport->mapFromItem(m_content, touchPoint), eventTimestampMillis);
 }
 
 void QtViewportInteractionEngine::panGestureCancelled()
 {
     ViewportUpdateGuard guard(this);
-    // FIXME: reset physics.
-    panGestureEnded();
+    stopAnimations();
 }
 
-void QtViewportInteractionEngine::panGestureEnded()
+void QtViewportInteractionEngine::panGestureEnded(const QPointF& touchPoint, qint64 eventTimestampMillis)
 {
     ViewportUpdateGuard guard(this);
-    animateContentIntoBoundariesIfNeeded();
-    // FIXME: emit viewportTrajectoryVectorChanged(QPointF()) when the pan throw animation ends and the page stops moving.
+    scroller()->handleInput(QScroller::InputRelease, m_viewport->mapFromItem(m_content, touchPoint), eventTimestampMillis);
 }
 
-void QtViewportInteractionEngine::pinchGestureStarted()
+void QtViewportInteractionEngine::pinchGestureStarted(const QPointF& pinchCenterInContentCoordinate)
 {
     if (!m_constraints.isUserScalable)
         return;
 
     m_pinchViewportUpdateDeferrer = adoptPtr(new ViewportUpdateGuard(this));
-
+    m_lastPinchCenterInViewportCoordinates = m_viewport->mapFromItem(m_content, pinchCenterInContentCoordinate);
     m_userInteractionFlags |= UserHasScaledContent;
     m_userInteractionFlags |= UserHasMovedContent;
     m_pinchStartScale = m_content->scale();
@@ -180,12 +335,19 @@
     ViewportUpdateGuard guard(this);
 
     //  Changes of the center position should move the page even if the zoom factor
-    //  does not change. Both the zoom and the panning should be handled through the physics engine.
+    //  does not change.
     const qreal scale = m_pinchStartScale * totalScaleFactor;
-    QPointF oldPinchCenterOnParent = m_content->mapToItem(m_content->parentItem(), pinchCenterInContentCoordinate);
-    m_content->setScale(scale);
-    QPointF newPinchCenterOnParent = m_content->mapToItem(m_content->parentItem(), pinchCenterInContentCoordinate);
-    m_content->setPos(m_content->pos() - (newPinchCenterOnParent - oldPinchCenterOnParent));
+
+    // Allow zooming out beyond mimimum scale on pages that do not explicitly disallow it.
+    const qreal boundedScale = outerBoundedScale(scale);
+
+    QPointF pinchCenterInViewportCoordinates = m_viewport->mapFromItem(m_content, pinchCenterInContentCoordinate);
+
+    scaleContent(pinchCenterInContentCoordinate, boundedScale);
+
+    const QPointF positionDiff = pinchCenterInViewportCoordinates - m_lastPinchCenterInViewportCoordinates;
+    m_lastPinchCenterInViewportCoordinates = pinchCenterInViewportCoordinates;
+    m_content->setPos(m_content->pos() + positionDiff);
 }
 
 void QtViewportInteractionEngine::pinchGestureEnded()
@@ -193,105 +355,21 @@
     if (!m_constraints.isUserScalable)
         return;
 
-    {
-        ViewportUpdateGuard guard(this);
-        // FIXME: resume the engine after the animation.
-        animateContentIntoBoundariesIfNeeded();
-    }
     m_pinchViewportUpdateDeferrer.clear();
+    // FIXME: resume the engine after the animation.
+    animateContentIntoBoundariesIfNeeded();
 }
 
 void QtViewportInteractionEngine::contentViewportChanged()
 {
-    if (m_isUpdatingContent)
+    if (m_pendingUpdates)
         return;
 
     ViewportUpdateGuard guard(this);
-    updateContentIfNeeded();
 
-    // We must notify the change so the client can rely on us for all change of Geometry.
-    emit viewportUpdateRequested();
+    animateContentIntoBoundariesIfNeeded();
 }
 
-void QtViewportInteractionEngine::updateContentIfNeeded()
-{
-    updateContentScaleIfNeeded();
-    updateContentPositionIfNeeded();
-}
-
-void QtViewportInteractionEngine::updateContentScaleIfNeeded()
-{
-    const qreal currentContentScale = m_content->scale();
-    qreal contentScale = m_content->scale();
-    if (!(m_userInteractionFlags & UserHasScaledContent))
-        contentScale = m_constraints.initialScale;
-
-    contentScale = qBound(m_constraints.minimumScale, contentScale, m_constraints.maximumScale);
-
-    if (contentScale != currentContentScale) {
-        const QPointF centerOfInterest = visibleRectInContentCoordinate(m_content, m_viewport).center();
-        scaleContent(centerOfInterest, contentScale);
-    }
-}
-
-void QtViewportInteractionEngine::updateContentPositionIfNeeded()
-{
-    if (!(m_userInteractionFlags & UserHasMovedContent)) {
-        m_content->setX((m_viewport->width() - contentRectInViewportCoordinate(m_content, m_viewport).width()) / 2);
-        m_content->setY(0);
-    }
-
-    // FIXME: if the item can be fully in the viewport and is over a side, push it back in view
-    // FIXME: if the item cannot be fully in viewport, and is not covering the viewport, push it back in view
-}
-
-void QtViewportInteractionEngine::animateContentIntoBoundariesIfNeeded()
-{
-    animateContentScaleIntoBoundariesIfNeeded();
-    animateContentPositionIntoBoundariesIfNeeded();
-}
-
-void QtViewportInteractionEngine::animateContentPositionIntoBoundariesIfNeeded()
-{
-    const QRectF contentGeometry = m_viewport->mapRectFromItem(m_content, m_content->boundingRect());
-    QPointF newPos = contentGeometry.topLeft();
-
-    // Horizontal correction.
-    if (contentGeometry.width() < m_viewport->width())
-        newPos.setX((m_viewport->width() - contentGeometry.width()) / 2);
-    else {
-        newPos.setX(qMin<qreal>(0., newPos.x()));
-        const qreal rightSideGap = m_viewport->boundingRect().right() - contentGeometry.right();
-        if (rightSideGap > 0.)
-            newPos.setX(newPos.x() + rightSideGap);
-    }
-
-    // Vertical correction.
-    if (contentGeometry.height() < m_viewport->height())
-        newPos.setY(0);
-    else {
-        newPos.setY(qMin<qreal>(0., newPos.y()));
-        const qreal bottomSideGap = m_viewport->boundingRect().bottom() - contentGeometry.bottom();
-        if (bottomSideGap > 0.)
-            newPos.setY(newPos.y() + bottomSideGap);
-    }
-
-    if (newPos != m_content->pos())
-        // FIXME: Do you know what ANIMATE means?
-        m_content->setPos(newPos);
-}
-
-void QtViewportInteractionEngine::animateContentScaleIntoBoundariesIfNeeded()
-{
-    const qreal currentScale = m_content->scale();
-    const qreal boundedScale = qBound(m_constraints.minimumScale, currentScale, m_constraints.maximumScale);
-    if (currentScale != boundedScale) {
-        // FIXME: Do you know what ANIMATE means?
-        const QPointF centerOfInterest = visibleRectInContentCoordinate(m_content, m_viewport).center();
-        scaleContent(centerOfInterest, boundedScale);
-    }
-}
-
 void QtViewportInteractionEngine::scaleContent(const QPointF& centerInContentCoordinate, qreal scale)
 {
     QPointF oldPinchCenterOnParent = m_content->mapToItem(m_content->parentItem(), centerInContentCoordinate);

Modified: trunk/Source/WebKit2/UIProcess/qt/QtViewportInteractionEngine.h (98459 => 98460)


--- trunk/Source/WebKit2/UIProcess/qt/QtViewportInteractionEngine.h	2011-10-26 09:41:27 UTC (rev 98459)
+++ trunk/Source/WebKit2/UIProcess/qt/QtViewportInteractionEngine.h	2011-10-26 09:49:21 UTC (rev 98460)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2011 Benjamin Poulain <benja...@webkit.org>
  *
  * This library is free software; you can redistribute it and/or
@@ -22,8 +23,11 @@
 #define QtViewportInteractionEngine_h
 
 #include "OwnPtr.h"
+#include <QScroller>
 #include "qwebkitglobal.h"
 #include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QVariantAnimation>
 
 QT_BEGIN_NAMESPACE
 class QPointF;
@@ -41,6 +45,7 @@
     QtViewportInteractionEngine(const QSGItem*, QSGItem*);
     ~QtViewportInteractionEngine();
 
+
     struct Constraints {
         Constraints()
             : initialScale(1.0)
@@ -55,17 +60,20 @@
         bool isUserScalable;
     };
 
+    bool event(QEvent*);
+
     void reset();
     void setConstraints(const Constraints&);
 
-    void panGestureStarted();
-    void panGestureRequestUpdate(qreal deltaX, qreal deltaY);
+    void panGestureStarted(const QPointF& touchPoint, qint64 eventTimestampMillis);
+    void panGestureRequestUpdate(const QPointF& touchPoint, qint64 eventTimestampMillis);
     void panGestureCancelled();
-    void panGestureEnded();
+    void panGestureEnded(const QPointF& touchPoint, qint64 eventTimestampMillis);
 
-    void pinchGestureStarted();
+    void pinchGestureStarted(const QPointF& pinchCenterInContentCoordinate);
     void pinchGestureRequestUpdate(const QPointF& pinchCenterInContentCoordinate, qreal totalScaleFactor);
     void pinchGestureEnded();
+    void stopAnimations();
 
 Q_SIGNALS:
     void viewportUpdateRequested();
@@ -74,34 +82,48 @@
 private Q_SLOTS:
     // Respond to changes of content that are not driven by us, like the page resizing itself.
     void contentViewportChanged();
+    void updateVisibleRect(QVariant visibleRectVariant);
+    void scaleAnimationStateChanged(QAbstractAnimation::State, QAbstractAnimation::State);
 
 private:
-    void updateContentIfNeeded();
-    void updateContentScaleIfNeeded();
-    void updateContentPositionIfNeeded();
-
+    qreal innerBoundedScale(qreal scale);
+    qreal outerBoundedScale(qreal scale);
+    const QRectF calculateBoundariesForScale(const QSizeF contentSize, const QSizeF viewportSize, qreal scale);
     void animateContentIntoBoundariesIfNeeded();
-    void animateContentPositionIntoBoundariesIfNeeded();
-    void animateContentScaleIntoBoundariesIfNeeded();
 
     void scaleContent(const QPointF& centerInContentCoordinate, qreal scale);
 
+    // As long as the object exists this function will always return the same QScroller instance.
+    QScroller* scroller() { return QScroller::scroller(this); }
+
     friend class ViewportUpdateGuard;
 
     const QSGItem* const m_viewport;
     QSGItem* const m_content;
 
     Constraints m_constraints;
-    bool m_isUpdatingContent;
+    int m_pendingUpdates;
     OwnPtr<ViewportUpdateGuard> m_pinchViewportUpdateDeferrer;
     enum UserInteractionFlag {
         UserHasNotInteractedWithContent = 0,
         UserHasMovedContent = 1,
-        UserHasScaledContent = 2
+        UserHasScaledContent = 2,
+        UserHasStoppedAnimations = 4
     };
     Q_DECLARE_FLAGS(UserInteractionFlags, UserInteractionFlag);
     UserInteractionFlags m_userInteractionFlags;
 
+    class ScaleAnimation : public QVariantAnimation {
+    public:
+        ScaleAnimation(QObject* parent = 0)
+            : QVariantAnimation(parent)
+        { }
+
+        virtual void updateCurrentValue(const QVariant&) { }
+    };
+
+    ScaleAnimation* m_scaleAnimation;
+    QPointF m_lastPinchCenterInViewportCoordinates;
     qreal m_pinchStartScale;
 };
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to