/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example graphicsview/padnavigator
    \title Pad Navigator Example
    \ingroup examples-qtstatemachine
    \brief Demonstrates how to create animated user interface.

    The Pad Navigator Example shows how you can use Graphics View together with
    embedded widgets and Qt's \l{Qt State Machine Overview}{state machine
    framework} to create a simple but useful, dynamic, animated user interface.

    \image padnavigator-example.png

    The interface consists of a flippable, rotating pad with icons that can be
    selected using the arrow keys on your keyboard or keypad. Pressing enter
    will flip the pad around and reveal its back side, which has a form
    embedded into a QGraphicsProxyWidget. You can interact with the form, and
    press the enter key to flip back to the front side of the pad at any time.

    Graphics View provides the QGraphicsScene class for managing and
    interacting with a large number of custom-made 2D graphical items derived
    from the QGraphicsItem class, and a QGraphicsView widget for visualizing
    the items, with support for zooming and rotation.

    This example consists of a \c RoundRectItem class, a \c FlippablePad class,
    a \c PadNavigator class, a \c SplashItem class, and a \c main() function.

    \section1 RoundRectItem Class Definition

    The \c RoundRectItem class is used by itself to display the icons on the
    pad, and as a base class for \c FlippablePad, the class for the pad itself.
    The role of the class is to paint a round rectangle of a specified size and
    gradient color, and optionally to paint a pixmap icon on top. To support \c
    FlippablePad it also allows filling its contents with a plain window
    background color.

    Let's start by reviewing the \c RoundRectItem class declaration.

    \snippet graphicsview/padnavigator/roundrectitem.h 0

    \c RoundRectItem inherits QGraphicsObject, which makes it easy to control
    its properties using QPropertyAnimation. Its constructor takes a rectangle
    to determine its bounds, and a color.

    Besides implementing the mandatory \l{QGraphicsItem::paint()}{paint()} and
    \l{QGraphicsItem::boundingRect()}{boundingRect()} pure virtual functions,
    it also provides the \c pixmap and \c fill properties.

    The \c pixmap property sets an optional pixmap that is drawn on top of the
    round rectangle. The \c fill property will, when true, fill the round
    rectangle contents with a fixed QPalette::Window background color.
    Otherwise the contents are filled using a gradient based on the color
    passed to \c RoundRectItem's constructor.

    \snippet graphicsview/padnavigator/roundrectitem.h 1

    The private data members are:

    \list
    \li \c pix: The optional pixmap that is drawn on top of the rectangle.
    \li \c fillRect: Corresponds to the \c fill property.
    \li \c color: The configurable gradient color fill of the rectangle.
    \li \c bounds: The bounds of the rectangle.
    \li \c gradient: A precalculated gradient used to fill the rectangle.
    \endlist

    We will now review the \c RoundRectItem implementation. Let's start by
    looking at its constructor:

    \snippet graphicsview/padnavigator/roundrectitem.cpp 0

    The constructor initializes its member variables and forwards the \c parent
    argument to QGraphicsObject's constructor. It then constructs the linear
    gradient that is used in \l{QGraphicsItem::paint()}{paint()} to draw the
    round rectangle's gradient background. The linear gradient's starting point
    is at the top-left corner of the bounds, and the end is at the bottom-left
    corner. The start color is identical to the color passed as an argument,
    and a slightly darker color is chosen for the final stop.

    We store this gradient as a member variable to avoid having to recreate the
    gradient every time the item is repainted.

    Finally we set the cache mode
    \l{QGraphicsItem::ItemCoordinateCache}{ItemCoordinateCache}. This mode
    causes the item's rendering to be cached into an off-screen pixmap that
    remains persistent as we move and transform the item. This mode is ideal
    for this example, and works particularly well with OpenGL and OpenGL ES.

    \snippet graphicsview/padnavigator/roundrectitem.cpp 1

    The \c pixmap property implementation simple returns the member pixmap, or
    sets it and then calls \l{QGraphicsItem::update()}{update()}.

    \snippet graphicsview/padnavigator/roundrectitem.cpp 2

    As the \l{QGraphicsItem::paint()}{paint()} implementation below draws a
    simple drop shadow down and to the right of the item, we return a slightly
    adjusted rectangle from \l{QGraphicsItem::boundingRect()}{boundingRect()}.

    \snippet graphicsview/padnavigator/roundrectitem.cpp 3

    The \l{QGraphicsItem::paint()}{paint()} implementation starts by rendering
    a semi transparent black round rectangle drop shadow, two units down and to
    the right of the main item.

    \snippet graphicsview/padnavigator/roundrectitem.cpp 4

    We then draw the "foreground" round rectangle itself. The fill depends on
    the \c fill property; if true, we will with a plain QPalette::Window color.
    We get the current brush from QApplication::palette(). We assign a single
    unit wide pen for the stroke, assign the brush, and then draw the
    rectangle.

    \snippet graphicsview/padnavigator/roundrectitem.cpp 5

    If a pixmap has been assigned to the \e pixmap property, we draw this
    pixmap in the center of the rectangle item. The pixmaps are scaled to match
    the size of the icons; in arguably a better approach would have been to
    store the icons with the right size in the first places.

    \snippet graphicsview/padnavigator/roundrectitem.cpp 6

    Finally, for completeness we include the \c fill property implementation.
    It returns the \c fill member variable's value, and when assigned to, it
    calls \l{QGraphicsItem::update()}{update()}.

    As mentioned already, \c RoundRectItem is the base class for \c
    FlippablePad, which is the class representing the tilting pad itself. We
    will proceed to reviewing \c FlippablePad.

    \section1 FlippablePad Class Definition

    \c FlippablePad is, in addition to its inherited \c RoundRectItem
    responsibilities, responsible for creating and managing a grid of icons.

    \snippet graphicsview/padnavigator/flippablepad.h 0

    Its declaration is very simple: It inherits \c RoundRectItem and does not
    need any special polymorphic behavior. It's suitable to declare its own
    constructor, and a getter-function that allows \c PadNavigator to access
    the icons in the grid by (row, column).

    The example has no "real" behavior or logic of any kind, and because of
    that, the icons do not need to provide any \e behavior or special
    interactions management. In a real application, however, it would be
    natural for the \c FlippablePad and its icons to handle more of the
    navigation logic. In this example, we have chosen to leave this to
    the \c PadNavigator class, which we will get back to below.

    We will now review the \c FlippablePad implementation. This implementation
    starts with two helper functions: \c boundsFromSize() and \c
    posForLocation():

    \snippet graphicsview/padnavigator/flippablepad.cpp 0

    \c boundsForSize() takes a QSize argument, and returns the bounding
    rectangle of the flippable pad item. The QSize determines how many rows and
    columns the icon grid should have. Each icon is given 150x150 units of
    space, and this determines the bounds.

    \snippet graphicsview/padnavigator/flippablepad.cpp 1

    \c posForLocation() returns the position of an icon given its row and
    column position. Like \c boundsForSize(), the function assumes each icon is
    given 150x150 units of space, and that all icons are centered around the
    flippable pad item's origin (0, 0).

    \snippet graphicsview/padnavigator/flippablepad.cpp 2

    The \c FlippablePad constructor passes suitable bounds (using \c
    boundsForSize()) and specific color to \c RoundRectItem's constructor.

    \snippet graphicsview/padnavigator/flippablepad.cpp 3

    It then loads pixmaps from compiled-in resources to use for its icons.
    QDirIterator is very useful in this context, as it allows us to fetch all
    resource "*.png" files inside the \c :/images directory without explicitly
    naming the files.

    We also make sure not to load more pixmaps than we need.

    \snippet graphicsview/padnavigator/flippablepad.cpp 4

    Now that we have the pixmaps, we can create icons, position then and assign
    pixmaps. We start by finding a suitable size and color for the icons, and
    initializing a convenient grid structure for storing the icons. This \c
    iconGrid is also used later to find the icon for a specific (column, row)
    location.

    For each row and column in our grid, we proceed to constructing each icon
    as an instance of \c RoundRectItem. The item is placed by using the \c
    posForLocation() helper function. To make room for the slip-behind
    selection item, we give each icon a \l{QGraphicsItem::zValue()}{Z-value} of
    1. The pixmaps are distributed to the icons in round-robin fashion.

    Again, this approach is only suitable for example purposes. In a real-life
    application where each icon represents a specific action, it would be more
    natural to assign the pixmaps directly, or that the icons themselves
    provide suitable pixmaps.

    \snippet graphicsview/padnavigator/flippablepad.cpp 5

    Finally, the \c iconAt() function returns a pointer to the icon at a
    specific row and column. It makes a somewhat bold assumption that the input
    is valid, which is fair because the \c PadNavigator class only calls this
    function with correct input.

    We will now review the \c SplashItem class.

    \section1 SplashItem Class Definition

    The \c SplashItem class represents the "splash window", a semitransparent
    white overlay with text that appears immediately after the application has
    started, and disappears after pressing any key. The animation is controlled
    by \c PadNavigator; this class is very simple by itself.

    \snippet graphicsview/padnavigator/splashitem.h 0

    The class declaration shows that \c SplashItem inherits QGraphicsObject to
    allow it to be controlled by QPropertyAnimation. It reimplements the
    mandatory \l{QGraphicsItem::paint()}{paint()} and
    \l{QGraphicsItem::boundingRect()}{boundingRect()} pure virtual functions,
    and keeps a \c text member variable which will contain the information text
    displayed on this splash item.

    Let's look at its implementation.

    \snippet graphicsview/padnavigator/splashitem.cpp 0

    The constructor forwards to QGraphicsObject as expected, assigns a text
    message to the \c text member variable, and enables
    \l{QGraphicsItem::DeviceCoordinateCache}{DeviceCoordinateCache}. This cache
    mode is suitable because the splash item only moves and is never
    transformed, and because it contains text, it's important that it has a
    pixel perfect visual appearance (in contrast to
    \l{QGraphicsItem::ItemCoordinateCache}{ItemCoordinateCache}, where the
    visual appearance is not as good).

    We use caching to avoid having to relayout and rerender the text for each
    frame. An alterative approach would be to use the new QStaticText class.

    \snippet graphicsview/padnavigator/splashitem.cpp 1

    \c SplashItem's bounding rectangle is fixed at (400x175).

    \snippet graphicsview/padnavigator/splashitem.cpp 2

    The \l{QGraphicsItem::paint()}{paint()} implementation draws a clipped
    round rectangle with a thick 2-unit border and a semi-transparent white
    background. It proceeds to finding a suitable text area by adjusting the
    splash item's bounding rectangle with 10 units in each side. The text is
    rendered inside this rectangle, with top-left alignment, and with word
    wrapping enabled.

    The main class now remains. We will proceed to reviewing \c PadNavigator.

    \section1 PadNavigator Class Definition

    \c PadNavigator represents the main window of our Pad Navigator Example
    application. It creates and controls a somewhat complex state machine, and
    several animations. Its class declaration is very simple:

    \snippet graphicsview/padnavigator/padnavigator.h 0

    It inherits QGraphicsView and reimplements only one function:
    \l{QGraphicsView::resizeEvent()}{resizeEvent()}, to ensure the scene is
    scaled to fit inside the view when resizing the main window.

    The \c PadNavigator constructor takes a QSize argument that determines the
    number or rows and columns in the grid.

    It also keeps a private member instance, \c form, which is the generated
    code for the pad's back side item's QGraphicsProxyWidget-embedded form.

    \snippet graphicsview/padnavigator/padnavigator.cpp 0

    \c PadNavigator's constructor is a bit long. In short, its job is to create
    all items, including the \c FlippablePad, the \c SplashItem and the
    QGraphicsProxyWidget \c backItem, and then to set up all animations, states
    and transitions that control the behavior of the application.

    It starts out simple, by forwarding to QGraphicsView's constructor.

    \snippet graphicsview/padnavigator/padnavigator.cpp 1

    The first item to be created is \c SplashItem. This is going to be a top-level
    item in the scene, next to \c FlippablePad, and stacked on top of it, so we
    assign it a \l{QGraphicsItem::zValue()}{Z-value} of 1.

    \snippet graphicsview/padnavigator/padnavigator.cpp 2

    Now we construct the \c FlippablePad item, passing its column-row count to
    its constructor.

    The pad is controlled by three transformations, and we create one
    QGraphicsRotation object for each of these.

    \list
    \li \c flipRotation: Rotates the grid around its Qt::YAxis. This rotation is
    animated from 0 to 180, and eventually back, when enter is pressed on the
    keyboard, flipping the pad around.
    \li \c xRotation: Rotates the grid around its Qt::XAxis. This is used to
    tilt the pad vertically corresponding to which item is currently selected.
    This way, the selected item is always kept in front.
    \li \c yRotation: Rotates the grid around its Qt::YAxis. This is used to
    tilt the pad horizontally corresponding to which item is selected. This
    way, the selected item is always kept in front.
    \endlist

    The combination of all three rotations is assigned via
    QGraphicsItem::setTransformations().

    \snippet graphicsview/padnavigator/padnavigator.cpp 3

    Now we construct the QGraphicsProxyWidget-embedded \c backItem. The proxy
    widget is created as a child of the pad. We create a new QWidget and
    populate it with the \c form member. To ensure the \c hostName line edit is
    the first to receive input focus when this item is shown, we call
    \l{QWidget::setFocus()}{setFocus()} immediately. This will not give the
    widget focus right away; it will only prepare the item to automatically
    receive focus once it is shown.

    The QWidget based form is embedded into the proxy widget. The proxy is
    hidden initially; we only want to show it when the pad is rotated at least
    90 degrees, and we also rotate the proxy itself by 180 degrees. This way we
    give the impression that the proxy widget is "behind" the flipped pad, when
    in fact, it's actually \e{on top of it}.

    We enable \l{QGraphicsItem::ItemCoordinateCache}{ItemCoordinateCache} to
    ensure the flip animation can run smoothly.

    \snippet graphicsview/padnavigator/padnavigator.cpp 4

    We now create the selection item. This is simply another instance of \c
    RoundRectItem that is slightly larger than the icons on the pad. We create
    it as an immediate child of the \c FlippablePad, so the selection item is a
    sibling to all the icons. By giving it a
    \l{QGraphicsItem::zValue()}{Z-value} of 0.5 we ensure it will slide between
    the pad and its icons.

    What follows now is a series of animation initializations.

    \snippet graphicsview/padnavigator/padnavigator.cpp 5

    We begin with the animations that apply to the splash item. The first
    animation, \c smoothSplashMove, ensures that the "y" property of \c splash
    will be animated with a 250-millisecond duration
    \l{QEasingCurve::InQuad}{InQuad} easing function. \c smoothSplashOpacity
    ensures the opacity of \c splash eases in and out in 250 milliseconds.

    The values are assigned by \c PadNavigator's state machine, which is
    created later.

    \snippet graphicsview/padnavigator/padnavigator.cpp 6

    These are the animations that control the selection item's movement and the
    \c xRotation and \c yRotation QGraphicsRotation objects that tilt the pad.
    All animations have a duration of 125 milliseconds, and they all use the
    \l{QEasingCurve::InOutQuad}{InOutQuad} easing function.

    \snippet graphicsview/padnavigator/padnavigator.cpp 7

    We now create the animations that control the flip-effect when you press
    the enter key. The main goal is to rotate the pad by 180 degrees or back.

    \list
    \li \c smoothFlipRotation: Animates the main 180 degree rotation of the pad.
    \li \c smoothFlipScale: Scales the pad out and then in again while the pad is rotating.
    \li \c flipAnimation: A parallel animation group that ensures the above animations are run in parallel.
    \endlist

    All animations are given a 500 millisecond duration and an
    \l{QEasingCurve::InOutQuad}{InOutQuad} easing function.

    It's worth taking a close look at \c smoothFlipScale. This animation's
    start and end values are both 1.0, but at animation step 0.5 the
    animation's value is 0.7. This means that after 50% of the animation's
    duration, or 250 milliseconds, the pad will be scaled down to 0.7x of its
    original size, which gives a great visual effect while flipping.

    \snippet graphicsview/padnavigator/padnavigator.cpp 8

    This section uses a trick to ensure that certain properties are assigned
    precisely when the flip animation passes 50%, or 90 degrees, rotation. In
    short, the pad's icons and selection item are all hidden, the pad's \c fill
    property is enabled, and \c backItem is shown when flipping over. When
    flipping back, the reverse properties are applied.

    The way this is achieved is by running a sequential animation in parallel
    to the other animations. This sequence, dubbed \c setVariablesSequence,
    starts with a 250 millisecond pause, and then executes several animations
    with a duration of 0. Each animation will ensure that properties are set
    immediate at this point.

    This approach can also be used to call functions or set any other
    properties at a specific time while an animation is running.

    \snippet graphicsview/padnavigator/padnavigator.cpp 9

    We will now create the state machine. The whole \c PadNavigator state
    machinery is controlled by one single state machine that has a
    straight-forward state structure. The state engine itself is created
    as a child of the \c PadNavigator itself. We then create three top level
    states:

    \list
    \li \c splashState: The initial state where the splash item is visible.
    \li \c frontState: The base state where the splash is gone and we can see
    the front side of the pad, and navigate the selection item.
    \li \c backState: The flipped state where the \c backItem is visible, and we
    can interact with the QGraphicsProxyWidget-embedded form.
    \endlist

    \snippet graphicsview/padnavigator/padnavigator.cpp 10

    Each state assigns specific properties to objects on entry. Most
    interesting perhaps is the assignment of the value 0.0 to the pad's \c
    flipRotation angle property when in \c frontState, and 180.0 when in \c
    backState.

    At the end of this section we register default animations with the state
    engine; these animations will apply to their respective objects and
    properties for any state transition. Otherwise it's common to assign
    animations to specific transitions.

    Specifically, we use default animations to control the selection item's
    movement and tilt rotations. The tilt rotations are set to 0 when the pad
    is flipped, and restored back to their original values when flipped back.

    The \c splashState state is set as the initial state. This is required
    before we start the state engine. We proceed with creating some
    transitions.

    \snippet graphicsview/padnavigator/padnavigator.cpp 11

    QEventTransition defines a very flexible transition type. You can use this
    class to trigger a transition based on an object receiving an event of a
    specific type. In this case, we would like to transition from \c
    splashState into \c frontState if \c PadNavigator receives any key press
    event (QEvent::KeyPress).

    We register the \c splashItem's animations to this transition to ensure they
    are used to animate the item's movement and opacity.

    \snippet graphicsview/padnavigator/padnavigator.cpp 12

    We use QKeyEventTransition to capture specific key events. In this case, we
    detect that the user presses Qt::Key_Return or Qt::Key_Enter, and use this
    to trigger transitions between \c frontState and backState. We register \c
    flipAnimation, our complex parallel animation group, with these
    transitions.

    We continue by defining the states for each of the icons in the grid.

    \snippet graphicsview/padnavigator/padnavigator.cpp 13

    We will use state groups to control transitions between icons. Each icon
    represents a \e substate of \c frontState. We will then define transitions
    between the states by detecting key presses, using QKeyEventTransition.

    We start by creating all the substates, and at the same time we create a
    temporary grid structure for the states to make it easier to find which
    states represents icons that are up, down, left and to the right each
    other.

    Once the first substate is known, we set this up as the initial substate of
    \c frontState. We will use the (0, 0), or top-left, icon for the initial
    substate. We initialize the selection item's position to be exactly where
    the top-left icon is.

    \snippet graphicsview/padnavigator/padnavigator.cpp 14

    We can now create four transitions for each icon. Each transition ensures
    that we move to the state corresponding to which arrow key has been
    pressed. It's clear from this technique that we could design any other
    specific transitions to and from each of the sub states depending on these
    and other keys.

    \snippet graphicsview/padnavigator/padnavigator.cpp 15

    Also, for each of the icons, we assign suitable values to the \c xRotation
    and \c yRotation objects' "angle"-properties. If you recall, these
    properties "tilt" the pad corresponding to which item is currently
    selected. We ensure each icon is invisible when the pad is flipped, and
    visible when the pad is not flipped. To ensure the visible property is
    assigned at the right time, we add property-controlling animations to the
    \c setVariableSequence animation defined earlier.

    \snippet graphicsview/padnavigator/padnavigator.cpp 16

    We are now finished with all states, transitions, and animations. We now
    create the scene that will contain all our items. The scene gets a defined
    background pixmap, and we disable item indexing (as most items in this
    scene are animated). We add our \c pad item to the scene, and use its
    bounding rectangle to fixate the scene rectangle. This rectangle is used by
    the view to find a suitable size for the application window.

    Then the scene is assigned to the view, or in our case, \c PadNavigator
    itself.

    \snippet graphicsview/padnavigator/padnavigator.cpp 17

    Now that the scene has received its final size, we can position the splash
    item at the very top, find its fade-out position, and add it to the scene.

    \snippet graphicsview/padnavigator/padnavigator.cpp 18

    The view toggles a few necessary properties:

    \list
    \li It disables its scroll bars - this application has no use for scroll bars.
    \li It assigns a minimum size. This is necessary to avoid numerical errors
    in our fit-in-view \c resizeEvent() implementation.
    \li It sets \l{QGraphicsView::FullViewportUpdate}{FullViewportUpdate}, to
    ensure QGraphicsView doesn't spend time figuring out precisely what needs
    to be redrawn. This application is very simple - if anything changes,
    everything is updated.
    \li It enables background caching - this makes no performance difference
    with OpenGL, but without OpenGL it avoids unnecessary re-scaling of the
    background pixmap.
    \li It sets render hints that increase rendering quality.
    \li If OpenGL is supported, a QOpenGLWidget viewport is assigned to the view.
    \endlist

    Finally, we start the state engine.

    \snippet graphicsview/padnavigator/padnavigator.cpp 19

    The \l{QGraphicsView::resizeEvent()}{resizeEvent()} implementation calls
    the base implementation, and then calls QGraphicsView::fitInView() to scale
    the scene so that it fits perfectly inside the view.

    By resizing the main application window, you can see this effect yourself.
    The scene contents grow when you make the window larger, and shrink when
    you make it smaller, while keeping the aspect ratio intact.

    \section1 The main() Function

    \snippet graphicsview/padnavigator/main.cpp 0

    The \c main function creates the QApplication instance, uses
    Q_INIT_RESOURCE to ensure our compiled-in resources aren't removed by the
    linker, and then creates a 3x3 \c PadNavigator instance and shows it.

    Our flippable pad shows up with a suitable splash item once control returns
    to the event loop.

    \section1 Performance Notes

    The example uses OpenGL if this is available, to achieve optimal
    performance; otherwise perspective transformations can be quite costly.

    Although this example does use QGraphicsProxyWidget to demonstrate
    integration of Qt widget components integrated into Graphics View, using
    QGraphicsProxyWidget comes with a performance penalty, and is therefore not
    recommended for embedded development.

    This example uses extensive item caching to avoid rerendering of static
    elements, at the expense of graphics memory.
*/
