diff --git a/src/Core/GameProject/CVirtualDirectory.cpp b/src/Core/GameProject/CVirtualDirectory.cpp index a4720e82..42818952 100644 --- a/src/Core/GameProject/CVirtualDirectory.cpp +++ b/src/Core/GameProject/CVirtualDirectory.cpp @@ -178,9 +178,7 @@ bool CVirtualDirectory::AddChild(const TString &rkPath, CResourceEntry *pEntry) } mSubdirectories.push_back(pSubdir); - std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool { - return (pLeft->Name().ToUpper() < pRight->Name().ToUpper()); - }); + SortSubdirectories(); // As an optimization, don't recurse here. We've already verified the full path is valid, so we don't need to do it again. // We also know none of the remaining directories already exist because this is a new, empty directory. @@ -224,9 +222,7 @@ bool CVirtualDirectory::AddChild(CVirtualDirectory *pDir) if (FindChildDirectory(pDir->Name(), false) != nullptr) return false; mSubdirectories.push_back(pDir); - std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool { - return (pLeft->Name().ToUpper() < pRight->Name().ToUpper()); - }); + SortSubdirectories(); return true; } @@ -259,6 +255,13 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry) return false; } +void CVirtualDirectory::SortSubdirectories() +{ + std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool { + return (pLeft->Name().ToUpper() < pRight->Name().ToUpper()); + }); +} + bool CVirtualDirectory::Rename(const TString& rkNewName) { Log::Write("MOVING DIRECTORY: " + FullPath() + " -> " + mpParent->FullPath() + rkNewName + '/'); @@ -274,6 +277,7 @@ bool CVirtualDirectory::Rename(const TString& rkNewName) { mName = rkNewName; mpStore->SetCacheDirty(); + mpParent->SortSubdirectories(); return true; } } diff --git a/src/Core/GameProject/CVirtualDirectory.h b/src/Core/GameProject/CVirtualDirectory.h index ad2bd1b4..737023bc 100644 --- a/src/Core/GameProject/CVirtualDirectory.h +++ b/src/Core/GameProject/CVirtualDirectory.h @@ -36,6 +36,7 @@ public: bool AddChild(CVirtualDirectory *pDir); bool RemoveChildDirectory(CVirtualDirectory *pSubdir); bool RemoveChildResource(CResourceEntry *pEntry); + void SortSubdirectories(); bool Rename(const TString& rkNewName); bool Delete(); void DeleteEmptySubdirectories(); diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 123b0b81..d1462a52 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -267,7 +267,8 @@ SOURCES += \ ResourceBrowser/CResourceDelegate.cpp \ ResourceBrowser/CResourceTableContextMenu.cpp \ ResourceBrowser/CResourceTableModel.cpp \ - ResourceBrowser/CResourceTableView.cpp + ResourceBrowser/CResourceTableView.cpp \ + ResourceBrowser/CVirtualDirectoryModel.cpp # UI Files FORMS += \ diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.cpp b/src/Editor/ResourceBrowser/CResourceBrowser.cpp index 5a606183..3cbddf34 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.cpp +++ b/src/Editor/ResourceBrowser/CResourceBrowser.cpp @@ -68,7 +68,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent) mpUI->ResourceTableView->installEventFilter(this); // Set up directory tree model - mpDirectoryModel = new CVirtualDirectoryModel(this); + mpDirectoryModel = new CVirtualDirectoryModel(this, this); mpUI->DirectoryTreeView->setModel(mpDirectoryModel); RefreshResources(); @@ -501,6 +501,7 @@ bool CResourceBrowser::CreateDirectory() ASSERT(Index.isValid()); QModelIndex ProxyIndex = mpProxyModel->mapFromSource(Index); + mpUI->ResourceTableView->selectionModel()->select(ProxyIndex, QItemSelectionModel::ClearAndSelect); mpUI->ResourceTableView->edit(ProxyIndex); } diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.h b/src/Editor/ResourceBrowser/CResourceBrowser.h index 322470da..5f804c68 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.h +++ b/src/Editor/ResourceBrowser/CResourceBrowser.h @@ -112,11 +112,18 @@ public slots: signals: void SelectedResourceChanged(CResourceEntry *pNewRes); + + void ResourceAboutToBeMoved(CResourceEntry *pRes, QString NewPath); + void ResourceMoved(CResourceEntry *pRes, CVirtualDirectory *pOldDir, TString OldName); + + void DirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath); + void DirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName); + + void DirectoryAboutToBeCreated(QString DirPath); void DirectoryCreated(CVirtualDirectory *pDir); + void DirectoryAboutToBeDeleted(CVirtualDirectory *pDir); void DirectoryDeleted(); - void ResourceMoved(CResourceEntry *pRes, CVirtualDirectory *pOldDir, TString OldName); - void DirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName); }; #endif // CRESOURCEBROWSER_H diff --git a/src/Editor/ResourceBrowser/CResourceTableModel.cpp b/src/Editor/ResourceBrowser/CResourceTableModel.cpp index ac722076..46b43302 100644 --- a/src/Editor/ResourceBrowser/CResourceTableModel.cpp +++ b/src/Editor/ResourceBrowser/CResourceTableModel.cpp @@ -234,7 +234,7 @@ void CResourceTableModel::FillEntryList(CVirtualDirectory *pDir, bool AssetListM } if (pDir) - mModelDescription = pDir->IsRoot() ? "Root" : TO_QSTRING(pDir->FullPath()); + mModelDescription = pDir->IsRoot() ? "Resources" : TO_QSTRING(pDir->FullPath()); else mModelDescription = "Nothing"; diff --git a/src/Editor/ResourceBrowser/CVirtualDirectoryModel.cpp b/src/Editor/ResourceBrowser/CVirtualDirectoryModel.cpp new file mode 100644 index 00000000..0e79c66e --- /dev/null +++ b/src/Editor/ResourceBrowser/CVirtualDirectoryModel.cpp @@ -0,0 +1,233 @@ +#include "CVirtualDirectoryModel.h" +#include "CResourceBrowser.h" + +CVirtualDirectoryModel::CVirtualDirectoryModel(CResourceBrowser *pBrowser, QObject *pParent /*= 0*/) + : QAbstractItemModel(pParent) + , mpRoot(nullptr) + , mInsertingRows(false) + , mRemovingRows(false) + , mChangingLayout(false) +{ + connect(pBrowser, SIGNAL(DirectoryAboutToBeMoved(CVirtualDirectory*,QString)), this, SLOT(OnDirectoryAboutToBeMoved(CVirtualDirectory*,QString))); + connect(pBrowser, SIGNAL(DirectoryMoved(CVirtualDirectory*,CVirtualDirectory*,TString)), this, SLOT(FinishModelChanges())); + + connect(pBrowser, SIGNAL(DirectoryAboutToBeCreated(QString)), this, SLOT(OnDirectoryAboutToBeCreated(QString))); + connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(FinishModelChanges())); + + connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(OnDirectoryAboutToBeDeleted(CVirtualDirectory*))); + connect(pBrowser, SIGNAL(DirectoryDeleted()), this, SLOT(FinishModelChanges())); +} + +QModelIndex CVirtualDirectoryModel::index(int Row, int Column, const QModelIndex& rkParent) const +{ + if (!hasIndex(Row, Column, rkParent)) + return QModelIndex(); + + CVirtualDirectory *pDir = IndexDirectory(rkParent); + + if (pDir && pDir->NumSubdirectories() > (u32) Row) + return createIndex(Row, Column, pDir->SubdirectoryByIndex(Row)); + + else if (!pDir) + return createIndex(Row, Column, mpRoot); + + return QModelIndex(); +} + +QModelIndex CVirtualDirectoryModel::parent(const QModelIndex& rkChild) const +{ + CVirtualDirectory *pDir = IndexDirectory(rkChild); + CVirtualDirectory *pParent = pDir->Parent(); + + if (pParent) + { + CVirtualDirectory *pGrandparent = pParent->Parent(); + + if (pGrandparent) + { + for (u32 iSub = 0; iSub < pGrandparent->NumSubdirectories(); iSub++) + { + if (pGrandparent->SubdirectoryByIndex(iSub) == pParent) + return createIndex(iSub, 0, pParent); + } + } + + else return createIndex(0, 0, mpRoot); + } + + return QModelIndex(); +} + +int CVirtualDirectoryModel::rowCount(const QModelIndex& rkParent) const +{ + CVirtualDirectory *pDir = IndexDirectory(rkParent); + if (pDir) return pDir->NumSubdirectories(); + else return mpRoot ? 1 : 0; +} + +int CVirtualDirectoryModel::columnCount(const QModelIndex&) const +{ + return 1; +} + +QVariant CVirtualDirectoryModel::data(const QModelIndex& rkIndex, int Role) const +{ + if (!rkIndex.isValid()) + return QVariant::Invalid; + + if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole) + { + if (!rkIndex.parent().isValid()) + return "Resources"; + + else + { + CVirtualDirectory *pDir = IndexDirectory(rkIndex); + if (pDir) return TO_QSTRING(pDir->Name()); + } + } + + if (Role == Qt::DecorationRole) + { + return QIcon(":/icons/Open_24px.png"); + } + + return QVariant::Invalid; +} + +QModelIndex CVirtualDirectoryModel::GetIndexForDirectory(CVirtualDirectory *pDir) +{ + if (!pDir) + return QModelIndex(); + + QVector Indices; + CVirtualDirectory *pOriginal = pDir; + CVirtualDirectory *pParent = pDir->Parent(); + + // Get index list + while (pParent) + { + bool Found = false; + + for (u32 iDir = 0; iDir < pParent->NumSubdirectories(); iDir++) + { + if (pParent->SubdirectoryByIndex(iDir) == pDir) + { + Indices.push_front(iDir); + pDir = pParent; + pParent = pParent->Parent(); + Found = true; + break; + } + } + + ASSERT(Found); // it should not be possible for this not to work + } + + // Traverse hierarchy + QModelIndex Out = index(0, 0, QModelIndex()); + + foreach (int Idx, Indices) + Out = index(Idx, 0, Out); + + ASSERT(IndexDirectory(Out) == pOriginal); + return Out; +} + +CVirtualDirectory* CVirtualDirectoryModel::IndexDirectory(const QModelIndex& rkIndex) const +{ + if (!rkIndex.isValid()) return nullptr; + return static_cast(rkIndex.internalPointer()); +} + +void CVirtualDirectoryModel::SetRoot(CVirtualDirectory *pDir) +{ + beginResetModel(); + mpRoot = pDir; + endResetModel(); +} + +bool CVirtualDirectoryModel::GetProposedIndex(QString Path, QModelIndex& rOutParent, int& rOutRow) +{ + // Get parent path + TString FullPath = TO_TSTRING(Path); + + if (FullPath.EndsWith('/') || FullPath.EndsWith('\\')) + FullPath = FullPath.ChopBack(1); + + u32 LastSlash = FullPath.LastIndexOf("\\/"); + TString ParentPath = FullPath.ChopBack( FullPath.Size() - LastSlash ); + + // Find parent index + CVirtualDirectory *pParent = (ParentPath.IsEmpty() ? mpRoot : mpRoot->FindChildDirectory(ParentPath, false)); + if (!pParent) return false; + + QModelIndex ParentIndex = GetIndexForDirectory(pParent); + if (!ParentIndex.isValid()) return false; + + // Determine the row number that the new directory will be inserted at + QString DirName = TO_QSTRING(FullPath.ChopFront( LastSlash + 1 )); + int NumRows = rowCount(ParentIndex); + int RowIdx = 0; + + for (; RowIdx < NumRows; RowIdx++) + { + QModelIndex Index = index(RowIdx, 0, ParentIndex); + QString OtherName = data(Index, Qt::DisplayRole).toString(); + + if (QString::compare(DirName, OtherName, Qt::CaseInsensitive) < 0) + break; + } + + rOutParent = ParentIndex; + rOutRow = RowIdx; + return true; +} + +void CVirtualDirectoryModel::OnDirectoryAboutToBeMoved(CVirtualDirectory *, QString ) +{ + emit layoutAboutToBeChanged(); + mChangingLayout = true; +} + +void CVirtualDirectoryModel::OnDirectoryAboutToBeCreated(QString DirPath) +{ + QModelIndex Parent; + int Row; + + if (GetProposedIndex(DirPath, Parent, Row)) + { + beginInsertRows(Parent, Row, Row); + mInsertingRows = true; + } +} + +void CVirtualDirectoryModel::OnDirectoryAboutToBeDeleted(CVirtualDirectory *pDir) +{ + QModelIndex Index = GetIndexForDirectory(pDir); + + if (Index.isValid()) + { + beginRemoveRows(Index.parent(), Index.row(), Index.row()); + mRemovingRows = true; + } +} + +void CVirtualDirectoryModel::FinishModelChanges() +{ + if (mInsertingRows) + { + endInsertRows(); + mInsertingRows = false; + } + if (mRemovingRows) + { + endRemoveRows(); + mRemovingRows = false; + } + if (mChangingLayout) + { + emit layoutChanged(); + mChangingLayout = false; + } +} diff --git a/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h b/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h index 593be8a0..144cb899 100644 --- a/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h +++ b/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h @@ -10,132 +10,31 @@ class CVirtualDirectoryModel : public QAbstractItemModel { Q_OBJECT CVirtualDirectory *mpRoot; + bool mInsertingRows; + bool mRemovingRows; + bool mChangingLayout; public: - CVirtualDirectoryModel(QObject *pParent = 0) - : QAbstractItemModel(pParent) - , mpRoot(nullptr) - {} + CVirtualDirectoryModel(CResourceBrowser *pBrowser, QObject *pParent = 0); - QModelIndex index(int Row, int Column, const QModelIndex& rkParent) const - { - if (!hasIndex(Row, Column, rkParent)) - return QModelIndex(); + QModelIndex index(int Row, int Column, const QModelIndex& rkParent) const; + QModelIndex parent(const QModelIndex& rkChild) const; + int rowCount(const QModelIndex& rkParent) const; + int columnCount(const QModelIndex& /*rkParent*/) const; + QVariant data(const QModelIndex& rkIndex, int Role) const; - CVirtualDirectory *pDir = IndexDirectory(rkParent); + QModelIndex GetIndexForDirectory(CVirtualDirectory *pDir); + CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const; + void SetRoot(CVirtualDirectory *pDir); - if (pDir && pDir->NumSubdirectories() > (u32) Row) - return createIndex(Row, Column, pDir->SubdirectoryByIndex(Row)); +protected: + bool GetProposedIndex(QString Path, QModelIndex& rOutParent, int& rOutRow); - else if (!pDir) - return createIndex(Row, Column, mpRoot); - - return QModelIndex(); - } - - QModelIndex parent(const QModelIndex& rkChild) const - { - CVirtualDirectory *pDir = IndexDirectory(rkChild); - CVirtualDirectory *pParent = pDir->Parent(); - - if (pParent) - { - CVirtualDirectory *pGrandparent = pParent->Parent(); - - if (pGrandparent) - { - for (u32 iSub = 0; iSub < pGrandparent->NumSubdirectories(); iSub++) - { - if (pGrandparent->SubdirectoryByIndex(iSub) == pParent) - return createIndex(iSub, 0, pParent); - } - } - - else return createIndex(0, 0, mpRoot); - } - - return QModelIndex(); - } - - int rowCount(const QModelIndex& rkParent) const - { - CVirtualDirectory *pDir = IndexDirectory(rkParent); - if (pDir) return pDir->NumSubdirectories(); - else return mpRoot ? 1 : 0; - } - - int columnCount(const QModelIndex& /*rkParent*/) const - { - return 1; - } - - QVariant data(const QModelIndex& rkIndex, int Role) const - { - if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole) - { - CVirtualDirectory *pDir = IndexDirectory(rkIndex); - if (pDir) return TO_QSTRING(pDir->Name()); - } - - if (Role == Qt::DecorationRole) - { - return QIcon(":/icons/Open_24px.png"); - } - - return QVariant::Invalid; - } - - QModelIndex GetIndexForDirectory(CVirtualDirectory *pDir) - { - if (!pDir) - return QModelIndex(); - - QVector Indices; - CVirtualDirectory *pOriginal = pDir; - CVirtualDirectory *pParent = pDir->Parent(); - - // Get index list - while (pParent) - { - bool Found = false; - - for (u32 iDir = 0; iDir < pParent->NumSubdirectories(); iDir++) - { - if (pParent->SubdirectoryByIndex(iDir) == pDir) - { - Indices.push_front(iDir); - pDir = pParent; - pParent = pParent->Parent(); - Found = true; - break; - } - } - - ASSERT(Found); // it should not be possible for this not to work - } - - // Traverse hierarchy - QModelIndex Out = index(0, 0, QModelIndex()); - - foreach (int Idx, Indices) - Out = index(Idx, 0, Out); - - ASSERT(IndexDirectory(Out) == pOriginal); - return Out; - } - - inline CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const - { - if (!rkIndex.isValid()) return nullptr; - return static_cast(rkIndex.internalPointer()); - } - - inline void SetRoot(CVirtualDirectory *pDir) - { - beginResetModel(); - mpRoot = pDir; - endResetModel(); - } +public slots: + void OnDirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath); + void OnDirectoryAboutToBeCreated(QString DirPath); + void OnDirectoryAboutToBeDeleted(CVirtualDirectory *pDir); + void FinishModelChanges(); }; #endif // CVIRTUALDIRECTORYMODEL diff --git a/src/Editor/Undo/CMoveDirectoryCommand.h b/src/Editor/Undo/CMoveDirectoryCommand.h index d2b1cfb6..0011c19c 100644 --- a/src/Editor/Undo/CMoveDirectoryCommand.h +++ b/src/Editor/Undo/CMoveDirectoryCommand.h @@ -34,6 +34,8 @@ protected: CVirtualDirectory *pParent = mpStore->GetVirtualDirectory(rkPath, false); ASSERT(pDir && pParent); + gpEdApp->ResourceBrowser()->DirectoryAboutToBeMoved(pDir, TO_QSTRING(rkPath)); + TString OldName = pDir->Name(); CVirtualDirectory *pOldParent = pDir->Parent(); pDir->SetParent(pParent); diff --git a/src/Editor/Undo/CMoveResourceCommand.h b/src/Editor/Undo/CMoveResourceCommand.h index f21d7d67..c78eb780 100644 --- a/src/Editor/Undo/CMoveResourceCommand.h +++ b/src/Editor/Undo/CMoveResourceCommand.h @@ -33,6 +33,9 @@ public: protected: void DoMove(const TString& rkPath, bool IsAutoDir) { + TString ResName = mpEntry->CookedAssetPath(true).GetFileName(); + gpEdApp->ResourceBrowser()->ResourceAboutToBeMoved(mpEntry, TO_QSTRING(rkPath + ResName)); + CVirtualDirectory *pOldDir = mpEntry->Directory(); bool Success = mpEntry->Move(rkPath, IsAutoDir); ASSERT(Success); // todo better error handling diff --git a/src/Editor/Undo/CRenameDirectoryCommand.h b/src/Editor/Undo/CRenameDirectoryCommand.h index 79feacba..7fba010c 100644 --- a/src/Editor/Undo/CRenameDirectoryCommand.h +++ b/src/Editor/Undo/CRenameDirectoryCommand.h @@ -27,6 +27,9 @@ public: protected: void DoMove(const TString& rkName) { + QString ParentPath = TO_QSTRING(mpDir->Parent() ? mpDir->Parent()->FullPath() : ""); + gpEdApp->ResourceBrowser()->DirectoryAboutToBeMoved(mpDir, ParentPath + TO_QSTRING(rkName)); + TString OldName = mpDir->Name(); bool Success = mpDir->Rename(rkName); ASSERT(Success); diff --git a/src/Editor/Undo/CRenameResourceCommand.h b/src/Editor/Undo/CRenameResourceCommand.h index 2d78e1cd..d382bf67 100644 --- a/src/Editor/Undo/CRenameResourceCommand.h +++ b/src/Editor/Undo/CRenameResourceCommand.h @@ -29,6 +29,9 @@ public: protected: void DoMove(const TString& rkName, bool IsAutoName) { + TString FullNewName = rkName + "." + mpEntry->CookedExtension().ToString(); + gpEdApp->ResourceBrowser()->ResourceAboutToBeMoved(mpEntry, TO_QSTRING(mpEntry->Directory()->FullPath() + FullNewName)); + TString OldName = mpEntry->Name(); bool Success = mpEntry->Rename(rkName, IsAutoName); ASSERT(Success); diff --git a/src/Editor/Undo/ICreateDeleteDirectoryCommand.h b/src/Editor/Undo/ICreateDeleteDirectoryCommand.h index 2ebbd01d..6c72c9fa 100644 --- a/src/Editor/Undo/ICreateDeleteDirectoryCommand.h +++ b/src/Editor/Undo/ICreateDeleteDirectoryCommand.h @@ -31,10 +31,9 @@ protected: if (pParent) { + gpEdApp->ResourceBrowser()->DirectoryAboutToBeCreated( TO_QSTRING(mParentPath + mDirName) ); mpDir = pParent->FindChildDirectory(mDirName, true); - - if (mpDir) - gpEdApp->ResourceBrowser()->DirectoryCreated(mpDir); + gpEdApp->ResourceBrowser()->DirectoryCreated(mpDir); } }