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

/*!
    \example widgets/tooltips
    \title Tool Tips Example
    \ingroup examples-widgets
    \brief The Tool Tips example shows how to provide static and dynamic tool
    tips for an application's widgets.

    The simplest and most common way to set a widget's tool tip is by
    calling its QWidget::setToolTip() function (static tool
    tips). Then the tool tip is shown whenever the cursor points at
    the widget. We show how to do this with our application's tool
    buttons. But it is also possible to show different tool tips
    depending on the cursor's position (dynamic tooltips). This
    approach uses mouse tracking and event handling to determine what
    widgets are located under the cursor at any point in time, and
    displays their tool tips. The tool tips for the shape items in our
    application are implemented using the latter approach.

    \image tooltips-example.png

    With the \c Tooltips application the user can create new shape
    items with the provided tool buttons, and move the items around
    using the mouse. Tooltips are provided whenever the cursor is
    pointing to a shape item or one of the buttons.

    The Tooltips example consists of two classes:

    \list
    \li \c ShapeItem is a custom widget representing one single shape item.
    \li \c SortingBox inherits from QWidget and is the application's main
        widget.
    \endlist

    First we will review the \c SortingBox class, then we will take a
    look at the \c ShapeItem class.

    \section1 SortingBox Class Definition

    \snippet widgets/tooltips/sortingbox.h 0

    The \c SortingBox class inherits QWidget, and it is the Tooltips
    application's main widget. We reimplement several of the event
    handlers.

    The \c event() function provides tooltips, the \c resize()
    function makes sure the application appears consistently when the
    user resizes the main widget, and the \c paintEvent() function
    displays the shape items within the \c SortingBox widget. The
    mouse event handlers are reimplemented to make the user able to
    move the items around.

    In addition we need three private slots to make the user able to
    create new shape items.

    \snippet widgets/tooltips/sortingbox.h 1

    We also create several private functions: We use the \c
    initialItemPosition(), \c initialItemColor() and \c
    createToolButton() functions when we are constructing the widget,
    and we use the \c updateButtonGeometry() function whenever the
    user is resizing the application's main widget.

    The \c itemAt() function determines if there is a shape item at a
    particular position, and the \c moveItemTo() function moves an
    item to a new position. We use the \c createShapeItem(), \c
    randomItemPosition() and \c randomItemColor() functions to create
    new shape items.

    \snippet widgets/tooltips/sortingbox.h 2

    We keep all the shape items in a QList, and we keep three
    QPainterPath objects holding the shapes of a circle, a square and
    a triangle. We also need to have a pointer to an item when it is
    moving, and we need to know its previous position.

    \section1 SortingBox Class Implementation

    \snippet widgets/tooltips/sortingbox.cpp 0

    In the constructor, we first set the Qt::WA_StaticContents
    attribute on the widget. This attribute indicates that the widget
    contents are north-west aligned and static. On resize, such a
    widget will receive paint events only for the newly visible part
    of itself.

    \snippet widgets/tooltips/sortingbox.cpp 1

    To be able to show the appropriate tooltips while the user is
    moving the cursor around, we need to enable mouse tracking for the
    widget.

    If mouse tracking is disabled (the default), the widget only
    receives mouse move events when at least one mouse button is
    pressed while the mouse is being moved. If mouse tracking is
    enabled, the widget receives mouse move events even if no buttons
    are pressed.

    \snippet widgets/tooltips/sortingbox.cpp 2

    A widget's background role defines the brush from the widget's
    palette that is used to render the background, and QPalette::Base
    is typically white.

    \snippet widgets/tooltips/sortingbox.cpp 3

    After creating the application's tool buttons using the private \c
    createToolButton() function, we construct the shapes of a circle,
    a square and a triangle using QPainterPath.

    The QPainterPath class provides a container for painting
    operations, enabling graphical shapes to be constructed and
    reused. The main advantage of painter paths over normal drawing
    operations is that complex shapes only need to be created once,
    but they can be drawn many times using only calls to
    QPainter::drawPath().

    \snippet widgets/tooltips/sortingbox.cpp 4

    Then we set the window title, resize the widget to a suitable
    size, and finally create three initial shape items using the
    private \c createShapeItem(), \c initialItemPosition() and \c
    initialItemColor() functions.

    \snippet widgets/tooltips/sortingbox.cpp 5

    QWidget::event() is the main event handler and receives all the
    widget's events. Normally, we recommend reimplementing one of the
    specialized event handlers instead of this function. But here we
    want to catch the QEvent::ToolTip events, and since these are
    rather rare, there exists no specific event handler. For that
    reason we reimplement the main event handler, and the first thing
    we need to do is to determine the event's type:

    \snippet widgets/tooltips/sortingbox.cpp 6

    If the type is QEvent::ToolTip, we cast the event to a QHelpEvent,
    otherwise we propagate the event using the QWidget::event()
    function.

    The QHelpEvent class provides an event that is used to request
    helpful information about a particular point in a widget.

    For example, the QHelpEvent::pos() function returns the event's
    position relative to the widget to which the event is dispatched.
    Here we use this information to determine if the position of the
    event is contained within the area of any of the shape items. If
    it is, we display the shape item's tooltip at the position of the
    event. If not, we hide the tooltip and explicitly ignore the event.
    This makes sure that the calling code does not start any tooltip
    specific modes as a result of the event. Note that the
    QToolTip::showText() function needs the event's position in global
    coordinates provided by QHelpEvent::globalPos().

    \snippet widgets/tooltips/sortingbox.cpp 7

    The \c resizeEvent() function is reimplemented to receive the
    resize events dispatched to the widget. It makes sure that the
    tool buttons keep their position relative to the main widget when
    the widget is resized. We want the buttons to always be vertically
    aligned in the application's bottom right corner, so each time the
    main widget is resized we update the buttons geometry.

    \snippet widgets/tooltips/sortingbox.cpp 8

    The \c paintEvent() function is reimplemented to receive paint
    events for the widget. We create a QPainter for the \c SortingBox
    widget, and run through the list of created shape items, drawing
    each item at its defined position.

    \snippet widgets/tooltips/sortingbox.cpp 9

    The painter will by default draw all the shape items at position
    (0,0) in the \c SortingBox widget. The QPainter::translate()
    function translates the coordinate system by the given offset,
    making each shape item appear at its defined position. But
    remember to translate the coordinate system back when the item is
    drawn, otherwise the next shape item will appear at a position
    relative to the item drawn last.

    \snippet widgets/tooltips/sortingbox.cpp 10

    The QPainter::setBrush() function sets the current brush used by
    the painter. When the provided argument is a QColor, the function
    calls the appropriate QBrush constructor which creates a brush with
    the specified color and Qt::SolidPattern style. The
    QPainter::drawPath() function draws the given path using the
    current pen for outline and the current brush for filling.

    \snippet widgets/tooltips/sortingbox.cpp 11

    The \c mousePressEvent() function is reimplemented to receive the
    mouse press events dispatched to the widget. It determines if an
    event's position is contained within the area of any of the shape
    items, using the private \c itemAt() function.

    If an item covers the position, we store a pointer to that item
    and the event's position. If several of the shape items cover the
    position, we store the pointer to the uppermost item. Finally, we
    move the shape item to the end of the list, and make a call to the
    QWidget::update() function to make the item appear on top.

    The QWidget::update() function does not cause an immediate
    repaint; instead it schedules a paint event for processing when Qt
    returns to the main event loop.

    \snippet widgets/tooltips/sortingbox.cpp 12

    The \c mouseMoveEvent() function is reimplemented to receive mouse
    move events for the widget. If the left mouse button is pressed
    and there exists a shape item in motion, we use the private \c
    moveItemTo() function to move the item with an offset
    corresponding to the offset between the positions of the current
    mouse event and the previous one.

    \snippet widgets/tooltips/sortingbox.cpp 13

    The \c mouseReleaseEvent() function is reimplemented to receive
    the mouse release events dispatched to the widget. If the left
    mouse button is pressed and there exists a shape item in motion,
    we use the private \c moveItemTo() function to move the item like
    we did in \c mouseMoveEvent(). But then we remove the pointer to
    the item in motion, making the shape item's position final for
    now. To move the item further, the user will need to press the
    left mouse button again.

    \snippet widgets/tooltips/sortingbox.cpp 14
    \codeline
    \snippet widgets/tooltips/sortingbox.cpp 15
    \codeline
    \snippet widgets/tooltips/sortingbox.cpp 16

    The \c createNewCircle(), \c createNewSquare() and \c
    createNewTriangle() slots simply create new shape items, using the
    private \c createShapeItem(), \c randomItemPosition() and \c
    randomItemColor() functions.

    \snippet widgets/tooltips/sortingbox.cpp 17

    In the \c itemAt() function, we run through the list of created
    shape items to check if the given position is contained within the
    area of any of the shape items.

    For each shape item we use the QPainterPath::contains() function
    to find out if the item's painter path contains the position. If
    it does we return the index of the item, otherwise we return
    -1. We run through the list backwards to get the index of the
    uppermost shape item in case several items cover the position.

    \snippet widgets/tooltips/sortingbox.cpp 18

    The \c moveItemTo() function moves the shape item in motion, and
    the parameter \c pos is the position of a mouse event. First we
    calculate the offset between the parameter \c pos and the previous
    mouse event position. Then we add the offset to the current
    position of the item in motion.

    It is tempting to simply set the position of the item to be the
    parameter \c pos. But an item's position defines the top left
    corner of the item's bounding rectangle, and the parameter \c pos
    can be any point; The suggested shortcut would cause the item to
    jump to a position where the cursor is pointing to the bounding
    rectangle's top left corner, regardless of the item's previous
    position.

    \snippet widgets/tooltips/sortingbox.cpp 19

    Finally, we update the previous mouse event position, and make a
    call to the QWidget::update() function to make the item appear at
    its new position.

    \snippet widgets/tooltips/sortingbox.cpp 20

    In the \c updateButtonGeometry() function we set the geometry for
    the given button. The parameter coordinates define the bottom
    right corner of the button. We use these coordinates and the
    button's size hint to determine the position of the upper left
    corner. This position, and the button's width and height, are the
    arguments required by the QWidget::setGeometry() function.

    In the end, we calculate and return the y-coordinate of the bottom
    right corner of the next button. We use the QWidget::style()
    function to retrieve the widget's GUI style, and then
    QStyle::pixelMetric() to determine the widget's preferred default
    spacing between its child widgets.

    \snippet widgets/tooltips/sortingbox.cpp 21

    The \c createShapeItem() function creates a single shape item. It
    sets the path, tooltip, position and color, using the item's own
    functions. In the end, the function appends the new item to the
    list of shape items, and calls the QWidget::update() function to
    make it appear with the other items within the \c SortingBox
    widget.

    \snippet widgets/tooltips/sortingbox.cpp 22

    The \c createToolButton() function is called from the \c
    SortingBox constructor. We create a tool button with the given
    tooltip and icon. The button's parent is the \c SortingBox widget,
    and its size is 32 x 32 pixels. Before we return the button, we
    connect it to the given slot.

    \snippet widgets/tooltips/sortingbox.cpp 23

    The \c initialItemPosition() function is also called from the
    constructor. We want the three first items to initially be
    centered in the middle of the \c SortingBox widget, and we use
    this function to calculate their positions.

    \snippet widgets/tooltips/sortingbox.cpp 24

    Whenever the user creates a new shape item, we want the new item
    to appear at a random position, and we use the \c
    randomItemPosition() function to calculate such a position. We
    make sure that the item appears within the visible area of the
    \c SortingBox widget, using the widget's current width and height
    when calculating the random coordinates.

    \snippet widgets/tooltips/sortingbox.cpp 25

    As with \c initialItemPosition(), the \c initialItemColor()
    function is called from the constructor. The purposes of both
    functions are purely cosmetic: We want to control the initial
    position and color of the three first items.

    \snippet widgets/tooltips/sortingbox.cpp 26

    Finally the \c randomItemColor() function is implemented to give
    the shape items the user creates, a random color.

    \section1 ShapeItem Class Definition

    \snippet widgets/tooltips/shapeitem.h 0

    The \c ShapeItem class is a custom widget representing one single
    shape item. The widget has a path, a position, a color and a
    tooltip. We need functions to set or modify these objects, as well
    as functions that return them. We make the latter functions \c
    const to prohibit any modifications of the objects,
    i.e. prohibiting unauthorized manipulation of the shape items
    appearance.

    \section1 ShapeItem Class Implementation

    \snippet widgets/tooltips/shapeitem.cpp 0
    \codeline
    \snippet widgets/tooltips/shapeitem.cpp 1
    \codeline
    \snippet widgets/tooltips/shapeitem.cpp 2
    \codeline
    \snippet widgets/tooltips/shapeitem.cpp 3

    This first group of functions simply return the objects that are
    requested. The objects are returned as constants, i.e. they cannot
    be modified.

    \snippet widgets/tooltips/shapeitem.cpp 4
    \codeline
    \snippet widgets/tooltips/shapeitem.cpp 5
    \codeline
    \snippet widgets/tooltips/shapeitem.cpp 6
    \codeline
    \snippet widgets/tooltips/shapeitem.cpp 7

    The last group of functions set or modify the shape item's path,
    position, color and tooltip, respectively.
*/
