Project Manager Gem Dependencies (#4132)

* Fix engine API change and add gem dependencies

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Add GemCatalog dependency test

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Clarify display name and fix missing const

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Moving a couple helper functions into private scope

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Update gem count when unselecting a gem #4074

This addresses the following issue https://github.com/o3de/o3de/issues/4074

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Active/Inactive filter and dependency tooltips

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Accessors for previously added and dependencies

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Cart displays gem dependency changes

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Shorten titles to fit in summary popup

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Remove QString::number

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Remove extra space

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Consolidate source model accesor helpers

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Addressing minor feedback

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>

* Remove unused local variable

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>
This commit is contained in:
Alex Peterson
2021-09-24 09:01:17 -07:00
committed by GitHub
parent 4c0796f11e
commit 43244d30e1
18 changed files with 627 additions and 188 deletions
@@ -8,6 +8,7 @@
#include <AzCore/std/string/string.h>
#include <GemCatalog/GemModel.h>
#include <GemCatalog/GemSortFilterProxyModel.h>
#include <AzCore/Casting/numeric_cast.h>
namespace O3DE::ProjectManager
@@ -40,8 +41,7 @@ namespace O3DE::ProjectManager
item->setData(gemInfo.m_isAdded, RoleIsAdded);
item->setData(gemInfo.m_directoryLink, RoleDirectoryLink);
item->setData(gemInfo.m_documentationLink, RoleDocLink);
item->setData(gemInfo.m_dependingGemUuids, RoleDependingGems);
item->setData(gemInfo.m_conflictingGemUuids, RoleConflictingGems);
item->setData(gemInfo.m_dependencies, RoleDependingGems);
item->setData(gemInfo.m_version, RoleVersion);
item->setData(gemInfo.m_lastUpdatedDate, RoleLastUpdated);
item->setData(gemInfo.m_binarySizeInKB, RoleBinarySize);
@@ -60,6 +60,39 @@ namespace O3DE::ProjectManager
clear();
}
void GemModel::UpdateGemDependencies()
{
m_gemDependencyMap.clear();
m_gemReverseDependencyMap.clear();
for (auto iter = m_nameToIndexMap.begin(); iter != m_nameToIndexMap.end(); ++iter)
{
const QString& key = iter.key();
const QModelIndex modelIndex = iter.value();
QSet<QModelIndex> dependencies;
GetAllDependingGems(modelIndex, dependencies);
if (!dependencies.isEmpty())
{
m_gemDependencyMap.insert(key, dependencies);
}
}
for (auto iter = m_gemDependencyMap.begin(); iter != m_gemDependencyMap.end(); ++iter)
{
const QString& dependant = iter.key();
for (const QModelIndex& dependency : iter.value())
{
const QString& dependencyName = dependency.data(RoleName).toString();
if (!m_gemReverseDependencyMap.contains(dependencyName))
{
m_gemReverseDependencyMap.insert(dependencyName, QSet<QModelIndex>());
}
m_gemReverseDependencyMap[dependencyName].insert(m_nameToIndexMap[dependant]);
}
}
}
QString GemModel::GetName(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleName).toString();
@@ -125,49 +158,46 @@ namespace O3DE::ProjectManager
return {};
}
void GemModel::FindGemNamesByNameStrings(QStringList& inOutGemNames)
void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames)
{
for (QString& dependingGemString : inOutGemNames)
for (QString& name : inOutGemNames)
{
QModelIndex modelIndex = FindIndexByNameString(dependingGemString);
QModelIndex modelIndex = FindIndexByNameString(name);
if (modelIndex.isValid())
{
dependingGemString = GetDisplayName(modelIndex);
name = GetDisplayName(modelIndex);
}
}
}
QStringList GemModel::GetDependingGemUuids(const QModelIndex& modelIndex)
QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleDependingGems).toStringList();
}
void GemModel::GetAllDependingGems(const QModelIndex& modelIndex, QSet<QModelIndex>& inOutGems)
{
QStringList dependencies = GetDependingGems(modelIndex);
for (const QString& dependency : dependencies)
{
QModelIndex dependencyIndex = FindIndexByNameString(dependency);
if (!inOutGems.contains(dependencyIndex))
{
inOutGems.insert(dependencyIndex);
GetAllDependingGems(dependencyIndex, inOutGems);
}
}
}
QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex)
{
QStringList result = GetDependingGemUuids(modelIndex);
QStringList result = GetDependingGems(modelIndex);
if (result.isEmpty())
{
return {};
}
FindGemNamesByNameStrings(result);
return result;
}
QStringList GemModel::GetConflictingGemUuids(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleConflictingGems).toStringList();
}
QStringList GemModel::GetConflictingGemNames(const QModelIndex& modelIndex)
{
QStringList result = GetConflictingGemUuids(modelIndex);
if (result.isEmpty())
{
return {};
}
FindGemNamesByNameStrings(result);
FindGemDisplayNamesByNameStrings(result);
return result;
}
@@ -201,29 +231,146 @@ namespace O3DE::ProjectManager
return modelIndex.data(RoleRequirement).toString();
}
GemModel* GemModel::GetSourceModel(QAbstractItemModel* model)
{
GemSortFilterProxyModel* proxyModel = qobject_cast<GemSortFilterProxyModel*>(model);
if (proxyModel)
{
return proxyModel->GetSourceModel();
}
else
{
return qobject_cast<GemModel*>(model);
}
}
const GemModel* GemModel::GetSourceModel(const QAbstractItemModel* model)
{
const GemSortFilterProxyModel* proxyModel = qobject_cast<const GemSortFilterProxyModel*>(model);
if (proxyModel)
{
return proxyModel->GetSourceModel();
}
else
{
return qobject_cast<const GemModel*>(model);
}
}
bool GemModel::IsAdded(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleIsAdded).toBool();
}
bool GemModel::IsAddedDependency(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleIsAddedDependency).toBool();
}
void GemModel::SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
{
model.setData(modelIndex, isAdded, RoleIsAdded);
UpdateDependencies(model, modelIndex);
}
bool GemModel::HasDependentGems(const QModelIndex& modelIndex) const
{
QVector<QModelIndex> dependentGems = GatherDependentGems(modelIndex);
for (const QModelIndex& dependency : dependentGems)
{
if (IsAdded(dependency))
{
return true;
}
}
return false;
}
void GemModel::UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex)
{
GemModel* gemModel = GetSourceModel(&model);
AZ_Assert(gemModel, "Failed to obtain GemModel");
QVector<QModelIndex> dependencies = gemModel->GatherGemDependencies(modelIndex);
if (IsAdded(modelIndex))
{
for (const QModelIndex& dependency : dependencies)
{
SetIsAddedDependency(*gemModel, dependency, true);
}
}
else
{
// still a dependency if some added gem depends on this one
SetIsAddedDependency(model, modelIndex, gemModel->HasDependentGems(modelIndex));
for (const QModelIndex& dependency : dependencies)
{
SetIsAddedDependency(*gemModel, dependency, gemModel->HasDependentGems(dependency));
}
}
}
void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
{
model.setData(modelIndex, isAdded, RoleIsAddedDependency);
}
void GemModel::SetWasPreviouslyAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded)
{
model.setData(modelIndex, wasAdded, RoleWasPreviouslyAdded);
if (wasAdded)
{
// update all dependencies
GemModel* gemModel = GetSourceModel(&model);
AZ_Assert(gemModel, "Failed to obtain GemModel");
QVector<QModelIndex> dependencies = gemModel->GatherGemDependencies(modelIndex);
for (const QModelIndex& dependency : dependencies)
{
SetWasPreviouslyAddedDependency(*gemModel, dependency, true);
}
}
}
bool GemModel::NeedsToBeAdded(const QModelIndex& modelIndex)
void GemModel::SetWasPreviouslyAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded)
{
return (!modelIndex.data(RoleWasPreviouslyAdded).toBool() && modelIndex.data(RoleIsAdded).toBool());
model.setData(modelIndex, wasAdded, RoleWasPreviouslyAddedDependency);
}
bool GemModel::NeedsToBeRemoved(const QModelIndex& modelIndex)
bool GemModel::WasPreviouslyAdded(const QModelIndex& modelIndex)
{
return (modelIndex.data(RoleWasPreviouslyAdded).toBool() && !modelIndex.data(RoleIsAdded).toBool());
return modelIndex.data(RoleWasPreviouslyAdded).toBool();
}
bool GemModel::WasPreviouslyAddedDependency(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleWasPreviouslyAddedDependency).toBool();
}
bool GemModel::NeedsToBeAdded(const QModelIndex& modelIndex, bool includeDependencies)
{
bool previouslyAdded = modelIndex.data(RoleWasPreviouslyAdded).toBool();
bool added = modelIndex.data(RoleIsAdded).toBool();
if (includeDependencies)
{
previouslyAdded |= modelIndex.data(RoleWasPreviouslyAddedDependency).toBool();
added |= modelIndex.data(RoleIsAddedDependency).toBool();
}
return !previouslyAdded && added;
}
bool GemModel::NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies)
{
bool previouslyAdded = modelIndex.data(RoleWasPreviouslyAdded).toBool();
bool added = modelIndex.data(RoleIsAdded).toBool();
if (includeDependencies)
{
previouslyAdded |= modelIndex.data(RoleWasPreviouslyAddedDependency).toBool();
added |= modelIndex.data(RoleIsAddedDependency).toBool();
}
return previouslyAdded && !added;
}
bool GemModel::HasRequirement(const QModelIndex& modelIndex)
@@ -244,13 +391,44 @@ namespace O3DE::ProjectManager
return false;
}
QVector<QModelIndex> GemModel::GatherGemsToBeAdded() const
QVector<QModelIndex> GemModel::GatherGemDependencies(const QModelIndex& modelIndex) const
{
QVector<QModelIndex> result;
const QString& gemName = modelIndex.data(RoleName).toString();
if (m_gemDependencyMap.contains(gemName))
{
for (const QModelIndex& dependency : m_gemDependencyMap[gemName])
{
result.push_back(dependency);
}
}
return result;
}
QVector<QModelIndex> GemModel::GatherDependentGems(const QModelIndex& modelIndex, bool addedOnly) const
{
QVector<QModelIndex> result;
const QString& gemName = modelIndex.data(RoleName).toString();
if (m_gemReverseDependencyMap.contains(gemName))
{
for (const QModelIndex& dependency : m_gemReverseDependencyMap[gemName])
{
if (!addedOnly || GemModel::IsAdded(dependency))
{
result.push_back(dependency);
}
}
}
return result;
}
QVector<QModelIndex> GemModel::GatherGemsToBeAdded(bool includeDependencies) const
{
QVector<QModelIndex> result;
for (int row = 0; row < rowCount(); ++row)
{
const QModelIndex modelIndex = index(row, 0);
if (NeedsToBeAdded(modelIndex))
if (NeedsToBeAdded(modelIndex, includeDependencies))
{
result.push_back(modelIndex);
}
@@ -258,13 +436,13 @@ namespace O3DE::ProjectManager
return result;
}
QVector<QModelIndex> GemModel::GatherGemsToBeRemoved() const
QVector<QModelIndex> GemModel::GatherGemsToBeRemoved(bool includeDependencies) const
{
QVector<QModelIndex> result;
for (int row = 0; row < rowCount(); ++row)
{
const QModelIndex modelIndex = index(row, 0);
if (NeedsToBeRemoved(modelIndex))
if (NeedsToBeRemoved(modelIndex, includeDependencies))
{
result.push_back(modelIndex);
}
@@ -272,13 +450,13 @@ namespace O3DE::ProjectManager
return result;
}
int GemModel::TotalAddedGems() const
int GemModel::TotalAddedGems(bool includeDependencies) const
{
int result = 0;
for (int row = 0; row < rowCount(); ++row)
{
const QModelIndex modelIndex = index(row, 0);
if (IsAdded(modelIndex))
if (IsAdded(modelIndex) || (includeDependencies && IsAddedDependency(modelIndex)))
{
++result;
}