Added type filtering to the resource browser

This commit is contained in:
Aruki 2017-02-02 21:20:54 -07:00
parent c53352c290
commit 7f9bed653b
7 changed files with 231 additions and 23 deletions

View File

@ -141,7 +141,7 @@ void CVirtualDirectory::AddChild(const TWideString &rkPath, CResourceEntry *pEnt
mSubdirectories.push_back(pSubdir); mSubdirectories.push_back(pSubdir);
std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool { std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool {
return (pLeft->Name() < pRight->Name()); return (pLeft->Name().ToUpper() < pRight->Name().ToUpper());
}); });
} }

View File

@ -128,7 +128,6 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
CResTypeInfo *pType = new CResTypeInfo(eAnimation, "Animation"); CResTypeInfo *pType = new CResTypeInfo(eAnimation, "Animation");
AddExtension(pType, "ANIM", ePrimeDemo, eReturns); AddExtension(pType, "ANIM", ePrimeDemo, eReturns);
pType->mRawExtension = "ani"; pType->mRawExtension = "ani";
pType->mHidden = true;
} }
{ {
CResTypeInfo *pType = new CResTypeInfo(eAnimCollisionPrimData, "Animation Collision Primitive Data"); CResTypeInfo *pType = new CResTypeInfo(eAnimCollisionPrimData, "Animation Collision Primitive Data");
@ -171,7 +170,6 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
CResTypeInfo *pType = new CResTypeInfo(eBinaryData, "Generic Data"); CResTypeInfo *pType = new CResTypeInfo(eBinaryData, "Generic Data");
AddExtension(pType, "DUMB", ePrimeDemo, eCorruption); AddExtension(pType, "DUMB", ePrimeDemo, eCorruption);
pType->mRawExtension = "dat"; pType->mRawExtension = "dat";
pType->mHidden = true;
} }
{ {
CResTypeInfo *pType = new CResTypeInfo(eBurstFireData, "Burst Fire Data"); CResTypeInfo *pType = new CResTypeInfo(eBurstFireData, "Burst Fire Data");
@ -206,7 +204,6 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{ {
CResTypeInfo *pType = new CResTypeInfo(eHintSystem, "Hint System Data"); CResTypeInfo *pType = new CResTypeInfo(eHintSystem, "Hint System Data");
AddExtension(pType, "HINT", ePrime, eCorruption); AddExtension(pType, "HINT", ePrime, eCorruption);
pType->mHidden = true;
} }
{ {
CResTypeInfo *pType = new CResTypeInfo(eMapArea, "Area Map"); CResTypeInfo *pType = new CResTypeInfo(eMapArea, "Area Map");

View File

@ -3,6 +3,7 @@
#include "Editor/CEditorApplication.h" #include "Editor/CEditorApplication.h"
#include <Core/GameProject/AssetNameGeneration.h> #include <Core/GameProject/AssetNameGeneration.h>
#include <Core/GameProject/CAssetNameMap.h> #include <Core/GameProject/CAssetNameMap.h>
#include <QCheckBox>
#include <QFileDialog> #include <QFileDialog>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
@ -13,6 +14,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
, mpSelectedEntry(nullptr) , mpSelectedEntry(nullptr)
, mpStore(gpResourceStore) , mpStore(gpResourceStore)
, mpSelectedDir(nullptr) , mpSelectedDir(nullptr)
, mAssetListMode(true)
, mSearching(false) , mSearching(false)
{ {
mpUI->setupUi(this); mpUI->setupUi(this);
@ -35,6 +37,31 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
RefreshResources(); RefreshResources();
// Set up filter checkboxes
mpFilterBoxesLayout = new QVBoxLayout();
mpFilterBoxesLayout->setContentsMargins(2, 2, 2, 2);
mpFilterBoxesLayout->setSpacing(1);
mpFilterBoxesContainerWidget = new QWidget(this);
mpFilterBoxesContainerWidget->setLayout(mpFilterBoxesLayout);
mpUI->FilterCheckboxScrollArea->setWidget(mpFilterBoxesContainerWidget);
mpUI->FilterCheckboxScrollArea->setBackgroundRole(QPalette::AlternateBase);
mFilterBoxFont = font();
mFilterBoxFont.setPointSize(mFilterBoxFont.pointSize() - 1);
QFont AllBoxFont = mFilterBoxFont;
AllBoxFont.setBold(true);
mpFilterAllBox = new QCheckBox(this);
mpFilterAllBox->setChecked(true);
mpFilterAllBox->setFont(AllBoxFont);
mpFilterAllBox->setText("All");
mpFilterBoxesLayout->addWidget(mpFilterAllBox);
CreateFilterCheckboxes();
// Set up Import Names menu // Set up Import Names menu
QMenu *pImportNamesMenu = new QMenu(this); QMenu *pImportNamesMenu = new QMenu(this);
mpUI->ImportNamesButton->setMenu(pImportNamesMenu); mpUI->ImportNamesButton->setMenu(pImportNamesMenu);
@ -53,6 +80,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
// Set up connections // Set up connections
connect(mpUI->StoreComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateStore())); connect(mpUI->StoreComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateStore()));
connect(mpUI->SearchBar, SIGNAL(textChanged(QString)), this, SLOT(OnSearchStringChanged())); connect(mpUI->SearchBar, SIGNAL(textChanged(QString)), this, SLOT(OnSearchStringChanged()));
connect(mpUI->DisplayTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnDisplayModeChanged(int)));
connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int))); 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->DirectoryTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex,QModelIndex)));
connect(mpUI->ResourceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickTable(QModelIndex))); connect(mpUI->ResourceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickTable(QModelIndex)));
@ -61,6 +89,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap())); connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap()));
connect(mpUI->ExportNamesButton, SIGNAL(clicked()), this, SLOT(ExportAssetNames())); connect(mpUI->ExportNamesButton, SIGNAL(clicked()), this, SLOT(ExportAssetNames()));
connect(&mUpdateFilterTimer, SIGNAL(timeout()), this, SLOT(UpdateFilter())); connect(&mUpdateFilterTimer, SIGNAL(timeout()), this, SLOT(UpdateFilter()));
connect(mpFilterAllBox, SIGNAL(toggled(bool)), this, SLOT(OnFilterTypeBoxTicked(bool)));
#if !PUBLIC_RELEASE #if !PUBLIC_RELEASE
connect(pGenerateAssetNamesAction, SIGNAL(triggered()), this, SLOT(OnGenerateAssetNames())); connect(pGenerateAssetNamesAction, SIGNAL(triggered()), this, SLOT(OnGenerateAssetNames()));
@ -101,10 +130,51 @@ void CResourceBrowser::SelectDirectory(CVirtualDirectory *pDir)
mpUI->DirectoryTreeView->selectionModel()->setCurrentIndex(Index, QItemSelectionModel::ClearAndSelect); mpUI->DirectoryTreeView->selectionModel()->setCurrentIndex(Index, QItemSelectionModel::ClearAndSelect);
} }
void CResourceBrowser::CreateFilterCheckboxes()
{
// Delete existing checkboxes
foreach (const SResourceType& rkType, mTypeList)
delete rkType.pFilterCheckBox;
mTypeList.clear();
// No store - leave empty
if (!mpStore) return;
// Get new type list
std::list<CResTypeInfo*> TypeList;
CResTypeInfo::GetAllTypesInGame(mpStore->Game(), TypeList);
for (auto Iter = TypeList.begin(); Iter != TypeList.end(); Iter++)
{
CResTypeInfo *pType = *Iter;
if (pType->IsVisibleInBrowser())
{
QCheckBox *pCheck = new QCheckBox(this);
pCheck->setFont(mFilterBoxFont);
pCheck->setText(TO_QSTRING(pType->TypeName()));
mTypeList << SResourceType { pType, pCheck };
}
}
qSort(mTypeList.begin(), mTypeList.end(), [](const SResourceType& rkLeft, const SResourceType& rkRight) -> bool {
return rkLeft.pTypeInfo->TypeName().ToUpper() < rkRight.pTypeInfo->TypeName().ToUpper();
});
// Add sorted checkboxes to the UI
foreach (const SResourceType& rkType, mTypeList)
{
QCheckBox *pCheck = rkType.pFilterCheckBox;
mpFilterBoxesLayout->addWidget(rkType.pFilterCheckBox);
connect(pCheck, SIGNAL(toggled(bool)), this, SLOT(OnFilterTypeBoxTicked(bool)));
}
}
void CResourceBrowser::RefreshResources() void CResourceBrowser::RefreshResources()
{ {
// Fill resource table // Fill resource table
mpModel->FillEntryList(mpSelectedDir, mSearching); mpModel->FillEntryList(mpSelectedDir, InAssetListMode());
// Mark directories to span all three columns // Mark directories to span all three columns
mpUI->ResourceTableView->clearSpans(); mpUI->ResourceTableView->clearSpans();
@ -116,6 +186,8 @@ void CResourceBrowser::RefreshResources()
void CResourceBrowser::UpdateDescriptionLabel() void CResourceBrowser::UpdateDescriptionLabel()
{ {
QString Desc; QString Desc;
Desc += (mAssetListMode ? "[Assets]" : "[Filesystem]");
Desc += " ";
bool ValidDir = mpSelectedDir && !mpSelectedDir->IsRoot(); bool ValidDir = mpSelectedDir && !mpSelectedDir->IsRoot();
QString Path = (ValidDir ? '\\' + TO_QSTRING(mpSelectedDir->FullPath()) : ""); QString Path = (ValidDir ? '\\' + TO_QSTRING(mpSelectedDir->FullPath()) : "");
@ -123,7 +195,7 @@ void CResourceBrowser::UpdateDescriptionLabel()
if (mSearching) if (mSearching)
{ {
QString SearchText = mpUI->SearchBar->text(); QString SearchText = mpUI->SearchBar->text();
Desc = QString("Searching \"%1\"").arg(SearchText); Desc += QString("Searching \"%1\"").arg(SearchText);
if (ValidDir) if (ValidDir)
Desc += QString(" in %1").arg(Path); Desc += QString(" in %1").arg(Path);
@ -131,7 +203,9 @@ void CResourceBrowser::UpdateDescriptionLabel()
else else
{ {
if (ValidDir) if (ValidDir)
Desc = Path; Desc += Path;
else
Desc += "Root";
} }
mpUI->TableDescriptionLabel->setText(Desc); mpUI->TableDescriptionLabel->setText(Desc);
@ -146,6 +220,9 @@ void CResourceBrowser::UpdateStore()
{ {
mpStore = pNewStore; mpStore = pNewStore;
// Refresh type filter list
CreateFilterCheckboxes();
// Refresh directory tree // Refresh directory tree
mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr); mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr);
QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex()); QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex());
@ -155,6 +232,15 @@ void CResourceBrowser::UpdateStore()
} }
} }
void CResourceBrowser::OnDisplayModeChanged(int Index)
{
bool OldIsAssetList = InAssetListMode();
mAssetListMode = Index == 0;
if (InAssetListMode() != OldIsAssetList)
RefreshResources();
}
void CResourceBrowser::OnSortModeChanged(int Index) void CResourceBrowser::OnSortModeChanged(int Index)
{ {
CResourceProxyModel::ESortMode Mode = (Index == 0 ? CResourceProxyModel::eSortByName : CResourceProxyModel::eSortBySize); CResourceProxyModel::ESortMode Mode = (Index == 0 ? CResourceProxyModel::eSortByName : CResourceProxyModel::eSortBySize);
@ -251,15 +337,60 @@ void CResourceBrowser::ExportAssetNames()
void CResourceBrowser::UpdateFilter() void CResourceBrowser::UpdateFilter()
{ {
bool OldIsAssetList = InAssetListMode();
QString SearchText = mpUI->SearchBar->text(); QString SearchText = mpUI->SearchBar->text();
bool NewSearching = !SearchText.isEmpty(); mSearching = !SearchText.isEmpty();
if (mSearching != NewSearching) if (InAssetListMode() != OldIsAssetList)
{ {
mSearching = NewSearching;
RefreshResources(); RefreshResources();
} }
UpdateDescriptionLabel(); UpdateDescriptionLabel();
mpProxyModel->SetSearchString( TO_TWIDESTRING(mpUI->SearchBar->text()) ); mpProxyModel->SetSearchString( TO_TWIDESTRING(mpUI->SearchBar->text()) );
mpProxyModel->invalidate();
}
void CResourceBrowser::ResetTypeFilter()
{
mpFilterAllBox->setChecked(true);
}
void CResourceBrowser::OnFilterTypeBoxTicked(bool Checked)
{
static bool ReentrantGuard = false;
if (ReentrantGuard) return;
ReentrantGuard = true;
if (sender() == mpFilterAllBox)
{
if (!Checked && !mpProxyModel->HasTypeFilter())
mpFilterAllBox->setChecked(true);
else if (Checked)
{
foreach (const SResourceType& rkType, mTypeList)
{
rkType.pFilterCheckBox->setChecked(false);
mpProxyModel->SetTypeFilter(rkType.pTypeInfo, false);
}
}
}
else
{
foreach (const SResourceType& rkType, mTypeList)
{
if (rkType.pFilterCheckBox == sender())
{
mpProxyModel->SetTypeFilter(rkType.pTypeInfo, Checked);
break;
}
}
mpFilterAllBox->setChecked(!mpProxyModel->HasTypeFilter());
}
mpProxyModel->invalidate();
ReentrantGuard = false;
} }

View File

@ -4,8 +4,10 @@
#include "CResourceProxyModel.h" #include "CResourceProxyModel.h"
#include "CResourceTableModel.h" #include "CResourceTableModel.h"
#include "CVirtualDirectoryModel.h" #include "CVirtualDirectoryModel.h"
#include <QCheckBox>
#include <QDialog> #include <QDialog>
#include <QTimer> #include <QTimer>
#include <QVBoxLayout>
namespace Ui { namespace Ui {
class CResourceBrowser; class CResourceBrowser;
@ -22,21 +24,39 @@ class CResourceBrowser : public QDialog
CVirtualDirectory *mpSelectedDir; CVirtualDirectory *mpSelectedDir;
CVirtualDirectoryModel *mpDirectoryModel; CVirtualDirectoryModel *mpDirectoryModel;
QTimer mUpdateFilterTimer; QTimer mUpdateFilterTimer;
bool mAssetListMode;
bool mSearching; bool mSearching;
// Type Filter
QWidget *mpFilterBoxesContainerWidget;
QVBoxLayout *mpFilterBoxesLayout;
QCheckBox *mpFilterAllBox;
QFont mFilterBoxFont;
struct SResourceType
{
CResTypeInfo *pTypeInfo;
QCheckBox *pFilterCheckBox;
};
QList<SResourceType> mTypeList;
public: public:
explicit CResourceBrowser(QWidget *pParent = 0); explicit CResourceBrowser(QWidget *pParent = 0);
~CResourceBrowser(); ~CResourceBrowser();
void SelectResource(CResourceEntry *pEntry); void SelectResource(CResourceEntry *pEntry);
void SelectDirectory(CVirtualDirectory *pDir); void SelectDirectory(CVirtualDirectory *pDir);
void CreateFilterCheckboxes();
// Accessors // Accessors
inline CResourceEntry* SelectedEntry() const { return mpSelectedEntry; } inline CResourceEntry* SelectedEntry() const { return mpSelectedEntry; }
inline bool InAssetListMode() const { return mAssetListMode || mSearching; }
public slots: public slots:
void RefreshResources(); void RefreshResources();
void UpdateDescriptionLabel(); void UpdateDescriptionLabel();
void UpdateStore(); void UpdateStore();
void OnDisplayModeChanged(int Index);
void OnSortModeChanged(int Index); void OnSortModeChanged(int Index);
void OnSearchStringChanged(); void OnSearchStringChanged();
void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex); void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex);
@ -48,6 +68,9 @@ public slots:
void ExportAssetNames(); void ExportAssetNames();
void UpdateFilter(); void UpdateFilter();
void ResetTypeFilter();
void OnFilterTypeBoxTicked(bool Checked);
signals: signals:
void SelectedResourceChanged(CResourceEntry *pNewRes); void SelectedResourceChanged(CResourceEntry *pNewRes);
}; };

View File

@ -79,6 +79,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QComboBox" name="DisplayTypeComboBox">
<item>
<property name="text">
<string>Asset List</string>
</property>
</item>
<item>
<property name="text">
<string>Filesystem</string>
</property>
</item>
</widget>
</item>
<item> <item>
<widget class="QComboBox" name="SortComboBox"> <widget class="QComboBox" name="SortComboBox">
<property name="sizePolicy"> <property name="sizePolicy">
@ -99,8 +113,37 @@
</item> </item>
</widget> </widget>
</item> </item>
<item>
<widget class="QScrollArea" name="FilterCheckboxScrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>189</width>
<height>128</height>
</rect>
</property>
</widget>
</widget>
</item>
<item> <item>
<widget class="QTreeView" name="DirectoryTreeView"> <widget class="QTreeView" name="DirectoryTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="font"> <property name="font">
<font> <font>
<pointsize>10</pointsize> <pointsize>10</pointsize>

View File

@ -2,6 +2,7 @@
#define CRESOURCEPROXYMODEL #define CRESOURCEPROXYMODEL
#include "CResourceTableModel.h" #include "CResourceTableModel.h"
#include <QSet>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
class CResourceProxyModel : public QSortFilterProxyModel class CResourceProxyModel : public QSortFilterProxyModel
@ -16,15 +17,14 @@ public:
private: private:
CResourceTableModel *mpModel; CResourceTableModel *mpModel;
CVirtualDirectory *mpDirectory;
TWideString mSearchString; TWideString mSearchString;
ESortMode mSortMode; ESortMode mSortMode;
QSet<CResTypeInfo*> mTypeFilter;
public: public:
explicit CResourceProxyModel(QObject *pParent = 0) explicit CResourceProxyModel(QObject *pParent = 0)
: QSortFilterProxyModel(pParent) : QSortFilterProxyModel(pParent)
, mpModel(nullptr) , mpModel(nullptr)
, mpDirectory(nullptr)
{ {
SetSortMode(eSortByName); SetSortMode(eSortByName);
} }
@ -66,6 +66,9 @@ public:
CVirtualDirectory *pDir = mpModel->IndexDirectory(Index); CVirtualDirectory *pDir = mpModel->IndexDirectory(Index);
CResourceEntry *pEntry = mpModel->IndexEntry(Index); CResourceEntry *pEntry = mpModel->IndexEntry(Index);
if (pEntry && HasTypeFilter() && !mTypeFilter.contains(pEntry->TypeInfo()))
return false;
if (!mSearchString.IsEmpty()) if (!mSearchString.IsEmpty())
{ {
if (pDir) if (pDir)
@ -77,6 +80,24 @@ public:
return true; return true;
} }
inline void SetTypeFilter(CResTypeInfo *pInfo, bool Allow)
{
if (Allow)
mTypeFilter.insert(pInfo);
else
mTypeFilter.remove(pInfo);
}
inline void ClearTypeFilter()
{
mTypeFilter.clear();
}
inline bool HasTypeFilter() const
{
return !mTypeFilter.isEmpty();
}
inline void SetSortMode(ESortMode Mode) inline void SetSortMode(ESortMode Mode)
{ {
if (mSortMode != Mode) if (mSortMode != Mode)
@ -86,17 +107,10 @@ public:
} }
} }
inline void SetDirectory(CVirtualDirectory *pDir)
{
mpDirectory = pDir;
invalidate();
}
public slots: public slots:
void SetSearchString(const TWideString& rkString) void SetSearchString(const TWideString& rkString)
{ {
mSearchString = rkString.ToUpper(); mSearchString = rkString.ToUpper();
invalidate();
} }
}; };

View File

@ -116,7 +116,7 @@ public:
return rkIndex.row() < mDirectories.size(); return rkIndex.row() < mDirectories.size();
} }
void FillEntryList(CVirtualDirectory *pDir, bool IsSearching) void FillEntryList(CVirtualDirectory *pDir, bool AssetListMode)
{ {
beginResetModel(); beginResetModel();
@ -126,8 +126,8 @@ public:
if (pDir) if (pDir)
{ {
// When not searching, show only subdirectories and assets in the current directory. // In filesystem mode, show only subdirectories and assets in the current directory.
if (!IsSearching) if (!AssetListMode)
{ {
if (!pDir->IsRoot()) if (!pDir->IsRoot())
{ {
@ -147,7 +147,7 @@ public:
} }
} }
// When searching, do not show subdirectories and show all assets in current directory + all subdirectories. // In asset list mode, do not show subdirectories and show all assets in current directory + all subdirectories.
else else
RecursiveAddDirectoryContents(pDir); RecursiveAddDirectoryContents(pDir);
} }