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
main
Alex Peterson 5 years ago committed by GitHub
parent 9b1be43367
commit 13de9de3c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6
size 5466
oid sha256:40949893ed7009eeaa90b7ce6057cb6be9dfaf7b162e3c26ba9dadf985939d7d
size 2038

@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3H11V11H3V13H11V21H13V13H21V11H13V3Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 246 B

@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3H11V11H3V13H11V21H13V13H21V11H13V3Z" fill="#1e70eb"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 248 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.02554 10L9 15.0362V18L0 9L9 0V2.98885L4 8H18V10H4.02554Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98407 4H2V6V7V19.8145H2.07539L6.02059 7.99123H19V6H11.328L9.98407 4ZM19 19.7473L22.0766 10H7.05436L3.68763 19.8329H18.973L18.9788 19.8145H19V19.7473Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 358 B

@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98407 4H2V6V7V19.8145H2.07539L6.02059 7.99123H19V6H11.328L9.98407 4ZM19 19.7473L22.0766 10H7.05436L3.68763 19.8329H18.973L18.9788 19.8145H19V19.7473Z" fill="#1e70eb"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 360 B

@ -4,6 +4,12 @@
</qresource>
<qresource prefix="/">
<file>Add.svg</file>
<file>AddOffset.svg</file>
<file>AddOffset_Hover.svg</file>
<file>ArrowBack.svg</file>
<file>build.svg</file>
<file>FolderOffset.svg</file>
<file>FolderOffset_Hover.svg</file>
<file>Select_Folder.svg</file>
<file>o3de_editor.ico</file>
<file>Windows.svg</file>
@ -14,6 +20,9 @@
<file>DefaultProjectImage.png</file>
<file>ArrowDownLine.svg</file>
<file>ArrowUpLine.svg</file>
<file>o3de.svg</file>
<file>menu.svg</file>
<file>menu_hover.svg</file>
<file>Backgrounds/FirstTimeBackgroundImage.jpg</file>
<file>ArrowDownLine.svg</file>
<file>ArrowUpLine.svg</file>

@ -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;
}
}

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="16" width="5" height="5" fill="white"/>
<rect x="9.5" y="16" width="5" height="5" fill="white"/>
<rect x="16" y="16" width="5" height="5" fill="white"/>
<rect x="3" y="9.5" width="5" height="5" fill="white"/>
<rect x="9.5" y="9.5" width="5" height="5" fill="white"/>
<rect x="19.415" y="8.58496" width="5" height="5" transform="rotate(60 19.415 8.58496)" fill="white"/>
<rect x="3" y="3" width="5" height="5" fill="white"/>
<rect x="8.58496" y="6.41504" width="5" height="5" transform="rotate(-60 8.58496 6.41504)" fill="white"/>
<rect x="19.5" y="0.964844" width="5" height="5" transform="rotate(45 19.5 0.964844)" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 749 B

@ -0,0 +1,5 @@
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.3335" y="2.66675" width="13.3333" height="1.33333" fill="white"/>
<rect x="1.3335" y="7.33325" width="13.3333" height="1.33333" fill="white"/>
<rect x="1.3335" y="12" width="13.3333" height="1.33333" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

@ -0,0 +1,5 @@
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.3335" y="2.66675" width="13.3333" height="1.33333" fill="#1e70eb"/>
<rect x="1.3335" y="7.33325" width="13.3333" height="1.33333" fill="#1e70eb"/>
<rect x="1.3335" y="12" width="13.3333" height="1.33333" fill="#1e70eb"/>
</svg>

After

Width:  |  Height:  |  Size: 335 B

@ -0,0 +1,3 @@
<svg width="38" height="32" viewBox="0 0 38 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.0335 27.6364C30.2006 30.4503 26.4498 32 22.4729 32H22.4719L22.4007 31.9999C18.3948 31.9813 14.6226 30.396 11.7792 27.5358C7.98134 23.7148 6.67845 18.3374 7.86239 13.445H13.291C12.8522 14.5772 12.6238 15.7937 12.6313 17.0465C12.6466 19.6692 13.682 22.1248 15.5465 23.9605C17.4134 25.7987 19.8692 26.811 22.462 26.811L22.4897 26.8108C25.0998 26.8025 27.555 25.7736 29.4067 23.9136C31.2794 22.0326 32.3023 19.5505 32.2872 16.9246C32.2718 14.2963 31.2187 11.8234 29.3215 9.96131C27.4791 8.153 25.0581 7.16098 22.4942 7.16098C22.4558 7.16098 22.4177 7.16127 22.379 7.16171C20.9514 7.17701 19.5695 7.49947 18.3061 8.09355V2.5551C19.6438 2.16896 21.0401 1.96569 22.4658 1.96569L22.5394 1.96598C26.5556 1.98507 30.3331 3.58486 33.1758 6.47058C35.976 9.31332 37.5035 13.0777 37.4771 17.0703C37.4508 21.0641 35.8727 24.8164 33.0335 27.6364ZM1.73437 2.98423H4.71974V0H1.73437V2.98423ZM0 11.9513H4.68388V7.26925H0V11.9513ZM7.65831 10.0451H14.8796V2.82656H7.65831V10.0451Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -14,11 +14,17 @@
#include <ScreensCtrl.h>
#include <PythonBindingsInterface.h>
#include <NewProjectSettingsScreen.h>
#include <ScreenHeaderWidget.h>
#include <GemCatalog/GemCatalogScreen.h>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
#include <QStackedWidget>
#include <QLabel>
#include <QSizePolicy>
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<ScreenWidget*>(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<ScreenWidget*>(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

@ -12,15 +12,18 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include "ProjectInfo.h"
#include <ScreenWidget.h>
#include <ScreensCtrl.h>
#include <QPushButton>
#include <ProjectInfo.h>
#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<ProjectManagerScreen> m_screensOrder;
QString m_projectTemplatePath;
ProjectInfo m_projectInfo;

@ -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

@ -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();

@ -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 <FirstTimeUseScreen.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QIcon>
#include <QSpacerItem>
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("<html><head/><body><p>Welcome to O3DE! Start something new by creating a project. Not sure what to create? </p><p>Explore what\342\200\231s available by downloading our sample project.</p></body></html>"));
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

@ -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 <ScreenWidget.h>
#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

@ -12,6 +12,9 @@
#include <NewProjectSettingsScreen.h>
#include <PythonBindingsInterface.h>
#include <FormLineEditWidget.h>
#include <FormBrowseEditWidget.h>
#include <PathValidator.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
@ -23,6 +26,7 @@
#include <QPushButton>
#include <QSpacerItem>
#include <QStandardPaths>
#include <QFrame>
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;

@ -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;
};

@ -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); });

@ -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;

@ -11,52 +11,46 @@
*/
#include <ProjectManagerWindow.h>
#include <ScreenFactory.h>
#include <ScreensCtrl.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzCore/IO/Path/Path.h>
#include <QDir>
#include <Source/ui_ProjectManagerWindow.h>
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<PythonBindings>(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<ProjectManagerScreen> 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<int>(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<ProjectManagerScreen> 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

@ -13,17 +13,9 @@
#if !defined(Q_MOC_RUN)
#include <QMainWindow>
#include <ScreensCtrl.h>
#include <PythonBindings.h>
#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<Ui::ProjectManagerWindowClass> m_ui;
ScreensCtrl* m_screensCtrl;
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
};

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProjectManagerWindowClass</class>
<widget class="QMainWindow" name="ProjectManagerWindowClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>800</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>O3DE Project Manager</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>36</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<widget class="QMenu" name="iconMenu">
<property name="title">
<string>Icon</string>
</property>
<property name="icon">
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/o3de_editor.ico</normaloff>:/o3de_editor.ico</iconset>
</property>
</widget>
<widget class="QMenu" name="projectsMenu">
<property name="title">
<string>Projects</string>
</property>
</widget>
<widget class="QMenu" name="engineMenu">
<property name="title">
<string>Engine</string>
</property>
</widget>
<addaction name="iconMenu"/>
<addaction name="projectsMenu"/>
<addaction name="engineMenu"/>
</widget>
</widget>
<resources>
<include location="../Resources/ProjectManager.qrc"/>
</resources>
<connections/>
</ui>

@ -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 <ProjectsHomeScreen.h>
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <AzCore/Platform.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/AzFramework_Traits_Platform.h>
#include <AzFramework/Process/ProcessCommon.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzCore/Utils/Utils.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QListView>
#include <QSpacerItem>
#include <QListWidget>
#include <QListWidgetItem>
#include <QFileInfo>
#include <QScrollArea>
#include <QMessageBox>
#include <QTimer>
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<ProjectButton*>(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

@ -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 <ProjectsScreen.h>
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <AzQtComponents/Components/FlowLayout.h>
#include <AzCore/Platform.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/AzFramework_Traits_Platform.h>
#include <AzFramework/Process/ProcessCommon.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzCore/Utils/Utils.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QListView>
#include <QSpacerItem>
#include <QListWidget>
#include <QListWidgetItem>
#include <QFileInfo>
#include <QScrollArea>
#include <QStackedWidget>
#include <QFrame>
#include <QIcon>
#include <QPixmap>
#include <QSettings>
#include <QMessageBox>
#include <QTimer>
//#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<ProjectButton*>(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

@ -15,16 +15,26 @@
#include <ScreenWidget.h>
#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

@ -17,11 +17,10 @@ namespace O3DE::ProjectManager
{
Invalid = -1,
Empty,
FirstTimeUse,
CreateProject,
NewProjectSettings,
GemCatalog,
ProjectsHome,
Projects,
UpdateProject,
ProjectSettings,
EngineSettings

@ -11,12 +11,11 @@
*/
#include <ScreenFactory.h>
#include <FirstTimeUseScreen.h>
#include <CreateProjectCtrl.h>
#include <UpdateProjectCtrl.h>
#include <NewProjectSettingsScreen.h>
#include <GemCatalog/GemCatalogScreen.h>
#include <ProjectsHomeScreen.h>
#include <ProjectsScreen.h>
#include <ProjectSettingsScreen.h>
#include <EngineSettingsScreen.h>
@ -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);

@ -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 <ScreenHeaderWidget.h>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
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

@ -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 <QFrame>
#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

@ -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

@ -14,6 +14,7 @@
#include <ScreenFactory.h>
#include <ScreenWidget.h>
#include <QTabWidget>
#include <QVBoxLayout>
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<ProjectManagerScreen> screens)
@ -57,7 +60,14 @@ namespace O3DE::ProjectManager
ScreenWidget* ScreensCtrl::GetCurrentScreen()
{
return reinterpret_cast<ScreenWidget*>(m_screenStack->currentWidget());
if (m_screenStack->currentWidget() == m_tabWidget)
{
return reinterpret_cast<ScreenWidget*>(m_tabWidget->currentWidget());
}
else
{
return reinterpret_cast<ScreenWidget*>(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<ScreenWidget*>(m_tabWidget->currentWidget());
if (screen)
{
screen->NotifyCurrentScreen();
}
}
} // namespace O3DE::ProjectManager

@ -18,6 +18,8 @@
#include <QStack>
#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<ProjectManagerScreen, ScreenWidget*> m_screenMap;
QStack<ProjectManagerScreen> m_screenVisitOrder;
QTabWidget* m_tabWidget;
};
} // namespace O3DE::ProjectManager

@ -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
{

@ -35,7 +35,6 @@ int main(int argc, char* argv[])
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
AZ::AllocatorInstance<AZ::SystemAllocator>::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<AZ::SystemAllocator>::Destroy();

@ -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

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6
size 5466
oid sha256:40949893ed7009eeaa90b7ce6057cb6be9dfaf7b162e3c26ba9dadf985939d7d
size 2038

Loading…
Cancel
Save