From 548fcb2f8ef4afeef7703afcb33c1b4aebeb21b8 Mon Sep 17 00:00:00 2001 From: Aruki Date: Wed, 1 Feb 2017 20:23:51 -0700 Subject: [PATCH] Modified resource browser to use a filesystem browser view --- src/Editor/CEditorApplication.cpp | 2 +- src/Editor/PropertyEdit/CPropertyDelegate.cpp | 3 +- .../ResourceBrowser/CResourceBrowser.cpp | 100 ++++++++++++--- src/Editor/ResourceBrowser/CResourceBrowser.h | 6 +- .../ResourceBrowser/CResourceBrowser.ui | 119 ++++++++++++------ .../ResourceBrowser/CResourceProxyModel.h | 35 ++++-- .../ResourceBrowser/CResourceTableModel.h | 88 +++++++++++-- .../ResourceBrowser/CVirtualDirectoryModel.h | 36 ++++++ 8 files changed, 309 insertions(+), 80 deletions(-) diff --git a/src/Editor/CEditorApplication.cpp b/src/Editor/CEditorApplication.cpp index 3525f47b..be00ed25 100644 --- a/src/Editor/CEditorApplication.cpp +++ b/src/Editor/CEditorApplication.cpp @@ -34,7 +34,7 @@ void CEditorApplication::InitEditor() mpWorldEditor = new CWorldEditor(); mpResourceBrowser = new CResourceBrowser(mpWorldEditor); mpProjectDialog = new CProjectOverviewDialog(); - connect(mpProjectDialog, SIGNAL(ActiveProjectChanged(CGameProject*)), mpResourceBrowser, SLOT(RefreshResources())); + connect(mpProjectDialog, SIGNAL(ActiveProjectChanged(CGameProject*)), mpResourceBrowser, SLOT(UpdateStore())); } void CEditorApplication::EditResource(CResourceEntry *pEntry) diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index e098b12f..b76a79af 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -666,7 +666,8 @@ void CPropertyDelegate::SetCharacterModelData(QWidget *pEditor, const QModelInde if (Type == eAssetProperty) { - Params.SetResource( static_cast(pEditor)->Entry()->ID() ); + CResourceEntry *pEntry = static_cast(pEditor)->Entry(); + Params.SetResource( pEntry ? pEntry->ID() : CAssetID::InvalidID(mpEditor->CurrentGame()) ); } else if (Type == eEnumProperty) diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.cpp b/src/Editor/ResourceBrowser/CResourceBrowser.cpp index a738dc37..7458baba 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.cpp +++ b/src/Editor/ResourceBrowser/CResourceBrowser.cpp @@ -12,6 +12,8 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent) , mpUI(new Ui::CResourceBrowser) , mpSelectedEntry(nullptr) , mpStore(gpResourceStore) + , mpSelectedDir(nullptr) + , mSearching(false) { mpUI->setupUi(this); setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint); @@ -49,11 +51,11 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent) pImportNamesMenu->addAction(pImportFromAssetNameMapAction); // Set up connections - connect(mpUI->StoreComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(RefreshResources())); + connect(mpUI->StoreComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateStore())); connect(mpUI->SearchBar, SIGNAL(textChanged(QString)), this, SLOT(OnSearchStringChanged())); connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int))); connect(mpUI->DirectoryTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex,QModelIndex))); - connect(mpUI->ResourceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickResource(QModelIndex))); + connect(mpUI->ResourceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickTable(QModelIndex))); connect(mpUI->ResourceTableView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnResourceSelectionChanged(QModelIndex, QModelIndex))); connect(pImportFromContentsTxtAction, SIGNAL(triggered()), this, SLOT(OnImportPakContentsTxt())); connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap())); @@ -73,15 +75,55 @@ CResourceBrowser::~CResourceBrowser() void CResourceBrowser::RefreshResources() { // Fill resource table - mpStore = (mpUI->StoreComboBox->currentIndex() == 0 ? gpResourceStore : gpEditorStore); - mpModel->FillEntryList(mpStore); + mpModel->FillEntryList(mpSelectedDir, mSearching); - // Fill directory tree - mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr); - QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex()); - mpUI->DirectoryTreeView->expand(RootIndex); - mpUI->DirectoryTreeView->clearSelection(); - OnDirectorySelectionChanged(QModelIndex(), QModelIndex()); + // Mark directories to span all three columns + mpUI->ResourceTableView->clearSpans(); + + for (u32 iDir = 0; iDir < mpModel->NumDirectories(); iDir++) + mpUI->ResourceTableView->setSpan(iDir, 0, 1, 3); +} + +void CResourceBrowser::UpdateDescriptionLabel() +{ + QString Desc; + + bool ValidDir = mpSelectedDir && !mpSelectedDir->IsRoot(); + QString Path = (ValidDir ? '\\' + TO_QSTRING(mpSelectedDir->FullPath()) : ""); + + if (mSearching) + { + QString SearchText = mpUI->SearchBar->text(); + Desc = QString("Searching \"%1\"").arg(SearchText); + + if (ValidDir) + Desc += QString(" in %1").arg(Path); + } + else + { + if (ValidDir) + Desc = Path; + } + + mpUI->TableDescriptionLabel->setText(Desc); +} + +void CResourceBrowser::UpdateStore() +{ + int StoreIndex = mpUI->StoreComboBox->currentIndex(); + CResourceStore *pNewStore = (StoreIndex == 0 ? gpResourceStore : gpEditorStore); + + if (mpStore != pNewStore) + { + mpStore = pNewStore; + + // Refresh directory tree + mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr); + QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex()); + mpUI->DirectoryTreeView->expand(RootIndex); + mpUI->DirectoryTreeView->clearSelection(); + OnDirectorySelectionChanged(QModelIndex(), QModelIndex()); + } } void CResourceBrowser::OnSortModeChanged(int Index) @@ -98,19 +140,33 @@ void CResourceBrowser::OnSearchStringChanged() void CResourceBrowser::OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& /*rkPrevIndex*/) { - CVirtualDirectory *pDir = nullptr; - if (rkNewIndex.isValid()) - pDir = mpDirectoryModel->IndexDirectory(rkNewIndex); + mpSelectedDir = mpDirectoryModel->IndexDirectory(rkNewIndex); + else + mpSelectedDir = mpStore ? mpStore->RootDirectory() : nullptr; - mpProxyModel->SetDirectory(pDir); + UpdateDescriptionLabel(); + RefreshResources(); } -void CResourceBrowser::OnDoubleClickResource(QModelIndex Index) +void CResourceBrowser::OnDoubleClickTable(QModelIndex Index) { QModelIndex SourceIndex = mpProxyModel->mapToSource(Index); - CResourceEntry *pEntry = mpModel->IndexEntry(SourceIndex); - gpEdApp->EditResource(pEntry); + + // Directory - switch to the selected directory + if (mpModel->IsIndexDirectory(SourceIndex)) + { + CVirtualDirectory *pDir = mpModel->IndexDirectory(SourceIndex); + QModelIndex Index = mpDirectoryModel->GetIndexForDirectory(pDir); + mpUI->DirectoryTreeView->selectionModel()->setCurrentIndex(Index, QItemSelectionModel::ClearAndSelect); + } + + // Resource - open resource for editing + else + { + CResourceEntry *pEntry = mpModel->IndexEntry(SourceIndex); + gpEdApp->EditResource(pEntry); + } } void CResourceBrowser::OnResourceSelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& /*rkPrevIndex*/) @@ -167,5 +223,15 @@ void CResourceBrowser::ExportAssetNames() void CResourceBrowser::UpdateFilter() { + QString SearchText = mpUI->SearchBar->text(); + bool NewSearching = !SearchText.isEmpty(); + + if (mSearching != NewSearching) + { + mSearching = NewSearching; + RefreshResources(); + } + + UpdateDescriptionLabel(); mpProxyModel->SetSearchString( TO_TWIDESTRING(mpUI->SearchBar->text()) ); } diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.h b/src/Editor/ResourceBrowser/CResourceBrowser.h index 9be50a36..b243f1a1 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.h +++ b/src/Editor/ResourceBrowser/CResourceBrowser.h @@ -19,8 +19,10 @@ class CResourceBrowser : public QDialog CResourceStore *mpStore; CResourceTableModel *mpModel; CResourceProxyModel *mpProxyModel; + CVirtualDirectory *mpSelectedDir; CVirtualDirectoryModel *mpDirectoryModel; QTimer mUpdateFilterTimer; + bool mSearching; public: explicit CResourceBrowser(QWidget *pParent = 0); @@ -31,10 +33,12 @@ public: public slots: void RefreshResources(); + void UpdateDescriptionLabel(); + void UpdateStore(); void OnSortModeChanged(int Index); void OnSearchStringChanged(); void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex); - void OnDoubleClickResource(QModelIndex Index); + void OnDoubleClickTable(QModelIndex Index); void OnResourceSelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex); void OnImportPakContentsTxt(); void OnGenerateAssetNames(); diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.ui b/src/Editor/ResourceBrowser/CResourceBrowser.ui index c53c8f05..5fd29660 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.ui +++ b/src/Editor/ResourceBrowser/CResourceBrowser.ui @@ -150,51 +150,90 @@ - + - + 3 0 - - - 10 - - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - true - - - false - - - false - - - 21 - - - 21 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 8 + + + + + + + + + + + + 3 + 0 + + + + + 10 + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + false + + + 21 + + + 21 + + + + diff --git a/src/Editor/ResourceBrowser/CResourceProxyModel.h b/src/Editor/ResourceBrowser/CResourceProxyModel.h index 50ba6dfd..59d09603 100644 --- a/src/Editor/ResourceBrowser/CResourceProxyModel.h +++ b/src/Editor/ResourceBrowser/CResourceProxyModel.h @@ -39,25 +39,40 @@ public: bool lessThan(const QModelIndex& rkLeft, const QModelIndex& rkRight) const { - CResourceEntry *pLeft = mpModel->IndexEntry(rkLeft); - CResourceEntry *pRight = mpModel->IndexEntry(rkRight); + CVirtualDirectory *pLeftDir = mpModel->IndexDirectory(rkLeft); + CVirtualDirectory *pRightDir = mpModel->IndexDirectory(rkRight); + CResourceEntry *pLeftRes = mpModel->IndexEntry(rkLeft); + CResourceEntry *pRightRes = mpModel->IndexEntry(rkRight); + + if (pLeftDir && !pRightDir) + return true; + + else if (pRightDir && !pLeftDir) + return false; + + else if (pLeftDir && pRightDir) + return rkLeft.row() < rkRight.row(); // leave original directory order intact + + else if (mSortMode == eSortByName) + return pLeftRes->UppercaseName() < pRightRes->UppercaseName(); - if (mSortMode == eSortByName) - return pLeft->UppercaseName() < pRight->UppercaseName(); else - return pLeft->Size() < pRight->Size(); + return pLeftRes->Size() < pRightRes->Size(); } bool filterAcceptsRow(int SourceRow, const QModelIndex& rkSourceParent) const { QModelIndex Index = mpModel->index(SourceRow, 0, rkSourceParent); + CVirtualDirectory *pDir = mpModel->IndexDirectory(Index); CResourceEntry *pEntry = mpModel->IndexEntry(Index); - if (mpDirectory && !pEntry->IsInDirectory(mpDirectory)) - return false; - - if (!mSearchString.IsEmpty() && !pEntry->UppercaseName().Contains(mSearchString)) - return false; + if (!mSearchString.IsEmpty()) + { + if (pDir) + return false; + else + return pEntry->UppercaseName().Contains(mSearchString); + } return true; } diff --git a/src/Editor/ResourceBrowser/CResourceTableModel.h b/src/Editor/ResourceBrowser/CResourceTableModel.h index 489b9340..f6efac0c 100644 --- a/src/Editor/ResourceBrowser/CResourceTableModel.h +++ b/src/Editor/ResourceBrowser/CResourceTableModel.h @@ -12,7 +12,10 @@ class CResourceTableModel : public QAbstractTableModel { Q_OBJECT + + QList mDirectories; QList mEntries; + bool mHasParent; public: CResourceTableModel(QObject *pParent = 0) @@ -21,7 +24,7 @@ public: int rowCount(const QModelIndex& /*rkParent*/) const { - return mEntries.size(); + return mDirectories.size() + mEntries.size(); } int columnCount(const QModelIndex& /*rkParent*/) const @@ -31,9 +34,29 @@ public: QVariant data(const QModelIndex& rkIndex, int Role) const { - CResourceEntry *pEntry = mEntries[rkIndex.row()]; u32 Col = rkIndex.column(); + // Directory + if (IsIndexDirectory(rkIndex)) + { + if (Col != 0) + return QVariant::Invalid; + + CVirtualDirectory *pDir = IndexDirectory(rkIndex); + + if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole) + return (mHasParent && rkIndex.row() == 0 ? ".." : TO_QSTRING(pDir->Name())); + + else if (Role == Qt::DecorationRole) + return QIcon(":/icons/Open_24px.png"); + + else + return QVariant::Invalid; + } + + // Resource + CResourceEntry *pEntry = IndexEntry(rkIndex); + if (Role == Qt::DisplayRole) { if (Col == 0) @@ -64,28 +87,73 @@ public: CResourceEntry* IndexEntry(const QModelIndex& rkIndex) const { - return mEntries[rkIndex.row()]; + int Index = rkIndex.row() - mDirectories.size(); + return (Index >= 0 ? mEntries[Index] : nullptr); } - void FillEntryList(CResourceStore *pStore) + CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const + { + return (rkIndex.row() < mDirectories.size() ? mDirectories[rkIndex.row()] : nullptr); + } + + bool IsIndexDirectory(const QModelIndex& rkIndex) const + { + return rkIndex.row() < mDirectories.size(); + } + + void FillEntryList(CVirtualDirectory *pDir, bool IsSearching) { beginResetModel(); + mEntries.clear(); + mDirectories.clear(); + mHasParent = false; - if (pStore) + if (pDir) { - for (CResourceIterator It(pStore); It; ++It) + // When not searching, show only subdirectories and assets in the current directory. + if (!IsSearching) { - if (It->IsTransient()) continue; + if (!pDir->IsRoot()) + { + mDirectories << pDir->Parent(); + mHasParent = true; + } - CResTypeInfo *pInfo = It->TypeInfo(); - if (pInfo->IsVisibleInBrowser() && !It->IsHidden()) - mEntries << *It; + for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++) + mDirectories << pDir->SubdirectoryByIndex(iDir); + + for (u32 iRes = 0; iRes < pDir->NumResources(); iRes++) + { + CResourceEntry *pEntry = pDir->ResourceByIndex(iRes); + + if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden()) + mEntries << pEntry; + } } + + // When searching, do not show subdirectories and show all assets in current directory + all subdirectories. + else + RecursiveAddDirectoryContents(pDir); } endResetModel(); } + +protected: + void RecursiveAddDirectoryContents(CVirtualDirectory *pDir) + { + for (u32 iRes = 0; iRes < pDir->NumResources(); iRes++) + mEntries << pDir->ResourceByIndex(iRes); + + for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++) + RecursiveAddDirectoryContents(pDir->SubdirectoryByIndex(iDir)); + } + +public: + // Accessors + inline u32 NumDirectories() const { return mDirectories.size(); } + inline u32 NumResources() const { return mEntries.size(); } }; #endif // CRESOURCELISTMODEL diff --git a/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h b/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h index f25537f5..86f10401 100644 --- a/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h +++ b/src/Editor/ResourceBrowser/CVirtualDirectoryModel.h @@ -85,6 +85,42 @@ public: return QVariant::Invalid; } + QModelIndex GetIndexForDirectory(CVirtualDirectory *pDir) + { + 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;