diff -r 0f2326c2a325 -r 1c3b8676e58c ginebra2/ContentViews/ScrollableWebContentView.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ginebra2/ContentViews/ScrollableWebContentView.cpp Tue Jul 06 14:03:49 2010 +0300 @@ -0,0 +1,518 @@ +/* +* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, version 2.1 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, +* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/". +* +* Description: +* +*/ + +#include "ScrollableWebContentView.h" + +#include "Gestures/GestureRecognizer.h" +#include "Kinetics/KineticScroller.h" +#include "ScrollableViewBase.h" +#include "ViewportMetaDataParser.h" +#include "WebContentAnimationItem.h" + +#include +#include +#include +#include +#include +#include + +//Kinetic scroll constants +static const int ScrollsPerSecond = 30; +static const int MinimumScrollVelocity = 10; +static const qreal AxisLockThreshold = .8; + + +//Zooming constants +static const int ZoomAnimationDuration = 300; //ms. Zooming transition duration +static const qreal ZoomStep = .5; //Incremental zoom step +const int TileUpdateEnableDelay = 500; //Wait duration before tiling updates are enabled. + +namespace GVA { + +ScrollableWebContentView::ScrollableWebContentView(WebContentAnimationItem* webAnimationItem, QGraphicsItem* parent) + : ScrollableViewBase(parent) + , m_gestureRecognizer(this) +{ + m_viewportMetaData = new ViewportMetaData(); + + //Kinetic scroller settings + //Sets the number of scrolls (frames) per second to sps. + m_kineticScroller->setScrollsPerSecond(ScrollsPerSecond); + //For elastic scroll in page edges + m_kineticScroller->setOvershootPolicy(KineticScroller::OvershootWhenScrollable); + + //Gesture settings + //For detecting scroll direction + m_gestureRecognizer.setAxisLockThreshold(AxisLockThreshold); + //To enable touch and drag scrolling + m_gestureRecognizer.setMinimumVelocity(MinimumScrollVelocity); + + setWidget(webAnimationItem); + //FIX ME : Revisit this code. Duplicate info sharing! + webAnimationItem->setViewportMetaData(m_viewportMetaData); + + + m_tileUpdateEnableTimer.setSingleShot(true); + connect(&m_tileUpdateEnableTimer, SIGNAL(timeout()), webAnimationItem, SLOT(enableContentUpdates())); + + //Setup zooming animator + m_zoomAnimator = new QPropertyAnimation(webAnimationItem, "geometry"); + m_zoomAnimator->setDuration(ZoomAnimationDuration); + connect(m_zoomAnimator, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), this, SLOT(zoomAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State))); +} + +ScrollableWebContentView::~ScrollableWebContentView() +{ + delete m_viewportMetaData; + delete m_kineticScroller; + + if(m_zoomAnimator) { + m_zoomAnimator->stop(); + delete m_zoomAnimator; + } +} + +WebContentAnimationItem* ScrollableWebContentView::viewportWidget() const +{ + return qobject_cast(scrollWidget()); +} + +void ScrollableWebContentView::zoomToScreenCenter(bool zoomIn) +{ + //If viewport metadata has user scalable false. + //Do not zoom. + if (!m_viewportMetaData->m_userScalable) + return; + + qreal scale = 1; + scale += ZoomStep; + + if (!zoomIn) + scale = 1/scale; + + qreal curScale = viewportWidget()->zoomScale(); + + if (zoomIn && (curScale * scale > m_viewportMetaData->m_maximumScale)) + scale = m_viewportMetaData->m_maximumScale / curScale; + else if (!zoomIn && (curScale * scale < m_viewportMetaData->m_minimumScale)) + scale = m_viewportMetaData->m_minimumScale / curScale; + + if(scale == 1.) + return; + + //Screen center + QPointF scrCenter(size().width()/2, size().height()/2); + //Map screen center to document + QPointF docPoint(viewportWidget()->mapFromScene(scrCenter)); + //Maintain that spot in the same point on the viewport + QPointF docPointInScr(viewportWidget()->mapToParent(docPoint)); + startZoomAnimToItemHotspot(docPoint, docPointInScr, scale); +} + +ZoomMetaData ScrollableWebContentView::currentPageInfo() +{ + ZoomMetaData data; + + data.initialScale = m_viewportMetaData->m_initialScale; + data.minScale = m_viewportMetaData->m_minimumScale; + data.maxScale = m_viewportMetaData->m_maximumScale; + data.userScalable = m_viewportMetaData->m_userScalable; + data.m_specifiedWidth = m_viewportMetaData->m_specifiedData.m_width; + data.m_specifiedHeight= m_viewportMetaData->m_specifiedData.m_height; + + data.rect = viewportWidget()->geometry(); + data.scale = viewportWidget()->zoomScale(); + data.webViewSize = viewportWidget()->webView()->geometry(); + data.viewportSize = size(); + + return data; +} + +void ScrollableWebContentView::setCurrentPageInfo(ZoomMetaData data) +{ + m_viewportMetaData->m_initialScale = data.initialScale; + m_viewportMetaData->m_minimumScale = data.minScale; + m_viewportMetaData->m_maximumScale = data.maxScale; + m_viewportMetaData->m_userScalable = data.userScalable; + m_viewportMetaData->m_specifiedData.m_width = data.m_specifiedWidth; + m_viewportMetaData->m_specifiedData.m_height = data.m_specifiedHeight; + m_viewportMetaData->m_isValid = true; + + m_viewportMetaData->m_width = data.webViewSize.width(); + m_viewportMetaData->m_height = data.webViewSize.height(); + + viewportWidget()->webView()->setGeometry(data.webViewSize); + viewportWidget()->setZoomScale(data.scale, true); + viewportWidget()->setGeometry(data.rect); + + if (data.viewportSize.width() != size().width()) + adjustViewportSize(data.viewportSize, size()); +} + +ZoomMetaData ScrollableWebContentView::defaultZoomData() +{ + ZoomMetaData data; + + data.initialScale = m_viewportMetaData->m_initialScale; + data.minScale = m_viewportMetaData->m_minimumScale; + data.maxScale = m_viewportMetaData->m_maximumScale; + data.userScalable = m_viewportMetaData->m_userScalable; + + data.scale = 1.0; + data.rect = QRectF(); + data.webViewSize = QRectF(); + data.viewportSize = QSizeF(); + + return data; +} + +void ScrollableWebContentView::updatePreferredContentSize() +{ + viewportWidget()->updatePreferredContentSize(QSize(m_viewportMetaData->m_width + , m_viewportMetaData->m_height)); +} + +void ScrollableWebContentView::setSuperPage() +{ + m_viewportMetaData->m_initialScale = 1.; + m_viewportMetaData->m_minimumScale = 1.; + m_viewportMetaData->m_maximumScale = 1.; + m_viewportMetaData->m_specifiedData.m_width = "device-width"; + m_viewportMetaData->m_specifiedData.m_height = "device-height"; + m_viewportMetaData->m_userScalable = false; + + QSize contentSize = viewportWidget()->contentsSize(); + QRect webViewRect(0, 0, size().width(), contentSize.height()); + viewportWidget()->webView()->setGeometry(webViewRect); + viewportWidget()->setZoomScale(1., true); + viewportWidget()->setGeometry(webViewRect); + + m_viewportMetaData->m_width = size().width(); + m_viewportMetaData->m_height = size().height(); + m_viewportMetaData->m_isValid = true; + + updatePreferredContentSize(); +} + +void ScrollableWebContentView::reset() +{ + // TODO: INVESTIGATE: In the case of multiple windows loading pages simultaneously, it is possible + // to be calling this slot on a signal from a frame that is not + // the frame of the page saved here. It might be better to use 'sender' instead of + // page->mainFrame() to get the metaData so that we use the meta data of the corresponding + // frame + + QWebPage* page = viewportWidget()->webView()->page(); + if (!page) + return; + + //Initialize viewport metadata + m_viewportMetaData->reset(); + + QWebFrame* frame = page->mainFrame(); + QMap metaData = frame->metaData(); + QString viewportTag = metaData.value("viewport"); + + QRect clientRect = geometry().toAlignedRect(); + ViewportMetaDataParser parser(clientRect); + *m_viewportMetaData = parser.parse(viewportTag); + + updatePreferredContentSize(); + setViewportWidgetGeometry(QRectF(QPointF(), + QSize(m_viewportMetaData->m_width, m_viewportMetaData->m_height) + * m_viewportMetaData->m_initialScale)); +} + +void ScrollableWebContentView::contentsSizeChanged(const QSize& newContentSize) +{ + QRect clientRect = geometry().toAlignedRect(); + m_viewportMetaData->updateViewportData(newContentSize, clientRect); + viewportWidget()->resize(QSize(m_viewportMetaData->m_width, m_viewportMetaData->m_height) + * m_viewportMetaData->m_initialScale); +} + +void ScrollableWebContentView::pageLoadFinished(bool ok) +{ + Q_UNUSED(ok); + QSize contentSize = viewportWidget()->contentsSize(); + QRect clientRect = geometry().toAlignedRect(); + m_viewportMetaData->updateViewportData(contentSize, clientRect); + + viewportWidget()->resize(QSize(m_viewportMetaData->m_width, m_viewportMetaData->m_height) + * m_viewportMetaData->m_initialScale); + viewportWidget()->setZoomScale(m_viewportMetaData->m_initialScale, true); +} + +bool ScrollableWebContentView::sceneEventFilter(QGraphicsItem* item, QEvent* event) +{ + Q_UNUSED(item); + + bool handled = false; + if (!isVisible()) + return handled; + + //Pass all events to recognizer + handled = m_gestureRecognizer.mouseEventFilter(static_cast(event)); + return handled; +} + +void ScrollableWebContentView::handleGesture(GestureEvent* gestureEvent) +{ + switch (gestureEvent->type()) { + case GestureEvent::Touch: + handlePress(gestureEvent); + break; + case GestureEvent::Release: + handleRelease(gestureEvent); + break; + case GestureEvent::Pan: + handlePan(gestureEvent); + break; + case GestureEvent::Flick: + handleFlick(gestureEvent); + break; + case GestureEvent::DoubleTap: + handleDoubleTap(gestureEvent); + break; + case GestureEvent::LongTap: + handleLongTap(gestureEvent); + break; + default: + break; + } + +} + +void ScrollableWebContentView::handlePress(GestureEvent* gestureEvent) +{ + m_kineticScroller->stop(); + QPointF pos = gestureEvent->position(); + sendEventToWebKit(QEvent::GraphicsSceneMousePress, pos); +} + +void ScrollableWebContentView::handleRelease(GestureEvent* gestureEvent) +{ + //Cache release event to send on release + QPointF pos = gestureEvent->position(); + sendEventToWebKit(QEvent::GraphicsSceneMouseRelease, pos); +} + +void ScrollableWebContentView::handleDoubleTap(GestureEvent* gestureEvent) +{ + if (!m_viewportMetaData->m_userScalable) + return; + + QRectF target; + WebContentAnimationItem* webViewProxy = viewportWidget(); + + // Contentview center is the focus hotspot + QPointF viewTargetHotspot(size().width() / 2, size().height() / 2); + + //Get the focussable element rect from current touch position + QPointF touchPoint = webViewProxy->mapFromScene(gestureEvent->position()); + QRectF zoomRect = webViewProxy->findZoomableRectForPoint(touchPoint); + + if (!zoomRect.isValid()) { + //FIX ME: Add an event ignore animation + return; + } + + // target is the center of the identified rect x-wise + // y-wise it's the place user touched + QPointF hotspot(zoomRect.center().x(), touchPoint.y()); + qreal scale = size().width() / zoomRect.size().width(); + startZoomAnimToItemHotspot(hotspot, viewTargetHotspot, scale, zoomRect); +} + +void ScrollableWebContentView::handlePan(GestureEvent* gestureEvent) +{ + QPoint scrollPos = ScrollableViewBase::scrollPosition(); + m_kineticScroller->doPan(gestureEvent->delta()); + QPoint delta; + delta.setX(-gestureEvent->delta().x()); + delta.setY(-gestureEvent->delta().y()); + emit viewScrolled(scrollPos, delta); +} + +void ScrollableWebContentView::handleFlick(GestureEvent* gestureEvent) +{ + QPoint scrollPos = ScrollableViewBase::scrollPosition(); + m_kineticScroller->doFlick(gestureEvent->velocity()); +} + +void ScrollableWebContentView::handleLongTap(GestureEvent* gestureEvent) +{ + QWebPage* page = viewportWidget()->webView()->page(); + QPointF contextPt = viewportWidget()->webView()->mapFromScene(gestureEvent->position()); + QWebHitTestResult result = page->currentFrame()->hitTestContent(contextPt.toPoint()); + + //Notify context menu observers + emit contextEventObject(&result); +} + +void ScrollableWebContentView::setViewportWidgetGeometry(const QRectF& r) +{ + ScrollableViewBase::setScrollWidgetGeometry(r); +} + +void ScrollableWebContentView::startZoomAnimToItemHotspot(const QPointF& hotspot, const QPointF& viewTargetHotspot, qreal scale, QRectF target) +{ + WebContentAnimationItem* animWidget = viewportWidget(); + + QPointF newHotspot = hotspot * scale; + QPointF newViewportOrigon = newHotspot - viewTargetHotspot; + QRectF zoomedRect(-newViewportOrigon, animWidget->size() * scale); + + QRectF temp = adjustScrollWidgetRect(zoomedRect); + qreal diff = qAbs(scrollWidget()->geometry().y() - temp.y()); + + //FIX ME : Seperate the logic for centerzoom and block-focus zoom + if (qFuzzyCompare(scrollWidget()->geometry().topLeft().x(), temp.topLeft().x()) + && qFuzzyCompare(scrollWidget()->geometry().width(), temp.width()) + && qFuzzyCompare(scrollWidget()->geometry().height(), temp.height()) + && !target.isEmpty() && (diff <= target.height())) { + + scale = size().width() / animWidget->size().width(); + newHotspot = QPointF(0, -animWidget->pos().y()) * scale; + newViewportOrigon = newHotspot - viewTargetHotspot; + zoomedRect = QRectF(-newViewportOrigon, animWidget->size() * scale); + } + + startZoomAnimation(zoomedRect); +} + +bool ScrollableWebContentView::isZoomedIn() const +{ + return size().width() < viewportWidget()->size().width(); +} + +void ScrollableWebContentView::stateChanged(KineticScrollable::State oldState + , KineticScrollable::State newState) +{ + ScrollableViewBase::stateChanged(oldState, newState); + + if (newState == KineticScrollable::Pushing) { + m_tileUpdateEnableTimer.stop(); + viewportWidget()->disableContentUpdates(); + } + else if (newState == KineticScrollable::AutoScrolling) { + m_tileUpdateEnableTimer.stop(); + viewportWidget()->disableContentUpdates(); + } + else if (newState == KineticScrollable::Inactive) { + m_tileUpdateEnableTimer.start(TileUpdateEnableDelay); + } +} + +void ScrollableWebContentView::startZoomAnimation(const QRectF& destRect) +{ + QAbstractAnimation::State animState = m_zoomAnimator->state(); + if (animState == QAbstractAnimation::Running) + return; + + m_zoomAnimator->setStartValue(scrollWidget()->geometry()); + m_animationEndRect = adjustScrollWidgetRect(destRect); + m_zoomAnimator->setEndValue(m_animationEndRect); + m_zoomAnimator->start(); +} + +void ScrollableWebContentView::stopZoomAnimation() +{ + m_animationEndRect = QRectF(); + m_zoomAnimator->stop(); +} + +void ScrollableWebContentView::updateZoomEndRect() +{ + if (m_animationEndRect.isValid()) + scrollWidget()->setGeometry(m_animationEndRect); +} + +void ScrollableWebContentView::zoomAnimationStateChanged(QAbstractAnimation::State newState,QAbstractAnimation::State) +{ + switch (newState) { + case QAbstractAnimation::Stopped: + updateZoomEndRect(); + break; + default: + break; + } +} + +void ScrollableWebContentView::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + QGraphicsWidget::resizeEvent(event); + + //Ignore resize when chrome is being still setup + if (!event->oldSize().width()) + return; + + adjustViewportSize(event->oldSize(), event->newSize()); +} + +void ScrollableWebContentView::adjustViewportSize(QSizeF oldSize, QSizeF newSize) +{ + //FIX ME : Check this + if (m_viewportMetaData->m_isValid) { + + QRect clientRect = geometry().toAlignedRect(); + if (m_viewportMetaData->isLayoutNeeded()) { + m_viewportMetaData->orientationChanged(clientRect); + updatePreferredContentSize(); + return; + } else + m_viewportMetaData->updateViewportData(viewportWidget()->contentsSize(), clientRect); + } + + qreal scale = newSize.width() / oldSize.width(); + QPointF middleLeft(0, oldSize.height()/2); + QPointF docPoint(viewportWidget()->mapFromScene(middleLeft)); + + QPointF resizedMiddleLeft(0, newSize.height()/2); + QPointF resizedDocPoint(viewportWidget()->mapFromScene(resizedMiddleLeft)); + QPointF docPointInScr(viewportWidget()->mapToParent(resizedDocPoint)); + + //FIX ME : Should be handled with only following function call + //Since its not working, work-around is added. Plz fix it + //startZoomAnimToItemHotspot(docPoint, docPointInScr, scale); + + QPointF newHotspot = docPoint * scale; + QPointF newViewportOrigon = newHotspot - docPointInScr; + QRectF zoomedRect(-newViewportOrigon, viewportWidget()->size() * scale); + QRectF adjustRect = adjustScrollWidgetRect(zoomedRect); + + setScrollWidgetGeometry(zoomedRect); +} + +void ScrollableWebContentView::sendEventToWebKit(QEvent::Type type, QPointF& scenPos) +{ + //Setup event and send it to webkit + QGraphicsSceneMouseEvent event(type); + event.setScenePos(scenPos); + event.setPos(viewportWidget()->webView()->mapFromScene(event.scenePos())); + event.setButton(Qt::LeftButton); + event.setButtons(Qt::LeftButton); + event.setModifiers(Qt::NoModifier); + + viewportWidget()->webView()->page()->event(&event); +} + +} //namespace GVA