// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example widgets/sliders
    \title Sliders Example
    \examplecategory {User Interface Components}
    \ingroup examples-widgets
    \brief The Sliders example shows how to use the different types of sliders
    available in Qt: QSlider, QScrollBar and QDial.

    Qt provides three types of slider-like widgets: QSlider,
    QScrollBar and QDial. They all inherit most of their
    functionality from QAbstractSlider, and can in theory replace
    each other in an application since the differences only concern
    their look and feel. This example shows what they look like, how
    they work and how their behavior and appearance can be
    manipulated through their properties.

    The example also demonstrates how signals and slots can be used to
    synchronize the behavior of two or more widgets, and how to override
    \l{QWidget::}{resizeEvent()} to implement a responsive layout.

    \borderedimage sliders-example.png
    \caption Screenshot of the Sliders example

    The Sliders example consists of two classes:

    \list

    \li \c SlidersGroup is a custom widget. It combines a QSlider, a
    QScrollBar and a QDial.

    \li \c Window is the main widget combining a QGroupBox and a
    SlidersGroup. The QGroupBox contains several widgets that control
    the behavior of the slider-like widgets.

    \endlist

    First we will review the \c Window class, then we
    will take a look at the \c SlidersGroup class.

    \section1 Window Class Definition

    \snippet widgets/sliders/window.h 0

    The \c Window class inherits from QWidget. It displays the slider
    widgets and allows the user to set their minimum, maximum and
    current values and to customize their appearance, key bindings
    and orientation. We use a private \c createControls() function to
    create the widgets that provide these controlling mechanisms and
    to connect them to the slider widgets.

    \section1 Window Class Implementation

    \snippet widgets/sliders/window.cpp 0

    In the constructor we first create the \c SlidersGroup widget
    that displays the slider widgets. With \c createControls() we
    create the controlling widgets, and connect those to to the
    sliders.

    \snippet widgets/sliders/window.cpp 1

    We put the groups of control widgets and the sliders in a
    horizontal layout before we initialize the minimum, maximum and
    current values. The initialization of the current value will
    propagate to the slider widgets through the connection we made
    between \c valueSpinBox and the \c SlidersGroup widgets. The
    minimum and maximum values propagate through the connections we
    created with \c createControls().

    \snippet widgets/sliders/window.cpp 2
    \snippet widgets/sliders/window.cpp 3

    In the private \c createControls() function, we let a QGroupBox
    (\c controlsGroup) display the control widgets. A group box can
    provide a frame, a title and a keyboard shortcut, and displays
    various other widgets inside itself. The group of control widgets
    is composed by two checkboxes, and three spin boxes with labels.

    After creating the labels, we create the two checkboxes.
    Checkboxes are typically used to represent features in an
    application that can be enabled or disabled. When \c
    invertedAppearance is enabled, the slider values are inverted.
    The table below shows the appearance for the different
    slider-like widgets:

    \table
    \header \li                \li{2,1} QSlider                   \li{2,1} QScrollBar                \li{2,1} QDial
    \header \li                \li Normal        \li Inverted      \li Normal        \li Inverted      \li Normal         \li Inverted
    \row    \li Qt::Horizontal \li Left to right \li Right to left \li Left to right \li Right to left \li Clockwise \li Counterclockwise
    \row    \li Qt::Vertical   \li Bottom to top \li Top to bottom \li Top to bottom \li Bottom to top \li Clockwise \li Counterclockwise
    \endtable

    It is common to invert the appearance of a vertical QSlider. A
    vertical slider that controls volume, for example, will typically
    go from bottom to top (the non-inverted appearance), whereas a
    vertical slider that controls the position of an object on screen
    might go from top to bottom, because screen coordinates go from
    top to bottom.

    When the \c invertedKeyBindings option is enabled (corresponding
    to the QAbstractSlider::invertedControls property), the slider's
    wheel and key events are inverted. The normal key bindings mean
    that scrolling the mouse wheel "up" or using keys like page up
    will increase the slider's current value towards its maximum.
    Inverted, the same wheel and key events will move the value
    toward the slider's minimum. This can be useful if the \e
    appearance of a slider is inverted: Some users might expect the
    keys to still work the same way on the value, whereas others
    might expect \uicontrol PageUp to mean "up" on the screen.

    Note that for horizontal and vertical scroll bars, the key
    bindings are inverted by default: \uicontrol PageDown increases the
    current value, and \uicontrol PageUp decreases it.

    \snippet widgets/sliders/window.cpp 4
    \snippet widgets/sliders/window.cpp 5

    Then we create the spin boxes. QSpinBox allows the user to choose
    a value by clicking the up and down buttons or pressing the \uicontrol
    Up and \uicontrol Down keys on the keyboard to modify the value
    currently displayed. The user can also type in the value
    manually. The spin boxes control the minimum, maximum and current
    values for the QSlider, QScrollBar, and QDial widgets.

    \snippet widgets/sliders/window.cpp 6

    Then we connect the \c slidersGroup and the \c valueSpinBox to each
    other, so that the slider widgets and the control widget will behave
    synchronized when the current value of one of them changes.
    The \c valueChanged() signal is emitted with the new value as
    argument. The \c setValue() slot sets the current value of the
    widget to the new value, and emits \c valueChanged() if the new
    value is different from the old one.

    We synchronize the behavior of the control widgets and the slider
    widgets through their signals and slots. We connect each control
    widget to both the horizontal and vertical group of slider
    widgets. We also connect \c orientationCombo to the
    QStackedWidget, so that the correct "page" is shown. Finally, we
    lay out the control widgets in a QGridLayout within the \c
    controlsGroup group box.

    \snippet widgets/sliders/window.cpp 7

    Lastly, we override resizeEvent() from QWidget. We guard against
    dividing by zero, and otherwise compute the aspect ratio of the
    widget. If the window has a portrait format, then we set the
    layout to organize the groups of control widgets and sliders
    vertically, and we give the sliders a horizontal orientation.
    If the window has a landscape format, then we change the layout
    to show the sliders and controlling widgets side by side, and
    give the sliders a vertical orientation.

    \section1 SlidersGroup Class Definition

    \snippet widgets/sliders/slidersgroup.h 0

    The \c SlidersGroup class inherits from QGroupBox. It provides a
    frame and a title, and contains a QSlider, a QScrollBar and a
    QDial.

    We provide a \c valueChanged() signal and a public \c setValue()
    slot with equivalent functionality to the ones in QAbstractSlider
    and QSpinBox. In addition, we implement several other public
    slots to set the minimum and maximum value, and invert the slider
    widgets' appearance as well as key bindings, and set the
    orientation.

    \section1 SlidersGroup Class Implementation

    \snippet widgets/sliders/slidersgroup.cpp 0

    First we create the slider-like widgets with the appropriate
    properties. In particular we set the focus policy for each
    widget. Qt::FocusPolicy is an enum type that defines the various
    policies a widget can have with respect to acquiring keyboard
    focus. The Qt::StrongFocus policy means that the widget accepts
    focus by both tabbing and clicking.

    \snippet widgets/sliders/slidersgroup.cpp 1

    Then we connect the widgets with each other, so that they will
    stay synchronized when the current value of one of them changes.

    We connect \c {dial}'s \c valueChanged() signal to the
    \c{SlidersGroup}'s \c valueChanged() signal, to notify the other
    widgets in the application (i.e., the control widgets) of the
    changed value.

    \codeline
    \snippet widgets/sliders/slidersgroup.cpp 4

    Finally, we create the layout for the slider widgets within the
    group box. We start with a horizontal arrangement of the sliders.

    \snippet widgets/sliders/slidersgroup.cpp 5
    \snippet widgets/sliders/slidersgroup.cpp 6

    The \c setValue() slot sets the value of the QSlider. We don't
    need to explicitly call
    \l{QAbstractSlider::setValue()}{setValue()} on the QScrollBar and
    QDial widgets, since QSlider will emit the
    \l{QAbstractSlider::valueChanged()}{valueChanged()} signal when
    its value changes, triggering a domino effect.

    \snippet widgets/sliders/slidersgroup.cpp 7
    \snippet widgets/sliders/slidersgroup.cpp 8
    \codeline
    \snippet widgets/sliders/slidersgroup.cpp 9
    \snippet widgets/sliders/slidersgroup.cpp 10

    The \c setMinimum() and \c setMaximum() slots are used by the \c
    Window class to set the range of the QSlider, QScrollBar, and
    QDial widgets.

    \snippet widgets/sliders/slidersgroup.cpp 11
    \snippet widgets/sliders/slidersgroup.cpp 12
    \codeline
    \snippet widgets/sliders/slidersgroup.cpp 13
    \snippet widgets/sliders/slidersgroup.cpp 14

    The \c invertAppearance() and \c invertKeyBindings() slots
    control the child widgets'
    \l{QAbstractSlider::invertedAppearance}{invertedAppearance} and
    \l{QAbstractSlider::invertedControls}{invertedControls}
    properties.

    \snippet widgets/sliders/slidersgroup.cpp 15

    The setOrientation() slot controls the direction of the layout
    and the orientation of the sliders. In a horizontal group, the
    sliders have a horizontal orientation, and are laid out on top
    of each other. In a vertical group, the sliders have a vertical
    orientation, and are laid out next to each other.
*/
