You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
400 lines
14 KiB
C++
400 lines
14 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzCore/Script/ScriptTimePoint.h>
|
|
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
|
|
|
|
#include <QMimeData>
|
|
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QRegularExpression::d': class 'QExplicitlySharedDataPointer<QRegularExpressionPrivate>' needs to have dll-interface to be used by clients of class 'QRegularExpression'
|
|
#include <QRegularExpression>
|
|
AZ_POP_DISABLE_WARNING
|
|
|
|
namespace AzToolsFramework
|
|
{
|
|
namespace AssetBrowser
|
|
{
|
|
AssetBrowserModel::AssetBrowserModel(QObject* parent)
|
|
: QAbstractItemModel(parent)
|
|
, m_rootEntry(nullptr)
|
|
, m_loaded(false)
|
|
, m_addingEntry(false)
|
|
, m_removingEntry(false)
|
|
{
|
|
AssetBrowserModelRequestBus::Handler::BusConnect();
|
|
AZ::TickBus::Handler::BusConnect();
|
|
}
|
|
|
|
AssetBrowserModel::~AssetBrowserModel()
|
|
{
|
|
AssetBrowserModelRequestBus::Handler::BusDisconnect();
|
|
AZ::TickBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
QModelIndex AssetBrowserModel::findIndex(const QString& absoluteAssetPath) const
|
|
{
|
|
// Split the path based on either platform's slash
|
|
QRegularExpression regex(QStringLiteral("[\\/]"));
|
|
|
|
QStringList assetPathComponents = absoluteAssetPath.split(regex);
|
|
|
|
AssetBrowserEntry* cursor = m_rootEntry.get();
|
|
|
|
if (cursor && absoluteAssetPath.contains(cursor->GetFullPath().c_str()))
|
|
{
|
|
while (true)
|
|
{
|
|
// find the child entry that contains more
|
|
bool foundChild = false;
|
|
for (int i = 0; i < cursor->GetChildCount(); i++)
|
|
{
|
|
AssetBrowserEntry* child = cursor->GetChild(i);
|
|
if (child)
|
|
{
|
|
QString newPath = child->GetFullPath().c_str();
|
|
if (absoluteAssetPath.startsWith(newPath))
|
|
{
|
|
if (absoluteAssetPath == newPath)
|
|
{
|
|
QModelIndex index;
|
|
if (GetEntryIndex(child, index))
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
|
|
// Confirm that this is a real match as opposed to a partial match.
|
|
// For instance, an asset absolute path C:/somepath/someotherpath/blah.tga will partial match with c:/somepath/some
|
|
// and get us here.
|
|
|
|
QStringList possibleMatchComponents = newPath.split(regex);
|
|
QString possibleMatchDirectory = possibleMatchComponents.last();
|
|
Q_ASSERT(assetPathComponents.count() >= possibleMatchComponents.count());
|
|
if (possibleMatchDirectory == assetPathComponents[possibleMatchComponents.count() - 1])
|
|
{
|
|
cursor = child;
|
|
foundChild = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundChild)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
QModelIndex AssetBrowserModel::index(int row, int column, const QModelIndex& parent) const
|
|
{
|
|
if (!hasIndex(row, column, parent))
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
AssetBrowserEntry* parentEntry;
|
|
if (!parent.isValid())
|
|
{
|
|
parentEntry = m_rootEntry.get();
|
|
}
|
|
else
|
|
{
|
|
parentEntry = reinterpret_cast<AssetBrowserEntry*>(parent.internalPointer());
|
|
}
|
|
|
|
AssetBrowserEntry* childEntry = parentEntry->m_children[row];
|
|
|
|
if (!childEntry)
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
QModelIndex index;
|
|
GetEntryIndex(childEntry, index);
|
|
return index;
|
|
}
|
|
|
|
int AssetBrowserModel::rowCount(const QModelIndex& parent) const
|
|
{
|
|
if (!m_rootEntry)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//If the column of the parent is one of those we don't want any more rows as children
|
|
if (parent.isValid())
|
|
{
|
|
if ((parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName)) &&
|
|
(parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Name)) &&
|
|
(parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Path)))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
AssetBrowserEntry* parentAssetEntry;
|
|
if (!parent.isValid())
|
|
{
|
|
parentAssetEntry = m_rootEntry.get();
|
|
}
|
|
else
|
|
{
|
|
parentAssetEntry = static_cast<AssetBrowserEntry*>(parent.internalPointer());
|
|
}
|
|
return parentAssetEntry->GetChildCount();
|
|
}
|
|
|
|
int AssetBrowserModel::columnCount(const QModelIndex& /*parent*/) const
|
|
{
|
|
return aznumeric_cast<int>(AssetBrowserEntry::Column::Count);
|
|
}
|
|
|
|
QVariant AssetBrowserModel::data(const QModelIndex& index, int role) const
|
|
{
|
|
if (!index.isValid())
|
|
{
|
|
return QVariant();
|
|
}
|
|
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
const AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
|
|
return item->GetDisplayName();
|
|
}
|
|
|
|
if (role == Roles::EntryRole)
|
|
{
|
|
const AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
|
|
return QVariant::fromValue(item);
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags AssetBrowserModel::flags(const QModelIndex& index) const
|
|
{
|
|
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
|
|
|
|
if (index.isValid())
|
|
{
|
|
// allow retrieval of mimedata of sources or products only (i.e. cant drag folders or root)
|
|
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
|
|
if (item && (item->RTTI_IsTypeOf(ProductAssetBrowserEntry::RTTI_Type()) || item->RTTI_IsTypeOf(SourceAssetBrowserEntry::RTTI_Type())))
|
|
{
|
|
return Qt::ItemIsDragEnabled | defaultFlags;
|
|
}
|
|
}
|
|
return defaultFlags;
|
|
//return Qt::ItemFlags(~Qt::ItemIsDragEnabled & defaultFlags);
|
|
}
|
|
|
|
QMimeData* AssetBrowserModel::mimeData(const QModelIndexList& indexes) const
|
|
{
|
|
QMimeData* mimeData = new QMimeData;
|
|
|
|
for (const auto& index : indexes)
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
|
|
if (item)
|
|
{
|
|
item->AddToMimeData(mimeData);
|
|
}
|
|
}
|
|
}
|
|
return mimeData;
|
|
}
|
|
|
|
QVariant AssetBrowserModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (orientation == Qt::Horizontal && role == Roles::EntryRole)
|
|
{
|
|
return tr(AssetBrowserEntry::m_columnNames[section]);
|
|
}
|
|
|
|
return QAbstractItemModel::headerData(section, orientation, role);
|
|
}
|
|
|
|
void AssetBrowserModel::SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector<AZ::Data::AssetId>& assetIds)
|
|
{
|
|
for (const auto& index : indexes)
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
|
|
|
|
if (item->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
|
|
{
|
|
assetIds.push_back(static_cast<ProductAssetBrowserEntry*>(item)->GetAssetId());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector<AssetBrowserEntry*>& entries)
|
|
{
|
|
for (const auto& index : indexes)
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
|
|
entries.push_back(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
AZStd::shared_ptr<RootAssetBrowserEntry> AssetBrowserModel::GetRootEntry() const
|
|
{
|
|
return m_rootEntry;
|
|
}
|
|
|
|
void AssetBrowserModel::SetRootEntry(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry)
|
|
{
|
|
m_rootEntry = rootEntry;
|
|
}
|
|
|
|
QModelIndex AssetBrowserModel::parent(const QModelIndex& child) const
|
|
{
|
|
if (!child.isValid())
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
AssetBrowserEntry* childAssetEntry = static_cast<AssetBrowserEntry*>(child.internalPointer());
|
|
AssetBrowserEntry* parentEntry = childAssetEntry->GetParent();
|
|
|
|
QModelIndex parentIndex;
|
|
if (GetEntryIndex(parentEntry, parentIndex))
|
|
{
|
|
return parentIndex;
|
|
}
|
|
return QModelIndex();
|
|
}
|
|
|
|
bool AssetBrowserModel::IsLoaded() const
|
|
{
|
|
return m_loaded;
|
|
}
|
|
|
|
void AssetBrowserModel::BeginAddEntry(AssetBrowserEntry* parent)
|
|
{
|
|
QModelIndex parentIndex;
|
|
if (GetEntryIndex(parent, parentIndex))
|
|
{
|
|
m_addingEntry = true;
|
|
int row = parent->GetChildCount();
|
|
beginInsertRows(parentIndex, row, row);
|
|
}
|
|
}
|
|
|
|
void AssetBrowserModel::EndAddEntry(AssetBrowserEntry* parent)
|
|
{
|
|
if (m_addingEntry)
|
|
{
|
|
m_addingEntry = false;
|
|
endInsertRows();
|
|
|
|
// we have to also invalidate our parent all the way up the chain.
|
|
// since in this model, the children's data is actually relevant to the filtering of a parent
|
|
// since a parent "matches" the filter if its children do.
|
|
if ((m_rootEntry) && (!m_rootEntry->IsInitialUpdate()))
|
|
{
|
|
// this is only necessary if its not the initial refresh.
|
|
while (parent)
|
|
{
|
|
QModelIndex parentIndex;
|
|
if (GetEntryIndex(parent, parentIndex))
|
|
{
|
|
Q_EMIT dataChanged(parentIndex, parentIndex);
|
|
}
|
|
parent = parent->GetParent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetBrowserModel::BeginRemoveEntry(AssetBrowserEntry* entry)
|
|
{
|
|
int row = entry->row();
|
|
QModelIndex parentIndex;
|
|
if (GetEntryIndex(entry->m_parentAssetEntry, parentIndex))
|
|
{
|
|
m_removingEntry = true;
|
|
beginRemoveRows(parentIndex, row, row);
|
|
}
|
|
}
|
|
|
|
void AssetBrowserModel::EndRemoveEntry()
|
|
{
|
|
if (m_removingEntry)
|
|
{
|
|
m_removingEntry = false;
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
|
|
void AssetBrowserModel::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
|
{
|
|
// if any entries changed since last tick, notify the views
|
|
if (EntryCache* cache = EntryCache::GetInstance())
|
|
{
|
|
if (!cache->m_dirtyThumbnailsSet.empty())
|
|
{
|
|
for (AssetBrowserEntry* entry : cache->m_dirtyThumbnailsSet)
|
|
{
|
|
QModelIndex index;
|
|
if (GetEntryIndex(entry, index))
|
|
{
|
|
AZ_PUSH_DISABLE_WARNING(4127, "-Wunknown-warning-option") // conditional expression is constant
|
|
Q_EMIT dataChanged(index, index, { Roles::EntryRole });
|
|
AZ_POP_DISABLE_WARNING
|
|
}
|
|
}
|
|
cache->m_dirtyThumbnailsSet.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AssetBrowserModel::GetEntryIndex(AssetBrowserEntry* entry, QModelIndex& index) const
|
|
{
|
|
if (!entry)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (azrtti_istypeof<RootAssetBrowserEntry*>(entry))
|
|
{
|
|
index = QModelIndex();
|
|
return true;
|
|
}
|
|
|
|
if (!entry->m_parentAssetEntry)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int row = entry->row();
|
|
index = createIndex(row, aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName), entry);
|
|
return true;
|
|
}
|
|
|
|
} // namespace AssetBrowser
|
|
} // namespace AzToolsFramework
|
|
|
|
#include "AssetBrowser/moc_AssetBrowserModel.cpp"
|