Plasma
scrollwidget.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2009 Marco Martin <notmart@gmail.com> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU Library General Public License as 00006 * published by the Free Software Foundation; either version 2, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details 00013 * 00014 * You should have received a copy of the GNU Library General Public 00015 * License along with this program; if not, write to the 00016 * Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "scrollwidget.h" 00021 00022 #include <cmath> 00023 00024 //Qt 00025 #include <QGraphicsSceneResizeEvent> 00026 #include <QGraphicsGridLayout> 00027 #include <QGraphicsScene> 00028 #include <QApplication> 00029 #include <QKeyEvent> 00030 #include <QWidget> 00031 #include <QTimer> 00032 #include <QTime> 00033 #include <QPropertyAnimation> 00034 #include <QSequentialAnimationGroup> 00035 00036 #include <QLabel> 00037 00038 //KDE 00039 #include <kmimetype.h> 00040 #include <kdebug.h> 00041 #include <kglobalsettings.h> 00042 #include <kiconloader.h> 00043 #include <ktextedit.h> 00044 #include <ktextbrowser.h> 00045 00046 //Plasma 00047 #include <plasma/widgets/scrollbar.h> 00048 #include <plasma/widgets/svgwidget.h> 00049 #include <plasma/widgets/label.h> 00050 #include <plasma/widgets/textedit.h> 00051 #include <plasma/widgets/textbrowser.h> 00052 #include <plasma/animator.h> 00053 #include <plasma/svg.h> 00054 00055 00056 #define DEBUG 0 00057 00058 /* 00059 The flicking code is largely based on the behavior of 00060 the flickable widget in QDeclerative so porting between 00061 the two should preserve the behavior. 00062 The code that figures out velocity could use some 00063 improvements, in particular IGNORE_SUSPICIOUS_MOVES 00064 is a hack that shouldn't be necessary. 00065 */ 00066 00067 //XXX fixme 00068 // we use a timer between move events to figure out 00069 // the velocity of a move, but sometimes we're getting move 00070 // events with big positional changes with no break 00071 // in between them, which causes us to compute 00072 // huge velocities. this define just filters out 00073 // events which come at insanly small time intervals. 00074 // at some point we need to figure out how to do it properly 00075 #define IGNORE_SUSPICIOUS_MOVES 1 00076 00077 // FlickThreshold determines how far the "mouse" must have moved 00078 // before we perform a flick. 00079 static const int FlickThreshold = 20; 00080 00081 00082 static const qreal MinimumFlickVelocity = 200; 00083 static const qreal MaxVelocity = 2000; 00084 00085 // time it takes the widget to flick back to its 00086 // bounds when overshot 00087 static const qreal FixupDuration = 600; 00088 00089 namespace Plasma 00090 { 00091 00092 class ScrollWidgetPrivate 00093 { 00094 public: 00095 enum Gesture { 00096 GestureNone = 0, 00097 GestureUndefined, 00098 GestureScroll, 00099 GestureZoom 00100 }; 00101 00102 ScrollWidgetPrivate(ScrollWidget *parent) 00103 : q(parent), 00104 topBorder(0), 00105 bottomBorder(0), 00106 leftBorder(0), 00107 rightBorder(0), 00108 dragging(false), 00109 overflowBordersVisible(true), 00110 multitouchGesture(GestureNone) 00111 { 00112 } 00113 00114 ~ScrollWidgetPrivate() 00115 { 00116 } 00117 00118 void commonConstructor() 00119 { 00120 q->setFocusPolicy(Qt::StrongFocus); 00121 q->setFiltersChildEvents(true); 00122 layout = new QGraphicsGridLayout(q); 00123 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 00124 layout->setContentsMargins(0, 0, 0, 0); 00125 scrollingWidget = new QGraphicsWidget(q); 00126 scrollingWidget->setFlag(QGraphicsItem::ItemHasNoContents); 00127 scrollingWidget->installEventFilter(q); 00128 layout->addItem(scrollingWidget, 0, 0); 00129 borderSvg = new Plasma::Svg(q); 00130 borderSvg->setImagePath("widgets/scrollwidget"); 00131 00132 adjustScrollbarsTimer = new QTimer(q); 00133 adjustScrollbarsTimer->setSingleShot(true); 00134 QObject::connect(adjustScrollbarsTimer, SIGNAL(timeout()), q, SLOT(adjustScrollbars())); 00135 00136 wheelTimer = new QTimer(q); 00137 wheelTimer->setSingleShot(true); 00138 00139 verticalScrollBarPolicy = Qt::ScrollBarAsNeeded; 00140 verticalScrollBar = new Plasma::ScrollBar(q); 00141 verticalScrollBar->setFocusPolicy(Qt::NoFocus); 00142 layout->addItem(verticalScrollBar, 0, 1); 00143 verticalScrollBar->nativeWidget()->setMinimum(0); 00144 verticalScrollBar->nativeWidget()->setMaximum(100); 00145 QObject::connect(verticalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(verticalScroll(int))); 00146 00147 horizontalScrollBarPolicy = Qt::ScrollBarAsNeeded; 00148 horizontalScrollBar = new Plasma::ScrollBar(q); 00149 verticalScrollBar->setFocusPolicy(Qt::NoFocus); 00150 horizontalScrollBar->setOrientation(Qt::Horizontal); 00151 layout->addItem(horizontalScrollBar, 1, 0); 00152 horizontalScrollBar->nativeWidget()->setMinimum(0); 00153 horizontalScrollBar->nativeWidget()->setMaximum(100); 00154 QObject::connect(horizontalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(horizontalScroll(int))); 00155 00156 layout->setColumnSpacing(0, 0); 00157 layout->setColumnSpacing(1, 0); 00158 layout->setRowSpacing(0, 0); 00159 layout->setRowSpacing(1, 0); 00160 00161 flickAnimationX = 0; 00162 flickAnimationY = 0; 00163 fixupAnimation.groupX = 0; 00164 fixupAnimation.startX = 0; 00165 fixupAnimation.endX = 0; 00166 fixupAnimation.groupY = 0; 00167 fixupAnimation.startY = 0; 00168 fixupAnimation.endY = 0; 00169 fixupAnimation.snapX = 0; 00170 fixupAnimation.snapY = 0; 00171 directMoveAnimation = 0; 00172 stealEvent = false; 00173 hasOvershoot = true; 00174 00175 alignment = Qt::AlignLeft | Qt::AlignTop; 00176 00177 hasContentsProperty = false; 00178 hasOffsetProperty = false; 00179 hasXProperty = false; 00180 hasYProperty = false; 00181 } 00182 00183 void adjustScrollbars() 00184 { 00185 if (!widget) { 00186 return; 00187 } 00188 00189 const bool verticalVisible = widget.data()->size().height() > q->size().height(); 00190 const bool horizontalVisible = widget.data()->size().width() > q->size().width(); 00191 00192 verticalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().height() - scrollingWidget->size().height())/10))); 00193 verticalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().height())/10); 00194 00195 if (verticalScrollBarPolicy == Qt::ScrollBarAlwaysOff || 00196 !verticalVisible) { 00197 if (layout->count() > 2 && layout->itemAt(2) == verticalScrollBar) { 00198 layout->removeAt(2); 00199 } else if (layout->count() > 1 && layout->itemAt(1) == verticalScrollBar) { 00200 layout->removeAt(1); 00201 } 00202 verticalScrollBar->hide(); 00203 } else if (!verticalScrollBar->isVisible()) { 00204 layout->addItem(verticalScrollBar, 0, 1); 00205 verticalScrollBar->show(); 00206 } 00207 00208 horizontalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().width() - scrollingWidget->size().width())/10))); 00209 horizontalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().width())/10); 00210 00211 if (horizontalScrollBarPolicy == Qt::ScrollBarAlwaysOff || 00212 !horizontalVisible) { 00213 if (layout->count() > 2 && layout->itemAt(2) == horizontalScrollBar) { 00214 layout->removeAt(2); 00215 } else if (layout->count() > 1 && layout->itemAt(1) == horizontalScrollBar) { 00216 layout->removeAt(1); 00217 } 00218 horizontalScrollBar->hide(); 00219 } else if (!horizontalScrollBar->isVisible()) { 00220 layout->addItem(horizontalScrollBar, 1, 0); 00221 horizontalScrollBar->show(); 00222 } 00223 00224 if (widget && !topBorder && verticalVisible) { 00225 topBorder = new Plasma::SvgWidget(q); 00226 topBorder->setSvg(borderSvg); 00227 topBorder->setElementID("border-top"); 00228 topBorder->setZValue(900); 00229 topBorder->resize(topBorder->effectiveSizeHint(Qt::PreferredSize)); 00230 topBorder->setVisible(overflowBordersVisible); 00231 00232 bottomBorder = new Plasma::SvgWidget(q); 00233 bottomBorder->setSvg(borderSvg); 00234 bottomBorder->setElementID("border-bottom"); 00235 bottomBorder->setZValue(900); 00236 bottomBorder->resize(bottomBorder->effectiveSizeHint(Qt::PreferredSize)); 00237 bottomBorder->setVisible(overflowBordersVisible); 00238 } else if (topBorder && widget && !verticalVisible) { 00239 //FIXME: in some cases topBorder->deleteLater() is deleteNever(), why? 00240 topBorder->hide(); 00241 bottomBorder->hide(); 00242 topBorder->deleteLater(); 00243 bottomBorder->deleteLater(); 00244 topBorder = 0; 00245 bottomBorder = 0; 00246 } 00247 00248 00249 if (widget && !leftBorder && horizontalVisible) { 00250 leftBorder = new Plasma::SvgWidget(q); 00251 leftBorder->setSvg(borderSvg); 00252 leftBorder->setElementID("border-left"); 00253 leftBorder->setZValue(900); 00254 leftBorder->resize(leftBorder->effectiveSizeHint(Qt::PreferredSize)); 00255 leftBorder->setVisible(overflowBordersVisible); 00256 00257 rightBorder = new Plasma::SvgWidget(q); 00258 rightBorder->setSvg(borderSvg); 00259 rightBorder->setElementID("border-right"); 00260 rightBorder->setZValue(900); 00261 rightBorder->resize(rightBorder->effectiveSizeHint(Qt::PreferredSize)); 00262 rightBorder->setVisible(overflowBordersVisible); 00263 } else if (leftBorder && widget && !horizontalVisible) { 00264 leftBorder->hide(); 00265 rightBorder->hide(); 00266 leftBorder->deleteLater(); 00267 rightBorder->deleteLater(); 00268 leftBorder = 0; 00269 rightBorder = 0; 00270 } 00271 00272 layout->activate(); 00273 00274 if (topBorder) { 00275 topBorder->resize(q->size().width(), topBorder->size().height()); 00276 bottomBorder->resize(q->size().width(), bottomBorder->size().height()); 00277 bottomBorder->setPos(0, q->size().height() - topBorder->size().height()); 00278 } 00279 if (leftBorder) { 00280 leftBorder->resize(leftBorder->size().width(), q->size().height()); 00281 rightBorder->resize(rightBorder->size().width(), q->size().height()); 00282 rightBorder->setPos(q->size().width() - rightBorder->size().width(), 0); 00283 } 00284 00285 QSizeF widgetSize = widget.data()->size(); 00286 if (widget.data()->sizePolicy().expandingDirections() & Qt::Horizontal) { 00287 //keep a 1 pixel border 00288 widgetSize.setWidth(scrollingWidget->size().width()); 00289 } 00290 if (widget.data()->sizePolicy().expandingDirections() & Qt::Vertical) { 00291 widgetSize.setHeight(scrollingWidget->size().height()); 00292 } 00293 widget.data()->resize(widgetSize); 00294 00295 adjustClipping(); 00296 } 00297 00298 void verticalScroll(int value) 00299 { 00300 if (!widget) { 00301 return; 00302 } 00303 00304 if (!dragging) { 00305 widget.data()->setPos(QPoint(widget.data()->pos().x(), -value*10)); 00306 } 00307 } 00308 00309 void horizontalScroll(int value) 00310 { 00311 if (!widget) { 00312 return; 00313 } 00314 00315 if (!dragging) { 00316 widget.data()->setPos(QPoint(-value*10, widget.data()->pos().y())); 00317 } 00318 } 00319 00320 void adjustClipping() 00321 { 00322 if (!widget) { 00323 return; 00324 } 00325 00326 const bool clip = widget.data()->size().width() > scrollingWidget->size().width() || widget.data()->size().height() > scrollingWidget->size().height(); 00327 00328 scrollingWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, clip); 00329 } 00330 00331 qreal overShootDistance(qreal velocity, qreal size) const 00332 { 00333 if (MaxVelocity <= 0) 00334 return 0.0; 00335 00336 velocity = qAbs(velocity); 00337 if (velocity > MaxVelocity) 00338 velocity = MaxVelocity; 00339 qreal dist = size / 4 * velocity / MaxVelocity; 00340 return dist; 00341 } 00342 00343 void animateMoveTo(const QPointF &pos) 00344 { 00345 qreal duration = 800; 00346 QPointF start = q->scrollPosition(); 00347 QSizeF threshold = q->viewportGeometry().size(); 00348 QPointF diff = pos - start; 00349 00350 //reduce if it's within the viewport 00351 if (qAbs(diff.x()) < threshold.width() || 00352 qAbs(diff.y()) < threshold.height()) 00353 duration /= 2; 00354 00355 fixupAnimation.groupX->stop(); 00356 fixupAnimation.groupY->stop(); 00357 fixupAnimation.snapX->stop(); 00358 fixupAnimation.snapY->stop(); 00359 00360 directMoveAnimation->setStartValue(start); 00361 directMoveAnimation->setEndValue(pos); 00362 directMoveAnimation->setDuration(duration); 00363 directMoveAnimation->start(); 00364 } 00365 00366 void flick(QPropertyAnimation *anim, 00367 qreal velocity, 00368 qreal val, 00369 qreal minExtent, 00370 qreal maxExtent, 00371 qreal size) 00372 { 00373 qreal deceleration = 500; 00374 qreal maxDistance = -1; 00375 qreal target = 0; 00376 // -ve velocity means list is moving up 00377 if (velocity > 0) { 00378 if (val < minExtent) 00379 maxDistance = qAbs(minExtent - val + (hasOvershoot?overShootDistance(velocity,size):0)); 00380 target = minExtent; 00381 deceleration = -deceleration; 00382 } else { 00383 if (val > maxExtent) 00384 maxDistance = qAbs(maxExtent - val) + (hasOvershoot?overShootDistance(velocity,size):0); 00385 target = maxExtent; 00386 } 00387 if (maxDistance > 0) { 00388 qreal v = velocity; 00389 if (MaxVelocity != -1 && MaxVelocity < qAbs(v)) { 00390 if (v < 0) 00391 v = -MaxVelocity; 00392 else 00393 v = MaxVelocity; 00394 } 00395 qreal duration = qAbs(v / deceleration); 00396 qreal diffY = v * duration + (0.5 * deceleration * duration * duration); 00397 qreal startY = val; 00398 00399 qreal endY = startY + diffY; 00400 00401 if (velocity > 0) { 00402 if (endY > target) 00403 endY = startY + maxDistance; 00404 } else { 00405 if (endY < target) 00406 endY = startY - maxDistance; 00407 } 00408 duration = qAbs((endY-startY)/ (-v/2)); 00409 00410 if (hasYProperty) { 00411 startY = -startY; 00412 endY = -endY; 00413 } 00414 00415 00416 #if DEBUG 00417 qDebug()<<"XXX velocity = "<<v <<", target = "<< target 00418 <<", maxDist = "<<maxDistance; 00419 qDebug()<<"duration = "<<duration<<" secs, (" 00420 << (duration * 1000) <<" msecs)"; 00421 qDebug()<<"startY = "<<startY; 00422 qDebug()<<"endY = "<<endY; 00423 qDebug()<<"overshoot = "<<overShootDistance(v, size); 00424 qDebug()<<"avg velocity = "<< ((endY-startY)/duration); 00425 #endif 00426 00427 anim->setStartValue(startY); 00428 anim->setEndValue(endY); 00429 anim->setDuration(duration * 1000); 00430 anim->start(); 00431 } else { 00432 if (anim == flickAnimationX) 00433 fixupX(); 00434 else 00435 fixupY(); 00436 } 00437 } 00438 void flickX(qreal velocity) 00439 { 00440 flick(flickAnimationX, velocity, widgetX(), minXExtent(), maxXExtent(), 00441 q->viewportGeometry().width()); 00442 } 00443 void flickY(qreal velocity) 00444 { 00445 flick(flickAnimationY, velocity, widgetY(),minYExtent(), maxYExtent(), 00446 q->viewportGeometry().height()); 00447 } 00448 void fixup(QAnimationGroup *group, 00449 QPropertyAnimation *start, QPropertyAnimation *end, 00450 qreal val, qreal minExtent, qreal maxExtent) 00451 { 00452 if (val > minExtent || maxExtent > minExtent) { 00453 if (!qFuzzyCompare(val, minExtent)) { 00454 if (FixupDuration) { 00455 //TODO: we should consider the case where there is one axis available not the other 00456 if (hasXProperty && hasYProperty) { 00457 val = -val; 00458 minExtent = -minExtent; 00459 } 00460 qreal dist = minExtent - val; 00461 start->setStartValue(val); 00462 start->setEndValue(minExtent - dist/2); 00463 end->setStartValue(minExtent - dist/2); 00464 end->setEndValue(minExtent); 00465 start->setDuration(FixupDuration/4); 00466 end->setDuration(3*FixupDuration/4); 00467 group->start(); 00468 } else { 00469 QObject *obj = start->targetObject(); 00470 obj->setProperty(start->propertyName(), minExtent); 00471 } 00472 } 00473 } else if (val < maxExtent) { 00474 if (FixupDuration) { 00475 if (hasXProperty && hasYProperty) { 00476 val = -val; 00477 maxExtent = -maxExtent; 00478 } 00479 qreal dist = maxExtent - val; 00480 start->setStartValue(val); 00481 start->setEndValue(maxExtent - dist/2); 00482 end->setStartValue(maxExtent - dist/2); 00483 end->setEndValue(maxExtent); 00484 start->setDuration(FixupDuration/4); 00485 end->setDuration(3*FixupDuration/4); 00486 group->start(); 00487 } else { 00488 QObject *obj = start->targetObject(); 00489 obj->setProperty(start->propertyName(), maxExtent); 00490 } 00491 } else if (end == fixupAnimation.endX && snapSize.width() > 1 && 00492 q->contentsSize().width() > q->viewportGeometry().width()) { 00493 int target = snapSize.width() * round(val/snapSize.width()); 00494 fixupAnimation.snapX->setStartValue(val); 00495 fixupAnimation.snapX->setEndValue(target); 00496 fixupAnimation.snapX->setDuration(FixupDuration); 00497 fixupAnimation.snapX->start(); 00498 } else if (end == fixupAnimation.endY && snapSize.height() > 1 && 00499 q->contentsSize().height() > q->viewportGeometry().height()) { 00500 int target = snapSize.height() * round(val/snapSize.height()); 00501 fixupAnimation.snapY->setStartValue(val); 00502 fixupAnimation.snapY->setEndValue(target); 00503 fixupAnimation.snapY->setDuration(FixupDuration); 00504 fixupAnimation.snapY->start(); 00505 } 00506 } 00507 void fixupX() 00508 { 00509 fixup(fixupAnimation.groupX, fixupAnimation.startX, fixupAnimation.endX, 00510 widgetX(), minXExtent(), maxXExtent()); 00511 } 00512 void fixupY() 00513 { 00514 fixup(fixupAnimation.groupY, fixupAnimation.startY, fixupAnimation.endY, 00515 widgetY(), minYExtent(), maxYExtent()); 00516 } 00517 00518 void makeRectVisible() 00519 { 00520 if (!widget) { 00521 return; 00522 } 00523 00524 QRectF viewRect = scrollingWidget->boundingRect(); 00525 //ensure the rect is not outside the widget bounding rect 00526 QRectF mappedRect = QRectF(QPointF(qBound((qreal)0.0, rectToBeVisible.x(), widget.data()->size().width() - rectToBeVisible.width()), 00527 qBound((qreal)0.0, rectToBeVisible.y(), widget.data()->size().height() - rectToBeVisible.height())), 00528 rectToBeVisible.size()); 00529 mappedRect = widget.data()->mapToItem(scrollingWidget, mappedRect).boundingRect(); 00530 00531 if (viewRect.contains(mappedRect)) { 00532 return; 00533 } 00534 00535 QPointF delta(0, 0); 00536 00537 if (mappedRect.top() < 0) { 00538 delta.setY(-mappedRect.top()); 00539 } else if (mappedRect.bottom() > viewRect.bottom()) { 00540 delta.setY(viewRect.bottom() - mappedRect.bottom()); 00541 } 00542 00543 if (mappedRect.left() < 0) { 00544 delta.setX(-mappedRect.left()); 00545 } else if (mappedRect.right() > viewRect.right()) { 00546 delta.setX(viewRect.right() - mappedRect.right()); 00547 } 00548 00549 animateMoveTo(q->scrollPosition() - delta); 00550 } 00551 00552 void makeItemVisible(QGraphicsItem *itemToBeVisible) 00553 { 00554 if (!widget) { 00555 return; 00556 } 00557 00558 QRectF rect(widget.data()->mapFromScene(itemToBeVisible->scenePos()), itemToBeVisible->boundingRect().size()); 00559 rectToBeVisible = rect; 00560 00561 makeRectVisible(); 00562 } 00563 00564 void makeItemVisible() 00565 { 00566 if (widgetToBeVisible) { 00567 makeItemVisible(widgetToBeVisible.data()); 00568 } 00569 } 00570 00571 void stopAnimations() 00572 { 00573 flickAnimationX->stop(); 00574 flickAnimationY->stop(); 00575 fixupAnimation.groupX->stop(); 00576 fixupAnimation.groupY->stop(); 00577 } 00578 00579 void setWidgetX(qreal x) 00580 { 00581 if (hasXProperty) { 00582 widget.data()->setProperty("scrollPositionX", -x); 00583 } else 00584 widget.data()->setX(x); 00585 } 00586 void setWidgetY(qreal y) 00587 { 00588 if (hasYProperty) { 00589 widget.data()->setProperty("scrollPositionY", -y); 00590 } else 00591 widget.data()->setY(y); 00592 } 00593 qreal widgetX() const 00594 { 00595 if (hasXProperty) { 00596 return -widget.data()->property("scrollPositionX").toReal(); 00597 } else 00598 return widget.data()->x(); 00599 } 00600 qreal widgetY() const 00601 { 00602 if (hasYProperty) { 00603 return -widget.data()->property("scrollPositionY").toReal(); 00604 } else 00605 return widget.data()->y(); 00606 } 00607 00608 void handleKeyPressEvent(QKeyEvent *event) 00609 { 00610 if (!widget.data()) 00611 return; 00612 00613 QPointF start = q->scrollPosition(); 00614 QPointF end = start; 00615 00616 qreal step = 100; 00617 00618 switch (event->key()) { 00619 case Qt::Key_Left: 00620 if (canXFlick()) { 00621 end += QPointF(-step, 0); 00622 } 00623 break; 00624 case Qt::Key_Right: 00625 if (canXFlick()) { 00626 end += QPointF(step, 0); 00627 } 00628 break; 00629 case Qt::Key_Up: 00630 if (canYFlick()) { 00631 end += QPointF(0, -step); 00632 } 00633 break; 00634 case Qt::Key_Down: 00635 if (canYFlick()) { 00636 end += QPointF(0, step); 00637 } 00638 break; 00639 default: 00640 break; 00641 } 00642 00643 fixupAnimation.groupX->stop(); 00644 fixupAnimation.groupY->stop(); 00645 fixupAnimation.snapX->stop(); 00646 fixupAnimation.snapY->stop(); 00647 directMoveAnimation->setStartValue(start); 00648 directMoveAnimation->setEndValue(end); 00649 directMoveAnimation->setDuration(200); 00650 directMoveAnimation->start(); 00651 } 00652 00653 void handleMousePressEvent(QGraphicsSceneMouseEvent *event) 00654 { 00655 lastPos = QPoint(); 00656 lastPosTime = QTime::currentTime(); 00657 pressPos = event->scenePos(); 00658 pressScrollPos = -q->scrollPosition(); 00659 pressTime = QTime::currentTime(); 00660 velocity = QPointF(); 00661 stopAnimations(); 00662 } 00663 00664 void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) 00665 { 00666 if (lastPosTime.isNull()) 00667 return; 00668 bool rejectY = false; 00669 bool rejectX = false; 00670 bool moved = false; 00671 00672 if (canYFlick()) { 00673 int dy = int(event->scenePos().y() - pressPos.y()); 00674 if (qAbs(dy) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) { 00675 qreal newY = dy + pressScrollPos.y(); 00676 const qreal minY = minYExtent(); 00677 const qreal maxY = maxYExtent(); 00678 if (newY > minY) 00679 newY = minY + (newY - minY) / 2; 00680 if (newY < maxY && maxY - minY <= 0) 00681 newY = maxY + (newY - maxY) / 2; 00682 if (!hasOvershoot && (newY > minY || newY < maxY)) { 00683 if (newY > minY) 00684 newY = minY; 00685 else if (newY < maxY) 00686 newY = maxY; 00687 else 00688 rejectY = true; 00689 } 00690 if (!rejectY && stealEvent) { 00691 setWidgetY(qRound(newY)); 00692 moved = true; 00693 } 00694 if (qAbs(dy) > KGlobalSettings::dndEventDelay()) 00695 stealEvent = true; 00696 } 00697 } 00698 00699 if (canXFlick()) { 00700 int dx = int(event->scenePos().x() - pressPos.x()); 00701 if (qAbs(dx) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) { 00702 qreal newX = dx + pressScrollPos.x(); 00703 const qreal minX = minXExtent(); 00704 const qreal maxX = maxXExtent(); 00705 if (newX > minX) 00706 newX = minX + (newX - minX) / 2; 00707 if (newX < maxX && maxX - minX <= 0) 00708 newX = maxX + (newX - maxX) / 2; 00709 if (!hasOvershoot && (newX > minX || newX < maxX)) { 00710 if (newX > minX) 00711 newX = minX; 00712 else if (newX < maxX) 00713 newX = maxX; 00714 else 00715 rejectX = true; 00716 } 00717 if (!rejectX && stealEvent) { 00718 setWidgetX(qRound(newX)); 00719 moved = true; 00720 } 00721 00722 if (qAbs(dx) > KGlobalSettings::dndEventDelay()) 00723 stealEvent = true; 00724 } 00725 } 00726 00727 if (!lastPos.isNull()) { 00728 qreal msecs = qreal(restart(lastPosTime)); 00729 qreal elapsed = msecs / 1000.; 00730 #if IGNORE_SUSPICIOUS_MOVES 00731 if (msecs > 3) { 00732 #endif 00733 if (elapsed <= 0) 00734 elapsed = 1; 00735 if (canYFlick()) { 00736 qreal diff = event->scenePos().y() - lastPos.y(); 00737 // average to reduce the effect of spurious moves 00738 velocity.setY( velocity.y() + (diff / elapsed) ); 00739 velocity.setY( velocity.y() / 2 ); 00740 } 00741 00742 if (canXFlick()) { 00743 qreal diff = event->scenePos().x() - lastPos.x(); 00744 // average to reduce the effect of spurious moves 00745 velocity.setX( velocity.x() + (diff / elapsed) ); 00746 velocity.setX( velocity.x() / 2 ); 00747 } 00748 #if IGNORE_SUSPICIOUS_MOVES 00749 } 00750 #endif 00751 } 00752 00753 if (rejectX) velocity.setX(0); 00754 if (rejectY) velocity.setY(0); 00755 00756 lastPos = event->scenePos(); 00757 } 00758 00759 void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event) 00760 { 00761 stealEvent = false; 00762 if (lastPosTime.isNull()) 00763 return; 00764 00765 if (elapsed(lastPosTime) > 100) { 00766 // if we drag then pause before release we should not cause a flick. 00767 velocity = QPointF(); 00768 } 00769 00770 if (qAbs(velocity.y()) > 10 && 00771 qAbs(event->scenePos().y() - pressPos.y()) > FlickThreshold) { 00772 qreal vVelocity = velocity.y(); 00773 // Minimum velocity to avoid annoyingly slow flicks. 00774 if (qAbs(vVelocity) < MinimumFlickVelocity) 00775 vVelocity = vVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity; 00776 flickY(vVelocity); 00777 } else { 00778 fixupY(); 00779 } 00780 00781 if (qAbs(velocity.x()) > 10 && 00782 qAbs(event->scenePos().x() - pressPos.x()) > FlickThreshold) { 00783 qreal hVelocity = velocity.x(); 00784 // Minimum velocity to avoid annoyingly slow flicks. 00785 if (qAbs(hVelocity) < MinimumFlickVelocity) 00786 hVelocity = hVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity; 00787 flickX(hVelocity); 00788 } else { 00789 fixupX(); 00790 } 00791 00792 lastPosTime = QTime(); 00793 } 00794 00795 void handleWheelEvent(QGraphicsSceneWheelEvent *event) 00796 { 00797 //only scroll when the animation is done, this avoids to receive too many events and getting mad when they arrive from a touchpad 00798 if (!widget.data() || wheelTimer->isActive()) { 00799 return; 00800 } 00801 00802 QPointF start = q->scrollPosition(); 00803 QPointF end = start; 00804 00805 //At some point we should switch to 00806 // step = QApplication::wheelScrollLines() * 00807 // (event->delta()/120) * 00808 // scrollBar->singleStep(); 00809 // which gives us exactly the number of lines to scroll but the issue 00810 // is that at this point we don't have any clue what a "line" is and if 00811 // we make it a pixel then scrolling by 3 (default) pixels will be 00812 // very painful 00813 qreal step = -event->delta()/3; 00814 00815 //ifthe widget can scroll in a single axis and the wheel is the other one, scroll the other one 00816 Qt::Orientation orientation = event->orientation(); 00817 if (orientation == Qt::Vertical) { 00818 if (!canYFlick() && canXFlick()) { 00819 end += QPointF(step, 0); 00820 } else if (canYFlick()) { 00821 end += QPointF(0, step); 00822 } else { 00823 return; 00824 } 00825 } else { 00826 if (canYFlick() && !canXFlick()) { 00827 end += QPointF(0, step); 00828 } else if (canXFlick()) { 00829 end += QPointF(step, 0); 00830 } else { 00831 return; 00832 } 00833 } 00834 00835 fixupAnimation.groupX->stop(); 00836 fixupAnimation.groupY->stop(); 00837 fixupAnimation.snapX->stop(); 00838 fixupAnimation.snapY->stop(); 00839 directMoveAnimation->setStartValue(start); 00840 directMoveAnimation->setEndValue(end); 00841 directMoveAnimation->setDuration(200); 00842 directMoveAnimation->start(); 00843 wheelTimer->start(50); 00844 } 00845 00846 qreal minXExtent() const 00847 { 00848 if (alignment & Qt::AlignLeft) 00849 return 0; 00850 else { 00851 qreal vWidth = q->viewportGeometry().width(); 00852 qreal cWidth = q->contentsSize().width(); 00853 if (cWidth < vWidth) { 00854 if (alignment & Qt::AlignRight) 00855 return vWidth - cWidth; 00856 else if (alignment & Qt::AlignHCenter) 00857 return vWidth / 2 - cWidth / 2; 00858 } 00859 } 00860 00861 return 0; 00862 } 00863 00864 qreal maxXExtent() const 00865 { 00866 return q->viewportGeometry().width() - 00867 q->contentsSize().width(); 00868 } 00869 00870 qreal minYExtent() const 00871 { 00872 if (alignment & Qt::AlignTop) 00873 return 0; 00874 else { 00875 qreal vHeight = q->viewportGeometry().height(); 00876 qreal cHeight = q->contentsSize().height(); 00877 if (cHeight < vHeight) { 00878 if (alignment & Qt::AlignBottom) 00879 return vHeight - cHeight; 00880 else if (alignment & Qt::AlignVCenter) 00881 return vHeight / 2 - cHeight / 2; 00882 } 00883 } 00884 00885 return 0; 00886 } 00887 00888 qreal maxYExtent() const 00889 { 00890 return q->viewportGeometry().height() - 00891 q->contentsSize().height(); 00892 } 00893 00894 bool canXFlick() const 00895 { 00896 //make the thing feel quite "fixed" don't permit to flick when the contents size is less than the viewport 00897 return q->contentsSize().width() > q->viewportGeometry().width(); 00898 } 00899 00900 bool canYFlick() const 00901 { 00902 return q->contentsSize().height() > q->viewportGeometry().height(); 00903 } 00904 00905 int elapsed(const QTime &t) const 00906 { 00907 int n = t.msecsTo(QTime::currentTime()); 00908 if (n < 0) // passed midnight 00909 n += 86400 * 1000; 00910 return n; 00911 } 00912 00913 int restart(QTime &t) const 00914 { 00915 QTime time = QTime::currentTime(); 00916 int n = t.msecsTo(time); 00917 if (n < 0) // passed midnight 00918 n += 86400*1000; 00919 t = time; 00920 return n; 00921 } 00922 00923 void createFlickAnimations() 00924 { 00925 if (widget.data()) { 00926 QString xProp = QString::fromLatin1("x"); 00927 QString yProp = QString::fromLatin1("y"); 00928 00929 if (hasXProperty) 00930 xProp = QString::fromLatin1("scrollPositionX"); 00931 if (hasYProperty) 00932 yProp = QString::fromLatin1("scrollPositionY"); 00933 00934 flickAnimationX = new QPropertyAnimation(widget.data(), 00935 xProp.toLatin1(), widget.data()); 00936 flickAnimationY = new QPropertyAnimation(widget.data(), 00937 yProp.toLatin1(), widget.data()); 00938 QObject::connect(flickAnimationX, SIGNAL(finished()), 00939 q, SLOT(fixupX())); 00940 QObject::connect(flickAnimationY, SIGNAL(finished()), 00941 q, SLOT(fixupY())); 00942 00943 QObject::connect(flickAnimationX, 00944 SIGNAL(stateChanged(QAbstractAnimation::State, 00945 QAbstractAnimation::State)), 00946 q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, 00947 QAbstractAnimation::State))); 00948 QObject::connect(flickAnimationY, 00949 SIGNAL(stateChanged(QAbstractAnimation::State, 00950 QAbstractAnimation::State)), 00951 q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, 00952 QAbstractAnimation::State))); 00953 00954 flickAnimationX->setEasingCurve(QEasingCurve::OutCirc); 00955 flickAnimationY->setEasingCurve(QEasingCurve::OutCirc); 00956 00957 00958 fixupAnimation.groupX = new QSequentialAnimationGroup(widget.data()); 00959 fixupAnimation.groupY = new QSequentialAnimationGroup(widget.data()); 00960 fixupAnimation.startX = new QPropertyAnimation(widget.data(), 00961 xProp.toLatin1(), widget.data()); 00962 fixupAnimation.startY = new QPropertyAnimation(widget.data(), 00963 yProp.toLatin1(), widget.data()); 00964 fixupAnimation.endX = new QPropertyAnimation(widget.data(), 00965 xProp.toLatin1(), widget.data()); 00966 fixupAnimation.endY = new QPropertyAnimation(widget.data(), 00967 yProp.toLatin1(), widget.data()); 00968 fixupAnimation.groupX->addAnimation( 00969 fixupAnimation.startX); 00970 fixupAnimation.groupY->addAnimation( 00971 fixupAnimation.startY); 00972 fixupAnimation.groupX->addAnimation( 00973 fixupAnimation.endX); 00974 fixupAnimation.groupY->addAnimation( 00975 fixupAnimation.endY); 00976 00977 fixupAnimation.startX->setEasingCurve(QEasingCurve::InQuad); 00978 fixupAnimation.endX->setEasingCurve(QEasingCurve::OutQuint); 00979 fixupAnimation.startY->setEasingCurve(QEasingCurve::InQuad); 00980 fixupAnimation.endY->setEasingCurve(QEasingCurve::OutQuint); 00981 00982 fixupAnimation.snapX = new QPropertyAnimation(widget.data(), 00983 xProp.toLatin1(), widget.data()); 00984 fixupAnimation.snapY = new QPropertyAnimation(widget.data(), 00985 yProp.toLatin1(), widget.data()); 00986 fixupAnimation.snapX->setEasingCurve(QEasingCurve::InOutQuad); 00987 fixupAnimation.snapY->setEasingCurve(QEasingCurve::InOutQuad); 00988 00989 QObject::connect(fixupAnimation.groupX, 00990 SIGNAL(stateChanged(QAbstractAnimation::State, 00991 QAbstractAnimation::State)), 00992 q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, 00993 QAbstractAnimation::State))); 00994 QObject::connect(fixupAnimation.groupY, 00995 SIGNAL(stateChanged(QAbstractAnimation::State, 00996 QAbstractAnimation::State)), 00997 q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, 00998 QAbstractAnimation::State))); 00999 01000 directMoveAnimation = new QPropertyAnimation(q, 01001 "scrollPosition", 01002 q); 01003 QObject::connect(directMoveAnimation, SIGNAL(finished()), 01004 q, SLOT(fixupX())); 01005 QObject::connect(directMoveAnimation, SIGNAL(finished()), 01006 q, SLOT(fixupY())); 01007 QObject::connect(directMoveAnimation, 01008 SIGNAL(stateChanged(QAbstractAnimation::State, 01009 QAbstractAnimation::State)), 01010 q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, 01011 QAbstractAnimation::State))); 01012 directMoveAnimation->setEasingCurve(QEasingCurve::OutCirc); 01013 } 01014 } 01015 01016 void deleteFlickAnimations() 01017 { 01018 if (flickAnimationX) 01019 flickAnimationX->stop(); 01020 if (flickAnimationY) 01021 flickAnimationY->stop(); 01022 delete flickAnimationX; 01023 delete flickAnimationY; 01024 delete fixupAnimation.groupX; 01025 delete fixupAnimation.groupY; 01026 delete directMoveAnimation; 01027 delete fixupAnimation.snapX; 01028 delete fixupAnimation.snapY; 01029 } 01030 01031 void setScrollX() 01032 { 01033 if (horizontalScrollBarPolicy != Qt::ScrollBarAlwaysOff) { 01034 horizontalScrollBar->blockSignals(true); 01035 horizontalScrollBar->setValue(-widget.data()->pos().x()/10.); 01036 horizontalScrollBar->blockSignals(false); 01037 } 01038 } 01039 01040 void setScrollY() 01041 { 01042 if (verticalScrollBarPolicy != Qt::ScrollBarAlwaysOff) { 01043 verticalScrollBar->blockSignals(true); 01044 verticalScrollBar->setValue(-widget.data()->pos().y()/10.); 01045 verticalScrollBar->blockSignals(false); 01046 } 01047 } 01048 01049 ScrollWidget *q; 01050 QGraphicsWidget *scrollingWidget; 01051 QWeakPointer<QGraphicsWidget> widget; 01052 Plasma::Svg *borderSvg; 01053 Plasma::SvgWidget *topBorder; 01054 Plasma::SvgWidget *bottomBorder; 01055 Plasma::SvgWidget *leftBorder; 01056 Plasma::SvgWidget *rightBorder; 01057 QGraphicsGridLayout *layout; 01058 ScrollBar *verticalScrollBar; 01059 Qt::ScrollBarPolicy verticalScrollBarPolicy; 01060 ScrollBar *horizontalScrollBar; 01061 Qt::ScrollBarPolicy horizontalScrollBarPolicy; 01062 QString styleSheet; 01063 QWeakPointer<QGraphicsWidget> widgetToBeVisible; 01064 QRectF rectToBeVisible; 01065 QPointF dragHandleClicked; 01066 bool dragging; 01067 QTimer *adjustScrollbarsTimer; 01068 QTimer *wheelTimer; 01069 01070 QPointF pressPos; 01071 QPointF pressScrollPos; 01072 QPointF velocity; 01073 QPointF lastPos; 01074 QTime pressTime; 01075 QTime lastPosTime; 01076 QPropertyAnimation *flickAnimationX; 01077 QPropertyAnimation *flickAnimationY; 01078 struct { 01079 QAnimationGroup *groupX; 01080 QPropertyAnimation *startX; 01081 QPropertyAnimation *endX; 01082 01083 QAnimationGroup *groupY; 01084 QPropertyAnimation *startY; 01085 QPropertyAnimation *endY; 01086 01087 QPropertyAnimation *snapX; 01088 QPropertyAnimation *snapY; 01089 } fixupAnimation; 01090 QPropertyAnimation *directMoveAnimation; 01091 QSizeF snapSize; 01092 bool stealEvent; 01093 bool hasOvershoot; 01094 bool overflowBordersVisible; 01095 01096 Qt::Alignment alignment; 01097 01098 Gesture multitouchGesture; 01099 01100 bool hasContentsProperty; 01101 bool hasOffsetProperty; 01102 bool hasXProperty; 01103 bool hasYProperty; 01104 }; 01105 01106 01107 ScrollWidget::ScrollWidget(QGraphicsItem *parent) 01108 : QGraphicsWidget(parent), 01109 d(new ScrollWidgetPrivate(this)) 01110 { 01111 d->commonConstructor(); 01112 } 01113 01114 ScrollWidget::ScrollWidget(QGraphicsWidget *parent) 01115 : QGraphicsWidget(parent), 01116 d(new ScrollWidgetPrivate(this)) 01117 { 01118 d->commonConstructor(); 01119 } 01120 01121 ScrollWidget::~ScrollWidget() 01122 { 01123 delete d; 01124 } 01125 01126 void ScrollWidget::setWidget(QGraphicsWidget *widget) 01127 { 01128 if (d->widget && d->widget.data() != widget) { 01129 d->deleteFlickAnimations(); 01130 d->widget.data()->removeEventFilter(this); 01131 delete d->widget.data(); 01132 } 01133 01134 d->widget = widget; 01135 //it's not good it's setting a size policy here, but it's done to be retrocompatible with older applications 01136 if (widget) { 01137 d->hasContentsProperty = widget->property("contentsSize").isValid(); 01138 d->hasOffsetProperty = widget->property("scrollPosition").isValid(); 01139 d->hasXProperty = widget->property("scrollPositionX").isValid(); 01140 d->hasYProperty = widget->property("scrollPositionY").isValid(); 01141 d->createFlickAnimations(); 01142 01143 connect(widget, SIGNAL(xChanged()), this, SLOT(setScrollX())); 01144 connect(widget, SIGNAL(yChanged()), this, SLOT(setScrollY())); 01145 widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 01146 widget->setParentItem(d->scrollingWidget); 01147 widget->setPos(d->minXExtent(), d->minYExtent()); 01148 widget->installEventFilter(this); 01149 d->adjustScrollbarsTimer->start(200); 01150 } 01151 } 01152 01153 QGraphicsWidget *ScrollWidget::widget() const 01154 { 01155 return d->widget.data(); 01156 } 01157 01158 01159 void ScrollWidget::setHorizontalScrollBarPolicy(const Qt::ScrollBarPolicy policy) 01160 { 01161 d->horizontalScrollBarPolicy = policy; 01162 } 01163 01164 01165 Qt::ScrollBarPolicy ScrollWidget::horizontalScrollBarPolicy() const 01166 { 01167 return d->horizontalScrollBarPolicy; 01168 } 01169 01170 01171 void ScrollWidget::setVerticalScrollBarPolicy(const Qt::ScrollBarPolicy policy) 01172 { 01173 d->verticalScrollBarPolicy = policy; 01174 } 01175 01176 Qt::ScrollBarPolicy ScrollWidget::verticalScrollBarPolicy() const 01177 { 01178 return d->verticalScrollBarPolicy; 01179 } 01180 01181 bool ScrollWidget::overflowBordersVisible() const 01182 { 01183 return d->overflowBordersVisible; 01184 } 01185 01186 void ScrollWidget::setOverflowBordersVisible(const bool visible) 01187 { 01188 if (d->overflowBordersVisible == visible) { 01189 return; 01190 } 01191 01192 d->overflowBordersVisible = visible; 01193 d->adjustScrollbars(); 01194 } 01195 01196 void ScrollWidget::ensureRectVisible(const QRectF &rect) 01197 { 01198 if (!d->widget) { 01199 return; 01200 } 01201 01202 d->rectToBeVisible = rect; 01203 d->makeRectVisible(); 01204 } 01205 01206 void ScrollWidget::ensureItemVisible(QGraphicsItem *item) 01207 { 01208 if (!d->widget || !item) { 01209 return; 01210 } 01211 01212 QGraphicsItem *parentOfItem = item->parentItem(); 01213 while (parentOfItem != d->widget.data()) { 01214 if (!parentOfItem) { 01215 return; 01216 } 01217 01218 parentOfItem = parentOfItem->parentItem(); 01219 } 01220 01221 //since we can't ensure it'll stay alive we can delay only if it's a qgraphicswidget 01222 QGraphicsWidget *widget = qgraphicsitem_cast<QGraphicsWidget *>(item); 01223 if (widget) { 01224 d->widgetToBeVisible = widget; 01225 01226 // We need to wait for the parent item to resize... 01227 QTimer::singleShot(0, this, SLOT(makeItemVisible())); 01228 } else { 01229 d->makeItemVisible(item); 01230 } 01231 } 01232 01233 #ifndef KDE_NO_DEPRECATED 01234 void ScrollWidget::registerAsDragHandle(QGraphicsWidget *item) 01235 { 01236 Q_UNUSED(item); 01237 return; 01238 } 01239 #endif 01240 01241 #ifndef KDE_NO_DEPRECATED 01242 void ScrollWidget::unregisterAsDragHandle(QGraphicsWidget *item) 01243 { 01244 Q_UNUSED(item); 01245 return; 01246 } 01247 #endif 01248 01249 QRectF ScrollWidget::viewportGeometry() const 01250 { 01251 QRectF result; 01252 if (!d->widget) { 01253 return result; 01254 } 01255 01256 return d->scrollingWidget->boundingRect(); 01257 } 01258 01259 QSizeF ScrollWidget::contentsSize() const 01260 { 01261 if (d->widget) { 01262 if (d->hasContentsProperty) { 01263 QVariant var = d->widget.data()->property("contentsSize"); 01264 return var.toSizeF(); 01265 } else 01266 return d->widget.data()->size(); 01267 } 01268 return QSizeF(); 01269 } 01270 01271 void ScrollWidget::setScrollPosition(const QPointF &position) 01272 { 01273 if (d->widget) { 01274 if (d->hasOffsetProperty) 01275 d->widget.data()->setProperty("scrollPosition", position); 01276 else 01277 d->widget.data()->setPos(-position.toPoint()); 01278 } 01279 } 01280 01281 QPointF ScrollWidget::scrollPosition() const 01282 { 01283 if (d->widget) { 01284 if (d->hasOffsetProperty) { 01285 QVariant var = d->widget.data()->property("scrollPosition"); 01286 return var.toPointF(); 01287 } else { 01288 return -d->widget.data()->pos(); 01289 } 01290 } 01291 return QPointF(); 01292 } 01293 01294 void ScrollWidget::setSnapSize(const QSizeF &size) 01295 { 01296 d->snapSize = size; 01297 } 01298 01299 QSizeF ScrollWidget::snapSize() const 01300 { 01301 return d->snapSize; 01302 } 01303 01304 void ScrollWidget::setStyleSheet(const QString &styleSheet) 01305 { 01306 d->styleSheet = styleSheet; 01307 d->verticalScrollBar->setStyleSheet(styleSheet); 01308 d->horizontalScrollBar->setStyleSheet(styleSheet); 01309 } 01310 01311 QString ScrollWidget::styleSheet() const 01312 { 01313 return d->styleSheet; 01314 } 01315 01316 QWidget *ScrollWidget::nativeWidget() const 01317 { 01318 return 0; 01319 } 01320 01321 void ScrollWidget::focusInEvent(QFocusEvent *event) 01322 { 01323 Q_UNUSED(event) 01324 01325 if (d->widget) { 01326 d->widget.data()->setFocus(); 01327 } 01328 } 01329 01330 01331 void ScrollWidget::resizeEvent(QGraphicsSceneResizeEvent *event) 01332 { 01333 if (!d->widget) { 01334 QGraphicsWidget::resizeEvent(event); 01335 return; 01336 } 01337 01338 d->adjustScrollbarsTimer->start(200); 01339 01340 //if topBorder exists bottomBorder too 01341 if (d->topBorder) { 01342 d->topBorder->resize(event->newSize().width(), d->topBorder->size().height()); 01343 d->bottomBorder->resize(event->newSize().width(), d->bottomBorder->size().height()); 01344 d->bottomBorder->setPos(0, event->newSize().height() - d->bottomBorder->size().height()); 01345 } 01346 if (d->leftBorder) { 01347 d->leftBorder->resize(d->leftBorder->size().width(), event->newSize().height()); 01348 d->rightBorder->resize(d->rightBorder->size().width(), event->newSize().height()); 01349 d->rightBorder->setPos(event->newSize().width() - d->rightBorder->size().width(), 0); 01350 } 01351 01352 QGraphicsWidget::resizeEvent(event); 01353 } 01354 01355 void ScrollWidget::keyPressEvent(QKeyEvent *event) 01356 { 01357 d->handleKeyPressEvent(event); 01358 } 01359 01360 void ScrollWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 01361 { 01362 if (!d->widget) { 01363 return; 01364 } 01365 01366 d->handleMouseMoveEvent(event); 01367 event->accept(); 01368 01369 return QGraphicsWidget::mouseMoveEvent(event); 01370 } 01371 01372 void ScrollWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) 01373 { 01374 if (!d->widget) { 01375 return; 01376 } else if (!d->canYFlick() && !d->canXFlick()) { 01377 event->ignore(); 01378 return; 01379 } 01380 01381 d->handleMousePressEvent(event); 01382 01383 if (event->button() == Qt::LeftButton) { 01384 event->accept(); 01385 } else { 01386 QGraphicsWidget::mousePressEvent(event); 01387 } 01388 } 01389 01390 void ScrollWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 01391 { 01392 if (!d->widget) { 01393 return; 01394 } 01395 01396 d->handleMouseReleaseEvent(event); 01397 event->accept(); 01398 } 01399 01400 void ScrollWidget::wheelEvent(QGraphicsSceneWheelEvent *event) 01401 { 01402 if (!d->widget) { 01403 return; 01404 } else if (!d->canYFlick() && !d->canXFlick()) { 01405 event->ignore(); 01406 return; 01407 } 01408 d->handleWheelEvent(event); 01409 event->accept(); 01410 } 01411 01412 bool ScrollWidget::eventFilter(QObject *watched, QEvent *event) 01413 { 01414 if (!d->widget) { 01415 return false; 01416 } 01417 01418 if (watched == d->scrollingWidget && (event->type() == QEvent::GraphicsSceneResize || 01419 event->type() == QEvent::Move)) { 01420 emit viewportGeometryChanged(viewportGeometry()); 01421 } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneResize) { 01422 d->stopAnimations(); 01423 d->adjustScrollbarsTimer->start(200); 01424 updateGeometry(); 01425 ensureItemVisible(d->widget.data()); 01426 } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneMove) { 01427 d->horizontalScrollBar->blockSignals(true); 01428 d->verticalScrollBar->blockSignals(true); 01429 d->horizontalScrollBar->setValue(-d->widget.data()->pos().x()/10); 01430 d->verticalScrollBar->setValue(-d->widget.data()->pos().y()/10); 01431 d->horizontalScrollBar->blockSignals(false); 01432 d->verticalScrollBar->blockSignals(false); 01433 } 01434 01435 return false; 01436 } 01437 01438 QSizeF ScrollWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const 01439 { 01440 if (!d->widget || which == Qt::MaximumSize) { 01441 return QGraphicsWidget::sizeHint(which, constraint); 01442 //FIXME: it should ake the minimum hint of the contained widget, but the result is in a ridiculously big widget 01443 } else if (which == Qt::MinimumSize) { 01444 return QSizeF(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); 01445 } 01446 01447 QSizeF hint = d->widget.data()->effectiveSizeHint(which, constraint); 01448 if (d->horizontalScrollBar && d->horizontalScrollBar->isVisible()) { 01449 hint += QSize(0, d->horizontalScrollBar->size().height()); 01450 } 01451 if (d->verticalScrollBar && d->verticalScrollBar->isVisible()) { 01452 hint += QSize(d->verticalScrollBar->size().width(), 0); 01453 } 01454 01455 return hint; 01456 } 01457 01458 01459 bool ScrollWidget::sceneEventFilter(QGraphicsItem *i, QEvent *e) 01460 { 01461 //only the scrolling widget and its children 01462 if (!d->widget.data() || 01463 (!d->scrollingWidget->isAncestorOf(i) && i != d->scrollingWidget) || 01464 i == d->horizontalScrollBar || i == d->verticalScrollBar) { 01465 return false; 01466 } 01467 01468 if (i->isWidget()) { 01469 Plasma::Label *label = dynamic_cast<Plasma::Label *>(static_cast<QGraphicsWidget *>(i)); 01470 if (label && (label->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) { 01471 return false; 01472 } 01473 01474 Plasma::TextEdit *textEdit = dynamic_cast<Plasma::TextEdit *>(static_cast<QGraphicsWidget *>(i)); 01475 if (textEdit && (textEdit->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) { 01476 return false; 01477 } 01478 01479 Plasma::TextBrowser *textBrowser= dynamic_cast<Plasma::TextBrowser *>(static_cast<QGraphicsWidget *>(i)); 01480 if (textBrowser && (textBrowser->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) { 01481 return false; 01482 } 01483 } 01484 01485 bool stealThisEvent = d->stealEvent; 01486 //still pass around mouse moves: try to make still possible to make items start a drag event. thi could be either necessary or annoying, let's see how it goes. (add QEvent::GraphicsSceneMouseMove to block them) 01487 stealThisEvent &= (e->type() == QEvent::GraphicsSceneMousePress || 01488 e->type() == QEvent::GraphicsSceneMouseRelease); 01489 #if DEBUG 01490 qDebug()<<"sceneEventFilter = " <<i<<", " 01491 <<QTime::currentTime().toString(QString::fromLatin1("hh:mm:ss.zzz")); 01492 #endif 01493 switch (e->type()) { 01494 case QEvent::GraphicsSceneMousePress: 01495 d->handleMousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(e)); 01496 break; 01497 case QEvent::GraphicsSceneMouseMove: 01498 d->handleMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(e)); 01499 break; 01500 case QEvent::GraphicsSceneMouseRelease: 01501 d->handleMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(e)); 01502 break; 01503 01504 //Multitouch related events, we actually need only TouchUpdate 01505 case QEvent::TouchUpdate: { 01506 QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(e)->touchPoints(); 01507 if (touchPoints.count() == 2) { 01508 const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first(); 01509 const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last(); 01510 const QLineF line0(touchPoint0.lastPos(), touchPoint1.lastPos()); 01511 const QLineF line1(touchPoint0.pos(), touchPoint1.pos()); 01512 const QLineF startLine(touchPoint0.startPos(), touchPoint1.startPos()); 01513 const QPointF point = line1.pointAt(0.5); 01514 const QPointF lastPoint = line0.pointAt(0.5); 01515 01516 if (d->multitouchGesture == ScrollWidgetPrivate::GestureNone) { 01517 d->multitouchGesture = ScrollWidgetPrivate::GestureUndefined; 01518 } 01519 if (d->multitouchGesture == ScrollWidgetPrivate::GestureUndefined) { 01520 const int zoomDistance = qAbs(line1.length() - startLine.length()); 01521 const int dragDistance = (startLine.pointAt(0.5) - point).manhattanLength(); 01522 01523 if (zoomDistance - dragDistance > 30) { 01524 d->multitouchGesture = ScrollWidgetPrivate::GestureZoom; 01525 } else if (dragDistance - zoomDistance > 30) { 01526 d->multitouchGesture = ScrollWidgetPrivate::GestureScroll; 01527 } 01528 } 01529 01530 if (d->multitouchGesture == ScrollWidgetPrivate::GestureScroll) { 01531 QGraphicsSceneMouseEvent fakeEvent; 01532 fakeEvent.setPos(point); 01533 fakeEvent.setLastPos(lastPoint); 01534 d->handleMouseMoveEvent(&fakeEvent); 01535 } else if (d->multitouchGesture == ScrollWidgetPrivate::GestureZoom) { 01536 if (d->widget && d->widget.data()->property("zoomFactor").isValid()) { 01537 qreal scaleFactor = 1; 01538 if (line0.length() > 0) { 01539 scaleFactor = line1.length() / line0.length(); 01540 } 01541 01542 qreal zoom = d->widget.data()->property("zoomFactor").toReal(); 01543 d->widget.data()->setProperty("zoomFactor", zoom * scaleFactor); 01544 } 01545 } 01546 } 01547 break; 01548 } 01549 default: 01550 break; 01551 } 01552 if (stealThisEvent) 01553 return true; 01554 return QGraphicsWidget::sceneEventFilter(i, e); 01555 } 01556 01557 void Plasma::ScrollWidget::setAlignment(Qt::Alignment align) 01558 { 01559 d->alignment = align; 01560 if (d->widget.data() && 01561 d->widget.data()->isVisible()) { 01562 d->widget.data()->setPos(d->minXExtent(), 01563 d->minYExtent()); 01564 } 01565 } 01566 01567 Qt::Alignment Plasma::ScrollWidget::alignment() const 01568 { 01569 return d->alignment; 01570 } 01571 01572 void ScrollWidget::setOverShoot(bool enable) 01573 { 01574 d->hasOvershoot = enable; 01575 } 01576 01577 bool ScrollWidget::hasOverShoot() const 01578 { 01579 return d->hasOvershoot; 01580 } 01581 01582 } // namespace Plasma 01583 01584 01585 #include <scrollwidget.moc> 01586
KDE 4.6 API Reference