qsmoothscrollarea.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #include <QWheelEvent>
  2. #include <QApplication>
  3. #include <QScrollBar>
  4. #include <QTimer>
  5. #include <QDebug>
  6. #include <QDateTime>
  7. #include <QQueue>
  8. #include <qmath.h>
  9. #include "qsmoothscrollarea.h"
  10. QSmoothScrollArea::QSmoothScrollArea(QWidget *parent) :
  11. QScrollArea(parent)
  12. {
  13. lastWheelEvent = 0;
  14. smoothMoveTimer = new QTimer(this);
  15. connect(smoothMoveTimer, SIGNAL(timeout()), this, SLOT(slotSmoothMove()));
  16. m_fps = 60;
  17. m_duration = 500;
  18. m_smoothMode = CONSTANT;
  19. m_acceleration = 1.5;
  20. m_smallStepModifier = Qt::SHIFT;
  21. m_smallStepRatio = 1.0 / 5.0;
  22. m_bigStepModifier = Qt::ALT;
  23. m_bigStepRatio = 5.0;
  24. }
  25. int QSmoothScrollArea::fps()
  26. {
  27. return m_fps;
  28. }
  29. void QSmoothScrollArea::setFps(int fps)
  30. {
  31. m_fps = fps;
  32. }
  33. int QSmoothScrollArea::duration()
  34. {
  35. return m_duration;
  36. }
  37. void QSmoothScrollArea::setDuration(int mesc)
  38. {
  39. m_duration = mesc;
  40. }
  41. QSmoothScrollArea::SmoothMode QSmoothScrollArea::smoothMode()
  42. {
  43. return m_smoothMode;
  44. }
  45. void QSmoothScrollArea::setSmoothMode(QSmoothScrollArea::SmoothMode mode)
  46. {
  47. m_smoothMode = mode;
  48. }
  49. double QSmoothScrollArea::acceration()
  50. {
  51. return m_acceleration;
  52. }
  53. void QSmoothScrollArea::setAcceration(double acceleration)
  54. {
  55. m_acceleration = acceleration;
  56. }
  57. double QSmoothScrollArea::smallStepRatio()
  58. {
  59. return m_smallStepRatio;
  60. }
  61. void QSmoothScrollArea::setSmallStepRatio(double smallStepRatio)
  62. {
  63. m_smallStepRatio = smallStepRatio;
  64. }
  65. double QSmoothScrollArea::bigStepRatio()
  66. {
  67. return m_bigStepRatio;
  68. }
  69. void QSmoothScrollArea::setBigStepRatio(double bigStepRatio)
  70. {
  71. m_bigStepRatio = bigStepRatio;
  72. }
  73. Qt::Modifier QSmoothScrollArea::smallStepModifier()
  74. {
  75. return m_smallStepModifier;
  76. }
  77. void QSmoothScrollArea::setSmallStepModifier(
  78. Qt::Modifier smallStepModifier)
  79. {
  80. m_smallStepModifier = smallStepModifier;
  81. }
  82. Qt::Modifier QSmoothScrollArea::bigStepModifier()
  83. {
  84. return m_bigStepModifier;
  85. }
  86. void QSmoothScrollArea::setbigStepModifier(
  87. Qt::Modifier bigStepModifier)
  88. {
  89. m_bigStepModifier = bigStepModifier;
  90. }
  91. void QSmoothScrollArea::wheelEvent(QWheelEvent *e)
  92. {
  93. if (m_smoothMode == NO_SMOOTH) {
  94. QScrollArea::wheelEvent(e);
  95. return;
  96. }
  97. // According to my experiment, a normal person is able to scroll his wheel
  98. // at the speed about 36 times per second in average. Here we use a
  99. // conservative value 30: a user can achieve the maximum acceration when he
  100. // scrools his wheel at 30 times / second.
  101. static QQueue<qint64> scrollStamps;
  102. qint64 now = QDateTime::currentDateTime().toMSecsSinceEpoch();
  103. scrollStamps.enqueue(now);
  104. while (now - scrollStamps.front() > 500)
  105. scrollStamps.dequeue();
  106. double accerationRatio = qMin(scrollStamps.size() / 15.0, 1.0);
  107. if (!lastWheelEvent)
  108. lastWheelEvent = new QWheelEvent(*e);
  109. else
  110. *lastWheelEvent = *e;
  111. stepsTotal = m_fps * m_duration / 1000;
  112. double multiplier = 1.0;
  113. if (QApplication::keyboardModifiers() & smallStepModifier())
  114. multiplier *= smallStepRatio();
  115. if (QApplication::keyboardModifiers() & bigStepModifier())
  116. multiplier *= bigStepRatio();
  117. double delta = e->delta() * multiplier;
  118. if (acceration() > 0)
  119. delta += delta * acceration() * accerationRatio;
  120. stepsLeftQueue.push_back(qMakePair(delta, stepsTotal));
  121. smoothMoveTimer->start(1000 / m_fps);
  122. }
  123. void QSmoothScrollArea::slotSmoothMove()
  124. {
  125. double totalDelta = 0;
  126. for (QList< QPair<double, int> >::Iterator it = stepsLeftQueue.begin();
  127. it != stepsLeftQueue.end(); ++it)
  128. {
  129. totalDelta += subDelta(it->first, it->second);
  130. --(it->second);
  131. }
  132. while (!stepsLeftQueue.empty() && stepsLeftQueue.begin()->second == 0)
  133. stepsLeftQueue.pop_front();
  134. Qt::Orientation orientation = lastWheelEvent->orientation();
  135. // By default, when you press ALT, QT will scroll horizontally. But if we
  136. // have defined the use of ALT key, we ignore this setting since horizontal
  137. // scroll is not so useful in okular
  138. if ((bigStepModifier() & Qt::ALT) || (smallStepModifier() & Qt::ALT))
  139. orientation = Qt::Vertical;
  140. QWheelEvent e(
  141. lastWheelEvent->pos(),
  142. lastWheelEvent->globalPos(),
  143. qRound(totalDelta),
  144. lastWheelEvent->buttons(),
  145. 0,
  146. orientation
  147. );
  148. if (e.orientation() == Qt::Horizontal)
  149. QApplication::sendEvent(horizontalScrollBar(), &e);
  150. else
  151. QApplication::sendEvent(verticalScrollBar(), &e);
  152. if (stepsLeftQueue.empty()) {
  153. smoothMoveTimer->stop();
  154. }
  155. }
  156. double QSmoothScrollArea::subDelta(double delta, int stepsLeft)
  157. {
  158. Q_ASSERT(m_smoothMode != NO_SMOOTH);
  159. double m = stepsTotal / 2.0;
  160. double x = abs(stepsTotal - stepsLeft - m);
  161. // some mathmatical integral result.
  162. switch (m_smoothMode) {
  163. case NO_SMOOTH:
  164. return 0;
  165. break;
  166. case CONSTANT:
  167. return double(delta) / stepsTotal;
  168. break;
  169. case LINEAR:
  170. return 2.0*delta/stepsTotal * (m - x) / m;
  171. break;
  172. case QUADRATIC:
  173. return 3.0/4.0/m * (1.0 - x*x/m/m) * delta;
  174. break;
  175. case COSINE:
  176. return (cos(x * M_PI / m) + 1.0) / (2.0*m) * delta;
  177. break;
  178. }
  179. return 0;
  180. }