QmlDesigner: Track folders expanded/collapsed state in AssetsLibrary

Fixes: QDS-13791
Change-Id: I6d30e6d1d79da99466f0fa7b762267701bc2d2e2
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Shrief Gabr
2024-10-09 14:02:12 +03:00
parent 7047ec9a22
commit 785da8b434
5 changed files with 96 additions and 27 deletions

View File

@@ -48,6 +48,19 @@ TreeViewDelegate {
root.depth -= root.assetsView.rootPathDepth root.depth -= root.assetsView.rootPathDepth
root.initialDepth = root.depth root.initialDepth = root.depth
} }
// expand/collapse folder based on its stored expanded state
if (root.__isDirectory) {
// if the folder expand state is not stored yet, stores it as true (expanded)
root.assetsModel.initializeExpandState(root.__itemPath)
let expandState = assetsModel.folderExpandState(root.__itemPath)
if (expandState)
root.assetsView.expand(root.__currentRow)
else
root.assetsView.collapse(root.__currentRow)
}
} }
// workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721 // workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721
@@ -295,6 +308,8 @@ TreeViewDelegate {
} else { } else {
root.assetsView.expand(root.__currentRow) root.assetsView.expand(root.__currentRow)
} }
assetsModel.saveExpandState(root.__itemPath, root.expanded)
} }
function reloadImage() { function reloadImage() {

View File

@@ -93,7 +93,6 @@ TreeView {
function onDirectoryCreated(path) function onDirectoryCreated(path)
{ {
root.__createdDirectories.push(path) root.__createdDirectories.push(path)
updateRowsTimer.restart() updateRowsTimer.restart()
} }
@@ -118,15 +117,7 @@ TreeView {
// updating rows for safety: the rows might have been created before the // 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 // directory (esp. the root path) has been loaded, so we must make sure all rows are
// expanded -- otherwise, the tree may not become visible. // expanded -- otherwise, the tree may not become visible.
updateRowsTimer.restart() 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() function onRootPathChanged()
@@ -180,9 +171,9 @@ TreeView {
let index = assetsModel.indexForPath(dirPath) let index = assetsModel.indexForPath(dirPath)
let row = root.rowAtIndex(index) let row = root.rowAtIndex(index)
if (row > 0) if (row > 0) {
root.expand(row) root.expand(row)
else if (row === -1 && assetsModel.indexIsValid(index)) { } else if (row === -1 && assetsModel.indexIsValid(index)) {
// It is possible that this directory, dirPath, was created inside of a parent // 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 // 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 // bridge plugin. In such a situation, we don't have a "row" for it yet, so we have
@@ -194,6 +185,8 @@ TreeView {
root.expand(row) root.expand(row)
}) })
} }
assetsModel.saveExpandState(dirPath, root.isExpanded(row))
} }
// we have no way to know beyond doubt here if updateRows() was called due // we have no way to know beyond doubt here if updateRows() was called due
@@ -215,10 +208,10 @@ TreeView {
function __doExpandAll() function __doExpandAll()
{ {
let expandedAny = false let expandedAny = false
for (let nRow = 0; nRow < root.rows; ++nRow) { for (let r = 0; r < root.rows; ++r) {
let index = root.__modelIndex(nRow) let index = root.__modelIndex(r)
if (assetsModel.isDirectory(index) && !root.isExpanded(nRow)) { if (assetsModel.isDirectory(index) && !root.isExpanded(r)) {
root.expand(nRow); root.expand(r)
expandedAny = true expandedAny = true
} }
} }
@@ -352,15 +345,14 @@ TreeView {
} }
Keys.onRightPressed: { Keys.onRightPressed: {
root.toggleDirectoryState("expand") root.expandFolder(true)
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
root.toggleDirectoryState("collapse") root.expandFolder(false)
} }
function toggleDirectoryState(action) { function expandFolder(expand) {
let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath)
: root.__modelIndex(root.firstRow) : root.__modelIndex(root.firstRow)
@@ -369,9 +361,11 @@ TreeView {
let row = root.rowAtIndex(index) let row = root.rowAtIndex(index)
if (action === "expand") assetsModel.saveExpandState(root.currentFilePath, expand)
if (expand)
root.expand(row) root.expand(row)
else if (action === "collapse") else
root.collapse(row) root.collapse(row)
} }

View File

@@ -177,8 +177,18 @@ bool AssetsLibraryModel::deleteFolderRecursively(const QModelIndex &folderIndex)
{ {
auto idx = mapToSource(folderIndex); auto idx = mapToSource(folderIndex);
bool ok = m_sourceFsModel->remove(idx); bool ok = m_sourceFsModel->remove(idx);
if (!ok)
if (ok) {
Utils::FilePath parentPath = Utils::FilePath::fromString(filePath(folderIndex));
const QStringList paths = s_folderExpandStateHash.keys();
for (const QString &path : paths) {
if (Utils::FilePath::fromString(path).isChildOf(parentPath))
s_folderExpandStateHash.remove(path);
}
} else {
qWarning() << __FUNCTION__ << " could not remove folder recursively: " << m_sourceFsModel->filePath(idx); qWarning() << __FUNCTION__ << " could not remove folder recursively: " << m_sourceFsModel->filePath(idx);
}
return ok; return ok;
} }
@@ -205,6 +215,44 @@ bool AssetsLibraryModel::isSameOrDescendantPath(const QUrl &source, const QStrin
return srcPath == targetPath || targetPath.isChildOf(srcPath); return srcPath == targetPath || targetPath.isChildOf(srcPath);
} }
bool AssetsLibraryModel::folderExpandState(const QString &path) const
{
return s_folderExpandStateHash.value(path);
}
void AssetsLibraryModel::initializeExpandState(const QString &path)
{
if (!s_folderExpandStateHash.contains(path))
saveExpandState(path, true);
}
void AssetsLibraryModel::saveExpandState(const QString &path, bool expand)
{
s_folderExpandStateHash.insert(path, expand);
}
void AssetsLibraryModel::updateExpandPath(const Utils::FilePath &oldPath, const Utils::FilePath &newPath)
{
// update parent folder expand state
bool value = s_folderExpandStateHash.take(oldPath.toFSPathString());
saveExpandState(newPath.toFSPathString(), value);
const QStringList paths = s_folderExpandStateHash.keys();
for (const QString &path : paths) {
Utils::FilePath childPath = Utils::FilePath::fromString(path);
// update subfolders expand states
if (childPath.isChildOf(oldPath)) {
QString relativePath = Utils::FilePath::calcRelativePath(path, oldPath.toFSPathString());
Utils::FilePath newChildPath = newPath.pathAppended(relativePath);
value = s_folderExpandStateHash.take(path);
saveExpandState(newChildPath.toFSPathString(), value);
}
}
}
bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{ {
QString path = m_sourceFsModel->filePath(sourceParent); QString path = m_sourceFsModel->filePath(sourceParent);

View File

@@ -7,6 +7,7 @@
namespace Utils { namespace Utils {
class FileSystemWatcher; class FileSystemWatcher;
class FilePath;
} }
QT_FORWARD_DECLARE_CLASS(QFileSystemModel) QT_FORWARD_DECLARE_CLASS(QFileSystemModel)
@@ -51,6 +52,10 @@ public:
Q_INVOKABLE bool allFilePathsAreTextures(const QStringList &filePaths) const; Q_INVOKABLE bool allFilePathsAreTextures(const QStringList &filePaths) const;
Q_INVOKABLE bool allFilePathsAreComposedEffects(const QStringList &filePaths) const; Q_INVOKABLE bool allFilePathsAreComposedEffects(const QStringList &filePaths) const;
Q_INVOKABLE bool isSameOrDescendantPath(const QUrl &source, const QString &target) const; Q_INVOKABLE bool isSameOrDescendantPath(const QUrl &source, const QString &target) const;
Q_INVOKABLE bool folderExpandState(const QString &path) const;
Q_INVOKABLE void initializeExpandState(const QString &path);
Q_INVOKABLE void saveExpandState(const QString &path, bool expand);
void updateExpandPath(const Utils::FilePath &oldPath, const Utils::FilePath &newPath);
int columnCount(const QModelIndex &parent = QModelIndex()) const override int columnCount(const QModelIndex &parent = QModelIndex()) const override
{ {
@@ -80,6 +85,7 @@ private:
QFileSystemModel *m_sourceFsModel = nullptr; QFileSystemModel *m_sourceFsModel = nullptr;
bool m_isEmpty = true; bool m_isEmpty = true;
Utils::FileSystemWatcher *m_fileWatcher = nullptr; Utils::FileSystemWatcher *m_fileWatcher = nullptr;
inline static QHash<QString, bool> s_folderExpandStateHash;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -427,11 +427,17 @@ void AssetsLibraryWidget::handleAssetsDrop(const QList<QUrl> &urls, const QStrin
} }
} }
if (!src.renameFile(dest) && src.isDir()) { bool isDir = src.isDir();
QString message = tr("Failed to move folder \"%1\". "
"The folder might contain subfolders or one of its files is in use.") if (src.renameFile(dest)) {
.arg(src.fileName()); if (isDir)
Core::AsynchronousMessageBox::warning(tr("Folder move failure"), message); m_assetsModel->updateExpandPath(src, dest);
} else if (isDir) {
Core::AsynchronousMessageBox::warning(
tr("Folder move failure"),
tr("Failed to move folder \"%1\". The folder might contain subfolders or one of its files is in use.")
.arg(src.fileName())
);
} }
} }