/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include namespace O3DE::ProjectManager { GemCatalogScreen::GemCatalogScreen(QWidget* parent) : ScreenWidget(parent) { m_gemModel = new GemModel(this); m_proxModel = new GemSortFilterProxyModel(m_gemModel, this); QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setMargin(0); vLayout->setSpacing(0); setLayout(vLayout); m_downloadController = new DownloadController(); m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); vLayout->addWidget(m_headerWidget); connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); vLayout->addLayout(hLayout); m_gemListView = new GemListView(m_proxModel, m_proxModel->GetSelectionModel(), this); m_gemInspector = new GemInspector(m_gemModel, this); m_gemInspector->setFixedWidth(240); QWidget* filterWidget = new QWidget(this); filterWidget->setFixedWidth(240); m_filterWidgetLayout = new QVBoxLayout(); m_filterWidgetLayout->setMargin(0); m_filterWidgetLayout->setSpacing(0); filterWidget->setLayout(m_filterWidgetLayout); GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxModel); QVBoxLayout* middleVLayout = new QVBoxLayout(); middleVLayout->setMargin(0); middleVLayout->setSpacing(0); middleVLayout->addWidget(listHeaderWidget); middleVLayout->addWidget(m_gemListView); hLayout->addWidget(filterWidget); hLayout->addLayout(middleVLayout); hLayout->addWidget(m_gemInspector); m_notificationsView = AZStd::make_unique(this, AZ_CRC("GemCatalogNotificationsView")); m_notificationsView->SetOffset(QPoint(10, 70)); } void GemCatalogScreen::ReinitForProject(const QString& projectPath) { m_gemModel->clear(); FillModel(projectPath); if (m_filterWidget) { m_filterWidget->hide(); m_filterWidget->deleteLater(); } m_proxModel->ResetFilters(); m_filterWidget = new GemFilterWidget(m_proxModel); m_filterWidgetLayout->addWidget(m_filterWidget); m_headerWidget->ReinitForProject(); connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); // 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); }); } void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies) { if (m_notificationsEnabled) { bool added = GemModel::IsAdded(modelIndex); bool dependency = GemModel::IsAddedDependency(modelIndex); bool gemStateChanged = (added && !dependency) || (!added && !dependency); if (!gemStateChanged && !numChangedDependencies) { // no actual changes made return; } QString notification; if (gemStateChanged) { notification = GemModel::GetDisplayName(modelIndex); if (numChangedDependencies > 0) { notification += " " + tr("and") + " "; } } if (numChangedDependencies == 1 ) { notification += "1 Gem " + tr("dependency"); } else if (numChangedDependencies > 1) { notification += QString("%d Gem ").arg(numChangedDependencies) + tr("dependencies"); } notification += " " + (added ? tr("activated") : tr("deactivated")); AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Custom, notification, ""); toastConfiguration.m_customIconImage = ":/gem.svg"; toastConfiguration.m_borderRadius = 4; toastConfiguration.m_duration = AZStd::chrono::milliseconds(3000); m_notificationsView->ShowToastNotification(toastConfiguration); } } void GemCatalogScreen::hideEvent(QHideEvent* event) { ScreenWidget::hideEvent(event); m_notificationsView->OnHide(); } void GemCatalogScreen::showEvent(QShowEvent* event) { ScreenWidget::showEvent(event); m_notificationsView->OnShow(); } void GemCatalogScreen::resizeEvent(QResizeEvent* event) { ScreenWidget::resizeEvent(event); m_notificationsView->UpdateToastPosition(); } void GemCatalogScreen::moveEvent(QMoveEvent* event) { ScreenWidget::moveEvent(event); m_notificationsView->UpdateToastPosition(); } void GemCatalogScreen::FillModel(const QString& projectPath) { AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); if (allGemInfosResult.IsSuccess()) { // Add all available gems to the model. const QVector allGemInfos = allGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allGemInfos) { m_gemModel->AddGem(gemInfo); } m_gemModel->UpdateGemDependencies(); m_notificationsEnabled = false; // Gather enabled gems for the given project. auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); if (enabledGemNamesResult.IsSuccess()) { const QVector enabledGemNames = enabledGemNamesResult.GetValue(); for (const AZStd::string& enabledGemName : enabledGemNames) { const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str()); if (modelIndex.isValid()) { GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, true); GemModel::SetIsAdded(*m_gemModel, modelIndex, true); } else { AZ_Warning("ProjectManager::GemCatalog", false, "Cannot find entry for gem with name '%s'. The CMake target name probably does not match the specified name in the gem.json.", enabledGemName.c_str()); } } } else { QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve enabled gems for project %1.\n\nError:\n%2").arg(projectPath, enabledGemNamesResult.GetError().c_str())); } m_notificationsEnabled = true; } else { QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve gems for %1.\n\nError:\n%2").arg(projectPath, allGemInfosResult.GetError().c_str())); } } GemCatalogScreen::EnableDisableGemsResult GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath) { IPythonBindings* pythonBindings = PythonBindingsInterface::Get(); QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); if (m_gemModel->DoGemsToBeAddedHaveRequirements()) { GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, this); if(confirmRequirementsDialog->exec() == QDialog::Rejected) { return EnableDisableGemsResult::Cancel; } } if (m_gemModel->HasDependentGemsToRemove()) { GemDependenciesDialog* dependenciesDialog = new GemDependenciesDialog(m_gemModel, this); if(dependenciesDialog->exec() == QDialog::Rejected) { return EnableDisableGemsResult::Cancel; } toBeAdded = m_gemModel->GatherGemsToBeAdded(); toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); } for (const QModelIndex& modelIndex : toBeAdded) { const QString gemPath = GemModel::GetPath(modelIndex); const AZ::Outcome 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())); return EnableDisableGemsResult::Failed; } } for (const QModelIndex& modelIndex : toBeRemoved) { const QString gemPath = GemModel::GetPath(modelIndex); const AZ::Outcome 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())); return EnableDisableGemsResult::Failed; } } return EnableDisableGemsResult::Success; } void GemCatalogScreen::HandleOpenGemRepo() { QVector gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true); QVector gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true); if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty()) { QMessageBox::StandardButton warningResult = QMessageBox::warning( nullptr, "Pending Changes", "There are some unsaved changes to the gem selection,
they will be lost if you change screens.
Are you sure?", QMessageBox::No | QMessageBox::Yes); if (warningResult != QMessageBox::Yes) { return; } } emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); } ProjectManagerScreen GemCatalogScreen::GetScreenEnum() { return ProjectManagerScreen::GemCatalog; } } // namespace O3DE::ProjectManager