diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 6b19e839d7..205395c935 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index e676ba73d3..d6f4da0051 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -7,25 +7,32 @@ */ #include +#include + #include + #include #include #include #include #include -#include #include #include #include +#include +#include namespace O3DE::ProjectManager { - CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) - : QWidget(parent) + GemCartWidget::GemCartWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) + : QScrollArea(parent) , m_gemModel(gemModel) , m_downloadController(downloadController) { setObjectName("GemCatalogCart"); + setWidgetResizable(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_layout = new QVBoxLayout(); m_layout->setSpacing(0); @@ -118,17 +125,15 @@ namespace O3DE::ProjectManager } return dependencies; }); - - setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); } - CartOverlayWidget::~CartOverlayWidget() + GemCartWidget::~GemCartWidget() { // disconnect from all download controller signals disconnect(m_downloadController, nullptr, this, nullptr); } - void CartOverlayWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices) + void GemCartWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices) { QWidget* widget = new QWidget(); widget->setFixedWidth(s_width); @@ -164,12 +169,12 @@ namespace O3DE::ProjectManager update(); } - void CartOverlayWidget::OnCancelDownloadActivated(const QString& gemName) + void GemCartWidget::OnCancelDownloadActivated(const QString& gemName) { m_downloadController->CancelGemDownload(gemName); } - void CartOverlayWidget::CreateDownloadSection() + void GemCartWidget::CreateDownloadSection() { m_downloadSectionWidget = new QWidget(); m_downloadSectionWidget->setFixedWidth(s_width); @@ -223,12 +228,12 @@ namespace O3DE::ProjectManager } // connect to download controller data changed - connect(m_downloadController, &DownloadController::GemDownloadAdded, this, &CartOverlayWidget::GemDownloadAdded); - connect(m_downloadController, &DownloadController::GemDownloadRemoved, this, &CartOverlayWidget::GemDownloadRemoved); - connect(m_downloadController, &DownloadController::GemDownloadProgress, this, &CartOverlayWidget::GemDownloadProgress); + connect(m_downloadController, &DownloadController::GemDownloadAdded, this, &GemCartWidget::GemDownloadAdded); + connect(m_downloadController, &DownloadController::GemDownloadRemoved, this, &GemCartWidget::GemDownloadRemoved); + connect(m_downloadController, &DownloadController::GemDownloadProgress, this, &GemCartWidget::GemDownloadProgress); } - void CartOverlayWidget::GemDownloadAdded(const QString& gemName) + void GemCartWidget::GemDownloadAdded(const QString& gemName) { // Containing widget for the current download item QWidget* newGemDownloadWidget = new QWidget(); @@ -246,7 +251,7 @@ namespace O3DE::ProjectManager nameProgressLayout->addStretch(); QLabel* cancelText = new QLabel(tr("Cancel").arg(gemName), newGemDownloadWidget); cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); - connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); + connect(cancelText, &QLabel::linkActivated, this, &GemCartWidget::OnCancelDownloadActivated); nameProgressLayout->addWidget(cancelText); downloadingGemLayout->addLayout(nameProgressLayout); @@ -267,7 +272,7 @@ namespace O3DE::ProjectManager m_downloadingListWidget->show(); } - void CartOverlayWidget::GemDownloadRemoved(const QString& gemName) + void GemCartWidget::GemDownloadRemoved(const QString& gemName) { QWidget* gemToRemove = m_downloadingListWidget->findChild(gemName); if (gemToRemove) @@ -289,7 +294,7 @@ namespace O3DE::ProjectManager } } - void CartOverlayWidget::GemDownloadProgress(const QString& gemName, int bytesDownloaded, int totalBytes) + void GemCartWidget::GemDownloadProgress(const QString& gemName, int bytesDownloaded, int totalBytes) { QWidget* gemToUpdate = m_downloadingListWidget->findChild(gemName); if (gemToUpdate) @@ -324,7 +329,7 @@ namespace O3DE::ProjectManager } } - QVector CartOverlayWidget::GetTagsFromModelIndices(const QVector& gems) const + QVector GemCartWidget::GetTagsFromModelIndices(const QVector& gems) const { QVector tags; tags.reserve(gems.size()); @@ -349,7 +354,7 @@ namespace O3DE::ProjectManager iconButton->setFocusPolicy(Qt::NoFocus); iconButton->setIcon(QIcon(":/Summary.svg")); iconButton->setFixedSize(s_iconSize, s_iconSize); - connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowGemCart); m_layout->addWidget(iconButton); m_countLabel = new QLabel(); @@ -362,7 +367,7 @@ namespace O3DE::ProjectManager m_dropDownButton->setFocusPolicy(Qt::NoFocus); m_dropDownButton->setIcon(QIcon(":/CarrotArrowDown.svg")); m_dropDownButton->setFixedSize(s_arrowDownIconSize, s_arrowDownIconSize); - connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowGemCart); m_layout->addWidget(m_dropDownButton); // Adjust the label text whenever the model gets updated. @@ -377,28 +382,28 @@ namespace O3DE::ProjectManager m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty()); // Automatically close the overlay window in case there are no gems to be activated or deactivated anymore. - if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + if (m_gemCart && toBeAdded.isEmpty() && toBeRemoved.isEmpty()) { - m_cartOverlay->deleteLater(); - m_cartOverlay = nullptr; + m_gemCart->deleteLater(); + m_gemCart = nullptr; } }); } void CartButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) { - ShowOverlay(); + ShowGemCart(); } void CartButton::hideEvent(QHideEvent*) { - if (m_cartOverlay) + if (m_gemCart) { - m_cartOverlay->hide(); + m_gemCart->hide(); } } - void CartButton::ShowOverlay() + void CartButton::ShowGemCart() { const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true); const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true); @@ -407,37 +412,33 @@ namespace O3DE::ProjectManager return; } - if (m_cartOverlay) + if (m_gemCart) { // Directly delete the former overlay before creating the new one. // Don't use deleteLater() here. This might overwrite the new overlay pointer // depending on the event queue. - delete m_cartOverlay; + delete m_gemCart; } - m_cartOverlay = new CartOverlayWidget(m_gemModel, m_downloadController, this); - connect(m_cartOverlay, &QWidget::destroyed, this, [=] + m_gemCart = new GemCartWidget(m_gemModel, m_downloadController, this); + connect(m_gemCart, &QWidget::destroyed, this, [=] { // Reset the overlay pointer on destruction to prevent dangling pointers. - m_cartOverlay = nullptr; + m_gemCart = nullptr; + // Tell header gem cart is no longer open + UpdateGemCart(nullptr); }); - m_cartOverlay->show(); + m_gemCart->show(); - const QPoint parentPos = m_dropDownButton->mapToParent(m_dropDownButton->pos()); - const QPoint globalPos = m_dropDownButton->mapToGlobal(m_dropDownButton->pos()); - const QPoint offset(-4, 10); - m_cartOverlay->setGeometry(globalPos.x() - parentPos.x() - m_cartOverlay->width() + width() + offset.x(), - globalPos.y() + offset.y(), - m_cartOverlay->width(), - m_cartOverlay->height()); + emit UpdateGemCart(m_gemCart); } CartButton::~CartButton() { // Make sure the overlay window is automatically closed in case the gem catalog is destroyed. - if (m_cartOverlay) + if (m_gemCart) { - m_cartOverlay->deleteLater(); + m_gemCart->deleteLater(); } } @@ -514,6 +515,17 @@ namespace O3DE::ProjectManager connect(m_downloadController, &DownloadController::GemDownloadAdded, this, &GemCatalogHeaderWidget::GemDownloadAdded); connect(m_downloadController, &DownloadController::GemDownloadRemoved, this, &GemCatalogHeaderWidget::GemDownloadRemoved); + + connect( + m_cartButton, &CartButton::UpdateGemCart, this, + [this](QWidget* gemCart) + { + GemCartShown(gemCart); + if (gemCart) + { + emit UpdateGemCart(gemCart); + } + }); } void GemCatalogHeaderWidget::GemDownloadAdded(const QString& /*gemName*/) @@ -521,7 +533,7 @@ namespace O3DE::ProjectManager m_downloadSpinner->show(); m_downloadLabel->show(); m_downloadSpinnerMovie->start(); - m_cartButton->ShowOverlay(); + m_cartButton->ShowGemCart(); } void GemCatalogHeaderWidget::GemDownloadRemoved(const QString& /*gemName*/) @@ -534,8 +546,44 @@ namespace O3DE::ProjectManager } } + void GemCatalogHeaderWidget::GemCartShown(bool state) + { + m_showGemCart = state; + repaint(); + } + void GemCatalogHeaderWidget::ReinitForProject() { m_filterLineEdit->setText({}); } + + void GemCatalogHeaderWidget::paintEvent([[maybe_unused]] QPaintEvent* event) + { + // Only show triangle when cart is shown + if (!m_showGemCart) + { + return; + } + + const QPoint buttonPos = m_cartButton->pos(); + const QSize buttonSize = m_cartButton->size(); + + // Draw isosceles triangle with top point touching bottom of cartButton + // Bottom aligned with header bottom and top of right panel + const QPoint topPoint(buttonPos.x() + buttonSize.width() / 2, buttonPos.y() + buttonSize.height()); + const QPoint bottomLeftPoint(topPoint.x() - 20, height()); + const QPoint bottomRightPoint(topPoint.x() + 20, height()); + + QPainterPath trianglePath; + trianglePath.moveTo(topPoint); + trianglePath.lineTo(bottomLeftPoint); + trianglePath.lineTo(bottomRightPoint); + trianglePath.lineTo(topPoint); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setPen(Qt::NoPen); + painter.fillPath(trianglePath, QBrush(QColor("#555555"))); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 5174fde57d..b749e9831d 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -14,8 +14,10 @@ #include #include #include -#include #include + +#include +#include #endif QT_FORWARD_DECLARE_CLASS(QPushButton) @@ -28,14 +30,14 @@ QT_FORWARD_DECLARE_CLASS(QMovie) namespace O3DE::ProjectManager { - class CartOverlayWidget - : public QWidget + class GemCartWidget + : public QScrollArea { Q_OBJECT // AUTOMOC public: - CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); - ~CartOverlayWidget(); + GemCartWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); + ~GemCartWidget(); public slots: void GemDownloadAdded(const QString& gemName); @@ -68,7 +70,10 @@ namespace O3DE::ProjectManager public: CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); ~CartButton(); - void ShowOverlay(); + void ShowGemCart(); + + signals: + void UpdateGemCart(QWidget* gemCart); private: void mousePressEvent(QMouseEvent* event) override; @@ -78,7 +83,7 @@ namespace O3DE::ProjectManager QHBoxLayout* m_layout = nullptr; QLabel* m_countLabel = nullptr; QPushButton* m_dropDownButton = nullptr; - CartOverlayWidget* m_cartOverlay = nullptr; + GemCartWidget* m_gemCart = nullptr; DownloadController* m_downloadController = nullptr; inline constexpr static int s_iconSize = 24; @@ -99,11 +104,16 @@ namespace O3DE::ProjectManager public slots: void GemDownloadAdded(const QString& gemName); void GemDownloadRemoved(const QString& gemName); + void GemCartShown(bool state = false); signals: void AddGem(); void OpenGemsRepo(); void RefreshGems(); + void UpdateGemCart(QWidget* gemCart); + + protected slots: + void paintEvent(QPaintEvent* event) override; private: AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr; @@ -113,5 +123,6 @@ namespace O3DE::ProjectManager QLabel* m_downloadLabel = nullptr; QMovie* m_downloadSpinnerMovie = nullptr; CartButton* m_cartButton = nullptr; + bool m_showGemCart = false; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index bc0e6c240d..c11a9b9de1 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -8,6 +8,11 @@ #include #include +#include +#include +#include +#include +#include #include #include #include @@ -28,6 +33,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -51,9 +57,11 @@ namespace O3DE::ProjectManager vLayout->addWidget(m_headerWidget); connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); + connect(m_gemModel->GetSelectionModel(), &QItemSelectionModel::selectionChanged, this, [this]{ ShowInspector(); }); 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_headerWidget, &GemCatalogHeaderWidget::UpdateGemCart, this, &GemCatalogScreen::UpdateAndShowGemCart); connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult); QHBoxLayout* hLayout = new QHBoxLayout(); @@ -61,8 +69,11 @@ namespace O3DE::ProjectManager vLayout->addLayout(hLayout); m_gemListView = new GemListView(m_proxyModel, m_proxyModel->GetSelectionModel(), this); + + m_rightPanelStack = new QStackedWidget(this); + m_rightPanelStack->setFixedWidth(240); + m_gemInspector = new GemInspector(m_gemModel, this); - m_gemInspector->setFixedWidth(240); connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); }); connect(m_gemInspector, &GemInspector::UpdateGem, this, &GemCatalogScreen::UpdateGem); @@ -85,7 +96,9 @@ namespace O3DE::ProjectManager hLayout->addWidget(filterWidget); hLayout->addLayout(middleVLayout); - hLayout->addWidget(m_gemInspector); + + hLayout->addWidget(m_rightPanelStack); + m_rightPanelStack->addWidget(m_gemInspector); m_notificationsView = AZStd::make_unique(this, AZ_CRC("GemCatalogNotificationsView")); m_notificationsView->SetOffset(QPoint(10, 70)); @@ -303,6 +316,8 @@ namespace O3DE::ProjectManager QModelIndex proxyIndex = m_proxyModel->mapFromSource(modelIndex); m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect); m_gemListView->scrollTo(proxyIndex); + + ShowInspector(); } void GemCatalogScreen::UpdateGem(const QModelIndex& modelIndex) @@ -496,6 +511,12 @@ namespace O3DE::ProjectManager } } + void GemCatalogScreen::ShowInspector() + { + m_rightPanelStack->setCurrentIndex(RightPanelWidgetOrder::Inspector); + m_headerWidget->GemCartShown(); + } + GemCatalogScreen::EnableDisableGemsResult GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath) { IPythonBindings* pythonBindings = PythonBindingsInterface::Get(); @@ -577,6 +598,18 @@ namespace O3DE::ProjectManager emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); } + void GemCatalogScreen::UpdateAndShowGemCart(QWidget* cartWidget) + { + QWidget* previousCart = m_rightPanelStack->widget(RightPanelWidgetOrder::Cart); + if (previousCart) + { + m_rightPanelStack->removeWidget(previousCart); + } + + m_rightPanelStack->insertWidget(RightPanelWidgetOrder::Cart, cartWidget); + m_rightPanelStack->setCurrentIndex(RightPanelWidgetOrder::Cart); + } + void GemCatalogScreen::OnGemDownloadResult(const QString& gemName, bool succeeded) { if (succeeded) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index c5fbb057ef..2deaa41b9e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -12,18 +12,24 @@ #include #include #include -#include -#include -#include -#include -#include -#include + #include #include #endif +QT_FORWARD_DECLARE_CLASS(QVBoxLayout) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) + namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(GemCatalogHeaderWidget) + QT_FORWARD_DECLARE_CLASS(GemFilterWidget) + QT_FORWARD_DECLARE_CLASS(GemListView) + QT_FORWARD_DECLARE_CLASS(GemInspector) + QT_FORWARD_DECLARE_CLASS(GemModel) + QT_FORWARD_DECLARE_CLASS(GemSortFilterProxyModel) + QT_FORWARD_DECLARE_CLASS(DownloadController) + class GemCatalogScreen : public ScreenWidget { @@ -62,14 +68,22 @@ namespace O3DE::ProjectManager private slots: void HandleOpenGemRepo(); - + void UpdateAndShowGemCart(QWidget* cartWidget); + void ShowInspector(); private: + enum RightPanelWidgetOrder + { + Inspector = 0, + Cart + }; + void FillModel(const QString& projectPath); AZStd::unique_ptr m_notificationsView; GemListView* m_gemListView = nullptr; + QStackedWidget* m_rightPanelStack = nullptr; GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; GemCatalogHeaderWidget* m_headerWidget = nullptr; diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 5f0d0c3f72..36d78ed2ac 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -15,8 +15,8 @@ #include #include #include +#include #include - #include #include