void QtViewportInteractionEngine::focusEditableArea(const QRectF& caretArea, const QRectF& targetArea) { QRectF endArea = itemRectFromCSS(targetArea); qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(2.0)); const QRectF viewportRect = m_viewport->boundingRect(); qreal x; const qreal borderOffset = 10; if ((endArea.width() + borderOffset) * endItemScale <= viewportRect.width()) { // Center the input field in the middle of the view, if it is smaller than // the view at the scale target. x = viewportRect.center().x() - endArea.width() * endItemScale / 2.0; } else { // Ensure that the caret always has borderOffset contents pixels to the right // of it, and secondarily (if possible), that the area has borderOffset // contents pixels to the left of it. qreal caretOffset = itemCoordFromCSS(caretArea.x()) - endArea.x(); x = qMin(viewportRect.width() - (caretOffset + borderOffset) * endItemScale, borderOffset * endItemScale); } const QPointF hotspot = QPointF(endArea.x(), endArea.center().y()); const QPointF viewportHotspot = QPointF(x, /* FIXME: visibleCenter */ viewportRect.center().y()); 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); }
void PageViewportControllerClientQt::focusEditableArea(const QRectF& caretArea, const QRectF& targetArea) { // This can only happen as a result of a user interaction. ASSERT(m_controller->hadUserInteraction()); const float editingFixedScale = 2; float targetScale = m_controller->innerBoundedViewportScale(editingFixedScale); const QRectF viewportRect = m_viewportItem->boundingRect(); qreal x; const qreal borderOffset = 10; if ((targetArea.width() + borderOffset) * targetScale <= viewportRect.width()) { // Center the input field in the middle of the view, if it is smaller than // the view at the scale target. x = viewportRect.center().x() - targetArea.width() * targetScale / 2.0; } else { // Ensure that the caret always has borderOffset contents pixels to the right // of it, and secondarily (if possible), that the area has borderOffset // contents pixels to the left of it. qreal caretOffset = caretArea.x() - targetArea.x(); x = qMin(viewportRect.width() - (caretOffset + borderOffset) * targetScale, borderOffset * targetScale); } const QPointF hotspot = QPointF(targetArea.x(), targetArea.center().y()); const QPointF viewportHotspot = QPointF(x, /* FIXME: visibleCenter */ viewportRect.center().y()); QPointF endPosition = hotspot - viewportHotspot / targetScale; endPosition = m_controller->boundContentsPositionAtScale(endPosition, targetScale); QRectF endVisibleContentRect(endPosition, viewportRect.size() / targetScale); animateContentRectVisible(endVisibleContentRect); }
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); }
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); }
QRectF QtViewportHandler::initialRect() const { ASSERT(m_rawAttributes.initialScale > 0); qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(m_rawAttributes.initialScale)); const QRectF viewportRect = m_viewportItem->boundingRect(); QRectF endVisibleContentRect(QPointF(0, 0), viewportRect.size() / endItemScale); return endVisibleContentRect; }
void PageViewportController::pageDidRequestScroll(const IntPoint& cssPosition) { // Ignore the request if suspended. Can only happen due to delay in event delivery. if (m_hasSuspendedContent) return; FloatRect endVisibleContentRect(clampViewportToContents(cssPosition, m_effectiveScale), viewportSizeInContentsCoordinates()); if (m_lastFrameCoveredRect.intersects(endVisibleContentRect)) m_client->setViewportPosition(endVisibleContentRect.location()); else // Keep the unclamped position in case the contents size is changed later on. applyPositionAfterRenderingContents(cssPosition); }
void QtViewportInteractionEngine::pagePositionRequest(const QPoint& pagePosition) { // Ignore the request if suspended. Can only happen due to delay in event delivery. if (m_suspendCount) return; qreal endItemScale = m_content->contentsScale(); // Stay at same scale. QRectF endPosRange = computePosRangeForItemAtScale(endItemScale); QPointF endPosition = boundPosition(endPosRange.topLeft(), pagePosition * endItemScale, endPosRange.bottomRight()); QRectF endVisibleContentRect(endPosition / endItemScale, m_viewport->boundingRect().size() / endItemScale); setItemRectVisible(endVisibleContentRect); }
QRectF QtViewportHandler::nearestValidBounds() const { qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(currentCSSScale())); const QRectF viewportRect = m_viewportItem->boundingRect(); QPointF viewportHotspot = viewportRect.center(); QPointF endPosition = m_viewportItem->mapToWebContent(viewportHotspot) * endItemScale - viewportHotspot; QRectF endPosRange = computePosRangeForPageItemAtScale(endItemScale); endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight()); QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale); return endVisibleContentRect; }
void QtViewportHandler::pageContentPositionRequested(const QPoint& cssPosition) { // Ignore the request if suspended. Can only happen due to delay in event delivery. if (m_suspendCount) return; qreal endItemScale = m_pageItem->contentsScale(); // Stay at same scale. QRectF endPosRange = computePosRangeForPageItemAtScale(endItemScale); QPointF endPosition = boundPosition(endPosRange.topLeft(), cssPosition * endItemScale, endPosRange.bottomRight()); QRectF endVisibleContentRect(endPosition / endItemScale, m_viewportItem->boundingRect().size() / endItemScale); ViewportUpdateDeferrer guard(this); setPageItemRectVisible(endVisibleContentRect); }
void QtViewportHandler::zoomToAreaGestureEnded(const QPointF& touchPoint, const QRectF& targetArea) { // This can only happen as a result of a user interaction. ASSERT(m_hadUserInteraction); if (!targetArea.isValid()) return; if (m_suspendCount) 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_viewportItem->boundingRect(); qreal targetCSSScale = viewportRect.size().width() / endArea.size().width(); qreal endCSSScale = innerBoundedCSSScale(qMin(targetCSSScale, qreal(2.5))); qreal endItemScale = itemScaleFromCSS(endCSSScale); qreal currentScale = m_pageItem->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(), itemCoordFromCSS(touchPoint.y())); const QPointF viewportHotspot = viewportRect.center(); QPointF endPosition = hotspot * endCSSScale - viewportHotspot; QRectF endPosRange = computePosRangeForPageItemAtScale(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_viewportItem->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_viewportItem->contentPos().x())); m_zoomOutScale = endItemScale; break; case ZoomBack: { ScaleStackItem lastScale = m_scaleStack.takeLast(); endItemScale = lastScale.scale; endCSSScale = cssScaleFromItem(lastScale.scale); // Recalculate endPosition and bound it according to new scale. endPosition.setY(hotspot.y() * endCSSScale - viewportHotspot.y()); endPosition.setX(lastScale.xPosition); endPosRange = computePosRangeForPageItemAtScale(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; } animatePageItemRectVisible(endVisibleContentRect); }
void PageViewportControllerClientQt::zoomToAreaGestureEnded(const QPointF& touchPoint, const QRectF& targetArea) { // This can only happen as a result of a user interaction. ASSERT(m_controller->hadUserInteraction()); if (!targetArea.isValid()) return; if (m_scrollChange.inProgress() || m_scaleChange.inProgress()) return; const float margin = 10; // We want at least a little bit of margin. QRectF endArea = targetArea.adjusted(-margin, -margin, margin, margin); const QRectF viewportRect = m_viewportItem->boundingRect(); qreal minViewportScale = qreal(2.5); qreal targetScale = viewportRect.size().width() / endArea.size().width(); targetScale = m_controller->innerBoundedViewportScale(qMin(minViewportScale, targetScale)); qreal currentScale = m_pageItem->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()); const QPointF viewportHotspot = viewportRect.center(); QPointF endPosition = hotspot - viewportHotspot / targetScale; endPosition = m_controller->clampViewportToContents(endPosition, targetScale); QRectF endVisibleContentRect(endPosition, viewportRect.size() / targetScale); enum { ZoomIn, ZoomBack, ZoomOut, NoZoom } zoomAction = ZoomIn; // 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 (!m_scaleStack.isEmpty() && fuzzyCompare(targetScale, 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_viewportItem->mapRectToWebContent(viewportRect)); 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(targetScale, m_zoomOutScale, 0.01)) zoomAction = ZoomBack; else if (targetScale < currentScale) zoomAction = ZoomOut; switch (zoomAction) { case ZoomIn: m_scaleStack.append(ScaleStackItem(currentScale, m_viewportItem->contentPos().x() / currentScale)); m_zoomOutScale = targetScale; break; case ZoomBack: { if (m_scaleStack.isEmpty()) { targetScale = m_controller->minimumContentsScale(); endPosition.setY(hotspot.y() - viewportHotspot.y() / targetScale); endPosition.setX(0); m_zoomOutScale = 0; } else { ScaleStackItem lastScale = m_scaleStack.takeLast(); targetScale = lastScale.scale; // Recalculate endPosition and clamp it according to the new scale. endPosition.setY(hotspot.y() - viewportHotspot.y() / targetScale); endPosition.setX(lastScale.xPosition); } endPosition = m_controller->clampViewportToContents(endPosition, targetScale); endVisibleContentRect = QRectF(endPosition, viewportRect.size() / targetScale); 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 >= targetScale) m_scaleStack.removeLast(); m_zoomOutScale = targetScale; break; case NoZoom: break; } animateContentRectVisible(endVisibleContentRect); }