/src/qml/QtQuick/Controls/TableView.qml

https://github.com/butterfly-cody/deepin-boot-maker · QML · 1091 lines · 584 code · 118 blank · 389 comment · 107 complexity · 10010f9fe3de44a28c330a0b988dc2c2 MD5 · raw file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of the Qt Quick Controls module of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:BSD$
  9. ** You may use this file under the terms of the BSD license as follows:
  10. **
  11. ** "Redistribution and use in source and binary forms, with or without
  12. ** modification, are permitted provided that the following conditions are
  13. ** met:
  14. ** * Redistributions of source code must retain the above copyright
  15. ** notice, this list of conditions and the following disclaimer.
  16. ** * Redistributions in binary form must reproduce the above copyright
  17. ** notice, this list of conditions and the following disclaimer in
  18. ** the documentation and/or other materials provided with the
  19. ** distribution.
  20. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
  21. ** of its contributors may be used to endorse or promote products derived
  22. ** from this software without specific prior written permission.
  23. **
  24. **
  25. ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  36. **
  37. ** $QT_END_LICENSE$
  38. **
  39. ****************************************************************************/
  40. import QtQuick 2.2
  41. import QtQuick.Controls 1.2
  42. import QtQuick.Controls.Private 1.0
  43. import QtQuick.Controls.Styles 1.1
  44. import QtQuick.Window 2.1
  45. /*!
  46. \qmltype TableView
  47. \inqmlmodule QtQuick.Controls
  48. \since 5.1
  49. \ingroup views
  50. \brief Provides a list view with scroll bars, styling and header sections.
  51. \image tableview.png
  52. A TableView is similar to \l ListView, and adds scroll bars, selection, and
  53. resizable header sections. As with \l ListView, data for each row is provided through a \l model:
  54. \code
  55. ListModel {
  56. id: libraryModel
  57. ListElement{ title: "A Masterpiece" ; author: "Gabriel" }
  58. ListElement{ title: "Brilliance" ; author: "Jens" }
  59. ListElement{ title: "Outstanding" ; author: "Frederik" }
  60. }
  61. \endcode
  62. You provide title and size of a column header
  63. by adding a \l TableViewColumn as demonstrated below.
  64. \code
  65. TableView {
  66. TableViewColumn{ role: "title" ; title: "Title" ; width: 100 }
  67. TableViewColumn{ role: "author" ; title: "Author" ; width: 200 }
  68. model: libraryModel
  69. }
  70. \endcode
  71. The header sections are attached to values in the \l model by defining
  72. the model role they attach to. Each property in the model will
  73. then be shown in their corresponding column.
  74. You can customize the look by overriding the \l itemDelegate,
  75. \l rowDelegate, or \l headerDelegate properties.
  76. The view itself does not provide sorting. This has to
  77. be done on the model itself. However you can provide sorting
  78. on the model, and enable sort indicators on headers.
  79. \list
  80. \li int sortIndicatorColumn - The index of the current sort column
  81. \li bool sortIndicatorVisible - Whether the sort indicator should be enabled
  82. \li enum sortIndicatorOrder - Qt.AscendingOrder or Qt.DescendingOrder depending on state
  83. \endlist
  84. You can create a custom appearance for a TableView by
  85. assigning a \l {QtQuick.Controls.Styles::TableViewStyle}{TableViewStyle}.
  86. */
  87. ScrollView {
  88. id: root
  89. /*! \qmlproperty model TableView::model
  90. This property holds the model providing data for the table view.
  91. The model provides the set of data that is used to create the items in the view.
  92. Models can be created directly in QML using ListModel, XmlListModel or VisualItemModel,
  93. or provided by C++ model classes. \sa ListView::model
  94. Example model:
  95. \code
  96. model: ListModel {
  97. ListElement{ column1: "value 1" ; column2: "value 2" }
  98. ListElement{ column1: "value 3" ; column2: "value 4" }
  99. }
  100. \endcode
  101. \sa {qml-data-models}{Data Models}
  102. */
  103. property var model
  104. /*! This property is set to \c true if the view alternates the row color.
  105. The default value is \c true. */
  106. property bool alternatingRowColors: true
  107. /*! This property determines if the header is visible.
  108. The default value is \c true. */
  109. property bool headerVisible: true
  110. /*! \qmlproperty bool TableView::backgroundVisible
  111. This property determines if the background should be filled or not.
  112. The default value is \c true.
  113. \note The rowDelegate is not affected by this property
  114. */
  115. property alias backgroundVisible: colorRect.visible
  116. /*! This property defines a delegate to draw a specific cell.
  117. In the item delegate you have access to the following special properties:
  118. \list
  119. \li styleData.selected - if the item is currently selected
  120. \li styleData.value - the value or text for this item
  121. \li styleData.textColor - the default text color for an item
  122. \li styleData.row - the index of the row
  123. \li styleData.column - the index of the column
  124. \li styleData.elideMode - the elide mode of the column
  125. \li styleData.textAlignment - the horizontal text alignment of the column
  126. \endlist
  127. Example:
  128. \code
  129. itemDelegate: Item {
  130. Text {
  131. anchors.verticalCenter: parent.verticalCenter
  132. color: styleData.textColor
  133. elide: styleData.elideMode
  134. text: styleData.value
  135. }
  136. }
  137. \endcode
  138. \note For performance reasons, created delegates can be recycled
  139. across multiple table rows. This implies that when you make use of implicit
  140. properties such as \c styledata.row or \c model, these values can change also
  141. after the delegate has been constructed. In practice this means you should not assume
  142. that content is fixed when \c Component.onCompleted happens, but instead rely on
  143. bindings to such properties.
  144. */
  145. property Component itemDelegate: __style ? __style.itemDelegate : null
  146. /*! This property defines a delegate to draw a row.
  147. In the row delegate you have access to the following special properties:
  148. \list
  149. \li styleData.alternate - true when the row uses the alternate background color
  150. \li styleData.selected - true when the row is currently selected
  151. \li styleData.row - the index of the row
  152. \endlist
  153. \note For performance reasons, created delegates can be recycled
  154. across multiple table rows. This implies that when you make use of implicit
  155. properties such as \c styledata.row or \c model, these values can change also
  156. after the delegate has been constructed. In practice this means you should not assume
  157. that content is fixed when \c Component.onCompleted happens, but instead rely on
  158. bindings to such properties.
  159. */
  160. property Component rowDelegate: __style ? __style.rowDelegate : null
  161. /*! This property defines a delegate to draw a header.
  162. In the header delegate you have access to the following special properties:
  163. \list
  164. \li styleData.value - the value or text for this item
  165. \li styleData.column - the index of the column
  166. \li styleData.pressed - true when the column is being pressed
  167. \li styleData.containsMouse - true when the column is under the mouse
  168. \li styleData.textAlignment - the horizontal text alignment of the column (since QtQuickControls 1.1)
  169. \endlist
  170. */
  171. property Component headerDelegate: __style ? __style.headerDelegate : null
  172. /*! Index of the current sort column.
  173. The default value is \c {0}. */
  174. property int sortIndicatorColumn
  175. /*! This property shows or hides the sort indicator
  176. The default value is \c false.
  177. \note The view itself does not sort the data. */
  178. property bool sortIndicatorVisible: false
  179. /*!
  180. \qmlproperty enumeration TableView::sortIndicatorOrder
  181. This sets the sorting order of the sort indicator
  182. The allowed values are:
  183. \list
  184. \li Qt.AscendingOrder - the default
  185. \li Qt.DescendingOrder
  186. \endlist
  187. */
  188. property int sortIndicatorOrder: Qt.AscendingOrder
  189. /*! \internal */
  190. default property alias __columns: root.data
  191. /*! \qmlproperty Component TableView::contentHeader
  192. This is the content header of the TableView */
  193. property alias contentHeader: listView.header
  194. /*! \qmlproperty Component TableView::contentFooter
  195. This is the content footer of the TableView */
  196. property alias contentFooter: listView.footer
  197. /*! \qmlproperty int TableView::rowCount
  198. The current number of rows */
  199. readonly property alias rowCount: listView.count
  200. /*! \qmlproperty int TableView::columnCount
  201. The current number of columns */
  202. readonly property alias columnCount: columnModel.count
  203. /*! \qmlproperty string TableView::section.property
  204. \qmlproperty enumeration TableView::section.criteria
  205. \qmlproperty Component TableView::section.delegate
  206. \qmlproperty enumeration TableView::section.labelPositioning
  207. These properties determine the section labels.
  208. \sa ListView::section */
  209. property alias section: listView.section
  210. /*! \qmlproperty int TableView::currentRow
  211. The current row index of the view.
  212. The default value is \c -1 to indicate that no row is selected.
  213. */
  214. property alias currentRow: listView.currentIndex
  215. /*! \internal */
  216. property alias __currentRowItem: listView.currentItem
  217. /*! \internal */
  218. property alias __listView: listView
  219. /*! \qmlsignal TableView::activated(int row)
  220. Emitted when the user activates an item by mouse or keyboard interaction.
  221. Mouse activation is triggered by single- or double-clicking, depending on the platform.
  222. \a row int provides access to the activated row index.
  223. \note This signal is only emitted for mouse interaction that is not blocked in the row or item delegate.
  224. The corresponding handler is \c onActivated.
  225. */
  226. signal activated(int row)
  227. /*! \qmlsignal TableView::clicked(int row)
  228. Emitted when the user clicks a valid row by single clicking
  229. \a row int provides access to the clicked row index.
  230. \note This signal is only emitted if the row or item delegate does not accept mouse events.
  231. The corresponding handler is \c onClicked.
  232. */
  233. signal clicked(int row)
  234. /*! \qmlsignal TableView::doubleClicked(int row)
  235. Emitted when the user double clicks a valid row.
  236. \a row int provides access to the clicked row index.
  237. \note This signal is only emitted if the row or item delegate does not accept mouse events.
  238. The corresponding handler is \c onDoubleClicked.
  239. */
  240. signal doubleClicked(int row)
  241. /*!
  242. \qmlmethod TableView::positionViewAtRow( int row, PositionMode mode )
  243. Positions the view such that the specified \a row is at the position defined by \a mode:
  244. \list
  245. \li ListView.Beginning - position item at the top of the view.
  246. \li ListView.Center - position item in the center of the view.
  247. \li ListView.End - position item at bottom of the view.
  248. \li ListView.Visible - if any part of the item is visible then take no action, otherwise bring the item into view.
  249. \li ListView.Contain - ensure the entire item is visible. If the item is larger than the view the item is positioned
  250. at the top of the view.
  251. \endlist
  252. If positioning the \a row creates an empty space at the beginning
  253. or end of the view, then the view is positioned at the boundary.
  254. For example, to position the view at the end at startup:
  255. \code
  256. Component.onCompleted: table.positionViewAtRow(rowCount -1, ListView.Contain)
  257. \endcode
  258. Depending on how the model is populated, the model may not be ready when
  259. TableView Component.onCompleted is called. In that case you may need to
  260. delay the call to positionViewAtRow by using a \l {QtQml::Timer}{Timer}.
  261. \note This method should only be called after the component has completed.
  262. */
  263. function positionViewAtRow(row, mode) {
  264. listView.positionViewAtIndex(row, mode)
  265. }
  266. /*!
  267. \qmlmethod int TableView::rowAt( int x, int y )
  268. Returns the index of the visible row at the point \a x, \a y in content
  269. coordinates. If there is no visible row at the point specified, \c -1 is returned.
  270. \note This method should only be called after the component has completed.
  271. */
  272. function rowAt(x, y) {
  273. var obj = root.mapToItem(listView.contentItem, x, y)
  274. return listView.indexAt(obj.x, obj.y)
  275. }
  276. /*! Adds a \a column and returns the added column.
  277. The \a column argument can be an instance of TableViewColumn,
  278. or a Component. The component has to contain a TableViewColumn.
  279. Otherwise \c null is returned.
  280. */
  281. function addColumn(column) {
  282. return insertColumn(columnCount, column)
  283. }
  284. /*! Inserts a \a column at the given \a index and returns the inserted column.
  285. The \a column argument can be an instance of TableViewColumn,
  286. or a Component. The component has to contain a TableViewColumn.
  287. Otherwise \c null is returned.
  288. */
  289. function insertColumn(index, column) {
  290. var object = column
  291. if (typeof column['createObject'] === 'function')
  292. object = column.createObject(root)
  293. else if (object.__view) {
  294. console.warn("TableView::insertColumn(): you cannot add a column to multiple views")
  295. return null
  296. }
  297. if (index >= 0 && index <= columnCount && object.Accessible.role === Accessible.ColumnHeader) {
  298. object.__view = root
  299. columnModel.insert(index, {columnItem: object})
  300. return object
  301. }
  302. if (object !== column)
  303. object.destroy()
  304. console.warn("TableView::insertColumn(): invalid argument")
  305. return null
  306. }
  307. /*! Removes and destroys a column at the given \a index. */
  308. function removeColumn(index) {
  309. if (index < 0 || index >= columnCount) {
  310. console.warn("TableView::removeColumn(): invalid argument")
  311. return
  312. }
  313. var column = columnModel.get(index).columnItem
  314. columnModel.remove(index, 1)
  315. column.destroy()
  316. }
  317. /*! Moves a column \a from index \a to another. */
  318. function moveColumn(from, to) {
  319. if (from < 0 || from >= columnCount || to < 0 || to >= columnCount) {
  320. console.warn("TableView::moveColumn(): invalid argument")
  321. return
  322. }
  323. columnModel.move(from, to, 1)
  324. }
  325. /*! Returns the column at the given \a index
  326. or \c null if the \a index is invalid. */
  327. function getColumn(index) {
  328. if (index < 0 || index >= columnCount)
  329. return null
  330. return columnModel.get(index).columnItem
  331. }
  332. /*! \qmlproperty Selection TableView::selection
  333. \since QtQuick.Controls 1.1
  334. This property contains the current row-selection of the \l TableView.
  335. The selection allows you to select, deselect or iterate over selected rows.
  336. \list
  337. \li function \b clear() - deselects all rows
  338. \li function \b selectAll() - selects all rows
  339. \li function \b select(from, to) - select a range
  340. \li functton \b deselect(from, to) - de-selects a range
  341. \li function \b forEach(callback) - iterates over all selected rows
  342. \li function \b contains(index) - checks whether the selection includes the given index
  343. \li signal \b selectionChanged() - the current row selection changed
  344. \li readonly property int \b count - the number of selected rows
  345. \endlist
  346. \b Example:
  347. \code
  348. tableview.selection.select(0) // select row index 0
  349. tableview.selection.select(1, 3) // select row indexes 1, 2 and 3
  350. tableview.selection.deselect(0, 1) // deselects row index 0 and 1
  351. tableview.selection.deselect(2) // deselects row index 2
  352. \endcode
  353. \b Example: To iterate over selected indexes, you can pass a callback function.
  354. \a rowIndex is passed as as an argument to the callback function.
  355. \code
  356. tableview.selection.forEach( function(rowIndex) {console.log(rowIndex)} )
  357. \endcode
  358. */
  359. readonly property alias selection: selectionObject
  360. /*!
  361. \qmlproperty enumeration TableView::selectionMode
  362. \since QtQuick.Controls 1.1
  363. This enum indicates how the view responds to user selections:
  364. The possible modes are:
  365. \list
  366. \li SelectionMode.NoSelection - Items cannot be selected.
  367. \li SelectionMode.SingleSelection - When the user selects an item,
  368. any already-selected item becomes unselected, and the user cannot
  369. unselect the selected item. (Default)
  370. \li SelectionMode.MultiSelection - When the user selects an item in the usual way,
  371. the selection status of that item is toggled and the other items are left alone.
  372. \li SelectionMode.ExtendedSelection - When the user selects an item in the usual way,
  373. the selection is cleared and the new item selected. However, if the user presses the
  374. Ctrl key when clicking on an item, the clicked item gets toggled and all other items
  375. are left untouched. If the user presses the Shift key while clicking
  376. on an item, all items between the current item and the clicked item are selected or unselected,
  377. depending on the state of the clicked item. Multiple items can be selected by dragging the
  378. mouse over them.
  379. \li SelectionMode.ContiguousSelection - When the user selects an item in the usual way,
  380. the selection is cleared and the new item selected. However, if the user presses the Shift key while
  381. clicking on an item, all items between the current item and the clicked item are selected.
  382. \endlist
  383. */
  384. property int selectionMode: SelectionMode.SingleSelection
  385. /*! Resizes all columns to ensure that the column contents and the headers will fit.
  386. \since QtQuick.Controls 1.2 */
  387. function resizeColumnsToContents () {
  388. for (var i = 0; i < __columns.length; ++i) {
  389. var col = getColumn(i)
  390. var header = repeater.itemAt(i)
  391. if (col) {
  392. col.__index = i
  393. col.resizeToContents()
  394. if (col.width < header.implicitWidth)
  395. col.width = header.implicitWidth
  396. }
  397. }
  398. }
  399. Component.onCompleted: {
  400. for (var i = 0; i < __columns.length; ++i) {
  401. var column = __columns[i]
  402. if (column.Accessible.role === Accessible.ColumnHeader)
  403. addColumn(column)
  404. }
  405. }
  406. style: Qt.createComponent(Settings.style + "/TableViewStyle.qml", root)
  407. Accessible.role: Accessible.Table
  408. implicitWidth: 200
  409. implicitHeight: 150
  410. frameVisible: true
  411. __scrollBarTopMargin: (__style && __style.transientScrollBars || Qt.platform.os === "osx") ? headerrow.height : 0
  412. __viewTopMargin: headerrow.height
  413. /*! \internal */
  414. property bool __activateItemOnSingleClick: __style ? __style.activateItemOnSingleClick : false
  415. /*! \internal */
  416. function __decrementCurrentIndex() {
  417. __scroller.blockUpdates = true;
  418. listView.decrementCurrentIndex();
  419. __scroller.blockUpdates = false;
  420. var newIndex = listView.indexAt(0, listView.contentY)
  421. if (newIndex !== -1) {
  422. if (selectionMode > SelectionMode.SingleSelection)
  423. mousearea.dragRow = newIndex
  424. else if (selectionMode === SelectionMode.SingleSelection)
  425. selection.__selectOne(newIndex)
  426. }
  427. }
  428. /*! \internal */
  429. function __incrementCurrentIndex() {
  430. __scroller.blockUpdates = true;
  431. listView.incrementCurrentIndex();
  432. __scroller.blockUpdates = false;
  433. var newIndex = Math.max(0, listView.indexAt(0, listView.height + listView.contentY))
  434. if (newIndex !== -1) {
  435. if (selectionMode > SelectionMode.SingleSelection)
  436. mousearea.dragRow = newIndex
  437. else if (selectionMode === SelectionMode.SingleSelection)
  438. selection.__selectOne(newIndex)
  439. }
  440. }
  441. onModelChanged: selection.clear()
  442. ListView {
  443. id: listView
  444. focus: true
  445. activeFocusOnTab: root.activeFocusOnTab
  446. anchors.topMargin: tableHeader.height
  447. anchors.fill: parent
  448. currentIndex: -1
  449. visible: columnCount > 0
  450. interactive: Settings.hasTouchScreen
  451. property var rowItemStack: [] // Used as a cache for rowDelegates
  452. SystemPalette {
  453. id: palette
  454. colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled
  455. }
  456. Rectangle {
  457. id: colorRect
  458. parent: viewport
  459. anchors.fill: parent
  460. color: __style ? __style.backgroundColor : palette.base
  461. z: -2
  462. }
  463. MouseArea {
  464. id: mousearea
  465. z: -1
  466. anchors.fill: listView
  467. propagateComposedEvents: true
  468. property bool autoincrement: false
  469. property bool autodecrement: false
  470. property int mouseModifiers: 0
  471. property int previousRow: 0
  472. property int clickedRow: -1
  473. property int dragRow: -1
  474. property int firstKeyRow: -1
  475. onReleased: {
  476. autoincrement = false
  477. autodecrement = false
  478. var clickIndex = listView.indexAt(0, mouseY + listView.contentY)
  479. if (clickIndex > -1) {
  480. if (Settings.hasTouchScreen) {
  481. listView.currentIndex = clickIndex
  482. mouseSelect(clickIndex, mouse.modifiers)
  483. }
  484. previousRow = clickIndex
  485. }
  486. if (mousearea.dragRow >= 0) {
  487. selection.__select(selection.contains(mousearea.clickedRow), mousearea.clickedRow, mousearea.dragRow)
  488. mousearea.dragRow = -1
  489. }
  490. }
  491. // Handle vertical scrolling whem dragging mouse outside boundraries
  492. Timer { running: mousearea.autoincrement && __verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: __incrementCurrentIndex()}
  493. Timer { running: mousearea.autodecrement && __verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: __decrementCurrentIndex()}
  494. onPositionChanged: {
  495. if (mouseY > listView.height && pressed) {
  496. if (autoincrement) return;
  497. autodecrement = false;
  498. autoincrement = true;
  499. } else if (mouseY < 0 && pressed) {
  500. if (autodecrement) return;
  501. autoincrement = false;
  502. autodecrement = true;
  503. } else {
  504. autoincrement = false;
  505. autodecrement = false;
  506. }
  507. if (pressed && !Settings.hasTouchScreen) {
  508. var newIndex = Math.max(0, listView.indexAt(0, mouseY + listView.contentY))
  509. if (newIndex >= 0 && newIndex !== currentRow) {
  510. listView.currentIndex = newIndex;
  511. if (selectionMode === SelectionMode.SingleSelection) {
  512. selection.__selectOne(newIndex)
  513. } else if (selectionMode > 1) {
  514. dragRow = newIndex
  515. }
  516. }
  517. }
  518. mouseModifiers = mouse.modifiers
  519. }
  520. onClicked: {
  521. var clickIndex = listView.indexAt(0, mouseY + listView.contentY)
  522. if (clickIndex > -1) {
  523. if (root.__activateItemOnSingleClick)
  524. root.activated(clickIndex)
  525. root.clicked(clickIndex)
  526. }
  527. }
  528. onPressed: {
  529. var newIndex = listView.indexAt(0, mouseY + listView.contentY)
  530. listView.forceActiveFocus()
  531. if (newIndex > -1 && !Settings.hasTouchScreen) {
  532. listView.currentIndex = newIndex
  533. mouseSelect(newIndex, mouse.modifiers)
  534. mousearea.clickedRow = newIndex
  535. }
  536. mouseModifiers = mouse.modifiers
  537. }
  538. function mouseSelect(index, modifiers) {
  539. if (selectionMode) {
  540. if (modifiers & Qt.ShiftModifier && (selectionMode === SelectionMode.ExtendedSelection)) {
  541. selection.select(previousRow, index)
  542. } else if (selectionMode === SelectionMode.MultiSelection ||
  543. (selectionMode === SelectionMode.ExtendedSelection && modifiers & Qt.ControlModifier)) {
  544. selection.__select(!selection.contains(index) , index)
  545. } else {
  546. selection.__selectOne(index)
  547. }
  548. }
  549. }
  550. onDoubleClicked: {
  551. var clickIndex = listView.indexAt(0, mouseY + listView.contentY)
  552. if (clickIndex > -1) {
  553. if (!root.__activateItemOnSingleClick)
  554. root.activated(clickIndex)
  555. root.doubleClicked(clickIndex)
  556. }
  557. }
  558. // Note: with boolean preventStealing we are keeping the flickable from
  559. // eating our mouse press events
  560. preventStealing: !Settings.hasTouchScreen
  561. TableViewSelection { id: selectionObject }
  562. }
  563. // Fills extra rows with alternate color
  564. Column {
  565. id: rowfiller
  566. Loader {
  567. id: rowSizeItem
  568. sourceComponent: root.rowDelegate
  569. visible: false
  570. property QtObject styleData: QtObject {
  571. property bool alternate: false
  572. property bool selected: false
  573. property bool hasActiveFocus: false
  574. }
  575. }
  576. property int rowHeight: rowSizeItem.implicitHeight
  577. property int paddedRowCount: height/rowHeight
  578. y: listView.contentHeight
  579. width: parent.width
  580. visible: alternatingRowColors
  581. height: viewport.height - listView.contentHeight
  582. Repeater {
  583. model: visible ? parent.paddedRowCount : 0
  584. Loader {
  585. width: rowfiller.width
  586. height: rowfiller.rowHeight
  587. sourceComponent: root.rowDelegate
  588. property QtObject styleData: QtObject {
  589. readonly property bool alternate: (index + rowCount) % 2 === 1
  590. readonly property bool selected: false
  591. readonly property bool hasActiveFocus: root.activeFocus
  592. }
  593. readonly property var model: listView.model
  594. readonly property var modelData: null
  595. }
  596. }
  597. }
  598. ListModel {
  599. id: columnModel
  600. }
  601. highlightFollowsCurrentItem: true
  602. model: root.model
  603. function keySelect(shiftPressed, row) {
  604. if (row < 0 || row > rowCount - 1)
  605. return
  606. if (shiftPressed && (selectionMode >= SelectionMode.ExtendedSelection)) {
  607. selection.__ranges = new Array()
  608. selection.select(mousearea.firstKeyRow, row)
  609. } else {
  610. selection.__selectOne(row)
  611. }
  612. }
  613. Keys.onUpPressed: {
  614. event.accepted = false
  615. __scroller.blockUpdates = true;
  616. listView.decrementCurrentIndex();
  617. __scroller.blockUpdates = false;
  618. if (selectionMode)
  619. keySelect(event.modifiers & Qt.ShiftModifier, currentRow)
  620. }
  621. Keys.onDownPressed: {
  622. event.accepted = false
  623. __scroller.blockUpdates = true;
  624. listView.incrementCurrentIndex();
  625. __scroller.blockUpdates = false;
  626. if (selectionMode)
  627. keySelect(event.modifiers & Qt.ShiftModifier, currentRow)
  628. }
  629. Keys.onPressed: {
  630. if (event.key === Qt.Key_PageUp) {
  631. __verticalScrollBar.value = __verticalScrollBar.value - listView.height
  632. } else if (event.key === Qt.Key_PageDown)
  633. __verticalScrollBar.value = __verticalScrollBar.value + listView.height
  634. if (event.key === Qt.Key_Shift) {
  635. mousearea.firstKeyRow = currentRow
  636. }
  637. if (event.key === Qt.Key_A && event.modifiers & Qt.ControlModifier) {
  638. if (selectionMode > 1)
  639. selection.selectAll()
  640. }
  641. }
  642. Keys.onReleased: {
  643. if (event.key === Qt.Key_Shift)
  644. mousearea.firstKeyRow = -1
  645. }
  646. Keys.onReturnPressed: {
  647. event.accepted = false
  648. if (currentRow > -1)
  649. root.activated(currentRow);
  650. }
  651. delegate: Item {
  652. id: rowItemContainer
  653. property Item rowItem
  654. // We recycle instantiated row items to speed up list scrolling
  655. Component.onDestruction: {
  656. // move the rowItem back in cache
  657. if (rowItem) {
  658. rowItem.visible = false;
  659. rowItem.parent = null;
  660. listView.rowItemStack.push(rowItem); // return rowItem to cache
  661. }
  662. }
  663. Component.onCompleted: {
  664. // retrieve row item from cache
  665. if (listView.rowItemStack.length > 0)
  666. rowItem = listView.rowItemStack.pop();
  667. else
  668. rowItem = rowComponent.createObject(listView);
  669. // Bind container to item size
  670. rowItemContainer.width = Qt.binding( function() { return rowItem.width });
  671. rowItemContainer.height = Qt.binding( function() { return rowItem.height });
  672. // Reassign row-specific bindings
  673. rowItem.rowIndex = model.index;
  674. rowItem.itemModelData = Qt.binding( function() { return typeof modelData === "undefined" ? null : modelData });
  675. rowItem.itemModel = Qt.binding( function() { return model });
  676. rowItem.parent = rowItemContainer;
  677. rowItem.visible = true;
  678. }
  679. }
  680. Component {
  681. id: rowComponent
  682. FocusScope {
  683. id: rowitem
  684. visible: false
  685. property int rowIndex
  686. property var itemModelData
  687. property var itemModel
  688. property bool itemSelected: selected()
  689. property bool alternate: alternatingRowColors && rowIndex % 2 === 1
  690. readonly property color itemTextColor: itemSelected ? __style.highlightedTextColor : __style.textColor
  691. function selected() {
  692. if (mousearea.dragRow > -1 && (rowIndex >= mousearea.clickedRow && rowIndex <= mousearea.dragRow
  693. || rowIndex <= mousearea.clickedRow && rowIndex >=mousearea.dragRow))
  694. return selection.contains(mousearea.clickedRow)
  695. return selection.count && selection.contains(rowIndex)
  696. }
  697. width: itemrow.width
  698. height: rowstyle.height
  699. onActiveFocusChanged: {
  700. if (activeFocus)
  701. listView.currentIndex = rowIndex
  702. }
  703. Loader {
  704. id: rowstyle
  705. // row delegate
  706. sourceComponent: rowitem.itemModel !== undefined ? root.rowDelegate : null
  707. // Row fills the view width regardless of item size
  708. // But scrollbar should not adjust to it
  709. height: item ? item.height : 16
  710. width: parent.width + __horizontalScrollBar.width
  711. x: listView.contentX
  712. // these properties are exposed to the row delegate
  713. // Note: these properties should be mirrored in the row filler as well
  714. property QtObject styleData: QtObject {
  715. readonly property int row: rowitem.rowIndex
  716. readonly property bool alternate: rowitem.alternate
  717. readonly property bool selected: rowitem.itemSelected
  718. readonly property bool hasActiveFocus: root.activeFocus
  719. }
  720. readonly property var model: listView.model
  721. readonly property var modelData: rowitem.itemModelData
  722. }
  723. Row {
  724. id: itemrow
  725. height: parent.height
  726. Repeater {
  727. id: repeater
  728. model: columnModel
  729. Loader {
  730. id: itemDelegateLoader
  731. width: columnItem.width
  732. height: parent ? parent.height : 0
  733. visible: columnItem.visible
  734. sourceComponent: rowitem.itemModel !== undefined ? // delays construction until model is initialized
  735. (columnItem.delegate ? columnItem.delegate : itemDelegate) : null
  736. // these properties are exposed to the item delegate
  737. readonly property var model: itemModel
  738. readonly property var modelData: itemModelData
  739. property QtObject styleData: QtObject {
  740. readonly property int row: rowitem.rowIndex
  741. readonly property int column: index
  742. readonly property int elideMode: columnItem.elideMode
  743. readonly property int textAlignment: columnItem.horizontalAlignment
  744. readonly property bool selected: rowitem.itemSelected
  745. readonly property color textColor: rowitem.itemTextColor
  746. readonly property string role: columnItem.role
  747. readonly property var value: (itemModel && itemModel.hasOwnProperty(role))
  748. ? itemModel[role] // Qml ListModel and QAbstractItemModel
  749. : modelData && modelData.hasOwnProperty(role)
  750. ? modelData[role] // QObjectList / QObject
  751. : modelData != undefined ? modelData : "" // Models without role
  752. }
  753. }
  754. }
  755. }
  756. }
  757. }
  758. Text{ id:text }
  759. Item {
  760. id: tableHeader
  761. clip: true
  762. parent: __scroller
  763. visible: headerVisible
  764. anchors.top: parent.top
  765. anchors.topMargin: viewport.anchors.topMargin
  766. anchors.leftMargin: viewport.anchors.leftMargin
  767. anchors.margins: viewport.anchors.margins
  768. anchors.rightMargin: (frameVisible ? __scroller.rightMargin : 0) +
  769. (__scroller.outerFrame && __scrollBarTopMargin ? 0 : __verticalScrollBar.width
  770. + __scroller.scrollBarSpacing + root.__style.padding.right)
  771. anchors.left: parent.left
  772. anchors.right: parent.right
  773. height: headerrow.height
  774. Row {
  775. id: headerrow
  776. x: -listView.contentX
  777. Repeater {
  778. id: repeater
  779. property int targetIndex: -1
  780. property int dragIndex: -1
  781. model: columnModel
  782. delegate: Item {
  783. id: headerRowDelegate
  784. z:-index
  785. width: columnCount === 1 ? viewport.width + __verticalScrollBar.width : modelData.width
  786. implicitWidth: headerStyle.implicitWidth
  787. visible: modelData.visible
  788. height: headerVisible ? headerStyle.height : 0
  789. Loader {
  790. id: headerStyle
  791. sourceComponent: root.headerDelegate
  792. anchors.left: parent.left
  793. anchors.right: parent.right
  794. property QtObject styleData: QtObject {
  795. readonly property string value: modelData.title
  796. readonly property bool pressed: headerClickArea.pressed
  797. readonly property bool containsMouse: headerClickArea.containsMouse
  798. readonly property int column: index
  799. readonly property int textAlignment: modelData.horizontalAlignment
  800. }
  801. }
  802. Rectangle{
  803. id: targetmark
  804. width: parent.width
  805. height:parent.height
  806. opacity: (index === repeater.targetIndex && repeater.targetIndex !== repeater.dragIndex) ? 0.5 : 0
  807. Behavior on opacity { NumberAnimation{duration:160}}
  808. color: palette.highlight
  809. visible: modelData.movable
  810. }
  811. MouseArea{
  812. id: headerClickArea
  813. drag.axis: Qt.YAxis
  814. hoverEnabled: true
  815. anchors.fill: parent
  816. onClicked: {
  817. if (sortIndicatorColumn === index)
  818. sortIndicatorOrder = sortIndicatorOrder === Qt.AscendingOrder ? Qt.DescendingOrder : Qt.AscendingOrder
  819. sortIndicatorColumn = index
  820. }
  821. // Here we handle moving header sections
  822. // NOTE: the direction is different from the master branch
  823. // so this indicates that I am using an invalid assumption on item ordering
  824. onPositionChanged: {
  825. if (modelData.movable && pressed && columnCount > 1) { // only do this while dragging
  826. for (var h = columnCount-1 ; h >= 0 ; --h) {
  827. if (drag.target.x + listView.contentX + headerRowDelegate.width/2 > headerrow.children[h].x) {
  828. repeater.targetIndex = h
  829. break
  830. }
  831. }
  832. }
  833. }
  834. onPressed: {
  835. repeater.dragIndex = index
  836. }
  837. onReleased: {
  838. if (repeater.targetIndex >= 0 && repeater.targetIndex !== index ) {
  839. var targetColumn = columnModel.get(repeater.targetIndex).columnItem
  840. if (targetColumn.movable) {
  841. columnModel.move(index, repeater.targetIndex, 1)
  842. if (sortIndicatorColumn === index)
  843. sortIndicatorColumn = repeater.targetIndex
  844. }
  845. }
  846. repeater.targetIndex = -1
  847. }
  848. drag.maximumX: 1000
  849. drag.minimumX: -1000
  850. drag.target: modelData.movable && columnCount > 1 ? draghandle : null
  851. }
  852. Loader {
  853. id: draghandle
  854. property QtObject styleData: QtObject{
  855. readonly property string value: modelData.title
  856. readonly property bool pressed: headerClickArea.pressed
  857. readonly property bool containsMouse: headerClickArea.containsMouse
  858. readonly property int column: index
  859. readonly property int textAlignment: modelData.horizontalAlignment
  860. }
  861. parent: tableHeader
  862. x: headerRowDelegate.x - listView.contentX
  863. width: modelData.width
  864. height: parent.height
  865. sourceComponent: root.headerDelegate
  866. visible: headerClickArea.pressed
  867. opacity: 0.5
  868. }
  869. MouseArea {
  870. id: headerResizeHandle
  871. property int offset: 0
  872. property int minimumSize: 20
  873. preventStealing: true
  874. anchors.rightMargin: -width/2
  875. width: Settings.hasTouchScreen ? Screen.pixelDensity * 3.5 : 16
  876. height: parent.height
  877. anchors.right: parent.right
  878. enabled: modelData.resizable && columnCount > 1
  879. onPositionChanged: {
  880. var newHeaderWidth = modelData.width + (mouseX - offset)
  881. modelData.width = Math.max(minimumSize, newHeaderWidth)
  882. }
  883. onDoubleClicked: getColumn(index).resizeToContents()
  884. onPressedChanged: if (pressed) offset=mouseX
  885. cursorShape: enabled ? Qt.SplitHCursor : Qt.ArrowCursor
  886. }
  887. }
  888. }
  889. onWidthChanged: listView.contentWidth = width
  890. }
  891. Loader {
  892. id: loader
  893. property QtObject styleData: QtObject{
  894. readonly property string value: ""
  895. readonly property bool pressed: false
  896. readonly property bool containsMouse: false
  897. readonly property int column: -1
  898. readonly property int textAlignment: Text.AlignLeft
  899. }
  900. anchors.top: parent.top
  901. anchors.right: parent.right
  902. anchors.bottom: headerrow.bottom
  903. anchors.rightMargin: -2
  904. sourceComponent: root.headerDelegate
  905. width: root.width - headerrow.width + 2
  906. visible: root.columnCount
  907. z:-1
  908. }
  909. }
  910. }
  911. }