• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • workspace API Reference
  • KDE Home
  • Contact Us
 

liblancelot

  • sources
  • kde-4.14
  • workspace
  • kdeplasma-addons
  • libs
  • lancelot
  • widgets
kineticscroll.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 Igor Trindade Oliveira <igor.oliveira@indt.org.br>
3  Copyright (C) 2009 Adenilson Cavalcanti <adenilson.silva@idnt.org.br>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "kineticscroll_p.h"
20 
21 #include <QtCore/qglobal.h>
22 #include <QtCore/qmetatype.h>
23 #include <QGraphicsScene>
24 #include <QGraphicsSceneMouseEvent>
25 #include <QGraphicsSceneWheelEvent>
26 #include <QTime>
27 #include <QGraphicsWidget>
28 #include <QPoint>
29 #include <QPropertyAnimation>
30 #include <QCursor>
31 
32 #include <kdebug.h>
33 
34 /* TODO:
35  * - clean up the code(remove duplicated code, constify)
36  * - port to Plasma::Animator
37  */
38 
39 namespace Plasma
40 {
41 
42 class KineticScrollingPrivate
43 {
44 public:
45  enum Direction {
46  None,
47  Up,
48  Down,
49  Left,
50  Right
51  };
52 
53  enum Gesture {
54  GestureNone = 0,
55  GestureUndefined,
56  GestureScroll,
57  GestureZoom
58  };
59 
60  KineticScrollingPrivate()
61  : overshoot(20),
62  hasOvershoot(true),
63  parent(0),
64  forwardingEvent(false),
65  multitouchGesture(GestureNone)
66  {
67  }
68 
69  void count()
70  {
71  t = QTime::currentTime();
72  }
73 
74  void syncViewportRect()
75  {
76  contentsSize = parent->property("contentsSize").toSizeF();
77  viewportGeometry = parent->property("viewportGeometry").toRectF();
78  }
79 
80  bool canScroll(Direction direction, bool hasOvershoot = false) const
81  {
82  QPointF scrollPosition = -parent->property("scrollPosition").value<QPointF>();
83  int offset = (hasOvershoot?overshoot*2:0);
84 
85  switch (direction) {
86  case Up:
87  return (scrollPosition.y() < offset);
88  case Down:
89  return (scrollPosition.y() + contentsSize.height() + offset >= viewportGeometry.bottom());
90  case Left:
91  return (scrollPosition.x() < offset);
92  case Right:
93  return (scrollPosition.x() + contentsSize.width() + offset >= viewportGeometry.right());
94  default:
95  return true;
96  }
97  }
98 
99 
100  QPointF kinMovement;
101 
102  enum BounceStatus {
103  Running,
104  Finished
105  };
106 
107  BounceStatus bounceStatus;
108 
109  QPropertyAnimation *scrollAnimation;
110 
111  int overshoot;
112  QPointF cposition;
113  bool hasOvershoot;
114  QGraphicsWidget *parent;
115  QRectF viewportGeometry;
116  QSizeF contentsSize;
117  QPointF maximum, minimum;
118  bool forwardingEvent;
119  Gesture multitouchGesture;
120 
121  unsigned int timeDelta;
122  QTime t;
123 };
124 
125 
126 KineticScrolling::KineticScrolling(QGraphicsWidget *parent)
127  : d(new KineticScrollingPrivate)
128 {
129  setWidget(parent);
130 }
131 
132 KineticScrolling::~KineticScrolling()
133 {
134  delete d;
135 }
136 
137 void KineticScrolling::duration( )
138 {
139  d->timeDelta = d->t.msecsTo(QTime::currentTime());
140 }
141 
142 void KineticScrolling::overshoot()
143 {
144  QPointF scrollPosition = -d->parent->property("scrollPosition").value<QPointF>();
145 
146  if (!d->canScroll(KineticScrollingPrivate::Down) &&
147  !d->canScroll(KineticScrollingPrivate::Up)) {
148  return;
149  }
150 
151  if (d->bounceStatus != KineticScrollingPrivate::Running) {
152  if ((d->cposition.y() > 0 ) || (d->cposition.y() <= d->minimum.y() + d->overshoot)) {
153  QPointF finalPosition;
154  d->scrollAnimation->setEasingCurve( QEasingCurve::OutBounce );
155 
156  if (d->cposition.y() > 0) {
157  finalPosition = QPointF(scrollPosition.x(), 0);
158  } else {
159  finalPosition = QPointF(d->cposition.x(),
160  -d->contentsSize.height( ) + d->parent->size().height());
161  }
162 
163  resetAnimation(-finalPosition, 900);
164  d->bounceStatus = KineticScrollingPrivate::Running;
165  }
166  } else {
167  d->bounceStatus = KineticScrollingPrivate::Finished;
168  d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
169  }
170 }
171 
172 void KineticScrolling::setScrollValue(QPointF value)
173 {
174  const QPointF pos = thresholdPosition(value);
175  QPointF posf(-pos);
176  d->parent->setProperty("scrollPosition", posf);
177 
178  if ((pos.y() == d->overshoot) || (pos.y() == d->minimum.y())) {
179  overshoot();
180  }
181 }
182 
183 QPointF KineticScrolling::thresholdPosition(QPointF value) const
184 {
185  d->minimum.setX(-d->contentsSize.width() + d->viewportGeometry.width());
186  d->minimum.setY(-d->contentsSize.height() + d->viewportGeometry.height()
187  -d->overshoot);
188 
189  d->minimum.setY(qMin((qreal)d->overshoot, d->minimum.y()));
190  d->maximum = value;
191 
192  if(d->minimum.x() >= 0) {
193  d->cposition.setX(value.x());
194  } else {
195  d->cposition.setX(qBound(d->minimum.x(), d->maximum.x(), qreal(0)));
196  }
197 
198  if((-d->contentsSize.height() + d->viewportGeometry.height() - d->overshoot) >= 0) {
199  d->cposition.setY(value.y());
200  } else {
201  d->cposition.setY(qBound(d->minimum.y(), d->maximum.y(), qreal(d->overshoot)));
202  }
203 
204  return d->cposition;
205 }
206 
207 void KineticScrolling::resetAnimation(QPointF finalPosition, int duration)
208 {
209  if (d->scrollAnimation->state() != QAbstractAnimation::Stopped) {
210  d->scrollAnimation->stop();
211  }
212 
213  d->cposition = -finalPosition;
214  QPointF tmpPosition = d->parent->property("scrollPosition").value<QPointF>();
215  d->scrollAnimation->setStartValue(tmpPosition);
216 
217  tmpPosition = finalPosition;
218 
219  d->scrollAnimation->setEndValue(tmpPosition);
220  d->scrollAnimation->setDuration(duration);
221  d->scrollAnimation->start();
222 
223 }
224 
225 void KineticScrolling::mousePressEvent(QGraphicsSceneMouseEvent *event)
226 {
227  Q_UNUSED(event);
228 
229  if (d->scrollAnimation->state() != QAbstractAnimation::Stopped) {
230  d->scrollAnimation->stop();
231  }
232 
233  d->syncViewportRect();
234  d->cposition = -d->parent->property("scrollPosition").value<QPointF>();
235 
236  d->count();
237  d->kinMovement = QPointF(0,0);
238  d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
239 }
240 
241 void KineticScrolling::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
242 {
243  QPointF movement = event->lastPos().toPoint() - event->pos().toPoint();
244  const QPointF scrollPosition = -d->parent->property("scrollPosition").value<QPointF>();
245 
246  if (!movement.isNull()) {
247  if ((d->contentsSize.width() < d->viewportGeometry.width()) &&
248  (d->contentsSize.height() < d->viewportGeometry.height())) {
249  d->kinMovement = QPointF(0, 0);
250  movement = QPointF(0, 0);
251  } else if (d->contentsSize.height() < d->viewportGeometry.height()) {
252  d->kinMovement += QPointF(movement.x(), 0);
253  movement.setY(0);
254  } else if (d->contentsSize.width() < d->viewportGeometry.width()) {
255  d->kinMovement = QPointF(0, movement.y());
256  movement.setX(0);
257  } else {
258  d->kinMovement += movement;
259  }
260  setScrollValue(scrollPosition - movement);
261  }
262 }
263 
264 void KineticScrolling::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
265 {
266  Q_UNUSED(event);
267 
268  if (d->scrollAnimation->state() != QAbstractAnimation::Running) {
269  duration();
270 
271  if (d->kinMovement != QPointF(0, 0)) {
272  const QPointF scrollPosition = -d->parent->property("scrollPosition").toPointF();
273  d->kinMovement = QPointF(d->kinMovement.x()*3, d->kinMovement.y()*3);
274  const QPointF finalPos = thresholdPosition(scrollPosition - d->kinMovement);
275  resetAnimation( -finalPos, d->timeDelta*8 );
276  }
277  }
278 }
279 
280 void KineticScrolling::wheelReleaseEvent(QGraphicsSceneWheelEvent *event)
281 {
282  Q_UNUSED(event);
283  d->syncViewportRect();
284  d->kinMovement = QPointF(0,0);
285 
286  if((event->orientation() == Qt::Vertical) &&
287  (((event->delta() < 0) && d->canScroll(KineticScrollingPrivate::Down)) ||
288  ((event->delta() > 0) && d->canScroll(KineticScrollingPrivate::Up)))) {
289  d->kinMovement.setY(d->kinMovement.y() - event->delta());
290  } else if ((event->orientation() == Qt::Vertical) ||
291  (!d->canScroll(KineticScrollingPrivate::Down) &&
292  !d->canScroll(KineticScrollingPrivate::Up))) {
293  if (((event->delta() < 0) &&
294  d->canScroll(KineticScrollingPrivate::Right)) ||
295  (event->delta() > 0 && d->canScroll(KineticScrollingPrivate::Left))) {
296  d->kinMovement.setX(d->kinMovement.x() - event->delta());
297  } else {
298  event->ignore( );
299  }
300  } else {
301  event->ignore( );
302  return;
303  }
304 
305  const QPointF scrollPosition = -d->parent->property("scrollPosition").value<QPointF>();
306  const QPointF pos = scrollPosition - d->kinMovement*2;
307  const QPointF finalPos = thresholdPosition(pos);
308 
309  d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
310  resetAnimation(-finalPos, 900);
311 }
312 
313 void KineticScrolling::keyPressEvent(QKeyEvent *event)
314 {
315  const int movement = 30;
316  const int duration = 900;
317 
318  QPointF scrollPosition = -d->parent->property("scrollPosition").value<QPointF>();
319  QPointF finalPos;
320  switch (event->key()) {
321  case Qt::Key_Left:
322  scrollPosition.setX(scrollPosition.x() + movement);
323  finalPos = thresholdPosition(scrollPosition);
324  resetAnimation(-finalPos, duration);
325  break;
326  case Qt::Key_Right:
327  scrollPosition.setX(scrollPosition.x() - movement);
328  finalPos = thresholdPosition(scrollPosition);
329  resetAnimation(-finalPos, duration);
330  break;
331  case Qt::Key_Up:
332  scrollPosition.setY(scrollPosition.y() + movement);
333  finalPos = thresholdPosition(scrollPosition);
334  resetAnimation(-finalPos, duration);
335  break;
336  case Qt::Key_Down:
337  scrollPosition.setY(scrollPosition.y() - movement);
338  finalPos = thresholdPosition(scrollPosition);
339  resetAnimation(-finalPos, duration);
340  break;
341  default:
342  break;
343  }
344 }
345 
346 void KineticScrolling::setWidget(QGraphicsWidget *parent)
347 {
348  if (d->parent) {
349  d->parent->removeEventFilter(this);
350  disconnect(d->scrollAnimation, SIGNAL(finished()), this, SLOT(overshoot()));
351  disconnect(d->scrollAnimation,
352  SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), this,
353  SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
354  delete d->scrollAnimation;
355  }
356 
357  setParent(parent);
358 
359  d->parent = parent;
360 
361  d->scrollAnimation = new QPropertyAnimation(parent, "scrollPosition", parent);
362  connect(d->scrollAnimation, SIGNAL(finished()), this, SLOT(overshoot()));
363  connect(d->scrollAnimation,
364  SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), this,
365  SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
366  d->scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
367 
368  if (parent) {
369  d->parent->installEventFilter(this);
370  }
371  /* TODO: add a new property in plasma::ScrollWidget 'hasOvershoot' */
372 }
373 
374 void KineticScrolling::stop()
375 {
376  d->scrollAnimation->stop();
377 }
378 
379 
380 bool KineticScrolling::eventFilter(QObject *watched, QEvent *event)
381 {
382  Q_UNUSED(watched);
383  Q_UNUSED(event);
384 
385  if (d->forwardingEvent) {
386  return false;
387  }
388 
389  bool notBlocked = true;
390  if (d->multitouchGesture == KineticScrollingPrivate::GestureNone &&
391  d->parent && d->parent->scene()) {
392  d->forwardingEvent = true;
393  notBlocked = d->parent->scene()->sendEvent(d->parent, event);
394  d->forwardingEvent = false;
395  }
396 
397  if (event->type() != QEvent::TouchBegin &&
398  event->type() != QEvent::TouchUpdate &&
399  event->type() != QEvent::TouchEnd &&
400  (!notBlocked ||
401  ((event->type() != QEvent::GraphicsSceneMousePress && event->isAccepted()) &&
402  (event->type() != QEvent::GraphicsSceneWheel && event->isAccepted())))) {
403  return true;
404  }
405 
406  QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
407  QGraphicsSceneWheelEvent *we = static_cast<QGraphicsSceneWheelEvent *>(event);
408 
409  switch (event->type()) {
410  case QEvent::GraphicsSceneMousePress:
411  mousePressEvent(me);
412  break;
413  case QEvent::GraphicsSceneMouseRelease:
414  mouseReleaseEvent(me);
415  break;
416  case QEvent::GraphicsSceneMouseMove:
417  mouseMoveEvent(me);
418  break;
419  case QEvent::TouchBegin:
420  mousePressEvent(0);
421  break;
422  case QEvent::TouchUpdate: {
423  QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(event)->touchPoints();
424  if (touchPoints.count() == 2) {
425  const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
426  const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
427  const QLineF line0(touchPoint0.lastPos(), touchPoint1.lastPos());
428  const QLineF line1(touchPoint0.pos(), touchPoint1.pos());
429  const QLineF startLine(touchPoint0.startPos(), touchPoint1.startPos());
430  const QPointF point = line1.pointAt(0.5);
431  const QPointF lastPoint = line0.pointAt(0.5);
432 
433  if (d->multitouchGesture == KineticScrollingPrivate::GestureNone) {
434  d->multitouchGesture = KineticScrollingPrivate::GestureUndefined;
435  }
436  if (d->multitouchGesture == KineticScrollingPrivate::GestureUndefined) {
437  const int zoomDistance = qAbs(line1.length() - startLine.length());
438  const int dragDistance = (startLine.pointAt(0.5) - point).manhattanLength();
439 
440  if (zoomDistance - dragDistance > 30) {
441  d->multitouchGesture = KineticScrollingPrivate::GestureZoom;
442  } else if (dragDistance - zoomDistance > 30) {
443  d->multitouchGesture = KineticScrollingPrivate::GestureScroll;
444  }
445  }
446 
447  if (d->multitouchGesture == KineticScrollingPrivate::GestureScroll) {
448  QGraphicsSceneMouseEvent fakeEvent;
449  fakeEvent.setPos(point);
450  fakeEvent.setLastPos(lastPoint);
451  mouseMoveEvent(&fakeEvent);
452  } else if (d->multitouchGesture == KineticScrollingPrivate::GestureZoom) {
453  qreal scaleFactor = 1;
454  if (line0.length() > 0) {
455  scaleFactor = line1.length() / line0.length();
456  }
457 
458  qreal zoom = d->parent->property("zoomFactor").toReal();
459  d->parent->setProperty("zoomFactor", zoom * scaleFactor);
460  }
461  }
462  break;
463  }
464  case QEvent::TouchEnd:
465  mouseReleaseEvent(0);
466  d->multitouchGesture = KineticScrollingPrivate::GestureNone;
467  break;
468 
469  case QEvent::GraphicsSceneWheel:
470  wheelReleaseEvent(we);
471  break;
472  case QEvent::KeyPress: {
473  QKeyEvent *ke = static_cast<QKeyEvent *>(event);
474  keyPressEvent(ke);
475  break;
476  }
477  default:
478  break;
479  }
480 
481  return true;
482 }
483 
484 } // namespace Plasma
Plasma::KineticScrolling::~KineticScrolling
~KineticScrolling()
Definition: kineticscroll.cpp:132
QEvent
QEvent::type
Type type() const
QTouchEvent::TouchPoint::pos
QPointF pos() const
QGraphicsSceneWheelEvent
Plasma::KineticScrolling::KineticScrolling
KineticScrolling(QGraphicsWidget *parent)
Definition: kineticscroll.cpp:126
Plasma::KineticScrolling::wheelReleaseEvent
void wheelReleaseEvent(QGraphicsSceneWheelEvent *event)
Definition: kineticscroll.cpp:280
Plasma::KineticScrolling::eventFilter
bool eventFilter(QObject *watched, QEvent *event)
Definition: kineticscroll.cpp:380
QObject::disconnect
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QTime
kineticscroll_p.h
QTouchEvent::TouchPoint
Plasma::KineticScrolling::stop
void stop()
Definition: kineticscroll.cpp:374
QPointF
QObject::event
virtual bool event(QEvent *e)
Plasma::KineticScrolling::stateChanged
void stateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
QList::count
int count(const T &value) const
QPointF::x
qreal x() const
QPointF::y
qreal y() const
QPropertyAnimation
QObject
QGraphicsSceneMouseEvent
QTouchEvent::TouchPoint::startPos
QPointF startPos() const
QGraphicsWidget
QList::first
T & first()
QList
QLineF
Plasma::KineticScrolling::mouseReleaseEvent
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
Definition: kineticscroll.cpp:264
QKeyEvent::key
int key() const
QObject::setParent
void setParent(QObject *parent)
Plasma::KineticScrolling::mousePressEvent
void mousePressEvent(QGraphicsSceneMouseEvent *event)
Definition: kineticscroll.cpp:225
Plasma::KineticScrolling::keyPressEvent
void keyPressEvent(QKeyEvent *event)
Definition: kineticscroll.cpp:313
QTime::currentTime
QTime currentTime()
QKeyEvent
QGraphicsSceneWheelEvent::orientation
Qt::Orientation orientation() const
QSizeF
QPointF::toPoint
QPoint toPoint() const
QRectF
QPointF::setX
void setX(qreal x)
QPointF::setY
void setY(qreal y)
QList::last
T & last()
QTouchEvent::TouchPoint::lastPos
QPointF lastPos() const
QGraphicsSceneWheelEvent::delta
int delta() const
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::parent
QObject * parent() const
QTouchEvent
Plasma::KineticScrolling::mouseMoveEvent
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
Definition: kineticscroll.cpp:241
Plasma::KineticScrolling::setWidget
void setWidget(QGraphicsWidget *parent)
Definition: kineticscroll.cpp:346
QPointF::isNull
bool isNull() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:43:01 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

liblancelot

Skip menu "liblancelot"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

workspace API Reference

Skip menu "workspace API Reference"
  • kdeplasma-addons
  •       GroupingDesktop
  •     liblancelot

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal