diff options
| -rw-r--r-- | src/controls/Private/BasicTableView.qml | 800 | ||||
| -rw-r--r-- | src/controls/Private/private.pri | 1 | ||||
| -rw-r--r-- | src/controls/Private/qmldir | 1 | ||||
| -rw-r--r-- | src/controls/TableView.qml | 1068 | ||||
| -rw-r--r-- | src/controls/TableViewColumn.qml | 21 | ||||
| -rw-r--r-- | tests/auto/controls/data/tableview/tv_keys.qml | 6 | ||||
| -rw-r--r-- | tests/auto/controls/data/tst_tableview.qml | 4 |
7 files changed, 1036 insertions, 865 deletions
diff --git a/src/controls/Private/BasicTableView.qml b/src/controls/Private/BasicTableView.qml new file mode 100644 index 000000000..cac673eb9 --- /dev/null +++ b/src/controls/Private/BasicTableView.qml @@ -0,0 +1,800 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Controls.Private 1.0 +import QtQuick.Controls.Styles 1.2 +import QtQuick.Window 2.2 + +/*! + \qmltype BasicTableView + \internal + \qmlabstract + \inqmlmodule QtQuick.Controls.Private +*/ + +ScrollView { + id: root + + /*! \qmlproperty bool BasicTableView::alternatingRowColors + + This property is set to \c true if the view alternates the row color. + The default value is \c true. + */ + property bool alternatingRowColors: true + + /*! \qmlproperty bool BasicTableView::headerVisible + + This property determines if the header is visible. + The default value is \c true. + */ + property bool headerVisible: true + + /*! \qmlproperty bool BasicTableView::backgroundVisible + + This property determines if the background should be filled or not. + + The default value is \c true. + + \note The rowDelegate is not affected by this property + */ + property alias backgroundVisible: colorRect.visible + + /*! \qmlproperty Component BasicTableView::itemDelegate + + This property defines a delegate to draw a specific cell. + + In the item delegate you have access to the following special properties: + \list + \li styleData.selected - if the item is currently selected + \li styleData.value - the value or text for this item + \li styleData.textColor - the default text color for an item + \li styleData.row - the index of the row + \li styleData.column - the index of the column + \li styleData.elideMode - the elide mode of the column + \li styleData.textAlignment - the horizontal text alignment of the column + \li styleData.pressed - true when the item is pressed (since QtQuick.Controls 1.3) + \li styleData.hasActiveFocus - true when the row has focus (since QtQuick.Controls 1.3) + \endlist + + Example: + \code + itemDelegate: Item { + Text { + anchors.verticalCenter: parent.verticalCenter + color: styleData.textColor + elide: styleData.elideMode + text: styleData.value + } + } + \endcode + + \note For performance reasons, created delegates can be recycled + across multiple table rows. This implies that when you make use of implicit + properties such as \c styleData.row or \c model, these values can change + after the delegate has been constructed. This means that you should not assume + that content is fixed when \c Component.onCompleted is called, but instead rely on + bindings to such properties. + */ + property Component itemDelegate: __style ? __style.itemDelegate : null + + /*! \qmlproperty Component BasicTableView::rowDelegate + + This property defines a delegate to draw a row. + + In the row delegate you have access to the following special properties: + \list + \li styleData.alternate - true when the row uses the alternate background color + \li styleData.selected - true when the row is currently selected + \li styleData.row - the index of the row + \li styleData.hasActiveFocus - true when the row has focus (since QtQuick.Controls 1.3) + \li styleData.pressed - true when the row is pressed (since QtQuick.Controls 1.3) + \endlist + + \note For performance reasons, created delegates can be recycled + across multiple table rows. This implies that when you make use of implicit + properties such as \c styleData.row or \c model, these values can change + after the delegate has been constructed. This means that you should not assume + that content is fixed when \c Component.onCompleted is called, but instead rely on + bindings to such properties. + */ + property Component rowDelegate: __style ? __style.rowDelegate : null + + /*! \qmlproperty Component BasicTableView::headerDelegate + + This property defines a delegate to draw a header. + + In the header delegate you have access to the following special properties: + \list + \li styleData.value - the value or text for this item + \li styleData.column - the index of the column + \li styleData.pressed - true when the column is being pressed + \li styleData.containsMouse - true when the column is under the mouse + \li styleData.textAlignment - the horizontal text alignment of the column (since QtQuickControls 1.1) + \endlist + */ + property Component headerDelegate: __style ? __style.headerDelegate : null + + /*! \qmlproperty int BasicTableView::sortIndicatorColumn + + Index of the current sort column. + The default value is \c {0}. + */ + property int sortIndicatorColumn + + /*! \qmlproperty bool BasicTableView::sortIndicatorVisible + + This property shows or hides the sort indicator + The default value is \c false. + \note The view itself does not sort the data. + */ + property bool sortIndicatorVisible: false + + /*! \qmlproperty enumeration BasicTableView::sortIndicatorOrder + + This sets the sorting order of the sort indicator + The allowed values are: + \list + \li Qt.AscendingOrder - the default + \li Qt.DescendingOrder + \endlist + */ + property int sortIndicatorOrder: Qt.AscendingOrder + + /*! \qmlproperty Component BasicTableView::contentHeader + This is the content header of the view. + */ + property alias contentHeader: listView.header + + /*! \qmlproperty Component BasicTableView::contentFooter + This is the content footer of the view. + */ + property alias contentFooter: listView.footer + + /*! \qmlproperty int BasicTableView::columnCount + The current number of columns + */ + readonly property alias columnCount: columnModel.count + + /*! \qmlproperty string BasicTableView::section.property + \qmlproperty enumeration BasicTableView::section.criteria + \qmlproperty Component BasicTableView::section.delegate + \qmlproperty enumeration BasicTableView::section.labelPositioning + + These properties determine the section labels. + \sa ListView::section + */ + property alias section: listView.section + + /*! + \qmlproperty enumeration BasicTableView::selectionMode + \since QtQuick.Controls 1.1 + + This enum indicates how the view responds to user selections: + + The possible modes are: + + \list + + \li SelectionMode.NoSelection - Items cannot be selected. + + \li SelectionMode.SingleSelection - When the user selects an item, + any already-selected item becomes unselected, and the user cannot + unselect the selected item. (Default) + + \li SelectionMode.MultiSelection - When the user selects an item in the usual way, + the selection status of that item is toggled and the other items are left alone. + + \li SelectionMode.ExtendedSelection - When the user selects an item in the usual way, + the selection is cleared and the new item selected. However, if the user presses the + Ctrl key when clicking on an item, the clicked item gets toggled and all other items + are left untouched. If the user presses the Shift key while clicking + on an item, all items between the current item and the clicked item are selected or unselected, + depending on the state of the clicked item. Multiple items can be selected by dragging the + mouse over them. + + \li SelectionMode.ContiguousSelection - When the user selects an item in the usual way, + the selection is cleared and the new item selected. However, if the user presses the Shift key while + clicking on an item, all items between the current item and the clicked item are selected. + + \endlist + */ + property int selectionMode: SelectionMode.SingleSelection + + /*! + \qmlmethod TableViewColumn BasicTableView::addColumn(object column) + + Adds a \a column and returns the added column. + + The \a column argument can be an instance of TableViewColumn, + or a Component. The component has to contain a TableViewColumn. + Otherwise \c null is returned. + */ + function addColumn(column) { + return insertColumn(columnCount, column) + } + + /*! + \qmlmethod TableViewColumn BasicTableView::insertColumn(int index, object column) + + Inserts a \a column at the given \a index and returns the inserted column. + + The \a column argument can be an instance of TableViewColumn, + or a Component. The component has to contain a TableViewColumn. + Otherwise \c null is returned. + */ + function insertColumn(index, column) { + var object = column + if (typeof column['createObject'] === 'function') + object = column.createObject(root) + + else if (object.__view) { + console.warn(__viewTypeName + "::insertColumn(): you cannot add a column to multiple views") + return null + } + if (index >= 0 && index <= columnCount && object.Accessible.role === Accessible.ColumnHeader) { + object.__view = root + columnModel.insert(index, {columnItem: object}) + return object + } + + if (object !== column) + object.destroy() + console.warn(__viewTypeName + "::insertColumn(): invalid argument") + return null + } + + /*! + \qmlmethod void BasicTableView::removeColumn(int index) + + Removes and destroys a column at the given \a index. + */ + function removeColumn(index) { + if (index < 0 || index >= columnCount) { + console.warn(__viewTypeName + "::removeColumn(): invalid argument") + return + } + var column = columnModel.get(index).columnItem + columnModel.remove(index, 1) + column.destroy() + } + + /*! + \qmlmethod void BasicTableView::moveColumn(int from, int to) + + Moves a column \a from index \a to another. + */ + function moveColumn(from, to) { + if (from < 0 || from >= columnCount || to < 0 || to >= columnCount) { + console.warn(__viewTypeName + "::moveColumn(): invalid argument") + return + } + columnModel.move(from, to, 1) + } + + /*! + \qmlmethod TableViewColumn BasicTableView::getColumn(int index) + + Returns the column at the given \a index + or \c null if the \a index is invalid. + */ + function getColumn(index) { + if (index < 0 || index >= columnCount) + return null + return columnModel.get(index).columnItem + } + + /*! + \qmlmethod void BasicTableView::resizeColumnsToContents() + + Resizes all columns to ensure that the column contents and the headers will fit. + \since QtQuick.Controls 1.2 + */ + function resizeColumnsToContents () { + for (var i = 0; i < __columns.length; ++i) { + var col = getColumn(i) + var header = repeater.itemAt(i) + if (col) { + col.__index = i + col.resizeToContents() + if (col.width < header.implicitWidth) + col.width = header.implicitWidth + } + } + } + + // Internal stuff. Do not look + + Component.onCompleted: { + for (var i = 0; i < __columns.length; ++i) { + var column = __columns[i] + if (column.Accessible.role === Accessible.ColumnHeader) + addColumn(column) + } + } + + implicitWidth: 200 + implicitHeight: 150 + + frameVisible: true + __scrollBarTopMargin: (__style && __style.transientScrollBars || Qt.platform.os === "osx") ? headerrow.height : 0 + __viewTopMargin: headerrow.height + + /*! \internal + Use this to display user-friendly messages in TableView and TreeView common functions. + */ + property string __viewTypeName + + /*! \internal */ + default property alias __columns: root.data + + /*! \internal */ + property alias __currentRowItem: listView.currentItem + + /*! \internal + This property is forwarded to TableView::currentRow, but not to any TreeView property. + */ + property alias __currentRow: listView.currentIndex + + /*! \internal */ + readonly property alias __listView: listView + + /*! \internal + Allows to override the model property in cases like TreeView, + where we want to use a proxy/adaptor model between the user's model + and whatever a ListView can swallow. + */ + property var __model + + /*! \internal */ + property bool __activateItemOnSingleClick: __style ? __style.activateItemOnSingleClick : false + + /*! \internal */ + property Item __mouseArea + + ListView { + id: listView + focus: true + activeFocusOnTab: false + Keys.forwardTo: [__mouseArea] + anchors.topMargin: headerVisible ? tableHeader.height : 0 + anchors.fill: parent + currentIndex: -1 + visible: columnCount > 0 + interactive: Settings.hasTouchScreen + property var rowItemStack: [] // Used as a cache for rowDelegates + + function incrementCurrentIndexBlocking() { + var oldIndex = __listView.currentIndex + __scroller.blockUpdates = true; + incrementCurrentIndex(); + __scroller.blockUpdates = false; + return oldIndex !== __listView.currentIndex + } + + function decrementCurrentIndexBlocking() { + var oldIndex = __listView.currentIndex + __scroller.blockUpdates = true; + decrementCurrentIndex(); + __scroller.blockUpdates = false; + return oldIndex !== __listView.currentIndex + } + + function scrollIfNeeded(key) { + var diff = key === Qt.Key_PageDown ? height : + key === Qt.Key_PageUp ? -height : 0 + if (diff !== 0) + __verticalScrollBar.value += diff + } + + SystemPalette { + id: palette + colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled + } + + Rectangle { + id: colorRect + parent: viewport + anchors.fill: parent + color: __style ? __style.backgroundColor : palette.base + z: -2 + } + + // Fills extra rows with alternate color + Column { + id: rowfiller + Loader { + id: rowSizeItem + sourceComponent: root.rowDelegate + visible: false + property QtObject styleData: QtObject { + property bool alternate: false + property bool selected: false + property bool hasActiveFocus: false + property bool pressed: false + } + } + property int rowHeight: rowSizeItem.implicitHeight + property int paddedRowCount: height/rowHeight + + y: listView.contentHeight - listView.contentY + listView.originY + width: parent.width + visible: alternatingRowColors + height: viewport.height - listView.contentHeight + Repeater { + model: visible ? parent.paddedRowCount : 0 + Loader { + width: rowfiller.width + height: rowfiller.rowHeight + sourceComponent: root.rowDelegate + property QtObject styleData: QtObject { + readonly property bool alternate: (index + __listView.count) % 2 === 1 + readonly property bool selected: false + readonly property bool hasActiveFocus: false + readonly property bool pressed: false + } + readonly property var model: null + readonly property var modelData: null + } + } + } + + ListModel { + id: columnModel + } + + highlightFollowsCurrentItem: true + model: root.__model + + delegate: FocusScope { + id: rowItemContainer + + activeFocusOnTab: false + z: rowItem.activeFocus ? 0.7 : rowItem.itemSelected ? 0.5 : 0 + + property Item rowItem + // We recycle instantiated row items to speed up list scrolling + + Component.onDestruction: { + // move the rowItem back in cache + if (rowItem) { + rowItem.visible = false; + rowItem.parent = null; + rowItem.rowIndex = -1; + listView.rowItemStack.push(rowItem); // return rowItem to cache + } + } + + Component.onCompleted: { + // retrieve row item from cache + if (listView.rowItemStack.length > 0) + rowItem = listView.rowItemStack.pop(); + else + rowItem = rowComponent.createObject(listView); + + // Bind container to item size + rowItemContainer.width = Qt.binding( function() { return rowItem.width }); + rowItemContainer.height = Qt.binding( function() { return rowItem.height }); + + // Reassign row-specific bindings + rowItem.rowIndex = Qt.binding( function() { return model.index }); + rowItem.itemModelData = Qt.binding( function() { return typeof modelData === "undefined" ? null : modelData }); + rowItem.itemModel = Qt.binding( function() { return model }); + rowItem.parent = rowItemContainer; + rowItem.visible = true; + } + } + + Component { + id: rowComponent + + FocusScope { + id: rowitem + visible: false + + property int rowIndex + property var itemModelData + property var itemModel + property bool itemSelected: __mouseArea.selected(rowIndex) + property bool alternate: alternatingRowColors && rowIndex % 2 === 1 + readonly property color itemTextColor: itemSelected ? __style.highlightedTextColor : __style.textColor + + width: itemrow.width + height: rowstyle.height + + onActiveFocusChanged: { + if (activeFocus) + listView.currentIndex = rowIndex + } + + Loader { + id: rowstyle + // row delegate + sourceComponent: rowitem.itemModel !== undefined ? root.rowDelegate : null + // Row fills the view width regardless of item size + // But scrollbar should not adjust to it + height: item ? item.height : 16 + width: parent.width + __horizontalScrollBar.width + x: listView.contentX + + // these properties are exposed to the row delegate + // Note: these properties should be mirrored in the row filler as well + property QtObject styleData: QtObject { + readonly property int row: rowitem.rowIndex + readonly property bool alternate: rowitem.alternate + readonly property bool selected: rowitem.itemSelected + readonly property bool hasActiveFocus: rowitem.activeFocus + readonly property bool pressed: rowitem.rowIndex === __mouseArea.pressedRow + } + readonly property var model: rowitem.itemModel + readonly property var modelData: rowitem.itemModelData + } + Row { + id: itemrow + height: parent.height + Repeater { + id: repeater + model: columnModel + + Loader { + id: itemDelegateLoader + width: columnItem.width + height: parent ? parent.height : 0 + visible: columnItem.visible + sourceComponent: rowitem.itemModel !== undefined ? // delays construction until model is initialized + (columnItem.delegate ? columnItem.delegate : itemDelegate) : null + + // these properties are exposed to the item delegate + readonly property var model: itemModel + readonly property var modelData: itemModelData + + property QtObject styleData: QtObject { + readonly property int row: rowitem.rowIndex + readonly property int column: index + readonly property int elideMode: columnItem.elideMode + readonly property int textAlignment: columnItem.horizontalAlignment + readonly property bool selected: rowitem.itemSelected + readonly property bool hasActiveFocus: rowitem.activeFocus + readonly property bool pressed: row === __mouseArea.pressedRow && column === __mouseArea.pressedColumn + readonly property color textColor: rowitem.itemTextColor + readonly property string role: columnItem.role + readonly property var value: (itemModel && itemModel.hasOwnProperty(role)) + ? itemModel[role] // Qml ListModel and QAbstractItemModel + : modelData && modelData.hasOwnProperty(role) + ? modelData[role] // QObjectList / QObject + : modelData != undefined ? modelData : "" // Models without role + } + } + } + } + } + } + + Item { + id: tableHeader + clip: true + parent: __scroller + visible: headerVisible + anchors.top: parent.top + anchors.topMargin: viewport.anchors.topMargin + anchors.leftMargin: viewport.anchors.leftMargin + anchors.margins: viewport.anchors.margins + anchors.rightMargin: (frameVisible ? __scroller.rightMargin : 0) + + (__scroller.outerFrame && __scrollBarTopMargin ? 0 + : __verticalScrollBar.width + __scroller.scrollBarSpacing + root.__style.padding.right) + + anchors.left: parent.left + anchors.right: parent.right + + height: headerrow.height + + Row { + id: headerrow + x: -listView.contentX + + Repeater { + id: repeater + + property int targetIndex: -1 + property int dragIndex: -1 + + model: columnModel + + delegate: Item { + id: headerRowDelegate + readonly property int column: index + z:-index + width: modelData.width + implicitWidth: columnCount === 1 ? viewport.width + __verticalScrollBar.width : headerStyle.implicitWidth + visible: modelData.visible + height: headerStyle.height + + Loader { + id: headerStyle + sourceComponent: root.headerDelegate + anchors.left: parent.left + anchors.right: parent.right + property QtObject styleData: QtObject { + readonly property string value: modelData.title + readonly property bool pressed: headerClickArea.pressed + readonly property bool containsMouse: headerClickArea.containsMouse + readonly property int column: index + readonly property int textAlignment: modelData.horizontalAlignment + readonly property bool resizable: modelData.resizable + } + } + + Rectangle{ + id: targetmark + width: parent.width + height:parent.height + opacity: (index === repeater.targetIndex && repeater.targetIndex !== repeater.dragIndex) ? 0.5 : 0 + Behavior on opacity { NumberAnimation { duration: 160 } } + color: palette.highlight + visible: modelData.movable + } + + MouseArea{ + id: headerClickArea + drag.axis: Qt.YAxis + hoverEnabled: true + anchors.fill: parent + onClicked: { + if (sortIndicatorColumn === index) + sortIndicatorOrder = sortIndicatorOrder === Qt.AscendingOrder ? Qt.DescendingOrder : Qt.AscendingOrder + sortIndicatorColumn = index + } + // Here we handle moving header sections + // NOTE: the direction is different from the master branch + // so this indicates that I am using an invalid assumption on item ordering + onPositionChanged: { + if (modelData.movable && pressed && columnCount > 1) { // only do this while dragging + for (var h = columnCount-1 ; h >= 0 ; --h) { + if (drag.target.x + listView.contentX + headerRowDelegate.width/2 > headerrow.children[h].x) { + repeater.targetIndex = h + break + } + } + } + } + + onPressed: { + repeater.dragIndex = index + } + + onReleased: { + if (repeater.targetIndex >= 0 && repeater.targetIndex !== index ) { + var targetColumn = columnModel.get(repeater.targetIndex).columnItem + if (targetColumn.movable) { + columnModel.move(index, repeater.targetIndex, 1) + if (sortIndicatorColumn === index) + sortIndicatorColumn = repeater.targetIndex + } + } + repeater.targetIndex = -1 + repeater.dragIndex = -1 + } + drag.target: modelData.movable && columnCount > 1 ? draghandle : null + } + + Loader { + id: draghandle + property QtObject styleData: QtObject{ + readonly property string value: modelData.title + readonly property bool pressed: headerClickArea.pressed + readonly property bool containsMouse: headerClickArea.containsMouse + readonly property int column: index + readonly property int textAlignment: modelData.horizontalAlignment + } + parent: tableHeader + x: __implicitX + property double __implicitX: headerRowDelegate.x - listView.contentX + width: modelData.width + height: parent.height + sourceComponent: root.headerDelegate + visible: headerClickArea.pressed + onVisibleChanged: { + if (!visible) + x = Qt.binding(function () { return __implicitX }) + } + opacity: 0.5 + } + + + MouseArea { + id: headerResizeHandle + property int offset: 0 + readonly property int minimumSize: 20 + preventStealing: true + anchors.rightMargin: -width/2 + width: Settings.hasTouchScreen ? Screen.pixelDensity * 3.5 : 16 + height: parent.height + anchors.right: parent.right + enabled: modelData.resizable && columnCount > 0 + onPositionChanged: { + var newHeaderWidth = modelData.width + (mouseX - offset) + modelData.width = Math.max(minimumSize, newHeaderWidth) + } + + onDoubleClicked: getColumn(index).resizeToContents() + onPressedChanged: if (pressed) offset=mouseX + cursorShape: enabled && repeater.dragIndex==-1 ? Qt.SplitHCursor : Qt.ArrowCursor + } + } + } + + onWidthChanged: listView.contentWidth = width + } + + Loader { + id: loader + property QtObject styleData: QtObject{ + readonly property string value: "" + readonly property bool pressed: false + readonly property bool containsMouse: false + readonly property int column: -1 + readonly property int textAlignment: Text.AlignLeft + } + + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: headerrow.bottom + anchors.rightMargin: -2 + sourceComponent: root.headerDelegate + width: root.width - headerrow.width + 2 + visible: root.columnCount + z:-1 + } + } + + function columnAt(offset) { + var item = headerrow.childAt(offset, 0) + return item ? item.column : -1 + } + } +} diff --git a/src/controls/Private/private.pri b/src/controls/Private/private.pri index e69d0a295..3d6a71624 100644 --- a/src/controls/Private/private.pri +++ b/src/controls/Private/private.pri @@ -61,6 +61,7 @@ PRIVATE_QML_FILES += \ $$PWD/EditMenu_base.qml \ $$PWD/EditMenu_ios.qml \ $$PWD/ToolMenuButton.qml \ + $$PWD/BasicTableView.qml \ $$PWD/qmldir QML_FILES += $$PRIVATE_QML_FILES diff --git a/src/controls/Private/qmldir b/src/controls/Private/qmldir index 82e1a9859..c2c2d7ca9 100644 --- a/src/controls/Private/qmldir +++ b/src/controls/Private/qmldir @@ -33,3 +33,4 @@ EditMenu 1.0 EditMenu.qml EditMenu_base 1.0 EditMenu_base.qml EditMenu_ios 1.0 EditMenu_ios.qml ToolMenuButton 1.0 ToolMenuButton.qml +BasicTableView 1.0 BasicTableView.qml diff --git a/src/controls/TableView.qml b/src/controls/TableView.qml index 218f21256..629c95dd0 100644 --- a/src/controls/TableView.qml +++ b/src/controls/TableView.qml @@ -35,7 +35,7 @@ ****************************************************************************/ import QtQuick 2.2 -import QtQuick.Controls 1.2 +import QtQuick.Controls 1.3 import QtQuick.Controls.Private 1.0 import QtQuick.Controls.Styles 1.1 import QtQuick.Window 2.1 @@ -110,7 +110,7 @@ import QtQuick.Window 2.1 assigning a \l {QtQuick.Controls.Styles::TableViewStyle}{TableViewStyle}. */ -ScrollView { +BasicTableView { id: root /*! \qmlproperty model TableView::model @@ -124,160 +124,29 @@ ScrollView { \code model: ListModel { - ListElement{ column1: "value 1" ; column2: "value 2" } - ListElement{ column1: "value 3" ; column2: "value 4" } + ListElement { + column1: "value 1" + column2: "value 2" + } + ListElement { + column1: "value 3" + column2: "value 4" + } } \endcode \sa {qml-data-models}{Data Models} */ property var model - /*! This property is set to \c true if the view alternates the row color. - The default value is \c true. */ - property bool alternatingRowColors: true - - /*! This property determines if the header is visible. - The default value is \c true. */ - property bool headerVisible: true - - /*! \qmlproperty bool TableView::backgroundVisible - - This property determines if the background should be filled or not. - - The default value is \c true. - - \note The rowDelegate is not affected by this property - */ - property alias backgroundVisible: colorRect.visible - - /*! This property defines a delegate to draw a specific cell. - - In the item delegate you have access to the following special properties: - \list - \li styleData.selected - if the item is currently selected - \li styleData.value - the value or text for this item - \li styleData.textColor - the default text color for an item - \li styleData.row - the index of the row - \li styleData.column - the index of the column - \li styleData.elideMode - the elide mode of the column - \li styleData.textAlignment - the horizontal text alignment of the column - \li styleData.pressed - true when the item is pressed (since QtQuick.Controls 1.3) - \li styleData.hasActiveFocus - true when the row has focus (since QtQuick.Controls 1.3) - \endlist - - Example: - \code - itemDelegate: Item { - Text { - anchors.verticalCenter: parent.verticalCenter - color: styleData.textColor - elide: styleData.elideMode - text: styleData.value - } - } - \endcode - - \note For performance reasons, created delegates can be recycled - across multiple table rows. This implies that when you make use of implicit - properties such as \c styleData.row or \c model, these values can change - after the delegate has been constructed. This means that you should not assume - that content is fixed when \c Component.onCompleted is called, but instead rely on - bindings to such properties. - */ - property Component itemDelegate: __style ? __style.itemDelegate : null - - /*! This property defines a delegate to draw a row. - - In the row delegate you have access to the following special properties: - \list - \li styleData.alternate - true when the row uses the alternate background color - \li styleData.selected - true when the row is currently selected - \li styleData.row - the index of the row - \li styleData.hasActiveFocus - true when the row has focus - \li styleData.pressed - true when the row is pressed (since QtQuick.Controls 1.3) - \endlist - - \note For performance reasons, created delegates can be recycled - across multiple table rows. This implies that when you make use of implicit - properties such as \c styleData.row or \c model, these values can change - after the delegate has been constructed. This means that you should not assume - that content is fixed when \c Component.onCompleted is called, but instead rely on - bindings to such properties. - */ - property Component rowDelegate: __style ? __style.rowDelegate : null - - /*! This property defines a delegate to draw a header. - - In the header delegate you have access to the following special properties: - \list - \li styleData.value - the value or text for this item - \li styleData.column - the index of the column - \li styleData.pressed - true when the column is being pressed - \li styleData.containsMouse - true when the column is under the mouse - \li styleData.textAlignment - the horizontal text alignment of the column (since QtQuickControls 1.1) - \endlist - */ - property Component headerDelegate: __style ? __style.headerDelegate : null - - /*! Index of the current sort column. - The default value is \c {0}. */ - property int sortIndicatorColumn - - /*! This property shows or hides the sort indicator - The default value is \c false. - \note The view itself does not sort the data. */ - property bool sortIndicatorVisible: false - - /*! - \qmlproperty enumeration TableView::sortIndicatorOrder - - This sets the sorting order of the sort indicator - The allowed values are: - \list - \li Qt.AscendingOrder - the default - \li Qt.DescendingOrder - \endlist - */ - property int sortIndicatorOrder: Qt.AscendingOrder - - /*! \internal */ - default property alias __columns: root.data - - /*! \qmlproperty Component TableView::contentHeader - This is the content header of the TableView */ - property alias contentHeader: listView.header - - /*! \qmlproperty Component TableView::contentFooter - This is the content footer of the TableView */ - property alias contentFooter: listView.footer - /*! \qmlproperty int TableView::rowCount The current number of rows */ - readonly property alias rowCount: listView.count - - /*! \qmlproperty int TableView::columnCount - The current number of columns */ - readonly property alias columnCount: columnModel.count - - /*! \qmlproperty string TableView::section.property - \qmlproperty enumeration TableView::section.criteria - \qmlproperty Component TableView::section.delegate - \qmlproperty enumeration TableView::section.labelPositioning - These properties determine the section labels. - \sa ListView::section */ - property alias section: listView.section + readonly property int rowCount: __listView.count /*! \qmlproperty int TableView::currentRow The current row index of the view. The default value is \c -1 to indicate that no row is selected. */ - property alias currentRow: listView.currentIndex - - /*! \internal */ - property alias __currentRowItem: listView.currentItem - - /*! \internal */ - property alias __listView: listView + property alias currentRow: root.__currentRow /*! \qmlsignal TableView::activated(int row) @@ -332,34 +201,33 @@ ScrollView { /*! \qmlmethod TableView::positionViewAtRow( int row, PositionMode mode ) - Positions the view such that the specified \a row is at the position defined by \a mode: - \list - \li ListView.Beginning - position item at the top of the view. - \li ListView.Center - position item in the center of the view. - \li ListView.End - position item at bottom of the view. - \li ListView.Visible - if any part of the item is visible then take no action, otherwise bring the item into view. - \li ListView.Contain - ensure the entire item is visible. If the item is larger than the view the item is positioned - at the top of the view. - \endlist + Positions the view such that the specified \a row is at the position defined by \a mode: + \list + \li ListView.Beginning - position item at the top of the view. + \li ListView.Center - position item in the center of the view. + \li ListView.End - position item at bottom of the view. + \li ListView.Visible - if any part of the item is visible then take no action, otherwise bring the item into view. + \li ListView.Contain - ensure the entire item is visible. If the item is larger than the view the item is positioned + at the top of the view. + \endlist - If positioning the \a row creates an empty space at the beginning - or end of the view, then the view is positioned at the boundary. + If positioning the \a row creates an empty space at the beginning + or end of the view, then the view is positioned at the boundary. - For example, to position the view at the end at startup: + For example, to position the view at the end at startup: - \code - Component.onCompleted: table.positionViewAtRow(rowCount -1, ListView.Contain) - \endcode + \code + Component.onCompleted: table.positionViewAtRow(rowCount -1, ListView.Contain) + \endcode - Depending on how the model is populated, the model may not be ready when - TableView Component.onCompleted is called. In that case you may need to - delay the call to positionViewAtRow by using a \l {QtQml::Timer}{Timer}. + Depending on how the model is populated, the model may not be ready when + TableView Component.onCompleted is called. In that case you may need to + delay the call to positionViewAtRow by using a \l {QtQml::Timer}{Timer}. - \note This method should only be called after the component has completed. + \note This method should only be called after the component has completed. */ - function positionViewAtRow(row, mode) { - listView.positionViewAtIndex(row, mode) + __listView.positionViewAtIndex(row, mode) } /*! @@ -370,427 +238,237 @@ ScrollView { \note This method should only be called after the component has completed. */ - function rowAt(x, y) { - var obj = root.mapToItem(listView.contentItem, x, y) - return listView.indexAt(obj.x, obj.y) - } - - /*! Adds a \a column and returns the added column. - - The \a column argument can be an instance of TableViewColumn, - or a Component. The component has to contain a TableViewColumn. - Otherwise \c null is returned. - */ - function addColumn(column) { - return insertColumn(columnCount, column) - } - - /*! Inserts a \a column at the given \a index and returns the inserted column. - - The \a column argument can be an instance of TableViewColumn, - or a Component. The component has to contain a TableViewColumn. - Otherwise \c null is returned. - */ - function insertColumn(index, column) { - var object = column - if (typeof column['createObject'] === 'function') - object = column.createObject(root) - - else if (object.__view) { - console.warn("TableView::insertColumn(): you cannot add a column to multiple views") - return null - } - if (index >= 0 && index <= columnCount && object.Accessible.role === Accessible.ColumnHeader) { - object.__view = root - columnModel.insert(index, {columnItem: object}) - return object - } - - if (object !== column) - object.destroy() - console.warn("TableView::insertColumn(): invalid argument") - return null - } - - /*! Removes and destroys a column at the given \a index. */ - function removeColumn(index) { - if (index < 0 || index >= columnCount) { - console.warn("TableView::removeColumn(): invalid argument") - return - } - var column = columnModel.get(index).columnItem - columnModel.remove(index, 1) - column.destroy() - } - - /*! Moves a column \a from index \a to another. */ - function moveColumn(from, to) { - if (from < 0 || from >= columnCount || to < 0 || to >= columnCount) { - console.warn("TableView::moveColumn(): invalid argument") - return - } - columnModel.move(from, to, 1) - } - - /*! Returns the column at the given \a index - or \c null if the \a index is invalid. */ - function getColumn(index) { - if (index < 0 || index >= columnCount) - return null - return columnModel.get(index).columnItem + var obj = root.mapToItem(__listView.contentItem, x, y) + return __listView.indexAt(obj.x, obj.y) } /*! \qmlproperty Selection TableView::selection - \since QtQuick.Controls 1.1 - - This property contains the current row-selection of the \l TableView. - The selection allows you to select, deselect or iterate over selected rows. - - \list - \li function \b clear() - deselects all rows - \li function \b selectAll() - selects all rows - \li function \b select(from, to) - select a range - \li functton \b deselect(from, to) - de-selects a range - \li function \b forEach(callback) - iterates over all selected rows - \li function \b contains(index) - checks whether the selection includes the given index - \li signal \b selectionChanged() - the current row selection changed - \li readonly property int \b count - the number of selected rows - \endlist - - \b Example: - \code - tableview.selection.select(0) // select row index 0 - - tableview.selection.select(1, 3) // select row indexes 1, 2 and 3 - - tableview.selection.deselect(0, 1) // deselects row index 0 and 1 - - tableview.selection.deselect(2) // deselects row index 2 - \endcode - - \b Example: To iterate over selected indexes, you can pass a callback function. - \a rowIndex is passed as as an argument to the callback function. - \code - tableview.selection.forEach( function(rowIndex) {console.log(rowIndex)} ) - \endcode - - */ - - readonly property alias selection: selectionObject - - /*! - \qmlproperty enumeration TableView::selectionMode \since QtQuick.Controls 1.1 - This enum indicates how the view responds to user selections: - - The possible modes are: + This property contains the current row-selection of the \l TableView. + The selection allows you to select, deselect or iterate over selected rows. \list + \li function \b clear() - deselects all rows + \li function \b selectAll() - selects all rows + \li function \b select(from, to) - select a range + \li functton \b deselect(from, to) - de-selects a range + \li function \b forEach(callback) - iterates over all selected rows + \li function \b contains(index) - checks whether the selection includes the given index + \li signal \b selectionChanged() - the current row selection changed + \li readonly property int \b count - the number of selected rows + \endlist - \li SelectionMode.NoSelection - Items cannot be selected. - - \li SelectionMode.SingleSelection - When the user selects an item, - any already-selected item becomes unselected, and the user cannot - unselect the selected item. (Default) + \b Example: + \code + tableview.selection.select(0) // select row index 0 - \li SelectionMode.MultiSelection - When the user selects an item in the usual way, - the selection status of that item is toggled and the other items are left alone. + tableview.selection.select(1, 3) // select row indexes 1, 2 and 3 - \li SelectionMode.ExtendedSelection - When the user selects an item in the usual way, - the selection is cleared and the new item selected. However, if the user presses the - Ctrl key when clicking on an item, the clicked item gets toggled and all other items - are left untouched. If the user presses the Shift key while clicking - on an item, all items between the current item and the clicked item are selected or unselected, - depending on the state of the clicked item. Multiple items can be selected by dragging the - mouse over them. + tableview.selection.deselect(0, 1) // deselects row index 0 and 1 - \li SelectionMode.ContiguousSelection - When the user selects an item in the usual way, - the selection is cleared and the new item selected. However, if the user presses the Shift key while - clicking on an item, all items between the current item and the clicked item are selected. + tableview.selection.deselect(2) // deselects row index 2 + \endcode - \endlist + \b Example: To iterate over selected indexes, you can pass a callback function. + \a rowIndex is passed as as an argument to the callback function. + \code + tableview.selection.forEach( function(rowIndex) {console.log(rowIndex)} ) + \endcode */ - property int selectionMode: SelectionMode.SingleSelection - - /*! Resizes all columns to ensure that the column contents and the headers will fit. - \since QtQuick.Controls 1.2 */ - function resizeColumnsToContents () { - for (var i = 0; i < __columns.length; ++i) { - var col = getColumn(i) - var header = repeater.itemAt(i) - if (col) { - col.__index = i - col.resizeToContents() - if (col.width < header.implicitWidth) - col.width = header.implicitWidth - } - } - } + readonly property alias selection: selectionObject - Component.onCompleted: { - for (var i = 0; i < __columns.length; ++i) { - var column = __columns[i] - if (column.Accessible.role === Accessible.ColumnHeader) - addColumn(column) - } - } + onModelChanged: selection.clear() style: Qt.createComponent(Settings.style + "/TableViewStyle.qml", root) - Accessible.role: Accessible.Table - implicitWidth: 200 - implicitHeight: 150 - - frameVisible: true - __scrollBarTopMargin: (__style && __style.transientScrollBars || Qt.platform.os === "osx") ? headerrow.height : 0 - __viewTopMargin: headerrow.height - - /*! \internal */ - property bool __activateItemOnSingleClick: __style ? __style.activateItemOnSingleClick : false + __viewTypeName: "TableView" + __model: model - /*! \internal */ - function __decrementCurrentIndex() { - __scroller.blockUpdates = true; - listView.decrementCurrentIndex(); - __scroller.blockUpdates = false; + __mouseArea: MouseArea { + id: mousearea - var newIndex = listView.indexAt(0, listView.contentY) - if (newIndex !== -1) { - if (selectionMode > SelectionMode.SingleSelection) - mousearea.dragRow = newIndex - else if (selectionMode === SelectionMode.SingleSelection) - selection.__selectOne(newIndex) - } - } + parent: __listView + width: __listView.width + height: __listView.height + z: -1 + propagateComposedEvents: true + focus: true - /*! \internal */ - function __incrementCurrentIndex() { - __scroller.blockUpdates = true; - listView.incrementCurrentIndex(); - __scroller.blockUpdates = false; - - var newIndex = Math.max(0, listView.indexAt(0, listView.height + listView.contentY)) - if (newIndex !== -1) { - if (selectionMode > SelectionMode.SingleSelection) - mousearea.dragRow = newIndex - else if (selectionMode === SelectionMode.SingleSelection) - selection.__selectOne(newIndex) + property bool autoincrement: false + property bool autodecrement: false + property int previousRow: 0 + property int clickedRow: -1 + property int dragRow: -1 + property int firstKeyRow: -1 + property int pressedRow: -1 + property int pressedColumn: -1 + + TableViewSelection { + id: selectionObject } - } - - onModelChanged: selection.clear() - ListView { - id: listView - focus: true - activeFocusOnTab: false - anchors.topMargin: headerVisible ? tableHeader.height : 0 - anchors.fill: parent - currentIndex: -1 - visible: columnCount > 0 - interactive: Settings.hasTouchScreen - property var rowItemStack: [] // Used as a cache for rowDelegates - - SystemPalette { - id: palette - colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled - } + function selected(rowIndex) { + if (dragRow > -1 && (rowIndex >= clickedRow && rowIndex <= dragRow + || rowIndex <= clickedRow && rowIndex >= dragRow)) + return selection.contains(clickedRow) - Rectangle { - id: colorRect - parent: viewport - anchors.fill: parent - color: __style ? __style.backgroundColor : palette.base - z: -2 + return selection.count && selection.contains(rowIndex) } - MouseArea { - id: mousearea - - z: -1 - anchors.fill: listView - propagateComposedEvents: true - - property bool autoincrement: false - property bool autodecrement: false - property int mouseModifiers: 0 - property int previousRow: 0 - property int clickedRow: -1 - property int dragRow: -1 - property int firstKeyRow: -1 - property int pressedRow: -1 - property int pressedColumn: -1 - - onReleased: { - pressedRow = -1 - pressedColumn = -1 - autoincrement = false - autodecrement = false - var clickIndex = listView.indexAt(0, mouseY + listView.contentY) - if (clickIndex > -1) { - if (Settings.hasTouchScreen) { - listView.currentIndex = clickIndex - mouseSelect(clickIndex, mouse.modifiers) - } - previousRow = clickIndex + onReleased: { + pressedRow = -1 + pressedColumn = -1 + autoincrement = false + autodecrement = false + var clickIndex = __listView.indexAt(0, mouseY + __listView.contentY) + if (clickIndex > -1) { + if (Settings.hasTouchScreen) { + __listView.currentIndex = clickIndex + mouseSelect(clickIndex, mouse.modifiers) } + previousRow = clickIndex + } - if (mousearea.dragRow >= 0) { - selection.__select(selection.contains(mousearea.clickedRow), mousearea.clickedRow, mousearea.dragRow) - mousearea.dragRow = -1 - } + if (mousearea.dragRow >= 0) { + selection.__select(selection.contains(mousearea.clickedRow), mousearea.clickedRow, mousearea.dragRow) + mousearea.dragRow = -1 } + } - // Handle vertical scrolling whem dragging mouse outside boundraries - Timer { running: mousearea.autoincrement && __verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: __incrementCurrentIndex()} - Timer { running: mousearea.autodecrement && __verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: __decrementCurrentIndex()} - - onPositionChanged: { - if (mouseY > listView.height && pressed) { - if (autoincrement) return; - autodecrement = false; - autoincrement = true; - } else if (mouseY < 0 && pressed) { - if (autodecrement) return; - autoincrement = false; - autodecrement = true; - } else { - autoincrement = false; - autodecrement = false; - } + function decrementCurrentIndex() { + __listView.decrementCurrentIndexBlocking(); - if (pressed && containsMouse) { - pressedRow = Math.max(0, listView.indexAt(0, mouseY + listView.contentY)) - pressedColumn = headerrow.columnAt(mouseX) - if (!Settings.hasTouchScreen) { - if (pressedRow >= 0 && pressedRow !== currentRow) { - listView.currentIndex = pressedRow; - if (selectionMode === SelectionMode.SingleSelection) { - selection.__selectOne(pressedRow) - } else if (selectionMode > 1) { - dragRow = pressedRow - } - } - } - } - mouseModifiers = mouse.modifiers + var newIndex = __listView.indexAt(0, __listView.contentY) + if (newIndex !== -1) { + if (selectionMode > SelectionMode.SingleSelection) + mousearea.dragRow = newIndex + else if (selectionMode === SelectionMode.SingleSelection) + selection.__selectOne(newIndex) } + } - onClicked: { - var clickIndex = listView.indexAt(0, mouseY + listView.contentY) - if (clickIndex > -1) { - if (root.__activateItemOnSingleClick) - root.activated(clickIndex) - root.clicked(clickIndex) - } - } + function incrementCurrentIndex() { + __listView.incrementCurrentIndexBlocking(); - onPressed: { - pressedRow = listView.indexAt(0, mouseY + listView.contentY) - pressedColumn = headerrow.columnAt(mouseX) - listView.forceActiveFocus() - if (pressedRow > -1 && !Settings.hasTouchScreen) { - listView.currentIndex = pressedRow - mouseSelect(pressedRow, mouse.modifiers) - mousearea.clickedRow = pressedRow - } - mouseModifiers = mouse.modifiers + var newIndex = Math.max(0, __listView.indexAt(0, __listView.height + __listView.contentY)) + if (newIndex !== -1) { + if (selectionMode > SelectionMode.SingleSelection) + mousearea.dragRow = newIndex + else if (selectionMode === SelectionMode.SingleSelection) + selection.__selectOne(newIndex) } + } - onExited: { - mousearea.pressedRow = -1 - mousearea.pressedColumn = -1 - } + // Handle vertical scrolling whem dragging mouse outside boundraries + Timer { + running: mousearea.autoincrement && __verticalScrollBar.visible + repeat: true + interval: 20 + onTriggered: mousearea.incrementCurrentIndex() + } - onCanceled: { - mousearea.pressedRow = -1 - mousearea.pressedColumn = -1 + Timer { + running: mousearea.autodecrement && __verticalScrollBar.visible + repeat: true + interval: 20 + onTriggered: mousearea.decrementCurrentIndex() + } + + onPositionChanged: { + if (mouseY > __listView.height && pressed) { + if (autoincrement) return; + autodecrement = false; + autoincrement = true; + } else if (mouseY < 0 && pressed) { + if (autodecrement) return; + autoincrement = false; + autodecrement = true; + } else { + autoincrement = false; + autodecrement = false; } - function mouseSelect(index, modifiers) { - if (selectionMode) { - if (modifiers & Qt.ShiftModifier && (selectionMode === SelectionMode.ExtendedSelection)) { - selection.select(previousRow, index) - } else if (selectionMode === SelectionMode.MultiSelection || - (selectionMode === SelectionMode.ExtendedSelection && modifiers & Qt.ControlModifier)) { - selection.__select(!selection.contains(index) , index) - } else { - selection.__selectOne(index) + if (pressed && containsMouse) { + pressedRow = Math.max(0, __listView.indexAt(0, mouseY + __listView.contentY)) + pressedColumn = __listView.columnAt(mouseX) + if (!Settings.hasTouchScreen) { + if (pressedRow >= 0 && pressedRow !== currentRow) { + __listView.currentIndex = pressedRow; + if (selectionMode === SelectionMode.SingleSelection) { + selection.__selectOne(pressedRow) + } else if (selectionMode > 1) { + dragRow = pressedRow + } } } } + } - onDoubleClicked: { - var clickIndex = listView.indexAt(0, mouseY + listView.contentY) - if (clickIndex > -1) { - if (!root.__activateItemOnSingleClick) - root.activated(clickIndex) - root.doubleClicked(clickIndex) - } + onClicked: { + var clickIndex = __listView.indexAt(0, mouseY + __listView.contentY) + if (clickIndex > -1) { + if (root.__activateItemOnSingleClick) + root.activated(clickIndex) + root.clicked(clickIndex) } + } - onPressAndHold: { - var pressIndex = listView.indexAt(0, mouseY + listView.contentY) - if (pressIndex > -1) - root.pressAndHold(pressIndex) + onPressed: { + pressedRow = __listView.indexAt(0, mouseY + __listView.contentY) + pressedColumn = __listView.columnAt(mouseX) + __listView.forceActiveFocus() + if (pressedRow > -1 && !Settings.hasTouchScreen) { + __listView.currentIndex = pressedRow + mouseSelect(pressedRow, mouse.modifiers) + mousearea.clickedRow = pressedRow } + } - // Note: with boolean preventStealing we are keeping the flickable from - // eating our mouse press events - preventStealing: !Settings.hasTouchScreen + onExited: { + mousearea.pressedRow = -1 + mousearea.pressedColumn = -1 + } - TableViewSelection { id: selectionObject } + onCanceled: { + mousearea.pressedRow = -1 + mousearea.pressedColumn = -1 } - // Fills extra rows with alternate color - Column { - id: rowfiller - Loader { - id: rowSizeItem - sourceComponent: root.rowDelegate - visible: false - property QtObject styleData: QtObject { - property bool alternate: false - property bool selected: false - property bool hasActiveFocus: false - property bool pressed: false + function mouseSelect(index, modifiers) { + if (selectionMode) { + if (modifiers & Qt.ShiftModifier && (selectionMode === SelectionMode.ExtendedSelection)) { + selection.select(previousRow, index) + } else if (selectionMode === SelectionMode.MultiSelection || + (selectionMode === SelectionMode.ExtendedSelection && modifiers & Qt.ControlModifier)) { + selection.__select(!selection.contains(index) , index) + } else { + selection.__selectOne(index) } } - property int rowHeight: rowSizeItem.implicitHeight - property int paddedRowCount: height/rowHeight - - y: listView.contentHeight - listView.contentY + listView.originY - width: parent.width - visible: alternatingRowColors - height: viewport.height - listView.contentHeight - Repeater { - model: visible ? parent.paddedRowCount : 0 - Loader { - width: rowfiller.width - height: rowfiller.rowHeight - sourceComponent: root.rowDelegate - property QtObject styleData: QtObject { - readonly property bool alternate: (index + rowCount) % 2 === 1 - readonly property bool selected: false - readonly property bool hasActiveFocus: false - readonly property bool pressed: false - } - readonly property var model: null - readonly property var modelData: null - } + } + + onDoubleClicked: { + var clickIndex = __listView.indexAt(0, mouseY + __listView.contentY) + if (clickIndex > -1) { + if (!root.__activateItemOnSingleClick) + root.activated(clickIndex) + root.doubleClicked(clickIndex) } } - ListModel { - id: columnModel + onPressAndHold: { + var pressIndex = __listView.indexAt(0, mouseY + __listView.contentY) + if (pressIndex > -1) + root.pressAndHold(pressIndex) } - highlightFollowsCurrentItem: true - model: root.model + // Note: with boolean preventStealing we are keeping the flickable from + // eating our mouse press events + preventStealing: !Settings.hasTouchScreen function keySelect(shiftPressed, row) { if (row < 0 || row > rowCount - 1) @@ -803,37 +481,25 @@ ScrollView { } } - Keys.forwardTo: root + Keys.forwardTo: [root] + Keys.onUpPressed: { - var oldIndex = listView.currentIndex - __scroller.blockUpdates = true; - listView.decrementCurrentIndex(); - __scroller.blockUpdates = false; - if (oldIndex === listView.currentIndex) - event.accepted = false + event.accepted = __listView.decrementCurrentIndexBlocking() if (selectionMode) keySelect(event.modifiers & Qt.ShiftModifier, currentRow) } Keys.onDownPressed: { - var oldIndex = listView.currentIndex - __scroller.blockUpdates = true; - listView.incrementCurrentIndex(); - __scroller.blockUpdates = false; - if (oldIndex === listView.currentIndex) - event.accepted = false + event.accepted = __listView.incrementCurrentIndexBlocking() if (selectionMode) keySelect(event.modifiers & Qt.ShiftModifier, currentRow) } Keys.onPressed: { - if (event.key === Qt.Key_PageUp) { - __verticalScrollBar.value = __verticalScrollBar.value - listView.height - } else if (event.key === Qt.Key_PageDown) - __verticalScrollBar.value = __verticalScrollBar.value + listView.height + __listView.scrollIfNeeded(event.key) if (event.key === Qt.Key_Shift) { - mousearea.firstKeyRow = currentRow + firstKeyRow = currentRow } if (event.key === Qt.Key_A && event.modifiers & Qt.ControlModifier) { @@ -844,7 +510,7 @@ ScrollView { Keys.onReleased: { if (event.key === Qt.Key_Shift) - mousearea.firstKeyRow = -1 + firstKeyRow = -1 } Keys.onReturnPressed: { @@ -853,317 +519,5 @@ ScrollView { else event.accepted = false } - - delegate: FocusScope { - id: rowItemContainer - - activeFocusOnTab: false - z: rowItem.activeFocus ? 0.7 : rowItem.itemSelected ? 0.5 : 0 - - property Item rowItem - // We recycle instantiated row items to speed up list scrolling - - Component.onDestruction: { - // move the rowItem back in cache - if (rowItem) { - rowItem.visible = false; - rowItem.parent = null; - rowItem.rowIndex = -1; - listView.rowItemStack.push(rowItem); // return rowItem to cache - } - } - - Component.onCompleted: { - // retrieve row item from cache - if (listView.rowItemStack.length > 0) - rowItem = listView.rowItemStack.pop(); - else - rowItem = rowComponent.createObject(listView); - - // Bind container to item size - rowItemContainer.width = Qt.binding( function() { return rowItem.width }); - rowItemContainer.height = Qt.binding( function() { return rowItem.height }); - - // Reassign row-specific bindings - rowItem.rowIndex = Qt.binding( function() { return model.index }); - rowItem.itemModelData = Qt.binding( function() { return typeof modelData === "undefined" ? null : modelData }); - rowItem.itemModel = Qt.binding( function() { return model }); - rowItem.parent = rowItemContainer; - rowItem.visible = true; - } - } - - Component { - id: rowComponent - - FocusScope { - id: rowitem - visible: false - - property int rowIndex - property var itemModelData - property var itemModel - property bool itemSelected: selected() - property bool alternate: alternatingRowColors && rowIndex % 2 === 1 - readonly property color itemTextColor: itemSelected ? __style.highlightedTextColor : __style.textColor - - function selected() { - if (mousearea.dragRow > -1 && (rowIndex >= mousearea.clickedRow && rowIndex <= mousearea.dragRow - || rowIndex <= mousearea.clickedRow && rowIndex >=mousearea.dragRow)) - return selection.contains(mousearea.clickedRow) - - return selection.count && selection.contains(rowIndex) - } - - - width: itemrow.width - height: rowstyle.height - - onActiveFocusChanged: { - if (activeFocus) - listView.currentIndex = rowIndex - } - - Loader { - id: rowstyle - // row delegate - sourceComponent: rowitem.itemModel !== undefined ? root.rowDelegate : null - // Row fills the view width regardless of item size - // But scrollbar should not adjust to it - height: item ? item.height : 16 - width: parent.width + __horizontalScrollBar.width - x: listView.contentX - - // these properties are exposed to the row delegate - // Note: these properties should be mirrored in the row filler as well - property QtObject styleData: QtObject { - readonly property int row: rowitem.rowIndex - readonly property bool alternate: rowitem.alternate - readonly property bool selected: rowitem.itemSelected - readonly property bool hasActiveFocus: rowitem.activeFocus - readonly property bool pressed: rowitem.rowIndex === mousearea.pressedRow - } - readonly property var model: rowitem.itemModel - readonly property var modelData: rowitem.itemModelData - } - Row { - id: itemrow - height: parent.height - Repeater { - id: repeater - model: columnModel - - Loader { - id: itemDelegateLoader - width: columnItem.width - height: parent ? parent.height : 0 - visible: columnItem.visible - sourceComponent: rowitem.itemModel !== undefined ? // delays construction until model is initialized - (columnItem.delegate ? columnItem.delegate : itemDelegate) : null - - // these properties are exposed to the item delegate - readonly property var model: itemModel - readonly property var modelData: itemModelData - - property QtObject styleData: QtObject { - readonly property int row: rowitem.rowIndex - readonly property int column: index - readonly property int elideMode: columnItem.elideMode - readonly property int textAlignment: columnItem.horizontalAlignment - readonly property bool selected: rowitem.itemSelected - readonly property bool hasActiveFocus: rowitem.activeFocus - readonly property bool pressed: row === mousearea.pressedRow && column === mousearea.pressedColumn - readonly property color textColor: rowitem.itemTextColor - readonly property string role: columnItem.role - readonly property var value: (itemModel && itemModel.hasOwnProperty(role)) - ? itemModel[role] // Qml ListModel and QAbstractItemModel - : modelData && modelData.hasOwnProperty(role) - ? modelData[role] // QObjectList / QObject - : modelData != undefined ? modelData : "" // Models without role - } - } - } - } - } - } - - Item { - id: tableHeader - clip: true - parent: __scroller - visible: headerVisible - anchors.top: parent.top - anchors.topMargin: viewport.anchors.topMargin - anchors.leftMargin: viewport.anchors.leftMargin - anchors.margins: viewport.anchors.margins - anchors.rightMargin: (frameVisible ? __scroller.rightMargin : 0) + - (__scroller.outerFrame && __scrollBarTopMargin ? 0 : __verticalScrollBar.width - + __scroller.scrollBarSpacing + root.__style.padding.right) - - anchors.left: parent.left - anchors.right: parent.right - - height: headerrow.height - - Row { - id: headerrow - x: -listView.contentX - - function columnAt(offset) { - var item = childAt(offset, 0) - return item ? item.column : -1 - } - - Repeater { - id: repeater - - property int targetIndex: -1 - property int dragIndex: -1 - - model: columnModel - - delegate: Item { - id: headerRowDelegate - readonly property int column: index - z:-index - width: modelData.width - implicitWidth: columnCount === 1 ? viewport.width + __verticalScrollBar.width : headerStyle.implicitWidth - visible: modelData.visible - height: headerStyle.height - - Loader { - id: headerStyle - sourceComponent: root.headerDelegate - anchors.left: parent.left - anchors.right: parent.right - property QtObject styleData: QtObject { - readonly property string value: modelData.title - readonly property bool pressed: headerClickArea.pressed - readonly property bool containsMouse: headerClickArea.containsMouse - readonly property int column: index - readonly property int textAlignment: modelData.horizontalAlignment - readonly property bool resizable: modelData.resizable - } - } - Rectangle{ - id: targetmark - width: parent.width - height:parent.height - opacity: (index === repeater.targetIndex && repeater.targetIndex !== repeater.dragIndex) ? 0.5 : 0 - Behavior on opacity { NumberAnimation{duration:160}} - color: palette.highlight - visible: modelData.movable - } - - MouseArea{ - id: headerClickArea - drag.axis: Qt.YAxis - hoverEnabled: true - anchors.fill: parent - onClicked: { - if (sortIndicatorColumn === index) - sortIndicatorOrder = sortIndicatorOrder === Qt.AscendingOrder ? Qt.DescendingOrder : Qt.AscendingOrder - sortIndicatorColumn = index - } - // Here we handle moving header sections - // NOTE: the direction is different from the master branch - // so this indicates that I am using an invalid assumption on item ordering - onPositionChanged: { - if (modelData.movable && pressed && columnCount > 1) { // only do this while dragging - for (var h = columnCount-1 ; h >= 0 ; --h) { - if (drag.target.x + listView.contentX + headerRowDelegate.width/2 > headerrow.children[h].x) { - repeater.targetIndex = h - break - } - } - } - } - - onPressed: { - repeater.dragIndex = index - } - - onReleased: { - if (repeater.targetIndex >= 0 && repeater.targetIndex !== index ) { - var targetColumn = columnModel.get(repeater.targetIndex).columnItem - if (targetColumn.movable) { - columnModel.move(index, repeater.targetIndex, 1) - if (sortIndicatorColumn === index) - sortIndicatorColumn = repeater.targetIndex - } - } - repeater.targetIndex = -1 - repeater.dragIndex = -1 - } - drag.target: modelData.movable && columnCount > 1 ? draghandle : null - } - - Loader { - id: draghandle - property QtObject styleData: QtObject{ - readonly property string value: modelData.title - readonly property bool pressed: headerClickArea.pressed - readonly property bool containsMouse: headerClickArea.containsMouse - readonly property int column: index - readonly property int textAlignment: modelData.horizontalAlignment - } - parent: tableHeader - x: __implicitX - property double __implicitX: headerRowDelegate.x - listView.contentX - width: modelData.width - height: parent.height - sourceComponent: root.headerDelegate - visible: headerClickArea.pressed - onVisibleChanged: { - if (!visible) - x = Qt.binding(function () { return __implicitX }) - } - opacity: 0.5 - } - - - MouseArea { - id: headerResizeHandle - property int offset: 0 - readonly property int minimumSize: 20 - preventStealing: true - anchors.rightMargin: -width/2 - width: Settings.hasTouchScreen ? Screen.pixelDensity * 3.5 : 16 - height: parent.height - anchors.right: parent.right - enabled: modelData.resizable && columnCount > 0 - onPositionChanged: { - var newHeaderWidth = modelData.width + (mouseX - offset) - modelData.width = Math.max(minimumSize, newHeaderWidth) - } - - onDoubleClicked: getColumn(index).resizeToContents() - onPressedChanged: if (pressed) offset=mouseX - cursorShape: enabled && repeater.dragIndex==-1 ? Qt.SplitHCursor : Qt.ArrowCursor - } - } - } - onWidthChanged: listView.contentWidth = width - } - Loader { - id: loader - property QtObject styleData: QtObject{ - readonly property string value: "" - readonly property bool pressed: false - readonly property bool containsMouse: false - readonly property int column: -1 - readonly property int textAlignment: Text.AlignLeft - } - - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: headerrow.bottom - anchors.rightMargin: -2 - sourceComponent: root.headerDelegate - width: root.width - headerrow.width + 2 - visible: root.columnCount - z:-1 - } - } } } diff --git a/src/controls/TableViewColumn.qml b/src/controls/TableViewColumn.qml index 6fa07045c..fc6d6a3d5 100644 --- a/src/controls/TableViewColumn.qml +++ b/src/controls/TableViewColumn.qml @@ -139,14 +139,27 @@ QtObject { function resizeToContents() { var minWidth = 0 var listdata = __view.__listView.children[0] - for (var i = 0; __index < 0 && i < __view.__columns.length; ++i) + for (var i = 0; __index === -1 && i < __view.__columns.length; ++i) { if (__view.__columns[i] === this) __index = i + } + // ### HACK We don't have direct access to the instantiated item, + // so we go spelunking. Each 'item' variable check is annotated + // with the expected object it should point to in BasicTableView. for (var row = 0 ; row < listdata.children.length ; ++row) { var item = listdata.children[row] ? listdata.children[row].rowItem : undefined - if (item && item.children[1] && item.children[1].children[__index] && item.children[1].children[__index].children[0] && - item.children[1].children[__index].children[0].hasOwnProperty("implicitWidth")) - minWidth = Math.max(minWidth, item.children[1].children[__index].children[0].implicitWidth) + if (item) { // FocusScope { id: rowitem } + item = item.children[1] + if (item) { // Row { id: itemrow } + item = item.children[__index] + if (item) { // Loader { id: itemDelegateLoader } + item = item.item + if (item && item.hasOwnProperty("implicitWidth")) { + minWidth = Math.max(minWidth, item.implicitWidth) + } + } + } + } } if (minWidth) width = minWidth diff --git a/tests/auto/controls/data/tableview/tv_keys.qml b/tests/auto/controls/data/tableview/tv_keys.qml index 46428fa44..1ee39e677 100644 --- a/tests/auto/controls/data/tableview/tv_keys.qml +++ b/tests/auto/controls/data/tableview/tv_keys.qml @@ -45,11 +45,13 @@ Row { TableView { id: _control1 property bool gotit: false + + TableViewColumn { } + Keys.onPressed: { + event.accepted = !gotit if ((!gotit) && (event.key === Qt.Key_Down)) { gotit = true; - event.accepted = true; - return; } } } diff --git a/tests/auto/controls/data/tst_tableview.qml b/tests/auto/controls/data/tst_tableview.qml index 335990003..b149e76b6 100644 --- a/tests/auto/controls/data/tst_tableview.qml +++ b/tests/auto/controls/data/tst_tableview.qml @@ -512,7 +512,7 @@ TestCase { table.forceActiveFocus(); // to go to next row (this model has 10 rows) - table.__incrementCurrentIndex() + table.__listView.incrementCurrentIndex() // read data from the model directly var valuefrommodel = table.model.dataAt(table.currentRow) @@ -541,7 +541,7 @@ TestCase { table.forceActiveFocus(); // to go to next row (this model has 3 rows, read the second row) - table.__incrementCurrentIndex() + table.__listView.incrementCurrentIndex() verify(table.__currentRowItem !== undefined, "No current item found") var label = findAChild(table.__currentRowItem, "label") |
