Engine settings UX update and basic functionality

main
Alex Peterson 5 years ago committed by GitHub
parent d20f3d8bd4
commit d112ae403b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3H11V11H3V13H11V21H13V13H21V11H13V3Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 209 B

@ -0,0 +1,16 @@
<RCC>
<qresource prefix="/ProjectManager/style">
<file>ProjectManager.qss</file>
</qresource>
<qresource prefix="/">
<file>Add.svg</file>
<file>Select_Folder.svg</file>
<file>o3de_editor.ico</file>
<file>Windows.svg</file>
<file>Android.svg</file>
<file>iOS.svg</file>
<file>Linux.svg</file>
<file>macOS.svg</file>
<file>Backgrounds/FirstTimeBackgroundImage.jpg</file>
</qresource>
</RCC>

@ -0,0 +1,73 @@
/************** General (MainWindow) **************/
QMainWindow {
background-color: #333333;
}
QPushButton:focus {
outline: none;
border:1px solid #1e70eb;
}
/************** General (Forms) **************/
#formLineEditWidget,
#formBrowseEditWidget {
max-width: 780px;
}
#formFrame {
max-width: 720px;
background-color: #444444;
border:1px solid #dddddd;
border-radius: 4px;
padding: 0px 10px 2px 6px;
margin-top:10px;
margin-left:30px;
}
#formFrame[Focus="true"] {
border:1px solid #1e70eb;
}
#formFrame[Valid="false"] {
border:1px solid red;
}
#formFrame QLabel {
font-size: 13px;
color: #cccccc;
}
#formFrame QPushButton {
background-color: transparent;
background:transparent url(:/Select_Folder.svg) no-repeat center;
qproperty-flat: true;
}
#formFrame QPushButton:focus {
border:none;
}
#formFrame QLineEdit {
background-color: rgba(0,0,0,0);
font-size: 18px;
color: #ffffff;
border:0;
line-height: 30px;
height: 1em;
padding-top: -4px;
}
#formErrorLabel {
color: #ec3030;
font-size: 14px;
margin-left: 40px;
}
#formTitleLabel {
font-size:21px;
color:#ffffff;
margin: 10px 0 10px 30px;
}

@ -1,4 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" fill="#444444"/>
<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"/>
</svg>

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 321 B

@ -14,8 +14,16 @@
namespace O3DE::ProjectManager
{
EngineInfo::EngineInfo(const QString& path)
EngineInfo::EngineInfo(const QString& path, const QString& name, const QString& version, const QString& thirdPartyPath)
: m_path(path)
, m_name(name)
, m_version(version)
, m_thirdPartyPath(thirdPartyPath)
{
}
bool EngineInfo::IsValid() const
{
return !m_path.isEmpty();
}
} // namespace O3DE::ProjectManager

@ -22,8 +22,20 @@ namespace O3DE::ProjectManager
{
public:
EngineInfo() = default;
EngineInfo(const QString& path);
EngineInfo(const QString& path, const QString& name, const QString& version, const QString& thirdPartyPath);
// from engine.json
QString m_version;
QString m_name;
QString m_thirdPartyPath;
// from o3de_manifest.json
QString m_path;
QString m_defaultProjectsFolder;
QString m_defaultGemsFolder;
QString m_defaultTemplatesFolder;
QString m_defaultRestrictedFolder;
bool IsValid() const;
};
} // namespace O3DE::ProjectManager

@ -11,20 +11,99 @@
*/
#include <EngineSettingsScreen.h>
#include <Source/ui_EngineSettingsScreen.h>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <FormLineEditWidget.h>
#include <FormBrowseEditWidget.h>
#include <PythonBindingsInterface.h>
#include <PathValidator.h>
namespace O3DE::ProjectManager
{
EngineSettingsScreen::EngineSettingsScreen(QWidget* parent)
: ScreenWidget(parent)
, m_ui(new Ui::EngineSettingsClass())
{
m_ui->setupUi(this);
auto* layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignTop);
setObjectName("engineSettingsScreen");
EngineInfo engineInfo;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
}
QLabel* formTitleLabel = new QLabel(tr("O3DE Settings"), this);
formTitleLabel->setObjectName("formTitleLabel");
layout->addWidget(formTitleLabel);
m_engineVersion = new FormLineEditWidget(tr("Engine Version"), engineInfo.m_version, this);
m_engineVersion->lineEdit()->setReadOnly(true);
layout->addWidget(m_engineVersion);
m_thirdParty = new FormBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this);
m_thirdParty->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
m_thirdParty->lineEdit()->setReadOnly(true);
m_thirdParty->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
connect(m_thirdParty->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
layout->addWidget(m_thirdParty);
m_defaultProjects = new FormBrowseEditWidget(tr("Default Projects Folder"), engineInfo.m_defaultProjectsFolder, this);
m_defaultProjects->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
m_defaultProjects->lineEdit()->setReadOnly(true);
m_defaultProjects->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
connect(m_defaultProjects->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
layout->addWidget(m_defaultProjects);
m_defaultGems = new FormBrowseEditWidget(tr("Default Gems Folder"), engineInfo.m_defaultGemsFolder, this);
m_defaultGems->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
m_defaultGems->lineEdit()->setReadOnly(true);
m_defaultGems->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
connect(m_defaultGems->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
layout->addWidget(m_defaultGems);
m_defaultProjectTemplates = new FormBrowseEditWidget(tr("Default Project Templates Folder"), engineInfo.m_defaultTemplatesFolder, this);
m_defaultProjectTemplates->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
m_defaultProjectTemplates->lineEdit()->setReadOnly(true);
m_defaultProjectTemplates->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
connect(m_defaultProjectTemplates->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
layout->addWidget(m_defaultProjectTemplates);
setLayout(layout);
}
ProjectManagerScreen EngineSettingsScreen::GetScreenEnum()
{
return ProjectManagerScreen::EngineSettings;
}
void EngineSettingsScreen::OnTextChanged()
{
// save engine settings
auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
EngineInfo engineInfo;
engineInfo = engineInfoResult.GetValue();
engineInfo.m_thirdPartyPath = m_thirdParty->lineEdit()->text();
engineInfo.m_defaultProjectsFolder = m_defaultProjects->lineEdit()->text();
engineInfo.m_defaultGemsFolder = m_defaultGems->lineEdit()->text();
engineInfo.m_defaultTemplatesFolder = m_defaultProjectTemplates->lineEdit()->text();
bool result = PythonBindingsInterface::Get()->SetEngineInfo(engineInfo);
if (!result)
{
QMessageBox::critical(this, tr("Engine Settings"), tr("Failed to save engine settings."));
}
}
else
{
QMessageBox::critical(this, tr("Engine Settings"), tr("Failed to get engine settings."));
}
}
} // namespace O3DE::ProjectManager

@ -15,13 +15,11 @@
#include <ScreenWidget.h>
#endif
namespace Ui
{
class EngineSettingsClass;
}
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(FormLineEditWidget)
QT_FORWARD_DECLARE_CLASS(FormBrowseEditWidget)
class EngineSettingsScreen
: public ScreenWidget
{
@ -30,8 +28,15 @@ namespace O3DE::ProjectManager
~EngineSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
protected slots:
void OnTextChanged();
private:
QScopedPointer<Ui::EngineSettingsClass> m_ui;
FormLineEditWidget* m_engineVersion;
FormBrowseEditWidget* m_thirdParty;
FormBrowseEditWidget* m_defaultProjects;
FormBrowseEditWidget* m_defaultGems;
FormBrowseEditWidget* m_defaultProjectTemplates;
};
} // namespace O3DE::ProjectManager

@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EngineSettingsClass</class>
<widget class="QWidget" name="EngineSettingsClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>839</width>
<height>597</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>O3DE Settings</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Engine Version</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>v1.01</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>3rd Party Software Folder</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Restricted Folder</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_2"/>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Default Gems Folder</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_3"/>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Default Project Templates Folder</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_4"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -49,11 +49,11 @@ namespace O3DE::ProjectManager
QHBoxLayout* buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(s_buttonSpacing);
m_createProjectButton = CreateLargeBoxButton(QIcon(":/Resources/Add.svg"), tr("Create Project"), this);
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(":/Resources/Select_Folder.svg"), tr("Add a Project"), this);
m_addProjectButton = CreateLargeBoxButton(QIcon(":/Select_Folder.svg"), tr("Add a Project"), this);
m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
buttonLayout->addWidget(m_addProjectButton);
@ -66,7 +66,7 @@ namespace O3DE::ProjectManager
vLayout->addItem(verticalSpacer);
// Using border-image allows for scaling options background-image does not support
setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Resources/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }");
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);

@ -0,0 +1,49 @@
/*
* 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 <FormBrowseEditWidget.h>
#include <AzQtComponents/Components/StyledLineEdit.h>
#include <QPushButton>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QLineEdit>
#include <QStandardPaths>
#include <QIcon>
namespace O3DE::ProjectManager
{
FormBrowseEditWidget::FormBrowseEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
: FormLineEditWidget(labelText, valueText, parent)
{
setObjectName("formBrowseEditWidget");
QPushButton* browseButton = new QPushButton(this);
connect(browseButton, &QPushButton::pressed, this, &FormBrowseEditWidget::HandleBrowseButton);
m_frameLayout->addWidget(browseButton);
}
void FormBrowseEditWidget::HandleBrowseButton()
{
QString defaultPath = m_lineEdit->text();
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
if (!directory.isEmpty())
{
m_lineEdit->setText(directory);
}
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,33 @@
/*
* 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 <FormLineEditWidget.h>
#endif
namespace O3DE::ProjectManager
{
class FormBrowseEditWidget
: public FormLineEditWidget
{
Q_OBJECT
public:
explicit FormBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
~FormBrowseEditWidget() = default;
private slots:
void HandleBrowseButton();
};
} // namespace O3DE::ProjectManager

@ -0,0 +1,123 @@
/*
* 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 <FormLineEditWidget.h>
#include <AzQtComponents/Components/StyledLineEdit.h>
#include <AzQtComponents/Components/Widgets/LineEdit.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QFrame>
#include <QValidator>
#include <QStyle>
namespace O3DE::ProjectManager
{
FormLineEditWidget::FormLineEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
: QWidget(parent)
{
setObjectName("formLineEditWidget");
QVBoxLayout* mainLayout = new QVBoxLayout();
mainLayout->setAlignment(Qt::AlignTop);
{
m_frame = new QFrame(this);
m_frame->setObjectName("formFrame");
// use a horizontal box layout so buttons can be added to the right of the field
m_frameLayout = new QHBoxLayout();
{
QVBoxLayout* fieldLayout = new QVBoxLayout();
QLabel* label = new QLabel(labelText, this);
fieldLayout->addWidget(label);
m_lineEdit = new AzQtComponents::StyledLineEdit(this);
m_lineEdit->setFlavor(AzQtComponents::StyledLineEdit::Question);
AzQtComponents::LineEdit::setErrorIconEnabled(m_lineEdit, false);
m_lineEdit->setText(valueText);
connect(m_lineEdit, &AzQtComponents::StyledLineEdit::flavorChanged, this, &FormLineEditWidget::flavorChanged);
connect(m_lineEdit, &AzQtComponents::StyledLineEdit::onFocus, this, &FormLineEditWidget::onFocus);
connect(m_lineEdit, &AzQtComponents::StyledLineEdit::onFocusOut, this, &FormLineEditWidget::onFocusOut);
m_lineEdit->setFrame(false);
fieldLayout->addWidget(m_lineEdit);
m_frameLayout->addLayout(fieldLayout);
QWidget* emptyWidget = new QWidget(this);
m_frameLayout->addWidget(emptyWidget);
}
m_frame->setLayout(m_frameLayout);
mainLayout->addWidget(m_frame);
m_errorLabel = new QLabel(this);
m_errorLabel->setObjectName("formErrorLabel");
m_errorLabel->setVisible(false);
mainLayout->addWidget(m_errorLabel);
}
setLayout(mainLayout);
}
void FormLineEditWidget::setErrorLabelText(const QString& labelText)
{
m_errorLabel->setText(labelText);
}
QLineEdit* FormLineEditWidget::lineEdit() const
{
return m_lineEdit;
}
void FormLineEditWidget::flavorChanged()
{
if (m_lineEdit->flavor() == AzQtComponents::StyledLineEdit::Flavor::Invalid)
{
m_frame->setProperty("Valid", false);
m_errorLabel->setVisible(true);
}
else
{
m_frame->setProperty("Valid", true);
m_errorLabel->setVisible(false);
}
refreshStyle();
}
void FormLineEditWidget::onFocus()
{
m_frame->setProperty("Focus", true);
refreshStyle();
}
void FormLineEditWidget::onFocusOut()
{
m_frame->setProperty("Focus", false);
refreshStyle();
}
void FormLineEditWidget::refreshStyle()
{
// we must unpolish/polish every child after changing a property
// or else they won't use the correct stylesheet selector
for (auto child : findChildren<QWidget*>())
{
child->style()->unpolish(child);
child->style()->polish(child);
}
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,60 @@
/*
* 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 <QWidget>
#endif
QT_FORWARD_DECLARE_CLASS(QLineEdit)
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QFrame)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
namespace AzQtComponents
{
class StyledLineEdit;
}
namespace O3DE::ProjectManager
{
class FormLineEditWidget
: public QWidget
{
Q_OBJECT
public:
explicit FormLineEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
~FormLineEditWidget() = default;
//! Set the error message for to display when invalid.
void setErrorLabelText(const QString& labelText);
//! Returns a pointer to the underlying LineEdit.
QLineEdit* lineEdit() const;
protected:
QLabel* m_errorLabel = nullptr;
QFrame* m_frame = nullptr;
QHBoxLayout* m_frameLayout = nullptr;
AzQtComponents::StyledLineEdit* m_lineEdit = nullptr;
private slots:
void flavorChanged();
void onFocus();
void onFocusOut();
private:
void refreshStyle();
};
} // namespace O3DE::ProjectManager

@ -22,11 +22,11 @@ namespace O3DE::ProjectManager
: QStyledItemDelegate(parent)
, m_gemModel(gemModel)
{
AddPlatformIcon(GemInfo::Android, ":/Resources/Android.svg");
AddPlatformIcon(GemInfo::iOS, ":/Resources/iOS.svg");
AddPlatformIcon(GemInfo::Linux, ":/Resources/Linux.svg");
AddPlatformIcon(GemInfo::macOS, ":/Resources/macOS.svg");
AddPlatformIcon(GemInfo::Windows, ":/Resources/Windows.svg");
AddPlatformIcon(GemInfo::Android, ":/Android.svg");
AddPlatformIcon(GemInfo::iOS, ":/iOS.svg");
AddPlatformIcon(GemInfo::Linux, ":/Linux.svg");
AddPlatformIcon(GemInfo::macOS, ":/macOS.svg");
AddPlatformIcon(GemInfo::Windows, ":/Windows.svg");
}
void GemItemDelegate::AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath)

@ -0,0 +1,65 @@
/*
* 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 "PathValidator.h"
#include <QWidget>
#include <QFileInfo>
#include <QDir>
namespace O3DE::ProjectManager
{
PathValidator::PathValidator(PathMode pathMode, QWidget* parent)
: QValidator(parent)
, m_pathMode(pathMode)
{
}
void PathValidator::setAllowEmpty(bool allowEmpty)
{
m_allowEmpty = allowEmpty;
}
void PathValidator::setPathMode(PathMode pathMode)
{
m_pathMode = pathMode;
}
QValidator::State PathValidator::validate(QString &text, int &) const
{
if(text.isEmpty())
{
return m_allowEmpty ? QValidator::Acceptable : QValidator::Intermediate;
}
QFileInfo pathInfo(text);
if(!pathInfo.dir().exists())
{
return QValidator::Intermediate;
}
switch(m_pathMode)
{
case PathMode::AnyFile://acceptable, as long as it's not an directoy
return pathInfo.isDir() ? QValidator::Intermediate : QValidator::Acceptable;
case PathMode::ExistingFile://must be an existing file
return pathInfo.exists() && pathInfo.isFile() ? QValidator::Acceptable : QValidator::Intermediate;
case PathMode::ExistingFolder://must be an existing folder
return pathInfo.exists() && pathInfo.isDir() ? QValidator::Acceptable : QValidator::Intermediate;
default:
Q_UNREACHABLE();
}
return QValidator::Invalid;
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,45 @@
/*
* 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 <QValidator>
#endif
QT_FORWARD_DECLARE_CLASS(QWidget)
namespace O3DE::ProjectManager
{
class PathValidator
: public QValidator
{
public:
enum class PathMode {
ExistingFile, //!< A single, existings file. Useful for "Open file"
ExistingFolder, //!< A single, existing directory. Useful for "Open Folder"
AnyFile //!< A single, valid file, doesn't have to exist but the directory must. Useful for "Save File"
};
explicit PathValidator(PathMode pathMode, QWidget* parent = nullptr);
~PathValidator() = default;
void setAllowEmpty(bool allowEmpty);
void setPathMode(PathMode pathMode);
QValidator::State validate(QString &text, int &) const override;
private:
PathMode m_pathMode = PathMode::AnyFile;
bool m_allowEmpty = false;
};
} // namespace O3DE::ProjectManager

@ -44,10 +44,10 @@ namespace O3DE::ProjectManager
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(":/ProjectManagerWindow");
AzQtComponents::StyleManager::addSearchPaths("projectmanagerwindow", pathOnDisk, qrcPath, engineRootPath);
const auto qrcPath = QStringLiteral(":/ProjectManager/style");
AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath);
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("projectlauncherwindow:ProjectManagerWindow.qss"));
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss"));
QVector<ProjectManagerScreen> screenEnums =
{

@ -41,8 +41,8 @@
<string>Icon</string>
</property>
<property name="icon">
<iconset resource="../project_manager.qrc">
<normaloff>:/Resources/o3de_editor.ico</normaloff>:/Resources/o3de_editor.ico</iconset>
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/o3de_editor.ico</normaloff>:/o3de_editor.ico</iconset>
</property>
</widget>
<widget class="QMenu" name="projectsMenu">
@ -61,7 +61,7 @@
</widget>
</widget>
<resources>
<include location="../project_manager.qrc"/>
<include location="../Resources/ProjectManager.qrc"/>
</resources>
<connections/>
</ui>

@ -106,6 +106,7 @@ namespace O3DE::ProjectManager
auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo);
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);
}
else

@ -48,8 +48,8 @@
<string/>
</property>
<property name="icon">
<iconset resource="../project_manager.qrc">
<normaloff>:/Resources/Add.svg</normaloff>:/Resources/Add.svg</iconset>
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/Add.svg</normaloff>:/Add.svg</iconset>
</property>
</widget>
</item>
@ -65,8 +65,8 @@
<string/>
</property>
<property name="icon">
<iconset resource="../project_manager.qrc">
<normaloff>:/Resources/Select_Folder.svg</normaloff>:/Resources/Select_Folder.svg</iconset>
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/Select_Folder.svg</normaloff>:/Select_Folder.svg</iconset>
</property>
</widget>
</item>
@ -131,7 +131,7 @@
</layout>
</widget>
<resources>
<include location="../project_manager.qrc"/>
<include location="../Resources/ProjectManager.qrc"/>
</resources>
<connections/>
</ui>

@ -327,13 +327,92 @@ namespace O3DE::ProjectManager
}
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo()
{
EngineInfo engineInfo;
bool result = ExecuteWithLock([&] {
pybind11::str enginePath = m_registration.attr("get_this_engine_path")();
auto o3deData = m_registration.attr("load_o3de_manifest")();
if (pybind11::isinstance<pybind11::dict>(o3deData))
{
engineInfo.m_path = Py_To_String(enginePath);
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]);
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path","");
}
auto engineData = m_registration.attr("get_engine_data")(pybind11::none(), enginePath);
if (pybind11::isinstance<pybind11::dict>(engineData))
{
try
{
engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE");
}
catch ([[maybe_unused]] const std::exception& e)
{
AZ_Warning("PythonBindings", false, "Failed to get EngineInfo from %s", Py_To_String(enginePath));
}
}
});
if (!result || !engineInfo.IsValid())
{
return AZ::Failure();
}
else
{
return AZ::Success(AZStd::move(engineInfo));
}
bool PythonBindings::SetEngineInfo([[maybe_unused]] const EngineInfo& engineInfo)
return AZ::Failure();
}
bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo)
{
return false;
bool result = ExecuteWithLock([&] {
pybind11::str enginePath = engineInfo.m_path.toStdString();
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString();
auto registrationResult = m_registration.attr("register")(
enginePath, // engine_path
pybind11::none(), // project_path
pybind11::none(), // gem_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
defaultProjectsFolder,
defaultGemsFolder,
defaultTemplatesFolder
);
if (registrationResult.cast<int>() != 0)
{
result = false;
}
auto manifest = m_registration.attr("load_o3de_manifest")();
if (pybind11::isinstance<pybind11::dict>(manifest))
{
try
{
manifest["third_party_path"] = engineInfo.m_thirdPartyPath.toStdString();
m_registration.attr("save_o3de_manifest")(manifest);
}
catch ([[maybe_unused]] const std::exception& e)
{
AZ_Warning("PythonBindings", false, "Failed to set third party path.");
}
}
});
return result;
}
AZ::Outcome<GemInfo> PythonBindings::GetGem(const QString& path)

@ -1,16 +0,0 @@
<RCC>
<qresource prefix="/">
<file>Resources/ProjectManager.qss</file>
<file>Resources/Add.svg</file>
<file>Resources/Select_Folder.svg</file>
<file>Resources/o3de_editor.ico</file>
<file>Resources/Windows.svg</file>
<file>Resources/Android.svg</file>
<file>Resources/iOS.svg</file>
<file>Resources/Linux.svg</file>
<file>Resources/macOS.svg</file>
<file>Resources/ArrowDownLine.svg</file>
<file>Resources/ArrowUpLine.svg</file>
<file>Resources/Backgrounds/FirstTimeBackgroundImage.jpg</file>
</qresource>
</RCC>

@ -10,7 +10,8 @@
#
set(FILES
project_manager.qrc
Resources/ProjectManager.qrc
Resources/ProjectManager.qss
Source/main.cpp
Source/ScreenDefs.h
Source/ScreenFactory.h
@ -22,6 +23,12 @@ set(FILES
Source/EngineInfo.cpp
Source/FirstTimeUseScreen.h
Source/FirstTimeUseScreen.cpp
Source/FormLineEditWidget.h
Source/FormLineEditWidget.cpp
Source/FormBrowseEditWidget.h
Source/FormBrowseEditWidget.cpp
Source/PathValidator.h
Source/PathValidator.cpp
Source/ProjectManagerWindow.h
Source/ProjectManagerWindow.cpp
Source/ProjectTemplateInfo.h
@ -44,7 +51,6 @@ set(FILES
Source/ProjectSettingsScreen.ui
Source/EngineSettingsScreen.h
Source/EngineSettingsScreen.cpp
Source/EngineSettingsScreen.ui
Source/LinkWidget.h
Source/LinkWidget.cpp
Source/TagWidget.h

@ -2118,27 +2118,35 @@ def find_engine_data(json_data: dict,
return None
def get_engine_data(engine_name: str = None,
engine_path: str or pathlib.Path = None, ) -> dict or None:
def _validate_engine_name_and_path(engine_name: str = None,
engine_path: str or pathlib.Path = None) -> pathlib.Path or None:
if not engine_name and not engine_path:
logger.error('Must specify either a Engine name or Engine Path.')
return 1
return None
if engine_name and not engine_path:
engine_path = get_registered(engine_name=engine_name)
if not engine_path:
logger.error(f'Engine Path {engine_path} has not been registered.')
return 1
return None
engine_path = pathlib.Path(engine_path).resolve()
engine_json = engine_path / 'engine.json'
if not engine_json.is_file():
logger.error(f'Engine json {engine_json} is not present.')
return 1
return None
if not valid_o3de_engine_json(engine_json):
logger.error(f'Engine json {engine_json} is not valid.')
return 1
return None
return engine_json
def get_engine_data(engine_name: str = None,
engine_path: str or pathlib.Path = None ) -> dict or None:
engine_json = _validate_engine_name_and_path(engine_name, engine_path)
if not engine_json:
return None
with engine_json.open('r') as f:
try:
@ -2150,6 +2158,26 @@ def get_engine_data(engine_name: str = None,
return None
def set_engine_data(engine_name: str = None,
engine_path: str or pathlib.Path = None,
engine_data: dict = None ) -> int:
if not engine_data:
logger.error('Must provide engine data.')
return 1
engine_json = _validate_engine_name_and_path(engine_name, engine_path)
if not engine_json:
return 1
with engine_json.open('w') as f:
try:
json.dump(engine_data, f, indent=4)
except Exception as e:
logger.warn(f'Failed to load or write {engine_json}: {str(e)}')
return 1
return 0
def get_project_data(project_name: str = None,
project_path: str or pathlib.Path = None, ) -> dict or None:
@ -2184,27 +2212,36 @@ def get_project_data(project_name: str = None,
return None
def get_gem_data(gem_name: str = None,
gem_path: str or pathlib.Path = None, ) -> dict or None:
def _validate_gem_name_and_path(gem_name: str = None,
gem_path: str or pathlib.Path = None) -> pathlib.Path or None:
if not gem_name and not gem_path:
logger.error('Must specify either a Gem name or Gem Path.')
return 1
return None
if gem_name and not gem_path:
gem_path = get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Gem Path {gem_path} has not been registered.')
return 1
return None
gem_path = pathlib.Path(gem_path).resolve()
gem_json = gem_path / 'gem.json'
if not gem_json.is_file():
logger.error(f'Gem json {gem_json} is not present.')
return 1
return None
if not valid_o3de_gem_json(gem_json):
logger.error(f'Gem json {gem_json} is not valid.')
return 1
return None
return gem_json
def get_gem_data(gem_name: str = None,
gem_path: str or pathlib.Path = None) -> dict or None:
gem_json = _validate_gem_name_and_path(gem_name, gem_path)
if not gem_json:
return None
with gem_json.open('r') as f:
try:
@ -2217,6 +2254,27 @@ def get_gem_data(gem_name: str = None,
return None
def set_gem_data(gem_name: str = None,
gem_path: str or pathlib.Path = None,
gem_data: dict = None) -> int:
if not gem_data:
logger.error('Must provide Gem data.')
return 1
gem_json = _validate_gem_name_and_path(gem_name, gem_path)
if not gem_json:
return 1
with gem_json.open('w') as f:
try:
json.dump(gem_data, f, indent=4)
except Exception as e:
logger.warn(f'Failed to load and write {gem_json}: {str(e)}')
return 1
return 0
def get_template_data(template_name: str = None,
template_path: str or pathlib.Path = None, ) -> dict or None:
if not template_name and not template_path:

Loading…
Cancel
Save