aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquicktableview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquicktableview.cpp')
-rw-r--r--src/quick/items/qquicktableview.cpp1053
1 files changed, 849 insertions, 204 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 81eddb03ce..02c50a88b6 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -40,6 +40,16 @@
in a TableView. To create models with multiple columns, either use
\l TableModel or a C++ model that inherits QAbstractItemModel.
+ A TableView does not include headers by default. You can add headers
+ using the \l HorizontalHeaderView and \l VerticalHeaderView from
+ Qt Quick Controls.
+
+ \note TableView will only \l {isRowLoaded()}{load} as many delegate items as
+ needed to fill up the view. There is no guarantee that items outside the view
+ will be loaded, although TableView will sometimes pre-load items for
+ optimization reasons. Hence, a TableView with zero width or height might not
+ load any delegate items at all.
+
\section1 Example Usage
\section2 C++ Models
@@ -102,7 +112,7 @@
the width of the column. Otherwise, it will check if an explicit width has
been set with \l setColumnWidth(). If not, \l implicitColumnWidth() will be used.
The implicit width of a column is the same as the largest
- \l {implicit width}{QQuickItem::implicitWidth()} found among the currently loaded
+ \l {Item::implicitWidth}{implicit width} found among the currently loaded
delegate items in that column. Trying to set an explicit \c width directly on
a delegate has no effect, and will be ignored and overwritten. The same logic also
applies to row heights.
@@ -126,7 +136,7 @@
of the view, and is recalculated again if it's flicked back in. This means that if the
width depends on the \l implicitColumnWidth(), the calculation can be different each time,
depending on which row you're at when the column enters (since \l implicitColumnWidth()
- only considers the delegate items that are currently \l {loaded}{isColumnLoaded()}).
+ only considers the delegate items that are currently \l {isColumnLoaded()}{loaded}).
To avoid this, you should use a \l columnWidthProvider, or ensure that all the delegate
items in the same column have the same \c implicitWidth.
@@ -156,13 +166,13 @@
You can let the user edit table cells by providing an edit delegate. The
edit delegate will be instantiated according to the \l editTriggers, which
by default is when the user double taps on a cell, or presses e.g
- \l Qt.Key_Enter or \l Qt.Key_Return. The edit delegate is set using
+ \l Qt::Key_Enter or \l Qt::Key_Return. The edit delegate is set using
\l {TableView::editDelegate}, which is an attached property that you set
on the \l delegate. The following snippet shows how to do that:
\snippet qml/tableview/editdelegate.qml 0
- If the user presses Qt.Key_Enter or Qt.Key_Return while the edit delegate
+ If the user presses Qt::Key_Enter or Qt::Key_Return while the edit delegate
is active, TableView will emit the \l TableView::commit signal to the edit
delegate, so that it can write back the changed data to the model.
@@ -207,7 +217,7 @@
\snippet qml/tableview/overlay.qml 0
You could also parent the overlay directly to the cell instead of the
- \l contentItem. But doing so will be fragile since the cell is unloaded
+ \l {Flickable::}{contentItem}. But doing so will be fragile since the cell is unloaded
or reused whenever it's flicked out of the viewport.
\sa layoutChanged()
@@ -248,7 +258,7 @@
to let the user select cells.
\note By default, a cell will become
- \l {QQuickItemSelectionModel::currentIndex()}{current}, and any selections will
+ \l {ItemSelectionModel::currentIndex}{current}, and any selections will
be removed, when the user taps on it. If such default tap behavior is not wanted
(e.g if you use custom pointer handlers inside your delegate), you can set
\l pointerNavigationEnabled to \c false.
@@ -257,11 +267,17 @@
In order to support keyboard navigation, you need to assign an \l ItemSelectionModel
to the \l selectionModel property. TableView will then use this model to manipulate
- the model's \l {ItemSelectionModel::currentIndex}{currentIndex}. You can
- disable keyboard navigation fully (in case you want to implement your own key
- handlers) by setting \l keyNavigationEnabled to \c false. Below is an
- example that demonstrates how to use keyboard navigation together with
- \c current and \c selected properties:
+ the model's \l {ItemSelectionModel::currentIndex}{currentIndex}.
+
+ It's the responsibility of the delegate to render itself as
+ \l {ItemSelectionModel::currentIndex}{current}. You can do this by adding a
+ property \c {required property bool current} to it, and let the appearance
+ depend on its state. The \c current property's value is set by the TableView.
+ You can also disable keyboard navigation fully (in case you want to implement your
+ own key handlers) by setting \l keyNavigationEnabled to \c false.
+
+ The following example demonstrates how you can use keyboard navigation together
+ with \c current and \c selected properties:
\snippet qml/tableview/keyboard-navigation.qml 0
@@ -324,7 +340,7 @@
}
\endcode
- \sa mimeData(), dropMimeData(), QUndoStack, QUndoCommand, QClipboard
+ \sa QAbstractItemModel::mimeData(), QAbstractItemModel::dropMimeData(), QUndoStack, QUndoCommand, QClipboard
*/
/*!
@@ -442,11 +458,32 @@
\l {Item::}{implicitHeight}. The TableView lays out the items based on that
information. Explicit width or height settings are ignored and overwritten.
+ Inside the delegate, you can optionally add one or more of the following
+ properties. TableView modifies the values of these properties to inform the
+ delegate which state it's in. This can be used by the delegate to render
+ itself differently according on its own state.
+
+ \list
+ \li required property bool current - \c true if the delegate is \l {Keyboard navigation}{current.}
+ \li required property bool selected - \c true if the delegate is \l {Selecting items}{selected.}
+ \li required property bool editing - \c true if the delegate is being \l {Editing cells}{edited.}
+ \endlist
+
+ The following example shows how to use these properties:
+ \code
+ delegate: Rectangle {
+ required property bool current
+ required property bool selected
+ border.width: current ? 1 : 0
+ color: selected ? palette.highlight : palette.base
+ }
+ \endcode
+
\note Delegates are instantiated as needed and may be destroyed at any time.
They are also reused if the \l reuseItems property is set to \c true. You
should therefore avoid storing state information in the delegates.
- \sa {Row heights and column widths}, {Reusing items}
+ \sa {Row heights and column widths}, {Reusing items}, {Required Properties}
*/
/*!
@@ -570,7 +607,7 @@
\readonly
This read-only property holds the column in the view that contains the
- item that is current. If no item is current, it will be \c -1.
+ item that is \l {Keyboard navigation}{current.} If no item is current, it will be \c -1.
\note In order for TableView to report what the current column is, you
need to assign an \l ItemSelectionModel to \l selectionModel.
@@ -583,7 +620,7 @@
\readonly
This read-only property holds the row in the view that contains the item
- that is current. If no item is current, it will be \c -1.
+ that is \l {Keyboard navigation}{current.} If no item is current, it will be \c -1.
\note In order for TableView to report what the current row is, you
need to assign an \l ItemSelectionModel to \l selectionModel.
@@ -656,16 +693,47 @@
\qmlproperty enumeration QtQuick::TableView::selectionBehavior
\since 6.4
- This property holds whether the user can select single cells, rows or columns.
+ This property holds whether the user can select cells, rows or columns.
- \list
- \li TableView.SelectionDisabled - the user cannot perform selections.
- \li TableView.SelectCells (default) - the user can select individual cells.
- \li TableView.SelectRows - the user can only select rows.
- \li TableView.SelectColumns - the user can only select columns.
- \endlist
+ \value TableView.SelectionDisabled
+ The user cannot perform selections
+ \value TableView.SelectCells
+ (Default value) The user can select individual cells
+ \value TableView.SelectRows
+ The user can only select rows
+ \value TableView.SelectColumns
+ The user can only select columns
- \sa {Selecting items}, selectionModel, keyNavigationEnabled
+ \sa {Selecting items}, selectionMode, selectionModel, keyNavigationEnabled
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::TableView::selectionMode
+ \since 6.6
+
+ If \l selectionBehavior is set to \c {TableView.SelectCells}, this property holds
+ whether the user can select one cell at a time, or multiple cells.
+ If \l selectionBehavior is set to \c {TableView.SelectRows}, this property holds
+ whether the user can select one row at a time, or multiple rows.
+ If \l selectionBehavior is set to \c {TableView.SelectColumns}, this property holds
+ whether the user can select one column at a time, or multiple columns.
+
+ The following modes are available:
+
+ \value TableView.SingleSelection
+ The user can select a single cell, row or column.
+ \value TableView.ContiguousSelection
+ The user can select a single contiguous block of cells.
+ An existing selection can be made bigger or smaller by holding down
+ the \c Shift modifier while selecting.
+ \value TableView.ExtendedSelection
+ (Default value) The user can select multiple individual blocks of
+ cells. An existing selection can be made bigger or smaller by
+ holding down the \c Shift modifier while selecting. A new selection
+ block can be started without clearing the current selection by
+ holding down the \c Control modifier while selecting.
+
+ \sa {Selecting items}, selectionBehavior, selectionModel, keyNavigationEnabled
*/
/*!
@@ -698,25 +766,25 @@
But the application can call \l edit() and \l closeEditor() manually.
\value TableView.SingleTapped - the user can edit a cell by single tapping it.
\value TableView.DoubleTapped - the user can edit a cell by double tapping it.
- \value TableView.SelectedTapped - the user can edit the
- \l {QItemSelectionModel::currentIndex()}{current cell} by tapping it.
+ \value TableView.SelectedTapped - the user can edit a
+ \l {QItemSelectionModel::selectedIndexes()}{selected cell} by tapping it.
\value TableView.EditKeyPressed - the user can edit the
- \l {QItemSelectionModel::currentIndex()}{current cell} by pressing one
+ \l {ItemSelectionModel::currentIndex}{current cell} by pressing one
of the edit keys. The edit keys are decided by the OS, but are normally
- \c Qt.Key_Enter and \c Qt.Key_Return.
+ \c Qt::Key_Enter and \c Qt::Key_Return.
\value TableView.AnyKeyPressed - the user can edit the
- \l {TableView::current}{current cell} by pressing any key, other
+ \l {ItemSelectionModel::currentIndex}{current cell} by pressing any key, other
than the cell navigation keys. The pressed key is also sent to the
focus object inside the \l {TableView::editDelegate}{edit delegate}.
For \c TableView.SelectedTapped, \c TableView.EditKeyPressed, and
\c TableView.AnyKeyPressed to have any effect, TableView needs to have a
\l {selectionModel}{selection model} assigned, since they depend on a
- \l {QItemSelectionModel::currentIndex()}{current index} being set. To be
+ \l {ItemSelectionModel::currentIndex}{current index} being set. To be
able to receive any key events at all, TableView will also need to have
\l QQuickItem::activeFocus.
- When editing a cell, the user can press \c Qt.Key_Tab or \c Qt.Key_Backtab
+ When editing a cell, the user can press \c Qt::Key_Tab or \c Qt::Key_Backtab
to \l {TableView::commit}{commit} the data, and move editing to the next
cell. This behavior can be disabled by setting
\l QQuickItem::activeFocusOnTab on TableView to \c false.
@@ -801,6 +869,20 @@
*/
/*!
+ \qmlmethod QtQuick::TableView::positionViewAtIndex(QModelIndex index, PositionMode mode, point offset, rect subRect)
+ \since 6.5
+
+ Positions the view such that \a index is at the position specified
+ by \a mode, \a offset and \a subRect.
+
+ Convenience method for calling
+ \code
+ positionViewAtRow(rowAtIndex(index), mode & Qt.AlignVertical_Mask, offset.y, subRect)
+ positionViewAtColumn(columnAtIndex(index), mode & Qt.AlignVertical_Mask, offset.x, subRect)
+ \endcode
+*/
+
+/*!
\qmlmethod bool QtQuick::TableView::isColumnLoaded(int column)
\since 6.2
@@ -832,14 +914,9 @@
/*!
\qmlmethod QtQuick::TableView::positionViewAtCell(int column, int row, PositionMode mode, point offset, rect subRect)
+ \deprecated
- Positions \l {Flickable::}{contentX} and \l {Flickable::}{contentY} such
- that \a row and \a column is at the position specified by \a mode, \a offset and \a subRect.
-
- Convenience for calling
- \code
- positionViewAtCell(Qt.point(column, row), mode, offset, subRect)
- \endcode
+ Use \l {positionViewAtIndex()}{positionViewAtIndex(index(row, column), ...)} instead.
*/
/*!
@@ -867,6 +944,48 @@
*/
/*!
+ \qmlmethod QtQuick::TableView::moveColumn(int source, int destination)
+ \since 6.8
+
+ Moves a column from the \a source to the \a destination position.
+
+ \note If a syncView is set, the sync view will control the internal index mapping for
+ column reordering. Therefore, in that case, a call to this function will be forwarded to
+ the sync view instead.
+*/
+
+/*!
+ \qmlmethod QtQuick::TableView::clearColumnReordering()
+ \since 6.8
+
+ Resets any previously applied column reordering.
+
+ \note If a syncView is set, a call to this function will be forwarded to
+ corresponding view item and reset the column ordering.
+*/
+
+/*!
+ \qmlmethod QtQuick::TableView::moveRow(int source, int destination)
+ \since 6.8
+
+ Moves a row from the \a source to the \a destination position.
+
+ \note If a syncView is set, the sync view will control the internal index mapping for
+ row reordering. Therefore, in that case, a call to this function will be forwarded to
+ the sync view instead.
+*/
+
+/*!
+ \qmlmethod QtQuick::TableView::clearRowReordering()
+ \since 6.8
+
+ Resets any previously applied row reordering.
+
+ \note If a syncView is set, a call to this function will be forwarded to
+ the corresponding view item and reset the row ordering.
+*/
+
+/*!
\qmlmethod Item QtQuick::TableView::itemAtCell(point cell)
Returns the delegate item at \a cell if loaded, otherwise \c null.
@@ -879,8 +998,27 @@
/*!
\qmlmethod Item QtQuick::TableView::itemAtCell(int column, int row)
+ \deprecated
- Convenience for calling \c{itemAtCell(Qt.point(column, row))}.
+ Use \l {itemAtIndex()}{itemAtIndex(index(row, column))} instead.
+*/
+
+/*!
+ \qmlmethod Item QtQuick::TableView::itemAtIndex(QModelIndex index)
+ \since 6.5
+
+ Returns the instantiated delegate item for the cell that represents
+ \a index. If the item is not \l {isRowLoaded()}{loaded}, the value
+ will be \c null.
+
+ \note only the items that are visible in the view are normally loaded.
+ As soon as a cell is flicked out of the view, the item inside will
+ either be unloaded or placed in the recycle pool. As such, the return
+ value should never be stored.
+
+ \note If the \l model is not a QAbstractItemModel, you can also use
+ \l {itemAtCell()}{itemAtCell(Qt.point(column, row))}. But be aware
+ that \c {point.x} maps to columns and \c {point.y} maps to rows.
*/
/*!
@@ -929,15 +1067,6 @@
Returns the width of the given \a column. If the column is not
loaded (and therefore not visible), the return value will be \c -1.
- \note It's the applications responsibility to store what the
- column widths are, by using a \l columnWidthProvider. Hence,
- there is no setter function. This getter function is mostly
- useful if the TableView doesn't have a columnWidthProvider set, since
- otherwise you can call that function instead (which will work, even
- for columns that are not currently visible).
- If no columnWidthProvider is set, the width of a column will be
- equal to its \l implicitColumnWidth().
-
\sa columnWidthProvider, implicitColumnWidth(), isColumnLoaded(), {Row heights and column widths}
*/
@@ -948,15 +1077,6 @@
Returns the height of the given \a row. If the row is not
loaded (and therefore not visible), the return value will be \c -1.
- \note It's the applications responsibility to store what the
- row heights are, by using a \l rowHeightProvider. Hence,
- there is no setter function. This getter function is mostly
- useful if the TableView doesn't have a rowHeightProvider set, since
- otherwise you can call that function instead (which will work, even
- for rows that are not currently visible).
- If no rowHeightProvider is set, the height of a row will be
- equal to its \l implicitRowHeight().
-
\sa rowHeightProvider, implicitRowHeight(), isRowLoaded(), {Row heights and column widths}
*/
@@ -1052,7 +1172,7 @@
\qmlmethod qreal QtQuick::TableView::explicitColumnWidth(int column)
Returns the width of the \a column set with \l setColumnWidth(). This width might
- differ from the actual width of the column, if a \l columnWidthProvider()
+ differ from the actual width of the column, if a \l columnWidthProvider
is in use. To get the actual width of a column, use \l columnWidth().
A return value equal to \c 0 means that the column has been told to hide.
@@ -1124,7 +1244,7 @@
\qmlmethod qreal QtQuick::TableView::explicitRowHeight(int row)
Returns the height of the \a row set with \l setRowHeight(). This height might
- differ from the actual height of the column, if a \l rowHeightProvider()
+ differ from the actual height of the column, if a \l rowHeightProvider
is in use. To get the actual height of a row, use \l rowHeight().
A return value equal to \c 0 means that the row has been told to hide.
@@ -1139,18 +1259,14 @@
/*!
\qmlmethod QModelIndex QtQuick::TableView::modelIndex(int row, int column)
\since 6.4
+ \deprecated
- Returns the \l QModelIndex that maps to \a column and \a row in the view.
-
- \a row and \a column should be the row and column in the view (table row and
- table column), and not a row and column in the model.
+ Use \l {QtQuick::TableView::}{index(int row, int column)} instead.
- \note Because of an API incompatible change in Qt 6.4.0 and Qt 6.4.1, the
+ \note Because of an API incompatible change between Qt 6.4.0 and Qt 6.4.2, the
order of \c row and \c column was specified in the opposite order. If you
rely on the order to be \c {modelIndex(column, row)}, you can set the
environment variable \c QT_QUICK_TABLEVIEW_COMPAT_VERSION to \c 6.4
-
- \sa rowAtIndex(), columnAtIndex()
*/
/*!
@@ -1162,19 +1278,35 @@
modelIndex(cell.y, cell.x)
\endcode
- A cell is simply a \l point that combines row and column into
+ A \a cell is simply a \l point that combines row and column into
a single type.
\note \c {point.x} will map to the column, and \c {point.y} will map to the row.
*/
/*!
+ \qmlmethod QModelIndex QtQuick::TableView::index(int row, int column)
+ \since 6.4.3
+
+ Returns the \l QModelIndex that maps to \a row and \a column in the view.
+
+ \a row and \a column should be the row and column in the view (table row and
+ table column), and not a row and column in the model. For a plain
+ TableView, this is equivalent of calling \c {model.index(row, column).}
+ But for a subclass of TableView, like TreeView, where the data model is
+ wrapped inside an internal proxy model that flattens the tree structure
+ into a table, you need to use this function to resolve the model index.
+
+ \sa rowAtIndex(), columnAtIndex()
+*/
+
+/*!
\qmlmethod int QtQuick::TableView::rowAtIndex(QModelIndex modelIndex)
\since 6.4
Returns the row in the view that maps to \a modelIndex in the model.
- \sa columnAtIndex(), modelIndex()
+ \sa columnAtIndex(), index()
*/
/*!
@@ -1183,7 +1315,7 @@
Returns the column in the view that maps to \a modelIndex in the model.
- \sa rowAtIndex(), modelIndex()
+ \sa rowAtIndex(), index()
*/
/*!
@@ -1249,6 +1381,24 @@
*/
/*!
+ \qmlsignal QtQuick::TableView::columnMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
+ \since 6.8
+
+ This signal is emitted when a column is moved. The column's logical index is specified by
+ \a logicalIndex, the old index by \a oldVisualIndex, and the new index position by
+ \a newVisualIndex.
+*/
+
+/*!
+ \qmlsignal QtQuick::TableView::rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
+ \since 6.8
+
+ This signal is emitted when a row is moved. The row's logical index is specified by
+ \a logicalIndex, the old index by \a oldVisualIndex, and the new index position by
+ \a newVisualIndex.
+*/
+
+/*!
\qmlattachedproperty TableView QtQuick::TableView::view
This attached property holds the view that manages the delegate instance.
@@ -1291,13 +1441,13 @@
This signal is emitted by the \l {TableView::editDelegate}{edit delegate}
This attached signal is emitted when the \l {TableView::editDelegate}{edit delegate}
- is active, and the user presses \l Qt.Key_Enter or \l Qt.Key_Return. It will also
+ is active, and the user presses \l Qt::Key_Enter or \l Qt::Key_Return. It will also
be emitted if TableView has \l QQuickItem::activeFocusOnTab set, and the user
- presses Qt.Key_Tab or Qt.Key_Backtab.
+ presses Qt::Key_Tab or Qt::Key_Backtab.
This signal will \e not be emitted if editing ends because of reasons other
than the ones mentioned. This includes e.g if the user presses
- Qt.Key_Escape, taps outside the delegate, the row or column being
+ Qt::Key_Escape, taps outside the delegate, the row or column being
edited is deleted, or if the application calls \l closeEditor().
Upon receiving the signal, the edit delegate should write any modified data
@@ -1315,7 +1465,7 @@
This attached property holds the edit delegate. It's instantiated
when editing begins, and parented to the delegate it edits. It
supports the same required properties as the
- \l {\l delegate}{TableView delegate}, including \c index, \c row and \c column.
+ \l {TableView::delegate}{TableView delegate}, including \c index, \c row and \c column.
Properties of the model, like \c display and \c edit, are also available
(depending on the \l {QAbstractItemModel::roleNames()}{role names} exposed
by the model).
@@ -1330,12 +1480,12 @@
and \l closeEditor(), respectively. The \c Qt::ItemIsEditable flag will
then be ignored.
- Editing ends when the user presses \c Qt.Key_Enter or \c Qt.Key_Return
- (and also \c Qt.Key_Tab or \c Qt.Key_Backtab, if TableView has
+ Editing ends when the user presses \c Qt::Key_Enter or \c Qt::Key_Return
+ (and also \c Qt::Key_Tab or \c Qt::Key_Backtab, if TableView has
\l QQuickItem::activeFocusOnTab set). In that case, the \l TableView::commit
signal will be emitted, so that the edit delegate can respond by writing any
modified data back to the model. If editing ends because of other reasons
- (e.g if the user presses Qt.Key_Escape), the signal will not be emitted.
+ (e.g if the user presses Qt::Key_Escape), the signal will not be emitted.
In any case will \l {Component::destruction}{destruction()} be emitted in the end.
While the edit delegate is showing, the cell underneath will still be visible, and
@@ -1344,7 +1494,7 @@
of the edit delegate be a solid \l Rectangle, or hide some of the items
inside the \l {TableView::delegate}{TableView delegate.}. The latter can be done
by defining a property \c {required property bool editing} inside it, that you
- bind to the \l visible property of some of the child items.
+ bind to the \l {QQuickItem::}{visible} property of some of the child items.
The following snippet shows how to do that:
\snippet qml/tableview/editdelegate.qml 1
@@ -1370,6 +1520,51 @@ static const char* kRequiredProperty_selected = "selected";
static const char* kRequiredProperty_current = "current";
static const char* kRequiredProperty_editing = "editing";
+QDebug operator<<(QDebug dbg, QQuickTableViewPrivate::RebuildState state)
+{
+#define TV_REBUILDSTATE(STATE) \
+ case QQuickTableViewPrivate::RebuildState::STATE: \
+ dbg << QStringLiteral(#STATE); break;
+
+ switch (state) {
+ TV_REBUILDSTATE(Begin);
+ TV_REBUILDSTATE(LoadInitalTable);
+ TV_REBUILDSTATE(VerifyTable);
+ TV_REBUILDSTATE(LayoutTable);
+ TV_REBUILDSTATE(CancelOvershoot);
+ TV_REBUILDSTATE(UpdateContentSize);
+ TV_REBUILDSTATE(PreloadColumns);
+ TV_REBUILDSTATE(PreloadRows);
+ TV_REBUILDSTATE(MovePreloadedItemsToPool);
+ TV_REBUILDSTATE(Done);
+ }
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, QQuickTableViewPrivate::RebuildOptions options)
+{
+#define TV_REBUILDOPTION(OPTION) \
+ if (options & QQuickTableViewPrivate::RebuildOption::OPTION) \
+ dbg << QStringLiteral(#OPTION)
+
+ if (options == QQuickTableViewPrivate::RebuildOption::None) {
+ dbg << QStringLiteral("None");
+ } else {
+ TV_REBUILDOPTION(All);
+ TV_REBUILDOPTION(LayoutOnly);
+ TV_REBUILDOPTION(ViewportOnly);
+ TV_REBUILDOPTION(CalculateNewTopLeftRow);
+ TV_REBUILDOPTION(CalculateNewTopLeftColumn);
+ TV_REBUILDOPTION(CalculateNewContentWidth);
+ TV_REBUILDOPTION(CalculateNewContentHeight);
+ TV_REBUILDOPTION(PositionViewAtRow);
+ TV_REBUILDOPTION(PositionViewAtColumn);
+ }
+
+ return dbg;
+}
+
QQuickTableViewPrivate::EdgeRange::EdgeRange()
: startIndex(kEdgeIndexNotSet)
, endIndex(kEdgeIndexNotSet)
@@ -1528,10 +1723,15 @@ QQuickItem *QQuickTableViewPrivate::selectionPointerHandlerTarget() const
return const_cast<QQuickTableView *>(q_func())->contentItem();
}
-bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
+bool QQuickTableViewPrivate::startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers)
{
Q_Q(QQuickTableView);
- Q_UNUSED(pos);
+ if (!selectionModel) {
+ if (warnNoSelectionModel)
+ qmlWarning(q_func()) << "Cannot start selection: no SelectionModel assigned!";
+ warnNoSelectionModel = false;
+ return false;
+ }
if (selectionBehavior == QQuickTableView::SelectionDisabled) {
qmlWarning(q) << "Cannot start selection: TableView.selectionBehavior == TableView.SelectionDisabled";
@@ -1542,6 +1742,25 @@ bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
if (resizeHandler->state() != QQuickTableViewResizeHandler::Listening)
return false;
+ // For SingleSelection and ContiguousSelection, we should only allow one selection at a time
+ if (selectionMode == QQuickTableView::SingleSelection
+ || selectionMode == QQuickTableView::ContiguousSelection)
+ clearSelection();
+ else if (selectionModel)
+ existingSelection = selectionModel->selection();
+
+ // If pos is on top of an unselected cell, we start a session where the user selects which
+ // cells to become selected. Otherwise, if pos is on top of an already selected cell and
+ // ctrl is being held, we start a session where the user selects which selected cells to
+ // become unselected.
+ selectionFlag = QItemSelectionModel::Select;
+ if (modifiers & Qt::ControlModifier) {
+ QPoint startCell = clampedCellAtPos(pos);
+ const QModelIndex startIndex = q->index(startCell.y(), startCell.x());
+ if (selectionModel->isSelected(startIndex))
+ selectionFlag = QItemSelectionModel::Deselect;
+ }
+
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
q->closeEditor();
@@ -1550,6 +1769,8 @@ bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
{
+ Q_Q(QQuickTableView);
+ Q_ASSERT(selectionFlag != QItemSelectionModel::NoUpdate);
if (loadedItems.isEmpty())
return;
if (!selectionModel) {
@@ -1562,13 +1783,26 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
if (!qaim)
return;
+ if (selectionMode == QQuickTableView::SingleSelection
+ && cellIsValid(selectionStartCell)) {
+ return;
+ }
+
const QRect prevSelection = selection();
- const QPoint clampedCell = clampedCellAtPos(pos);
+
+ QPoint clampedCell;
+ if (pos.x() == -1) {
+ // Special case: use current cell as start cell
+ clampedCell = q->cellAtIndex(selectionModel->currentIndex());
+ } else {
+ clampedCell = clampedCellAtPos(pos);
+ if (cellIsValid(clampedCell))
+ setCurrentIndex(clampedCell);
+ }
+
if (!cellIsValid(clampedCell))
return;
- setCurrentIndex(clampedCell);
-
switch (selectionBehavior) {
case QQuickTableView::SelectCells:
selectionStartCell = clampedCell;
@@ -1587,11 +1821,13 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
{
+ Q_ASSERT(selectionFlag != QItemSelectionModel::NoUpdate);
if (loadedItems.isEmpty())
return;
if (!selectionModel) {
@@ -1605,9 +1841,15 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
const QRect prevSelection = selection();
- const QPoint clampedCell = clampedCellAtPos(pos);
- if (!cellIsValid(clampedCell))
- return;
+
+ QPoint clampedCell;
+ if (selectionMode == QQuickTableView::SingleSelection) {
+ clampedCell = selectionStartCell;
+ } else {
+ clampedCell = clampedCellAtPos(pos);
+ if (!cellIsValid(clampedCell))
+ return;
+ }
setCurrentIndex(clampedCell);
@@ -1629,6 +1871,7 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
@@ -1659,42 +1902,84 @@ void QQuickTableViewPrivate::updateSelection(const QRect &oldSelection, const QR
const QRect oldRect = oldSelection.normalized();
const QRect newRect = newSelection.normalized();
+ QItemSelection select;
+ QItemSelection deselect;
+
// Select cells inside the new selection rect
{
const QModelIndex startIndex = qaim->index(newRect.y(), newRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() + newRect.height(), newRect.x() + newRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ select.append(QItemSelection(logicalModelIndex, logicalModelIndex));
+ }
}
// Unselect cells in the new minus old rects
if (oldRect.x() < newRect.x()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), oldRect.x());
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), newRect.x() - 1);
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
} else if (oldRect.x() + oldRect.width() > newRect.x() + newRect.width()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), newRect.x() + newRect.width() + 1);
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ for (auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
}
if (oldRect.y() < newRect.y()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), oldRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() - 1, oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
} else if (oldRect.y() + oldRect.height() > newRect.y() + newRect.height()) {
const QModelIndex startIndex = qaim->index(newRect.y() + newRect.height() + 1, oldRect.x());
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
+ }
+
+ if (selectionFlag == QItemSelectionModel::Select) {
+ // Don't clear the selection that existed before the user started a new selection block
+ deselect.merge(existingSelection, QItemSelectionModel::Deselect);
+ selectionModel->select(deselect, QItemSelectionModel::Deselect);
+ selectionModel->select(select, QItemSelectionModel::Select);
+ } else if (selectionFlag == QItemSelectionModel::Deselect){
+ QItemSelection oldSelection = existingSelection;
+ oldSelection.merge(select, QItemSelectionModel::Deselect);
+ selectionModel->select(oldSelection, QItemSelectionModel::Select);
+ selectionModel->select(select, QItemSelectionModel::Deselect);
+ } else {
+ Q_UNREACHABLE();
}
}
-void QQuickTableViewPrivate::clearSelection()
+void QQuickTableViewPrivate::cancelSelectionTracking()
{
+ // Cancel any ongoing key/mouse aided selection tracking
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
+ existingSelection.clear();
+ selectionFlag = QItemSelectionModel::NoUpdate;
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::CancelSelection);
+}
- if (selectionModel)
- selectionModel->clearSelection();
+void QQuickTableViewPrivate::clearSelection()
+{
+ if (!selectionModel)
+ return;
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
+ selectionModel->clearSelection();
}
void QQuickTableViewPrivate::normalizeSelection()
@@ -1728,7 +2013,7 @@ QRectF QQuickTableViewPrivate::selectionRectangle() const
// If the corner cells of the selection are loaded, we can position the
// selection rectangle at its exact location. Otherwise we extend it out
// to the edges of the content item. This is not ideal, but the best we
- // can do while the location of the the corner cells are unknown.
+ // can do while the location of the corner cells are unknown.
// This will at least move the selection handles (and other overlay) out
// of the viewport until the affected cells are eventually loaded.
int left = 0;
@@ -1832,6 +2117,11 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
return dist;
}
+void QQuickTableViewPrivate::setCallback(std::function<void (CallBackFlag)> func)
+{
+ selectableCallbackFunction = func;
+}
+
QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
{
QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
@@ -1870,14 +2160,14 @@ QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const
}
}
-int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex) const
+int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex, bool visualIndex) const
{
// Convert QModelIndex to cell index. A cell index is just an
// integer representation of a cell instead of using a QPoint.
const QPoint cell = q_func()->cellAtIndex(modelIndex);
if (!cellIsValid(cell))
return -1;
- return modelIndexAtCell(cell);
+ return modelIndexAtCell(visualIndex ? cell : QPoint(modelIndex.column(), modelIndex.row()));
}
int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) const
@@ -2019,7 +2309,10 @@ void QQuickTableViewPrivate::updateContentWidth()
if (loadedItems.isEmpty()) {
QBoolBlocker fixupGuard(inUpdateContentSize, true);
- q->QQuickFlickable::setContentWidth(0);
+ if (model && model->count() > 0 && tableModel && tableModel->delegate())
+ q->QQuickFlickable::setContentWidth(kDefaultColumnWidth);
+ else
+ q->QQuickFlickable::setContentWidth(0);
return;
}
@@ -2052,7 +2345,10 @@ void QQuickTableViewPrivate::updateContentHeight()
if (loadedItems.isEmpty()) {
QBoolBlocker fixupGuard(inUpdateContentSize, true);
- q->QQuickFlickable::setContentHeight(0);
+ if (model && model->count() > 0 && tableModel && tableModel->delegate())
+ q->QQuickFlickable::setContentHeight(kDefaultRowHeight);
+ else
+ q->QQuickFlickable::setContentHeight(0);
return;
}
@@ -2365,21 +2661,22 @@ void QQuickTableViewPrivate::forceLayout(bool immediate)
const QSize actualTableSize = calculateTableSize();
if (tableSize != actualTableSize) {
- // This can happen if the app is calling forceLayout while
- // the model is updated, but before we're notified about it.
- rebuildOptions = RebuildOption::All;
- } else {
- // Resizing a column (or row) can result in the table going from being
- // e.g completely inside the viewport to go outside. And in the latter
- // case, the user needs to be able to scroll the viewport, also if
- // flags such as Flickable.StopAtBounds is in use. So we need to
- // update contentWidth/Height to support that case.
- rebuildOptions = RebuildOption::LayoutOnly
- | RebuildOption::CalculateNewContentWidth
- | RebuildOption::CalculateNewContentHeight
- | checkForVisibilityChanges();
+ // The table size will have changed if forceLayout is called after
+ // the row count in the model has changed, but before we received
+ // a rowsInsertedCallback about it (and vice versa for columns).
+ rebuildOptions |= RebuildOption::ViewportOnly;
}
+ // Resizing a column (or row) can result in the table going from being
+ // e.g completely inside the viewport to go outside. And in the latter
+ // case, the user needs to be able to scroll the viewport, also if
+ // flags such as Flickable.StopAtBounds is in use. So we need to
+ // update contentWidth/Height to support that case.
+ rebuildOptions |= RebuildOption::LayoutOnly
+ | RebuildOption::CalculateNewContentWidth
+ | RebuildOption::CalculateNewContentHeight
+ | checkForVisibilityChanges();
+
scheduleRebuildTable(rebuildOptions);
if (immediate) {
@@ -2425,7 +2722,9 @@ FxTableItem *QQuickTableViewPrivate::createFxTableItem(const QPoint &cell, QQmlI
Q_Q(QQuickTableView);
bool ownItem = false;
- int modelIndex = modelIndexAtCell(cell);
+
+ int modelIndex = modelIndexAtCell(isTransposed ? QPoint(logicalRowIndex(cell.x()), logicalColumnIndex(cell.y())) :
+ QPoint(logicalColumnIndex(cell.x()), logicalRowIndex(cell.y())));
QObject* object = model->object(modelIndex, incubationMode);
if (!object) {
@@ -2661,21 +2960,6 @@ qreal QQuickTableViewPrivate::sizeHintForRow(int row) const
return rowHeight;
}
-void QQuickTableViewPrivate::updateTableSize()
-{
- // tableSize is the same as row and column count, and will always
- // be the same as the number of rows and columns in the model.
- Q_Q(QQuickTableView);
-
- const QSize prevTableSize = tableSize;
- tableSize = calculateTableSize();
-
- if (prevTableSize.width() != tableSize.width())
- emit q->columnsChanged();
- if (prevTableSize.height() != tableSize.height())
- emit q->rowsChanged();
-}
-
QSize QQuickTableViewPrivate::calculateTableSize()
{
QSize size(0, 0);
@@ -2793,7 +3077,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
const int noExplicitColumnWidth = -1;
- if (cachedColumnWidth.startIndex == column)
+ if (cachedColumnWidth.startIndex == logicalColumnIndex(column))
return cachedColumnWidth.size;
if (syncHorizontally)
@@ -2812,7 +3096,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
qreal columnWidth = noExplicitColumnWidth;
if (columnWidthProvider.isCallable()) {
- auto const columnAsArgument = QJSValueList() << QJSValue(column);
+ auto const columnAsArgument = QJSValueList() << QJSValue(logicalColumnIndex(column));
columnWidth = columnWidthProvider.call(columnAsArgument).toNumber();
if (qIsNaN(columnWidth) || columnWidth < 0)
columnWidth = noExplicitColumnWidth;
@@ -2824,7 +3108,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
columnWidth = noExplicitColumnWidth;
}
- cachedColumnWidth.startIndex = column;
+ cachedColumnWidth.startIndex = logicalColumnIndex(column);
cachedColumnWidth.size = columnWidth;
return columnWidth;
}
@@ -2839,7 +3123,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
const int noExplicitRowHeight = -1;
- if (cachedRowHeight.startIndex == row)
+ if (cachedRowHeight.startIndex == logicalRowIndex(row))
return cachedRowHeight.size;
if (syncVertically)
@@ -2858,7 +3142,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
qreal rowHeight = noExplicitRowHeight;
if (rowHeightProvider.isCallable()) {
- auto const rowAsArgument = QJSValueList() << QJSValue(row);
+ auto const rowAsArgument = QJSValueList() << QJSValue(logicalRowIndex(row));
rowHeight = rowHeightProvider.call(rowAsArgument).toNumber();
if (qIsNaN(rowHeight) || rowHeight < 0)
rowHeight = noExplicitRowHeight;
@@ -2870,7 +3154,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
rowHeight = noExplicitRowHeight;
}
- cachedRowHeight.startIndex = row;
+ cachedRowHeight.startIndex = logicalRowIndex(row);
cachedRowHeight.size = rowHeight;
return rowHeight;
}
@@ -3206,18 +3490,8 @@ void QQuickTableViewPrivate::processRebuildTable()
Q_Q(QQuickTableView);
if (rebuildState == RebuildState::Begin) {
- if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
- qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q;
- if (rebuildOptions & RebuildOption::All)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All, options:" << rebuildOptions;
- else if (rebuildOptions & RebuildOption::ViewportOnly)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly, options:" << rebuildOptions;
- else if (rebuildOptions & RebuildOption::LayoutOnly)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::LayoutOnly, options:" << rebuildOptions;
- else
- Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
- }
-
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q << "options:" << rebuildOptions;
+ tableSizeBeforeRebuild = tableSize;
edgesBeforeRebuild = loadedItems.isEmpty() ? QMargins()
: QMargins(q->leftColumn(), q->topRow(), q->rightColumn(), q->bottomRow());
}
@@ -3286,6 +3560,10 @@ void QQuickTableViewPrivate::processRebuildTable()
}
if (rebuildState == RebuildState::Done) {
+ if (tableSizeBeforeRebuild.width() != tableSize.width())
+ emit q->columnsChanged();
+ if (tableSizeBeforeRebuild.height() != tableSize.height())
+ emit q->rowsChanged();
if (edgesBeforeRebuild.left() != q->leftColumn())
emit q->leftColumnChanged();
if (edgesBeforeRebuild.right() != q->rightColumn())
@@ -3324,7 +3602,7 @@ bool QQuickTableViewPrivate::moveToNextRebuildState()
else
rebuildState = RebuildState(int(rebuildState) + 1);
- qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ qCDebug(lcTableViewDelegateLifecycle()) << rebuildState;
return true;
}
@@ -3435,7 +3713,7 @@ void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeftCell, QPointF &topL
void QQuickTableViewPrivate::loadInitialTable()
{
- updateTableSize();
+ tableSize = calculateTableSize();
if (positionXAnimation.isRunning()) {
positionXAnimation.stop();
@@ -3941,10 +4219,11 @@ bool QQuickTableViewPrivate::currentInSelectionModel(const QPoint &cell) const
void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected)
{
- if (!selectionModel->hasSelection()) {
- // Ensure that we cancel any ongoing key/mouse-based selections
- // if selectionModel.clearSelection() is called.
- clearSelection();
+ if (!inSelectionModelUpdate) {
+ // The selection model was manipulated outside of TableView
+ // and SelectionRectangle. In that case we cancel any ongoing
+ // selection tracking.
+ cancelSelectionTracking();
}
const auto &selectedIndexes = selected.indexes();
@@ -3957,6 +4236,13 @@ void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelecti
void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select)
{
+ if (modelIndex.isValid() && modelIndex.model() != selectionSourceModel()) {
+ qmlWarning(q_func())
+ << "Cannot select cells: TableView.selectionModel.model is not "
+ << "compatible with the model displayed in the view";
+ return;
+ }
+
const int cellIndex = modelIndexToCellIndex(modelIndex);
if (!loadedItems.contains(cellIndex))
return;
@@ -3965,6 +4251,24 @@ void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelI
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(select), cellIndex, item, false);
}
+QAbstractItemModel *QQuickTableViewPrivate::selectionSourceModel()
+{
+ // TableView.selectionModel.model should always be the same as TableView.model.
+ // After all, when the user selects an index in the view, the same index should
+ // be selected in the selection model. We therefore set the model in
+ // selectionModel.model automatically.
+ // But it's not always the case that the model shown in the view is the same
+ // as TableView.model. Subclasses with a proxy model will instead show the
+ // proxy model (e.g TreeView and HeaderView). And then it's no longer clear if
+ // we should use the proxy model or the TableView.model as source model in
+ // TableView.selectionModel. It's up to the subclass. But in short, if the proxy
+ // model shares the same model items as TableView.model (just with e.g a filter
+ // applied, or sorted etc), then TableView.model should be used. If the proxy
+ // model is a completely different model that shares no model items with
+ // TableView.model, then the proxy model should be used (e.g HeaderView).
+ return qaim(modelImpl());
+}
+
QAbstractItemModel *QQuickTableViewPrivate::qaim(QVariant modelAsVariant) const
{
// If modelAsVariant wraps a qaim, return it
@@ -3992,11 +4296,12 @@ void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems()
void QQuickTableViewPrivate::currentChangedInSelectionModel(const QModelIndex &current, const QModelIndex &previous)
{
- // Warn if the source models are not the same
- const QAbstractItemModel *qaimInSelection = selectionModel ? selectionModel->model() : nullptr;
- const QAbstractItemModel *qaimInTableView = qaim(modelImpl());
- if (qaimInSelection && qaimInSelection != qaimInTableView)
- qmlWarning(q_func()) << "TableView.selectionModel.model differs from TableView.model";
+ if (current.isValid() && current.model() != selectionSourceModel()) {
+ qmlWarning(q_func())
+ << "Cannot change current index: TableView.selectionModel.model is not "
+ << "compatible with the model displayed in the view";
+ return;
+ }
updateCurrentRowAndColumn();
setCurrentOnDelegateItem(previous, false);
@@ -4060,8 +4365,9 @@ void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
item->setZ(1);
const QPoint cell = cellAtModelIndex(modelIndex);
- const bool current = currentInSelectionModel(cell);
- const bool selected = selectedInSelectionModel(cell);
+ const QPoint visualCell = QPoint(visualColumnIndex(cell.x()), visualRowIndex(cell.y()));
+ const bool current = currentInSelectionModel(visualCell);
+ const bool selected = selectedInSelectionModel(visualCell);
setRequiredProperty(kRequiredProperty_current, QVariant::fromValue(current), modelIndex, object, true);
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, true);
setRequiredProperty(kRequiredProperty_editing, QVariant::fromValue(false), modelIndex, item, true);
@@ -4081,8 +4387,9 @@ void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
{
const QPoint cell = cellAtModelIndex(modelIndex);
- const bool current = currentInSelectionModel(cell);
- const bool selected = selectedInSelectionModel(cell);
+ const QPoint visualCell = QPoint(visualColumnIndex(cell.x()), visualRowIndex(cell.y()));
+ const bool current = currentInSelectionModel(visualCell);
+ const bool selected = selectedInSelectionModel(visualCell);
setRequiredProperty(kRequiredProperty_current, QVariant::fromValue(current), modelIndex, object, false);
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, false);
// Note: the edit item will never be reused, so no reason to set kRequiredProperty_editing
@@ -4159,9 +4466,6 @@ QVariant QQuickTableViewPrivate::modelImpl() const
void QQuickTableViewPrivate::setModelImpl(const QVariant &newModel)
{
- if (newModel == assignedModel)
- return;
-
assignedModel = newModel;
scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
emit q_func()->modelChanged();
@@ -4169,7 +4473,7 @@ void QQuickTableViewPrivate::setModelImpl(const QVariant &newModel)
void QQuickTableViewPrivate::syncModel()
{
- if (modelVariant == assignedModel)
+ if (compareModel(modelVariant, assignedModel))
return;
if (model) {
@@ -4445,6 +4749,13 @@ void QQuickTableViewPrivate::modelResetCallback()
scheduleRebuildTable(RebuildOption::All);
}
+bool QQuickTableViewPrivate::compareModel(const QVariant& model1, const QVariant& model2) const
+{
+ return (model1 == model2 ||
+ (model1.userType() == qMetaTypeId<QJSValue>() && model2.userType() == qMetaTypeId<QJSValue>() &&
+ model1.value<QJSValue>().strictlyEquals(model2.value<QJSValue>())));
+}
+
void QQuickTableViewPrivate::positionViewAtRow(int row, Qt::Alignment alignment, qreal offset, const QRectF subRect)
{
Qt::Alignment verticalAlignment = alignment & (Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom);
@@ -4660,7 +4971,7 @@ void QQuickTableViewPrivate::init()
positionYAnimation.setProperty(QStringLiteral("contentY"));
positionYAnimation.setEasing(QEasingCurve::OutQuart);
- auto tapHandler = new QQuickTapHandler(q->contentItem());
+ auto tapHandler = new QQuickTableViewTapHandler(q);
hoverHandler = new QQuickTableViewHoverHandler(q);
resizeHandler = new QQuickTableViewResizeHandler(q);
@@ -4724,24 +5035,33 @@ void QQuickTableViewPrivate::handleTap(const QQuickHandlerPoint &point)
if (resizeHandler->state() != QQuickTableViewResizeHandler::Listening)
return;
- QModelIndex prevIndex;
- if (selectionModel) {
- prevIndex = selectionModel->currentIndex();
- if (pointerNavigationEnabled) {
- clearSelection();
- setCurrentIndexFromTap(point.position());
- }
- }
+ const QModelIndex tappedIndex = q->modelIndex(q->cellAtPosition(point.position()));
+ bool tappedCellIsSelected = false;
- if (editTriggers != QQuickTableView::NoEditTriggers)
- q->closeEditor();
+ if (selectionModel)
+ tappedCellIsSelected = selectionModel->isSelected(tappedIndex);
- const QModelIndex tappedIndex = q->modelIndex(q->cellAtPosition(point.position()));
if (canEdit(tappedIndex, false)) {
- if (editTriggers & QQuickTableView::SingleTapped)
+ if (editTriggers & QQuickTableView::SingleTapped) {
+ if (selectionBehavior != QQuickTableView::SelectionDisabled)
+ clearSelection();
q->edit(tappedIndex);
- else if ((editTriggers & QQuickTableView::SelectedTapped) && tappedIndex == prevIndex)
+ return;
+ } else if (editTriggers & QQuickTableView::SelectedTapped && tappedCellIsSelected) {
q->edit(tappedIndex);
+ return;
+ }
+ }
+
+ // Since the tap didn't result in selecting or editing cells, we clear
+ // the current selection and move the current index instead.
+ if (pointerNavigationEnabled) {
+ q->closeEditor();
+ if (selectionBehavior != QQuickTableView::SelectionDisabled) {
+ clearSelection();
+ cancelSelectionTracking();
+ }
+ setCurrentIndexFromTap(point.position());
}
}
@@ -4840,7 +5160,6 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
const QModelIndex currentIndex = selectionModel->currentIndex();
const QPoint currentCell = q->cellAtIndex(currentIndex);
- const bool select = (e->modifiers() & Qt::ShiftModifier) && (e->key() != Qt::Key_Backtab);
if (!q->activeFocusOnTab()) {
switch (e->key()) {
@@ -4864,7 +5183,7 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
case Qt::Key_Backtab:
// Special case: the current index doesn't map to a cell in the view (perhaps
// because it isn't set yet). In that case, we set it to be the top-left cell.
- const QModelIndex topLeftIndex = q->modelIndex(topRow(), leftColumn());
+ const QModelIndex topLeftIndex = q->index(topRow(), leftColumn());
selectionModel->setCurrentIndex(topLeftIndex, QItemSelectionModel::NoUpdate);
return true;
}
@@ -4872,25 +5191,38 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
}
auto beginMoveCurrentIndex = [&](){
- if (!select) {
+ const bool shouldSelect = (e->modifiers() & Qt::ShiftModifier) && (e->key() != Qt::Key_Backtab);
+ const bool startNewSelection = selectionRectangle().isEmpty();
+ if (!shouldSelect) {
clearSelection();
- } else if (selectionRectangle().isEmpty()) {
+ cancelSelectionTracking();
+ } else if (startNewSelection) {
+ // Try to start a new selection if no selection exists from before.
+ // The startSelection() call is theoretically allowed to refuse, although this
+ // is less likely when starting a selection using the keyboard.
const int serializedStartIndex = modelIndexToCellIndex(selectionModel->currentIndex());
if (loadedItems.contains(serializedStartIndex)) {
const QRectF startGeometry = loadedItems.value(serializedStartIndex)->geometry();
- setSelectionStartPos(startGeometry.center());
+ if (startSelection(startGeometry.center(), Qt::ShiftModifier)) {
+ setSelectionStartPos(startGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
+ }
}
}
};
auto endMoveCurrentIndex = [&](const QPoint &cell){
- if (select) {
+ const bool isSelecting = selectionFlag != QItemSelectionModel::NoUpdate;
+ if (isSelecting) {
if (polishScheduled)
forceLayout(true);
const int serializedEndIndex = modelIndexAtCell(cell);
if (loadedItems.contains(serializedEndIndex)) {
const QRectF endGeometry = loadedItems.value(serializedEndIndex)->geometry();
setSelectionEndPos(endGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
}
}
selectionModel->setCurrentIndex(q->modelIndex(cell), QItemSelectionModel::NoUpdate);
@@ -5170,6 +5502,14 @@ QQuickTableView::QQuickTableView(QQuickTableViewPrivate &dd, QQuickItem *parent)
QQuickTableView::~QQuickTableView()
{
+ Q_D(QQuickTableView);
+
+ if (d->syncView) {
+ // Remove this TableView as a sync child from the syncView
+ auto syncView_d = d->syncView->d_func();
+ syncView_d->syncChildren.removeOne(this);
+ syncView_d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ }
}
void QQuickTableView::componentFinalized()
@@ -5301,12 +5641,13 @@ QVariant QQuickTableView::model() const
void QQuickTableView::setModel(const QVariant &newModel)
{
Q_D(QQuickTableView);
+ if (d->compareModel(newModel, d->assignedModel))
+ return;
closeEditor();
d->setModelImpl(newModel);
-
if (d->selectionModel)
- d->selectionModel->setModel(d->qaim(newModel));
+ d->selectionModel->setModel(d->selectionSourceModel());
}
QQmlComponent *QQuickTableView::delegate() const
@@ -5406,6 +5747,10 @@ void QQuickTableView::setSyncView(QQuickTableView *view)
if (d->assignedSyncView == view)
return;
+ // Clear existing index mapping information maintained
+ // in the current view
+ d->clearIndexMapping();
+
d->assignedSyncView = view;
d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
@@ -5473,7 +5818,7 @@ void QQuickTableView::setSelectionModel(QItemSelectionModel *selectionModel)
d->selectionModel = selectionModel;
if (d->selectionModel) {
- d->selectionModel->setModel(d->qaim(d->modelImpl()));
+ d->selectionModel->setModel(d->selectionSourceModel());
QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::selectionChanged,
d, &QQuickTableViewPrivate::selectionChangedInSelectionModel);
QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::currentChanged,
@@ -5574,7 +5919,7 @@ int QQuickTableView::currentColumn() const
void QQuickTableView::positionViewAtRow(int row, PositionMode mode, qreal offset, const QRectF &subRect)
{
Q_D(QQuickTableView);
- if (row < 0 || row >= rows())
+ if (row < 0 || row >= rows() || d->loadedRows.isEmpty())
return;
// Note: PositionMode::Contain is from here on translated to (Qt::AlignTop | Qt::AlignBottom).
@@ -5640,7 +5985,7 @@ void QQuickTableView::positionViewAtRow(int row, PositionMode mode, qreal offset
void QQuickTableView::positionViewAtColumn(int column, PositionMode mode, qreal offset, const QRectF &subRect)
{
Q_D(QQuickTableView);
- if (column < 0 || column >= columns())
+ if (column < 0 || column >= columns() || d->loadedColumns.isEmpty())
return;
// Note: PositionMode::Contain is from here on translated to (Qt::AlignLeft | Qt::AlignRight).
@@ -5705,9 +6050,35 @@ void QQuickTableView::positionViewAtColumn(int column, PositionMode mode, qreal
void QQuickTableView::positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset, const QRectF &subRect)
{
- positionViewAtCell(cell.x(), cell.y(), mode, offset, subRect);
+ PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
+ PositionMode verticalMode = mode & ~(AlignLeft | AlignRight | AlignHCenter);
+ if (!horizontalMode && !verticalMode) {
+ qmlWarning(this) << "Unsupported mode:" << int(mode);
+ return;
+ }
+
+ if (horizontalMode)
+ positionViewAtColumn(cell.x(), horizontalMode, offset.x(), subRect);
+ if (verticalMode)
+ positionViewAtRow(cell.y(), verticalMode, offset.y(), subRect);
}
+void QQuickTableView::positionViewAtIndex(const QModelIndex &index, PositionMode mode, const QPointF &offset, const QRectF &subRect)
+{
+ PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
+ PositionMode verticalMode = mode & ~(AlignLeft | AlignRight | AlignHCenter);
+ if (!horizontalMode && !verticalMode) {
+ qmlWarning(this) << "Unsupported mode:" << int(mode);
+ return;
+ }
+
+ if (horizontalMode)
+ positionViewAtColumn(columnAtIndex(index), horizontalMode, offset.x(), subRect);
+ if (verticalMode)
+ positionViewAtRow(rowAtIndex(index), verticalMode, offset.y(), subRect);
+}
+
+#if QT_DEPRECATED_SINCE(6, 5)
void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset, const QRectF &subRect)
{
PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
@@ -5722,6 +6093,157 @@ void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode,
if (verticalMode)
positionViewAtRow(row, verticalMode, offset.y(), subRect);
}
+#endif
+
+void QQuickTableView::moveColumn(int source, int destination)
+{
+ Q_D(QQuickTableView);
+ d->moveSection(source, destination, Qt::Horizontal);
+}
+
+void QQuickTableView::moveRow(int source, int destination)
+{
+ Q_D(QQuickTableView);
+ d->moveSection(source, destination, Qt::Vertical);
+}
+
+void QQuickTableViewPrivate::moveSection(int source, int destination, Qt::Orientations orientation)
+{
+ Q_Q(QQuickTableView);
+
+ if (source < 0 || destination < 0 ||
+ (orientation == Qt::Horizontal &&
+ (source >= tableSize.width() || destination >= tableSize.width())) ||
+ (orientation == Qt::Vertical &&
+ (source >= tableSize.height() || destination >= tableSize.height())))
+ return;
+
+ if (source == destination)
+ return;
+
+ if (m_sectionState != SectionState::Moving) {
+ m_sectionState = SectionState::Moving;
+ if (syncView)
+ syncView->d_func()->moveSection(source, destination, orientation);
+ else {
+ // Initialize the visual and logical index mapping
+ initializeIndexMapping();
+
+ // Set current index mapping according to moving rows or columns
+ SectionData *visualIndex = nullptr;
+ SectionData *logicalIndex = nullptr;
+
+ if (orientation == Qt::Horizontal) {
+ visualIndex = visualIndices[0].data();
+ logicalIndex = logicalIndices[0].data();
+ } else if (orientation == Qt::Vertical) {
+ visualIndex = visualIndices[1].data();
+ logicalIndex = logicalIndices[1].data();
+ }
+
+ const int logical = logicalIndex[source].index;
+ int visual = source;
+
+ if (destination > source) {
+ while (visual < destination) {
+ SectionData &visualData = visualIndex[logicalIndex[visual + 1].index];
+ SectionData &logicalData = logicalIndex[visual];
+ visualData.prevIndex = visualData.index;
+ visualData.index = visual;
+ logicalData.prevIndex = logicalData.index;
+ logicalData.index = logicalIndex[visual + 1].index;
+ ++visual;
+ }
+ } else {
+ while (visual > destination) {
+ SectionData &visualData = visualIndex[logicalIndex[visual - 1].index];
+ SectionData &logicalData = logicalIndex[visual];
+ visualData.prevIndex = visualData.index;
+ visualData.index = visual;
+ logicalData.prevIndex = logicalData.index;
+ logicalData.index = logicalIndex[visual - 1].index;
+ --visual;
+ }
+ }
+
+ visualIndex[logical].prevIndex = visualIndex[logical].index;
+ visualIndex[logical].index = destination;
+ logicalIndex[destination].prevIndex = logicalIndex[destination].index;
+ logicalIndex[destination].index = logical;
+
+ // Trigger section move for horizontal and vertical child views
+ // Used in a case where moveSection() triggered for table view
+ for (auto syncChild : std::as_const(syncChildren)) {
+ auto syncChild_d = syncChild->d_func();
+ if (syncChild_d->m_sectionState != SectionState::Moving &&
+ ((syncChild_d->syncHorizontally && orientation == Qt::Horizontal) ||
+ (syncChild_d->syncVertically && orientation == Qt::Vertical)))
+ syncChild_d->moveSection(source, destination, orientation);
+ }
+ }
+
+ // Rebuild the view to reflect the section order
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+ m_sectionState = SectionState::Idle;
+
+ // Emit section moved signal for the sections moved in the view
+ const int startIndex = (source > destination) ? destination : source;
+ const int endIndex = (source > destination) ? source : destination;
+ const int mapIndex = static_cast<int>(orientation) - 1;
+ for (int index = startIndex; index <= endIndex; index++) {
+ const SectionData *logicalDataIndices = (syncView ? syncView->d_func()->logicalIndices[mapIndex].constData() : logicalIndices[mapIndex].constData());
+ const SectionData *visualDataIndices = syncView ? syncView->d_func()->visualIndices[mapIndex].constData() : visualIndices[mapIndex].constData();
+ const int prevLogicalIndex = logicalDataIndices[index].prevIndex;
+ if (orientation == Qt::Horizontal)
+ emit q->columnMoved(prevLogicalIndex, visualDataIndices[prevLogicalIndex].prevIndex, visualDataIndices[prevLogicalIndex].index);
+ else
+ emit q->rowMoved(prevLogicalIndex, visualDataIndices[prevLogicalIndex].prevIndex, visualDataIndices[prevLogicalIndex].index);
+ }
+ }
+}
+
+void QQuickTableView::clearColumnReordering()
+{
+ Q_D(QQuickTableView);
+ d->clearSection(Qt::Horizontal);
+}
+
+void QQuickTableView::clearRowReordering()
+{
+ Q_D(QQuickTableView);
+ d->clearSection(Qt::Vertical);
+}
+
+void QQuickTableViewPrivate::clearSection(Qt::Orientations orientation)
+{
+ Q_Q(QQuickTableView);
+
+ const int mapIndex = static_cast<int>(orientation) - 1;
+ const QList<SectionData> oldLogicalIndices = syncView ? syncView->d_func()->logicalIndices[mapIndex] : logicalIndices[mapIndex];
+ const QList<SectionData> oldVisualIndices = syncView ? syncView->d_func()->visualIndices[mapIndex] : visualIndices[mapIndex];;
+
+ if (syncView)
+ syncView->d_func()->clearSection(orientation);
+ else {
+ // Clear the index mapping and rebuild the table
+ logicalIndices[mapIndex].clear();
+ visualIndices[mapIndex].clear();
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+ }
+
+ // Emit section moved signal for the sections moved in the view
+ for (int index = 0; index < oldLogicalIndices.size(); index++) {
+ const SectionData *logicalDataIndices = oldLogicalIndices.constData();
+ const SectionData *visualDataIndices = oldVisualIndices.constData();
+ if (logicalDataIndices[index].index != index) {
+ const int currentIndex = logicalDataIndices[index].index;
+ if (orientation == Qt::Horizontal)
+ emit q->columnMoved(currentIndex, visualDataIndices[currentIndex].index, index);
+ else
+ emit q->rowMoved(currentIndex, visualDataIndices[currentIndex].index, index);
+ }
+ }
+}
QQuickItem *QQuickTableView::itemAtCell(const QPoint &cell) const
{
@@ -5732,10 +6254,21 @@ QQuickItem *QQuickTableView::itemAtCell(const QPoint &cell) const
return d->loadedItems.value(modelIndex)->item;
}
+#if QT_DEPRECATED_SINCE(6, 5)
QQuickItem *QQuickTableView::itemAtCell(int column, int row) const
{
return itemAtCell(QPoint(column, row));
}
+#endif
+
+QQuickItem *QQuickTableView::itemAtIndex(const QModelIndex &index) const
+{
+ Q_D(const QQuickTableView);
+ const int serializedIndex = d->modelIndexToCellIndex(index);
+ if (!d->loadedItems.contains(serializedIndex))
+ return nullptr;
+ return d->loadedItems.value(serializedIndex)->item;
+}
#if QT_DEPRECATED_SINCE(6, 4)
QPoint QQuickTableView::cellAtPos(qreal x, qreal y, bool includeSpacing) const
@@ -5890,9 +6423,9 @@ void QQuickTableView::setColumnWidth(int column, qreal size)
return;
if (size < 0)
- d->explicitColumnWidths.remove(column);
+ d->explicitColumnWidths.remove(d->logicalColumnIndex(column));
else
- d->explicitColumnWidths.insert(column, size);
+ d->explicitColumnWidths.insert(d->logicalColumnIndex(column), size);
if (d->loadedItems.isEmpty())
return;
@@ -5925,7 +6458,7 @@ qreal QQuickTableView::explicitColumnWidth(int column) const
if (d->syncHorizontally)
return d->syncView->explicitColumnWidth(column);
- const auto it = d->explicitColumnWidths.constFind(column);
+ const auto it = d->explicitColumnWidths.constFind(d->logicalColumnIndex(column));
if (it != d->explicitColumnWidths.constEnd())
return *it;
return -1;
@@ -5948,9 +6481,9 @@ void QQuickTableView::setRowHeight(int row, qreal size)
return;
if (size < 0)
- d->explicitRowHeights.remove(row);
+ d->explicitRowHeights.remove(d->logicalRowIndex(row));
else
- d->explicitRowHeights.insert(row, size);
+ d->explicitRowHeights.insert(d->logicalRowIndex(row), size);
if (d->loadedItems.isEmpty())
return;
@@ -5983,7 +6516,7 @@ qreal QQuickTableView::explicitRowHeight(int row) const
if (d->syncVertically)
return d->syncView->explicitRowHeight(row);
- const auto it = d->explicitRowHeights.constFind(row);
+ const auto it = d->explicitRowHeights.constFind(d->logicalRowIndex(row));
if (it != d->explicitRowHeights.constEnd())
return *it;
return -1;
@@ -5999,16 +6532,18 @@ QModelIndex QQuickTableView::modelIndex(const QPoint &cell) const
if (!qaim)
return {};
- return qaim->index(cell.y(), cell.x());
+ return qaim->index(d->logicalRowIndex(cell.y()), d->logicalColumnIndex(cell.x()));
}
QPoint QQuickTableView::cellAtIndex(const QModelIndex &index) const
{
if (!index.isValid() || index.parent().isValid())
return {-1, -1};
- return {index.column(), index.row()};
+ Q_D(const QQuickTableView);
+ return {d->visualColumnIndex(index.column()), d->visualRowIndex(index.row())};
}
+#if QT_DEPRECATED_SINCE(6, 4)
QModelIndex QQuickTableView::modelIndex(int row, int column) const
{
static bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
@@ -6019,9 +6554,18 @@ QModelIndex QQuickTableView::modelIndex(int row, int column) const
// to continue accepting calls to modelIndex(column, row).
return modelIndex({row, column});
} else {
+ qmlWarning(this) << "modelIndex(row, column) is deprecated. "
+ "Use index(row, column) instead. For more information, see "
+ "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
return modelIndex({column, row});
}
}
+#endif
+
+QModelIndex QQuickTableView::index(int row, int column) const
+{
+ return modelIndex({column, row});
+}
int QQuickTableView::rowAtIndex(const QModelIndex &index) const
{
@@ -6061,7 +6605,8 @@ void QQuickTableView::edit(const QModelIndex &index)
// is currently dependent of the QQmlTableInstanceModel that was used to create the object
// in order to initialize required properties, so we need to set the editItem variable
// early on, so that we can use it in setRequiredProperty.
- d->editIndex = modelIndex(d->cellAtModelIndex(serializedModelIndex));
+ const QPoint cell = d->cellAtModelIndex(serializedModelIndex);
+ d->editIndex = modelIndex({d->visualColumnIndex(cell.x()), d->visualRowIndex(cell.y())});
d->editItem = qmlobject_cast<QQuickItem*>(object);
if (!d->editItem)
return;
@@ -6090,7 +6635,7 @@ void QQuickTableView::edit(const QModelIndex &index)
d->editModel->setModel(d->tableModel->model());
d->editModel->setDelegate(attached->editDelegate());
- const int cellIndex = d->modelIndexToCellIndex(index);
+ const int cellIndex = d->modelIndexToCellIndex(index, false);
QObject* object = d->editModel->object(cellIndex, QQmlIncubator::Synchronous);
if (!object) {
d->editIndex = QModelIndex();
@@ -6141,7 +6686,7 @@ void QQuickTableView::closeEditor()
d->editItem = nullptr;
cellItem->setZ(1);
- const int cellIndex = d->modelIndexToCellIndex(d->editIndex);
+ const int cellIndex = d->modelIndexToCellIndex(d->editIndex, false);
d->setRequiredProperty(kRequiredProperty_editing, QVariant::fromValue(false), cellIndex, cellItem, false);
// Remove the extra reference we sat on the cell item from edit()
d->model->release(cellItem, QQmlInstanceModel::NotReusable);
@@ -6307,6 +6852,21 @@ void QQuickTableView::setSelectionBehavior(SelectionBehavior selectionBehavior)
emit selectionBehaviorChanged();
}
+QQuickTableView::SelectionMode QQuickTableView::selectionMode() const
+{
+ return d_func()->selectionMode;
+}
+
+void QQuickTableView::setSelectionMode(SelectionMode selectionMode)
+{
+ Q_D(QQuickTableView);
+ if (d->selectionMode == selectionMode)
+ return;
+
+ d->selectionMode = selectionMode;
+ emit selectionModeChanged();
+}
+
bool QQuickTableView::resizableColumns() const
{
return d_func()->resizableColumns;
@@ -6344,7 +6904,6 @@ void QQuickTableView::setResizableRows(bool enabled)
}
// ----------------------------------------------
-
QQuickTableViewHoverHandler::QQuickTableViewHoverHandler(QQuickTableView *view)
: QQuickHoverHandler(view->contentItem())
{
@@ -6402,6 +6961,7 @@ QQuickTableViewResizeHandler::QQuickTableViewResizeHandler(QQuickTableView *view
// Set a grab permission that stops the flickable, as well as
// any drag handler inside the delegate, from stealing the drag.
setGrabPermissions(QQuickPointerHandler::CanTakeOverFromAnything);
+ setObjectName("tableViewResizeHandler");
}
void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
@@ -6430,8 +6990,13 @@ void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
bool QQuickTableViewResizeHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
{
- Q_UNUSED(event);
- Q_UNUSED(point);
+ if (!QQuickSinglePointHandler::wantsEventPoint(event, point))
+ return false;
+
+ // If we have a mouse wheel event then we do not want to do anything related to resizing.
+ if (event->type() == QEvent::Type::Wheel)
+ return false;
+
// When the user is flicking, we disable resizing, so that
// he doesn't start to resize by accident.
auto tableView = static_cast<QQuickTableView *>(parentItem()->parent());
@@ -6514,7 +7079,7 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
#if QT_CONFIG(cursor)
tableViewPrivate->updateCursor();
#endif
- // fallthrough
+ Q_FALLTHROUGH();
case Dragging: {
const qreal distX = point.position().x() - m_columnStartX;
const qreal distY = point.position().y() - m_rowStartY;
@@ -6532,6 +7097,86 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
}
}
+void QQuickTableViewPrivate::initializeIndexMapping()
+{
+ auto initIndices = [](auto& visualIndex, auto& logicalIndex, int size) {
+ visualIndex.resize(size);
+ logicalIndex.resize(size);
+ for (int index = 0; index < size; ++index)
+ visualIndex[index].index = logicalIndex[index].index = index;
+ };
+
+ if (!tableSize.isEmpty()) {
+ if (visualIndices[0].size() != tableSize.width()
+ || logicalIndices[0].size() != tableSize.width())
+ initIndices(visualIndices[0], logicalIndices[0], tableSize.width());
+
+ if (visualIndices[1].size() != tableSize.height()
+ || logicalIndices[1].size() != tableSize.height())
+ initIndices(visualIndices[1], logicalIndices[1], tableSize.height());
+ }
+}
+
+void QQuickTableViewPrivate::clearIndexMapping()
+{
+ logicalIndices[0].clear();
+ visualIndices[0].clear();
+
+ logicalIndices[1].clear();
+ visualIndices[1].clear();
+}
+
+int QQuickTableViewPrivate::logicalRowIndex(const int visualIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->logicalRowIndex(visualIndex);
+ if (logicalIndices[1].isEmpty() || visualIndex < 0)
+ return visualIndex;
+ return logicalIndices[1].constData()[visualIndex].index;
+}
+
+int QQuickTableViewPrivate::logicalColumnIndex(const int visualIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->logicalColumnIndex(visualIndex);
+ if (logicalIndices[0].isEmpty() || visualIndex < 0)
+ return visualIndex;
+ return logicalIndices[0].constData()[visualIndex].index;
+}
+
+int QQuickTableViewPrivate::visualRowIndex(const int logicalIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->visualRowIndex(logicalIndex);
+ if (visualIndices[1].isEmpty() || logicalIndex < 0)
+ return logicalIndex;
+ return visualIndices[1].constData()[logicalIndex].index;
+}
+
+int QQuickTableViewPrivate::visualColumnIndex(const int logicalIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->visualColumnIndex(logicalIndex);
+ if (visualIndices[0].isEmpty() || logicalIndex < 0)
+ return logicalIndex;
+ return visualIndices[0].constData()[logicalIndex].index;
+}
+
+// ----------------------------------------------
+
+QQuickTableViewTapHandler::QQuickTableViewTapHandler(QQuickTableView *view)
+ : QQuickTapHandler(view->contentItem())
+{
+ setObjectName("tableViewTapHandler");
+}
+
+bool QQuickTableViewTapHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
+{
+ auto tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ return tableViewPrivate->pointerNavigationEnabled && QQuickTapHandler::wantsEventPoint(event, point);
+}
+
QT_END_NAMESPACE
#include "moc_qquicktableview_p.cpp"