Files
qt-creator/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
Samuel Ghinet 7e5ecfe8bc Fix glitch in AssetsView when dragging files onto it
Moved the DropArea from the AssetDelegate into the AssetsView, so that
moving the cursor through the delegates no longer denies and then
permits dragging. This glitch happened because the DropArea was inside
the delegate, while the TreeView also has rowSpacing, which are areas
that do not belong to the delegates, and for which you don't normally
have drag & drop support.

Task-number: QDS-8232
Change-Id: If49a384f25bb870105448156f436e048479e880c
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
2022-12-15 11:54:34 +00:00

396 lines
12 KiB
QML

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
TreeView {
id: root
clip: true
interactive: verticalScrollBar.visible && !root.contextMenu.opened
reuseItems: false
boundsBehavior: Flickable.StopAtBounds
rowSpacing: 5
required property Item assetsRoot
required property StudioControls.Menu contextMenu
property alias verticalScrollBar: verticalScrollBar
property var selectedAssets: ({})
// used to see if the op requested is to expand or to collapse.
property int lastRowCount: -1
// we need this to know if we need to expand further, while we're in onRowsChanged()
property bool requestedExpandAll: true
// used to compute the visual depth of the items we show to the user.
property int rootPathDepth: 0
property int rootPathRow: 0
// i.e. first child of the root path
readonly property int firstRow: root.rootPathRow + 1
property int rowToExpand: -1
property var __createdDirectories: []
rowHeightProvider: (row) => {
if (row <= root.rootPathRow)
return 0
return -1
}
ScrollBar.vertical: HelperWidgets.VerticalScrollBar {
id: verticalScrollBar
scrollBarVisible: root.contentHeight > root.height
}
model: assetsModel
onRowsChanged: {
if (root.rows > root.rootPathRow + 1 && !assetsModel.haveFiles ||
root.rows <= root.rootPathRow + 1 && assetsModel.haveFiles) {
assetsModel.syncHaveFiles()
}
updateRows()
}
Timer {
id: updateRowsTimer
interval: 200
repeat: false
onTriggered: {
root.updateRows()
}
}
Connections {
target: rootView
function onDirectoryCreated(path)
{
root.__createdDirectories.push(path)
updateRowsTimer.restart()
}
}
Connections {
target: assetsModel
function onDirectoryLoaded(path)
{
// updating rows for safety: the rows might have been created before the
// directory (esp. the root path) has been loaded, so we must make sure all rows are
// expanded -- otherwise, the tree may not become visible.
updateRowsTimer.restart()
let idx = assetsModel.indexForPath(path)
let row = root.rowAtIndex(idx)
let column = root.columnAtIndex(idx)
if (row >= root.rootPathRow && !root.isExpanded(row))
root.expand(row)
}
function onRootPathChanged()
{
// when we switch from one project to another, we need to reset the state of the
// view: make sure we will do an "expand all" (otherwise, the whole tree might
// be collapsed, and with our visible root not being the actual root of the tree,
// the entire tree would be invisible)
root.lastRowCount = -1
root.requestedExpandAll = true
}
function onFileChanged(filePath)
{
rootView.invalidateThumbnail(filePath)
let index = assetsModel.indexForPath(filePath)
let cell = root.cellAtIndex(index)
let fileItem = root.itemAtCell(cell)
if (fileItem)
fileItem.reloadImage()
}
} // Connections
function addCreatedFolder(path)
{
root.__createdDirectories.push(path)
}
function selectedPathsAsList()
{
return Object.keys(root.selectedAssets)
.filter(itemPath => root.selectedAssets[itemPath])
}
// workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721
function resetVerticalScrollPosition()
{
root.contentY = 0
}
function updateRows()
{
if (root.rows <= 0)
return
while (root.__createdDirectories.length > 0) {
let dirPath = root.__createdDirectories.pop()
let index = assetsModel.indexForPath(dirPath)
let row = root.rowAtIndex(index)
if (row > 0)
root.expand(row)
else if (row === -1 && assetsModel.indexIsValid(index)) {
// It is possible that this directory, dirPath, was created inside of a parent
// directory that was not yet expanded in the TreeView. This can happen with the
// bridge plugin. In such a situation, we don't have a "row" for it yet, so we have
// to expand its parents, from root to our `index`
let parents = assetsModel.parentIndices(index);
parents.reverse().forEach(idx => {
let row = root.rowAtIndex(idx)
if (row > 0)
root.expand(row)
})
}
}
// we have no way to know beyond doubt here if updateRows() was called due
// to a request to expand or to collapse rows - but it should be safe to
// assume that, if we have more rows now than the last time, then it's an expand
var expanding = (root.rows >= root.lastRowCount)
if (expanding) {
if (root.requestedExpandAll)
root.__doExpandAll()
} else {
if (root.rowToExpand > 0) {
root.expand(root.rowToExpand)
root.rowToExpand = -1
}
// on collapsing, set expandAll flag to false.
root.requestedExpandAll = false;
}
root.lastRowCount = root.rows
}
function __doExpandAll()
{
let expandedAny = false
for (let nRow = 0; nRow < root.rows; ++nRow) {
let index = root.__modelIndex(nRow)
if (assetsModel.isDirectory(index) && !root.isExpanded(nRow)) {
root.expand(nRow);
expandedAny = true
}
}
if (!expandedAny)
Qt.callLater(root.forceLayout)
}
function expandAll()
{
// In order for __doExpandAll() to be called repeatedly (every time a new node is
// loaded, and then, expanded), we need to set requestedExpandAll to true.
root.requestedExpandAll = true
root.__doExpandAll()
}
function collapseAll()
{
root.resetVerticalScrollPosition()
// collapse all, except for the root path - from the last item (leaves) up to the root
for (let nRow = root.rows - 1; nRow >= 0; --nRow) {
let index = root.__modelIndex(nRow)
// we don't want to collapse the root path, because doing so will hide the contents
// of the tree.
if (assetsModel.filePath(index) === assetsModel.rootPath())
break
root.collapse(nRow)
}
}
// workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721
onContentHeightChanged: {
if (root.contentHeight <= root.height) {
let first = root.itemAtCell(0, root.firstRow)
if (!first)
root.contentY = 0
}
}
function computeAllExpandedState()
{
var dirsWithChildren = [...Array(root.rows).keys()].filter(row => {
let index = root.__modelIndex(row)
return assetsModel.isDirectory(index) && assetsModel.hasChildren(index)
})
var countExpanded = dirsWithChildren.filter(row => root.isExpanded(row)).length
if (countExpanded === dirsWithChildren.length)
return "all_expanded"
if (countExpanded === 0)
return "all_collapsed"
return ""
}
function startDropHoverOver(row)
{
let index = root.__modelIndex(row)
if (assetsModel.isDirectory(index)) {
let item = root.__getDelegateItemForIndex(index)
if (item)
item.isHighlighted = true
return
}
let parentItem = root.__getDelegateParentForIndex(index)
if (parentItem)
parentItem.hasChildWithDropHover = true
}
function endDropHover(row)
{
let index = root.__modelIndex(row)
if (assetsModel.isDirectory(index)) {
let item = root.__getDelegateItemForIndex(index)
if (item)
item.isHighlighted = false
return
}
let parentItem = root.__getDelegateParentForIndex(index)
if (parentItem)
parentItem.hasChildWithDropHover = false
}
function isAssetSelected(itemPath)
{
return root.selectedAssets[itemPath] ? true : false
}
function clearSelectedAssets()
{
root.selectedAssets = {}
}
function setAssetSelected(itemPath, selected)
{
root.selectedAssets[itemPath] = selected
root.selectedAssetsChanged()
}
function __getDelegateParentForIndex(index)
{
let parentIndex = assetsModel.parentDirIndex(index)
let parentCell = root.cellAtIndex(parentIndex)
return root.itemAtCell(parentCell)
}
function __getDelegateItemForIndex(index)
{
let cell = root.cellAtIndex(index)
return root.itemAtCell(cell)
}
function __modelIndex(row)
{
// The modelIndex() function exists since 6.3. In Qt 6.3, this modelIndex() function was a
// member of the TreeView, while in Qt6.4 it was moved to TableView. In Qt6.4, the order of
// the arguments was changed.
if (assetsRoot.qtVersionAtLeast6_4)
return root.modelIndex(0, row)
else
return root.modelIndex(row, 0)
}
DropArea {
id: dropArea
enabled: true
anchors.fill: parent
property bool __isHoveringDrop: false
property int __rowHoveringOver: -1
function __rowAndItem(drag)
{
let pos = dropArea.mapToItem(root, drag.x, drag.y)
let cell = root.cellAtPos(pos.x, pos.y, true)
let item = root.itemAtCell(cell)
return [cell.y, item]
}
onEntered: (drag) => {
root.assetsRoot.updateDropExtFiles(drag)
let [row, item] = dropArea.__rowAndItem(drag)
dropArea.__isHoveringDrop = drag.accepted && root.assetsRoot.dropSimpleExtFiles.length > 0
if (item && dropArea.__isHoveringDrop)
root.startDropHoverOver(row)
dropArea.__rowHoveringOver = row
}
onDropped: (drag) => {
let [row, item] = dropArea.__rowAndItem(drag)
if (item) {
root.endDropHover(row)
let dirPath = item.getDirPath()
rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles,
root.assetsRoot.dropComplexExtFiles,
dirPath)
}
dropArea.__isHoveringDrop = false
dropArea.__rowHoveringOver = -1
}
onPositionChanged: (drag) => {
let [row, item] = dropArea.__rowAndItem(drag)
if (dropArea.__rowHoveringOver !== row && dropArea.__rowHoveringOver > -1) {
root.endDropHover(dropArea.__rowHoveringOver)
if (item)
root.startDropHoverOver(row)
}
dropArea.__rowHoveringOver = row
}
onExited: {
if (!dropArea.__isHoveringDrop || dropArea.__rowHoveringOver === -1)
return
root.endDropHover(dropArea.__rowHoveringOver)
dropArea.__isHoveringDrop = false
dropArea.__rowHoveringOver = -1
}
}
delegate: AssetDelegate {
assetsView: root
assetsRoot: root.assetsRoot
indentation: 5
}
} // TreeView