/* * Copyright 2009 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "scrollwidget.h" #include //Qt #include #include #include #include #include #include #include #include #include #include #include #include //KDE #include #include #include #include #include //Plasma #include #include #include #include #include #include #include #define DEBUG 0 /* The flicking code is largely based on the behavior of the flickable widget in QDeclerative so porting between the two should preserve the behavior. The code that figures out velocity could use some improvements, in particular IGNORE_SUSPICIOUS_MOVES is a hack that shouldn't be necessary. */ //XXX fixme // we use a timer between move events to figure out // the velocity of a move, but sometimes we're getting move // events with big positional changes with no break // in between them, which causes us to compute // huge velocities. this define just filters out // events which come at insanly small time intervals. // at some point we need to figure out how to do it properly #define IGNORE_SUSPICIOUS_MOVES 1 // FlickThreshold determines how far the "mouse" must have moved // before we perform a flick. static const int FlickThreshold = 20; static const qreal MinimumFlickVelocity = 200; static const qreal MaxVelocity = 2000; // time it takes the widget to flick back to its // bounds when overshot static const qreal FixupDuration = 600; namespace Plasma { class ScrollWidgetPrivate { public: ScrollWidgetPrivate(ScrollWidget *parent) : q(parent), topBorder(0), bottomBorder(0), leftBorder(0), rightBorder(0), overflowBordersVisible(true) { } ~ScrollWidgetPrivate() { } void commonConstructor() { q->setFocusPolicy(Qt::StrongFocus); q->setFiltersChildEvents(true); layout = new QGraphicsGridLayout(q); q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout->setContentsMargins(0, 0, 0, 0); scrollingWidget = new QGraphicsWidget(q); scrollingWidget->setFlag(QGraphicsItem::ItemHasNoContents); scrollingWidget->installEventFilter(q); layout->addItem(scrollingWidget, 0, 0); borderSvg = new Plasma::Svg(q); borderSvg->setImagePath("widgets/scrollwidget"); wheelTimer = new QTimer(q); wheelTimer->setSingleShot(true); verticalScrollBarPolicy = Qt::ScrollBarAsNeeded; verticalScrollBar = new Plasma::ScrollBar(q); verticalScrollBar->setFocusPolicy(Qt::NoFocus); layout->addItem(verticalScrollBar, 0, 1); verticalScrollBar->nativeWidget()->setMinimum(0); verticalScrollBar->nativeWidget()->setMaximum(100); QObject::connect(verticalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(verticalScroll(int))); horizontalScrollBarPolicy = Qt::ScrollBarAsNeeded; horizontalScrollBar = new Plasma::ScrollBar(q); verticalScrollBar->setFocusPolicy(Qt::NoFocus); horizontalScrollBar->setOrientation(Qt::Horizontal); layout->addItem(horizontalScrollBar, 1, 0); horizontalScrollBar->nativeWidget()->setMinimum(0); horizontalScrollBar->nativeWidget()->setMaximum(100); QObject::connect(horizontalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(horizontalScroll(int))); layout->setColumnSpacing(0, 0); layout->setColumnSpacing(1, 0); layout->setRowSpacing(0, 0); layout->setRowSpacing(1, 0); flickAnimationX = 0; flickAnimationY = 0; fixupAnimation.groupX = 0; fixupAnimation.startX = 0; fixupAnimation.endX = 0; fixupAnimation.groupY = 0; fixupAnimation.startY = 0; fixupAnimation.endY = 0; fixupAnimation.snapX = 0; fixupAnimation.snapY = 0; directMoveAnimation = 0; stealEvent = false; hasOvershoot = true; alignment = (Qt::AlignLeft | Qt::AlignTop); } void adjustScrollbars() { if (!widget) { return; } const bool verticalVisible = widget.data()->size().height() > q->size().height(); const bool horizontalVisible = widget.data()->size().width() > q->size().width(); verticalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().height() - scrollingWidget->size().height())/10))); verticalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().height())/10); if (verticalScrollBarPolicy == Qt::ScrollBarAlwaysOff || !verticalVisible) { if (layout->count() > 2 && layout->itemAt(2) == verticalScrollBar) { layout->removeAt(2); } else if (layout->count() > 1 && layout->itemAt(1) == verticalScrollBar) { layout->removeAt(1); } verticalScrollBar->hide(); } else if (!verticalScrollBar->isVisible()) { layout->addItem(verticalScrollBar, 0, 1); verticalScrollBar->show(); } horizontalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().width() - scrollingWidget->size().width())/10))); horizontalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().width())/10); if (horizontalScrollBarPolicy == Qt::ScrollBarAlwaysOff || !horizontalVisible) { if (layout->count() > 2 && layout->itemAt(2) == horizontalScrollBar) { layout->removeAt(2); } else if (layout->count() > 1 && layout->itemAt(1) == horizontalScrollBar) { layout->removeAt(1); } horizontalScrollBar->hide(); } else if (!horizontalScrollBar->isVisible()) { layout->addItem(horizontalScrollBar, 1, 0); horizontalScrollBar->show(); } if (widget && !topBorder && verticalVisible) { topBorder = new Plasma::SvgWidget(q); topBorder->setSvg(borderSvg); topBorder->setElementID("border-top"); topBorder->setZValue(900); topBorder->resize(topBorder->effectiveSizeHint(Qt::PreferredSize)); topBorder->setVisible(overflowBordersVisible); bottomBorder = new Plasma::SvgWidget(q); bottomBorder->setSvg(borderSvg); bottomBorder->setElementID("border-bottom"); bottomBorder->setZValue(900); bottomBorder->resize(bottomBorder->effectiveSizeHint(Qt::PreferredSize)); bottomBorder->setVisible(overflowBordersVisible); } else if (topBorder && widget && !verticalVisible) { //FIXME: in some cases topBorder->deleteLater() is deleteNever(), why? topBorder->hide(); bottomBorder->hide(); topBorder->deleteLater(); bottomBorder->deleteLater(); topBorder = 0; bottomBorder = 0; } if (widget && !leftBorder && horizontalVisible) { leftBorder = new Plasma::SvgWidget(q); leftBorder->setSvg(borderSvg); leftBorder->setElementID("border-left"); leftBorder->setZValue(900); leftBorder->resize(leftBorder->effectiveSizeHint(Qt::PreferredSize)); leftBorder->setVisible(overflowBordersVisible); rightBorder = new Plasma::SvgWidget(q); rightBorder->setSvg(borderSvg); rightBorder->setElementID("border-right"); rightBorder->setZValue(900); rightBorder->resize(rightBorder->effectiveSizeHint(Qt::PreferredSize)); rightBorder->setVisible(overflowBordersVisible); } else if (leftBorder && widget && !horizontalVisible) { leftBorder->hide(); rightBorder->hide(); leftBorder->deleteLater(); rightBorder->deleteLater(); leftBorder = 0; rightBorder = 0; } layout->activate(); if (topBorder) { topBorder->resize(q->size().width(), topBorder->size().height()); bottomBorder->resize(q->size().width(), bottomBorder->size().height()); bottomBorder->setPos(0, q->size().height() - topBorder->size().height()); } if (leftBorder) { leftBorder->resize(leftBorder->size().width(), q->size().height()); rightBorder->resize(rightBorder->size().width(), q->size().height()); rightBorder->setPos(q->size().width() - rightBorder->size().width(), 0); } QSizeF widgetSize = widget.data()->size(); if (widget.data()->sizePolicy().expandingDirections() & Qt::Horizontal) { //keep a 1 pixel border widgetSize.setWidth(scrollingWidget->size().width()); } if (widget.data()->sizePolicy().expandingDirections() & Qt::Vertical) { widgetSize.setHeight(scrollingWidget->size().height()); } widget.data()->resize(widgetSize); adjustClipping(); } void verticalScroll(int value) { if (!widget) { return; } widget.data()->setPos(QPoint(widget.data()->pos().x(), -value*10)); } void horizontalScroll(int value) { if (!widget) { return; } widget.data()->setPos(QPoint(-value*10, widget.data()->pos().y())); } void adjustClipping() { if (!widget) { return; } const bool clip = widget.data()->size().width() > scrollingWidget->size().width() || widget.data()->size().height() > scrollingWidget->size().height(); scrollingWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, clip); } qreal overShootDistance(qreal velocity, qreal size) const { if (MaxVelocity <= 0) return 0.0; velocity = qAbs(velocity); if (velocity > MaxVelocity) velocity = MaxVelocity; qreal dist = size / 4 * velocity / MaxVelocity; return dist; } void animateMoveTo(const QPointF &pos) { qreal duration = 800; QPointF start = q->scrollPosition(); QSizeF threshold = q->viewportGeometry().size(); QPointF diff = pos - start; //reduce if it's within the viewport if (qAbs(diff.x()) < threshold.width() || qAbs(diff.y()) < threshold.height()) duration /= 2; fixupAnimation.groupX->stop(); fixupAnimation.groupY->stop(); fixupAnimation.snapX->stop(); fixupAnimation.snapY->stop(); directMoveAnimation->setStartValue(start); directMoveAnimation->setEndValue(pos); directMoveAnimation->setDuration(duration); directMoveAnimation->start(); } void flick(QPropertyAnimation *anim, qreal velocity, qreal val, qreal minExtent, qreal maxExtent, qreal size) { qreal deceleration = 500; qreal maxDistance = -1; qreal target = 0; // -ve velocity means list is moving up if (velocity > 0) { if (val < minExtent) maxDistance = qAbs(minExtent - val + (hasOvershoot?overShootDistance(velocity,size):0)); target = minExtent; deceleration = -deceleration; } else { if (val > maxExtent) maxDistance = qAbs(maxExtent - val) + (hasOvershoot?overShootDistance(velocity,size):0); target = maxExtent; } if (maxDistance > 0) { qreal v = velocity; if (MaxVelocity != -1 && MaxVelocity < qAbs(v)) { if (v < 0) v = -MaxVelocity; else v = MaxVelocity; } qreal duration = qAbs(v / deceleration); qreal diffY = v * duration + (0.5 * deceleration * duration * duration); qreal startY = val; qreal endY = startY + diffY; if (velocity > 0) { if (endY > target) endY = startY + maxDistance; } else { if (endY < target) endY = startY - maxDistance; } duration = qAbs((endY-startY)/ (-v/2)); #if DEBUG qDebug()<<"XXX velocity = "<delta()/3; //ifthe widget can scroll in a single axis and the wheel is the other one, scroll the other one Qt::Orientation orientation = event->orientation(); if (orientation == Qt::Vertical) { if (!canYFlick() && canXFlick()) { end += QPointF(step, 0); } else if (canYFlick()) { end += QPointF(0, step); } else { return; } } else { if (canYFlick() && !canXFlick()) { end += QPointF(0, step); } else if (canXFlick()) { end += QPointF(step, 0); } else { return; } } fixupAnimation.groupX->stop(); fixupAnimation.groupY->stop(); fixupAnimation.snapX->stop(); fixupAnimation.snapY->stop(); directMoveAnimation->setStartValue(start); directMoveAnimation->setEndValue(end); directMoveAnimation->setDuration(200); directMoveAnimation->start(); wheelTimer->start(50); } qreal minXExtent() const { if (alignment & Qt::AlignLeft) return 0; else { qreal vWidth = q->viewportGeometry().width(); qreal cWidth = q->contentsSize().width(); if (cWidth < vWidth) { if (alignment & Qt::AlignRight) return vWidth - cWidth; else if (alignment & Qt::AlignHCenter) return vWidth / 2 - cWidth / 2; } } return 0; } qreal maxXExtent() const { return q->viewportGeometry().width() - q->contentsSize().width(); } qreal minYExtent() const { if (alignment & Qt::AlignTop) return 0; else { qreal vHeight = q->viewportGeometry().height(); qreal cHeight = q->contentsSize().height(); if (cHeight < vHeight) { if (alignment & Qt::AlignBottom) return vHeight - cHeight; else if (alignment & Qt::AlignVCenter) return vHeight / 2 - cHeight / 2; } } return 0; } qreal maxYExtent() const { return q->viewportGeometry().height() - q->contentsSize().height(); } bool canXFlick() const { //make the thing feel quite "fixed" don't permit to flick when the contents size is less than the viewport return q->contentsSize().width() > q->viewportGeometry().width(); } bool canYFlick() const { return q->contentsSize().height() > q->viewportGeometry().height(); } void createFlickAnimations() { if (widget.data()) { QString xProp = QString::fromLatin1("x"); QString yProp = QString::fromLatin1("y"); flickAnimationX = new QPropertyAnimation(widget.data(), xProp.toLatin1(), widget.data()); flickAnimationY = new QPropertyAnimation(widget.data(), yProp.toLatin1(), widget.data()); QObject::connect(flickAnimationX, SIGNAL(finished()), q, SLOT(fixupX())); QObject::connect(flickAnimationY, SIGNAL(finished()), q, SLOT(fixupY())); QObject::connect(flickAnimationX, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); QObject::connect(flickAnimationY, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); flickAnimationX->setEasingCurve(QEasingCurve::OutCirc); flickAnimationY->setEasingCurve(QEasingCurve::OutCirc); fixupAnimation.groupX = new QSequentialAnimationGroup(widget.data()); fixupAnimation.groupY = new QSequentialAnimationGroup(widget.data()); fixupAnimation.startX = new QPropertyAnimation(widget.data(), xProp.toLatin1(), widget.data()); fixupAnimation.startY = new QPropertyAnimation(widget.data(), yProp.toLatin1(), widget.data()); fixupAnimation.endX = new QPropertyAnimation(widget.data(), xProp.toLatin1(), widget.data()); fixupAnimation.endY = new QPropertyAnimation(widget.data(), yProp.toLatin1(), widget.data()); fixupAnimation.groupX->addAnimation( fixupAnimation.startX); fixupAnimation.groupY->addAnimation( fixupAnimation.startY); fixupAnimation.groupX->addAnimation( fixupAnimation.endX); fixupAnimation.groupY->addAnimation( fixupAnimation.endY); fixupAnimation.startX->setEasingCurve(QEasingCurve::InQuad); fixupAnimation.endX->setEasingCurve(QEasingCurve::OutQuint); fixupAnimation.startY->setEasingCurve(QEasingCurve::InQuad); fixupAnimation.endY->setEasingCurve(QEasingCurve::OutQuint); fixupAnimation.snapX = new QPropertyAnimation(widget.data(), xProp.toLatin1(), widget.data()); fixupAnimation.snapY = new QPropertyAnimation(widget.data(), yProp.toLatin1(), widget.data()); fixupAnimation.snapX->setEasingCurve(QEasingCurve::InOutQuad); fixupAnimation.snapY->setEasingCurve(QEasingCurve::InOutQuad); QObject::connect(fixupAnimation.groupX, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); QObject::connect(fixupAnimation.groupY, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); directMoveAnimation = new QPropertyAnimation(q, "scrollPosition", q); QObject::connect(directMoveAnimation, SIGNAL(finished()), q, SLOT(fixupX())); QObject::connect(directMoveAnimation, SIGNAL(finished()), q, SLOT(fixupY())); QObject::connect(directMoveAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), q, SIGNAL(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); directMoveAnimation->setEasingCurve(QEasingCurve::OutCirc); } } void deleteFlickAnimations() { if (flickAnimationX) flickAnimationX->stop(); if (flickAnimationY) flickAnimationY->stop(); delete flickAnimationX; delete flickAnimationY; delete fixupAnimation.groupX; delete fixupAnimation.groupY; delete directMoveAnimation; delete fixupAnimation.snapX; delete fixupAnimation.snapY; } void setScrollX() { if (horizontalScrollBarPolicy != Qt::ScrollBarAlwaysOff) { horizontalScrollBar->blockSignals(true); horizontalScrollBar->setValue(-widget.data()->pos().x()/10.); horizontalScrollBar->blockSignals(false); } } void setScrollY() { if (verticalScrollBarPolicy != Qt::ScrollBarAlwaysOff) { verticalScrollBar->blockSignals(true); verticalScrollBar->setValue(-widget.data()->pos().y()/10.); verticalScrollBar->blockSignals(false); } } ScrollWidget *q; QGraphicsWidget *scrollingWidget; QWeakPointer widget; Plasma::Svg *borderSvg; Plasma::SvgWidget *topBorder; Plasma::SvgWidget *bottomBorder; Plasma::SvgWidget *leftBorder; Plasma::SvgWidget *rightBorder; QGraphicsGridLayout *layout; ScrollBar *verticalScrollBar; Qt::ScrollBarPolicy verticalScrollBarPolicy; ScrollBar *horizontalScrollBar; Qt::ScrollBarPolicy horizontalScrollBarPolicy; QWeakPointer widgetToBeVisible; QRectF rectToBeVisible; QTimer *wheelTimer; QPointF pressPos; QPointF pressScrollPos; QPointF velocity; QPointF lastPos; QElapsedTimer pressTime; QElapsedTimer lastPosTime; QPropertyAnimation *flickAnimationX; QPropertyAnimation *flickAnimationY; struct { QAnimationGroup *groupX; QPropertyAnimation *startX; QPropertyAnimation *endX; QAnimationGroup *groupY; QPropertyAnimation *startY; QPropertyAnimation *endY; QPropertyAnimation *snapX; QPropertyAnimation *snapY; } fixupAnimation; QPropertyAnimation *directMoveAnimation; QSizeF snapSize; bool stealEvent; bool hasOvershoot; bool overflowBordersVisible; Qt::Alignment alignment; }; ScrollWidget::ScrollWidget(QGraphicsItem *parent) : QGraphicsWidget(parent), d(new ScrollWidgetPrivate(this)) { d->commonConstructor(); } ScrollWidget::ScrollWidget(QGraphicsWidget *parent) : QGraphicsWidget(parent), d(new ScrollWidgetPrivate(this)) { d->commonConstructor(); } ScrollWidget::~ScrollWidget() { delete d; } void ScrollWidget::setWidget(QGraphicsWidget *widget) { if (d->widget && d->widget.data() != widget) { d->deleteFlickAnimations(); d->widget.data()->removeEventFilter(this); delete d->widget.data(); } d->widget = widget; // it's not good it's setting a size policy here, but it's done to be retrocompatible with older applications if (widget) { d->createFlickAnimations(); connect(widget, SIGNAL(xChanged()), this, SLOT(setScrollX())); connect(widget, SIGNAL(yChanged()), this, SLOT(setScrollY())); widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widget->setParentItem(d->scrollingWidget); widget->setPos(d->minXExtent(), d->minYExtent()); widget->installEventFilter(this); d->adjustScrollbars(); } } QGraphicsWidget *ScrollWidget::widget() const { return d->widget.data(); } void ScrollWidget::setHorizontalScrollBarPolicy(const Qt::ScrollBarPolicy policy) { d->horizontalScrollBarPolicy = policy; } Qt::ScrollBarPolicy ScrollWidget::horizontalScrollBarPolicy() const { return d->horizontalScrollBarPolicy; } void ScrollWidget::setVerticalScrollBarPolicy(const Qt::ScrollBarPolicy policy) { d->verticalScrollBarPolicy = policy; } Qt::ScrollBarPolicy ScrollWidget::verticalScrollBarPolicy() const { return d->verticalScrollBarPolicy; } bool ScrollWidget::overflowBordersVisible() const { return d->overflowBordersVisible; } void ScrollWidget::setOverflowBordersVisible(const bool visible) { if (d->overflowBordersVisible == visible) { return; } d->overflowBordersVisible = visible; d->adjustScrollbars(); } void ScrollWidget::ensureRectVisible(const QRectF &rect) { if (!d->widget) { return; } d->rectToBeVisible = rect; d->makeRectVisible(); } void ScrollWidget::ensureItemVisible(QGraphicsItem *item) { if (!d->widget || !item) { return; } QGraphicsItem *parentOfItem = item->parentItem(); while (parentOfItem != d->widget.data()) { if (!parentOfItem) { return; } parentOfItem = parentOfItem->parentItem(); } //since we can't ensure it'll stay alive we can delay only if it's a qgraphicswidget QGraphicsWidget *widget = qgraphicsitem_cast(item); if (widget) { d->widgetToBeVisible = widget; // We need to wait for the parent item to resize... QTimer::singleShot(0, this, SLOT(makeItemVisible())); } else { d->makeItemVisible(item); } } QRectF ScrollWidget::viewportGeometry() const { QRectF result; if (!d->widget) { return result; } return d->scrollingWidget->boundingRect(); } QSizeF ScrollWidget::contentsSize() const { if (d->widget) { return d->widget.data()->size(); } return QSizeF(); } void ScrollWidget::setScrollPosition(const QPointF &position) { if (d->widget) { d->widget.data()->setPos(-position.toPoint()); } } QPointF ScrollWidget::scrollPosition() const { if (d->widget) { return -d->widget.data()->pos(); } return QPointF(); } void ScrollWidget::setSnapSize(const QSizeF &size) { d->snapSize = size; } QSizeF ScrollWidget::snapSize() const { return d->snapSize; } QWidget *ScrollWidget::nativeWidget() const { return 0; } void ScrollWidget::focusInEvent(QFocusEvent *event) { Q_UNUSED(event) if (d->widget) { d->widget.data()->setFocus(); } } void ScrollWidget::resizeEvent(QGraphicsSceneResizeEvent *event) { if (!d->widget) { QGraphicsWidget::resizeEvent(event); return; } d->adjustScrollbars(); //if topBorder exists bottomBorder too if (d->topBorder) { d->topBorder->resize(event->newSize().width(), d->topBorder->size().height()); d->bottomBorder->resize(event->newSize().width(), d->bottomBorder->size().height()); d->bottomBorder->setPos(0, event->newSize().height() - d->bottomBorder->size().height()); } if (d->leftBorder) { d->leftBorder->resize(d->leftBorder->size().width(), event->newSize().height()); d->rightBorder->resize(d->rightBorder->size().width(), event->newSize().height()); d->rightBorder->setPos(event->newSize().width() - d->rightBorder->size().width(), 0); } QGraphicsWidget::resizeEvent(event); } void ScrollWidget::keyPressEvent(QKeyEvent *event) { d->handleKeyPressEvent(event); } void ScrollWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (!d->widget) { return; } d->handleMouseMoveEvent(event); event->accept(); return QGraphicsWidget::mouseMoveEvent(event); } void ScrollWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (!d->widget) { return; } else if (!d->canYFlick() && !d->canXFlick()) { event->ignore(); return; } d->handleMousePressEvent(event); if (event->button() == Qt::LeftButton) { event->accept(); } else { QGraphicsWidget::mousePressEvent(event); } } void ScrollWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (!d->widget) { return; } d->handleMouseReleaseEvent(event); event->accept(); } void ScrollWidget::wheelEvent(QGraphicsSceneWheelEvent *event) { if (!d->widget) { return; } else if (!d->canYFlick() && !d->canXFlick()) { event->ignore(); return; } d->handleWheelEvent(event); event->accept(); } bool ScrollWidget::eventFilter(QObject *watched, QEvent *event) { if (!d->widget) { return false; } if (watched == d->scrollingWidget && (event->type() == QEvent::GraphicsSceneResize || event->type() == QEvent::Move)) { emit viewportGeometryChanged(viewportGeometry()); } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneResize) { d->stopAnimations(); d->adjustScrollbars(); updateGeometry(); QPointF newPos = d->widget.data()->pos(); if (d->widget.data()->size().width() <= viewportGeometry().width()) { newPos.setX(d->minXExtent()); } if (d->widget.data()->size().height() <= viewportGeometry().height()) { newPos.setY(d->minYExtent()); } //check if the content is visible if (d->widget.data()->geometry().right() < 0) { newPos.setX(-d->widget.data()->geometry().width()+viewportGeometry().width()); } if (d->widget.data()->geometry().bottom() < 0) { newPos.setY(-d->widget.data()->geometry().height()+viewportGeometry().height()); } d->widget.data()->setPos(newPos); } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneMove) { d->horizontalScrollBar->blockSignals(true); d->verticalScrollBar->blockSignals(true); d->horizontalScrollBar->setValue(-d->widget.data()->pos().x()/10); d->verticalScrollBar->setValue(-d->widget.data()->pos().y()/10); d->horizontalScrollBar->blockSignals(false); d->verticalScrollBar->blockSignals(false); } return false; } QSizeF ScrollWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const { if (!d->widget || which == Qt::MaximumSize) { return QGraphicsWidget::sizeHint(which, constraint); //FIXME: it should ake the minimum hint of the contained widget, but the result is in a ridiculously big widget } else if (which == Qt::MinimumSize) { return QSizeF(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); } QSizeF hint = d->widget.data()->effectiveSizeHint(which, constraint); if (d->horizontalScrollBar && d->horizontalScrollBar->isVisible()) { hint += QSize(0, d->horizontalScrollBar->size().height()); } if (d->verticalScrollBar && d->verticalScrollBar->isVisible()) { hint += QSize(d->verticalScrollBar->size().width(), 0); } return hint; } bool ScrollWidget::sceneEventFilter(QGraphicsItem *i, QEvent *e) { //only the scrolling widget and its children if (!d->widget.data() || (!d->scrollingWidget->isAncestorOf(i) && i != d->scrollingWidget) || i == d->horizontalScrollBar || i == d->verticalScrollBar) { return false; } if (i->isWidget()) { Plasma::Label *label = dynamic_cast(static_cast(i)); if (label && (label->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) { return false; } Plasma::TextEdit *textEdit = dynamic_cast(static_cast(i)); if (textEdit && (textEdit->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) { return false; } Plasma::TextBrowser *textBrowser= dynamic_cast(static_cast(i)); if (textBrowser && (textBrowser->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) { return false; } } bool stealThisEvent = d->stealEvent; //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) stealThisEvent &= (e->type() == QEvent::GraphicsSceneMousePress || e->type() == QEvent::GraphicsSceneMouseRelease); #if DEBUG qDebug() << "sceneEventFilter = " << i <<", " << QTime::currentTime().toString(QString::fromLatin1("hh:mm:ss.zzz")); #endif switch (e->type()) { case QEvent::GraphicsSceneMousePress: { d->handleMousePressEvent(static_cast(e)); break; } case QEvent::GraphicsSceneMouseMove: { d->handleMouseMoveEvent(static_cast(e)); break; } case QEvent::GraphicsSceneMouseRelease: { d->handleMouseReleaseEvent(static_cast(e)); break; } default: { break; } } if (stealThisEvent) return true; return QGraphicsWidget::sceneEventFilter(i, e); } void Plasma::ScrollWidget::setAlignment(Qt::Alignment align) { d->alignment = align; if (d->widget.data() && d->widget.data()->isVisible()) { d->widget.data()->setPos(d->minXExtent(), d->minYExtent()); } } Qt::Alignment Plasma::ScrollWidget::alignment() const { return d->alignment; } void ScrollWidget::setOverShoot(bool enable) { d->hasOvershoot = enable; } bool ScrollWidget::hasOverShoot() const { return d->hasOvershoot; } } // namespace Plasma #include "moc_scrollwidget.cpp"