Qt 5.6 | Qt Widgets | Pad Navigator Example | padnavigator.cpp Example File

Qt 5.6.2 Reference Documentation

# padnavigator.cpp Example File

graphicsview/padnavigator/padnavigator.cpp

      /****************************************************************************
      **
      ** Copyright (C) 2015 The Qt Company Ltd.
      ** Contact: http://www.qt.io/licensing/
      **
      ** 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 The Qt Company Ltd 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"

      #ifndef QT_NO_OPENGL
      #include <QtOpenGL>
      #else
      #include <QtWidgets>
      #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 QOpenGLWidget);
      #endif

          stateMachine->start();
      }

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