diff options
author | Bernd Lamecker <bernd.lamecker@basyskom.de> | 2010-03-05 11:58:10 +0100 |
---|---|---|
committer | Bernd Lamecker <bernd.lamecker@basyskom.de> | 2010-03-05 11:58:57 +0100 |
commit | f65a3a6e0264cd814f882e3944174aad5f509681 (patch) | |
tree | 1ecd3c93b556e05629abd132de7314885b9df0de | |
parent | 94434787eb7b5dadef65a05f1eb0c830125dba2b (diff) |
Changes: Bouncing for kinetic scrolling
RevBy: Torsten Rahn
-rw-r--r-- | plainqt/style/qtmaemo6kineticscrolling.cpp | 171 | ||||
-rw-r--r-- | plainqt/style/qtmaemo6kineticscrolling.h | 13 |
2 files changed, 135 insertions, 49 deletions
diff --git a/plainqt/style/qtmaemo6kineticscrolling.cpp b/plainqt/style/qtmaemo6kineticscrolling.cpp index ec7f2dbd..0948c9fd 100644 --- a/plainqt/style/qtmaemo6kineticscrolling.cpp +++ b/plainqt/style/qtmaemo6kineticscrolling.cpp @@ -20,7 +20,7 @@ #include "qtmaemo6kineticscrolling.h" #include <QAbstractItemView> -#include <QAbstractScrollArea> +#include <QScrollArea> #include <QApplication> #include <QList> #include <QMouseEvent> @@ -28,6 +28,10 @@ #include <QScrollBar> #include <QDebug> +void QtMaemo6KineticScrolling::KineticData::setState(State state) { + m_state = state; +} + QtMaemo6KineticScrolling::QtMaemo6KineticScrolling(QObject *parent) : QObject(parent), m_scrollStartDelay(50), @@ -54,8 +58,8 @@ void QtMaemo6KineticScrolling::enableOn(QAbstractScrollArea *scrollArea) m_kineticData.remove(viewport); m_kineticData[viewport] = new KineticData; - m_kineticData[viewport]->widget = scrollArea; - m_kineticData[viewport]->state = KineticData::Waiting; + m_kineticData[viewport]->scrollArea = scrollArea; + m_kineticData[viewport]->setState(KineticData::Waiting); if(QAbstractItemView* itemView = qobject_cast<QAbstractItemView*>(scrollArea)) { //FIXME: public API usage... scrollPerItem prevents the kinetic from working @@ -66,30 +70,56 @@ void QtMaemo6KineticScrolling::enableOn(QAbstractScrollArea *scrollArea) return; } -static QPoint scrollOffset(QWidget *widget, bool rightToLeft) +static QPoint scrollOffset(QAbstractScrollArea *scrollArea, bool rightToLeft) { - QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget); - if (scrollArea) { - if(rightToLeft) - return QPoint(scrollArea->horizontalScrollBar()->maximum() - scrollArea->horizontalScrollBar()->value(), - scrollArea->verticalScrollBar()->value()); - else - return QPoint(scrollArea->horizontalScrollBar()->value(), - scrollArea->verticalScrollBar()->value()); - } - return QPoint(); + if(rightToLeft) + return QPoint(scrollArea->horizontalScrollBar()->maximum() - scrollArea->horizontalScrollBar()->value(), + scrollArea->verticalScrollBar()->value()); + else + return QPoint(scrollArea->horizontalScrollBar()->value(), + scrollArea->verticalScrollBar()->value()); } -static void setScrollOffset(QWidget *widget, const QPoint &p, bool rightToLeft) +static int scrollRange(QScrollBar* scrollBar) { - QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget); - if (scrollArea) { - if(rightToLeft) - scrollArea->horizontalScrollBar()->setValue(scrollArea->horizontalScrollBar()->maximum() - p.x()); - else - scrollArea->horizontalScrollBar()->setValue(p.x()); - scrollArea->verticalScrollBar()->setValue(p.y()); - } + return scrollBar->maximum() - scrollBar->minimum(); +} + +static bool setScrollOffset(QAbstractScrollArea *scrollArea, const QPoint &p, bool rightToLeft) +{ + bool ret = true; + bool hasHorizontalScrolling = scrollRange(scrollArea->horizontalScrollBar()) > 0; + bool hasVerticalScrolling = scrollRange(scrollArea->verticalScrollBar()) > 0; + + bool xOffsetInRange = p.x() >= scrollArea->horizontalScrollBar()->minimum() + && p.x() <= scrollArea->horizontalScrollBar()->maximum(); + bool yOffsetInRange = p.y() >= scrollArea->verticalScrollBar()->minimum() + && p.y() <= scrollArea->verticalScrollBar()->maximum(); + + if(hasHorizontalScrolling && !xOffsetInRange) + ret = false; + if(hasVerticalScrolling && !yOffsetInRange) + ret = false; + + //...but scroll at least to the end of the scrollbar anyway + QPoint realScroll = p; + if(realScroll.x() < scrollArea->horizontalScrollBar()->minimum()) + realScroll.setX(scrollArea->horizontalScrollBar()->minimum()); + if(realScroll.x() > scrollArea->horizontalScrollBar()->maximum()) + realScroll.setX(scrollArea->horizontalScrollBar()->maximum()); + if(realScroll.y() < scrollArea->verticalScrollBar()->minimum()) + realScroll.setY(scrollArea->verticalScrollBar()->minimum()); + if(realScroll.y() > scrollArea->verticalScrollBar()->maximum()) + realScroll.setY(scrollArea->verticalScrollBar()->maximum()); + + if(rightToLeft) + scrollArea->horizontalScrollBar()->setValue(scrollArea->horizontalScrollBar()->maximum() - p.x()); + else + scrollArea->horizontalScrollBar()->setValue(p.x()); + + scrollArea->verticalScrollBar()->setValue(p.y()); + + return ret; } static QPoint deaccelerate(const QPoint &speed, int a, int max) @@ -105,8 +135,7 @@ static QPoint deaccelerate(const QPoint &speed, int a, int max) y = qMax(0, y - a); else y = qMin(0, y + a); - //x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a); - //y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a); + return QPoint(x, y); } @@ -154,15 +183,15 @@ bool QtMaemo6KineticScrolling::eventFilter(QObject *object, QEvent *event) bool consumed = false; - switch (data->state) { + switch (data->state()) { case KineticData::Waiting: if (mouseEvent->type() == QEvent::MouseButtonPress) if (mouseEvent->buttons() == Qt::LeftButton) { consumed = true; - data->state = KineticData::Pressed; + data->setState(KineticData::Pressed); data->pressPos = mouseEvent->pos(); data->pressedWidget = qobject_cast<QWidget*>(object); - data->offset = scrollOffset(data->widget, m_rightToLeft); + data->offset = scrollOffset(data->scrollArea, m_rightToLeft); if (!m_ticker.isActive()) m_ticker.start(m_scrollStartDelay, this); } @@ -171,7 +200,7 @@ bool QtMaemo6KineticScrolling::eventFilter(QObject *object, QEvent *event) case KineticData::Pressed: if (mouseEvent->type() == QEvent::MouseButtonRelease) { consumed = true; - data->state = KineticData::Waiting; + data->setState(KineticData::Waiting); QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress, data->pressPos, Qt::LeftButton, @@ -187,7 +216,7 @@ bool QtMaemo6KineticScrolling::eventFilter(QObject *object, QEvent *event) consumed = true; QPoint offset = mouseEvent->pos() - data->pressPos; if(offset.manhattanLength() > m_scrollStartOffset) { - data->state = KineticData::Panning; + data->setState(KineticData::Panning); data->dragPos = QCursor::pos(); if(m_ticker.isActive()) m_ticker.stop(); @@ -205,11 +234,11 @@ bool QtMaemo6KineticScrolling::eventFilter(QObject *object, QEvent *event) if(speed != QPoint(0,0)) data->speed = speed; data->dragPos = QCursor::pos(); - setScrollOffset(data->widget, data->offset - delta, m_rightToLeft); + setScrollOffset(data->scrollArea, data->offset - delta, m_rightToLeft); } if (mouseEvent->type() == QEvent::MouseButtonRelease) { consumed = true; - data->state = KineticData::KineticScroll; + data->setState(KineticData::KineticScroll); if (!m_ticker.isActive()) m_ticker.start(m_deaccelerationInterval, this); } @@ -218,13 +247,13 @@ bool QtMaemo6KineticScrolling::eventFilter(QObject *object, QEvent *event) case KineticData::KineticScroll: if (mouseEvent->type() == QEvent::MouseButtonPress) { consumed = true; - data->state = KineticData::Stop; + data->setState(KineticData::Stop); if (!m_ticker.isActive()) m_ticker.start(m_scrollStartDelay, this); } if (mouseEvent->type() == QEvent::MouseButtonRelease) { consumed = true; - data->state = KineticData::Waiting; + data->setState(KineticData::Waiting); data->speed = QPoint(0, 0); } break; @@ -232,11 +261,11 @@ bool QtMaemo6KineticScrolling::eventFilter(QObject *object, QEvent *event) case KineticData::Stop: if (mouseEvent->type() == QEvent::MouseButtonRelease) { consumed = true; - data->state = KineticData::Waiting; + data->setState(KineticData::Waiting); } if (mouseEvent->type() == QEvent::MouseMove) { consumed = true; - data->state = KineticData::Panning; + data->setState(KineticData::Panning); data->dragPos = QCursor::pos(); if (!m_ticker.isActive()) m_ticker.start(m_deaccelerationInterval, this); @@ -256,26 +285,78 @@ void QtMaemo6KineticScrolling::timerEvent(QTimerEvent *event) while (item.hasNext()) { item.next(); KineticData *data = item.value(); - if( data->state == KineticData::Pressed) { + if( data->state() == KineticData::Pressed) { m_ticker.stop(); QMouseEvent *newPressEvent = new QMouseEvent(QEvent::MouseButtonPress, data->pressPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); data->ignored << newPressEvent; - data->state = KineticData::Waiting; + data->setState(KineticData::Waiting); QApplication::postEvent(data->pressedWidget, newPressEvent); } - if (data->state == KineticData::KineticScroll) { + if (data->state() == KineticData::KineticScroll) { data->speed = deaccelerate(data->speed, m_deaccelerationStrength, m_maxKineticScrollSpeed); - QPoint p = scrollOffset(data->widget, m_rightToLeft); - setScrollOffset(data->widget, p - data->speed, m_rightToLeft); - if (data->speed == QPoint(0, 0)) { - data->state = KineticData::Waiting; + QPoint p = scrollOffset(data->scrollArea, m_rightToLeft); + bool scrollOffsetSet = setScrollOffset(data->scrollArea, p - data->speed, m_rightToLeft); + + qCritical() << "Start Bounce" << scrollOffsetSet << data->speed.manhattanLength(); + if(!scrollOffsetSet && data->speed.manhattanLength() > 0) { + data->viewportOrigPos = data->scrollArea->viewport()->pos(); + data->setState(KineticData::Bounce); + } else if(data->speed.manhattanLength() == 0) { + data->setState(KineticData::Waiting); + m_ticker.stop(); + } + } + if(data->state() == KineticData::Bounce) { + QPoint speed = data->speed; + //only bounce on axis that have a valid scrollrange + if(scrollRange(data->scrollArea->horizontalScrollBar()) == 0) + speed.setX(0); + if(scrollRange(data->scrollArea->verticalScrollBar()) == 0) + speed.setY(0); + data->scrollArea->viewport()->move(data->scrollArea->viewport()->pos() + speed); + + //deaccelerate here by halveing the speed in each step, but care for the speed to stay bigger than 0 + int deaccelerationStrength = qMax(qAbs(data->speed.x()), qAbs(data->speed.y())) / 2; + if( deaccelerationStrength == 0) + deaccelerationStrength = 1; + + data->speed = deaccelerate(data->speed, deaccelerationStrength, m_maxKineticScrollSpeed); + if(data->speed == QPoint(0,0)) { + data->setState(KineticData::BounceBack); + } + } + if (data->state() == KineticData::BounceBack) { + QPoint pos = data->scrollArea->viewport()->pos(); + //bounce back to orignal position, this is required if e.g. headers are enabled + pos -= data->viewportOrigPos; + QPoint bounceStep; + + //limit the last bounceStep to, to bounce back to original position + int stepX = 10; + if(qAbs(pos.x()) - stepX < 0) + stepX = qAbs(pos.x()); + int stepY = 10; + if(qAbs(pos.y()) - stepY < 0) + stepY = qAbs(pos.y()); + + //invert the step if bouncing positions are negative + if(pos.x() < 0) + stepX = -stepX; + if(pos.y() < 0) + stepY = -stepY; + + bounceStep = QPoint(stepX, stepY); + + data->scrollArea->viewport()->move(data->scrollArea->viewport()->pos() - bounceStep); + if(bounceStep.manhattanLength() == 0) { + data->setState(KineticData::Waiting); m_ticker.stop(); } } - if (data->state == KineticData::Stop) { - data->state = KineticData::Waiting; + if (data->state() == KineticData::Stop) { + data->setState(KineticData::Waiting); data->speed = QPoint(0,0); m_ticker.stop(); } diff --git a/plainqt/style/qtmaemo6kineticscrolling.h b/plainqt/style/qtmaemo6kineticscrolling.h index 62ea45a6..e10a4908 100644 --- a/plainqt/style/qtmaemo6kineticscrolling.h +++ b/plainqt/style/qtmaemo6kineticscrolling.h @@ -153,16 +153,21 @@ protected: void installEventFilter(QObjectList list); private: - struct KineticData { - typedef enum { Waiting, Pressed, Panning, KineticScroll, Stop } State; - State state; - QWidget *widget; + class KineticData { + public: + typedef enum { Waiting, Pressed, Panning, KineticScroll, Bounce, BounceBack, Stop } State; + void setState(State state); + State state() const { return m_state; }; + QAbstractScrollArea *scrollArea; QWidget* pressedWidget; QPoint pressPos; QPoint offset; QPoint dragPos; QPoint speed; + QPoint viewportOrigPos; QList<QEvent*> ignored; + protected: + State m_state; }; QHash<QWidget*, KineticData*> m_kineticData; |