Qt Reference Documentation

padnavigator.cpp Example File

graphicsview/padnavigator/padnavigator.cpp
 /****************************************************************************
 **
 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the examples of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** You may use this file under the terms of the BSD license as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 **     of its contributors may be used to endorse or promote products derived
 **     from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 **
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include "flippablepad.h"
 #include "padnavigator.h"
 #include "splashitem.h"

 #include <QtGui/QtGui>
 #ifndef QT_NO_OPENGL
 #include <QtOpenGL/QtOpenGL>
 #endif

 PadNavigator::PadNavigator(const QSize &size, QWidget *parent)
     : QGraphicsView(parent)
 {
     // Splash item
     SplashItem *splash = new SplashItem;
     splash->setZValue(1);

     // Pad item
     FlippablePad *pad = new FlippablePad(size);
     QGraphicsRotation *flipRotation = new QGraphicsRotation(pad);
     QGraphicsRotation *xRotation = new QGraphicsRotation(pad);
     QGraphicsRotation *yRotation = new QGraphicsRotation(pad);
     flipRotation->setAxis(Qt::YAxis);
     xRotation->setAxis(Qt::YAxis);
     yRotation->setAxis(Qt::XAxis);
     pad->setTransformations(QList<QGraphicsTransform *>()
                             << flipRotation
                             << xRotation << yRotation);

     // Back (proxy widget) item
     QGraphicsProxyWidget *backItem = new QGraphicsProxyWidget(pad);
     QWidget *widget = new QWidget;
     form.setupUi(widget);
     form.hostName->setFocus();
     backItem->setWidget(widget);
     backItem->setVisible(false);
     backItem->setFocus();
     backItem->setCacheMode(QGraphicsItem::ItemCoordinateCache);
     const QRectF r = backItem->rect();
     backItem->setTransform(QTransform()
                            .rotate(180, Qt::YAxis)
                            .translate(-r.width()/2, -r.height()/2));

     // Selection item
     RoundRectItem *selectionItem = new RoundRectItem(QRectF(-60, -60, 120, 120),
                                                      Qt::gray, pad);
     selectionItem->setZValue(0.5);

     // Splash animations
     QPropertyAnimation *smoothSplashMove = new QPropertyAnimation(splash, "y");
     QPropertyAnimation *smoothSplashOpacity = new QPropertyAnimation(splash, "opacity");
     smoothSplashMove->setEasingCurve(QEasingCurve::InQuad);
     smoothSplashMove->setDuration(250);
     smoothSplashOpacity->setDuration(250);

     // Selection animation
     QPropertyAnimation *smoothXSelection = new QPropertyAnimation(selectionItem, "x");
     QPropertyAnimation *smoothYSelection = new QPropertyAnimation(selectionItem, "y");
     QPropertyAnimation *smoothXRotation = new QPropertyAnimation(xRotation, "angle");
     QPropertyAnimation *smoothYRotation = new QPropertyAnimation(yRotation, "angle");
     smoothXSelection->setDuration(125);
     smoothYSelection->setDuration(125);
     smoothXRotation->setDuration(125);
     smoothYRotation->setDuration(125);
     smoothXSelection->setEasingCurve(QEasingCurve::InOutQuad);
     smoothYSelection->setEasingCurve(QEasingCurve::InOutQuad);
     smoothXRotation->setEasingCurve(QEasingCurve::InOutQuad);
     smoothYRotation->setEasingCurve(QEasingCurve::InOutQuad);

     // Flip animation setup
     QPropertyAnimation *smoothFlipRotation = new QPropertyAnimation(flipRotation, "angle");
     QPropertyAnimation *smoothFlipScale = new QPropertyAnimation(pad, "scale");
     QPropertyAnimation *smoothFlipXRotation = new QPropertyAnimation(xRotation, "angle");
     QPropertyAnimation *smoothFlipYRotation = new QPropertyAnimation(yRotation, "angle");
     QParallelAnimationGroup *flipAnimation = new QParallelAnimationGroup(this);
     smoothFlipScale->setDuration(500);
     smoothFlipRotation->setDuration(500);
     smoothFlipXRotation->setDuration(500);
     smoothFlipYRotation->setDuration(500);
     smoothFlipScale->setEasingCurve(QEasingCurve::InOutQuad);
     smoothFlipRotation->setEasingCurve(QEasingCurve::InOutQuad);
     smoothFlipXRotation->setEasingCurve(QEasingCurve::InOutQuad);
     smoothFlipYRotation->setEasingCurve(QEasingCurve::InOutQuad);
     smoothFlipScale->setKeyValueAt(0, qvariant_cast<qreal>(1.0));
     smoothFlipScale->setKeyValueAt(0.5, qvariant_cast<qreal>(0.7));
     smoothFlipScale->setKeyValueAt(1, qvariant_cast<qreal>(1.0));
     flipAnimation->addAnimation(smoothFlipRotation);
     flipAnimation->addAnimation(smoothFlipScale);
     flipAnimation->addAnimation(smoothFlipXRotation);
     flipAnimation->addAnimation(smoothFlipYRotation);

     // Flip animation delayed property assignment
     QSequentialAnimationGroup *setVariablesSequence = new QSequentialAnimationGroup;
     QPropertyAnimation *setFillAnimation = new QPropertyAnimation(pad, "fill");
     QPropertyAnimation *setBackItemVisibleAnimation = new QPropertyAnimation(backItem, "visible");
     QPropertyAnimation *setSelectionItemVisibleAnimation = new QPropertyAnimation(selectionItem, "visible");
     setFillAnimation->setDuration(0);
     setBackItemVisibleAnimation->setDuration(0);
     setSelectionItemVisibleAnimation->setDuration(0);
     setVariablesSequence->addPause(250);
     setVariablesSequence->addAnimation(setBackItemVisibleAnimation);
     setVariablesSequence->addAnimation(setSelectionItemVisibleAnimation);
     setVariablesSequence->addAnimation(setFillAnimation);
     flipAnimation->addAnimation(setVariablesSequence);

     // Build the state machine
     QStateMachine *stateMachine = new QStateMachine(this);
     QState *splashState = new QState(stateMachine);
     QState *frontState = new QState(stateMachine);
     QHistoryState *historyState = new QHistoryState(frontState);
     QState *backState = new QState(stateMachine);
     frontState->assignProperty(pad, "fill", false);
     frontState->assignProperty(splash, "opacity", 0.0);
     frontState->assignProperty(backItem, "visible", false);
     frontState->assignProperty(flipRotation, "angle", qvariant_cast<qreal>(0.0));
     frontState->assignProperty(selectionItem, "visible", true);
     backState->assignProperty(pad, "fill", true);
     backState->assignProperty(backItem, "visible", true);
     backState->assignProperty(xRotation, "angle", qvariant_cast<qreal>(0.0));
     backState->assignProperty(yRotation, "angle", qvariant_cast<qreal>(0.0));
     backState->assignProperty(flipRotation, "angle", qvariant_cast<qreal>(180.0));
     backState->assignProperty(selectionItem, "visible", false);
     stateMachine->addDefaultAnimation(smoothXRotation);
     stateMachine->addDefaultAnimation(smoothYRotation);
     stateMachine->addDefaultAnimation(smoothXSelection);
     stateMachine->addDefaultAnimation(smoothYSelection);
     stateMachine->setInitialState(splashState);

     // Transitions
     QEventTransition *anyKeyTransition = new QEventTransition(this, QEvent::KeyPress, splashState);
     anyKeyTransition->setTargetState(frontState);
     anyKeyTransition->addAnimation(smoothSplashMove);
     anyKeyTransition->addAnimation(smoothSplashOpacity);

     QKeyEventTransition *enterTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                    Qt::Key_Enter, backState);
     QKeyEventTransition *returnTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                     Qt::Key_Return, backState);
     QKeyEventTransition *backEnterTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                        Qt::Key_Enter, frontState);
     QKeyEventTransition *backReturnTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                         Qt::Key_Return, frontState);
     enterTransition->setTargetState(historyState);
     returnTransition->setTargetState(historyState);
     backEnterTransition->setTargetState(backState);
     backReturnTransition->setTargetState(backState);
     enterTransition->addAnimation(flipAnimation);
     returnTransition->addAnimation(flipAnimation);
     backEnterTransition->addAnimation(flipAnimation);
     backReturnTransition->addAnimation(flipAnimation);

     // Create substates for each icon; store in temporary grid.
     int columns = size.width();
     int rows = size.height();
     QVector< QVector< QState * > > stateGrid;
     stateGrid.resize(rows);
     for (int y = 0; y < rows; ++y) {
         stateGrid[y].resize(columns);
         for (int x = 0; x < columns; ++x)
             stateGrid[y][x] = new QState(frontState);
     }
     frontState->setInitialState(stateGrid[0][0]);
     selectionItem->setPos(pad->iconAt(0, 0)->pos());

     // Enable key navigation using state transitions
     for (int y = 0; y < rows; ++y) {
         for (int x = 0; x < columns; ++x) {
             QState *state = stateGrid[y][x];
             QKeyEventTransition *rightTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                            Qt::Key_Right, state);
             QKeyEventTransition *leftTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                           Qt::Key_Left, state);
             QKeyEventTransition *downTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                           Qt::Key_Down, state);
             QKeyEventTransition *upTransition = new QKeyEventTransition(this, QEvent::KeyPress,
                                                                         Qt::Key_Up, state);
             rightTransition->setTargetState(stateGrid[y][(x + 1) % columns]);
             leftTransition->setTargetState(stateGrid[y][((x - 1) + columns) % columns]);
             downTransition->setTargetState(stateGrid[(y + 1) % rows][x]);
             upTransition->setTargetState(stateGrid[((y - 1) + rows) % rows][x]);
             RoundRectItem *icon = pad->iconAt(x, y);
             state->assignProperty(xRotation, "angle", -icon->x() / 6.0);
             state->assignProperty(yRotation, "angle", icon->y() / 6.0);
             state->assignProperty(selectionItem, "x", icon->x());
             state->assignProperty(selectionItem, "y", icon->y());
             frontState->assignProperty(icon, "visible", true);
             backState->assignProperty(icon, "visible", false);

             QPropertyAnimation *setIconVisibleAnimation = new QPropertyAnimation(icon, "visible");
             setIconVisibleAnimation->setDuration(0);
             setVariablesSequence->addAnimation(setIconVisibleAnimation);
         }
     }

     // Scene
     QGraphicsScene *scene = new QGraphicsScene(this);
     scene->setBackgroundBrush(QPixmap(":/images/blue_angle_swirl.jpg"));
     scene->setItemIndexMethod(QGraphicsScene::NoIndex);
     scene->addItem(pad);
     scene->setSceneRect(scene->itemsBoundingRect());
     setScene(scene);

     // Adjust splash item to scene contents
     const QRectF sbr = splash->boundingRect();
     splash->setPos(-sbr.width() / 2, scene->sceneRect().top() - 2);
     frontState->assignProperty(splash, "y", splash->y() - 100.0);
     scene->addItem(splash);

     // View
     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setMinimumSize(50, 50);
     setViewportUpdateMode(FullViewportUpdate);
     setCacheMode(CacheBackground);
     setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing);
 #ifndef QT_NO_OPENGL
     setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
 #endif

     stateMachine->start();
 }

 void PadNavigator::resizeEvent(QResizeEvent *event)
 {
     QGraphicsView::resizeEvent(event);
     fitInView(scene()->sceneRect(), Qt::KeepAspectRatio);
 }