Modified resource browser to use a filesystem browser view

This commit is contained in:
Aruki 2017-02-01 20:23:51 -07:00
parent c51d79cc42
commit 548fcb2f8e
8 changed files with 309 additions and 80 deletions

View File

@ -34,7 +34,7 @@ void CEditorApplication::InitEditor()
mpWorldEditor = new CWorldEditor(); mpWorldEditor = new CWorldEditor();
mpResourceBrowser = new CResourceBrowser(mpWorldEditor); mpResourceBrowser = new CResourceBrowser(mpWorldEditor);
mpProjectDialog = new CProjectOverviewDialog(); mpProjectDialog = new CProjectOverviewDialog();
connect(mpProjectDialog, SIGNAL(ActiveProjectChanged(CGameProject*)), mpResourceBrowser, SLOT(RefreshResources())); connect(mpProjectDialog, SIGNAL(ActiveProjectChanged(CGameProject*)), mpResourceBrowser, SLOT(UpdateStore()));
} }
void CEditorApplication::EditResource(CResourceEntry *pEntry) void CEditorApplication::EditResource(CResourceEntry *pEntry)

View File

@ -666,7 +666,8 @@ void CPropertyDelegate::SetCharacterModelData(QWidget *pEditor, const QModelInde
if (Type == eAssetProperty) if (Type == eAssetProperty)
{ {
Params.SetResource( static_cast<CResourceSelector*>(pEditor)->Entry()->ID() ); CResourceEntry *pEntry = static_cast<CResourceSelector*>(pEditor)->Entry();
Params.SetResource( pEntry ? pEntry->ID() : CAssetID::InvalidID(mpEditor->CurrentGame()) );
} }
else if (Type == eEnumProperty) else if (Type == eEnumProperty)

View File

@ -12,6 +12,8 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
, mpUI(new Ui::CResourceBrowser) , mpUI(new Ui::CResourceBrowser)
, mpSelectedEntry(nullptr) , mpSelectedEntry(nullptr)
, mpStore(gpResourceStore) , mpStore(gpResourceStore)
, mpSelectedDir(nullptr)
, mSearching(false)
{ {
mpUI->setupUi(this); mpUI->setupUi(this);
setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint); setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint);
@ -49,11 +51,11 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
pImportNamesMenu->addAction(pImportFromAssetNameMapAction); pImportNamesMenu->addAction(pImportFromAssetNameMapAction);
// Set up connections // 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->SearchBar, SIGNAL(textChanged(QString)), this, SLOT(OnSearchStringChanged()));
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(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(mpUI->ResourceTableView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnResourceSelectionChanged(QModelIndex, QModelIndex)));
connect(pImportFromContentsTxtAction, SIGNAL(triggered()), this, SLOT(OnImportPakContentsTxt())); connect(pImportFromContentsTxtAction, SIGNAL(triggered()), this, SLOT(OnImportPakContentsTxt()));
connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap())); connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap()));
@ -73,15 +75,55 @@ CResourceBrowser::~CResourceBrowser()
void CResourceBrowser::RefreshResources() void CResourceBrowser::RefreshResources()
{ {
// Fill resource table // Fill resource table
mpStore = (mpUI->StoreComboBox->currentIndex() == 0 ? gpResourceStore : gpEditorStore); mpModel->FillEntryList(mpSelectedDir, mSearching);
mpModel->FillEntryList(mpStore);
// Fill directory tree // Mark directories to span all three columns
mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr); mpUI->ResourceTableView->clearSpans();
QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex());
mpUI->DirectoryTreeView->expand(RootIndex); for (u32 iDir = 0; iDir < mpModel->NumDirectories(); iDir++)
mpUI->DirectoryTreeView->clearSelection(); mpUI->ResourceTableView->setSpan(iDir, 0, 1, 3);
OnDirectorySelectionChanged(QModelIndex(), QModelIndex()); }
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) void CResourceBrowser::OnSortModeChanged(int Index)
@ -98,19 +140,33 @@ void CResourceBrowser::OnSearchStringChanged()
void CResourceBrowser::OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& /*rkPrevIndex*/) void CResourceBrowser::OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& /*rkPrevIndex*/)
{ {
CVirtualDirectory *pDir = nullptr;
if (rkNewIndex.isValid()) 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); 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*/) void CResourceBrowser::OnResourceSelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& /*rkPrevIndex*/)
@ -167,5 +223,15 @@ void CResourceBrowser::ExportAssetNames()
void CResourceBrowser::UpdateFilter() 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()) ); mpProxyModel->SetSearchString( TO_TWIDESTRING(mpUI->SearchBar->text()) );
} }

View File

@ -19,8 +19,10 @@ class CResourceBrowser : public QDialog
CResourceStore *mpStore; CResourceStore *mpStore;
CResourceTableModel *mpModel; CResourceTableModel *mpModel;
CResourceProxyModel *mpProxyModel; CResourceProxyModel *mpProxyModel;
CVirtualDirectory *mpSelectedDir;
CVirtualDirectoryModel *mpDirectoryModel; CVirtualDirectoryModel *mpDirectoryModel;
QTimer mUpdateFilterTimer; QTimer mUpdateFilterTimer;
bool mSearching;
public: public:
explicit CResourceBrowser(QWidget *pParent = 0); explicit CResourceBrowser(QWidget *pParent = 0);
@ -31,10 +33,12 @@ public:
public slots: public slots:
void RefreshResources(); void RefreshResources();
void UpdateDescriptionLabel();
void UpdateStore();
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);
void OnDoubleClickResource(QModelIndex Index); void OnDoubleClickTable(QModelIndex Index);
void OnResourceSelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex); void OnResourceSelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex);
void OnImportPakContentsTxt(); void OnImportPakContentsTxt();
void OnGenerateAssetNames(); void OnGenerateAssetNames();

View File

@ -150,51 +150,90 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QTableView" name="ResourceTableView"> <widget class="QWidget" name="TableContainerWidget" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>3</horstretch> <horstretch>3</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="font"> <layout class="QVBoxLayout" name="verticalLayout_2">
<font> <property name="spacing">
<pointsize>10</pointsize> <number>0</number>
</font> </property>
</property> <property name="leftMargin">
<property name="editTriggers"> <number>0</number>
<set>QAbstractItemView::NoEditTriggers</set> </property>
</property> <property name="topMargin">
<property name="alternatingRowColors"> <number>0</number>
<bool>true</bool> </property>
</property> <property name="rightMargin">
<property name="selectionMode"> <number>0</number>
<enum>QAbstractItemView::ExtendedSelection</enum> </property>
</property> <property name="bottomMargin">
<property name="selectionBehavior"> <number>0</number>
<enum>QAbstractItemView::SelectRows</enum> </property>
</property> <item>
<property name="verticalScrollMode"> <widget class="QLabel" name="TableDescriptionLabel">
<enum>QAbstractItemView::ScrollPerPixel</enum> <property name="font">
</property> <font>
<property name="horizontalScrollMode"> <pointsize>8</pointsize>
<enum>QAbstractItemView::ScrollPerPixel</enum> </font>
</property> </property>
<property name="sortingEnabled"> <property name="text">
<bool>true</bool> <string/>
</property> </property>
<attribute name="horizontalHeaderVisible"> </widget>
<bool>false</bool> </item>
</attribute> <item>
<attribute name="verticalHeaderVisible"> <widget class="QTableView" name="ResourceTableView">
<bool>false</bool> <property name="sizePolicy">
</attribute> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<attribute name="verticalHeaderDefaultSectionSize"> <horstretch>3</horstretch>
<number>21</number> <verstretch>0</verstretch>
</attribute> </sizepolicy>
<attribute name="verticalHeaderMinimumSectionSize"> </property>
<number>21</number> <property name="font">
</attribute> <font>
<pointsize>10</pointsize>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>21</number>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>21</number>
</attribute>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -39,25 +39,40 @@ public:
bool lessThan(const QModelIndex& rkLeft, const QModelIndex& rkRight) const bool lessThan(const QModelIndex& rkLeft, const QModelIndex& rkRight) const
{ {
CResourceEntry *pLeft = mpModel->IndexEntry(rkLeft); CVirtualDirectory *pLeftDir = mpModel->IndexDirectory(rkLeft);
CResourceEntry *pRight = mpModel->IndexEntry(rkRight); 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 else
return pLeft->Size() < pRight->Size(); return pLeftRes->Size() < pRightRes->Size();
} }
bool filterAcceptsRow(int SourceRow, const QModelIndex& rkSourceParent) const bool filterAcceptsRow(int SourceRow, const QModelIndex& rkSourceParent) const
{ {
QModelIndex Index = mpModel->index(SourceRow, 0, rkSourceParent); QModelIndex Index = mpModel->index(SourceRow, 0, rkSourceParent);
CVirtualDirectory *pDir = mpModel->IndexDirectory(Index);
CResourceEntry *pEntry = mpModel->IndexEntry(Index); CResourceEntry *pEntry = mpModel->IndexEntry(Index);
if (mpDirectory && !pEntry->IsInDirectory(mpDirectory)) if (!mSearchString.IsEmpty())
return false; {
if (pDir)
if (!mSearchString.IsEmpty() && !pEntry->UppercaseName().Contains(mSearchString)) return false;
return false; else
return pEntry->UppercaseName().Contains(mSearchString);
}
return true; return true;
} }

View File

@ -12,7 +12,10 @@
class CResourceTableModel : public QAbstractTableModel class CResourceTableModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
QList<CVirtualDirectory*> mDirectories;
QList<CResourceEntry*> mEntries; QList<CResourceEntry*> mEntries;
bool mHasParent;
public: public:
CResourceTableModel(QObject *pParent = 0) CResourceTableModel(QObject *pParent = 0)
@ -21,7 +24,7 @@ public:
int rowCount(const QModelIndex& /*rkParent*/) const int rowCount(const QModelIndex& /*rkParent*/) const
{ {
return mEntries.size(); return mDirectories.size() + mEntries.size();
} }
int columnCount(const QModelIndex& /*rkParent*/) const int columnCount(const QModelIndex& /*rkParent*/) const
@ -31,9 +34,29 @@ public:
QVariant data(const QModelIndex& rkIndex, int Role) const QVariant data(const QModelIndex& rkIndex, int Role) const
{ {
CResourceEntry *pEntry = mEntries[rkIndex.row()];
u32 Col = rkIndex.column(); 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 (Role == Qt::DisplayRole)
{ {
if (Col == 0) if (Col == 0)
@ -64,28 +87,73 @@ public:
CResourceEntry* IndexEntry(const QModelIndex& rkIndex) const 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(); beginResetModel();
mEntries.clear(); 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(); for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++)
if (pInfo->IsVisibleInBrowser() && !It->IsHidden()) mDirectories << pDir->SubdirectoryByIndex(iDir);
mEntries << *It;
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(); 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 #endif // CRESOURCELISTMODEL

View File

@ -85,6 +85,42 @@ public:
return QVariant::Invalid; return QVariant::Invalid;
} }
QModelIndex GetIndexForDirectory(CVirtualDirectory *pDir)
{
QVector<int> 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 inline CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const
{ {
if (!rkIndex.isValid()) return nullptr; if (!rkIndex.isValid()) return nullptr;