/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: http://www.qt-project.org/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include "scroller.h" #include "scroller_p.h" #include "abstractscrollarea.h" #include "scrollbar.h" const int ScrollStep = 1; const int UpdateScrollingInterval = 55; const int UpdateScrollingSmoothInterval = 0; static const qreal MaxScrollingSpeed = 48.0; ScrollerPrivate::ScrollerPrivate(Scroller *scroller) : m_scrollArea(0) , m_scrollFactor(1.0) , m_state(Stopped) , q_ptr(scroller) , m_eventViewport(0) { } ScrollerPrivate::~ScrollerPrivate() { } void ScrollerPrivate::stopScrolling() { m_state = ScrollerPrivate::Started; m_cursorPos = QCursor::pos(); m_speed = QPoint(0, 0); if (m_scrollTimer.isActive()) m_scrollTimer.stop(); } //Maps screen coordinates to scrollArea coordinates though current m_eventViewport widget QPointF ScrollerPrivate::mapToScrollArea(const QPoint &point) { if (!m_scrollArea || !m_eventViewport) return point; QObject *vparent = m_eventViewport->parent(); if (!vparent) return point; QGraphicsView *view = qobject_cast(vparent); if (!view) return point; QPoint pt = view->mapFromGlobal(point); return m_scrollArea->mapFromScene(view->mapToScene(pt)); } bool ScrollerPrivate::eventFilter(QObject *obj, QEvent *event) { if (obj != m_scrollArea || (event->type() != QEvent::GraphicsSceneMouseMove && event->type() != QEvent::GraphicsSceneMousePress && event->type() != QEvent::GraphicsSceneMouseRelease /*&& event->type() != QEvent::GraphicsSceneKeyPressed && event->type() != QEvent::GraphicsSceneKeyReleased*/)) return false; QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); m_eventViewport = mouseEvent->widget(); bool eventConsumed = false; switch (m_state) { case ScrollerPrivate::Stopped: if (mouseEvent->type() == QEvent::GraphicsSceneMousePress && mouseEvent->buttons() == Qt::LeftButton) { m_cursorPos = QCursor::pos(); m_speed = QPointF(0.0, 0.0); m_state = Started; } eventConsumed = true; break; case ScrollerPrivate::Started: if (mouseEvent->type() == QEvent::GraphicsSceneMouseMove) { m_cursorPos = QCursor::pos(); m_state = ManualScrolling; if (!m_scrollTimer.isActive()) m_scrollTimer.start(UpdateScrollingInterval); else { m_scrollTimer.stop(); m_scrollTimer.start(UpdateScrollingInterval); } } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { m_speed = QPoint(0, 0); m_state = Stopped; if (m_scrollTimer.isActive()) m_scrollTimer.stop(); } eventConsumed = true; break; case ScrollerPrivate::ManualScrolling: if (mouseEvent->type() == QEvent::GraphicsSceneMouseMove && m_scrollArea->viewport()->boundingRect().contains(mouseEvent->pos()) ) { ScrollBar *hscroll = m_scrollArea->horizontalScrollBar(); ScrollBar *vscroll = m_scrollArea->verticalScrollBar(); QPointF d = m_scrollFactor * (mapToScrollArea(QCursor::pos()) - mapToScrollArea(m_cursorPos)); hscroll->setSliderPosition(hscroll->sliderPosition() - d.x()); vscroll->setSliderPosition(vscroll->sliderPosition() - d.y()); if (m_lastCursorTime.elapsed() > UpdateScrollingInterval) { m_speed = mapToScrollArea(QCursor::pos()) - mapToScrollArea(m_cursorPos); m_lastCursorTime.restart(); } m_lastFrameTime.restart(); m_cursorPos = QCursor::pos(); } else if (mouseEvent->type() == QEvent::GraphicsSceneMouseRelease) { m_state = AutoScrolling; m_scrollSlowAccum = 0; if (m_scrollTimer.isActive()) { m_scrollTimer.stop(); m_scrollTimer.start(UpdateScrollingSmoothInterval); } } eventConsumed = true; break; case ScrollerPrivate::AutoScrolling: if (mouseEvent->type() == QEvent::GraphicsSceneMousePress) { stopScrolling(); } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { m_state = Stopped; } eventConsumed = true; break; default: break; } return eventConsumed; } void ScrollerPrivate::updateScrolling() { bool scrollOngoing = false; if (!m_scrollArea) { m_scrollTimer.stop(); return; } if (m_state == ManualScrolling) { scrollOngoing = true; m_speed = mapToScrollArea(QCursor::pos()) - mapToScrollArea(m_cursorPos); m_cursorPos = QCursor::pos(); } else if (m_state == AutoScrolling) { scrollOngoing = true; qreal x = qMax(-MaxScrollingSpeed, qMin(m_speed.x(), MaxScrollingSpeed)); qreal y = qMax(-MaxScrollingSpeed, qMin(m_speed.y(), MaxScrollingSpeed)); int sinceLast = m_lastFrameTime.elapsed(); int slowdown = (ScrollStep * sinceLast) + m_scrollSlowAccum; m_scrollSlowAccum = slowdown & 0x3F; slowdown >>= 6; if (x > 0) x= qMax(qreal(0.0), x - slowdown); else x = qMin(qreal(0.0), x + slowdown); if (y > 0) y = qMax(qreal(0.0), y - slowdown); else y = qMin(qreal(0.0), y + slowdown); m_speed = QPoint(x,y); if (m_speed != QPoint(0,0)) { QPointF d; int xstep = (int(m_speed.x()) * sinceLast)>>6; // >>6 ~= *60 /1000 (==*64 /1024) int ystep = (int(m_speed.y()) * sinceLast)>>6; //qDebug() << sinceLast << "speedy" << speed.y()<<"ystep" << ystep; QPoint step = QPoint(xstep,ystep); if (ystep > 0) d = (m_scrollArea->pos() + step); else d = -(m_scrollArea->pos() - step); ScrollBar *hscroll = m_scrollArea->horizontalScrollBar(); ScrollBar *vscroll = m_scrollArea->verticalScrollBar(); hscroll->setSliderPosition(hscroll->sliderPosition() - m_scrollFactor * d.x()); vscroll->setSliderPosition(vscroll->sliderPosition() - m_scrollFactor * d.y()); } else { m_state = Stopped; scrollOngoing = false; } } m_lastFrameTime.restart(); if (!scrollOngoing) m_scrollTimer.stop(); } Scroller::Scroller(QObject *parent) : QObject(parent), d_ptr(new ScrollerPrivate(this)) { Q_D(Scroller); connect(&d->m_scrollTimer, SIGNAL(timeout()), this, SLOT(updateScrolling())); } Scroller::~Scroller() { delete d_ptr; } void Scroller::setScrollable(AbstractScrollArea *area) { Q_D(Scroller); if (!area) return; d->m_scrollArea = area; } void Scroller::setScrollFactor(qreal scrollFactor) { Q_D(Scroller); d->m_scrollFactor = scrollFactor; } bool Scroller::eventFilter(QObject *obj, QEvent *event) { Q_D(Scroller); return d->eventFilter(obj, event); } void Scroller::stopScrolling() { Q_D(Scroller); d->stopScrolling(); } #include "moc_scroller.cpp"