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

/*!
    \example itemviews/frozencolumn
    \title Frozen Column Example
    \examplecategory {User Interface Components}
    \ingroup examples-itemviews
    \brief This example demonstrates how to freeze a column within a QTableView.

    \image frozencolumn-example.png
           {Application showing a frozen column in a table}

    We use Qt's model/view framework to implement a table with its first
    column frozen. This technique can be applied to several columns or rows,
    as long as they are on the edge of the table.

    The model/view framework allows for one model to be displayed in different
    ways using multiple views. For this example, we use two views on the same
    model - two \l {QTableView}{table views} sharing one model. The frozen
    column is a child of the main tableview, and we provide the desired visual
    effect using an overlay technique which will be described step by step in
    the coming sections.

    \image frozencolumn-tableview.png
           {The layout of the table view, headers, and scrollbars}

    \section1 FreezeTableWidget Class Definition

    The \c FreezeTableWidget class has a constructor and a destructor. Also, it
    has two private members: the table view that we will use as an overlay, and
    the shared model for both table views. Two slots are added to help keep the
    section sizes in sync, as well as a function to readjust the frozen
    column's geometry. In addition, we reimplement two functions:
    \l{QAbstractItemView::}{resizeEvent()} and \l{QTableView::}{moveCursor()}.

    \snippet itemviews/frozencolumn/freezetablewidget.h Widget definition

    \note QAbstractItemView is \l{QTableView}'s ancestor.


    \section1 FreezeTableWidget Class Implementation

    The constructor takes \a model as an argument and creates a table view that
    we will use to display the frozen column. Then, within the constructor, we
    invoke the \c init() function to set up the frozen column. Finally, we
    connect the \l{QHeaderView::sectionResized()} signals (for horizontal and
    vertical headers) to the appropriate slots. This ensures that our frozen
    column's sections are in sync with the headers. We also connect the
    vertical scrollbars together so that the frozen column scrolls vertically
    with the rest of our table.

    \snippet itemviews/frozencolumn/freezetablewidget.cpp constructor


    In the \c init() function, we ensure that the overlay table view
    responsible for displaying the frozen column, is set up properly. This
    means that this table view, \c frozenTableView, has to have the same model
    as the main table view. However, the difference here is: \c frozenTableView's
    only visible column is its first column; we hide the others using
    \l{QTableView::}{setColumnHidden()}

    \snippet itemviews/frozencolumn/freezetablewidget.cpp init part1


    In terms of the frozen column's z-order, we stack it on top of the
    viewport. This is achieved by calling \l{QWidget::}{stackUnder()} on the
    viewport. For appearance's sake, we prevent the column from stealing focus
    from the main tableview. Also, we make sure that both views share the same
    selection model, so only one cell can be selected at a time. A few other
    tweaks are done to make our application look good and behave consistently
    with the main tableview. Note that we called \c updateFrozenTableGeometry()
    to make the column occupy the correct spot.

    \snippet itemviews/frozencolumn/freezetablewidget.cpp init part2

    When you resize the frozen column, the same column on the main table view
    must resize accordingly, to provide seamless integration. This is
    accomplished by getting the new size of the column from the \c newSize
    value from the \l{QHeaderView::}{sectionResized()} signal, emitted by both
    the horizontal and vertical header.

    \snippet itemviews/frozencolumn/freezetablewidget.cpp sections

    Since the width of the frozen column is modified, we adjust the geometry of
    the widget accordingly by invoking \c updateFrozenTableGeometry(). This
    function is further explained below.

    In our reimplementation of QTableView::resizeEvent(), we call
    \c updateFrozenTableGeometry() after invoking the base class
    implementation.

    \snippet itemviews/frozencolumn/freezetablewidget.cpp resize

    When navigating around the table with the keyboard, we need to ensure that
    the current selection does not disappear behind the frozen column. To
    synchronize this, we reimplement QTableView::moveCursor() and adjust the
    scrollbar positions if needed, after calling the base class implementation.

    \snippet itemviews/frozencolumn/freezetablewidget.cpp navigate

    The frozen column's geometry calculation is based on the geometry of the
    table underneath, so it always appears in the right place. Using the
    QFrame::frameWidth() function helps to calculate this geometry correctly,
    no matter which style is used. We rely on the geometry of the viewport and
    headers to set the boundaries for the frozen column.

    \snippet itemviews/frozencolumn/freezetablewidget.cpp geometry

*/

