Merge remote-tracking branch 'upstream/stabilization/2110' into Atom/santorac/MaterialEditorHandlesMissingTextures
This commit is contained in:
@@ -23,7 +23,7 @@ def create_jobs(request):
|
||||
jobDescriptorList = []
|
||||
for platformInfo in request.enabledPlatforms:
|
||||
jobDesc = azlmbr.asset.builder.JobDescriptor()
|
||||
jobDesc.jobKey = jobKeyName
|
||||
jobDesc.jobKey = f'{jobKeyName}-{platformInfo.identifier}'
|
||||
jobDesc.set_platform_identifier(platformInfo.identifier)
|
||||
jobDescriptorList.append(jobDesc)
|
||||
|
||||
@@ -38,7 +38,7 @@ def on_create_jobs(args):
|
||||
return create_jobs(request)
|
||||
except:
|
||||
log_exception_traceback()
|
||||
# returing back a default CreateJobsResponse() records an asset error
|
||||
# returning back a default CreateJobsResponse() records an asset error
|
||||
return azlmbr.asset.builder.CreateJobsResponse()
|
||||
|
||||
def process_file(request):
|
||||
@@ -58,6 +58,7 @@ def process_file(request):
|
||||
fileOutput = open(tempFilename, "w")
|
||||
fileOutput.write('{}')
|
||||
fileOutput.close()
|
||||
print(f'Wrote mock asset file: {tempFilename}')
|
||||
|
||||
# generate a product asset file entry
|
||||
subId = binascii.crc32(mockFilename.encode())
|
||||
|
||||
@@ -371,10 +371,8 @@ void CCryEditApp::RegisterActionHandlers()
|
||||
ON_COMMAND(ID_EDIT_FETCH, OnEditFetch)
|
||||
ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture)
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame)
|
||||
MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() {
|
||||
ed_previewGameInFullscreen_once = true;
|
||||
OnViewSwitchToGame();
|
||||
});
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME_VIEWPORT, OnViewSwitchToGame)
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME_FULLSCREEN, OnViewSwitchToGameFullScreen)
|
||||
ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject)
|
||||
ON_COMMAND(ID_RENAME_OBJ, OnRenameObj)
|
||||
ON_COMMAND(ID_UNDO, OnUndo)
|
||||
@@ -2575,6 +2573,12 @@ void CCryEditApp::OnViewSwitchToGame()
|
||||
GetIEditor()->SetInGameMode(inGame);
|
||||
}
|
||||
|
||||
void CCryEditApp::OnViewSwitchToGameFullScreen()
|
||||
{
|
||||
ed_previewGameInFullscreen_once = true;
|
||||
OnViewSwitchToGame();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CCryEditApp::OnExportSelectedObjects()
|
||||
{
|
||||
|
||||
@@ -212,6 +212,7 @@ public:
|
||||
void OnEditFetch();
|
||||
void OnFileExportToGameNoSurfaceTexture();
|
||||
void OnViewSwitchToGame();
|
||||
void OnViewSwitchToGameFullScreen();
|
||||
void OnViewDeploy();
|
||||
void DeleteSelectedEntities(bool includeDescendants);
|
||||
void OnMoveObject();
|
||||
|
||||
@@ -939,27 +939,27 @@ void MainWindow::InitActions()
|
||||
.Connect(&QAction::triggered, this, &MainWindow::OnRefreshAudioSystem);
|
||||
|
||||
// Game actions
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME, tr("Play &Game"))
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME, tr("Play Game"))
|
||||
.SetIcon(QIcon(":/stylesheet/img/UI20/toolbar/Play.svg"))
|
||||
.SetToolTip(tr("Play Game"))
|
||||
.SetStatusTip(tr("Activate the game input mode"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_VIEWPORT, tr("Play Game"))
|
||||
.SetShortcut(tr("Ctrl+G"))
|
||||
.SetToolTip(tr("Play Game (Ctrl+G)"))
|
||||
.SetStatusTip(tr("Activate the game input mode"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)"))
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play Game (Maximized)"))
|
||||
.SetShortcut(tr("Ctrl+Shift+G"))
|
||||
.SetStatusTip(tr("Activate the game input mode (maximized)"))
|
||||
.SetIcon(Style::icon("Play"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true);
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls"))
|
||||
.SetText(tr("Play Controls"));
|
||||
am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate"))
|
||||
.SetIcon(QIcon(":/stylesheet/img/UI20/toolbar/Simulate_Physics.svg"))
|
||||
.SetShortcut(tr("Ctrl+P"))
|
||||
.SetToolTip(tr("Simulate (Ctrl+P)"))
|
||||
.SetCheckable(true)
|
||||
.SetStatusTip(tr("Enable processing of Physics and AI."))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true)
|
||||
@@ -1266,7 +1266,9 @@ void MainWindow::OnGameModeChanged(bool inGameMode)
|
||||
// block signals on the switch to game actions before setting the checked state, as
|
||||
// setting the checked state triggers the action, which will re-enter this function
|
||||
// and result in an infinite loop
|
||||
AZStd::vector<QAction*> actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) };
|
||||
AZStd::vector<QAction*> actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_VIEWPORT),
|
||||
m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN),
|
||||
m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME)};
|
||||
for (auto action : actions)
|
||||
{
|
||||
action->blockSignals(true);
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
#define ID_FILE_EXPORTTOGAMENOSURFACETEXTURE 33473
|
||||
#define ID_VIEW_SWITCHTOGAME 33477
|
||||
#define ID_VIEW_SWITCHTOGAME_FULLSCREEN 33478
|
||||
#define ID_VIEW_SWITCHTOGAME_VIEWPORT 33479
|
||||
#define ID_MOVE_OBJECT 33481
|
||||
#define ID_RENAME_OBJ 33483
|
||||
#define ID_FETCH 33496
|
||||
|
||||
@@ -590,6 +590,16 @@ AmazonToolbar ToolbarManager::GetObjectToolbar() const
|
||||
return t;
|
||||
}
|
||||
|
||||
QMenu* ToolbarManager::CreatePlayButtonMenu() const
|
||||
{
|
||||
QMenu* playButtonMenu = new QMenu("Play Game");
|
||||
|
||||
playButtonMenu->addAction(m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_VIEWPORT));
|
||||
playButtonMenu->addAction(m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN));
|
||||
|
||||
return playButtonMenu;
|
||||
}
|
||||
|
||||
AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const
|
||||
{
|
||||
AmazonToolbar t = AmazonToolbar("PlayConsole", QObject::tr("Play Controls"));
|
||||
@@ -598,8 +608,17 @@ AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const
|
||||
t.AddAction(ID_TOOLBAR_WIDGET_SPACER_RIGHT, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_VIEW_SWITCHTOGAME, TOOLBARS_WITH_PLAY_GAME);
|
||||
t.AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, TOOLBARS_WITH_PLAY_GAME);
|
||||
|
||||
QAction* playAction = m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME);
|
||||
QToolButton* playButton = new QToolButton(t.Toolbar());
|
||||
|
||||
QMenu* menu = CreatePlayButtonMenu();
|
||||
menu->setParent(t.Toolbar());
|
||||
playAction->setMenu(menu);
|
||||
|
||||
playButton->setDefaultAction(playAction);
|
||||
t.AddWidget(playButton, ID_VIEW_SWITCHTOGAME, ORIGINAL_TOOLBAR_VERSION);
|
||||
|
||||
t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_SWITCH_PHYSICS, TOOLBARS_WITH_PLAY_GAME);
|
||||
return t;
|
||||
@@ -728,7 +747,14 @@ void AmazonToolbar::SetActionsOnInternalToolbar(ActionManager* actionManager)
|
||||
{
|
||||
if (actionManager->HasAction(actionId))
|
||||
{
|
||||
m_toolbar->addAction(actionManager->GetAction(actionId));
|
||||
if (actionData.widget != nullptr)
|
||||
{
|
||||
m_toolbar->addWidget(actionData.widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_toolbar->addAction(actionManager->GetAction(actionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1367,7 +1393,12 @@ void AmazonToolbar::InstantiateToolbar(QMainWindow* mainWindow, ToolbarManager*
|
||||
|
||||
void AmazonToolbar::AddAction(int actionId, int toolbarVersionAdded)
|
||||
{
|
||||
m_actions.push_back({ actionId, toolbarVersionAdded });
|
||||
AddWidget(nullptr, actionId, toolbarVersionAdded);
|
||||
}
|
||||
|
||||
void AmazonToolbar::AddWidget(QWidget* widget, int actionId, int toolbarVersionAdded)
|
||||
{
|
||||
m_actions.push_back({ actionId, toolbarVersionAdded, widget });
|
||||
}
|
||||
|
||||
void AmazonToolbar::Clear()
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
const QString& GetTranslatedName() const { return m_translatedName; }
|
||||
|
||||
void AddAction(int actionId, int toolbarVersionAdded = 0);
|
||||
void AddWidget(QWidget* widget, int actionId, int toolbarVersionAdded = 0);
|
||||
|
||||
QToolBar* Toolbar() const { return m_toolbar; }
|
||||
|
||||
@@ -117,6 +118,7 @@ private:
|
||||
{
|
||||
int actionId;
|
||||
int toolbarVersionAdded;
|
||||
QWidget* widget;
|
||||
|
||||
bool operator ==(const AmazonToolbar::ActionData& other) const
|
||||
{
|
||||
@@ -133,7 +135,9 @@ private:
|
||||
class AmazonToolBarExpanderWatcher;
|
||||
|
||||
class ToolbarManager
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ToolbarManager(ActionManager* actionManager, MainWindow* mainWindow);
|
||||
~ToolbarManager();
|
||||
@@ -178,6 +182,8 @@ private:
|
||||
void UpdateAllowedAreas(QToolBar* toolbar);
|
||||
bool IsDirty(const AmazonToolbar& toolbar) const;
|
||||
|
||||
QMenu* CreatePlayButtonMenu() const;
|
||||
|
||||
const AmazonToolbar* FindDefaultToolbar(const QString& toolbarName) const;
|
||||
AmazonToolbar* FindToolbar(const QString& toolbarName);
|
||||
|
||||
|
||||
@@ -32,7 +32,12 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
// Because the NameDictionary allocates memory using the AZ::Allocator and it is created
|
||||
// in the executable memory space, it's ownership cannot be transferred to other module memory spaces
|
||||
// Otherwise this could cause the the NameDictionary to be destroyed in static de-init
|
||||
// after the AZ::Allocators have been destroyed
|
||||
// Therefore we supply the isTransferOwnership value of false using CreateVariableEx
|
||||
s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -563,6 +563,52 @@ QProgressBar::chunk {
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
#gemCatalogUpdateGemButton,
|
||||
#gemCatalogUninstallGemButton
|
||||
{
|
||||
qproperty-flat: true;
|
||||
min-height:24px;
|
||||
max-height:24px;
|
||||
border-radius: 3px;
|
||||
text-align:center;
|
||||
font-size:12px;
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
#gemCatalogUpdateGemButton {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #888888, stop: 1.0 #555555);
|
||||
}
|
||||
#gemCatalogUpdateGemButton:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #999999, stop: 1.0 #666666);
|
||||
}
|
||||
#gemCatalogUpdateGemButton:pressed {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #555555, stop: 1.0 #777777);
|
||||
}
|
||||
|
||||
#footer > #gemCatalogUninstallGemButton,
|
||||
#gemCatalogUninstallGemButton {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #E32C27, stop: 1.0 #951D21);
|
||||
}
|
||||
#footer > #gemCatalogUninstallGemButton:hover,
|
||||
#gemCatalogUninstallGemButton:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #FD3129, stop: 1.0 #AF2221);
|
||||
}
|
||||
#footer > #gemCatalogUninstallGemButton:pressed,
|
||||
#gemCatalogUninstallGemButton:pressed {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #951D1F, stop: 1.0 #C92724);
|
||||
}
|
||||
|
||||
#gemCatalogDialogSubTitle {
|
||||
font-size:14px;
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
/************** Filter Tag widget **************/
|
||||
|
||||
#FilterTagWidgetTextLabel {
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
emit UpdateProgress(bytesDownloaded, totalBytes);
|
||||
};
|
||||
AZ::Outcome<void, AZStd::string> gemInfoResult = PythonBindingsInterface::Get()->DownloadGem(m_gemName, gemDownloadProgress);
|
||||
AZ::Outcome<void, AZStd::string> gemInfoResult =
|
||||
PythonBindingsInterface::Get()->DownloadGem(m_gemName, gemDownloadProgress, /*force*/true);
|
||||
|
||||
if (gemInfoResult.IsSuccess())
|
||||
{
|
||||
emit Done("");
|
||||
|
||||
@@ -500,6 +500,7 @@ namespace O3DE::ProjectManager
|
||||
hLayout->addSpacing(16);
|
||||
|
||||
QMenu* gemMenu = new QMenu(this);
|
||||
gemMenu->addAction( tr("Refresh"), [this]() { emit RefreshGems(); });
|
||||
gemMenu->addAction( tr("Show Gem Repos"), [this]() { emit OpenGemsRepo(); });
|
||||
gemMenu->addSeparator();
|
||||
gemMenu->addAction( tr("Add Existing Gem"), [this]() { emit AddGem(); });
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace O3DE::ProjectManager
|
||||
signals:
|
||||
void AddGem();
|
||||
void OpenGemsRepo();
|
||||
void RefreshGems();
|
||||
|
||||
private:
|
||||
AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr;
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <GemCatalog/GemRequirementDialog.h>
|
||||
#include <GemCatalog/GemDependenciesDialog.h>
|
||||
#include <GemCatalog/GemUpdateDialog.h>
|
||||
#include <GemCatalog/GemUninstallDialog.h>
|
||||
#include <DownloadController.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
@@ -47,6 +51,7 @@ namespace O3DE::ProjectManager
|
||||
vLayout->addWidget(m_headerWidget);
|
||||
|
||||
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::RefreshGems, this, &GemCatalogScreen::Refresh);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
|
||||
connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult);
|
||||
@@ -60,6 +65,8 @@ namespace O3DE::ProjectManager
|
||||
m_gemInspector->setFixedWidth(240);
|
||||
|
||||
connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); });
|
||||
connect(m_gemInspector, &GemInspector::UpdateGem, this, &GemCatalogScreen::UpdateGem);
|
||||
connect(m_gemInspector, &GemInspector::UninstallGem, this, &GemCatalogScreen::UninstallGem);
|
||||
|
||||
QWidget* filterWidget = new QWidget(this);
|
||||
filterWidget->setFixedWidth(240);
|
||||
@@ -99,7 +106,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
FillModel(projectPath);
|
||||
|
||||
m_proxyModel->ResetFilters();
|
||||
m_proxyModel->ResetFilters(false);
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
|
||||
if (m_filterWidget)
|
||||
@@ -118,9 +125,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Select the first entry after everything got correctly sized
|
||||
QTimer::singleShot(200, [=]{
|
||||
QModelIndex firstModelIndex = m_gemListView->model()->index(0,0);
|
||||
m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
|
||||
});
|
||||
QModelIndex firstModelIndex = m_gemModel->index(0, 0);
|
||||
m_gemModel->GetSelectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
|
||||
});
|
||||
}
|
||||
|
||||
void GemCatalogScreen::OnAddGemClicked()
|
||||
@@ -228,8 +235,11 @@ namespace O3DE::ProjectManager
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
|
||||
// temporary, until we can refresh filter counts
|
||||
m_proxyModel->ResetFilters();
|
||||
m_proxyModel->ResetFilters(false);
|
||||
m_filterWidget->ResetAllFilters();
|
||||
|
||||
// Reselect the same selection to proc UI updates
|
||||
m_proxyModel->GetSelectionModel()->select(m_proxyModel->GetSelectionModel()->selection(), QItemSelectionModel::Select);
|
||||
}
|
||||
|
||||
void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies)
|
||||
@@ -253,7 +263,7 @@ namespace O3DE::ProjectManager
|
||||
notification = GemModel::GetDisplayName(modelIndex);
|
||||
if (numChangedDependencies > 0)
|
||||
{
|
||||
notification += " " + tr("and") + " ";
|
||||
notification += tr(" and ");
|
||||
}
|
||||
if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
|
||||
{
|
||||
@@ -262,15 +272,15 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
if (numChangedDependencies == 1 )
|
||||
if (numChangedDependencies == 1)
|
||||
{
|
||||
notification += "1 Gem " + tr("dependency");
|
||||
notification += tr("1 Gem dependency");
|
||||
}
|
||||
else if (numChangedDependencies > 1)
|
||||
{
|
||||
notification += QString("%1 Gem ").arg(numChangedDependencies) + tr("dependencies");
|
||||
notification += tr("%1 Gem %2").arg(QString(numChangedDependencies), tr("dependencies"));
|
||||
}
|
||||
notification += " " + (added ? tr("activated") : tr("deactivated"));
|
||||
notification += (added ? tr(" activated") : tr(" deactivated"));
|
||||
|
||||
AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Custom, notification, "");
|
||||
toastConfiguration.m_customIconImage = ":/gem.svg";
|
||||
@@ -294,6 +304,88 @@ namespace O3DE::ProjectManager
|
||||
m_gemListView->scrollTo(proxyIndex);
|
||||
}
|
||||
|
||||
void GemCatalogScreen::UpdateGem(const QModelIndex& modelIndex)
|
||||
{
|
||||
const QString selectedGemName = m_gemModel->GetName(modelIndex);
|
||||
const QString selectedGemLastUpdate = m_gemModel->GetLastUpdated(modelIndex);
|
||||
const QString selectedDisplayGemName = m_gemModel->GetDisplayName(modelIndex);
|
||||
const QString selectedGemRepoUri = m_gemModel->GetRepoUri(modelIndex);
|
||||
|
||||
// Refresh gem repo
|
||||
if (!selectedGemRepoUri.isEmpty())
|
||||
{
|
||||
AZ::Outcome<void, AZStd::string> refreshResult = PythonBindingsInterface::Get()->RefreshGemRepo(selectedGemRepoUri);
|
||||
if (refreshResult.IsSuccess())
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("Operation failed"),
|
||||
tr("Failed to refresh gem repository %1<br>Error:<br>%2").arg(selectedGemRepoUri, refreshResult.GetError().c_str()));
|
||||
}
|
||||
}
|
||||
// If repo uri isn't specified warn user that repo might not be refreshed
|
||||
else
|
||||
{
|
||||
int result = QMessageBox::warning(
|
||||
this, tr("Gem Repository Unspecified"),
|
||||
tr("The repo for %1 is unspecfied. Repository cannot be automatically refreshed. "
|
||||
"Please ensure this gem's repo is refreshed before attempting to update.")
|
||||
.arg(selectedDisplayGemName),
|
||||
QMessageBox::Cancel, QMessageBox::Ok);
|
||||
|
||||
// Allow user to cancel update to manually refresh repo
|
||||
if (result != QMessageBox::Ok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is an update avaliable now that repo is refreshed
|
||||
bool updateAvaliable = PythonBindingsInterface::Get()->IsGemUpdateAvaliable(selectedGemName, selectedGemLastUpdate);
|
||||
|
||||
GemUpdateDialog* confirmUpdateDialog = new GemUpdateDialog(selectedGemName, updateAvaliable, this);
|
||||
if (confirmUpdateDialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
m_downloadController->AddGemDownload(selectedGemName);
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::UninstallGem(const QModelIndex& modelIndex)
|
||||
{
|
||||
const QString selectedDisplayGemName = m_gemModel->GetDisplayName(modelIndex);
|
||||
|
||||
GemUninstallDialog* confirmUninstallDialog = new GemUninstallDialog(selectedDisplayGemName, this);
|
||||
if (confirmUninstallDialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
const QString selectedGemPath = m_gemModel->GetPath(modelIndex);
|
||||
|
||||
// Unregister the gem
|
||||
auto unregisterResult = PythonBindingsInterface::Get()->UnregisterGem(selectedGemPath);
|
||||
if (!unregisterResult)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to unregister gem"), unregisterResult.GetError().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove gem from model
|
||||
m_gemModel->removeRow(modelIndex.row());
|
||||
|
||||
// Delete uninstalled gem directory
|
||||
if (!ProjectUtils::DeleteProjectFiles(selectedGemPath, /*force*/true))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("Failed to remove gem directory"), tr("Could not delete gem directory at:<br>%1").arg(selectedGemPath));
|
||||
}
|
||||
|
||||
// Show undownloaded remote gem again
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::hideEvent(QHideEvent* event)
|
||||
{
|
||||
ScreenWidget::hideEvent(event);
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace O3DE::ProjectManager
|
||||
void SelectGem(const QString& gemName);
|
||||
void OnGemDownloadResult(const QString& gemName, bool succeeded = true);
|
||||
void Refresh();
|
||||
void UpdateGem(const QModelIndex& modelIndex);
|
||||
void UninstallGem(const QModelIndex& modelIndex);
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
@@ -77,6 +79,6 @@ namespace O3DE::ProjectManager
|
||||
DownloadController* m_downloadController = nullptr;
|
||||
bool m_notificationsEnabled = true;
|
||||
QSet<QString> m_gemsToRegisterWithProject;
|
||||
QString m_projectPath = nullptr;
|
||||
QString m_projectPath;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace O3DE::ProjectManager
|
||||
QString m_licenseLink;
|
||||
QString m_directoryLink;
|
||||
QString m_documentationLink;
|
||||
QString m_repoUri;
|
||||
QString m_version = "Unknown Version";
|
||||
QString m_lastUpdatedDate = "Unknown Date";
|
||||
int m_binarySizeInKB = 0;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -70,6 +71,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemInspector::Update(const QModelIndex& modelIndex)
|
||||
{
|
||||
m_curModelIndex = modelIndex;
|
||||
|
||||
if (!modelIndex.isValid())
|
||||
{
|
||||
m_mainWidget->hide();
|
||||
@@ -123,6 +126,20 @@ namespace O3DE::ProjectManager
|
||||
const int binarySize = m_model->GetBinarySizeInKB(modelIndex);
|
||||
m_binarySizeLabel->setText(tr("Binary Size: %1").arg(binarySize ? tr("%1 KB").arg(binarySize) : tr("Unknown")));
|
||||
|
||||
// Update and Uninstall buttons
|
||||
if (m_model->GetGemOrigin(modelIndex) == GemInfo::Remote &&
|
||||
(m_model->GetDownloadStatus(modelIndex) == GemInfo::Downloaded ||
|
||||
m_model->GetDownloadStatus(modelIndex) == GemInfo::DownloadSuccessful))
|
||||
{
|
||||
m_updateGemButton->show();
|
||||
m_uninstallGemButton->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_updateGemButton->hide();
|
||||
m_uninstallGemButton->hide();
|
||||
}
|
||||
|
||||
m_mainWidget->adjustSize();
|
||||
m_mainWidget->show();
|
||||
}
|
||||
@@ -223,7 +240,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Depending gems
|
||||
m_dependingGems = new GemsSubWidget();
|
||||
connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); });
|
||||
connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [this](const Tag& tag){ emit TagClicked(tag); });
|
||||
m_mainLayout->addWidget(m_dependingGems);
|
||||
m_mainLayout->addSpacing(20);
|
||||
|
||||
@@ -234,5 +251,20 @@ namespace O3DE::ProjectManager
|
||||
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);
|
||||
|
||||
m_mainLayout->addSpacing(20);
|
||||
|
||||
// Update and Uninstall buttons
|
||||
m_updateGemButton = new QPushButton(tr("Update Gem"));
|
||||
m_updateGemButton->setObjectName("gemCatalogUpdateGemButton");
|
||||
m_mainLayout->addWidget(m_updateGemButton);
|
||||
connect(m_updateGemButton, &QPushButton::clicked, this , [this]{ emit UpdateGem(m_curModelIndex); });
|
||||
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
m_uninstallGemButton = new QPushButton(tr("Uninstall Gem"));
|
||||
m_uninstallGemButton->setObjectName("gemCatalogUninstallGemButton");
|
||||
m_mainLayout->addWidget(m_uninstallGemButton);
|
||||
connect(m_uninstallGemButton, &QPushButton::clicked, this , [this]{ emit UninstallGem(m_curModelIndex); });
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QScrollArea>
|
||||
#include <QSpacerItem>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
QT_FORWARD_DECLARE_CLASS(QSpacerItem)
|
||||
QT_FORWARD_DECLARE_CLASS(QPushButton)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -45,6 +46,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
signals:
|
||||
void TagClicked(const Tag& tag);
|
||||
void UpdateGem(const QModelIndex& modelIndex);
|
||||
void UninstallGem(const QModelIndex& modelIndex);
|
||||
|
||||
private slots:
|
||||
void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||
@@ -55,6 +58,7 @@ namespace O3DE::ProjectManager
|
||||
GemModel* m_model = nullptr;
|
||||
QWidget* m_mainWidget = nullptr;
|
||||
QVBoxLayout* m_mainLayout = nullptr;
|
||||
QModelIndex m_curModelIndex;
|
||||
|
||||
// General info (top) section
|
||||
QLabel* m_nameLabel = nullptr;
|
||||
@@ -77,5 +81,8 @@ namespace O3DE::ProjectManager
|
||||
QLabel* m_versionLabel = nullptr;
|
||||
QLabel* m_lastUpdatedLabel = nullptr;
|
||||
QLabel* m_binarySizeLabel = nullptr;
|
||||
|
||||
QPushButton* m_updateGemButton = nullptr;
|
||||
QPushButton* m_uninstallGemButton = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus);
|
||||
item->setData(gemInfo.m_licenseText, RoleLicenseText);
|
||||
item->setData(gemInfo.m_licenseLink, RoleLicenseLink);
|
||||
item->setData(gemInfo.m_repoUri, RoleRepoUri);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
@@ -255,6 +256,11 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleLicenseLink).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetRepoUri(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleRepoUri).toString();
|
||||
}
|
||||
|
||||
GemModel* GemModel::GetSourceModel(QAbstractItemModel* model)
|
||||
{
|
||||
GemSortFilterProxyModel* proxyModel = qobject_cast<GemSortFilterProxyModel*>(model);
|
||||
@@ -369,11 +375,30 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemModel::OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
bool selectedRowRemoved = false;
|
||||
for (int i = first; i <= last; ++i)
|
||||
{
|
||||
QModelIndex modelIndex = index(i, 0, parent);
|
||||
const QString& gemName = GetName(modelIndex);
|
||||
m_nameToIndexMap.remove(gemName);
|
||||
|
||||
if (GetSelectionModel()->isRowSelected(i))
|
||||
{
|
||||
selectedRowRemoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Select a valid row if currently selected row was removed
|
||||
if (selectedRowRemoved)
|
||||
{
|
||||
for (const QModelIndex& index : m_nameToIndexMap)
|
||||
{
|
||||
if (index.isValid())
|
||||
{
|
||||
GetSelectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@ namespace O3DE::ProjectManager
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus,
|
||||
RoleLicenseText,
|
||||
RoleLicenseLink
|
||||
RoleLicenseLink,
|
||||
RoleRepoUri
|
||||
};
|
||||
|
||||
void AddGem(const GemInfo& gemInfo);
|
||||
@@ -80,6 +81,7 @@ namespace O3DE::ProjectManager
|
||||
static QString GetRequirement(const QModelIndex& modelIndex);
|
||||
static QString GetLicenseText(const QModelIndex& modelIndex);
|
||||
static QString GetLicenseLink(const QModelIndex& modelIndex);
|
||||
static QString GetRepoUri(const QModelIndex& modelIndex);
|
||||
static GemModel* GetSourceModel(QAbstractItemModel* model);
|
||||
static const GemModel* GetSourceModel(const QAbstractItemModel* model);
|
||||
|
||||
|
||||
@@ -204,9 +204,12 @@ namespace O3DE::ProjectManager
|
||||
emit OnInvalidated();
|
||||
}
|
||||
|
||||
void GemSortFilterProxyModel::ResetFilters()
|
||||
void GemSortFilterProxyModel::ResetFilters(bool clearSearchString)
|
||||
{
|
||||
m_searchString.clear();
|
||||
if (clearSearchString)
|
||||
{
|
||||
m_searchString.clear();
|
||||
}
|
||||
m_gemSelectedFilter = GemSelected::NoFilter;
|
||||
m_gemActiveFilter = GemActive::NoFilter;
|
||||
m_gemOriginFilter = {};
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace O3DE::ProjectManager
|
||||
void SetFeatures(const QSet<QString>& features) { m_featureFilter = features; InvalidateFilter(); }
|
||||
|
||||
void InvalidateFilter();
|
||||
void ResetFilters();
|
||||
void ResetFilters(bool clearSearchString = true);
|
||||
|
||||
signals:
|
||||
void OnInvalidated();
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemUninstallDialog.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QVariant>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemUninstallDialog::GemUninstallDialog(const QString& gemName, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Uninstall Remote Gem"));
|
||||
setObjectName("GemUninstallDialog");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setMargin(30);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(layout);
|
||||
|
||||
// Body
|
||||
QLabel* subTitleLabel = new QLabel(tr("Are you sure you want to uninstall %1?").arg(gemName));
|
||||
subTitleLabel->setObjectName("gemCatalogDialogSubTitle");
|
||||
layout->addWidget(subTitleLabel);
|
||||
|
||||
layout->addSpacing(10);
|
||||
|
||||
QLabel* bodyLabel = new QLabel(tr("The Gem and its related files will be uninstalled. This does not affect the Gem's repository. "
|
||||
"You can reinstall this Gem from the Catalog, but its contents may be subject to change."));
|
||||
bodyLabel->setWordWrap(true);
|
||||
bodyLabel->setFixedSize(QSize(440, 80));
|
||||
layout->addWidget(bodyLabel);
|
||||
|
||||
layout->addSpacing(40);
|
||||
|
||||
// Buttons
|
||||
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
|
||||
dialogButtons->setObjectName("footer");
|
||||
layout->addWidget(dialogButtons);
|
||||
|
||||
QPushButton* cancelButton = dialogButtons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
cancelButton->setProperty("secondary", true);
|
||||
QPushButton* uninstallButton = dialogButtons->addButton(tr("Uninstall Gem"), QDialogButtonBox::ApplyRole);
|
||||
uninstallButton->setObjectName("gemCatalogUninstallGemButton");
|
||||
|
||||
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
|
||||
connect(uninstallButton, &QPushButton::clicked, this, &QDialog::accept);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QDialog>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class GemUninstallDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
public:
|
||||
explicit GemUninstallDialog(const QString& gemName, QWidget *parent = nullptr);
|
||||
~GemUninstallDialog() = default;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemUpdateDialog.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemUpdateDialog::GemUpdateDialog(const QString& gemName, bool updateAvaliable, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Update Remote Gem"));
|
||||
setObjectName("GemUpdateDialog");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setMargin(30);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(layout);
|
||||
|
||||
// Body
|
||||
QLabel* subTitleLabel = new QLabel(tr("%1 to the latest version of %2?").arg(
|
||||
updateAvaliable ? tr("Update") : tr("Force update"), gemName));
|
||||
subTitleLabel->setObjectName("gemCatalogDialogSubTitle");
|
||||
layout->addWidget(subTitleLabel);
|
||||
|
||||
layout->addSpacing(10);
|
||||
|
||||
QLabel* bodyLabel = new QLabel(tr("%1The latest version of this Gem may not be compatible with your engine. "
|
||||
"Updating this Gem will remove any local changes made to this Gem, "
|
||||
"and may remove old features that are in use.").arg(
|
||||
updateAvaliable ? "" : tr("No update detected for Gem. "
|
||||
"This will force a re-download of the gem. ")));
|
||||
bodyLabel->setWordWrap(true);
|
||||
bodyLabel->setFixedSize(QSize(440, 80));
|
||||
layout->addWidget(bodyLabel);
|
||||
|
||||
layout->addSpacing(40);
|
||||
|
||||
// Buttons
|
||||
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
|
||||
dialogButtons->setObjectName("footer");
|
||||
layout->addWidget(dialogButtons);
|
||||
|
||||
QPushButton* cancelButton = dialogButtons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
cancelButton->setProperty("secondary", true);
|
||||
QPushButton* updateButton =
|
||||
dialogButtons->addButton(tr("%1Update Gem").arg(updateAvaliable ? "" : tr("Force ")), QDialogButtonBox::ApplyRole);
|
||||
|
||||
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
|
||||
connect(updateButton, &QPushButton::clicked, this, &QDialog::accept);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QDialog>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class GemUpdateDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
public :
|
||||
explicit GemUpdateDialog(const QString& gemName, bool updateAvaliable = true, QWidget* parent = nullptr);
|
||||
~GemUpdateDialog() = default;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -515,7 +515,11 @@ namespace O3DE::ProjectManager
|
||||
auto pyProjectPath = QString_To_Py_Path(projectPath);
|
||||
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path, pyProjectPath));
|
||||
GemInfo gemInfo = GemInfoFromPath(path, pyProjectPath);
|
||||
// Mark as downloaded because this gem was registered with an existing directory
|
||||
gemInfo.m_downloadStatus = GemInfo::DownloadStatus::Downloaded;
|
||||
|
||||
gems.push_back(AZStd::move(gemInfo));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
@@ -560,7 +564,7 @@ namespace O3DE::ProjectManager
|
||||
return AZ::Success(AZStd::move(gemNames));
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::RegisterGem(const QString& gemPath, const QString& projectPath)
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::GemRegistration(const QString& gemPath, const QString& projectPath, bool remove)
|
||||
{
|
||||
bool registrationResult = false;
|
||||
auto result = ExecuteWithLockErrorHandling(
|
||||
@@ -582,7 +586,8 @@ namespace O3DE::ProjectManager
|
||||
pybind11::none(), // default_restricted_folder
|
||||
pybind11::none(), // default_third_party_folder
|
||||
pybind11::none(), // external_subdir_engine_path
|
||||
externalProjectPath // external_subdir_project_path
|
||||
externalProjectPath, // external_subdir_project_path
|
||||
remove // remove
|
||||
);
|
||||
|
||||
// Returns an exit code so boolify it then invert result
|
||||
@@ -595,12 +600,23 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else if (!registrationResult)
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(AZStd::string::format("Failed to register gem path %s", gemPath.toUtf8().constData()));
|
||||
return AZ::Failure<AZStd::string>(AZStd::string::format(
|
||||
"Failed to %s gem path %s", remove ? "unregister" : "register", gemPath.toUtf8().constData()));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::RegisterGem(const QString& gemPath, const QString& projectPath)
|
||||
{
|
||||
return GemRegistration(gemPath, projectPath);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::UnregisterGem(const QString& gemPath, const QString& projectPath)
|
||||
{
|
||||
return GemRegistration(gemPath, projectPath, /*remove*/true);
|
||||
}
|
||||
|
||||
bool PythonBindings::AddProject(const QString& path)
|
||||
{
|
||||
bool registrationResult = false;
|
||||
@@ -715,6 +731,7 @@ namespace O3DE::ProjectManager
|
||||
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", "");
|
||||
gemInfo.m_repoUri = Py_To_String_Optional(data, "repo_uri", "");
|
||||
|
||||
if (gemInfo.m_creator.contains("Open 3D Engine"))
|
||||
{
|
||||
@@ -728,6 +745,11 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Remote;
|
||||
}
|
||||
// If no origin was provided this cannot be remote and would be specified if O3DE so it should be local
|
||||
else
|
||||
{
|
||||
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Local;
|
||||
}
|
||||
|
||||
// As long Base Open3DEngine gems are installed before first startup non-remote gems will be downloaded
|
||||
if (gemInfo.m_gemOrigin != GemInfo::GemOrigin::Remote)
|
||||
@@ -1166,7 +1188,35 @@ namespace O3DE::ProjectManager
|
||||
return AZ::Success(AZStd::move(gemRepos));
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::DownloadGem(const QString& gemName, std::function<void(int, int)> gemProgressCallback)
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetAllGemRepoGemsInfos()
|
||||
{
|
||||
QVector<GemInfo> gemInfos;
|
||||
AZ::Outcome<void, AZStd::string> result = ExecuteWithLockErrorHandling(
|
||||
[&]
|
||||
{
|
||||
auto gemPaths = m_repo.attr("get_gem_json_paths_from_all_cached_repos")();
|
||||
|
||||
if (pybind11::isinstance<pybind11::set>(gemPaths))
|
||||
{
|
||||
for (auto path : gemPaths)
|
||||
{
|
||||
GemInfo gemInfo = GemInfoFromPath(path, pybind11::none());
|
||||
gemInfo.m_downloadStatus = GemInfo::DownloadStatus::NotDownloaded;
|
||||
gemInfos.push_back(gemInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(result.GetError());
|
||||
}
|
||||
|
||||
return AZ::Success(AZStd::move(gemInfos));
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::DownloadGem(
|
||||
const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force)
|
||||
{
|
||||
// This process is currently limited to download a single gem at a time.
|
||||
bool downloadSucceeded = false;
|
||||
@@ -1179,7 +1229,7 @@ namespace O3DE::ProjectManager
|
||||
QString_To_Py_String(gemName), // gem name
|
||||
pybind11::none(), // destination path
|
||||
false, // skip auto register
|
||||
false, // force
|
||||
force, // force overwrite
|
||||
pybind11::cpp_function(
|
||||
[this, gemProgressCallback](int bytesDownloaded, int totalBytes)
|
||||
{
|
||||
@@ -1209,30 +1259,19 @@ namespace O3DE::ProjectManager
|
||||
m_requestCancelDownload = true;
|
||||
}
|
||||
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> PythonBindings::GetAllGemRepoGemsInfos()
|
||||
bool PythonBindings::IsGemUpdateAvaliable(const QString& gemName, const QString& lastUpdated)
|
||||
{
|
||||
QVector<GemInfo> gemInfos;
|
||||
AZ::Outcome<void, AZStd::string> result = ExecuteWithLockErrorHandling(
|
||||
bool updateAvaliableResult = false;
|
||||
bool result = ExecuteWithLock(
|
||||
[&]
|
||||
{
|
||||
auto gemPaths = m_repo.attr("get_gem_json_paths_from_all_cached_repos")();
|
||||
auto pyGemName = QString_To_Py_String(gemName);
|
||||
auto pyLastUpdated = QString_To_Py_String(lastUpdated);
|
||||
auto pythonUpdateAvaliableResult = m_download.attr("is_o3de_gem_update_available")(pyGemName, pyLastUpdated);
|
||||
|
||||
if (pybind11::isinstance<pybind11::set>(gemPaths))
|
||||
{
|
||||
for (auto path : gemPaths)
|
||||
{
|
||||
GemInfo gemInfo = GemInfoFromPath(path, pybind11::none());
|
||||
gemInfo.m_downloadStatus = GemInfo::DownloadStatus::NotDownloaded;
|
||||
gemInfos.push_back(gemInfo);
|
||||
}
|
||||
}
|
||||
updateAvaliableResult = pythonUpdateAvaliableResult.cast<bool>();
|
||||
});
|
||||
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(result.GetError());
|
||||
}
|
||||
|
||||
return AZ::Success(AZStd::move(gemInfos));
|
||||
return result && updateAvaliableResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace O3DE::ProjectManager
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemInfos(const QString& projectPath) override;
|
||||
AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) override;
|
||||
AZ::Outcome<void, AZStd::string> RegisterGem(const QString& gemPath, const QString& projectPath = {}) override;
|
||||
AZ::Outcome<void, AZStd::string> UnregisterGem(const QString& gemPath, const QString& projectPath = {}) override;
|
||||
|
||||
// Project
|
||||
AZ::Outcome<ProjectInfo> CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) override;
|
||||
@@ -64,9 +65,10 @@ namespace O3DE::ProjectManager
|
||||
bool AddGemRepo(const QString& repoUri) override;
|
||||
bool RemoveGemRepo(const QString& repoUri) override;
|
||||
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() override;
|
||||
AZ::Outcome<void, AZStd::string> DownloadGem(const QString& gemName, std::function<void(int, int)> gemProgressCallback) override;
|
||||
void CancelDownload() override;
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemRepoGemsInfos() override;
|
||||
AZ::Outcome<void, AZStd::string> DownloadGem(const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force = false) override;
|
||||
void CancelDownload() override;
|
||||
bool IsGemUpdateAvaliable(const QString& gemName, const QString& lastUpdated) override;
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PythonBindings);
|
||||
@@ -77,6 +79,7 @@ namespace O3DE::ProjectManager
|
||||
GemRepoInfo GetGemRepoInfo(pybind11::handle repoUri);
|
||||
ProjectInfo ProjectInfoFromPath(pybind11::handle path);
|
||||
ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
|
||||
AZ::Outcome<void, AZStd::string> GemRegistration(const QString& gemPath, const QString& projectPath, bool remove = false);
|
||||
bool RegisterThisEngine();
|
||||
bool StopPython();
|
||||
|
||||
|
||||
@@ -94,11 +94,19 @@ namespace O3DE::ProjectManager
|
||||
/**
|
||||
* Registers the gem to the specified project, or to the o3de_manifest.json if no project path is given
|
||||
* @param gemPath the path to the gem
|
||||
* @param projectPath the path to the project. If empty, will register the external path in o3de_manifest.json
|
||||
* @param projectPath the path to the project. If empty, will register the external path in o3de_manifest.json
|
||||
* @return An outcome with the success flag as well as an error message in case of a failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> RegisterGem(const QString& gemPath, const QString& projectPath = {}) = 0;
|
||||
|
||||
/**
|
||||
* Unregisters the gem from the specified project, or from the o3de_manifest.json if no project path is given
|
||||
* @param gemPath the path to the gem
|
||||
* @param projectPath the path to the project. If empty, will unregister the external path in o3de_manifest.json
|
||||
* @return An outcome with the success flag as well as an error message in case of a failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> UnregisterGem(const QString& gemPath, const QString& projectPath = {}) = 0;
|
||||
|
||||
|
||||
// Projects
|
||||
|
||||
@@ -209,24 +217,34 @@ namespace O3DE::ProjectManager
|
||||
*/
|
||||
virtual AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() = 0;
|
||||
|
||||
/**
|
||||
* Downloads and registers a Gem.
|
||||
* @param gemName the name of the Gem to download
|
||||
* @param gemProgressCallback a callback function that is called with an int percentage download value
|
||||
* @return an outcome with a string error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> DownloadGem(const QString& gemName, std::function<void(int, int)> gemProgressCallback) = 0;
|
||||
|
||||
/**
|
||||
* Cancels the current download.
|
||||
*/
|
||||
virtual void CancelDownload() = 0;
|
||||
|
||||
/**
|
||||
* Gathers all gem infos for all gems registered from repos.
|
||||
* @return A list of gem infos.
|
||||
*/
|
||||
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemRepoGemsInfos() = 0;
|
||||
|
||||
/**
|
||||
* Downloads and registers a Gem.
|
||||
* @param gemName the name of the Gem to download.
|
||||
* @param gemProgressCallback a callback function that is called with an int percentage download value.
|
||||
* @param force should we forcibly overwrite the old version of the gem.
|
||||
* @return an outcome with a string error message on failure.
|
||||
*/
|
||||
virtual AZ::Outcome<void, AZStd::string> DownloadGem(
|
||||
const QString& gemName, std::function<void(int, int)> gemProgressCallback, bool force = false) = 0;
|
||||
|
||||
/**
|
||||
* Cancels the current download.
|
||||
*/
|
||||
virtual void CancelDownload() = 0;
|
||||
|
||||
/**
|
||||
* Checks if there is an update avaliable for a gem on a repo.
|
||||
* @param gemName the name of the gem to check.
|
||||
* @param lastUpdated last time the gem was update.
|
||||
* @return true if update is avaliable, false if not.
|
||||
*/
|
||||
virtual bool IsGemUpdateAvaliable(const QString& gemName, const QString& lastUpdated) = 0;
|
||||
};
|
||||
|
||||
using PythonBindingsInterface = AZ::Interface<IPythonBindings>;
|
||||
|
||||
@@ -96,6 +96,10 @@ set(FILES
|
||||
Source/GemCatalog/GemListHeaderWidget.cpp
|
||||
Source/GemCatalog/GemModel.h
|
||||
Source/GemCatalog/GemModel.cpp
|
||||
Source/GemCatalog/GemUninstallDialog.h
|
||||
Source/GemCatalog/GemUninstallDialog.cpp
|
||||
Source/GemCatalog/GemUpdateDialog.h
|
||||
Source/GemCatalog/GemUpdateDialog.cpp
|
||||
Source/GemCatalog/GemDependenciesDialog.h
|
||||
Source/GemCatalog/GemDependenciesDialog.cpp
|
||||
Source/GemCatalog/GemRequirementDialog.h
|
||||
|
||||
@@ -69,17 +69,13 @@ class ViewEditController(QObject):
|
||||
json_dict: Dict[str, any] = \
|
||||
json_utils.convert_resources_to_json_dict(self._proxy_model.get_resources(), self._config_file_json_source)
|
||||
|
||||
configuration: Configuration = self._configuration_manager.configuration
|
||||
if json_dict.get(json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME) == \
|
||||
json_utils.RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE:
|
||||
json_dict[json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = configuration.account_id
|
||||
|
||||
if json_dict == self._config_file_json_source:
|
||||
# skip because no difference found against existing json file
|
||||
return True
|
||||
|
||||
# try to write in memory json content into json file
|
||||
try:
|
||||
configuration: Configuration = self._configuration_manager.configuration
|
||||
config_file_full_path: str = file_utils.join_path(configuration.config_directory, config_file_name)
|
||||
json_utils.write_into_json_file(config_file_full_path, json_dict)
|
||||
self._config_file_json_source = json_dict
|
||||
|
||||
+49
-3
@@ -420,8 +420,30 @@ class TestViewEditController(TestCase):
|
||||
self._mocked_view_edit_page.config_file_combobox.currentText.return_value = \
|
||||
TestViewEditController._expected_config_file_name
|
||||
expected_json_dict: Dict[str, any] = {
|
||||
"dummyKey": "dummyValue",
|
||||
self._expected_account_id_attribute_name: self._expected_account_id_template_vale}
|
||||
"dummyKey": "dummyValue"
|
||||
}
|
||||
mock_json_utils.validate_resources_according_to_json_schema.return_value = []
|
||||
mock_json_utils.convert_resources_to_json_dict.return_value = expected_json_dict
|
||||
mock_file_utils.join_path.return_value = TestViewEditController._expected_config_file_full_path
|
||||
mocked_call_args: call = self._mocked_view_edit_page.save_changes_button.clicked.connect.call_args[0]
|
||||
|
||||
mocked_call_args[0]() # triggering save_changes_button connected function
|
||||
mock_json_utils.convert_resources_to_json_dict.assert_called_once()
|
||||
mock_json_utils.write_into_json_file.assert_called_once_with(
|
||||
TestViewEditController._expected_config_file_full_path, expected_json_dict)
|
||||
self._mocked_proxy_model.override_all_resources_status.assert_called_once_with(
|
||||
ResourceMappingAttributesStatus(ResourceMappingAttributesStatus.SUCCESS_STATUS_VALUE,
|
||||
[ResourceMappingAttributesStatus.SUCCESS_STATUS_VALUE]))
|
||||
|
||||
@patch("controller.view_edit_controller.file_utils")
|
||||
@patch("controller.view_edit_controller.json_utils")
|
||||
def test_page_save_changes_button_json_file_saved_and_template_account_id_unchanged(
|
||||
self, mock_json_utils: MagicMock, mock_file_utils: MagicMock) -> None:
|
||||
self._mocked_view_edit_page.config_file_combobox.currentText.return_value = \
|
||||
TestViewEditController._expected_config_file_name
|
||||
expected_json_dict: Dict[str, any] = {
|
||||
self._expected_account_id_attribute_name: self._expected_account_id_template_vale
|
||||
}
|
||||
mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME = self._expected_account_id_attribute_name
|
||||
mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE = self._expected_account_id_template_vale
|
||||
mock_json_utils.validate_resources_according_to_json_schema.return_value = []
|
||||
@@ -430,7 +452,31 @@ class TestViewEditController(TestCase):
|
||||
mocked_call_args: call = self._mocked_view_edit_page.save_changes_button.clicked.connect.call_args[0]
|
||||
|
||||
mocked_call_args[0]() # triggering save_changes_button connected function
|
||||
assert expected_json_dict["AccountId"] == self._mocked_configuration_manager.configuration.account_id
|
||||
assert expected_json_dict[mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] == self._expected_account_id_template_vale
|
||||
mock_json_utils.convert_resources_to_json_dict.assert_called_once()
|
||||
mock_json_utils.write_into_json_file.assert_called_once_with(
|
||||
TestViewEditController._expected_config_file_full_path, expected_json_dict)
|
||||
self._mocked_proxy_model.override_all_resources_status.assert_called_once_with(
|
||||
ResourceMappingAttributesStatus(ResourceMappingAttributesStatus.SUCCESS_STATUS_VALUE,
|
||||
[ResourceMappingAttributesStatus.SUCCESS_STATUS_VALUE]))
|
||||
|
||||
@patch("controller.view_edit_controller.file_utils")
|
||||
@patch("controller.view_edit_controller.json_utils")
|
||||
def test_page_save_changes_button_json_file_saved_and_empty_account_id_unchanged(
|
||||
self, mock_json_utils: MagicMock, mock_file_utils: MagicMock) -> None:
|
||||
self._mocked_view_edit_page.config_file_combobox.currentText.return_value = \
|
||||
TestViewEditController._expected_config_file_name
|
||||
expected_json_dict: Dict[str, any] = {
|
||||
self._expected_account_id_attribute_name: ''
|
||||
}
|
||||
mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME = self._expected_account_id_attribute_name
|
||||
mock_json_utils.validate_resources_according_to_json_schema.return_value = []
|
||||
mock_json_utils.convert_resources_to_json_dict.return_value = expected_json_dict
|
||||
mock_file_utils.join_path.return_value = TestViewEditController._expected_config_file_full_path
|
||||
mocked_call_args: call = self._mocked_view_edit_page.save_changes_button.clicked.connect.call_args[0]
|
||||
|
||||
mocked_call_args[0]() # triggering save_changes_button connected function
|
||||
assert expected_json_dict[mock_json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] == ''
|
||||
mock_json_utils.convert_resources_to_json_dict.assert_called_once()
|
||||
mock_json_utils.write_into_json_file.assert_called_once_with(
|
||||
TestViewEditController._expected_config_file_full_path, expected_json_dict)
|
||||
|
||||
@@ -103,6 +103,11 @@ class TestJsonUtils(TestCase):
|
||||
invalid_json_dict.pop(json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME)
|
||||
self.assertRaises(KeyError, json_utils.validate_json_dict_according_to_json_schema, invalid_json_dict)
|
||||
|
||||
def test_validate_json_dict_according_to_json_schema_raise_error_when_json_dict_has_empty_accountid(self) -> None:
|
||||
valid_json_dict: Dict[str, any] = copy.deepcopy(TestJsonUtils._expected_json_dict)
|
||||
valid_json_dict[json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = ''
|
||||
json_utils.validate_json_dict_according_to_json_schema(valid_json_dict)
|
||||
|
||||
def test_validate_json_dict_according_to_json_schema_pass_when_json_dict_has_template_accountid(self) -> None:
|
||||
valid_json_dict: Dict[str, any] = copy.deepcopy(TestJsonUtils._expected_json_dict)
|
||||
valid_json_dict[json_utils.RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME] = \
|
||||
|
||||
@@ -28,7 +28,7 @@ _RESOURCE_MAPPING_JSON_FORMAT_VERSION: str = "1.0.0"
|
||||
|
||||
RESOURCE_MAPPING_ACCOUNTID_JSON_KEY_NAME: str = "AccountId"
|
||||
RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE: str = "EMPTY"
|
||||
_RESOURCE_MAPPING_ACCOUNTID_PATTERN: str = f"^[0-9]{{12}}|{RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE}$"
|
||||
_RESOURCE_MAPPING_ACCOUNTID_PATTERN: str = f"^[0-9]{{12}}$|{RESOURCE_MAPPING_ACCOUNTID_TEMPLATE_VALUE}|^$"
|
||||
_RESOURCE_MAPPING_REGION_PATTERN: str = "^[a-z]{2}-[a-z]{4,9}-[0-9]{1}$"
|
||||
_RESOURCE_MAPPING_VERSION_PATTERN: str = "^[0-9]{1}.[0-9]{1}.[0-9]{1}$"
|
||||
|
||||
|
||||
@@ -41,9 +41,12 @@ namespace AZ
|
||||
AZ_Assert(readOnlyCache.empty(), "Inactive library has pipeline states in its global entry.");
|
||||
}
|
||||
|
||||
#if defined(AZ_DEBUG_BUILD)
|
||||
// the PipelineStateSet is expensive to duplicate, only do this in debug.
|
||||
PipelineStateSet readOnlyCacheCopy = readOnlyCache;
|
||||
AZ_Assert(AZStd::unique(readOnlyCacheCopy.begin(), readOnlyCacheCopy.end()) == readOnlyCacheCopy.end(),
|
||||
"'%d' Duplicates existed in the read-only cache!", readOnlyCache.size() - readOnlyCacheCopy.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
m_threadLibrarySet.ForEach([this](const ThreadLibrarySet& threadLibrarySet)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace AZ
|
||||
uint32_t m_swapChainsPerCommandList = 8;
|
||||
|
||||
// The maximum cost that can be associated with a single command list.
|
||||
uint32_t m_commandListCostThresholdMin = 1000;
|
||||
uint32_t m_commandListCostThresholdMin = 250;
|
||||
|
||||
// The maximum number of command lists per scope.
|
||||
uint32_t m_commandListsPerScopeMax = 16;
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace AZ
|
||||
uint32_t m_swapChainsPerCommandList = 8;
|
||||
|
||||
// The maximum cost that can be associated with a single command list.
|
||||
uint32_t m_commandListCostThresholdMin = 1000;
|
||||
uint32_t m_commandListCostThresholdMin = 250;
|
||||
|
||||
// The maximum number of command lists per scope.
|
||||
uint32_t m_commandListsPerScopeMax = 16;
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ namespace AZ
|
||||
uint32_t m_swapChainsPerCommandList = 8;
|
||||
|
||||
// The maximum cost that can be associated with a single command list.
|
||||
uint32_t m_commandListCostThresholdMin = 1000;
|
||||
uint32_t m_commandListCostThresholdMin = 250;
|
||||
|
||||
// The maximum number of command lists per scope.
|
||||
uint32_t m_commandListsPerScopeMax = 16;
|
||||
|
||||
@@ -26,8 +26,6 @@ ly_add_target(
|
||||
Gem::GradientSignal
|
||||
Gem::SurfaceData
|
||||
Gem::LmbrCentral
|
||||
|
||||
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
@@ -49,14 +47,14 @@ ly_add_target(
|
||||
)
|
||||
|
||||
# the above module is for use in all client/server types
|
||||
ly_create_alias(NAME Terrain.Servers NAMESPACE Gem TARGETS Gem::Terrain)
|
||||
ly_create_alias(NAME Terrain.Clients NAMESPACE Gem TARGETS Gem::Terrain)
|
||||
ly_create_alias(NAME Terrain.Servers NAMESPACE Gem TARGETS Gem::Terrain Gem::SurfaceData.Servers Gem::GradientSignal.Servers)
|
||||
ly_create_alias(NAME Terrain.Clients NAMESPACE Gem TARGETS Gem::Terrain Gem::SurfaceData.Clients Gem::GradientSignal.Clients)
|
||||
|
||||
# If we are on a host platform, we want to add the host tools targets like the Terrain.Editor target which
|
||||
# will also depend on Terrain.Static
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
NAME Terrain.Editor MODULE
|
||||
NAME Terrain.Editor GEM_MODULE
|
||||
NAMESPACE Gem
|
||||
AUTOMOC
|
||||
FILES_CMAKE
|
||||
@@ -78,8 +76,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
)
|
||||
|
||||
# the above module is for use in dev tool situations
|
||||
ly_create_alias(NAME Terrain.Builders NAMESPACE Gem TARGETS Gem::Terrain.Editor)
|
||||
ly_create_alias(NAME Terrain.Tools NAMESPACE Gem TARGETS Gem::Terrain.Editor)
|
||||
ly_create_alias(NAME Terrain.Builders NAMESPACE Gem TARGETS Gem::Terrain.Editor Gem::SurfaceData.Builders Gem::GradientSignal.Builders)
|
||||
ly_create_alias(NAME Terrain.Tools NAMESPACE Gem TARGETS Gem::Terrain.Editor Gem::SurfaceData.Tools Gem::GradientSignal.Tools)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -167,18 +167,16 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
||||
endforeach()
|
||||
list(JOIN INCLUDE_DIRECTORIES_PLACEHOLDER "\n" INCLUDE_DIRECTORIES_PLACEHOLDER)
|
||||
|
||||
string(REPEAT " " 8 PLACEHOLDER_INDENT)
|
||||
get_target_property(RUNTIME_DEPENDENCIES_PLACEHOLDER ${TARGET_NAME} MANUALLY_ADDED_DEPENDENCIES)
|
||||
if(RUNTIME_DEPENDENCIES_PLACEHOLDER) # not found properties return the name of the variable with a "-NOTFOUND" at the end, here we set it to empty if not found
|
||||
set(RUNTIME_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${RUNTIME_DEPENDENCIES_PLACEHOLDER}")
|
||||
list(JOIN RUNTIME_DEPENDENCIES_PLACEHOLDER "\n${PLACEHOLDER_INDENT}" RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||
else()
|
||||
unset(RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||
endif()
|
||||
|
||||
string(REPEAT " " 12 PLACEHOLDER_INDENT)
|
||||
get_property(interface_build_dependencies_props TARGET ${TARGET_NAME} PROPERTY LY_DELAYED_LINK)
|
||||
unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
|
||||
# We can have private build dependencies that contains direct or indirect runtime dependencies.
|
||||
# Since imported targets cannot contain build dependencies, we need another way to propagate the runtime dependencies.
|
||||
# We dont want to put such dependencies in the interface because a user can mistakenly use a symbol that is not available
|
||||
# when using the engine from source (and that the author of the target didn't want to set public).
|
||||
# To overcome this, we will actually expose the private build dependencies as runtime dependencies. Our runtime dependency
|
||||
# algorithm will walk recursively also through static libraries and will only copy binaries to the output.
|
||||
unset(RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||
if(interface_build_dependencies_props)
|
||||
cmake_parse_arguments(build_deps "" "" "PRIVATE;PUBLIC;INTERFACE" ${interface_build_dependencies_props})
|
||||
# Interface and public dependencies should always be exposed
|
||||
@@ -191,6 +189,8 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
||||
if("${target_type}" STREQUAL "STATIC_LIBRARY")
|
||||
set(build_deps_target "${build_deps_target};${build_deps_PRIVATE}")
|
||||
endif()
|
||||
# But we will also pass the private dependencies as runtime dependencies (note the comment above)
|
||||
set(RUNTIME_DEPENDENCIES_PLACEHOLDER ${build_deps_PRIVATE})
|
||||
foreach(build_dependency IN LISTS build_deps_target)
|
||||
# Skip wrapping produced when targets are not created in the same directory
|
||||
if(build_dependency)
|
||||
@@ -200,6 +200,18 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
|
||||
endif()
|
||||
list(JOIN INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER)
|
||||
|
||||
string(REPEAT " " 8 PLACEHOLDER_INDENT)
|
||||
get_target_property(manually_added_dependencies ${TARGET_NAME} MANUALLY_ADDED_DEPENDENCIES)
|
||||
if(manually_added_dependencies) # not found properties return the name of the variable with a "-NOTFOUND" at the end, here we set it to empty if not found
|
||||
list(APPEND RUNTIME_DEPENDENCIES_PLACEHOLDER ${manually_added_dependencies})
|
||||
endif()
|
||||
if(RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||
set(RUNTIME_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${RUNTIME_DEPENDENCIES_PLACEHOLDER}")
|
||||
list(JOIN RUNTIME_DEPENDENCIES_PLACEHOLDER "\n${PLACEHOLDER_INDENT}" RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||
else()
|
||||
unset(RUNTIME_DEPENDENCIES_PLACEHOLDER)
|
||||
endif()
|
||||
|
||||
string(REPEAT " " 8 PLACEHOLDER_INDENT)
|
||||
# If a target has an LY_PROJECT_NAME property, forward that property to new target
|
||||
get_target_property(target_project_association ${TARGET_NAME} LY_PROJECT_NAME)
|
||||
|
||||
@@ -139,11 +139,20 @@ endif()
|
||||
|
||||
# Configure system includes
|
||||
ly_set(LY_CXX_SYSTEM_INCLUDE_CONFIGURATION_FLAG
|
||||
/experimental:external # Turns on "external" headers feature for MSVC compilers
|
||||
/experimental:external # Turns on "external" headers feature for MSVC compilers, required for MSVC < 16.10
|
||||
/external:W0 # Set warning level in external headers to 0. This is used to suppress warnings 3rdParty libraries which uses the "system_includes" option in their json configuration
|
||||
)
|
||||
|
||||
# CMake 3.22rc added a definition for CMAKE_INCLUDE_SYSTEM_FLAG_CXX. However, its defined as "-external:I ", that space causes
|
||||
# issues when trying to use in TargetIncludeSystemDirectories_unsupported.cmake.
|
||||
# CMake 3.22rc has also not added support for external directories in MSVC through target_include_directories(... SYSTEM
|
||||
# So we will just fix the flag that was added by 3.22rc so it works with our TargetIncludeSystemDirectories_unsupported.cmake
|
||||
# Once target_include_directories(... SYSTEM is supported, we can branch and use TargetIncludeSystemDirectories_supported.cmake
|
||||
# Reported this here: https://gitlab.kitware.com/cmake/cmake/-/issues/17904#note_1078281
|
||||
if(NOT CMAKE_INCLUDE_SYSTEM_FLAG_CXX)
|
||||
ly_set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX /external:I)
|
||||
ly_set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "/external:I")
|
||||
else()
|
||||
string(STRIP ${CMAKE_INCLUDE_SYSTEM_FLAG_CXX} CMAKE_INCLUDE_SYSTEM_FLAG_CXX)
|
||||
endif()
|
||||
|
||||
include(cmake/Platform/Common/TargetIncludeSystemDirectories_unsupported.cmake)
|
||||
|
||||
@@ -74,11 +74,11 @@ def process_add_o3de_repo(file_name: str or pathlib.Path,
|
||||
manifest_json_uri = f'{o3de_object_uri}/{manifest_json}'
|
||||
manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode())
|
||||
cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json')
|
||||
if not cache_file.is_file():
|
||||
parsed_uri = urllib.parse.urlparse(manifest_json_uri)
|
||||
download_file_result = utils.download_file(parsed_uri, cache_file)
|
||||
if download_file_result != 0:
|
||||
return download_file_result
|
||||
|
||||
parsed_uri = urllib.parse.urlparse(manifest_json_uri)
|
||||
download_file_result = utils.download_file(parsed_uri, cache_file, True)
|
||||
if download_file_result != 0:
|
||||
return download_file_result
|
||||
|
||||
# Having a repo is also optional
|
||||
repo_list = []
|
||||
@@ -96,7 +96,7 @@ def process_add_o3de_repo(file_name: str or pathlib.Path,
|
||||
cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json')
|
||||
if cache_file.is_file():
|
||||
cache_file.unlink()
|
||||
download_file_result = utils.download_file(parsed_uri, cache_file)
|
||||
download_file_result = utils.download_file(parsed_uri, cache_file, True)
|
||||
if download_file_result != 0:
|
||||
return download_file_result
|
||||
|
||||
@@ -165,7 +165,7 @@ def refresh_repo(repo_uri: str,
|
||||
repo_sha256 = hashlib.sha256(parsed_uri.geturl().encode())
|
||||
cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json')
|
||||
|
||||
download_file_result = utils.download_file(parsed_uri, cache_file)
|
||||
download_file_result = utils.download_file(parsed_uri, cache_file, True)
|
||||
if download_file_result != 0:
|
||||
return download_file_result
|
||||
|
||||
@@ -178,12 +178,7 @@ def refresh_repo(repo_uri: str,
|
||||
|
||||
def refresh_repos() -> int:
|
||||
json_data = manifest.load_o3de_manifest()
|
||||
|
||||
# clear the cache
|
||||
cache_folder = manifest.get_o3de_cache_folder()
|
||||
shutil.rmtree(cache_folder)
|
||||
cache_folder = manifest.get_o3de_cache_folder() # will recreate it
|
||||
|
||||
result = 0
|
||||
|
||||
# set will stop circular references
|
||||
|
||||
Executable → Regular
Reference in New Issue
Block a user