void QtViewportInteractionEngine::animateItemRectVisible(const QRectF& itemRect)
{
    ASSERT(m_scaleAnimation->state() == QAbstractAnimation::Stopped);

    ASSERT(!scrollAnimationActive());
    if (scrollAnimationActive())
        return;

    QRectF currentItemRectVisible = m_viewport->mapRectToWebContent(m_viewport->boundingRect());
    if (itemRect == currentItemRectVisible)
        return;

    // FIXME: Investigate why that animation doesn't run when we are unfocused.
    if (!m_viewport->isVisible() || !m_viewport->hasFocus()) {
        // Apply the end result immediately when we are non-visible.
        setItemRectVisible(itemRect);
        return;
    }

    QEasingCurve easingCurve;
    easingCurve.setCustomType(physicalOvershoot);

    m_scaleAnimation->setDuration(kScaleAnimationDurationMillis);
    m_scaleAnimation->setEasingCurve(easingCurve);

    m_scaleAnimation->setStartValue(currentItemRectVisible);
    m_scaleAnimation->setEndValue(itemRect);

    m_scaleAnimation->start();
}
Example #2
0
void PageViewportControllerClientQt::animateContentRectVisible(const QRectF& contentRect)
{
    ASSERT(m_scaleAnimation->state() == QAbstractAnimation::Stopped);

    ASSERT(!scrollAnimationActive());
    if (scrollAnimationActive())
        return;

    QRectF viewportRectInContentCoords = m_viewportItem->mapRectToWebContent(m_viewportItem->boundingRect());
    if (contentRect == viewportRectInContentCoords) {
        resumeAndUpdateContent();
        return;
    }

    // Inform the web process about the requested visible content rect immediately so that new tiles
    // are rendered at the final destination during the animation.
    m_controller->didChangeContentsVisibility(contentRect.topLeft(), viewportScaleForRect(contentRect));

    // Since we have to animate scale and position at the same time the scale animation interpolates
    // from the current viewport rect in content coordinates to a visible rect of the content.
    m_scaleAnimation->setStartValue(viewportRectInContentCoords);
    m_scaleAnimation->setEndValue(contentRect);

    m_scaleAnimation->start();
}
void QtViewportInteractionEngine::wheelEvent(QWheelEvent* ev)
{
    if (scrollAnimationActive() || scaleAnimationActive() || pinchGestureActive())
        return; // Ignore.


    // A normal scroll-tick should have a delta of 120 (1/8) degrees. Convert this to
    // local standard scroll step of 3 lines of 20 pixels.
    static const int cDefaultQtScrollStep = 20;
    static const int wheelScrollLines = 3;
    const int wheelTick = wheelScrollLines * cDefaultQtScrollStep;

    int pixelDelta = ev->delta() * (wheelTick / 120.f);

    QPointF newPosition = m_viewport->contentPos();

    if (ev->orientation() == Qt::Horizontal)
        newPosition.rx() -= pixelDelta;
    else
        newPosition.ry() -= pixelDelta;

    QRectF endPosRange = computePosRangeForItemAtScale(m_content->contentsScale());

    QPointF currentPosition = m_viewport->contentPos();
    newPosition = boundPosition(endPosRange.topLeft(), newPosition, endPosRange.bottomRight());
    m_viewport->setContentPos(newPosition);

    emit contentViewportChanged(currentPosition - newPosition);
}
Example #4
0
bool QtViewportInteractionEngine::ensureContentWithinViewportBoundary(bool immediate)
{
    ASSERT(m_suspendCount);

    if (scrollAnimationActive() || scaleAnimationActive())
        return false;

    qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(currentCSSScale()));

    const QRectF viewportRect = m_viewport->boundingRect();
    QPointF viewportHotspot = viewportRect.center();

    QPointF endPosition = m_viewport->mapToWebContent(viewportHotspot) * endItemScale - viewportHotspot;

    QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
    endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());

    QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale);

    if (immediate) {
        setItemRectVisible(endVisibleContentRect);
        return true;
    }
    return !animateItemRectVisible(endVisibleContentRect);
}
Example #5
0
void QtViewportInteractionEngine::zoomToAreaGestureEnded(const QPointF& touchPoint, const QRectF& targetArea)
{
    if (!targetArea.isValid())
        return;

    if (scrollAnimationActive() || scaleAnimationActive())
        return;

    const int margin = 10; // We want at least a little bit or margin.
    QRectF endArea = itemRectFromCSS(targetArea.adjusted(-margin, -margin, margin, margin));

    const QRectF viewportRect = m_viewport->boundingRect();

    qreal targetCSSScale = cssScaleFromItem(viewportRect.size().width() / endArea.size().width());
    qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(qMin(targetCSSScale, qreal(2.5))));

    // We want to end up with the target area filling the whole width of the viewport (if possible),
    // and centralized vertically where the user requested zoom. Thus our hotspot is the center of
    // the targetArea x-wise and the requested zoom position, y-wise.
    const QPointF hotspot = QPointF(endArea.center().x(), touchPoint.y() * m_constraints.devicePixelRatio);
    const QPointF viewportHotspot = viewportRect.center();

    QPointF endPosition = hotspot * endItemScale - viewportHotspot;

    QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
    endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());

    QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale);

    animateItemRectVisible(endVisibleContentRect);
}
Example #6
0
void QtViewportInteractionEngine::wheelEvent(QWheelEvent* ev)
{
    if (scrollAnimationActive() || scaleAnimationActive() || pinchGestureActive())
        return; // Ignore.

    int delta = ev->delta();

    // A delta that is not mod 120 indicates a device that is sending
    // fine-resolution scroll events, so use the delta as number of wheel ticks
    // and number of pixels to scroll. See also webkit.org/b/29601
    bool fullTick = !(delta % 120);

    static const int cDefaultQtScrollStep = 20;
    static const int wheelScrollLines = 3;
    int scrollLines = (fullTick) ? wheelScrollLines * cDefaultQtScrollStep : 1;

    delta = (fullTick) ? delta / 120.0f : delta;
    delta *= scrollLines;

    QPointF newPosition = m_viewport->contentPos();

    if (ev->orientation() == Qt::Horizontal)
        newPosition.rx() += delta;
    else
        newPosition.ry() += delta;

    QRectF endPosRange = computePosRangeForItemAtScale(m_content->contentsScale());

    QPointF currentPosition = m_viewport->contentPos();
    newPosition = boundPosition(endPosRange.topLeft(), newPosition, endPosRange.bottomRight());
    m_viewport->setContentPos(newPosition);

    emit contentViewportChanged(currentPosition - newPosition);
}
Example #7
0
void QtViewportHandler::touchBegin()
{
    m_hadUserInteraction = true;

    // Prevents resuming the page between the user's flicks of the page while the animation is running.
    if (scrollAnimationActive())
        m_touchUpdateDeferrer = adoptPtr(new ViewportUpdateDeferrer(this, ViewportUpdateDeferrer::DeferUpdateAndSuspendContent));
}
Example #8
0
void QtViewportHandler::animatePageItemRectVisible(const QRectF& itemRect)
{
    ASSERT(m_scaleAnimation->state() == QAbstractAnimation::Stopped);

    ASSERT(!scrollAnimationActive());
    if (scrollAnimationActive())
        return;

    QRectF currentPageItemRectVisible = m_viewportItem->mapRectToWebContent(m_viewportItem->boundingRect());
    if (itemRect == currentPageItemRectVisible)
        return;

    m_scaleAnimation->setStartValue(currentPageItemRectVisible);
    m_scaleAnimation->setEndValue(itemRect);

    m_scaleAnimation->start();
}
Example #9
0
void PageViewportControllerClientQt::cancelScrollAnimation()
{
    if (!scrollAnimationActive())
        return;

    // If the pan gesture recognizer receives a touch begin event
    // during an ongoing kinetic scroll animation of a previous
    // pan gesture, the animation is stopped and the content is
    // immediately positioned back to valid boundaries.

    m_viewportItem->cancelFlick();
    setContentsRectToNearestValidBounds();
}
void QtViewportInteractionEngine::cancelScrollAnimation()
{
    if (!scrollAnimationActive())
        return;

    // If the pan gesture recognizer receives a touch begin event
    // during an ongoing kinetic scroll animation of a previous
    // pan gesture, the animation is stopped and the content is
    // immediately positioned back to valid boundaries.

    m_viewport->cancelFlick();
    setItemRectVisible(nearestValidBounds());
}
void QtViewportInteractionEngine::zoomToAreaGestureEnded(const QPointF& touchPoint, const QRectF& targetArea)
{
    if (!targetArea.isValid())
        return;

    if (scrollAnimationActive() || scaleAnimationActive())
        return;

    const int margin = 10; // We want at least a little bit of margin.
    QRectF endArea = itemRectFromCSS(targetArea.adjusted(-margin, -margin, margin, margin));

    const QRectF viewportRect = m_viewport->boundingRect();

    qreal targetCSSScale = cssScaleFromItem(viewportRect.size().width() / endArea.size().width());
    qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(qMin(targetCSSScale, qreal(2.5))));
    qreal currentScale = m_content->contentsScale();

    // We want to end up with the target area filling the whole width of the viewport (if possible),
    // and centralized vertically where the user requested zoom. Thus our hotspot is the center of
    // the targetArea x-wise and the requested zoom position, y-wise.
    const QPointF hotspot = QPointF(endArea.center().x(), touchPoint.y() * m_devicePixelRatio);
    const QPointF viewportHotspot = viewportRect.center();

    QPointF endPosition = hotspot * endItemScale - viewportHotspot;

    QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
    endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());

    QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale);

    enum { ZoomIn, ZoomBack, ZoomOut, NoZoom } zoomAction = ZoomIn;

    if (!m_scaleStack.isEmpty()) {
        // Zoom back out if attempting to scale to the same current scale, or
        // attempting to continue scaling out from the inner most level.
        // Use fuzzy compare with a fixed error to be able to deal with largish differences due to pixel rounding.
        if (fuzzyCompare(endItemScale, currentScale, 0.01)) {
            // If moving the viewport would expose more of the targetRect and move at least 40 pixels, update position but do not scale out.
            QRectF currentContentRect(m_viewport->contentPos() / currentScale, viewportRect.size() / currentScale);
            QRectF targetIntersection = endVisibleContentRect.intersected(targetArea);
            if (!currentContentRect.contains(targetIntersection) && (qAbs(endVisibleContentRect.top() - currentContentRect.top()) >= 40 || qAbs(endVisibleContentRect.left() - currentContentRect.left()) >= 40))
                zoomAction = NoZoom;
            else
                zoomAction = ZoomBack;
        } else if (fuzzyCompare(endItemScale, m_zoomOutScale, 0.01))
            zoomAction = ZoomBack;
        else if (endItemScale < currentScale)
            zoomAction = ZoomOut;
    }

    switch (zoomAction) {
    case ZoomIn:
        m_scaleStack.append(ScaleStackItem(currentScale, m_viewport->contentPos().x()));
        m_zoomOutScale = endItemScale;
        break;
    case ZoomBack: {
        ScaleStackItem lastScale = m_scaleStack.takeLast();
        endItemScale = lastScale.scale;
        // Recalculate endPosition and bound it according to new scale.
        endPosition.setY(hotspot.y() * endItemScale - viewportHotspot.y());
        endPosition.setX(lastScale.xPosition);
        endPosRange = computePosRangeForItemAtScale(endItemScale);
        endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());
        endVisibleContentRect = QRectF(endPosition / endItemScale, viewportRect.size() / endItemScale);
        break;
    }
    case ZoomOut:
        // Unstack all scale-levels deeper than the new level, so a zoom-back won't end up zooming in.
        while (!m_scaleStack.isEmpty() && m_scaleStack.last().scale >= endItemScale)
            m_scaleStack.removeLast();
        m_zoomOutScale = endItemScale;
        break;
    case NoZoom:
        break;
    }

    animateItemRectVisible(endVisibleContentRect);
}