diff --git a/src/controls/TableView.qml b/src/controls/TableView.qml index 4cde5d3e37193c550243d9043f2db9f2f2397689..93d75e92c3cc5f42f4300919d41bfb9ae90b7b6a 100644 --- a/src/controls/TableView.qml +++ b/src/controls/TableView.qml @@ -200,9 +200,8 @@ ScrollView { */ property int sortIndicatorOrder: Qt.AscendingOrder - /*! \qmlproperty list<TableViewColumn> TableView::columns - This property contains the TableViewColumn items */ - default property alias columns: listView.columnheader + /*! \internal */ + default property alias __columns: root.data /*! \qmlproperty Component TableView::contentHeader This is the content header of the TableView */ @@ -216,8 +215,9 @@ ScrollView { The current number of rows */ readonly property alias rowCount: listView.count - /*! The current number of columns */ - readonly property int columnCount: columns.length + /*! \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 @@ -288,6 +288,73 @@ ScrollView { 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) + + if (index >= 0 && index <= columnCount && object.Accessible.role === Accessible.ColumnHeader) { + 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 + } + + Component.onCompleted: { + for (var i = 0; i < __columns.length; ++i) { + var column = __columns[i] + if (column.Accessible.role === Accessible.ColumnHeader) + addColumn(column) + } + } style: Qt.createComponent(Settings.style + "/TableViewStyle.qml", root) @@ -429,7 +496,10 @@ ScrollView { } } - property list<TableViewColumn> columnheader + ListModel { + id: columnModel + } + highlightFollowsCurrentItem: true model: root.model @@ -483,7 +553,7 @@ ScrollView { height: parent.height Repeater { id: repeater - model: root.columnCount + model: columnModel Loader { id: itemDelegateLoader @@ -509,7 +579,7 @@ ScrollView { readonly property string role: __column.role } - readonly property TableViewColumn __column: columns[index] + readonly property TableViewColumn __column: columnItem readonly property bool __hasModelRole: styleData.role && itemModel.hasOwnProperty(styleData.role) readonly property bool __hasModelDataRole: styleData.role && modelData && modelData.hasOwnProperty(styleData.role) } @@ -548,12 +618,12 @@ ScrollView { property int targetIndex: -1 property int dragIndex: -1 - model: columnCount + model: columnModel delegate: Item { z:-index - width: columns[index].width - visible: columns[index].visible + width: modelData.width + visible: modelData.visible height: headerVisible ? headerStyle.height : 0 Loader { @@ -562,7 +632,7 @@ ScrollView { anchors.left: parent.left anchors.right: parent.right property QtObject styleData: QtObject { - readonly property string value: columns[index].title + readonly property string value: modelData.title readonly property bool pressed: headerClickArea.pressed readonly property bool containsMouse: headerClickArea.containsMouse readonly property int column: index @@ -608,13 +678,7 @@ ScrollView { onReleased: { if (repeater.targetIndex >= 0 && repeater.targetIndex != index ) { - // Rearrange the header sections - var items = new Array - for (var i = 0 ; i< columnCount ; ++i) - items.push(columns[i]) - items.splice(index, 1); - items.splice(repeater.targetIndex, 0, columns[index]); - columns = items + columnModel.move(index, repeater.targetIndex, 1) if (sortIndicatorColumn == index) sortIndicatorColumn = repeater.targetIndex } @@ -628,14 +692,14 @@ ScrollView { Loader { id: draghandle property QtObject styleData: QtObject{ - readonly property string value: columns[index].title + readonly property string value: modelData.title readonly property bool pressed: headerClickArea.pressed readonly property bool containsMouse: headerClickArea.containsMouse readonly property int column: index } parent: tableHeader - width: columns[index].width + width: modelData.width height: parent.height sourceComponent: root.headerDelegate visible: headerClickArea.pressed @@ -651,8 +715,8 @@ ScrollView { width: 16 ; height: parent.height anchors.right: parent.right onPositionChanged: { - var newHeaderWidth = columns[index].width + (mouseX - offset) - columns[index].width = Math.max(minimumSize, newHeaderWidth) + var newHeaderWidth = modelData.width + (mouseX - offset) + modelData.width = Math.max(minimumSize, newHeaderWidth) } property bool found:false @@ -667,7 +731,7 @@ ScrollView { minWidth = Math.max(minWidth, item.children[1].children[index].children[0].implicitWidth) } if (minWidth) - columns[index].width = minWidth + modelData.width = minWidth } onPressedChanged: if (pressed) offset=mouseX cursorShape: Qt.SplitHCursor diff --git a/src/controls/TableViewColumn.qml b/src/controls/TableViewColumn.qml index 919f30de455787582dae7d93f06197f8ccc9df7c..2c629dfbdf9f7fc078b31a8eadafd71bba3ac054 100644 --- a/src/controls/TableViewColumn.qml +++ b/src/controls/TableViewColumn.qml @@ -89,4 +89,6 @@ QtObject { /*! The delegate of the column. This can be used to set the \l TableView::itemDelegate for a specific column. */ property Component delegate + + Accessible.role: Accessible.ColumnHeader } diff --git a/tests/auto/controls/data/tableview/table_dynamiccolumns.qml b/tests/auto/controls/data/tableview/table_dynamiccolumns.qml new file mode 100644 index 0000000000000000000000000000000000000000..ce2cb93c4ce9964339f0b348caed1005260472c2 --- /dev/null +++ b/tests/auto/controls/data/tableview/table_dynamiccolumns.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 + +TableView { + id: tableView + + TableViewColumn { title: "static" } + + Component { + id: component + TableViewColumn { } + } + + Component.onCompleted: { + addColumn(component.createObject(tableView, {title: "added item"})) + + var col1 = addColumn(component) + col1.title = "added component" + + insertColumn(0, component.createObject(tableView, {title: "inserted item"})) + + var col2 = insertColumn(0, component) + col2.title = "inserted component" + } +} diff --git a/tests/auto/controls/data/tst_tableview.qml b/tests/auto/controls/data/tst_tableview.qml index 6de8d57791a119852603d9412626d4bb1cdcdcff..d092ae9d599c20f2365c1abce60a571d0b28e457 100644 --- a/tests/auto/controls/data/tst_tableview.qml +++ b/tests/auto/controls/data/tst_tableview.qml @@ -55,6 +55,11 @@ TestCase { width:400 height:400 + Component { + id: newColumn + TableViewColumn { } + } + function test_usingqmlmodel_data() { return [ {tag: "listmodel", a: "tableview/table5_listmodel.qml", expected: "A"}, @@ -173,7 +178,14 @@ TestCase { compare(component.status, Component.Ready) var table = component.createObject(container); verify(table !== null, "table created is null") - table.forceActiveFocus(); + + // wait for items to be created + var timeout = 2000 + while (timeout >= 0 && table.rowAt(15, 55) === -1) { + timeout -= 50 + wait(50) + } + compare(table.test, 0) mouseClick(table, 15 , 55, Qt.LeftButton) compare(table.test, 1) @@ -228,6 +240,128 @@ TestCase { table.destroy() } + function test_dynamicColumns() { + var component = Qt.createComponent("tableview/table_dynamiccolumns.qml") + compare(component.status, Component.Ready) + var table = component.createObject(container) + + // insertColumn(component), insertColumn(item), + // addColumn(component), addColumn(item), and static TableViewColumn {} + compare(table.columnCount, 5) + compare(table.getColumn(0).title, "inserted component") + compare(table.getColumn(1).title, "inserted item") + table.destroy() + } + + function test_addRemoveColumn() { + var tableView = Qt.createQmlObject('import QtQuick 2.1; import QtQuick.Controls 1.0; TableView { }', testCase, ''); + compare(tableView.columnCount, 0) + tableView.addColumn(newColumn.createObject(testCase, {title: "title 1"})) + compare(tableView.columnCount, 1) + tableView.addColumn(newColumn.createObject(testCase, {title: "title 2"})) + compare(tableView.columnCount, 2) + compare(tableView.getColumn(0).title, "title 1") + compare(tableView.getColumn(1).title, "title 2") + + tableView.insertColumn(1, newColumn.createObject(testCase, {title: "title 3"})) + compare(tableView.columnCount, 3) + compare(tableView.getColumn(0).title, "title 1") + compare(tableView.getColumn(1).title, "title 3") + compare(tableView.getColumn(2).title, "title 2") + + tableView.insertColumn(0, newColumn.createObject(testCase, {title: "title 4"})) + compare(tableView.columnCount, 4) + compare(tableView.getColumn(0).title, "title 4") + compare(tableView.getColumn(1).title, "title 1") + compare(tableView.getColumn(2).title, "title 3") + compare(tableView.getColumn(3).title, "title 2") + + tableView.removeColumn(0) + compare(tableView.columnCount, 3) + compare(tableView.getColumn(0).title, "title 1") + compare(tableView.getColumn(1).title, "title 3") + compare(tableView.getColumn(2).title, "title 2") + + tableView.removeColumn(1) + compare(tableView.columnCount, 2) + compare(tableView.getColumn(0).title, "title 1") + compare(tableView.getColumn(1).title, "title 2") + + tableView.removeColumn(1) + compare(tableView.columnCount, 1) + compare(tableView.getColumn(0).title, "title 1") + + tableView.removeColumn(0) + compare(tableView.columnCount, 0) + tableView.destroy() + } + + function test_moveColumn_data() { + return [ + {tag:"0->1 (0)", from: 0, to: 1}, + {tag:"0->1 (1)", from: 0, to: 1}, + {tag:"0->1 (2)", from: 0, to: 1}, + + {tag:"0->2 (0)", from: 0, to: 2}, + {tag:"0->2 (1)", from: 0, to: 2}, + {tag:"0->2 (2)", from: 0, to: 2}, + + {tag:"1->0 (0)", from: 1, to: 0}, + {tag:"1->0 (1)", from: 1, to: 0}, + {tag:"1->0 (2)", from: 1, to: 0}, + + {tag:"1->2 (0)", from: 1, to: 2}, + {tag:"1->2 (1)", from: 1, to: 2}, + {tag:"1->2 (2)", from: 1, to: 2}, + + {tag:"2->0 (0)", from: 2, to: 0}, + {tag:"2->0 (1)", from: 2, to: 0}, + {tag:"2->0 (2)", from: 2, to: 0}, + + {tag:"2->1 (0)", from: 2, to: 1}, + {tag:"2->1 (1)", from: 2, to: 1}, + {tag:"2->1 (2)", from: 2, to: 1}, + + {tag:"0->0", from: 0, to: 0}, + {tag:"-1->0", from: -1, to: 0}, + {tag:"0->-1", from: 0, to: -1}, + {tag:"1->10", from: 1, to: 10}, + {tag:"10->2", from: 10, to: 2}, + {tag:"10->-1", from: 10, to: -1} + ] + } + + function test_moveColumn(data) { + var tableView = Qt.createQmlObject('import QtQuick 2.1; import QtQuick.Controls 1.0; TableView { }', testCase, ''); + compare(tableView.columnCount, 0) + + var titles = ["title 1", "title 2", "title 3"] + + var i = 0; + for (i = 0; i < titles.length; ++i) + tableView.addColumn(newColumn.createObject(testCase, {title: titles[i]})) + + compare(tableView.columnCount, titles.length) + for (i = 0; i < tableView.columnCount; ++i) + compare(tableView.getColumn(i).title, titles[i]) + + tableView.moveColumn(data.from, data.to) + + compare(tableView.columnCount, titles.length) + + if (data.from >= 0 && data.from < tableView.columnCount && data.to >= 0 && data.to < tableView.columnCount) { + var title = titles[data.from] + titles.splice(data.from, 1) + titles.splice(data.to, 0, title) + } + + compare(tableView.columnCount, titles.length) + for (i = 0; i < tableView.columnCount; ++i) + compare(tableView.getColumn(i).title, titles[i]) + + tableView.destroy() + } + // In TableView, drawn text = table.__currentRowItem.children[1].children[1].itemAt(0).children[0].children[0].text function findAChild(item, name)