Files
qt-creator/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml

376 lines
12 KiB
QML
Raw Normal View History

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
import AssetsLibraryBackend
TreeView {
id: root
clip: true
interactive: verticalScrollBar.visible && !root.contextMenu.opened && !rootView.isDragging
reuseItems: false
boundsBehavior: Flickable.StopAtBounds
rowSpacing: 5
property bool adsFocus: false
// objectName is used by the dock widget to find this particular ScrollView
// and set the ads focus on it.
objectName: "__mainSrollView"
property var assetsModel: AssetsLibraryBackend.assetsModel
property var rootView: AssetsLibraryBackend.rootView
property var tooltipBackend: AssetsLibraryBackend.tooltipBackend
required property Item assetsRoot
required property StudioControls.Menu contextMenu
property alias verticalScrollBar: verticalScrollBar
property var selectedAssets: ({})
// the latest file that was clicked, or changed to via Up or Down keys
property string currentFilePath: ""
// 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
readonly property int lastRow: root.rows - 1
property var __createdDirectories: []
rowHeightProvider: (row) => {
if (row <= root.rootPathRow)
return 0
return -1
}
HoverHandler { id: hoverHandler }
ScrollBar.vertical: StudioControls.TransientScrollBar {
id: verticalScrollBar
style: StudioTheme.Values.viewStyle
parent: root
x: root.width - verticalScrollBar.width
y: 0
height: root.availableHeight
orientation: Qt.Vertical
show: (hoverHandler.hovered || root.adsFocus || verticalScrollBar.inUse)
&& verticalScrollBar.isNeeded
}
model: assetsModel
onRowsChanged: {
if (root.rows > root.rootPathRow + 1 && assetsModel.isEmpty ||
root.rows <= root.rootPathRow + 1 && !assetsModel.isEmpty) {
assetsModel.syncIsEmpty()
}
root.updateRows()
}
Timer {
id: updateRowsTimer
interval: 200
repeat: false
onTriggered: root.updateRows()
}
Connections {
target: rootView
function onDirectoryCreated(path)
{
root.__createdDirectories.push(path)
updateRowsTimer.restart()
}
function onDeleteSelectedAssetsRequested()
{
let selectedPaths = root.selectedPathsAsList()
if (!selectedPaths.length)
return
let deleted = assetsModel.requestDeleteFiles(selectedPaths)
if (!deleted) {
confirmDeleteFiles.files = selectedPaths
confirmDeleteFiles.open()
}
}
}
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 {
// 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 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 __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 Qt 6.4, the order of
// the arguments was changed, and in Qt 6.5 the order was changed again. Due to this mess,
// the whole function was deprecated in Qt 6.4.3 and replaced with index() function.
if (assetsRoot.qtVersion >= 0x060403)
return root.index(row, 0)
else if (assetsRoot.qtVersion >= 0x060400)
return root.modelIndex(0, row)
else
return root.modelIndex(row, 0)
}
function __selectRow(row: int)
{
let index = root.__modelIndex(row)
if (assetsModel.isDirectory(index))
return
let filePath = assetsModel.filePath(index)
root.clearSelectedAssets()
root.setAssetSelected(filePath, true)
root.currentFilePath = filePath
}
function moveSelection(amount)
{
if (assetsModel.isEmpty || !amount)
return
let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath)
: root.__modelIndex(root.firstRow)
let row = root.rowAtIndex(index)
let nextRow = row
let nextIndex = index
do {
nextRow = nextRow + amount
if ((amount < 0 && nextRow < root.firstRow) || (amount > 0 && nextRow > root.lastRow))
return
nextIndex = root.__modelIndex(nextRow)
} while (assetsModel.isDirectory(nextIndex))
root.__selectRow(nextRow)
root.positionViewAtRow(nextRow, TableView.Contain)
}
Keys.enabled: true
Keys.onUpPressed: {
moveSelection(-1)
}
Keys.onDownPressed: {
moveSelection(1)
}
ConfirmDeleteFilesDialog {
id: confirmDeleteFiles
parent: root
files: []
onAccepted: root.clearSelectedAssets()
onClosed: confirmDeleteFiles.files = []
}
delegate: AssetDelegate {
assetsView: root
assetsRoot: root.assetsRoot
indentation: 10
}
} // TreeView