--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ginebra2/ContentViews/ScrollableWebContentView.cpp Tue Jun 29 00:46:29 2010 -0400
@@ -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 <QApplication>
+#include <QGraphicsScene>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsWebView>
+#include <QWebElement>
+#include <QWebHitTestResult>
+
+//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<WebContentAnimationItem*>(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<QString, QString> 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<QGraphicsSceneMouseEvent *>(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