@@ -8,7 +8,7 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}'
|
||||
@@ -21,7 +21,7 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,9 @@ namespace AssetBundler
|
||||
}
|
||||
auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/project_path";
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
|
||||
|
||||
@@ -106,7 +106,9 @@ namespace AssetBundler
|
||||
}
|
||||
auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/project_path";
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
|
||||
|
||||
@@ -1067,15 +1067,12 @@ namespace AssetProcessor
|
||||
|
||||
if (dropAllTables)
|
||||
{
|
||||
AZ_TracePrintf("AssetDatabase", "Closing existing db connection\n"); // Temporary debug output to help with tracking down a crash
|
||||
// drop all tables by destroying the entire database.
|
||||
m_databaseConnection->Close();
|
||||
|
||||
AZ_TracePrintf("AssetDatabase", "Getting db file path\n"); // Temporary debug output to help with tracking down a crash
|
||||
AZStd::string dbFilePath = GetAssetDatabaseFilePath();
|
||||
if (dbFilePath != ":memory:")
|
||||
{
|
||||
AZ_TracePrintf("AssetDatabase", "Deleting existing db %s\n", dbFilePath.c_str()); // Temporary debug output to help with tracking down a crash
|
||||
// you cannot delete a memory database, but it drops all data when you close it anyway.
|
||||
if (!AZ::IO::SystemFile::Delete(dbFilePath.c_str()))
|
||||
{
|
||||
@@ -1085,7 +1082,7 @@ namespace AssetProcessor
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AZ_TracePrintf("AssetDatabase", "Re-opening connection\n"); // Temporary debug output to help with tracking down a crash
|
||||
|
||||
if (!m_databaseConnection->Open(dbFilePath, IsReadOnly()))
|
||||
{
|
||||
delete m_databaseConnection;
|
||||
|
||||
@@ -431,8 +431,9 @@ namespace AssetProcessor
|
||||
}
|
||||
file.Close();
|
||||
|
||||
AZ::u32 hashedSpecialization = static_cast<AZ::u32>(AZStd::hash<AZStd::string_view>{}(specializationString));
|
||||
AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s. This can result in a product ID collision with other builders for this asset.",
|
||||
const AZ::u32 hashedSpecialization = static_cast<AZ::u32>(AZStd::hash<AZStd::string_view>{}(specializationString));
|
||||
AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s."
|
||||
" This can result in a product ID collision with other builders for this asset.",
|
||||
AZ_STRING_ARG(specializationString));
|
||||
response.m_outputProducts.emplace_back(outputPath, m_assetType, hashedSpecialization);
|
||||
response.m_outputProducts.back().m_dependenciesHandled = true;
|
||||
|
||||
@@ -128,7 +128,9 @@ namespace AssetProcessor
|
||||
settingsRegistry->Set(cacheRootKey, m_data->m_temporarySourceDir.absoluteFilePath("Cache").toUtf8().constData());
|
||||
auto projectPathKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
||||
settingsRegistry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
|
||||
AssetUtilities::ComputeProjectCacheRoot(m_data->m_cacheRootDir);
|
||||
QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(m_data->m_cacheRootDir.absolutePath());
|
||||
|
||||
@@ -107,12 +107,13 @@ namespace AssetProcessorMessagesTests
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey
|
||||
};
|
||||
constexpr AZ::SettingsRegistryInterface::FixedValueString projectPathKey{ bootstrapKey + "/project_path" };
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
// Force the branch token into settings registry before starting the application manager.
|
||||
// This avoids writing the asset_processor.setreg file which can cause fileIO errors.
|
||||
const AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath();
|
||||
constexpr AZ::SettingsRegistryInterface::FixedValueString branchTokenKey{ bootstrapKey + "/assetProcessor_branch_token" };
|
||||
AZStd::string token;
|
||||
AZ::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token);
|
||||
|
||||
@@ -71,12 +71,13 @@ namespace AssetProcessor
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
auto bootstrapKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
auto projectPathKey = bootstrapKey + "/project_path";
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
// Forcing the branch token into settings registry before starting the application manager.
|
||||
// This avoids writing the asset_processor.setreg file which can cause fileIO errors.
|
||||
AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath();
|
||||
auto branchTokenKey = bootstrapKey + "/assetProcessor_branch_token";
|
||||
AZStd::string token;
|
||||
AzFramework::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token);
|
||||
|
||||
@@ -50,7 +50,9 @@ namespace AssetProcessor
|
||||
+ "/project_path";
|
||||
if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,9 @@ void AssetProcessorManagerTest::SetUp()
|
||||
registry->Set(cacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().constData());
|
||||
auto projectPathKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
m_data->m_databaseLocationListener.BusConnect();
|
||||
@@ -241,9 +243,7 @@ void AssetProcessorManagerTest::SetUp()
|
||||
ASSERT_TRUE(m_mockApplicationManager->RegisterAssetRecognizerAsBuilder(rec));
|
||||
m_mockApplicationManager->BusConnect();
|
||||
|
||||
AZ_Printf("UnitTest", "Allocating APM\n")
|
||||
m_assetProcessorManager.reset(new AssetProcessorManager_Test(m_config.get()));
|
||||
AZ_Printf("UnitTest", "APM ready\n");
|
||||
m_errorAbsorber->Clear();
|
||||
|
||||
m_isIdling = false;
|
||||
@@ -4466,9 +4466,7 @@ AssetBuilderSDK::AssetBuilderDesc MockBuilderInfoHandler::CreateBuilderDesc(cons
|
||||
|
||||
void FingerprintTest::SetUp()
|
||||
{
|
||||
AZ_Printf("FingerprintTest", "SetUp start\n");
|
||||
AssetProcessorManagerTest::SetUp();
|
||||
AZ_Printf("FingerprintTest", "SetUp self\n");
|
||||
|
||||
// We don't want the mock application manager to provide builder descriptors, mockBuilderInfoHandler will provide our own
|
||||
m_mockApplicationManager->BusDisconnect();
|
||||
@@ -4487,23 +4485,18 @@ void FingerprintTest::SetUp()
|
||||
});
|
||||
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(m_absolutePath, ""));
|
||||
AZ_Printf("FingerprintTest", "SetUp end\n");
|
||||
}
|
||||
|
||||
void FingerprintTest::TearDown()
|
||||
{
|
||||
AZ_Printf("FingerprintTest", "TearDown start\n");
|
||||
m_jobResults = AZStd::vector<AssetProcessor::JobDetails>{};
|
||||
m_mockBuilderInfoHandler = {};
|
||||
|
||||
AZ_Printf("FingerprintTest", "TearDown parent\n");
|
||||
AssetProcessorManagerTest::TearDown();
|
||||
AZ_Printf("FingerprintTest", "TearDown end\n");
|
||||
}
|
||||
|
||||
void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult)
|
||||
{
|
||||
AZ_Printf("FingerprintTest", "Fingerprint Test Start\n");
|
||||
m_mockBuilderInfoHandler.m_builderDesc.m_analysisFingerprint = builderFingerprint.toUtf8().data();
|
||||
m_mockBuilderInfoHandler.m_jobFingerprint = jobFingerprint;
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, m_absolutePath));
|
||||
@@ -4512,7 +4505,6 @@ void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString job
|
||||
ASSERT_EQ(m_mockBuilderInfoHandler.m_createJobsCount, 1);
|
||||
ASSERT_EQ(m_jobResults.size(), 1);
|
||||
ASSERT_EQ(m_jobResults[0].m_autoFail, expectedResult);
|
||||
AZ_Printf("FingerprintTest", "Fingerprint Test End\n");
|
||||
}
|
||||
|
||||
TEST_F(FingerprintTest, FingerprintChecking_JobFingerprint_NoBuilderFingerprint)
|
||||
|
||||
@@ -156,7 +156,6 @@ namespace UnitTestUtils
|
||||
|
||||
bool OnPreWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numWarningsAbsorbed;
|
||||
if (m_debugMessages)
|
||||
{
|
||||
@@ -167,7 +166,9 @@ namespace UnitTestUtils
|
||||
|
||||
bool OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
// Print out absorbed asserts since asserts are pretty important and accidentally absorbing unintended ones can lead to difficult-to-detect issues
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, "Absorbed Assert: %s\n", message);
|
||||
|
||||
++m_numAssertsAbsorbed;
|
||||
if (m_debugMessages)
|
||||
{
|
||||
@@ -178,7 +179,6 @@ namespace UnitTestUtils
|
||||
|
||||
bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numErrorsAbsorbed;
|
||||
if (m_debugMessages)
|
||||
{
|
||||
@@ -187,9 +187,8 @@ namespace UnitTestUtils
|
||||
return true; // I handled this, do not forward it
|
||||
}
|
||||
|
||||
bool OnPrintf(const char* /*window*/, const char* message) override
|
||||
bool OnPrintf(const char* /*window*/, const char* /*message*/) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numMessagesAbsorbed;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ protected:
|
||||
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
|
||||
auto projectPathKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::IO::FixedMaxPath enginePath;
|
||||
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
AZ::ComponentApplication::Descriptor desc;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <GemRepo/GemRepoScreen.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
@@ -47,9 +48,14 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_gemCatalogScreen = new GemCatalogScreen(this);
|
||||
m_stack->addWidget(m_gemCatalogScreen);
|
||||
|
||||
m_gemRepoScreen = new GemRepoScreen(this);
|
||||
m_stack->addWidget(m_gemRepoScreen);
|
||||
|
||||
vLayout->addWidget(m_stack);
|
||||
|
||||
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
|
||||
connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh);
|
||||
|
||||
// When there are multiple project templates present, we re-gather the gems when changing the selected the project template.
|
||||
connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex)
|
||||
@@ -89,6 +95,9 @@ namespace O3DE::ProjectManager
|
||||
buttons->setObjectName("footer");
|
||||
vLayout->addWidget(buttons);
|
||||
|
||||
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
|
||||
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
|
||||
|
||||
@@ -100,8 +109,6 @@ namespace O3DE::ProjectManager
|
||||
Update();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
|
||||
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
|
||||
|
||||
setLayout(vLayout);
|
||||
}
|
||||
@@ -122,6 +129,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Gather the enabled gems from the default project template when starting the create new project workflow.
|
||||
ReinitGemCatalogForSelectedTemplate();
|
||||
|
||||
// make sure the gem repo has the latest details
|
||||
m_gemRepoScreen->Reinit();
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::HandleBackButton()
|
||||
@@ -160,12 +170,21 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
m_header->setSubTitle(tr("Configure project with Gems"));
|
||||
m_secondaryButton->setVisible(false);
|
||||
m_primaryButton->setVisible(true);
|
||||
}
|
||||
else if (m_stack->currentWidget() == m_gemRepoScreen)
|
||||
{
|
||||
m_header->setSubTitle(tr("Gem Repositories"));
|
||||
m_secondaryButton->setVisible(true);
|
||||
m_secondaryButton->setText(tr("Back"));
|
||||
m_primaryButton->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setSubTitle(tr("Enter Project Details"));
|
||||
m_secondaryButton->setVisible(true);
|
||||
m_secondaryButton->setText(tr("Configure Gems"));
|
||||
m_primaryButton->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +194,10 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
HandleSecondaryButton();
|
||||
}
|
||||
else if (screen == ProjectManagerScreen::GemRepos)
|
||||
{
|
||||
NextScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit ChangeScreenRequest(screen);
|
||||
@@ -230,6 +253,12 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (m_newProjectSettingsScreen->Validate())
|
||||
{
|
||||
if (!m_gemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
|
||||
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace O3DE::ProjectManager
|
||||
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
|
||||
QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
|
||||
|
||||
class CreateProjectCtrl
|
||||
: public ScreenWidget
|
||||
@@ -64,6 +65,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr;
|
||||
GemCatalogScreen* m_gemCatalogScreen = nullptr;
|
||||
GemRepoScreen* m_gemRepoScreen = nullptr;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -82,12 +82,13 @@ namespace O3DE::ProjectManager
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
QString gemName = m_gemNames.front();
|
||||
m_gemNames.erase(m_gemNames.begin());
|
||||
emit Done(succeeded);
|
||||
emit Done(gemName, succeeded);
|
||||
|
||||
if (!m_gemNames.empty())
|
||||
{
|
||||
emit StartGemDownload(m_gemNames[0]);
|
||||
emit StartGemDownload(m_gemNames.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
signals:
|
||||
void StartGemDownload(const QString& gemName);
|
||||
void Done(bool success = true);
|
||||
void Done(const QString& gemName, bool success = true);
|
||||
void GemDownloadProgress(int percentage);
|
||||
|
||||
private:
|
||||
|
||||
@@ -39,6 +39,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_tabWidget->addTab(m_engineSettingsScreen, tr("General"));
|
||||
m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories"));
|
||||
|
||||
// when tab changes, notify the current screen so it can refresh
|
||||
connect(m_tabWidget, &QTabWidget::currentChanged, this, &EngineScreenCtrl::TabChanged);
|
||||
|
||||
topBarHLayout->addWidget(m_tabWidget);
|
||||
|
||||
vLayout->addWidget(topBarFrameWidget);
|
||||
@@ -46,6 +50,11 @@ namespace O3DE::ProjectManager
|
||||
setLayout(vLayout);
|
||||
}
|
||||
|
||||
void EngineScreenCtrl::TabChanged([[maybe_unused]] int index)
|
||||
{
|
||||
NotifyCurrentScreen();
|
||||
}
|
||||
|
||||
ProjectManagerScreen EngineScreenCtrl::GetScreenEnum()
|
||||
{
|
||||
return ProjectManagerScreen::UpdateProject;
|
||||
@@ -71,6 +80,15 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void EngineScreenCtrl::NotifyCurrentScreen()
|
||||
{
|
||||
ScreenWidget* screen = reinterpret_cast<ScreenWidget*>(m_tabWidget->currentWidget());
|
||||
if (screen)
|
||||
{
|
||||
screen->NotifyCurrentScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen)
|
||||
{
|
||||
if (screen == m_engineSettingsScreen->GetScreenEnum())
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace O3DE::ProjectManager
|
||||
bool IsTab() override;
|
||||
bool ContainsScreen(ProjectManagerScreen screen) override;
|
||||
void GoToScreen(ProjectManagerScreen screen) override;
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
public slots:
|
||||
void TabChanged(int index);
|
||||
|
||||
QTabWidget* m_tabWidget = nullptr;
|
||||
EngineSettingsScreen* m_engineSettingsScreen = nullptr;
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else
|
||||
{
|
||||
tagContainer->Update(ConvertFromModelIndices(tagIndices));
|
||||
tagContainer->Update(GetTagsFromModelIndices(tagIndices));
|
||||
label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle));
|
||||
widget->show();
|
||||
}
|
||||
@@ -234,17 +234,23 @@ namespace O3DE::ProjectManager
|
||||
for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber)
|
||||
{
|
||||
QHBoxLayout* nameProgressLayout = new QHBoxLayout();
|
||||
TagWidget* newTag = new TagWidget(downloadQueue[downloadingGemNumber]);
|
||||
|
||||
const QString& gemName = downloadQueue[downloadingGemNumber];
|
||||
TagWidget* newTag = new TagWidget({gemName, gemName});
|
||||
nameProgressLayout->addWidget(newTag);
|
||||
|
||||
QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued"));
|
||||
nameProgressLayout->addWidget(progress);
|
||||
|
||||
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
nameProgressLayout->addSpacerItem(spacer);
|
||||
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(downloadQueue[downloadingGemNumber]));
|
||||
|
||||
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(gemName));
|
||||
cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||
connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated);
|
||||
nameProgressLayout->addWidget(cancelText);
|
||||
downloadingItemLayout->addLayout(nameProgressLayout);
|
||||
|
||||
QProgressBar* downloadProgessBar = new QProgressBar();
|
||||
downloadingItemLayout->addWidget(downloadProgessBar);
|
||||
downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0);
|
||||
@@ -255,7 +261,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
};
|
||||
|
||||
auto downloadEnded = [=](bool /*success*/)
|
||||
auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/)
|
||||
{
|
||||
update(0); // update the list to remove the gem that has finished
|
||||
};
|
||||
@@ -265,15 +271,15 @@ namespace O3DE::ProjectManager
|
||||
update(0);
|
||||
}
|
||||
|
||||
QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector<QModelIndex>& gems) const
|
||||
QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const
|
||||
{
|
||||
QStringList gemNames;
|
||||
gemNames.reserve(gems.size());
|
||||
QVector<Tag> tags;
|
||||
tags.reserve(gems.size());
|
||||
for (const QModelIndex& modelIndex : gems)
|
||||
{
|
||||
gemNames.push_back(GemModel::GetDisplayName(modelIndex));
|
||||
tags.push_back({ GemModel::GetDisplayName(modelIndex), GemModel::GetName(modelIndex) });
|
||||
}
|
||||
return gemNames;
|
||||
return tags;
|
||||
}
|
||||
|
||||
CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent)
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace O3DE::ProjectManager
|
||||
CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
QStringList ConvertFromModelIndices(const QVector<QModelIndex>& gems) const;
|
||||
QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const;
|
||||
|
||||
using GetTagIndicesCallback = AZStd::function<QVector<QModelIndex>()>;
|
||||
void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QHash>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -30,7 +31,10 @@ namespace O3DE::ProjectManager
|
||||
: ScreenWidget(parent)
|
||||
{
|
||||
m_gemModel = new GemModel(this);
|
||||
m_proxModel = new GemSortFilterProxyModel(m_gemModel, this);
|
||||
m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this);
|
||||
|
||||
// default to sort by gem name
|
||||
m_proxyModel->setSortRole(GemModel::RoleName);
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setMargin(0);
|
||||
@@ -39,21 +43,24 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_downloadController = new DownloadController();
|
||||
|
||||
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController);
|
||||
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxyModel, m_downloadController);
|
||||
vLayout->addWidget(m_headerWidget);
|
||||
|
||||
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
|
||||
connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult);
|
||||
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setMargin(0);
|
||||
vLayout->addLayout(hLayout);
|
||||
|
||||
m_gemListView = new GemListView(m_proxModel, m_proxModel->GetSelectionModel(), this);
|
||||
m_gemListView = new GemListView(m_proxyModel, m_proxyModel->GetSelectionModel(), this);
|
||||
m_gemInspector = new GemInspector(m_gemModel, this);
|
||||
m_gemInspector->setFixedWidth(240);
|
||||
|
||||
connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); });
|
||||
|
||||
QWidget* filterWidget = new QWidget(this);
|
||||
filterWidget->setFixedWidth(240);
|
||||
m_filterWidgetLayout = new QVBoxLayout();
|
||||
@@ -61,7 +68,7 @@ namespace O3DE::ProjectManager
|
||||
m_filterWidgetLayout->setSpacing(0);
|
||||
filterWidget->setLayout(m_filterWidgetLayout);
|
||||
|
||||
GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxModel);
|
||||
GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxyModel);
|
||||
|
||||
QVBoxLayout* middleVLayout = new QVBoxLayout();
|
||||
middleVLayout->setMargin(0);
|
||||
@@ -80,19 +87,23 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
|
||||
{
|
||||
m_projectPath = projectPath;
|
||||
m_gemModel->Clear();
|
||||
m_gemsToRegisterWithProject.clear();
|
||||
FillModel(projectPath);
|
||||
|
||||
m_proxyModel->ResetFilters();
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
|
||||
if (m_filterWidget)
|
||||
{
|
||||
m_filterWidget->hide();
|
||||
m_filterWidget->deleteLater();
|
||||
m_filterWidget->ResetAllFilters();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_filterWidget = new GemFilterWidget(m_proxyModel);
|
||||
m_filterWidgetLayout->addWidget(m_filterWidget);
|
||||
}
|
||||
|
||||
m_proxModel->ResetFilters();
|
||||
m_filterWidget = new GemFilterWidget(m_proxModel);
|
||||
m_filterWidgetLayout->addWidget(m_filterWidget);
|
||||
|
||||
m_headerWidget->ReinitForProject();
|
||||
|
||||
@@ -140,11 +151,80 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
|
||||
m_gemModel->UpdateGemDependencies();
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::Refresh()
|
||||
{
|
||||
QHash<QString, GemInfo> gemInfoHash;
|
||||
|
||||
// create a hash with the gem name as key
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath);
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
const QVector<GemInfo>& gemInfos = allGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : gemInfos)
|
||||
{
|
||||
gemInfoHash.insert(gemInfo.m_name, gemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add all the gem repos into the hash
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
|
||||
if (allRepoGemInfosResult.IsSuccess())
|
||||
{
|
||||
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allRepoGemInfos)
|
||||
{
|
||||
if (!gemInfoHash.contains(gemInfo.m_name))
|
||||
{
|
||||
gemInfoHash.insert(gemInfo.m_name, gemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove gems from the model that no longer exist in the hash and are not project dependencies
|
||||
int i = 0;
|
||||
while (i < m_gemModel->rowCount())
|
||||
{
|
||||
QModelIndex index = m_gemModel->index(i,0);
|
||||
QString gemName = m_gemModel->GetName(index);
|
||||
const bool gemFound = gemInfoHash.contains(gemName);
|
||||
if (!gemFound && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index))
|
||||
{
|
||||
m_gemModel->removeRow(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gemFound && (m_gemModel->IsAdded(index) || m_gemModel->IsAddedDependency(index)))
|
||||
{
|
||||
const QString error = tr("Gem %1 was removed or unregistered, but is still used by the project.").arg(gemName);
|
||||
AZ_Warning("Project Manager", false, error.toUtf8().constData());
|
||||
QMessageBox::warning(this, tr("Gem not found"), error.toUtf8().constData());
|
||||
}
|
||||
|
||||
gemInfoHash.remove(gemName);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// add all gems remaining in the hash that were not removed
|
||||
for(auto iter = gemInfoHash.begin(); iter != gemInfoHash.end(); ++iter)
|
||||
{
|
||||
m_gemModel->AddGem(iter.value());
|
||||
}
|
||||
|
||||
m_gemModel->UpdateGemDependencies();
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
|
||||
// temporary, until we can refresh filter counts
|
||||
m_proxyModel->ResetFilters();
|
||||
m_filterWidget->ResetAllFilters();
|
||||
}
|
||||
|
||||
void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies)
|
||||
{
|
||||
if (m_notificationsEnabled)
|
||||
@@ -192,6 +272,20 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::SelectGem(const QString& gemName)
|
||||
{
|
||||
QModelIndex modelIndex = m_gemModel->FindIndexByNameString(gemName);
|
||||
if (!m_proxyModel->filterAcceptsRow(modelIndex.row(), QModelIndex()))
|
||||
{
|
||||
m_proxyModel->ResetFilters();
|
||||
m_filterWidget->ResetAllFilters();
|
||||
}
|
||||
|
||||
QModelIndex proxyIndex = m_proxyModel->mapFromSource(modelIndex);
|
||||
m_proxyModel->GetSelectionModel()->select(proxyIndex, QItemSelectionModel::ClearAndSelect);
|
||||
m_gemListView->scrollTo(proxyIndex);
|
||||
}
|
||||
|
||||
void GemCatalogScreen::hideEvent(QHideEvent* event)
|
||||
{
|
||||
ScreenWidget::hideEvent(event);
|
||||
@@ -218,20 +312,22 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::FillModel(const QString& projectPath)
|
||||
{
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
|
||||
m_projectPath = projectPath;
|
||||
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
// Add all available gems to the model.
|
||||
const QVector<GemInfo> allGemInfos = allGemInfosResult.GetValue();
|
||||
const QVector<GemInfo>& allGemInfos = allGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allGemInfos)
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
}
|
||||
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
|
||||
if (allRepoGemInfosResult.IsSuccess())
|
||||
{
|
||||
const QVector<GemInfo> allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allRepoGemInfos)
|
||||
{
|
||||
// do not add gems that have already been downloaded
|
||||
@@ -250,10 +346,10 @@ namespace O3DE::ProjectManager
|
||||
m_notificationsEnabled = false;
|
||||
|
||||
// Gather enabled gems for the given project.
|
||||
auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
|
||||
const auto& enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
|
||||
if (enabledGemNamesResult.IsSuccess())
|
||||
{
|
||||
const QVector<AZStd::string> enabledGemNames = enabledGemNamesResult.GetValue();
|
||||
const QVector<AZStd::string>& enabledGemNames = enabledGemNamesResult.GetValue();
|
||||
for (const AZStd::string& enabledGemName : enabledGemNames)
|
||||
{
|
||||
const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str());
|
||||
@@ -313,12 +409,24 @@ namespace O3DE::ProjectManager
|
||||
|
||||
for (const QModelIndex& modelIndex : toBeAdded)
|
||||
{
|
||||
const QString gemPath = GemModel::GetPath(modelIndex);
|
||||
const QString& gemPath = GemModel::GetPath(modelIndex);
|
||||
|
||||
// make sure any remote gems we added were downloaded successfully
|
||||
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
nullptr, "Cannot add gem that isn't downloaded",
|
||||
tr("Cannot add gem %1 to project because it isn't downloaded yet or failed to download.")
|
||||
.arg(GemModel::GetDisplayName(modelIndex)));
|
||||
|
||||
return EnableDisableGemsResult::Failed;
|
||||
}
|
||||
|
||||
const AZ::Outcome<void, AZStd::string> result = pythonBindings->AddGemToProject(gemPath, projectPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
QMessageBox::critical(nullptr, "Failed to add gem to project",
|
||||
tr("Cannot add gem %1 to project.<br><br>Error:<br>%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return EnableDisableGemsResult::Failed;
|
||||
}
|
||||
@@ -336,8 +444,8 @@ namespace O3DE::ProjectManager
|
||||
const AZ::Outcome<void, AZStd::string> result = pythonBindings->RemoveGemFromProject(gemPath, projectPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
QMessageBox::critical(nullptr, "Failed to remove gem from project",
|
||||
tr("Cannot remove gem %1 from project.<br><br>Error:<br>%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return EnableDisableGemsResult::Failed;
|
||||
}
|
||||
@@ -348,23 +456,35 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::HandleOpenGemRepo()
|
||||
{
|
||||
QVector<QModelIndex> gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true);
|
||||
QVector<QModelIndex> gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::GemRepos);
|
||||
}
|
||||
|
||||
if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty())
|
||||
void GemCatalogScreen::OnGemDownloadResult(const QString& gemName, bool succeeded)
|
||||
{
|
||||
if (succeeded)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
nullptr, "Pending Changes",
|
||||
"There are some unsaved changes to the gem selection,<br> they will be lost if you change screens.<br> Are you sure?",
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult != QMessageBox::Yes)
|
||||
// refresh the information for downloaded gems
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath);
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
return;
|
||||
// we should find the gem name now in all gem infos
|
||||
for (const GemInfo& gemInfo : allGemInfosResult.GetValue())
|
||||
{
|
||||
if (gemInfo.m_name == gemName)
|
||||
{
|
||||
QModelIndex index = m_gemModel->FindIndexByNameString(gemName);
|
||||
if (index.isValid())
|
||||
{
|
||||
m_gemModel->setData(index, GemInfo::Downloaded, GemModel::RoleDownloadStatus);
|
||||
m_gemModel->setData(index, gemInfo.m_path, GemModel::RolePath);
|
||||
m_gemModel->setData(index, gemInfo.m_path, GemModel::RoleDirectoryLink);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::GemRepos);
|
||||
}
|
||||
|
||||
ProjectManagerScreen GemCatalogScreen::GetScreenEnum()
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace O3DE::ProjectManager
|
||||
public slots:
|
||||
void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
|
||||
void OnAddGemClicked();
|
||||
void SelectGem(const QString& gemName);
|
||||
void OnGemDownloadResult(const QString& gemName, bool succeeded = true);
|
||||
void Refresh();
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
@@ -68,11 +71,12 @@ namespace O3DE::ProjectManager
|
||||
GemInspector* m_gemInspector = nullptr;
|
||||
GemModel* m_gemModel = nullptr;
|
||||
GemCatalogHeaderWidget* m_headerWidget = nullptr;
|
||||
GemSortFilterProxyModel* m_proxModel = nullptr;
|
||||
GemSortFilterProxyModel* m_proxyModel = nullptr;
|
||||
QVBoxLayout* m_filterWidgetLayout = nullptr;
|
||||
GemFilterWidget* m_filterWidget = nullptr;
|
||||
DownloadController* m_downloadController = nullptr;
|
||||
bool m_notificationsEnabled = true;
|
||||
QSet<QString> m_gemsToRegisterWithProject;
|
||||
QString m_projectPath = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -213,11 +213,99 @@ namespace O3DE::ProjectManager
|
||||
m_filterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
filterSection->setLayout(m_filterLayout);
|
||||
|
||||
ResetAllFilters();
|
||||
}
|
||||
|
||||
void GemFilterWidget::ResetAllFilters()
|
||||
{
|
||||
ResetGemStatusFilter();
|
||||
AddGemOriginFilter();
|
||||
AddTypeFilter();
|
||||
AddPlatformFilter();
|
||||
AddFeatureFilter();
|
||||
ResetGemOriginFilter();
|
||||
ResetTypeFilter();
|
||||
ResetPlatformFilter();
|
||||
ResetFeatureFilter();
|
||||
}
|
||||
|
||||
void GemFilterWidget::ResetFilterWidget(
|
||||
FilterCategoryWidget*& filterPtr,
|
||||
const QString& filterName,
|
||||
const QVector<QString>& elementNames,
|
||||
const QVector<int>& elementCounts,
|
||||
int defaultShowCount)
|
||||
{
|
||||
bool wasCollapsed = false;
|
||||
if (filterPtr)
|
||||
{
|
||||
wasCollapsed = filterPtr->IsCollapsed();
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget(
|
||||
filterName, elementNames, elementCounts, /*showAllLessButton=*/defaultShowCount != 4, /*collapsed*/ wasCollapsed,
|
||||
/*defaultShowCount=*/defaultShowCount);
|
||||
if (filterPtr)
|
||||
{
|
||||
m_filterLayout->replaceWidget(filterPtr, filterWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
}
|
||||
|
||||
filterPtr->deleteLater();
|
||||
filterPtr = filterWidget;
|
||||
}
|
||||
|
||||
template<typename filterType, typename filterFlagsType>
|
||||
void GemFilterWidget::ResetSimpleOrFilter(
|
||||
FilterCategoryWidget*& filterPtr,
|
||||
const QString& filterName,
|
||||
int numFilterElements,
|
||||
bool (*filterMatcher)(GemModel*, filterType, int),
|
||||
QString (*typeStringGetter)(filterType),
|
||||
filterFlagsType (GemSortFilterProxyModel::*filterFlagsGetter)() const,
|
||||
void (GemSortFilterProxyModel::*filterFlagsSetter)(const filterFlagsType&))
|
||||
{
|
||||
QVector<QString> elementNames;
|
||||
QVector<int> elementCounts;
|
||||
const int numGems = m_gemModel->rowCount();
|
||||
for (int filterIndex = 0; filterIndex < numFilterElements; ++filterIndex)
|
||||
{
|
||||
const filterType gemFilterToBeCounted = static_cast<filterType>(1 << filterIndex);
|
||||
|
||||
int gemFilterCount = 0;
|
||||
for (int gemIndex = 0; gemIndex < numGems; ++gemIndex)
|
||||
{
|
||||
// If filter matches increment filter count
|
||||
gemFilterCount += filterMatcher(m_gemModel, gemFilterToBeCounted, gemIndex);
|
||||
}
|
||||
elementNames.push_back(typeStringGetter(gemFilterToBeCounted));
|
||||
elementCounts.push_back(gemFilterCount);
|
||||
}
|
||||
|
||||
// Replace existing filter and delete old one
|
||||
ResetFilterWidget(filterPtr, filterName, elementNames, elementCounts);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterPtr->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
const filterType gemFilter = static_cast<filterType>(1 << i);
|
||||
QAbstractButton* button = buttons[i];
|
||||
|
||||
connect(
|
||||
button, &QAbstractButton::toggled, this,
|
||||
[=](bool checked)
|
||||
{
|
||||
filterFlagsType gemFilters = (m_filterProxyModel->*filterFlagsGetter)();
|
||||
if (checked)
|
||||
{
|
||||
gemFilters |= gemFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
gemFilters &= ~gemFilter;
|
||||
}
|
||||
(m_filterProxyModel->*filterFlagsSetter)(gemFilters);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GemFilterWidget::ResetGemStatusFilter()
|
||||
@@ -241,25 +329,7 @@ namespace O3DE::ProjectManager
|
||||
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
|
||||
elementCounts.push_back(totalGems - enabledGemTotal);
|
||||
|
||||
bool wasCollapsed = false;
|
||||
if (m_statusFilter)
|
||||
{
|
||||
wasCollapsed = m_statusFilter->IsCollapsed();
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget =
|
||||
new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed);
|
||||
if (m_statusFilter)
|
||||
{
|
||||
m_filterLayout->replaceWidget(m_statusFilter, filterWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
}
|
||||
|
||||
m_statusFilter->deleteLater();
|
||||
m_statusFilter = filterWidget;
|
||||
ResetFilterWidget(m_statusFilter, "Status", elementNames, elementCounts);
|
||||
|
||||
const QList<QAbstractButton*> buttons = m_statusFilter->GetButtonGroup()->buttons();
|
||||
|
||||
@@ -317,157 +387,42 @@ namespace O3DE::ProjectManager
|
||||
connect(activeButton, &QAbstractButton::toggled, this, updateGemActive);
|
||||
}
|
||||
|
||||
void GemFilterWidget::AddGemOriginFilter()
|
||||
void GemFilterWidget::ResetGemOriginFilter()
|
||||
{
|
||||
QVector<QString> elementNames;
|
||||
QVector<int> elementCounts;
|
||||
const int numGems = m_gemModel->rowCount();
|
||||
for (int originIndex = 0; originIndex < GemInfo::NumGemOrigins; ++originIndex)
|
||||
{
|
||||
const GemInfo::GemOrigin gemOriginToBeCounted = static_cast<GemInfo::GemOrigin>(1 << originIndex);
|
||||
|
||||
int gemOriginCount = 0;
|
||||
for (int gemIndex = 0; gemIndex < numGems; ++gemIndex)
|
||||
ResetSimpleOrFilter<GemInfo::GemOrigin, GemInfo::GemOrigins>
|
||||
(
|
||||
m_originFilter, "Provider", GemInfo::NumGemOrigins,
|
||||
[](GemModel* gemModel, GemInfo::GemOrigin origin, int gemIndex)
|
||||
{
|
||||
const GemInfo::GemOrigin gemOrigin = m_gemModel->GetGemOrigin(m_gemModel->index(gemIndex, 0));
|
||||
|
||||
// Is the gem of the given origin?
|
||||
if (gemOriginToBeCounted == gemOrigin)
|
||||
{
|
||||
gemOriginCount++;
|
||||
}
|
||||
}
|
||||
|
||||
elementNames.push_back(GemInfo::GetGemOriginString(gemOriginToBeCounted));
|
||||
elementCounts.push_back(gemOriginCount);
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
const GemInfo::GemOrigin gemOrigin = static_cast<GemInfo::GemOrigin>(1 << i);
|
||||
QAbstractButton* button = buttons[i];
|
||||
|
||||
connect(button, &QAbstractButton::toggled, this, [=](bool checked)
|
||||
{
|
||||
GemInfo::GemOrigins gemOrigins = m_filterProxyModel->GetGemOrigins();
|
||||
if (checked)
|
||||
{
|
||||
gemOrigins |= gemOrigin;
|
||||
}
|
||||
else
|
||||
{
|
||||
gemOrigins &= ~gemOrigin;
|
||||
}
|
||||
m_filterProxyModel->SetGemOrigins(gemOrigins);
|
||||
});
|
||||
}
|
||||
return origin == gemModel->GetGemOrigin(gemModel->index(gemIndex, 0));
|
||||
},
|
||||
&GemInfo::GetGemOriginString, &GemSortFilterProxyModel::GetGemOrigins, &GemSortFilterProxyModel::SetGemOrigins
|
||||
);
|
||||
}
|
||||
|
||||
void GemFilterWidget::AddTypeFilter()
|
||||
void GemFilterWidget::ResetTypeFilter()
|
||||
{
|
||||
QVector<QString> elementNames;
|
||||
QVector<int> elementCounts;
|
||||
const int numGems = m_gemModel->rowCount();
|
||||
for (int typeIndex = 0; typeIndex < GemInfo::NumTypes; ++typeIndex)
|
||||
{
|
||||
const GemInfo::Type type = static_cast<GemInfo::Type>(1 << typeIndex);
|
||||
|
||||
int typeGemCount = 0;
|
||||
for (int gemIndex = 0; gemIndex < numGems; ++gemIndex)
|
||||
ResetSimpleOrFilter<GemInfo::Type, GemInfo::Types>(
|
||||
m_typeFilter, "Type", GemInfo::NumTypes,
|
||||
[](GemModel* gemModel, GemInfo::Type type, int gemIndex)
|
||||
{
|
||||
const GemInfo::Types types = m_gemModel->GetTypes(m_gemModel->index(gemIndex, 0));
|
||||
|
||||
// Is type (Asset, Code, Tool) part of the gem?
|
||||
if (types & type)
|
||||
{
|
||||
typeGemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
elementNames.push_back(GemInfo::GetTypeString(type));
|
||||
elementCounts.push_back(typeGemCount);
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
const GemInfo::Type type = static_cast<GemInfo::Type>(1 << i);
|
||||
QAbstractButton* button = buttons[i];
|
||||
|
||||
connect(button, &QAbstractButton::toggled, this, [=](bool checked)
|
||||
{
|
||||
GemInfo::Types types = m_filterProxyModel->GetTypes();
|
||||
if (checked)
|
||||
{
|
||||
types |= type;
|
||||
}
|
||||
else
|
||||
{
|
||||
types &= ~type;
|
||||
}
|
||||
m_filterProxyModel->SetTypes(types);
|
||||
});
|
||||
}
|
||||
return static_cast<bool>(type & gemModel->GetTypes(gemModel->index(gemIndex, 0)));
|
||||
},
|
||||
&GemInfo::GetTypeString, &GemSortFilterProxyModel::GetTypes, &GemSortFilterProxyModel::SetTypes);
|
||||
}
|
||||
|
||||
void GemFilterWidget::AddPlatformFilter()
|
||||
void GemFilterWidget::ResetPlatformFilter()
|
||||
{
|
||||
QVector<QString> elementNames;
|
||||
QVector<int> elementCounts;
|
||||
const int numGems = m_gemModel->rowCount();
|
||||
for (int platformIndex = 0; platformIndex < GemInfo::NumPlatforms; ++platformIndex)
|
||||
{
|
||||
const GemInfo::Platform platform = static_cast<GemInfo::Platform>(1 << platformIndex);
|
||||
|
||||
int platformGemCount = 0;
|
||||
for (int gemIndex = 0; gemIndex < numGems; ++gemIndex)
|
||||
ResetSimpleOrFilter<GemInfo::Platform, GemInfo::Platforms>(
|
||||
m_platformFilter, "Supported Platforms", GemInfo::NumPlatforms,
|
||||
[](GemModel* gemModel, GemInfo::Platform platform, int gemIndex)
|
||||
{
|
||||
const GemInfo::Platforms platforms = m_gemModel->GetPlatforms(m_gemModel->index(gemIndex, 0));
|
||||
|
||||
// Is platform supported?
|
||||
if (platforms & platform)
|
||||
{
|
||||
platformGemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
elementNames.push_back(GemInfo::GetPlatformString(platform));
|
||||
elementCounts.push_back(platformGemCount);
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
const GemInfo::Platform platform = static_cast<GemInfo::Platform>(1 << i);
|
||||
QAbstractButton* button = buttons[i];
|
||||
|
||||
connect(button, &QAbstractButton::toggled, this, [=](bool checked)
|
||||
{
|
||||
GemInfo::Platforms platforms = m_filterProxyModel->GetPlatforms();
|
||||
if (checked)
|
||||
{
|
||||
platforms |= platform;
|
||||
}
|
||||
else
|
||||
{
|
||||
platforms &= ~platform;
|
||||
}
|
||||
m_filterProxyModel->SetPlatforms(platforms);
|
||||
});
|
||||
}
|
||||
return static_cast<bool>(platform & gemModel->GetPlatforms(gemModel->index(gemIndex, 0)));
|
||||
},
|
||||
&GemInfo::GetPlatformString, &GemSortFilterProxyModel::GetPlatforms, &GemSortFilterProxyModel::SetPlatforms);
|
||||
}
|
||||
|
||||
void GemFilterWidget::AddFeatureFilter()
|
||||
void GemFilterWidget::ResetFeatureFilter()
|
||||
{
|
||||
// Alphabetically sorted, unique features and their number of occurrences in the gem database.
|
||||
QMap<QString, int> uniqueFeatureCounts;
|
||||
@@ -497,11 +452,15 @@ namespace O3DE::ProjectManager
|
||||
elementCounts.push_back(iterator.value());
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts,
|
||||
/*showAllLessButton=*/true, false, /*defaultShowCount=*/5);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
ResetFilterWidget(m_featureFilter, "Features", elementNames, elementCounts, /*defaultShowCount=*/5);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (QMetaObject::Connection& connection : m_featureTagConnections)
|
||||
{
|
||||
disconnect(connection);
|
||||
}
|
||||
m_featureTagConnections.clear();
|
||||
|
||||
const QList<QAbstractButton*> buttons = m_featureFilter->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
const QString& feature = elementNames[i];
|
||||
@@ -523,13 +482,13 @@ namespace O3DE::ProjectManager
|
||||
});
|
||||
|
||||
// Sync the UI state with the proxy model filtering.
|
||||
connect(m_filterProxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=]
|
||||
m_featureTagConnections.push_back(connect(m_filterProxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=]
|
||||
{
|
||||
const QSet<QString>& filteredFeatureTags = m_filterProxyModel->GetFeatures();
|
||||
const bool isChecked = filteredFeatureTags.contains(button->text());
|
||||
QSignalBlocker signalsBlocker(button);
|
||||
button->setChecked(isChecked);
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -66,17 +66,41 @@ namespace O3DE::ProjectManager
|
||||
~GemFilterWidget() = default;
|
||||
|
||||
public slots:
|
||||
void ResetAllFilters();
|
||||
void ResetGemStatusFilter();
|
||||
|
||||
private:
|
||||
void AddGemOriginFilter();
|
||||
void AddTypeFilter();
|
||||
void AddPlatformFilter();
|
||||
void AddFeatureFilter();
|
||||
void ResetGemOriginFilter();
|
||||
void ResetTypeFilter();
|
||||
void ResetPlatformFilter();
|
||||
void ResetFeatureFilter();
|
||||
|
||||
void ResetFilterWidget(
|
||||
FilterCategoryWidget*& filterPtr,
|
||||
const QString& filterName,
|
||||
const QVector<QString>& elementNames,
|
||||
const QVector<int>& elementCounts,
|
||||
int defaultShowCount = 4);
|
||||
|
||||
template<typename filterType, typename filterFlagsType>
|
||||
void ResetSimpleOrFilter(
|
||||
FilterCategoryWidget*& filterPtr,
|
||||
const QString& filterName,
|
||||
int numFilterElements,
|
||||
bool (*filterMatcher)(GemModel*, filterType, int),
|
||||
QString (*typeStringGetter)(filterType),
|
||||
filterFlagsType (GemSortFilterProxyModel::*filterFlagsGetter)() const,
|
||||
void (GemSortFilterProxyModel::*filterFlagsSetter)(const filterFlagsType&));
|
||||
|
||||
QVBoxLayout* m_filterLayout = nullptr;
|
||||
GemModel* m_gemModel = nullptr;
|
||||
GemSortFilterProxyModel* m_filterProxyModel = nullptr;
|
||||
FilterCategoryWidget* m_statusFilter = nullptr;
|
||||
FilterCategoryWidget* m_originFilter = nullptr;
|
||||
FilterCategoryWidget* m_typeFilter = nullptr;
|
||||
FilterCategoryWidget* m_platformFilter = nullptr;
|
||||
FilterCategoryWidget* m_featureFilter = nullptr;
|
||||
|
||||
QVector<QMetaObject::Connection> m_featureTagConnections;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -81,6 +81,8 @@ namespace O3DE::ProjectManager
|
||||
DownloadStatus m_downloadStatus = UnknownDownloadStatus;
|
||||
QStringList m_features;
|
||||
QString m_requirement;
|
||||
QString m_licenseText;
|
||||
QString m_licenseLink;
|
||||
QString m_directoryLink;
|
||||
QString m_documentationLink;
|
||||
QString m_version = "Unknown Version";
|
||||
|
||||
@@ -52,6 +52,22 @@ namespace O3DE::ProjectManager
|
||||
Update(selectedIndices[0]);
|
||||
}
|
||||
|
||||
void SetLabelElidedText(QLabel* label, QString text)
|
||||
{
|
||||
QFontMetrics nameFontMetrics(label->font());
|
||||
int labelWidth = label->width();
|
||||
|
||||
// Don't elide if the widgets are sized too small (sometimes occurs when loading gem catalog)
|
||||
if (labelWidth > 100)
|
||||
{
|
||||
label->setText(nameFontMetrics.elidedText(text, Qt::ElideRight, labelWidth));
|
||||
}
|
||||
else
|
||||
{
|
||||
label->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void GemInspector::Update(const QModelIndex& modelIndex)
|
||||
{
|
||||
if (!modelIndex.isValid())
|
||||
@@ -59,38 +75,53 @@ namespace O3DE::ProjectManager
|
||||
m_mainWidget->hide();
|
||||
}
|
||||
|
||||
m_nameLabel->setText(m_model->GetDisplayName(modelIndex));
|
||||
m_creatorLabel->setText(m_model->GetCreator(modelIndex));
|
||||
SetLabelElidedText(m_nameLabel, m_model->GetDisplayName(modelIndex));
|
||||
SetLabelElidedText(m_creatorLabel, m_model->GetCreator(modelIndex));
|
||||
|
||||
m_summaryLabel->setText(m_model->GetSummary(modelIndex));
|
||||
m_summaryLabel->adjustSize();
|
||||
|
||||
m_licenseLinkLabel->setText(m_model->GetLicenseText(modelIndex));
|
||||
m_licenseLinkLabel->SetUrl(m_model->GetLicenseLink(modelIndex));
|
||||
|
||||
m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex));
|
||||
m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex));
|
||||
|
||||
if (m_model->HasRequirement(modelIndex))
|
||||
{
|
||||
m_reqirementsIconLabel->show();
|
||||
m_reqirementsTitleLabel->show();
|
||||
m_reqirementsTextLabel->show();
|
||||
m_requirementsIconLabel->show();
|
||||
m_requirementsTitleLabel->show();
|
||||
m_requirementsTextLabel->show();
|
||||
m_requirementsMainSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
m_reqirementsTitleLabel->setText("Requirement");
|
||||
m_reqirementsTextLabel->setText(m_model->GetRequirement(modelIndex));
|
||||
m_requirementsTitleLabel->setText(tr("Requirement"));
|
||||
m_requirementsTextLabel->setText(m_model->GetRequirement(modelIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reqirementsIconLabel->hide();
|
||||
m_reqirementsTitleLabel->hide();
|
||||
m_reqirementsTextLabel->hide();
|
||||
m_requirementsIconLabel->hide();
|
||||
m_requirementsTitleLabel->hide();
|
||||
m_requirementsTextLabel->hide();
|
||||
m_requirementsMainSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
// Depending gems
|
||||
m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex));
|
||||
const QVector<Tag>& dependingGemTags = m_model->GetDependingGemTags(modelIndex);
|
||||
if (!dependingGemTags.isEmpty())
|
||||
{
|
||||
m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGemTags);
|
||||
m_dependingGems->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dependingGems->hide();
|
||||
}
|
||||
|
||||
// Additional information
|
||||
m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
|
||||
m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
|
||||
m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex)));
|
||||
m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
|
||||
m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
|
||||
const int binarySize = m_model->GetBinarySizeInKB(modelIndex);
|
||||
m_binarySizeLabel->setText(tr("Binary Size: %1").arg(binarySize ? tr("%1 KB").arg(binarySize) : tr("Unknown")));
|
||||
|
||||
m_mainWidget->adjustSize();
|
||||
m_mainWidget->show();
|
||||
@@ -108,35 +139,51 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
// Gem name, creator and summary
|
||||
m_nameLabel = CreateStyledLabel(m_mainLayout, 18, s_headerColor);
|
||||
m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_headerColor);
|
||||
m_creatorLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor);
|
||||
m_mainLayout->addSpacing(5);
|
||||
|
||||
// TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size.
|
||||
// This results into squeezed elements in the layout in case the text is a little longer than a sentence.
|
||||
m_summaryLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_summaryLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor);
|
||||
m_mainLayout->addWidget(m_summaryLabel);
|
||||
m_summaryLabel->setWordWrap(true);
|
||||
m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
m_summaryLabel->setOpenExternalLinks(true);
|
||||
m_mainLayout->addSpacing(5);
|
||||
|
||||
// License
|
||||
{
|
||||
QHBoxLayout* licenseHLayout = new QHBoxLayout();
|
||||
licenseHLayout->setMargin(0);
|
||||
licenseHLayout->setAlignment(Qt::AlignLeft);
|
||||
m_mainLayout->addLayout(licenseHLayout);
|
||||
|
||||
QLabel* licenseLabel = CreateStyledLabel(licenseHLayout, s_baseFontSize, s_headerColor);
|
||||
licenseLabel->setText(tr("License: "));
|
||||
|
||||
m_licenseLinkLabel = new LinkLabel("", QUrl(), s_baseFontSize);
|
||||
licenseHLayout->addWidget(m_licenseLinkLabel);
|
||||
|
||||
licenseHLayout->addStretch();
|
||||
|
||||
m_mainLayout->addSpacing(5);
|
||||
}
|
||||
|
||||
// Directory and documentation links
|
||||
{
|
||||
QHBoxLayout* linksHLayout = new QHBoxLayout();
|
||||
linksHLayout->setMargin(0);
|
||||
m_mainLayout->addLayout(linksHLayout);
|
||||
|
||||
QSpacerItem* spacerLeft = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
linksHLayout->addSpacerItem(spacerLeft);
|
||||
linksHLayout->addStretch();
|
||||
|
||||
m_directoryLinkLabel = new LinkLabel("View in Directory");
|
||||
m_directoryLinkLabel = new LinkLabel(tr("View in Directory"));
|
||||
linksHLayout->addWidget(m_directoryLinkLabel);
|
||||
linksHLayout->addWidget(new QLabel("|"));
|
||||
m_documentationLinkLabel = new LinkLabel("Read Documentation");
|
||||
m_documentationLinkLabel = new LinkLabel(tr("Read Documentation"));
|
||||
linksHLayout->addWidget(m_documentationLinkLabel);
|
||||
|
||||
QSpacerItem* spacerRight = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
linksHLayout->addSpacerItem(spacerRight);
|
||||
linksHLayout->addStretch();
|
||||
|
||||
m_mainLayout->addSpacing(8);
|
||||
}
|
||||
@@ -144,46 +191,48 @@ namespace O3DE::ProjectManager
|
||||
// Separating line
|
||||
QFrame* hLine = new QFrame();
|
||||
hLine->setFrameShape(QFrame::HLine);
|
||||
hLine->setStyleSheet("color: #666666;");
|
||||
hLine->setObjectName("horizontalSeparatingLine");
|
||||
m_mainLayout->addWidget(hLine);
|
||||
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
// Requirements
|
||||
m_reqirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor);
|
||||
m_requirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor);
|
||||
|
||||
QHBoxLayout* requrementsLayout = new QHBoxLayout();
|
||||
requrementsLayout->setAlignment(Qt::AlignTop);
|
||||
requrementsLayout->setMargin(0);
|
||||
requrementsLayout->setSpacing(0);
|
||||
QHBoxLayout* requirementsLayout = new QHBoxLayout();
|
||||
requirementsLayout->setAlignment(Qt::AlignTop);
|
||||
requirementsLayout->setMargin(0);
|
||||
requirementsLayout->setSpacing(0);
|
||||
|
||||
m_reqirementsIconLabel = new QLabel();
|
||||
m_reqirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24));
|
||||
requrementsLayout->addWidget(m_reqirementsIconLabel);
|
||||
m_requirementsIconLabel = new QLabel();
|
||||
m_requirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24));
|
||||
requirementsLayout->addWidget(m_requirementsIconLabel);
|
||||
|
||||
m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor);
|
||||
m_reqirementsTextLabel->setWordWrap(true);
|
||||
m_reqirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
m_reqirementsTextLabel->setOpenExternalLinks(true);
|
||||
m_requirementsTextLabel = GemInspector::CreateStyledLabel(requirementsLayout, 10, s_textColor);
|
||||
m_requirementsTextLabel->setWordWrap(true);
|
||||
m_requirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
m_requirementsTextLabel->setOpenExternalLinks(true);
|
||||
|
||||
QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
requrementsLayout->addSpacerItem(reqirementsSpacer);
|
||||
QSpacerItem* requirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding);
|
||||
requirementsLayout->addSpacerItem(requirementsSpacer);
|
||||
|
||||
m_mainLayout->addLayout(requrementsLayout);
|
||||
m_mainLayout->addLayout(requirementsLayout);
|
||||
|
||||
m_mainLayout->addSpacing(20);
|
||||
m_requirementsMainSpacer = new QSpacerItem(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
m_mainLayout->addSpacerItem(m_requirementsMainSpacer);
|
||||
|
||||
// Depending gems
|
||||
m_dependingGems = new GemsSubWidget();
|
||||
connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); });
|
||||
m_mainLayout->addWidget(m_dependingGems);
|
||||
m_mainLayout->addSpacing(20);
|
||||
|
||||
// Additional information
|
||||
QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor);
|
||||
additionalInfoLabel->setText("Additional Information");
|
||||
additionalInfoLabel->setText(tr("Additional Information"));
|
||||
|
||||
m_versionLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_versionLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor);
|
||||
m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor);
|
||||
m_binarySizeLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QScrollArea>
|
||||
#include <QWidget>
|
||||
#include <QSpacerItem>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
|
||||
@@ -36,10 +36,16 @@ namespace O3DE::ProjectManager
|
||||
void Update(const QModelIndex& modelIndex);
|
||||
static QLabel* CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString);
|
||||
|
||||
// Fonts
|
||||
inline constexpr static int s_baseFontSize = 12;
|
||||
|
||||
// Colors
|
||||
inline constexpr static const char* s_headerColor = "#FFFFFF";
|
||||
inline constexpr static const char* s_textColor = "#DDDDDD";
|
||||
|
||||
signals:
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
private slots:
|
||||
void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||
|
||||
@@ -54,13 +60,15 @@ namespace O3DE::ProjectManager
|
||||
QLabel* m_nameLabel = nullptr;
|
||||
QLabel* m_creatorLabel = nullptr;
|
||||
QLabel* m_summaryLabel = nullptr;
|
||||
LinkLabel* m_licenseLinkLabel = nullptr;
|
||||
LinkLabel* m_directoryLinkLabel = nullptr;
|
||||
LinkLabel* m_documentationLinkLabel = nullptr;
|
||||
|
||||
// Requirements
|
||||
QLabel* m_reqirementsTitleLabel = nullptr;
|
||||
QLabel* m_reqirementsIconLabel = nullptr;
|
||||
QLabel* m_reqirementsTextLabel = nullptr;
|
||||
QLabel* m_requirementsTitleLabel = nullptr;
|
||||
QLabel* m_requirementsIconLabel = nullptr;
|
||||
QLabel* m_requirementsTextLabel = nullptr;
|
||||
QSpacerItem* m_requirementsMainSpacer = nullptr;
|
||||
|
||||
// Depending and conflicting gems
|
||||
GemsSubWidget* m_dependingGems = nullptr;
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace O3DE::ProjectManager
|
||||
: QStandardItemModel(parent)
|
||||
{
|
||||
m_selectionModel = new QItemSelectionModel(this, parent);
|
||||
connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GemModel::OnRowsAboutToBeRemoved);
|
||||
}
|
||||
|
||||
QItemSelectionModel* GemModel::GetSelectionModel() const
|
||||
@@ -58,6 +59,8 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_path, RolePath);
|
||||
item->setData(gemInfo.m_requirement, RoleRequirement);
|
||||
item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus);
|
||||
item->setData(gemInfo.m_licenseText, RoleLicenseText);
|
||||
item->setData(gemInfo.m_licenseLink, RoleLicenseLink);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
@@ -174,18 +177,6 @@ namespace O3DE::ProjectManager
|
||||
return {};
|
||||
}
|
||||
|
||||
void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames)
|
||||
{
|
||||
for (QString& name : inOutGemNames)
|
||||
{
|
||||
QModelIndex modelIndex = FindIndexByNameString(name);
|
||||
if (modelIndex.isValid())
|
||||
{
|
||||
name = GetDisplayName(modelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleDependingGems).toStringList();
|
||||
@@ -205,16 +196,23 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex)
|
||||
QVector<Tag> GemModel::GetDependingGemTags(const QModelIndex& modelIndex)
|
||||
{
|
||||
QStringList result = GetDependingGems(modelIndex);
|
||||
if (result.isEmpty())
|
||||
QVector<Tag> tags;
|
||||
|
||||
QStringList dependingGemNames = GetDependingGems(modelIndex);
|
||||
tags.reserve(dependingGemNames.size());
|
||||
|
||||
for (QString& gemName : dependingGemNames)
|
||||
{
|
||||
return {};
|
||||
const QModelIndex& dependingIndex = FindIndexByNameString(gemName);
|
||||
if (dependingIndex.isValid())
|
||||
{
|
||||
tags.push_back({ GetDisplayName(dependingIndex), GetName(dependingIndex) });
|
||||
}
|
||||
}
|
||||
|
||||
FindGemDisplayNamesByNameStrings(result);
|
||||
return result;
|
||||
return tags;
|
||||
}
|
||||
|
||||
QString GemModel::GetVersion(const QModelIndex& modelIndex)
|
||||
@@ -247,6 +245,16 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleRequirement).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetLicenseText(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleLicenseText).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetLicenseLink(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleLicenseLink).toString();
|
||||
}
|
||||
|
||||
GemModel* GemModel::GetSourceModel(QAbstractItemModel* model)
|
||||
{
|
||||
GemSortFilterProxyModel* proxyModel = qobject_cast<GemSortFilterProxyModel*>(model);
|
||||
@@ -359,6 +367,16 @@ namespace O3DE::ProjectManager
|
||||
gemModel->emit gemStatusChanged(gemName, numChangedDependencies);
|
||||
}
|
||||
|
||||
void GemModel::OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
for (int i = first; i <= last; ++i)
|
||||
{
|
||||
QModelIndex modelIndex = index(i, 0, parent);
|
||||
const QString& gemName = GetName(modelIndex);
|
||||
m_nameToIndexMap.remove(gemName);
|
||||
}
|
||||
}
|
||||
|
||||
void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
|
||||
{
|
||||
model.setData(modelIndex, isAdded, RoleIsAddedDependency);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <GemCatalog/GemInfo.h>
|
||||
#include <TagWidget.h>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
@@ -26,12 +27,39 @@ namespace O3DE::ProjectManager
|
||||
explicit GemModel(QObject* parent = nullptr);
|
||||
QItemSelectionModel* GetSelectionModel() const;
|
||||
|
||||
enum UserRole
|
||||
{
|
||||
RoleName = Qt::UserRole,
|
||||
RoleDisplayName,
|
||||
RoleCreator,
|
||||
RoleGemOrigin,
|
||||
RolePlatforms,
|
||||
RoleSummary,
|
||||
RoleWasPreviouslyAdded,
|
||||
RoleWasPreviouslyAddedDependency,
|
||||
RoleIsAdded,
|
||||
RoleIsAddedDependency,
|
||||
RoleDirectoryLink,
|
||||
RoleDocLink,
|
||||
RoleDependingGems,
|
||||
RoleVersion,
|
||||
RoleLastUpdated,
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath,
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus,
|
||||
RoleLicenseText,
|
||||
RoleLicenseLink
|
||||
};
|
||||
|
||||
void AddGem(const GemInfo& gemInfo);
|
||||
void Clear();
|
||||
void UpdateGemDependencies();
|
||||
|
||||
QModelIndex FindIndexByNameString(const QString& nameString) const;
|
||||
QStringList GetDependingGemNames(const QModelIndex& modelIndex);
|
||||
QVector<Tag> GetDependingGemTags(const QModelIndex& modelIndex);
|
||||
bool HasDependentGems(const QModelIndex& modelIndex) const;
|
||||
|
||||
static QString GetName(const QModelIndex& modelIndex);
|
||||
@@ -50,6 +78,8 @@ namespace O3DE::ProjectManager
|
||||
static QStringList GetFeatures(const QModelIndex& modelIndex);
|
||||
static QString GetPath(const QModelIndex& modelIndex);
|
||||
static QString GetRequirement(const QModelIndex& modelIndex);
|
||||
static QString GetLicenseText(const QModelIndex& modelIndex);
|
||||
static QString GetLicenseLink(const QModelIndex& modelIndex);
|
||||
static GemModel* GetSourceModel(QAbstractItemModel* model);
|
||||
static const GemModel* GetSourceModel(const QAbstractItemModel* model);
|
||||
|
||||
@@ -80,36 +110,13 @@ namespace O3DE::ProjectManager
|
||||
signals:
|
||||
void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
|
||||
|
||||
protected slots:
|
||||
void OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
|
||||
|
||||
private:
|
||||
void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames);
|
||||
void GetAllDependingGems(const QModelIndex& modelIndex, QSet<QModelIndex>& inOutGems);
|
||||
QStringList GetDependingGems(const QModelIndex& modelIndex);
|
||||
|
||||
enum UserRole
|
||||
{
|
||||
RoleName = Qt::UserRole,
|
||||
RoleDisplayName,
|
||||
RoleCreator,
|
||||
RoleGemOrigin,
|
||||
RolePlatforms,
|
||||
RoleSummary,
|
||||
RoleWasPreviouslyAdded,
|
||||
RoleWasPreviouslyAddedDependency,
|
||||
RoleIsAdded,
|
||||
RoleIsAddedDependency,
|
||||
RoleDirectoryLink,
|
||||
RoleDocLink,
|
||||
RoleDependingGems,
|
||||
RoleVersion,
|
||||
RoleLastUpdated,
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath,
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus
|
||||
};
|
||||
|
||||
QHash<QString, QModelIndex> m_nameToIndexMap;
|
||||
QItemSelectionModel* m_selectionModel = nullptr;
|
||||
QHash<QString, QSet<QModelIndex>> m_gemDependencyMap;
|
||||
|
||||
@@ -207,6 +207,8 @@ namespace O3DE::ProjectManager
|
||||
void GemSortFilterProxyModel::ResetFilters()
|
||||
{
|
||||
m_searchString.clear();
|
||||
m_gemSelectedFilter = GemSelected::NoFilter;
|
||||
m_gemActiveFilter = GemActive::NoFilter;
|
||||
m_gemOriginFilter = {};
|
||||
m_platformFilter = {};
|
||||
m_typeFilter = {};
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
// Included Gems
|
||||
m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex));
|
||||
m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemTags(modelIndex));
|
||||
|
||||
m_mainWidget->adjustSize();
|
||||
m_mainWidget->show();
|
||||
@@ -99,7 +99,7 @@ namespace O3DE::ProjectManager
|
||||
m_nameLabel->setObjectName("gemRepoInspectorNameLabel");
|
||||
m_mainLayout->addWidget(m_nameLabel);
|
||||
|
||||
m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this);
|
||||
m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(), 12, this);
|
||||
m_mainLayout->addWidget(m_repoLinkLabel);
|
||||
m_mainLayout->addSpacing(5);
|
||||
|
||||
|
||||
@@ -103,17 +103,17 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleIncludedGems).toStringList();
|
||||
}
|
||||
|
||||
QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex)
|
||||
QVector<Tag> GemRepoModel::GetIncludedGemTags(const QModelIndex& modelIndex)
|
||||
{
|
||||
QStringList gemNames;
|
||||
QVector<GemInfo> gemInfos = GetIncludedGemInfos(modelIndex);
|
||||
|
||||
QVector<Tag> tags;
|
||||
const QVector<GemInfo>& gemInfos = GetIncludedGemInfos(modelIndex);
|
||||
tags.reserve(gemInfos.size());
|
||||
for (const GemInfo& gemInfo : gemInfos)
|
||||
{
|
||||
gemNames.append(gemInfo.m_displayName);
|
||||
tags.append({ gemInfo.m_displayName, gemInfo.m_name });
|
||||
}
|
||||
|
||||
return gemNames;
|
||||
return tags;
|
||||
}
|
||||
|
||||
QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace O3DE::ProjectManager
|
||||
static QString GetPath(const QModelIndex& modelIndex);
|
||||
|
||||
static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex);
|
||||
static QStringList GetIncludedGemNames(const QModelIndex& modelIndex);
|
||||
static QVector<Tag> GetIncludedGemTags(const QModelIndex& modelIndex);
|
||||
static QVector<GemInfo> GetIncludedGemInfos(const QModelIndex& modelIndex);
|
||||
|
||||
static bool IsEnabled(const QModelIndex& modelIndex);
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace O3DE::ProjectManager
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void GemRepoScreen::NotifyCurrentScreen()
|
||||
{
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void GemRepoScreen::Reinit()
|
||||
{
|
||||
m_gemRepoModel->clear();
|
||||
@@ -91,6 +96,7 @@ namespace O3DE::ProjectManager
|
||||
if (addGemRepoResult)
|
||||
{
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -116,6 +122,7 @@ namespace O3DE::ProjectManager
|
||||
if (removeGemRepoResult)
|
||||
{
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -130,6 +137,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos();
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
|
||||
if (!refreshResult)
|
||||
{
|
||||
@@ -146,6 +154,7 @@ namespace O3DE::ProjectManager
|
||||
if (refreshResult.IsSuccess())
|
||||
{
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace O3DE::ProjectManager
|
||||
class GemRepoScreen
|
||||
: public ScreenWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GemRepoScreen(QWidget* parent = nullptr);
|
||||
~GemRepoScreen() = default;
|
||||
@@ -37,12 +38,18 @@ namespace O3DE::ProjectManager
|
||||
|
||||
GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; }
|
||||
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
signals:
|
||||
void OnRefresh();
|
||||
|
||||
public slots:
|
||||
void HandleAddRepoButton();
|
||||
void HandleRemoveRepoButton(const QModelIndex& modelIndex);
|
||||
void HandleRefreshAllButton();
|
||||
void HandleRefreshRepoButton(const QModelIndex& modelIndex);
|
||||
|
||||
|
||||
private:
|
||||
void FillModel();
|
||||
QFrame* CreateNoReposContent();
|
||||
|
||||
@@ -33,13 +33,14 @@ namespace O3DE::ProjectManager
|
||||
m_layout->addWidget(m_textLabel);
|
||||
|
||||
m_tagWidget = new TagContainerWidget();
|
||||
connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); });
|
||||
m_layout->addWidget(m_tagWidget);
|
||||
}
|
||||
|
||||
void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames)
|
||||
void GemsSubWidget::Update(const QString& title, const QString& text, const QVector<Tag>& tags)
|
||||
{
|
||||
m_titleLabel->setText(title);
|
||||
m_textLabel->setText(text);
|
||||
m_tagWidget->Update(gemNames);
|
||||
m_tagWidget->Update(tags);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -22,9 +22,14 @@ namespace O3DE::ProjectManager
|
||||
class GemsSubWidget
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
GemsSubWidget(QWidget* parent = nullptr);
|
||||
void Update(const QString& title, const QString& text, const QStringList& gemNames);
|
||||
void Update(const QString& title, const QString& text, const QVector<Tag>& tags);
|
||||
|
||||
signals:
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
private:
|
||||
QLabel* m_titleLabel = nullptr;
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace Platform
|
||||
|
||||
#define Py_To_String(obj) pybind11::str(obj).cast<std::string>().c_str()
|
||||
#define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string
|
||||
#define Py_To_Int(obj) obj.cast<int>()
|
||||
#define Py_To_Int_Optional(dict, key, default_int) dict.contains(key) ? Py_To_Int(dict[key]) : default_int
|
||||
#define QString_To_Py_String(value) pybind11::str(value.toStdString())
|
||||
#define QString_To_Py_Path(value) m_pathlib.attr("Path")(value.toStdString())
|
||||
|
||||
@@ -705,10 +707,14 @@ namespace O3DE::ProjectManager
|
||||
// optional
|
||||
gemInfo.m_displayName = Py_To_String_Optional(data, "display_name", gemInfo.m_name);
|
||||
gemInfo.m_summary = Py_To_String_Optional(data, "summary", "");
|
||||
gemInfo.m_version = "";
|
||||
gemInfo.m_version = Py_To_String_Optional(data, "version", gemInfo.m_version);
|
||||
gemInfo.m_lastUpdatedDate = Py_To_String_Optional(data, "last_updated", gemInfo.m_lastUpdatedDate);
|
||||
gemInfo.m_binarySizeInKB = Py_To_Int_Optional(data, "binary_size", gemInfo.m_binarySizeInKB);
|
||||
gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", "");
|
||||
gemInfo.m_creator = Py_To_String_Optional(data, "origin", "");
|
||||
gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", "");
|
||||
gemInfo.m_licenseText = Py_To_String_Optional(data, "license", "Unspecified License");
|
||||
gemInfo.m_licenseLink = Py_To_String_Optional(data, "license_url", "");
|
||||
|
||||
if (gemInfo.m_creator.contains("Open 3D Engine"))
|
||||
{
|
||||
|
||||
@@ -12,12 +12,18 @@
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
TagWidget::TagWidget(const QString& text, QWidget* parent)
|
||||
: QLabel(text, parent)
|
||||
TagWidget::TagWidget(const Tag& tag, QWidget* parent)
|
||||
: QLabel(tag.text, parent)
|
||||
, m_tag(tag)
|
||||
{
|
||||
setObjectName("TagWidget");
|
||||
}
|
||||
|
||||
void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
emit TagClicked(m_tag);
|
||||
}
|
||||
|
||||
TagContainerWidget::TagContainerWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
@@ -34,18 +40,34 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void TagContainerWidget::Update(const QStringList& tags)
|
||||
{
|
||||
FlowLayout* flowLayout = static_cast<FlowLayout*>(layout());
|
||||
Clear();
|
||||
|
||||
// remove old tags
|
||||
foreach (const QString& tag, tags)
|
||||
{
|
||||
TagWidget* tagWidget = new TagWidget({tag, tag});
|
||||
connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); });
|
||||
layout()->addWidget(tagWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void TagContainerWidget::Update(const QVector<Tag>& tags)
|
||||
{
|
||||
Clear();
|
||||
|
||||
foreach (const Tag& tag, tags)
|
||||
{
|
||||
TagWidget* tagWidget = new TagWidget(tag);
|
||||
connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); });
|
||||
layout()->addWidget(tagWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void TagContainerWidget::Clear()
|
||||
{
|
||||
QLayoutItem* layoutItem = nullptr;
|
||||
while ((layoutItem = layout()->takeAt(0)) != nullptr)
|
||||
{
|
||||
layoutItem->widget()->deleteLater();
|
||||
}
|
||||
|
||||
foreach (const QString& tag, tags)
|
||||
{
|
||||
flowLayout->addWidget(new TagWidget(tag));
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -10,12 +10,19 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QLabel>
|
||||
#include <QStringList>
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
struct Tag
|
||||
{
|
||||
QString text;
|
||||
QString id;
|
||||
};
|
||||
|
||||
// Single tag
|
||||
class TagWidget
|
||||
: public QLabel
|
||||
@@ -23,8 +30,17 @@ namespace O3DE::ProjectManager
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit TagWidget(const QString& text, QWidget* parent = nullptr);
|
||||
explicit TagWidget(const Tag& id, QWidget* parent = nullptr);
|
||||
~TagWidget() = default;
|
||||
|
||||
signals:
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
Tag m_tag;
|
||||
};
|
||||
|
||||
// Widget containing multiple tags, automatically wrapping based on the size
|
||||
@@ -37,6 +53,13 @@ namespace O3DE::ProjectManager
|
||||
explicit TagContainerWidget(QWidget* parent = nullptr);
|
||||
~TagContainerWidget() = default;
|
||||
|
||||
void Update(const QVector<Tag>& tags);
|
||||
void Update(const QStringList& tags);
|
||||
|
||||
signals:
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <GemRepo/GemRepoScreen.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
@@ -39,10 +40,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_updateSettingsScreen = new UpdateProjectSettingsScreen();
|
||||
m_gemCatalogScreen = new GemCatalogScreen();
|
||||
m_gemRepoScreen = new GemRepoScreen(this);
|
||||
|
||||
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, [this](ProjectManagerScreen screen){
|
||||
emit ChangeScreenRequest(screen);
|
||||
});
|
||||
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest);
|
||||
connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh);
|
||||
|
||||
m_stack = new QStackedWidget(this);
|
||||
m_stack->setObjectName("body");
|
||||
@@ -69,6 +70,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_stack->addWidget(topBarFrameWidget);
|
||||
m_stack->addWidget(m_gemCatalogScreen);
|
||||
m_stack->addWidget(m_gemRepoScreen);
|
||||
|
||||
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
|
||||
backNextButtons->setObjectName("footer");
|
||||
@@ -100,6 +102,22 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Gather the available gems that will be shown in the gem catalog.
|
||||
m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path);
|
||||
|
||||
// make sure the gem repo has the latest repo details
|
||||
m_gemRepoScreen->Reinit();
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen)
|
||||
{
|
||||
if (screen == ProjectManagerScreen::GemRepos)
|
||||
{
|
||||
m_stack->setCurrentWidget(m_gemRepoScreen);
|
||||
Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit ChangeScreenRequest(screen);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::HandleGemsButton()
|
||||
@@ -145,6 +163,7 @@ namespace O3DE::ProjectManager
|
||||
QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
|
||||
const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
if (result == GemCatalogScreen::EnableDisableGemsResult::Failed)
|
||||
@@ -181,18 +200,26 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void UpdateProjectCtrl::Update()
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
if (m_stack->currentIndex() == ScreenOrder::GemRepos)
|
||||
{
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_header->setSubTitle(QString(tr("Gem Repositories")));
|
||||
m_nextButton->setVisible(false);
|
||||
}
|
||||
else if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
{
|
||||
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_header->setSubTitle(QString(tr("Configure Gems")));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
m_nextButton->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setTitle("");
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
m_nextButton->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ namespace O3DE::ProjectManager
|
||||
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
|
||||
QT_FORWARD_DECLARE_CLASS(UpdateProjectSettingsScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
|
||||
|
||||
class UpdateProjectCtrl : public ScreenWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateProjectCtrl(QWidget* parent = nullptr);
|
||||
~UpdateProjectCtrl() = default;
|
||||
@@ -37,6 +39,7 @@ namespace O3DE::ProjectManager
|
||||
void HandleBackButton();
|
||||
void HandleNextButton();
|
||||
void HandleGemsButton();
|
||||
void OnChangeScreenRequest(ProjectManagerScreen screen);
|
||||
void UpdateCurrentProject(const QString& projectPath);
|
||||
|
||||
private:
|
||||
@@ -47,13 +50,15 @@ namespace O3DE::ProjectManager
|
||||
enum ScreenOrder
|
||||
{
|
||||
Settings,
|
||||
Gems
|
||||
Gems,
|
||||
GemRepos
|
||||
};
|
||||
|
||||
ScreenHeader* m_header = nullptr;
|
||||
QStackedWidget* m_stack = nullptr;
|
||||
UpdateProjectSettingsScreen* m_updateSettingsScreen = nullptr;
|
||||
GemCatalogScreen* m_gemCatalogScreen = nullptr;
|
||||
GemRepoScreen* m_gemRepoScreen = nullptr;
|
||||
|
||||
QPushButton* m_backButton = nullptr;
|
||||
QPushButton* m_nextButton = nullptr;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag)
|
||||
: QWidget()
|
||||
: QWidget(nullptr, Qt::Tool | Qt::WindowStaysOnTopHint)
|
||||
, m_traceTag(traceTag)
|
||||
, ui(new Ui::ProcessingOverlayWidget())
|
||||
, m_overlay(overlay)
|
||||
|
||||
Reference in New Issue
Block a user