From 13de9de3c1bd657a8e5edfeac9912347b81b9aa9 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 26 May 2021 19:47:17 -0700 Subject: [PATCH] Project Manager Toolbar Update - use flow control for projects page for automatic updates when resizing - made the first time screen only display the first time --- AutomatedTesting/preview.png | 4 +- .../ProjectManager/Resources/AddOffset.svg | 5 + .../Resources/AddOffset_Hover.svg | 5 + .../ProjectManager/Resources/ArrowBack.svg | 3 + .../ProjectManager/Resources/FolderOffset.svg | 5 + .../Resources/FolderOffset_Hover.svg | 5 + .../Resources/ProjectManager.qrc | 9 + .../Resources/ProjectManager.qss | 275 +++++++++++++- Code/Tools/ProjectManager/Resources/build.svg | 11 + Code/Tools/ProjectManager/Resources/menu.svg | 5 + .../ProjectManager/Resources/menu_hover.svg | 5 + Code/Tools/ProjectManager/Resources/o3de.svg | 3 + .../Source/CreateProjectCtrl.cpp | 82 +++-- .../ProjectManager/Source/CreateProjectCtrl.h | 16 +- .../Source/EngineSettingsScreen.cpp | 10 + .../Source/EngineSettingsScreen.h | 3 + .../Source/FirstTimeUseScreen.cpp | 95 ----- .../Source/FirstTimeUseScreen.h | 49 --- .../Source/NewProjectSettingsScreen.cpp | 143 ++++---- .../Source/NewProjectSettingsScreen.h | 8 +- .../Source/ProjectButtonWidget.cpp | 24 +- .../Source/ProjectButtonWidget.h | 1 - .../Source/ProjectManagerWindow.cpp | 51 +-- .../Source/ProjectManagerWindow.h | 14 - .../Source/ProjectManagerWindow.ui | 67 ---- .../Source/ProjectsHomeScreen.cpp | 206 ----------- .../ProjectManager/Source/ProjectsScreen.cpp | 347 ++++++++++++++++++ ...{ProjectsHomeScreen.h => ProjectsScreen.h} | 30 +- Code/Tools/ProjectManager/Source/ScreenDefs.h | 3 +- .../ProjectManager/Source/ScreenFactory.cpp | 10 +- .../Source/ScreenHeaderWidget.cpp | 62 ++++ .../Source/ScreenHeaderWidget.h | 42 +++ .../ProjectManager/Source/ScreenWidget.h | 15 + .../ProjectManager/Source/ScreensCtrl.cpp | 97 ++++- .../Tools/ProjectManager/Source/ScreensCtrl.h | 4 + .../Source/UpdateProjectCtrl.cpp | 2 +- Code/Tools/ProjectManager/Source/main.cpp | 7 +- .../project_manager_files.cmake | 9 +- Templates/DefaultProject/Template/preview.png | 4 +- 39 files changed, 1103 insertions(+), 633 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/AddOffset.svg create mode 100644 Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg create mode 100644 Code/Tools/ProjectManager/Resources/ArrowBack.svg create mode 100644 Code/Tools/ProjectManager/Resources/FolderOffset.svg create mode 100644 Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg create mode 100644 Code/Tools/ProjectManager/Resources/build.svg create mode 100644 Code/Tools/ProjectManager/Resources/menu.svg create mode 100644 Code/Tools/ProjectManager/Resources/menu_hover.svg create mode 100644 Code/Tools/ProjectManager/Resources/o3de.svg delete mode 100644 Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp delete mode 100644 Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h delete mode 100644 Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui delete mode 100644 Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp create mode 100644 Code/Tools/ProjectManager/Source/ProjectsScreen.cpp rename Code/Tools/ProjectManager/Source/{ProjectsHomeScreen.h => ProjectsScreen.h} (69%) create mode 100644 Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp create mode 100644 Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h diff --git a/AutomatedTesting/preview.png b/AutomatedTesting/preview.png index 2191a0ebc2..3d4fe78063 100644 --- a/AutomatedTesting/preview.png +++ b/AutomatedTesting/preview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6 -size 5466 +oid sha256:40949893ed7009eeaa90b7ce6057cb6be9dfaf7b162e3c26ba9dadf985939d7d +size 2038 diff --git a/Code/Tools/ProjectManager/Resources/AddOffset.svg b/Code/Tools/ProjectManager/Resources/AddOffset.svg new file mode 100644 index 0000000000..4c62234070 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/AddOffset.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg b/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg new file mode 100644 index 0000000000..a0e2a07eda --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ArrowBack.svg b/Code/Tools/ProjectManager/Resources/ArrowBack.svg new file mode 100644 index 0000000000..749bb5a02e --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowBack.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/FolderOffset.svg b/Code/Tools/ProjectManager/Resources/FolderOffset.svg new file mode 100644 index 0000000000..a048fbcc39 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FolderOffset.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg b/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg new file mode 100644 index 0000000000..fb13cd8558 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 2e60e84326..04d5e98a10 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -4,6 +4,12 @@ Add.svg + AddOffset.svg + AddOffset_Hover.svg + ArrowBack.svg + build.svg + FolderOffset.svg + FolderOffset_Hover.svg Select_Folder.svg o3de_editor.ico Windows.svg @@ -14,6 +20,9 @@ DefaultProjectImage.png ArrowDownLine.svg ArrowUpLine.svg + o3de.svg + menu.svg + menu_hover.svg Backgrounds/FirstTimeBackgroundImage.jpg ArrowDownLine.svg ArrowUpLine.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 849c9cbf5c..5eb92964dd 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -1,29 +1,69 @@ /************** General (MainWindow) **************/ QMainWindow { - background-color: #333333; + background:#131313 url(:/o3de.svg) no-repeat top left; + /* position the logo using padding and background-origin, Qt does not support background-position pixels */ + background-origin:content; + padding:25px 16px; + margin:0; } - QPushButton:focus { outline: none; border:1px solid #1e70eb; } +QTabBar { + background-color: transparent; +} +QTabWidget::tab-bar +{ + left: 78px; /* make room for the logo */ +} +QTabBar::tab { + height:82px; + background-color: transparent; + font-size:24px; + min-width:100px; + margin-right:40px; + border-bottom: 3px solid transparent; +} +QTabBar::tab:text +{ + text-align:left; +} +QTabWidget::pane { + background-color: #333333; + border:0 none; +} +QTabBar::tab:selected +{ + border-bottom: 3px solid #1e70eb; + color: #1e70eb; +} +QTabBar::tab:hover +{ + color: #1e70eb; +} +QTabBar::tab:pressed +{ + color: #0e60eb; +} + /************** General (Forms) **************/ #formLineEditWidget, #formBrowseEditWidget { - max-width: 780px; + max-width: 890px; } #formFrame { - max-width: 720px; + max-width: 840px; background-color: #444444; border:1px solid #dddddd; border-radius: 4px; padding: 0px 10px 2px 6px; margin-top:10px; - margin-left:30px; + margin-left:50px; } #formFrame[Focus="true"] { @@ -59,16 +99,235 @@ QPushButton:focus { padding-top: -4px; } + #formErrorLabel { color: #ec3030; font-size: 14px; - margin-left: 40px; + margin-left: 50px; } #formTitleLabel { font-size:21px; color:#ffffff; - margin: 10px 0 10px 30px; + margin: 24px 0 10px 50px; +} + +/************** General (Modal windows) **************/ + +#header { + background-color:#111111; + min-height:80px; + max-height:80px; +} + +#header QPushButton { + /* settings min/max lets us use a fixed size */ + min-width: 24px; + max-width: 24px; + min-height: 24px; + max-height: 24px; + margin: 20px 10px 0px 10px; + background:transparent url(:/ArrowBack.svg) no-repeat center; + background-origin:content; + qproperty-flat: true; + qproperty-iconSize: 50px; +} + +#header QPushButton:focus { + border:none; +} +#header QPushButton:hover { + background:#333333 url(:/ArrowBack.svg) no-repeat center; +} +#header QPushButton:pressed { + background:#222222 url(:/ArrowBack.svg) no-repeat center; +} + +#headerTitle { + font-size:14px; + text-align:left; + margin:0; + padding-top:10px; + padding-bottom:-5px; + min-height:15px; + max-height:15px; +} +#headerSubTitle { + font-size:24px; + text-align:left; + margin:0; + min-height:42px; + max-height:42px; +} + +#body { + background-color:#333333; +} +#footer { + /* settings min/max lets us use a fixed size */ + min-width: 50px; + min-height:54px; + max-height:54px; +} + +#footer > QPushButton { + qproperty-flat: true; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0095f2, stop: 1.0 #1e70eb); + border-radius: 3px; + min-height: 28px; + max-height: 28px; + min-width: 150px; + margin-right:30px; +} +#footer > QPushButton:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #10A5f2, stop: 1.0 #2e80eb); +} +#footer > QPushButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0085e2, stop: 1.0 #0e60db); +} + +#footer > QPushButton[secondary="true"] { + margin-right: 10px; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); +} +#footer > QPushButton[secondary="true"]:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); +} +#footer > QPushButton[secondary="true"]:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); +} + +/************** Project Settings **************/ +#projectSettings { + margin-top:42px; +} + +#projectTemplate { + margin: 55px 0 0 50px; + max-width: 780px; + min-height:200px; + max-height:200px; +} +#projectTemplateLabel { + font-size:16px; + font-weight:100; +} + +#projectTemplateDetailsLabel { + font-size:14px; + min-height:40px; + margin-bottom:20px; +} + +#projectTemplateDetails { + background-color:#444444; + max-width:240px; + min-width:240px; + margin-left:30px; +} + +/************** Projects **************/ +#firstTimeContent > #titleLabel { + font-size:60px; + margin:73px 0px 0px 0px; + qproperty-indent: 0; +} + +#firstTimeContent > #introLabel { + font-size:14px; + margin:10px 0 60px 0; + qproperty-indent: 0; +} + +#firstTimeContent > QPushButton { + min-width: 210px; + max-width: 210px; + min-height: 276px; + max-height: 276px; + qproperty-flat: true; + background-origin:content; + font-size:14px; + border: 1px solid #ffffff; +} + +#firstTimeContent > QPushButton:hover { + border: 1px solid #1e70eb; + color: #1e70eb; +} + +#firstTimeContent > QPushButton:pressed { + border: 1px solid #0e60eb; + color: #0e60eb; +} + +#createProjectButton { + background:rgba(0,0,0,180) url(:/AddOffset.svg) no-repeat center center; +} +#createProjectButton:hover, +#createProjectButton:pressed { + background:rgba(0,0,0,180) url(:/AddOffset_Hover.svg) no-repeat center center; +} + +#addProjectButton { + background:rgba(0,0,0,180) url(:/FolderOffset.svg) no-repeat center center; +} +#addProjectButton:hover, +#addProjectButton:pressed { + background:rgba(0,0,0,180) url(:/FolderOffset_Hover.svg) no-repeat center center; +} + +#projectsContent > QFrame { + margin-top:60px; +} + +#projectsContent > QFrame > #titleLabel { + font-size:24px; + qproperty-indent: 0; +} + +#projectsContent > QScrollArea { + margin-top:40px; + margin-bottom:5px; +} + +#projectButton > #labelButton { + border:1px solid white; +} +#projectButton > #labelButton:hover, +#projectButton > #labelButton:pressed { + border:1px solid #1e70eb; +} + +#projectButton > QFrame { + margin-top:6px; +} + +#projectButton > QFrame > QLabel { + font-weight:bold; + font-size:14px; + qproperty-indent: 0; +} + +#projectMenuButton { + qproperty-flat: true; + background:transparent url(:/menu.svg) no-repeat center center; + max-width:30px; + min-width:30px; + max-height:14px; + min-height:14px; +} + +#projectsContent > QFrame > #newProjectButton { + min-width:150px; + max-width:150px; + min-height:26px; + max-height:26px; } #labelButtonOverlay { @@ -77,4 +336,4 @@ QPushButton:focus { max-width:210px;; min-height:278px; max-height:278px; -} +} \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Resources/build.svg b/Code/Tools/ProjectManager/Resources/build.svg new file mode 100644 index 0000000000..b6c3546443 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/build.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Code/Tools/ProjectManager/Resources/menu.svg b/Code/Tools/ProjectManager/Resources/menu.svg new file mode 100644 index 0000000000..a639c74ab4 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/menu_hover.svg b/Code/Tools/ProjectManager/Resources/menu_hover.svg new file mode 100644 index 0000000000..4eea63faca --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/menu_hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/o3de.svg b/Code/Tools/ProjectManager/Resources/o3de.svg new file mode 100644 index 0000000000..bb6e596a00 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/o3de.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 03e6a34b89..69f0a3983d 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -14,11 +14,17 @@ #include #include #include +#include +#include #include +#include #include #include #include +#include +#include +#include namespace O3DE::ProjectManager { @@ -26,29 +32,34 @@ namespace O3DE::ProjectManager : ScreenWidget(parent) { QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); + vLayout->setContentsMargins(0,0,0,0); + + m_header = new ScreenHeader(this); + m_header->setTitle(tr("Create a New Project")); + m_header->setSubTitle(tr("Enter Project Details")); + connect(m_header->backButton(), &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); + vLayout->addWidget(m_header); - m_screensCtrl = new ScreensCtrl(); - vLayout->addWidget(m_screensCtrl); + m_stack = new QStackedWidget(this); + m_stack->setObjectName("body"); + m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding)); + m_stack->addWidget(new NewProjectSettingsScreen()); + m_stack->addWidget(new GemCatalogScreen()); + vLayout->addWidget(m_stack); QDialogButtonBox* backNextButtons = new QDialogButtonBox(); + backNextButtons->setObjectName("footer"); vLayout->addWidget(backNextButtons); m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_backButton->setProperty("secondary", true); m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); - connect(m_backButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleBackButton); - connect(m_nextButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleNextButton); - - m_screensOrder = - { - ProjectManagerScreen::NewProjectSettings, - ProjectManagerScreen::GemCatalog - }; - m_screensCtrl->BuildScreens(m_screensOrder); - m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::NewProjectSettings, false); + connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); + connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton); - UpdateNextButtonText(); + Update(); + setLayout(vLayout); } ProjectManagerScreen CreateProjectCtrl::GetScreenEnum() @@ -58,28 +69,20 @@ namespace O3DE::ProjectManager void CreateProjectCtrl::HandleBackButton() { - if (!m_screensCtrl->GotoPreviousScreen()) + if (m_stack->currentIndex() > 0) { - emit GotoPreviousScreenRequest(); + m_stack->setCurrentIndex(m_stack->currentIndex() - 1); + Update(); } else { - UpdateNextButtonText(); + emit GotoPreviousScreenRequest(); } } void CreateProjectCtrl::HandleNextButton() { - ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen(); + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); - auto screenOrderIter = m_screensOrder.begin(); - for (; screenOrderIter != m_screensOrder.end(); ++screenOrderIter) - { - if (*screenOrderIter == screenEnum) - { - ++screenOrderIter; - break; - } - } if (screenEnum == ProjectManagerScreen::NewProjectSettings) { @@ -97,10 +100,10 @@ namespace O3DE::ProjectManager } } - if (screenOrderIter != m_screensOrder.end()) + if (m_stack->currentIndex() != m_stack->count() - 1) { - m_screensCtrl->ChangeToScreen(*screenOrderIter); - UpdateNextButtonText(); + m_stack->setCurrentIndex(m_stack->currentIndex() + 1); + Update(); } else { @@ -108,7 +111,7 @@ namespace O3DE::ProjectManager if (result.IsSuccess()) { // adding gems is not implemented yet because we don't know what targets to add or how to add them - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { @@ -117,14 +120,21 @@ namespace O3DE::ProjectManager } } - void CreateProjectCtrl::UpdateNextButtonText() + void CreateProjectCtrl::Update() { - QString nextButtonText = tr("Next"); - if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog) + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); + if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog) + { + m_header->setTitle(tr("Create Project")); + m_header->setSubTitle(tr("Configure project with Gems")); + m_nextButton->setText(tr("Create Project")); + } + else { - nextButtonText = tr("Create Project"); + m_header->setTitle(tr("Create Project")); + m_header->setSubTitle(tr("Enter Project Details")); + m_nextButton->setText(tr("Next")); } - m_nextButton->setText(nextButtonText); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 213bff3bc2..01e3349b21 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -12,15 +12,18 @@ #pragma once #if !defined(Q_MOC_RUN) -#include "ProjectInfo.h" #include -#include -#include +#include #endif +QT_FORWARD_DECLARE_CLASS(QStackedWidget) +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QLabel) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(ScreenHeader) + class CreateProjectCtrl : public ScreenWidget { @@ -34,12 +37,13 @@ namespace O3DE::ProjectManager void HandleNextButton(); private: - void UpdateNextButtonText(); + void Update(); + + QStackedWidget* m_stack; + ScreenHeader* m_header; - ScreensCtrl* m_screensCtrl; QPushButton* m_backButton; QPushButton* m_nextButton; - QVector m_screensOrder; QString m_projectTemplatePath; ProjectInfo m_projectInfo; diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index f51996bd65..6342041da4 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -82,6 +82,16 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::EngineSettings; } + QString EngineSettingsScreen::GetTabText() + { + return tr("Engine"); + } + + bool EngineSettingsScreen::IsTab() + { + return true; + } + void EngineSettingsScreen::OnTextChanged() { // save engine settings diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h index 0e91ec2d3b..36e329cdf6 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h @@ -26,7 +26,10 @@ namespace O3DE::ProjectManager public: explicit EngineSettingsScreen(QWidget* parent = nullptr); ~EngineSettingsScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + QString GetTabText() override; + bool IsTab() override; protected slots: void OnTextChanged(); diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp deleted file mode 100644 index 8654b221fb..0000000000 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include -#include -#include -#include -#include - -namespace O3DE::ProjectManager -{ - FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent) - : ScreenWidget(parent) - { - QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); - vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins); - - QLabel* titleLabel = new QLabel(this); - titleLabel->setText(tr("Ready. Set. Create!")); - titleLabel->setStyleSheet("font-size: 60px"); - vLayout->addWidget(titleLabel); - - QLabel* introLabel = new QLabel(this); - introLabel->setTextFormat(Qt::AutoText); - introLabel->setText(tr("

Welcome to O3DE! Start something new by creating a project. Not sure what to create?

Explore what\342\200\231s available by downloading our sample project.

")); - introLabel->setStyleSheet("font-size: 14px"); - vLayout->addWidget(introLabel); - - QHBoxLayout* buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(s_buttonSpacing); - - m_createProjectButton = CreateLargeBoxButton(QIcon(":/Add.svg"), tr("Create Project"), this); - m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); - buttonLayout->addWidget(m_createProjectButton); - - m_addProjectButton = CreateLargeBoxButton(QIcon(":/Select_Folder.svg"), tr("Add a Project"), this); - m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); - buttonLayout->addWidget(m_addProjectButton); - - QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum); - buttonLayout->addItem(buttonSpacer); - - vLayout->addItem(buttonLayout); - - QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding); - vLayout->addItem(verticalSpacer); - - // Using border-image allows for scaling options background-image does not support - setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }"); - - connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton); - connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton); - } - - ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum() - { - return ProjectManagerScreen::FirstTimeUse; - } - - void FirstTimeUseScreen::HandleNewProjectButton() - { - emit ResetScreenRequest(ProjectManagerScreen::CreateProject); - emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); - } - void FirstTimeUseScreen::HandleAddProjectButton() - { - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); - } - - QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent) - { - QPushButton* largeBoxButton = new QPushButton(icon, text, parent); - - largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight); - largeBoxButton->setFlat(true); - largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); - largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }"); - - return largeBoxButton; - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h deleted file mode 100644 index 80a2310d7a..0000000000 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ -#pragma once - -#if !defined(Q_MOC_RUN) -#include -#endif - -QT_FORWARD_DECLARE_CLASS(QIcon) -QT_FORWARD_DECLARE_CLASS(QPushButton) - -namespace O3DE::ProjectManager -{ - class FirstTimeUseScreen - : public ScreenWidget - { - public: - explicit FirstTimeUseScreen(QWidget* parent = nullptr); - ~FirstTimeUseScreen() = default; - ProjectManagerScreen GetScreenEnum() override; - - protected slots: - void HandleNewProjectButton(); - void HandleAddProjectButton(); - - private: - QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); - - QPushButton* m_createProjectButton; - QPushButton* m_addProjectButton; - - inline constexpr static int s_contentMargins = 80; - inline constexpr static int s_buttonSpacing = 30; - inline constexpr static int s_iconSize = 24; - inline constexpr static int s_spacerSize = 20; - inline constexpr static int s_boxButtonWidth = 210; - inline constexpr static int s_boxButtonHeight = 280; - }; - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index ffbf1bf6fe..b57a2b35b2 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -12,6 +12,9 @@ #include #include +#include +#include +#include #include #include @@ -23,6 +26,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -31,64 +35,81 @@ namespace O3DE::ProjectManager NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - QHBoxLayout* hLayout = new QHBoxLayout(); - this->setLayout(hLayout); - + QHBoxLayout* hLayout = new QHBoxLayout(this); + hLayout->setAlignment(Qt::AlignLeft); + hLayout->setContentsMargins(0,0,0,0); + + // if we don't provide a parent for this box layout the stylesheet doesn't take + // if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally + QFrame* projectSettingsFrame = new QFrame(this); + projectSettingsFrame->setObjectName("projectSettings"); QVBoxLayout* vLayout = new QVBoxLayout(this); - QLabel* projectNameLabel = new QLabel(tr("Project Name"), this); - vLayout->addWidget(projectNameLabel); - - m_projectNameLineEdit = new QLineEdit(tr("New Project"), this); - vLayout->addWidget(m_projectNameLineEdit); - - QLabel* projectPathLabel = new QLabel(tr("Project Location"), this); - vLayout->addWidget(projectPathLabel); - + // you cannot remove content margins in qss + vLayout->setContentsMargins(0,0,0,0); + vLayout->setAlignment(Qt::AlignTop); { - QHBoxLayout* projectPathLayout = new QHBoxLayout(this); - - m_projectPathLineEdit = new QLineEdit(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this); - projectPathLayout->addWidget(m_projectPathLineEdit); - - QPushButton* browseButton = new QPushButton(tr("Browse"), this); - connect(browseButton, &QPushButton::pressed, this, &NewProjectSettingsScreen::HandleBrowseButton); - projectPathLayout->addWidget(browseButton); - - vLayout->addLayout(projectPathLayout); - } - - QLabel* projectTemplateLabel = new QLabel(this); - projectTemplateLabel->setText("Project Template"); - vLayout->addWidget(projectTemplateLabel); - - QHBoxLayout* templateLayout = new QHBoxLayout(this); - vLayout->addItem(templateLayout); - - m_projectTemplateButtonGroup = new QButtonGroup(this); - auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); - if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) - { - for (auto projectTemplate : templatesResult.GetValue()) + m_projectName = new FormLineEditWidget(tr("Project name"), tr("New Project"), this); + m_projectName->setErrorLabelText( + tr("A project with this name already exists at this location. Please choose a new name or location.")); + vLayout->addWidget(m_projectName); + + m_projectPath = + new FormBrowseEditWidget(tr("Project Location"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this); + m_projectPath->lineEdit()->setReadOnly(true); + m_projectPath->setErrorLabelText(tr("Please provide a valid path to a folder that exists")); + m_projectPath->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this)); + vLayout->addWidget(m_projectPath); + + // if we don't use a QFrame we cannot "contain" the widgets inside and move them around + // as a group + QFrame* projectTemplateWidget = new QFrame(this); + projectTemplateWidget->setObjectName("projectTemplate"); + QVBoxLayout* containerLayout = new QVBoxLayout(); + containerLayout->setAlignment(Qt::AlignTop); { - QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); - radioButton->setProperty(k_pathProperty, projectTemplate.m_path); - m_projectTemplateButtonGroup->addButton(radioButton); - - templateLayout->addWidget(radioButton); + QLabel* projectTemplateLabel = new QLabel(tr("Select a Project Template")); + projectTemplateLabel->setObjectName("projectTemplateLabel"); + containerLayout->addWidget(projectTemplateLabel); + + QLabel* projectTemplateDetailsLabel = new QLabel(tr("Project templates are pre-configured with relevant Gems that provide " + "additional functionality and content to the project.")); + projectTemplateDetailsLabel->setWordWrap(true); + projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel"); + containerLayout->addWidget(projectTemplateDetailsLabel); + + QHBoxLayout* templateLayout = new QHBoxLayout(this); + containerLayout->addItem(templateLayout); + + m_projectTemplateButtonGroup = new QButtonGroup(this); + m_projectTemplateButtonGroup->setObjectName("templateButtonGroup"); + auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); + if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) + { + for (auto projectTemplate : templatesResult.GetValue()) + { + QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); + radioButton->setProperty(k_pathProperty, projectTemplate.m_path); + m_projectTemplateButtonGroup->addButton(radioButton); + + containerLayout->addWidget(radioButton); + } + + m_projectTemplateButtonGroup->buttons().first()->setChecked(true); + } } - - m_projectTemplateButtonGroup->buttons().first()->setChecked(true); + projectTemplateWidget->setLayout(containerLayout); + vLayout->addWidget(projectTemplateWidget); } + projectSettingsFrame->setLayout(vLayout); - QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - vLayout->addItem(verticalSpacer); + hLayout->addWidget(projectSettingsFrame); - hLayout->addItem(vLayout); + QWidget* projectTemplateDetails = new QWidget(this); + projectTemplateDetails->setObjectName("projectTemplateDetails"); + hLayout->addWidget(projectTemplateDetails); - QWidget* gemsListPlaceholder = new QWidget(this); - gemsListPlaceholder->setFixedWidth(250); - hLayout->addWidget(gemsListPlaceholder); + this->setLayout(hLayout); } ProjectManagerScreen NewProjectSettingsScreen::GetScreenEnum() @@ -96,26 +117,12 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::NewProjectSettings; } - void NewProjectSettingsScreen::HandleBrowseButton() - { - QString defaultPath = m_projectPathLineEdit->text(); - if (defaultPath.isEmpty()) - { - defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - } - - QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("New project path"), defaultPath)); - if (!directory.isEmpty()) - { - m_projectPathLineEdit->setText(directory); - } - } ProjectInfo NewProjectSettingsScreen::GetProjectInfo() { ProjectInfo projectInfo; - projectInfo.m_projectName = m_projectNameLineEdit->text(); - projectInfo.m_path = QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + projectInfo.m_projectName); + projectInfo.m_projectName = m_projectName->lineEdit()->text(); + projectInfo.m_path = QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + projectInfo.m_projectName); return projectInfo; } @@ -127,18 +134,18 @@ namespace O3DE::ProjectManager bool NewProjectSettingsScreen::Validate() { bool projectNameIsValid = true; - if (m_projectNameLineEdit->text().isEmpty()) + if (m_projectName->lineEdit()->text().isEmpty()) { projectNameIsValid = false; } bool projectPathIsValid = true; - if (m_projectPathLineEdit->text().isEmpty()) + if (m_projectPath->lineEdit()->text().isEmpty()) { projectPathIsValid = false; } - QDir path(QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + m_projectNameLineEdit->text())); + QDir path(QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + m_projectName->lineEdit()->text())); if (path.exists() && !path.isEmpty()) { projectPathIsValid = false; diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index 1cfd3c9c35..f0e9609fdc 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -17,10 +17,12 @@ #endif QT_FORWARD_DECLARE_CLASS(QButtonGroup) -QT_FORWARD_DECLARE_CLASS(QLineEdit) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(FormLineEditWidget) + QT_FORWARD_DECLARE_CLASS(FormBrowseEditWidget) + class NewProjectSettingsScreen : public ScreenWidget { @@ -38,8 +40,8 @@ namespace O3DE::ProjectManager void HandleBrowseButton(); private: - QLineEdit* m_projectNameLineEdit; - QLineEdit* m_projectPathLineEdit; + FormLineEditWidget* m_projectName; + FormBrowseEditWidget* m_projectPath; QButtonGroup* m_projectTemplateButtonGroup; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index dada54b1a2..4be876e79f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -31,6 +31,7 @@ namespace O3DE::ProjectManager LabelButton::LabelButton(QWidget* parent) : QLabel(parent) { + setObjectName("labelButton"); m_overlayLabel = new QLabel("", this); m_overlayLabel->setObjectName("labelButtonOverlay"); m_overlayLabel->setWordWrap(true); @@ -75,6 +76,8 @@ namespace O3DE::ProjectManager void ProjectButton::Setup() { + setObjectName("projectButton"); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setSpacing(0); vLayout->setContentsMargins(0, 0, 0, 0); @@ -98,14 +101,21 @@ namespace O3DE::ProjectManager m_deleteProjectAction = newProjectMenu->addAction(tr("Delete the Project")); #endif - m_projectSettingsMenuButton = new QPushButton(this); - m_projectSettingsMenuButton->setText(m_projectName); - m_projectSettingsMenuButton->setMenu(newProjectMenu); - m_projectSettingsMenuButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); - m_projectSettingsMenuButton->setStyleSheet("font-size: 14px; text-align:left;"); - vLayout->addWidget(m_projectSettingsMenuButton); + QFrame* footer = new QFrame(this); + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(0, 0, 0, 0); + footer->setLayout(hLayout); + { + QLabel* projectNameLabel = new QLabel(m_projectName, this); + hLayout->addWidget(projectNameLabel); + + QPushButton* projectMenuButton = new QPushButton(this); + projectMenuButton->setObjectName("projectMenuButton"); + projectMenuButton->setMenu(newProjectMenu); + hLayout->addWidget(projectMenuButton); + } - setFixedSize(s_projectImageWidth, s_projectImageHeight + m_projectSettingsMenuButton->height()); + vLayout->addWidget(footer); connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectName); }); connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectName); }); diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index 43efaa1136..671debf6d0 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -73,7 +73,6 @@ namespace O3DE::ProjectManager QString m_projectName; QString m_projectImagePath; LabelButton* m_projectImageLabel; - QPushButton* m_projectSettingsMenuButton; QAction* m_editProjectAction; QAction* m_editProjectGemsAction; QAction* m_copyProjectAction; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index eb79f2da1e..76bcc2eb99 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -11,52 +11,46 @@ */ #include -#include +#include #include #include #include -#include - namespace O3DE::ProjectManager { ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath) : QMainWindow(parent) - , m_ui(new Ui::ProjectManagerWindowClass()) { - m_ui->setupUi(this); - QLayout* layout = m_ui->centralWidget->layout(); - layout->setMargin(0); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - m_pythonBindings = AZStd::make_unique(engineRootPath); - m_screensCtrl = new ScreensCtrl(); - m_ui->verticalLayout->addWidget(m_screensCtrl); + setWindowTitle(tr("O3DE Project Manager")); - connect(m_ui->projectsMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleProjectsMenu); - connect(m_ui->engineMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleEngineMenu); + ScreensCtrl* screensCtrl = new ScreensCtrl(); + // currently the tab order on the home page is based on the order of this list + QVector screenEnums = + { + ProjectManagerScreen::Projects, + ProjectManagerScreen::EngineSettings, + ProjectManagerScreen::CreateProject, + ProjectManagerScreen::UpdateProject + }; + screensCtrl->BuildScreens(screenEnums); + + setCentralWidget(screensCtrl); + + // setup stylesheets and hot reloading QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast(engineRootPath.Native().size())); const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); const auto qrcPath = QStringLiteral(":/ProjectManager/style"); AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath); + // set stylesheet after creating the screens or their styles won't get updated AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss")); - QVector screenEnums = - { - ProjectManagerScreen::FirstTimeUse, - ProjectManagerScreen::CreateProject, - ProjectManagerScreen::ProjectsHome, - ProjectManagerScreen::UpdateProject, - ProjectManagerScreen::EngineSettings - }; - m_screensCtrl->BuildScreens(screenEnums); - m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::FirstTimeUse, false); + screensCtrl->ForceChangeToScreen(ProjectManagerScreen::Projects, false); } ProjectManagerWindow::~ProjectManagerWindow() @@ -64,13 +58,4 @@ namespace O3DE::ProjectManager m_pythonBindings.reset(); } - void ProjectManagerWindow::HandleProjectsMenu() - { - m_screensCtrl->ChangeToScreen(ProjectManagerScreen::ProjectsHome); - } - void ProjectManagerWindow::HandleEngineMenu() - { - m_screensCtrl->ChangeToScreen(ProjectManagerScreen::EngineSettings); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h index d5c586e59b..74db3467c5 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h @@ -13,17 +13,9 @@ #if !defined(Q_MOC_RUN) #include - -#include - #include #endif -namespace Ui -{ - class ProjectManagerWindowClass; -} - namespace O3DE::ProjectManager { class ProjectManagerWindow @@ -35,13 +27,7 @@ namespace O3DE::ProjectManager explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath); ~ProjectManagerWindow(); - protected slots: - void HandleProjectsMenu(); - void HandleEngineMenu(); - private: - QScopedPointer m_ui; - ScreensCtrl* m_screensCtrl; AZStd::unique_ptr m_pythonBindings; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui deleted file mode 100644 index 633cd61182..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui +++ /dev/null @@ -1,67 +0,0 @@ - - - ProjectManagerWindowClass - - - - 0 - 0 - 1200 - 800 - - - - - 0 - 0 - - - - O3DE Project Manager - - - - - - - - 0 - 0 - 1200 - 36 - - - - - 16 - - - - - Icon - - - - :/o3de_editor.ico:/o3de_editor.ico - - - - - Projects - - - - - Engine - - - - - - - - - - - - diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp deleted file mode 100644 index 6c60685358..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace O3DE::ProjectManager -{ - ProjectsHomeScreen::ProjectsHomeScreen(QWidget* parent) - : ScreenWidget(parent) - { - QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); - vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins); - - QHBoxLayout* topLayout = new QHBoxLayout(); - - QLabel* titleLabel = new QLabel(this); - titleLabel->setText("My Projects"); - titleLabel->setStyleSheet("font-size: 24px"); - topLayout->addWidget(titleLabel); - - QSpacerItem* topSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum); - topLayout->addItem(topSpacer); - - QMenu* newProjectMenu = new QMenu(this); - m_createNewProjectAction = newProjectMenu->addAction("Create New Project"); - m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project"); - - QPushButton* newProjectMenuButton = new QPushButton(this); - newProjectMenuButton->setText("New Project..."); - newProjectMenuButton->setMenu(newProjectMenu); - newProjectMenuButton->setFixedWidth(s_newProjectButtonWidth); - newProjectMenuButton->setStyleSheet("font-size: 14px;"); - topLayout->addWidget(newProjectMenuButton); - - vLayout->addLayout(topLayout); - - // 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(); - QGridLayout* projectGridLayout = new QGridLayout(); - scrollWidget->setLayout(projectGridLayout); - projectsScrollArea->setWidget(scrollWidget); - projectsScrollArea->setWidgetResizable(true); - - int gridIndex = 0; - for (auto project : projectsResult.GetValue()) - { - ProjectButton* projectButton; - QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; - QFileInfo doesPreviewExist(projectPreviewPath); - if (doesPreviewExist.exists() && doesPreviewExist.isFile()) - { - projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this); - } - else - { - projectButton = new ProjectButton(project.m_projectName, this); - } - - // Create rows of projects buttons s_projectButtonRowCount buttons wide - projectGridLayout->addWidget(projectButton, gridIndex / s_projectButtonRowCount, gridIndex % s_projectButtonRowCount); - - connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsHomeScreen::HandleOpenProject); - connect(projectButton, &ProjectButton::EditProject, this, &ProjectsHomeScreen::HandleEditProject); - -#ifdef SHOW_ALL_PROJECT_ACTIONS - connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsHomeScreen::HandleEditProjectGems); - connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsHomeScreen::HandleCopyProject); - connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsHomeScreen::HandleRemoveProject); - connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsHomeScreen::HandleDeleteProject); -#endif - ++gridIndex; - } - - vLayout->addWidget(projectsScrollArea); - } - - // Using border-image allows for scaling options background-image does not support - setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }"); - - connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleNewProjectButton); - connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleAddProjectButton); - } - - ProjectManagerScreen ProjectsHomeScreen::GetScreenEnum() - { - return ProjectManagerScreen::ProjectsHome; - } - - void ProjectsHomeScreen::HandleNewProjectButton() - { - emit ResetScreenRequest(ProjectManagerScreen::CreateProject); - emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); - } - void ProjectsHomeScreen::HandleAddProjectButton() - { - // Do nothing for now - } - void ProjectsHomeScreen::HandleOpenProject(const QString& projectPath) - { - if (!projectPath.isEmpty()) - { - AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); - AZStd::string executableFilename = "Editor"; - AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); - auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = cmdPath; - bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - if (!launchSucceeded) - { - AZ_Error("ProjectManager", false, "Failed to launch editor"); - QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); - } - else - { - // prevent the user from accidentally pressing the button while the editor is launching - // and let them know what's happening - ProjectButton* button = qobject_cast(sender()); - if (button) - { - button->SetButtonEnabled(false); - button->SetButtonOverlayText(tr("Opening Editor...")); - } - - // enable the button after 3 seconds - constexpr int waitTimeInMs = 3000; - QTimer::singleShot(waitTimeInMs, this, [this, button] { - if (button) - { - button->SetButtonEnabled(true); - } - }); - } - } - else - { - AZ_Error("ProjectManager", false, "Cannot open editor because an empty project path was provided"); - QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor because the project path is invalid.")); - } - - } - void ProjectsHomeScreen::HandleEditProject(const QString& projectPath) - { - emit NotifyCurrentProject(projectPath); - emit ResetScreenRequest(ProjectManagerScreen::UpdateProject); - emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); - } - void ProjectsHomeScreen::HandleEditProjectGems(const QString& projectPath) - { - emit NotifyCurrentProject(projectPath); - emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); - } - void ProjectsHomeScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath) - { - // Open file dialog and choose location for copied project then register copy with O3DE - } - void ProjectsHomeScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath) - { - // Unregister Project from O3DE - } - void ProjectsHomeScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath) - { - // Remove project from 03DE and delete from disk - ProjectsHomeScreen::HandleRemoveProject(projectPath); - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp new file mode 100644 index 0000000000..5f1c0e2b36 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -0,0 +1,347 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DISPLAY_PROJECT_DEV_DATA true + +namespace O3DE::ProjectManager +{ + ProjectsScreen::ProjectsScreen(QWidget* parent) + : ScreenWidget(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setAlignment(Qt::AlignTop); + vLayout->setContentsMargins(s_contentMargins, 0, s_contentMargins, 0); + setLayout(vLayout); + + m_background.load(":/Backgrounds/FirstTimeBackgroundImage.jpg"); + + m_stack = new QStackedWidget(this); + + m_firstTimeContent = CreateFirstTimeContent(); + m_stack->addWidget(m_firstTimeContent); + + m_projectsContent = CreateProjectsContent(); + m_stack->addWidget(m_projectsContent); + + vLayout->addWidget(m_stack); + + connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton); + connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton); + } + + QFrame* ProjectsScreen::CreateFirstTimeContent() + { + QFrame* frame = new QFrame(this); + frame->setObjectName("firstTimeContent"); + { + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignTop); + frame->setLayout(layout); + + QLabel* titleLabel = new QLabel(tr("Ready. Set. Create."), this); + titleLabel->setObjectName("titleLabel"); + layout->addWidget(titleLabel); + + QLabel* introLabel = new QLabel(this); + introLabel->setObjectName("introLabel"); + introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project. Not sure what to create? \nExplore what's " + "available by downloading our sample project.")); + layout->addWidget(introLabel); + + QHBoxLayout* buttonLayout = new QHBoxLayout(this); + buttonLayout->setAlignment(Qt::AlignLeft); + buttonLayout->setSpacing(s_spacerSize); + + // use a newline to force the text up + QPushButton* createProjectButton = new QPushButton(tr("Create a Project\n"), this); + createProjectButton->setObjectName("createProjectButton"); + buttonLayout->addWidget(createProjectButton); + + QPushButton* addProjectButton = new QPushButton(tr("Add a Project\n"), this); + addProjectButton->setObjectName("addProjectButton"); + buttonLayout->addWidget(addProjectButton); + + connect(createProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleNewProjectButton); + connect(addProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleAddProjectButton); + + layout->addLayout(buttonLayout); + } + + return frame; + } + + QFrame* ProjectsScreen::CreateProjectsContent() + { + QFrame* frame = new QFrame(this); + frame->setObjectName("projectsContent"); + { + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + layout->setContentsMargins(0, 0, 0, 0); + frame->setLayout(layout); + + QFrame* header = new QFrame(this); + QHBoxLayout* headerLayout = new QHBoxLayout(); + { + QLabel* titleLabel = new QLabel(tr("My Projects"), this); + titleLabel->setObjectName("titleLabel"); + headerLayout->addWidget(titleLabel); + + QMenu* newProjectMenu = new QMenu(this); + m_createNewProjectAction = newProjectMenu->addAction("Create New Project"); + m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project"); + + connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton); + connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton); + + QPushButton* newProjectMenuButton = new QPushButton(tr("New Project..."), this); + newProjectMenuButton->setObjectName("newProjectButton"); + newProjectMenuButton->setMenu(newProjectMenu); + newProjectMenuButton->setDefault(true); + headerLayout->addWidget(newProjectMenuButton); + } + header->setLayout(headerLayout); + + 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(); + + FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(flowLayout); + + projectsScrollArea->setWidget(scrollWidget); + projectsScrollArea->setWidgetResizable(true); + +#ifndef DISPLAY_PROJECT_DEV_DATA + for (auto project : projectsResult.GetValue()) +#else + ProjectInfo project = projectsResult.GetValue().at(0); + for (int i = 0; i < 15; i++) +#endif + { + ProjectButton* projectButton; + QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; + QFileInfo doesPreviewExist(projectPreviewPath); + if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + { + projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this); + } + else + { + projectButton = new ProjectButton(project.m_projectName, this); + } + + flowLayout->addWidget(projectButton); + + connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); + connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); + + #ifdef DISPLAY_PROJECT_DEV_DATA + connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsScreen::HandleEditProjectGems); + connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); + connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); + connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); + #endif + } + + layout->addWidget(projectsScrollArea); + } + } + + return frame; + } + + ProjectManagerScreen ProjectsScreen::GetScreenEnum() + { + return ProjectManagerScreen::Projects; + } + + bool ProjectsScreen::IsTab() + { + return true; + } + + QString ProjectsScreen::GetTabText() + { + return tr("Projects"); + } + + void ProjectsScreen::paintEvent([[maybe_unused]] QPaintEvent* event) + { + // we paint the background here because qss does not support background cover scaling + QPainter painter(this); + + auto winSize = size(); + auto pixmapRatio = (float)m_background.width() / m_background.height(); + auto windowRatio = (float)winSize.width() / winSize.height(); + + if (pixmapRatio > windowRatio) + { + auto newWidth = (int)(winSize.height() * pixmapRatio); + auto offset = (newWidth - winSize.width()) / -2; + painter.drawPixmap(offset, 0, newWidth, winSize.height(), m_background); + } + else + { + auto newHeight = (int)(winSize.width() / pixmapRatio); + painter.drawPixmap(0, 0, winSize.width(), newHeight, m_background); + } + } + + void ProjectsScreen::HandleNewProjectButton() + { + emit ResetScreenRequest(ProjectManagerScreen::CreateProject); + emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); + } + void ProjectsScreen::HandleAddProjectButton() + { + // Do nothing for now + } + void ProjectsScreen::HandleOpenProject(const QString& projectPath) + { + if (!projectPath.isEmpty()) + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZStd::string executableFilename = "Editor"; + AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); + + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = cmdPath; + bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + if (!launchSucceeded) + { + AZ_Error("ProjectManager", false, "Failed to launch editor"); + QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); + } + else + { + // prevent the user from accidentally pressing the button while the editor is launching + // and let them know what's happening + ProjectButton* button = qobject_cast(sender()); + if (button) + { + button->SetButtonEnabled(false); + button->SetButtonOverlayText(tr("Opening Editor...")); + } + + // enable the button after 3 seconds + constexpr int waitTimeInMs = 3000; + QTimer::singleShot(waitTimeInMs, this, [this, button] { + if (button) + { + button->SetButtonEnabled(true); + } + }); + } + } + else + { + AZ_Error("ProjectManager", false, "Cannot open editor because an empty project path was provided"); + QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor because the project path is invalid.")); + } + + } + void ProjectsScreen::HandleEditProject(const QString& projectPath) + { + emit NotifyCurrentProject(projectPath); + emit ResetScreenRequest(ProjectManagerScreen::UpdateProject); + emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + } + void ProjectsScreen::HandleEditProjectGems(const QString& projectPath) + { + emit NotifyCurrentProject(projectPath); + emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); + } + void ProjectsScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath) + { + // Open file dialog and choose location for copied project then register copy with O3DE + } + void ProjectsScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath) + { + // Unregister Project from O3DE + } + void ProjectsScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath) + { + // Remove project from 03DE and delete from disk + ProjectsScreen::HandleRemoveProject(projectPath); + } + + void ProjectsScreen::NotifyCurrentScreen() + { + if (ShouldDisplayFirstTimeContent()) + { + m_stack->setCurrentWidget(m_firstTimeContent); + } + else + { + m_stack->setCurrentWidget(m_projectsContent); + } + } + + bool ProjectsScreen::ShouldDisplayFirstTimeContent() + { + auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); + if (!projectsResult.IsSuccess() || projectsResult.GetValue().isEmpty()) + { + return true; + } + + QSettings settings; + bool displayFirstTimeContent = settings.value("displayFirstTimeContent", true).toBool(); + if (displayFirstTimeContent) + { + settings.setValue("displayFirstTimeContent", false); + } + + return displayFirstTimeContent; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h similarity index 69% rename from Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h rename to Code/Tools/ProjectManager/Source/ProjectsScreen.h index e8d1ac4fb5..d88ba8398d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -15,16 +15,26 @@ #include #endif +QT_FORWARD_DECLARE_CLASS(QPaintEvent) +QT_FORWARD_DECLARE_CLASS(QFrame) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) + namespace O3DE::ProjectManager { - class ProjectsHomeScreen + class ProjectsScreen : public ScreenWidget { public: - explicit ProjectsHomeScreen(QWidget* parent = nullptr); - ~ProjectsHomeScreen() = default; + explicit ProjectsScreen(QWidget* parent = nullptr); + ~ProjectsScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + QString GetTabText() override; + bool IsTab() override; + + protected: + void NotifyCurrentScreen() override; protected slots: void HandleNewProjectButton(); @@ -36,16 +46,24 @@ namespace O3DE::ProjectManager void HandleRemoveProject(const QString& projectPath); void HandleDeleteProject(const QString& projectPath); + void paintEvent(QPaintEvent* event) override; + private: + QFrame* CreateFirstTimeContent(); + QFrame* CreateProjectsContent(); + bool ShouldDisplayFirstTimeContent(); + QAction* m_createNewProjectAction; QAction* m_addExistingProjectAction; + QPixmap m_background; + QFrame* m_firstTimeContent; + QFrame* m_projectsContent; + QStackedWidget* m_stack; const QString m_projectPreviewImagePath = "/preview.png"; + inline constexpr static int s_contentMargins = 80; inline constexpr static int s_spacerSize = 20; - inline constexpr static int s_projectButtonRowCount = 4; - inline constexpr static int s_newProjectButtonWidth = 156; - }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenDefs.h b/Code/Tools/ProjectManager/Source/ScreenDefs.h index 13289e2481..46d243f677 100644 --- a/Code/Tools/ProjectManager/Source/ScreenDefs.h +++ b/Code/Tools/ProjectManager/Source/ScreenDefs.h @@ -17,11 +17,10 @@ namespace O3DE::ProjectManager { Invalid = -1, Empty, - FirstTimeUse, CreateProject, NewProjectSettings, GemCatalog, - ProjectsHome, + Projects, UpdateProject, ProjectSettings, EngineSettings diff --git a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp index d37ccdb59f..b2b4376e14 100644 --- a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp +++ b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp @@ -11,12 +11,11 @@ */ #include -#include #include #include #include #include -#include +#include #include #include @@ -28,9 +27,6 @@ namespace O3DE::ProjectManager switch(screen) { - case (ProjectManagerScreen::FirstTimeUse): - newScreen = new FirstTimeUseScreen(parent); - break; case (ProjectManagerScreen::CreateProject): newScreen = new CreateProjectCtrl(parent); break; @@ -40,8 +36,8 @@ namespace O3DE::ProjectManager case (ProjectManagerScreen::GemCatalog): newScreen = new GemCatalogScreen(parent); break; - case (ProjectManagerScreen::ProjectsHome): - newScreen = new ProjectsHomeScreen(parent); + case (ProjectManagerScreen::Projects): + newScreen = new ProjectsScreen(parent); break; case (ProjectManagerScreen::UpdateProject): newScreen = new UpdateProjectCtrl(parent); diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp new file mode 100644 index 0000000000..29b1eb6ff6 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp @@ -0,0 +1,62 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + ScreenHeader::ScreenHeader(QWidget* parent) + : QFrame(parent) + { + setObjectName("header"); + + QHBoxLayout* layout = new QHBoxLayout(); + layout->setAlignment(Qt::AlignLeft); + layout->setContentsMargins(0,0,0,0); + + m_backButton = new QPushButton(); + m_backButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + layout->addWidget(m_backButton); + + QVBoxLayout* titleLayout = new QVBoxLayout(); + m_title = new QLabel(); + m_title->setObjectName("headerTitle"); + titleLayout->addWidget(m_title); + + m_subTitle = new QLabel(); + m_subTitle->setObjectName("headerSubTitle"); + titleLayout->addWidget(m_subTitle); + + layout->addLayout(titleLayout); + + setLayout(layout); + } + + void ScreenHeader::setTitle(const QString& text) + { + m_title->setText(text); + } + + void ScreenHeader::setSubTitle(const QString& text) + { + m_subTitle->setText(text); + } + + QPushButton* ScreenHeader::backButton() + { + return m_backButton; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h new file mode 100644 index 0000000000..c5fdb56195 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h @@ -0,0 +1,42 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QPushButton) + +namespace O3DE::ProjectManager +{ + class ScreenHeader + : public QFrame + { + Q_OBJECT // AUTOMOC + + public: + ScreenHeader(QWidget* parent = nullptr); + + void setTitle(const QString& text); + void setSubTitle(const QString& text); + + QPushButton* backButton(); + + private: + QLabel* m_title; + QLabel* m_subTitle; + QPushButton* m_backButton; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index e80747d67b..2ad6d30201 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -41,12 +41,27 @@ namespace O3DE::ProjectManager { return true; } + virtual bool IsTab() + { + return false; + } + virtual QString GetTabText() + { + return tr("Missing"); + } + + //! Notify this screen it is the current screen + virtual void NotifyCurrentScreen() + { + + } signals: void ChangeScreenRequest(ProjectManagerScreen screen); void GotoPreviousScreenRequest(); void ResetScreenRequest(ProjectManagerScreen screen); void NotifyCurrentProject(const QString& projectPath); + }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index a77c434026..7d31d02f6c 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -21,17 +22,19 @@ namespace O3DE::ProjectManager ScreensCtrl::ScreensCtrl(QWidget* parent) : QWidget(parent) { + setObjectName("ScreensCtrl"); + QVBoxLayout* vLayout = new QVBoxLayout(); - vLayout->setMargin(0); - vLayout->setSpacing(0); vLayout->setContentsMargins(0, 0, 0, 0); setLayout(vLayout); m_screenStack = new QStackedWidget(); vLayout->addWidget(m_screenStack); - //Track the bottom of the stack - m_screenVisitOrder.push(ProjectManagerScreen::Invalid); + // add a tab widget at the bottom of the stack + m_tabWidget = new QTabWidget(); + m_screenStack->addWidget(m_tabWidget); + connect(m_tabWidget, &QTabWidget::currentChanged, this, &ScreensCtrl::TabChanged); } void ScreensCtrl::BuildScreens(QVector screens) @@ -57,7 +60,14 @@ namespace O3DE::ProjectManager ScreenWidget* ScreensCtrl::GetCurrentScreen() { - return reinterpret_cast(m_screenStack->currentWidget()); + if (m_screenStack->currentWidget() == m_tabWidget) + { + return reinterpret_cast(m_tabWidget->currentWidget()); + } + else + { + return reinterpret_cast(m_screenStack->currentWidget()); + } } bool ScreensCtrl::ChangeToScreen(ProjectManagerScreen screen) @@ -79,13 +89,28 @@ namespace O3DE::ProjectManager if (iterator != m_screenMap.end()) { ScreenWidget* currentScreen = GetCurrentScreen(); - if (currentScreen != iterator.value()) + ScreenWidget* newScreen = iterator.value(); + + if (currentScreen != newScreen) { if (addVisit) { - m_screenVisitOrder.push(currentScreen->GetScreenEnum()); + ProjectManagerScreen oldScreen = currentScreen->GetScreenEnum(); + m_screenVisitOrder.push(oldScreen); + } + + if (newScreen->IsTab()) + { + m_tabWidget->setCurrentWidget(newScreen); + m_screenStack->setCurrentWidget(m_tabWidget); } - m_screenStack->setCurrentWidget(iterator.value()); + else + { + m_screenStack->setCurrentWidget(newScreen); + } + + newScreen->NotifyCurrentScreen(); + return true; } } @@ -95,23 +120,46 @@ namespace O3DE::ProjectManager bool ScreensCtrl::GotoPreviousScreen() { - // Don't go back if we are on the first set screen - if (m_screenVisitOrder.top() != ProjectManagerScreen::Invalid) + if (!m_screenVisitOrder.isEmpty()) { // We do not check with screen if we can go back, we should always be able to go back - return ForceChangeToScreen(m_screenVisitOrder.pop(), false); + ProjectManagerScreen previousScreen = m_screenVisitOrder.pop(); + return ForceChangeToScreen(previousScreen, false); } return false; } void ScreensCtrl::ResetScreen(ProjectManagerScreen screen) { + bool shouldRestoreCurrentScreen = false; + if (GetCurrentScreen() && GetCurrentScreen()->GetScreenEnum() == screen) + { + shouldRestoreCurrentScreen = true; + } + // Delete old screen if it exists to start fresh DeleteScreen(screen); // Add new screen ScreenWidget* newScreen = BuildScreen(this, screen); - m_screenStack->addWidget(newScreen); + if (newScreen->IsTab()) + { + m_tabWidget->addTab(newScreen, newScreen->GetTabText()); + if (shouldRestoreCurrentScreen) + { + m_tabWidget->setCurrentWidget(newScreen); + m_screenStack->setCurrentWidget(m_tabWidget); + } + } + else + { + m_screenStack->addWidget(newScreen); + if (shouldRestoreCurrentScreen) + { + m_screenStack->setCurrentWidget(newScreen); + } + } + m_screenMap.insert(screen, newScreen); connect(newScreen, &ScreenWidget::ChangeScreenRequest, this, &ScreensCtrl::ChangeToScreen); @@ -134,8 +182,21 @@ namespace O3DE::ProjectManager const auto iter = m_screenMap.find(screen); if (iter != m_screenMap.end()) { - m_screenStack->removeWidget(iter.value()); - iter.value()->deleteLater(); + ScreenWidget* screenToDelete = iter.value(); + if (screenToDelete->IsTab()) + { + int tabIndex = m_tabWidget->indexOf(screenToDelete); + if (tabIndex > -1) + { + m_tabWidget->removeTab(tabIndex); + } + } + else + { + // if the screen we delete is the current widget, a new one will + // be selected automatically (randomly?) + m_screenStack->removeWidget(screenToDelete); + } // Erase does not cause a rehash so interators remain valid m_screenMap.erase(iter); @@ -150,4 +211,12 @@ namespace O3DE::ProjectManager } } + void ScreensCtrl::TabChanged([[maybe_unused]] int index) + { + ScreenWidget* screen = reinterpret_cast(m_tabWidget->currentWidget()); + if (screen) + { + screen->NotifyCurrentScreen(); + } + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.h b/Code/Tools/ProjectManager/Source/ScreensCtrl.h index a9d1023b4b..935fc78e25 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.h +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.h @@ -18,6 +18,8 @@ #include #endif +QT_FORWARD_DECLARE_CLASS(QTabWidget) + namespace O3DE::ProjectManager { class ScreenWidget; @@ -46,11 +48,13 @@ namespace O3DE::ProjectManager void ResetAllScreens(); void DeleteScreen(ProjectManagerScreen screen); void DeleteAllScreens(); + void TabChanged(int index); private: QStackedWidget* m_screenStack; QHash m_screenMap; QStack m_screenVisitOrder; + QTabWidget* m_tabWidget; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 84e3d8359d..b3180966ce 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -108,7 +108,7 @@ namespace O3DE::ProjectManager auto result = PythonBindingsInterface::Get()->UpdateProject(m_projectInfo); if (result) { - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { diff --git a/Code/Tools/ProjectManager/Source/main.cpp b/Code/Tools/ProjectManager/Source/main.cpp index 3d8bb71a0c..cbeacbaf65 100644 --- a/Code/Tools/ProjectManager/Source/main.cpp +++ b/Code/Tools/ProjectManager/Source/main.cpp @@ -35,7 +35,6 @@ int main(int argc, char* argv[]) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - AZ::AllocatorInstance::Create(); int runSuccess = 0; { @@ -55,6 +54,12 @@ int main(int argc, char* argv[]) O3DE::ProjectManager::ProjectManagerWindow window(nullptr, engineRootPath); window.show(); + // somethings is preventing us from moving the window to the center of the + // primary screen - likely an Az style or component helper + constexpr int width = 1200; + constexpr int height = 800; + window.resize(width, height); + runSuccess = app.exec(); } AZ::AllocatorInstance::Destroy(); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index a41ddad21e..223465f3c8 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -21,8 +21,6 @@ set(FILES Source/ScreenWidget.h Source/EngineInfo.h Source/EngineInfo.cpp - Source/FirstTimeUseScreen.h - Source/FirstTimeUseScreen.cpp Source/FormLineEditWidget.h Source/FormLineEditWidget.cpp Source/FormBrowseEditWidget.h @@ -33,7 +31,6 @@ set(FILES Source/ProjectManagerWindow.cpp Source/ProjectTemplateInfo.h Source/ProjectTemplateInfo.cpp - Source/ProjectManagerWindow.ui Source/PythonBindings.h Source/PythonBindings.cpp Source/PythonBindingsInterface.h @@ -45,8 +42,8 @@ set(FILES Source/CreateProjectCtrl.cpp Source/UpdateProjectCtrl.h Source/UpdateProjectCtrl.cpp - Source/ProjectsHomeScreen.h - Source/ProjectsHomeScreen.cpp + Source/ProjectsScreen.h + Source/ProjectsScreen.cpp Source/ProjectSettingsScreen.h Source/ProjectSettingsScreen.cpp Source/ProjectSettingsScreen.ui @@ -54,6 +51,8 @@ set(FILES Source/EngineSettingsScreen.cpp Source/ProjectButtonWidget.h Source/ProjectButtonWidget.cpp + Source/ScreenHeaderWidget.h + Source/ScreenHeaderWidget.cpp Source/LinkWidget.h Source/LinkWidget.cpp Source/TagWidget.h diff --git a/Templates/DefaultProject/Template/preview.png b/Templates/DefaultProject/Template/preview.png index 2191a0ebc2..3d4fe78063 100644 --- a/Templates/DefaultProject/Template/preview.png +++ b/Templates/DefaultProject/Template/preview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6 -size 5466 +oid sha256:40949893ed7009eeaa90b7ce6057cb6be9dfaf7b162e3c26ba9dadf985939d7d +size 2038