diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index e18bf34931..1bd261da00 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -20,8 +20,7 @@ QPushButton:focus { QTabBar { background-color: transparent; } -QTabWidget::tab-bar -{ +QTabWidget::tab-bar { left: 78px; /* make room for the logo */ } QTabBar::tab { @@ -32,32 +31,35 @@ QTabBar::tab { margin-right:40px; border-bottom: 3px solid transparent; } -QTabBar::tab:text -{ +QTabBar::tab:text { text-align:left; } QTabWidget::pane { background-color: #333333; border:0 none; } -QTabBar::tab:selected -{ +QTabBar::tab:selected { + background-color: transparent; border-bottom: 3px solid #1e70eb; color: #1e70eb; + font-weight: 500; } -QTabBar::tab:hover -{ +QTabBar::tab:hover { color: #1e70eb; + font-weight: 500; } -QTabBar::tab:pressed -{ +QTabBar::tab:pressed { color: #0e60eb; } QTabBar::focus { outline: 0px; outline: none; outline-style: none; - } +} +QTabBar::tab:focus { + background-color: #525252; + color: #4082eb; +} /************** General (Forms) **************/ diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp index 0ff963e539..7981e9d758 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp @@ -52,6 +52,7 @@ namespace O3DE::ProjectManager if (projectButton) { + projectButton->SetProjectBuilding(); projectButton->SetProjectButtonAction(tr("Cancel Build"), [this] { HandleCancel(); }); if (m_lastProgress != 0) @@ -111,6 +112,10 @@ namespace O3DE::ProjectManager emit Done(false); return; } + else + { + m_projectInfo.m_buildFailed = false; + } emit Done(true); } diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index 82b4e8d84a..5bae3b807a 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -162,22 +162,9 @@ namespace O3DE::ProjectManager QDesktopServices::openUrl(m_logUrl); } - ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent, bool processing) + ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent) : QFrame(parent) , m_projectInfo(projectInfo) - { - BaseSetup(); - if (processing) - { - ProcessingSetup(); - } - else - { - ReadySetup(); - } - } - - void ProjectButton::BaseSetup() { setObjectName("projectButton"); @@ -199,50 +186,63 @@ namespace O3DE::ProjectManager } m_projectImageLabel->setPixmap(QPixmap(projectPreviewPath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); - m_projectFooter = new QFrame(this); + QFrame* projectFooter = new QFrame(this); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setContentsMargins(0, 0, 0, 0); - m_projectFooter->setLayout(hLayout); + projectFooter->setLayout(hLayout); { QLabel* projectNameLabel = new QLabel(m_projectInfo.GetProjectDisplayName(), this); hLayout->addWidget(projectNameLabel); + + QMenu* menu = new QMenu(this); + menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); }); + menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); }); + menu->addSeparator(); + menu->addAction(tr("Open Project folder..."), this, [this]() + { + AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path); + }); + menu->addSeparator(); + menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); }); + menu->addSeparator(); + menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); }); + menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); }); + + m_projectMenuButton = new QPushButton(this); + m_projectMenuButton->setObjectName("projectMenuButton"); + m_projectMenuButton->setMenu(menu); + hLayout->addWidget(m_projectMenuButton); } - vLayout->addWidget(m_projectFooter); + vLayout->addWidget(projectFooter); + + connect(m_projectImageLabel->GetOpenEditorButton(), &QPushButton::clicked, [this](){ emit OpenProject(m_projectInfo.m_path); }); } - void ProjectButton::ProcessingSetup() + const ProjectInfo& ProjectButton::GetProjectInfo() const { - m_projectImageLabel->SetEnabled(false); - m_projectImageLabel->SetOverlayText(tr("Processing...\n\n")); + return m_projectInfo; + } + + void ProjectButton::RestoreDefaultState() + { + m_projectImageLabel->SetEnabled(true); + m_projectImageLabel->SetOverlayText(""); + m_projectMenuButton->setVisible(true); QProgressBar* progressBar = m_projectImageLabel->GetProgressBar(); - progressBar->setVisible(true); + progressBar->setVisible(false); progressBar->setValue(0); - } - void ProjectButton::ReadySetup() - { - connect(m_projectImageLabel->GetOpenEditorButton(), &QPushButton::clicked, [this](){ emit OpenProject(m_projectInfo.m_path); }); + QPushButton* projectActionButton = m_projectImageLabel->GetActionButton(); + projectActionButton->setVisible(false); + if (m_actionButtonConnection) + { + disconnect(m_actionButtonConnection); + } - QMenu* menu = new QMenu(this); - menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); }); - menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); }); - menu->addSeparator(); - menu->addAction(tr("Open Project folder..."), this, [this]() - { - AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path); - }); - menu->addSeparator(); - menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); }); - menu->addSeparator(); - menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); }); - menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); }); - - QPushButton* projectMenuButton = new QPushButton(this); - projectMenuButton->setObjectName("projectMenuButton"); - projectMenuButton->setMenu(menu); - m_projectFooter->layout()->addWidget(projectMenuButton); + m_projectImageLabel->GetWarningIcon()->setVisible(false); + m_projectImageLabel->GetWarningLabel()->setVisible(false); } void ProjectButton::SetProjectButtonAction(const QString& text, AZStd::function lambda) @@ -292,9 +292,15 @@ namespace O3DE::ProjectManager SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); }); } - void ProjectButton::BuildThisProject() + void ProjectButton::SetProjectBuilding() { - emit BuildProject(m_projectInfo); + m_projectImageLabel->SetEnabled(false); + m_projectImageLabel->SetOverlayText(tr("Building...\n\n")); + m_projectMenuButton->setVisible(false); + + QProgressBar* progressBar = m_projectImageLabel->GetProgressBar(); + progressBar->setVisible(true); + progressBar->setValue(0); } void ProjectButton::SetLaunchButtonEnabled(bool enabled) diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index ff352e0afd..9f64b74eab 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -56,13 +56,14 @@ namespace O3DE::ProjectManager void OnLinkActivated(const QString& link); private: - QVBoxLayout* m_buildOverlayLayout; - QLabel* m_overlayLabel; - QProgressBar* m_progressBar; - QPushButton* m_openEditorButton; - QPushButton* m_actionButton; - QLabel* m_warningText; - QLabel* m_warningIcon; + QVBoxLayout* m_buildOverlayLayout = nullptr; + QLabel* m_overlayLabel = nullptr; + QProgressBar* m_progressBar = nullptr; + QPushButton* m_openEditorButton = nullptr; + QPushButton* m_actionButton = nullptr; + QLabel* m_warningText = nullptr; + QLabel* m_warningIcon = nullptr; + QUrl m_logUrl; bool m_enabled = true; }; @@ -73,13 +74,18 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr, bool processing = false); + explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr); ~ProjectButton() = default; + const ProjectInfo& GetProjectInfo() const; + + void RestoreDefaultState(); + void SetProjectButtonAction(const QString& text, AZStd::function lambda); void SetProjectBuildButtonAction(); void SetBuildLogsLink(const QUrl& logUrl); void ShowBuildFailed(bool show, const QUrl& logUrl); + void SetProjectBuilding(); void SetLaunchButtonEnabled(bool enabled); void SetButtonOverlayText(const QString& text); @@ -95,17 +101,14 @@ namespace O3DE::ProjectManager void BuildProject(const ProjectInfo& projectInfo); private: - void BaseSetup(); - void ProcessingSetup(); - void ReadySetup(); void enterEvent(QEvent* event) override; void leaveEvent(QEvent* event) override; - void BuildThisProject(); ProjectInfo m_projectInfo; - LabelButton* m_projectImageLabel; - QFrame* m_projectFooter; - QLayout* m_requiresBuildLayout; + + LabelButton* m_projectImageLabel = nullptr; + QPushButton* m_projectMenuButton = nullptr; + QLayout* m_requiresBuildLayout = nullptr; QMetaObject::Connection m_actionButtonConnection; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index e27eedd122..27e39926e4 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -68,9 +68,7 @@ namespace O3DE::ProjectManager } ProjectsScreen::~ProjectsScreen() - { - delete m_currentBuilder; } QFrame* ProjectsScreen::CreateFirstTimeContent() @@ -114,10 +112,8 @@ namespace O3DE::ProjectManager return frame; } - QFrame* ProjectsScreen::CreateProjectsContent(QString buildProjectPath, ProjectButton** projectButton) + QFrame* ProjectsScreen::CreateProjectsContent() { - RemoveInvalidProjects(); - QFrame* frame = new QFrame(this); frame->setObjectName("projectsContent"); { @@ -126,7 +122,7 @@ namespace O3DE::ProjectManager layout->setContentsMargins(0, 0, 0, 0); frame->setLayout(layout); - QFrame* header = new QFrame(this); + QFrame* header = new QFrame(frame); QHBoxLayout* headerLayout = new QHBoxLayout(); { QLabel* titleLabel = new QLabel(tr("My Projects"), this); @@ -150,87 +146,34 @@ namespace O3DE::ProjectManager layout->addWidget(header); - // Get all projects and create a horizontal scrolling list of them - auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); - if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty()) - { - QScrollArea* projectsScrollArea = new QScrollArea(this); - QWidget* scrollWidget = new QWidget(); + QScrollArea* projectsScrollArea = new QScrollArea(this); + QWidget* scrollWidget = new QWidget(); - FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); - scrollWidget->setLayout(flowLayout); + m_projectsFlowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(m_projectsFlowLayout); - projectsScrollArea->setWidget(scrollWidget); - projectsScrollArea->setWidgetResizable(true); + projectsScrollArea->setWidget(scrollWidget); + projectsScrollArea->setWidgetResizable(true); - QVector nonProcessingProjects; - buildProjectPath = QDir::fromNativeSeparators(buildProjectPath); - for (auto& project : projectsResult.GetValue()) - { - if (projectButton && !*projectButton) - { - if (QDir::fromNativeSeparators(project.m_path) == buildProjectPath) - { - *projectButton = CreateProjectButton(project, flowLayout, true); - continue; - } - } + ResetProjectsContent(); - nonProcessingProjects.append(project); - } - - for (auto& project : nonProcessingProjects) - { - ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout); - - if (BuildQueueContainsProject(project.m_path)) - { - projectButtonWidget->SetProjectButtonAction(tr("Cancel Queued Build"), - [this, project] - { - UnqueueBuildProject(project); - SuggestBuildProjectMsg(project, false); - }); - } - else if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end()) - { - auto buildProjectIterator = RequiresBuildProjectIterator(project.m_path); - if (buildProjectIterator != m_requiresBuild.end()) - { - if (buildProjectIterator->m_buildFailed) - { - projectButtonWidget->ShowBuildFailed(true, buildProjectIterator->m_logUrl); - } - else - { - projectButtonWidget->SetProjectBuildButtonAction(); - } - } - - } - } - - layout->addWidget(projectsScrollArea); - } + layout->addWidget(projectsScrollArea); } return frame; } - ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing) + ProjectButton* ProjectsScreen::CreateProjectButton(const ProjectInfo& project) { - ProjectButton* projectButton = new ProjectButton(project, this, processing); + ProjectButton* projectButton = new ProjectButton(project, this); + m_projectButtons.insert(project.m_path, projectButton); + m_projectsFlowLayout->addWidget(projectButton); - flowLayout->addWidget(projectButton); - - if (!processing) - { - connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); - connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); - connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); - connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); - connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); - } + connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); + connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); + connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); + connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); + connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); connect(projectButton, &ProjectButton::BuildProject, this, &ProjectsScreen::QueueBuildProject); return projectButton; @@ -238,29 +181,128 @@ namespace O3DE::ProjectManager void ProjectsScreen::ResetProjectsContent() { - // refresh the projects content by re-creating it for now - if (m_projectsContent) + RemoveInvalidProjects(); + + // Get all projects and create a vertical scrolling list of them + // Sort building and queued projects first + auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); + if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty()) { - m_stack->removeWidget(m_projectsContent); - m_projectsContent->deleteLater(); + QVector projectsVector = projectsResult.GetValue(); + // If a project path is in this set then the button for it will be kept + QSet keepProject; + for (const ProjectInfo& project : projectsVector) + { + keepProject.insert(project.m_path); + } + + // Clear flow and delete buttons for removed projects + auto projectButtonsIter = m_projectButtons.begin(); + while (projectButtonsIter != m_projectButtons.end()) + { + m_projectsFlowLayout->removeWidget(projectButtonsIter.value()); + + if (!keepProject.contains(projectButtonsIter.key())) + { + projectButtonsIter = m_projectButtons.erase(projectButtonsIter); + } + else + { + ++projectButtonsIter; + } + } + + QString buildProjectPath = ""; + if (m_currentBuilder) + { + buildProjectPath = m_currentBuilder->GetProjectInfo().m_path; + } + + // Put currently building project in front, then queued projects, then sorts alphabetically + std::sort(projectsVector.begin(), projectsVector.end(), [buildProjectPath, this](const ProjectInfo& arg1, const ProjectInfo& arg2) + { + if (arg1.m_path == buildProjectPath) + { + return true; + } + else if (arg2.m_path == buildProjectPath) + { + return false; + } + + bool arg1InBuildQueue = BuildQueueContainsProject(arg1.m_path); + bool arg2InBuildQueue = BuildQueueContainsProject(arg2.m_path); + if (arg1InBuildQueue && !arg2InBuildQueue) + { + return true; + } + else if (!arg1InBuildQueue && arg2InBuildQueue) + { + return false; + } + else + { + return arg1.m_displayName.toLower() < arg2.m_displayName.toLower(); + } + }); + + // Add any missing project buttons and restore buttons to default state + for (const ProjectInfo& project : projectsVector) + { + if (!m_projectButtons.contains(project.m_path)) + { + m_projectButtons.insert(project.m_path, CreateProjectButton(project)); + } + else + { + auto projectButtonIter = m_projectButtons.find(project.m_path); + if (projectButtonIter != m_projectButtons.end()) + { + projectButtonIter.value()->RestoreDefaultState(); + m_projectsFlowLayout->addWidget(projectButtonIter.value()); + } + } + } + + // Setup building button again + auto buildProjectIter = m_projectButtons.find(buildProjectPath); + if (buildProjectIter != m_projectButtons.end()) + { + m_currentBuilder->SetProjectButton(buildProjectIter.value()); + } + + for (const ProjectInfo& project : m_buildQueue) + { + auto projectIter = m_projectButtons.find(project.m_path); + if (projectIter != m_projectButtons.end()) + { + projectIter.value()->SetProjectButtonAction( + tr("Cancel Queued Build"), + [this, project] + { + UnqueueBuildProject(project); + SuggestBuildProjectMsg(project, false); + }); + } + } + + for (const ProjectInfo& project : m_requiresBuild) + { + auto projectIter = m_projectButtons.find(project.m_path); + if (projectIter != m_projectButtons.end()) + { + if (project.m_buildFailed) + { + projectIter.value()->ShowBuildFailed(true, project.m_logUrl); + } + else + { + projectIter.value()->SetProjectBuildButtonAction(); + } + } + } } - m_background.load(":/Backgrounds/DefaultBackground.jpg"); - - // Make sure to update builder with latest Project Button - if (m_currentBuilder) - { - ProjectButton* projectButtonPtr = nullptr; - - m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectInfo().m_path, &projectButtonPtr); - m_currentBuilder->SetProjectButton(projectButtonPtr); - } - else - { - m_projectsContent = CreateProjectsContent(); - } - - m_stack->addWidget(m_projectsContent); m_stack->setCurrentWidget(m_projectsContent); } @@ -466,7 +508,7 @@ namespace O3DE::ProjectManager if (m_buildQueue.empty() && !m_currentBuilder) { StartProjectBuild(projectInfo); - // Projects Content is already reset in fuction + // Projects Content is already reset in function } else { @@ -491,6 +533,7 @@ namespace O3DE::ProjectManager } else { + m_background.load(":/Backgrounds/DefaultBackground.jpg"); ResetProjectsContent(); } } diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h index 45605ab678..859f8d0eae 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -18,6 +18,7 @@ QT_FORWARD_DECLARE_CLASS(QPaintEvent) QT_FORWARD_DECLARE_CLASS(QFrame) QT_FORWARD_DECLARE_CLASS(QStackedWidget) QT_FORWARD_DECLARE_CLASS(QLayout) +QT_FORWARD_DECLARE_CLASS(FlowLayout) namespace O3DE::ProjectManager { @@ -59,8 +60,8 @@ namespace O3DE::ProjectManager private: QFrame* CreateFirstTimeContent(); - QFrame* CreateProjectsContent(QString buildProjectPath = "", ProjectButton** projectButton = nullptr); - ProjectButton* CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing = false); + QFrame* CreateProjectsContent(); + ProjectButton* CreateProjectButton(const ProjectInfo& project); void ResetProjectsContent(); bool ShouldDisplayFirstTimeContent(); bool RemoveInvalidProjects(); @@ -75,7 +76,9 @@ namespace O3DE::ProjectManager QPixmap m_background; QFrame* m_firstTimeContent = nullptr; QFrame* m_projectsContent = nullptr; + FlowLayout* m_projectsFlowLayout = nullptr; QStackedWidget* m_stack = nullptr; + QHash m_projectButtons; QList m_requiresBuild; QQueue m_buildQueue; ProjectBuilderController* m_currentBuilder = nullptr; diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index 30b8ef1d34..df0bdb29f4 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -30,6 +30,7 @@ namespace O3DE::ProjectManager // add a tab widget at the bottom of the stack m_tabWidget = new QTabWidget(); + m_tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus); m_screenStack->addWidget(m_tabWidget); connect(m_tabWidget, &QTabWidget::currentChanged, this, &ScreensCtrl::TabChanged); } diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 6aba261cd2..e1b6d740e2 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -54,6 +54,7 @@ namespace O3DE::ProjectManager QTabWidget* tabWidget = new QTabWidget(); tabWidget->setObjectName("projectSettingsTab"); tabWidget->tabBar()->setObjectName("projectSettingsTabBar"); + tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus); tabWidget->addTab(m_updateSettingsScreen, tr("General")); QPushButton* gemsButton = new QPushButton(tr("Configure Gems"), this);