/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: qt-info@nokia.com ** ** This software, including documentation, is protected by copyright ** controlled by Nokia Corporation. You may use this software in ** accordance with the terms and conditions contained in the Qt Phone ** Demo License Agreement. ** ****************************************************************************/ #include #include #include #include #include "pageslider.h" Page::Page(QGraphicsItem *parent) : QGraphicsWidget(parent), m_fullOpaque(true) { setContentsMargins(0, 0, 0, 0); } bool Page::isFullOpaque() const { return m_fullOpaque; } void Page::setFullOpaque(bool opaque) { if (m_fullOpaque == opaque) { m_fullOpaque = opaque; } } QRectF Page::flickableRect() const { return QRectF(); } QPainterPath Page::opaqueArea() const { if (m_fullOpaque) return shape(); return QGraphicsWidget::opaqueArea(); } class PageSlot : public QGraphicsWidget { Q_OBJECT public: PageSlot(QGraphicsItem *parent = 0); void setContents(Page *page); QAbstractAnimation *createAnimation(qreal finalX); protected: void resizeEvent(QGraphicsSceneResizeEvent *event); private: Page *m_contents; }; PageSlot::PageSlot(QGraphicsItem *parent) : QGraphicsWidget(parent), m_contents(0) { setFlag(QGraphicsItem::ItemHasNoContents); } void PageSlot::resizeEvent(QGraphicsSceneResizeEvent *event) { QGraphicsWidget::resizeEvent(event); if (m_contents) m_contents->resize(event->newSize()); } void PageSlot::setContents(Page *page) { if (m_contents && m_contents->parentItem() == this) { m_contents->setParentItem(0); if (m_contents->scene()) m_contents->scene()->removeItem(m_contents); } m_contents = page; if (m_contents) { m_contents->setParentItem(this); m_contents->setGeometry(boundingRect()); } } QAbstractAnimation *PageSlot::createAnimation(qreal finalX) { QPropertyAnimation *animation = new QPropertyAnimation(this, "x"); animation->setDuration(500); animation->setEasingCurve(QEasingCurve::OutQuart); animation->setStartValue(x()); animation->setEndValue(finalX); return animation; } class PageSliderPrivate { public: PageSliderPrivate(PageSlider *qptr); void init(); void rotate(int direction); void refillRenderers(); void repositionRenderers(); qreal getStandardPosition(int index); bool isAnimating() const; void adjustVisibility(bool hide); PageSlider *q; bool isBack; bool keepFlickable; int adjustmentThreshold; QList views; QList pageSlots; QParallelAnimationGroup *animationInOut; int current; int direction; QPointF oldPos; QPointF firstPos; }; PageSliderPrivate::PageSliderPrivate(PageSlider *qptr) : q(qptr), isBack(false), adjustmentThreshold(160), current(-1) { } void PageSliderPrivate::init() { animationInOut = new QParallelAnimationGroup(q); QObject::connect(animationInOut, SIGNAL(finished()), q, SLOT(transitionFinished())); pageSlots.append(new PageSlot(q)); pageSlots.append(new PageSlot(q)); pageSlots.append(new PageSlot(q)); repositionRenderers(); } void PageSliderPrivate::rotate(int direction) { keepFlickable = q->isFlickable(); q->setFlickable(false); this->direction = direction; animationInOut->clear(); QAbstractAnimation *anim; int nc = pageSlots.count(); for (int i = 0; i < nc; i++) { if (direction < 0) anim = pageSlots[i]->createAnimation(getStandardPosition(i - 1)); else if (direction > 0) anim = pageSlots[i]->createAnimation(getStandardPosition(i + 1)); else anim = pageSlots[i]->createAnimation(getStandardPosition(i)); animationInOut->addAnimation(anim); } animationInOut->start(); } void PageSliderPrivate::repositionRenderers() { const int w = q->size().width(); const int h = q->size().height(); for (int i = 0; i < pageSlots.count(); i++) { const int x = getStandardPosition(i); pageSlots[i]->setGeometry(x, 0, w, h); } } void PageSliderPrivate::refillRenderers() { const int nc = pageSlots.count(); for (int i = 0; i < nc; i++) { const int ri = (i + current - nc / 2); const bool ok = (ri >= 0 && ri < views.count()); if (ok) pageSlots[i]->setContents(views[ri]); pageSlots[i]->setVisible((ri == current)); } } qreal PageSliderPrivate::getStandardPosition(int index) { const int w = q->size().width(); return index * w - pageSlots.count() * w / 2 + q->size().width() / 2; } bool PageSliderPrivate::isAnimating() const { return (animationInOut->state() == QAbstractAnimation::Running); } void PageSliderPrivate::adjustVisibility(bool hide) { const int nc = pageSlots.count(); for (int i = 0; i < nc; i++) { const int ri = (i + current - nc / 2); if (hide) pageSlots[i]->setVisible((ri == current)); else pageSlots[i]->setVisible((ri >= 0 && ri < nc)); } } PageSlider::PageSlider(QGraphicsItem *parent) : FlickableArea(parent), d(new PageSliderPrivate(this)) { setFlag(QGraphicsItem::ItemHasNoContents); d->init(); } PageSlider::~PageSlider() { delete d; } int PageSlider::pageCount() const { return d->views.count(); } bool PageSlider::addPage(Page *view) { if (!view || d->isAnimating()) return false; d->views.append(view); if (d->views.count() == 1) d->current = 0; d->refillRenderers(); return true; } bool PageSlider::removePage(Page *view) { if (!view || d->isAnimating()) return false; return d->views.removeOne(view); } int PageSlider::currentPage() const { return d->current; } void PageSlider::setCurrentPage(int index) { if (d->isAnimating() || index < 0 || index >= d->views.count()) return; if (d->current != index) { d->current = index; d->repositionRenderers(); d->refillRenderers(); setFlickableRect(d->views[index]->flickableRect()); } } int PageSlider::adjustmentThreshold() const { return d->adjustmentThreshold; } void PageSlider::setAdjustmentThreshold(int value) { d->adjustmentThreshold = qMax(0, value); } void PageSlider::transitionFinished() { if (d->direction < 0) { PageSlot *p = d->pageSlots.takeFirst(); d->pageSlots.append(p); int index = d->pageSlots.count() - 1; p->setX(d->getStandardPosition(index)); setCurrentPage(d->current + 1); } else if (d->direction > 0) { PageSlot *p = d->pageSlots.takeLast(); d->pageSlots.prepend(p); p->setX(d->getStandardPosition(0)); setCurrentPage(d->current - 1); } else d->adjustVisibility(true); // restore flickable state setFlickable(d->keepFlickable); } void PageSlider::resizeEvent(QGraphicsSceneResizeEvent *event) { FlickableArea::resizeEvent(event); d->animationInOut->clear(); d->repositionRenderers(); d->refillRenderers(); } void PageSlider::mouseSlideStarted(const QPointF &p) { d->oldPos = p; d->firstPos = p; d->adjustVisibility(false); } void PageSlider::mouseSlideMoved(const QPointF &p) { qreal dx = (p.x() - d->oldPos.x()); qreal tdx = (p.x() - d->firstPos.x()); d->oldPos = p; foreach (PageSlot *slot, d->pageSlots) slot->moveBy(dx, 0); if (qAbs(tdx) > d->adjustmentThreshold) { if (tdx < 0) moveLeft(); else moveRight(); } } void PageSlider::mouseSlideFinished(const QPointF &) { d->rotate(0); } bool PageSlider::moveLeft() { if (d->isAnimating() || d->current + 1 >= d->views.count()) return false; d->rotate(-1); return true; } bool PageSlider::moveRight() { if (d->isAnimating() || d->current - 1 < 0) return false; d->rotate(1); return true; } #include "pageslider.moc"