git mv Code\Sandbox\Plugins Code/Editor/Plugins

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-06-29 12:42:54 -07:00
parent e34e36cb35
commit 1696680240
215 changed files with 0 additions and 0 deletions
@@ -0,0 +1,38 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME ProjectSettingsTool MODULE
NAMESPACE Legacy
OUTPUT_SUBDIRECTORY EditorPlugins
AUTOMOC
AUTOUIC
FILES_CMAKE
projectsettingstool_files.cmake
COMPILE_DEFINITIONS
PRIVATE
PLUGIN_EXPORTS
SANDBOX_IMPORTS
EDITOR_COMMON_IMPORTS
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Gui
3rdParty::Qt::Widgets
AZ::AzCore
AZ::AzToolsFramework
Legacy::CryCommon
Legacy::EditorLib
Legacy::EditorCommon
)
ly_add_dependencies(Editor ProjectSettingsTool)
set_property(GLOBAL APPEND PROPERTY LY_EDITOR_PLUGINS $<TARGET_FILE_NAME:Legacy::ProjectSettingsTool>)
@@ -0,0 +1,59 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "DefaultImageValidator.h"
#include "PropertyImagePreview.h"
// Error for no default image set in overrides
const static char* noDefaultImageError = "Default must be set if not all dpi overrides are set";
namespace ProjectSettingsTool
{
DefaultImageValidator::DefaultImageValidator(const FunctorValidator& validator)
: FunctorValidator(validator.Functor())
{
}
QValidator::State DefaultImageValidator::validate(QString& input, [[maybe_unused]] int& pos) const
{
return ValidateWithErrors(input).first;
}
FunctorValidator::ReturnType DefaultImageValidator::ValidateWithErrors(const QString& input) const
{
ReturnType result = m_functor(input);
if (result.first == QValidator::Acceptable)
{
if (input.isEmpty())
{
int numCustoms = 0;
// Check all specific overrides to see if any are set
for (const PropertyImagePreviewCtrl* preview : m_specificOverrides)
{
if (!preview->GetValue().isEmpty())
{
++numCustoms;
}
}
if (numCustoms != 0 && numCustoms != m_specificOverrides.size())
{
return FunctorValidator::ReturnType(QValidator::Intermediate, tr(noDefaultImageError));
}
}
}
return result;
}
void DefaultImageValidator::AddOverride(PropertyImagePreviewCtrl* preview)
{
m_specificOverrides.push_back(preview);
}
} // namespace ProjectSettingsTool
#include <moc_DefaultImageValidator.cpp>
@@ -0,0 +1,37 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "FunctorValidator.h"
#include <AzCore/std/containers/list.h>
#endif
namespace ProjectSettingsTool
{
// Forward Declaration
class PropertyImagePreviewCtrl;
class DefaultImageValidator
: public FunctorValidator
{
Q_OBJECT
public:
DefaultImageValidator(const FunctorValidator& validator);
QValidator::State validate(QString& input, int& pos) const override;
ReturnType ValidateWithErrors(const QString& input) const override;
// Adds a specific override to the list for this default override
void AddOverride(PropertyImagePreviewCtrl* preview);
protected:
AZStd::list<const PropertyImagePreviewCtrl*> m_specificOverrides;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,41 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "FunctorValidator.h"
namespace ProjectSettingsTool
{
FunctorValidator::FunctorValidator(FunctorType functor)
: QValidator()
, m_functor(functor)
{
}
FunctorValidator::FunctorValidator()
: QValidator()
, m_functor(nullptr)
{
}
QValidator::State FunctorValidator::validate(QString& input, [[maybe_unused]] int& pos) const
{
return m_functor(input).first;
}
FunctorValidator::ReturnType FunctorValidator::ValidateWithErrors(const QString& input) const
{
return m_functor(input);
}
FunctorValidator::FunctorType FunctorValidator::Functor() const
{
return m_functor;
}
} // namespace ProjectSettingsTool
#include <moc_FunctorValidator.cpp>
@@ -0,0 +1,43 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/utils.h>
#include <QValidator>
#endif
namespace ProjectSettingsTool
{
class FunctorValidator
: public QValidator
{
Q_OBJECT
public:
typedef AZStd::pair<QValidator::State, const QString> ReturnType;
typedef ReturnType(* FunctorType)(const QString&);
FunctorValidator(FunctorType functor);
// Validates using QValidates api
QValidator::State validate(QString& input, int& pos) const override;
// Validates and returns the result with an error string if one occurred
virtual ReturnType ValidateWithErrors(const QString& input) const;
// Returns the function used to validate
FunctorType Functor() const;
protected:
FunctorValidator();
// The function to use for validating
FunctorType m_functor;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,29 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace ProjectSettingsTool
{
class LastPathTraits
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<LastPathTraits>;
// Bus Configuration
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual QString GetLastImagePath() = 0;
virtual void SetLastImagePath(const QString& path) = 0;
};
typedef AZ::EBus<LastPathTraits> LastPathBus;
} // namespace ProjectSettingsTool
@@ -0,0 +1,12 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "PlatformSettings_Android.h"
#include "PlatformSettings_Base.h"
#include "PlatformSettings_Ios.h"
@@ -0,0 +1,262 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PlatformSettings_Android.h"
#include "PlatformSettings_common.h"
#include "Validators.h"
namespace ProjectSettingsTool
{
static const char* defaultImageTooltip = "Default image used if a specific DPI override is not given.";
static void* xmlFunctor = reinterpret_cast<void*>(&SelectXmlFromFileDialog);
void AndroidIcons::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AndroidIcons>()
->Version(1)
->Field("default", &AndroidIcons::m_default)
->Field("mdpi", &AndroidIcons::m_mdpi)
->Field("hdpi", &AndroidIcons::m_hdpi)
->Field("xhdpi", &AndroidIcons::m_xhdpi)
->Field("xxhdpi", &AndroidIcons::m_xxhdpi)
->Field("xxxhdpi", &AndroidIcons::m_xxxhdpi)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<AndroidIcons>("Icons", "All icon overrides for android.")
->DataElement(Handlers::ImagePreview, &AndroidIcons::m_default, "Default", defaultImageTooltip)
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidIconDefault)
->DataElement(Handlers::ImagePreview, &AndroidIcons::m_mdpi, "Medium Dpi (48px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<48>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidIcons, "mdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidIconDefault)
->DataElement(Handlers::ImagePreview, &AndroidIcons::m_hdpi, "High Dpi (72px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<72>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidIcons, "hdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidIconDefault)
->DataElement(Handlers::ImagePreview, &AndroidIcons::m_xhdpi, "XHigh Dpi (96px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<96>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidIcons, "xhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidIconDefault)
->DataElement(Handlers::ImagePreview, &AndroidIcons::m_xxhdpi, "XXHigh Dpi (144px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<144>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidIcons, "xxhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidIconDefault)
->DataElement(Handlers::ImagePreview, &AndroidIcons::m_xxxhdpi, "XXXHigh Dpi (192px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<192>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidIcons, "xxxhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidIconDefault)
;
}
}
}
void AndroidLandscapeSplashscreens::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AndroidLandscapeSplashscreens>()
->Version(1)
->Field("default", &AndroidLandscapeSplashscreens::m_default)
->Field("mdpi", &AndroidLandscapeSplashscreens::m_mdpi)
->Field("hdpi", &AndroidLandscapeSplashscreens::m_hdpi)
->Field("xhdpi", &AndroidLandscapeSplashscreens::m_xhdpi)
->Field("xxhdpi", &AndroidLandscapeSplashscreens::m_xxhdpi)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<AndroidLandscapeSplashscreens>("Landscape", "All landscape splashscreen overrides for Android.")
->DataElement(Handlers::ImagePreview, &AndroidLandscapeSplashscreens::m_default, "Default", defaultImageTooltip)
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidLandDefault)
->DataElement(Handlers::ImagePreview, &AndroidLandscapeSplashscreens::m_mdpi, "Medium Dpi", "Suggested 1024 x 640 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidLandscape, "mdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidLandDefault)
->DataElement(Handlers::ImagePreview, &AndroidLandscapeSplashscreens::m_hdpi, "High Dpi", "Suggested 1280 x 800 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidLandscape, "hdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidLandDefault)
->DataElement(Handlers::ImagePreview, &AndroidLandscapeSplashscreens::m_xhdpi, "XHigh Dpi", "Suggested 1920 x 1200 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidLandscape, "xhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidLandDefault)
->DataElement(Handlers::ImagePreview, &AndroidLandscapeSplashscreens::m_xxhdpi, "XXHigh Dpi", "Suggested 2560 x 1600 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidLandscape, "xxhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidLandDefault)
;
}
}
}
void AndroidPortraitSplashscreens::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AndroidPortraitSplashscreens>()
->Version(1)
->Field("default", &AndroidPortraitSplashscreens::m_default)
->Field("mdpi", &AndroidPortraitSplashscreens::m_mdpi)
->Field("hdpi", &AndroidPortraitSplashscreens::m_hdpi)
->Field("xhdpi", &AndroidPortraitSplashscreens::m_xhdpi)
->Field("xxhdpi", &AndroidPortraitSplashscreens::m_xxhdpi)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<AndroidPortraitSplashscreens>("Portrait", "All portrait splashscreen overrides for Android.")
->DataElement(Handlers::ImagePreview, &AndroidPortraitSplashscreens::m_default, "Default", defaultImageTooltip)
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidPortDefault)
->DataElement(Handlers::ImagePreview, &AndroidPortraitSplashscreens::m_mdpi, "Medium Dpi", "Suggested 640 x 1024 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidPortrait, "mdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidPortDefault)
->DataElement(Handlers::ImagePreview, &AndroidPortraitSplashscreens::m_hdpi, "High Dpi", "Suggested 800 x 1280 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidPortrait, "hdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidPortDefault)
->DataElement(Handlers::ImagePreview, &AndroidPortraitSplashscreens::m_xhdpi, "XHigh Dpi", "Suggested 1200 x 1920 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidPortrait, "xhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidPortDefault)
->DataElement(Handlers::ImagePreview, &AndroidPortraitSplashscreens::m_xxhdpi, "XXHigh Dpi", "Suggested 1600 x 2560 png.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidPngOrEmpty))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::AndroidPortrait, "xxhdpi"))
->Attribute(Attributes::DefaultImagePreview, Identfiers::AndroidPortDefault)
;
}
}
}
void AndroidSplashscreens::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
AndroidLandscapeSplashscreens::Reflect(context);
AndroidPortraitSplashscreens::Reflect(context);
serialize->Class<AndroidSplashscreens>()
->Version(1)
->Field("land", &AndroidSplashscreens::m_landscapeSplashscreens)
->Field("port", &AndroidSplashscreens::m_portraitSplashscreens)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<AndroidSplashscreens>("Splashscreens", "All splashscreen overrides for Android.")
->DataElement(0, &AndroidSplashscreens::m_landscapeSplashscreens)
->DataElement(0, &AndroidSplashscreens::m_portraitSplashscreens)
;
}
}
}
void AndroidSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
AndroidIcons::Reflect(context);
AndroidSplashscreens::Reflect(context);
serialize->Class<AndroidSettings>()
->Version(1)
->Field("package_name", &AndroidSettings::m_packageName)
->Field("version_name", &AndroidSettings::m_versionName)
->Field("version_number", &AndroidSettings::m_versionNumber)
->Field("orientation", &AndroidSettings::m_orientation)
->Field("app_public_key", &AndroidSettings::m_appPublicKey)
->Field("app_obfuscator_salt", &AndroidSettings::m_appObfuscatorSalt)
->Field("rc_pak_job", &AndroidSettings::m_rcPakJob)
->Field("rc_obb_job", &AndroidSettings::m_rcObbJob)
->Field("use_main_obb", &AndroidSettings::m_useMainObb)
->Field("use_patch_obb", &AndroidSettings::m_usePatchObb)
->Field("enable_key_screen_on", &AndroidSettings::m_enableKeyScreenOn)
->Field("disable_immersive_mode", &AndroidSettings::m_disableImmersiveMode)
->Field("icons", &AndroidSettings::m_icons)
->Field("splash_screen", &AndroidSettings::m_splashscreens)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<AndroidSettings>("Android Settings", "All settings related to Android not already defined by base settings.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(Handlers::LinkedLineEdit, &AndroidSettings::m_packageName, "Package Name", "Android application package identifier. Used for generating the project specific Java activity class and in the AndroidManifest.xml. Must be in dot separated format.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PackageName))
->Attribute(Attributes::LinkOptional, true)
->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidPackageName)
->Attribute(Attributes::LinkedProperty, Identfiers::IosBundleIdentifer)
->DataElement(Handlers::LinkedLineEdit, &AndroidSettings::m_versionName, "Version Name", "Human readable version number. Used to set the \"android: versionName\" tag in the AndroidManifest.xml and ultimately what will be displayed in the App Store.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSVersionNumber))
->Attribute(Attributes::LinkOptional, true)
->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidVersionName)
->Attribute(Attributes::LinkedProperty, Identfiers::IosVersionName)
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_versionNumber, "Version Number", "Internal application version number. Used to set the \"android:versionCode\" tag in the AndroidManifest.xml.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->Attribute(AZ::Edit::Attributes::Max, Validators::maxAndroidVersion)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AndroidSettings::m_orientation, "Orientation", "Desired orientation of the Android application. Used to set the \"android:screenOrientation\" tag in the AndroidManifest.xml.")
->Attribute(AZ::Edit::Attributes::StringList, AZStd::vector<AZStd::string>
{
"landscape",
"portrait",
"reverseLandscape",
"reversePortrait",
"sensorLandscape",
"sensorPortrait",
"sensor",
"fullSensor",
"noSensor",
"userLandscape",
"userPortrait",
"user",
"fullUser",
"locked",
"behind",
"unspecified"
})
->DataElement(Handlers::LinkedLineEdit, &AndroidSettings::m_appPublicKey, "Public App Key", "The application license key provided by Google Play. Required for using APK expansion files or other Google Play Services.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PublicAppKeyOrEmpty))
->Attribute(Attributes::Obfuscated, true)
->DataElement(Handlers::LinkedLineEdit, &AndroidSettings::m_appObfuscatorSalt, "App Obfuscation Salt", "Application specific salt value for (un)obfuscation when using APK expansion files.")
->Attribute(Attributes::Obfuscated, true)
->DataElement(Handlers::FileSelect, &AndroidSettings::m_rcPakJob, "Rc Job PAK Override", "Path to the RC job XML file used to override the normal PAK files generation used in release builds. Path must be relative to <build dir>.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidXmlOrEmpty))
->Attribute(Attributes::SelectFunction, xmlFunctor)
->DataElement(Handlers::FileSelect, &AndroidSettings::m_rcObbJob, "Rc Job APK Override", "Path to the RC job XML file used to override the normal APK Expansion file(s) generation used in release builds. Path must be relative to <build dir>.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidXmlOrEmpty))
->Attribute(Attributes::SelectFunction, xmlFunctor)
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_useMainObb, "Use Main APK", "Specify if the \"Main\" APK Expansion file should be used.")
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_usePatchObb, "Use Patch APK", "Specify if the \"Patch\" APK Expansion file should be used.")
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_enableKeyScreenOn, "Enable Screen Wake Lock", "Enabled or disable the screen wake lock (device won't go to sleep while the application is running).")
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_disableImmersiveMode, "Disable Immersive Mode", "Disable hiding of top and bottom system bars.")
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_icons)
->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_splashscreens)
;
}
}
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,142 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Serialization/EditContext.h>
namespace ProjectSettingsTool
{
class AndroidIcons
{
public:
AZ_TYPE_INFO(AndroidIcons, "{D807ABEA-9C79-4EDD-B418-9A823E812C9A}");
AZ_CLASS_ALLOCATOR(AndroidIcons, AZ::SystemAllocator, 0);
AndroidIcons()
: m_default("")
, m_mdpi("")
, m_hdpi("")
, m_xhdpi("")
, m_xxhdpi("")
, m_xxxhdpi("")
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_default;
AZStd::string m_mdpi;
AZStd::string m_hdpi;
AZStd::string m_xhdpi;
AZStd::string m_xxhdpi;
AZStd::string m_xxxhdpi;
};
class AndroidLandscapeSplashscreens
{
public:
AZ_TYPE_INFO(AndroidLandscapeSplashscreens, "{37888881-5050-47B6-9EB4-A408FD27D397}");
AZ_CLASS_ALLOCATOR(AndroidIcons, AZ::SystemAllocator, 0);
AndroidLandscapeSplashscreens()
: m_default("")
, m_mdpi("")
, m_hdpi("")
, m_xhdpi("")
, m_xxhdpi("")
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_default;
AZStd::string m_mdpi;
AZStd::string m_hdpi;
AZStd::string m_xhdpi;
AZStd::string m_xxhdpi;
};
class AndroidPortraitSplashscreens
{
public:
AZ_TYPE_INFO(AndroidPortraitSplashscreens, "{2AADA22F-B5A3-440C-A592-FB923BC66878}");
AZ_CLASS_ALLOCATOR(AndroidPortraitSplashscreens, AZ::SystemAllocator, 0);
AndroidPortraitSplashscreens()
: m_default("")
, m_mdpi("")
, m_hdpi("")
, m_xhdpi("")
, m_xxhdpi("")
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_default;
AZStd::string m_mdpi;
AZStd::string m_hdpi;
AZStd::string m_xhdpi;
AZStd::string m_xxhdpi;
};
class AndroidSplashscreens
{
public:
AZ_TYPE_INFO(AndroidSplashscreens, "{95985732-F45B-436A-86BC-7AE5249FF520}");
AZ_CLASS_ALLOCATOR(AndroidSplashscreens, AZ::SystemAllocator, 0);
AndroidSplashscreens()
: m_landscapeSplashscreens()
, m_portraitSplashscreens()
{}
AndroidLandscapeSplashscreens m_landscapeSplashscreens;
AndroidPortraitSplashscreens m_portraitSplashscreens;
static void Reflect(AZ::ReflectContext* context);
};
class AndroidSettings
{
public:
AZ_TYPE_INFO(AndroidSettings, "{5D57D014-4939-4D86-B862-AF35BAC705DC}");
AZ_CLASS_ALLOCATOR(AndroidSettings, AZ::SystemAllocator, 0);
AndroidSettings()
: m_packageName("")
, m_versionName("")
, m_versionNumber(1)
, m_orientation("landscape")
, m_appPublicKey("")
, m_appObfuscatorSalt("")
, m_rcPakJob("")
, m_rcObbJob("")
, m_useMainObb(false)
, m_usePatchObb(false)
, m_enableKeyScreenOn(false)
, m_disableImmersiveMode(false)
, m_icons()
, m_splashscreens()
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_packageName;
AZStd::string m_versionName;
int m_versionNumber;
AZStd::string m_orientation;
AZStd::string m_appPublicKey;
AZStd::string m_appObfuscatorSalt;
AZStd::string m_rcPakJob;
AZStd::string m_rcObbJob;
bool m_useMainObb;
bool m_usePatchObb;
bool m_enableKeyScreenOn;
bool m_disableImmersiveMode;
AndroidIcons m_icons;
AndroidSplashscreens m_splashscreens;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,59 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PlatformSettings_Base.h"
#include "PlatformSettings_common.h"
#include "Validators.h"
namespace ProjectSettingsTool
{
void BaseSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<BaseSettings>()
->Version(2)
->Field("project_name", &BaseSettings::m_projectName)
->Field("product_name", &BaseSettings::m_productName)
->Field("executable_name", &BaseSettings::m_executableName)
->Field("project_path", &BaseSettings::m_projectPath)
->Field("project_output_folder", &BaseSettings::m_projectOutputFolder)
->Field("code_folder", &BaseSettings::m_codeFolder)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<BaseSettings>("Project Settings", "All core settings for the game project and package and deployment.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(Handlers::LinkedLineEdit, &BaseSettings::m_projectName, "Project Name", "The name of the project.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName))
->Attribute(Attributes::PropertyIdentfier, Identfiers::ProjectName)
->Attribute(Attributes::LinkedProperty, Identfiers::IosBundleName)
->DataElement(Handlers::LinkedLineEdit, &BaseSettings::m_productName, "Product Name", "The project's user facing name.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IsNotEmpty))
->Attribute(Attributes::PropertyIdentfier, Identfiers::ProductName)
->Attribute(Attributes::LinkedProperty, Identfiers::IosDisplayName)
->DataElement(Handlers::LinkedLineEdit, &BaseSettings::m_executableName, "Executable Name", "The project launcher's name.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName))
->Attribute(Attributes::PropertyIdentfier, Identfiers::ExecutableName)
->Attribute(Attributes::LinkedProperty, Identfiers::IosExecutableName)
->DataElement(Handlers::QValidatedLineEdit, &BaseSettings::m_projectPath, "Project Path", "The project root folder path .")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileNameOrEmpty))
->Attribute(Attributes::PropertyIdentfier, Identfiers::ProductName)
->Attribute(Attributes::LinkedProperty, Identfiers::ExecutableName)
->DataElement(Handlers::QValidatedLineEdit, &BaseSettings::m_projectOutputFolder, "Output Folder", "The folder the packed project will be exported to.")
->DataElement(Handlers::QValidatedLineEdit, &BaseSettings::m_codeFolder, "Code Folder (legacy)", "A legacy setting specifing the folder for this project's code.")
;
}
}
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,40 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Serialization/EditContext.h>
namespace ProjectSettingsTool
{
class BaseSettings
{
public:
AZ_TYPE_INFO(BaseSettings, "{3202E013-46EC-4E97-989A-84934CA15C59}");
AZ_CLASS_ALLOCATOR(BaseSettings, AZ::SystemAllocator, 0);
BaseSettings()
: m_projectName("")
, m_productName("")
, m_executableName("")
, m_projectPath("")
, m_sysDllGame("")
, m_projectOutputFolder("")
, m_codeFolder("")
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_projectName;
AZStd::string m_productName;
AZStd::string m_executableName;
AZStd::string m_projectPath;
AZStd::string m_sysDllGame;
AZStd::string m_projectOutputFolder;
AZStd::string m_codeFolder;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,313 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PlatformSettings_Ios.h"
#include "PlatformSettings_common.h"
#include "Validators.h"
namespace ProjectSettingsTool
{
namespace Icons
{
static const char* appStore = "iOSAppStoreIcon1024x1024";
static const char* iphoneApp120 = "iPhoneAppIcon120x120";
static const char* iphoneApp180 = "iPhoneAppIcon180x180";
static const char* iphoneNotification40 = "iPhoneNotificationIcon40x40";
static const char* iphoneNotification60 = "iPhoneNotificationIcon60x60";
static const char* iphoneSettings58 = "iPhoneSettingsIcon58x58";
static const char* iphoneSettings87 = "iPhoneSettingsIcon87x87";
static const char* iphoneSpotlight80 = "iPhoneSpotlightIcon80x80";
static const char* iphoneSpotlight120 = "iPhoneSpotlightIcon120x120";
static const char* ipadApp76 = "iPadAppIcon76x76";
static const char* ipadApp152 = "iPadAppIcon152x152";
static const char* ipadProApp = "iPadProAppIcon167x167";
static const char* ipadNotification20 = "iPadNotificationIcon20x20";
static const char* ipadNotification40 = "iPadNotificationIcon40x40";
static const char* ipadSettings29 = "iPadSettingsIcon29x29";
static const char* ipadSettings58 = "iPadSettingsIcon58x58";
static const char* ipadSpotlight40 = "iPadSpotlightIcon40x40";
static const char* ipadSpotlight80 = "iPadSpotlightIcon80x80";
};
void IosIcons::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<IosIcons>()
->Version(1)
->Field(Icons::appStore, &IosIcons::m_appStore)
->Field(Icons::iphoneApp120, &IosIcons::m_iphoneApp120)
->Field(Icons::iphoneApp180, &IosIcons::m_iphoneApp180)
->Field(Icons::iphoneNotification40, &IosIcons::m_iphoneNotification40)
->Field(Icons::iphoneNotification60, &IosIcons::m_iphoneNotification60)
->Field(Icons::iphoneSettings58, &IosIcons::m_iphoneSettings58)
->Field(Icons::iphoneSettings87, &IosIcons::m_iphoneSettings87)
->Field(Icons::iphoneSpotlight80, &IosIcons::m_iphoneSpotlight80)
->Field(Icons::iphoneSpotlight120, &IosIcons::m_iphoneSpotlight120)
->Field(Icons::ipadApp76, &IosIcons::m_ipadApp76)
->Field(Icons::ipadApp152, &IosIcons::m_ipadApp152)
->Field(Icons::ipadProApp, &IosIcons::m_ipadProApp)
->Field(Icons::ipadNotification20, &IosIcons::m_ipadNotification20)
->Field(Icons::ipadNotification40, &IosIcons::m_ipadNotification40)
->Field(Icons::ipadSettings29, &IosIcons::m_ipadSettings29)
->Field(Icons::ipadSettings58, &IosIcons::m_ipadSettings58)
->Field(Icons::ipadSpotlight40, &IosIcons::m_ipadSpotlight40)
->Field(Icons::ipadSpotlight80, &IosIcons::m_ipadSpotlight80)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<IosIcons>("Icons", "All png icon overrides for iOS.")
->DataElement(Handlers::ImagePreview, &IosIcons::m_appStore, "App Store (1024px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<1024>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::appStore))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneApp120, "iPhone App (120px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<120>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneApp120))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneApp180, "iPhone App (180px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<180>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneApp180))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneNotification40, "iPhone Notification (40px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<40>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneNotification40))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneNotification60, "iPhone Notification (60px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<60>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneNotification60))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneSettings58, "iPhone Settings (58px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<58>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneSettings58))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneSettings87, "iPhone Settings (87px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<87>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneSettings87))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneSpotlight80, "iPhone Spotlight (80px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<80>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneSpotlight80))
->DataElement(Handlers::ImagePreview, &IosIcons::m_iphoneSpotlight120, "iPhone Spotlight (120px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<120>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::iphoneSpotlight120))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadApp76, "iPad App (76px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<76>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadApp76))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadApp152, "iPad App (152px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<152>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadApp152))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadProApp, "iPad Pro App (167px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<167>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadProApp))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadNotification20, "iPad Notification (20px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<20>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadNotification20))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadNotification40, "iPad Notification (40px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<40>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadNotification40))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadSettings29, "iPad Settings (29px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<29>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadSettings29))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadSettings58, "iPad Settings (58px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<58>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadSettings58))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadSpotlight40, "iPad Spotlight (40px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<40>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadSpotlight40))
->DataElement(Handlers::ImagePreview, &IosIcons::m_ipadSpotlight80, "iPad Spotlight (80px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<80>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosIcons, Icons::ipadSpotlight80))
;
}
}
}
namespace Launchscreens
{
static const char* iphone640x960 = "iPhoneLaunchImage640x960";
static const char* iphone640x1136 = "iPhoneLaunchImage640x1136";
static const char* iphone750x1334 = "iPhoneLaunchImage750x1334";
static const char* iphone1125x2436 = "iPhoneLaunchImage1125x2436";
static const char* iphone2436x1125 = "iPhoneLaunchImage2436x1125";
static const char* iphone1242x2208 = "iPhoneLaunchImage1242x2208";
static const char* iphone2208x1242 = "iPhoneLaunchImage2208x1242";
static const char* ipad768x1024 = "iPadLaunchImage768x1024";
static const char* ipad1024x768 = "iPadLaunchImage1024x768";
static const char* ipad1536x2048 = "iPadLaunchImage1536x2048";
static const char* ipad2048x1536 = "iPadLaunchImage2048x1536";
} // namespace Launchscreens
void IosLaunchscreens::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<IosLaunchscreens>()
->Version(1)
->Field(Launchscreens::iphone640x960, &IosLaunchscreens::m_iphone640x960)
->Field(Launchscreens::iphone640x1136, &IosLaunchscreens::m_iphone640x1136)
->Field(Launchscreens::iphone750x1334, &IosLaunchscreens::m_iphone750x1334)
->Field(Launchscreens::iphone1125x2436, &IosLaunchscreens::m_iphone1125x2436)
->Field(Launchscreens::iphone2436x1125, &IosLaunchscreens::m_iphone2436x1125)
->Field(Launchscreens::iphone1242x2208, &IosLaunchscreens::m_iphone1242x2208)
->Field(Launchscreens::iphone2208x1242, &IosLaunchscreens::m_iphone2208x1242)
->Field(Launchscreens::ipad768x1024, &IosLaunchscreens::m_ipad768x1024)
->Field(Launchscreens::ipad1024x768, &IosLaunchscreens::m_ipad1024x768)
->Field(Launchscreens::ipad1536x2048, &IosLaunchscreens::m_ipad1536x2048)
->Field(Launchscreens::ipad2048x1536, &IosLaunchscreens::m_ipad2048x1536)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<IosLaunchscreens>("Launchscreens", "All png launchscreen overrides for iOS.")
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone640x960, "iPhone (640x960px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<640, 960>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone640x960))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone640x1136, "iPhone (640x1136px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<640, 1136>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone640x1136))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone750x1334, "iPhone (750x1334px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<750, 1334>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone750x1334))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone1125x2436, "iPhone (1125x2436px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<1125, 2436>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone1125x2436))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone2436x1125, "iPhone (2436x1125px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<2436, 1125>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone2436x1125))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone1242x2208, "iPhone (1242x2208px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<1242, 2208>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone1242x2208))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_iphone2208x1242, "iPhone (2208x1242px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<2208, 1242>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::iphone2208x1242))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_ipad768x1024, "iPad (768x1024px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<768, 1024>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::ipad768x1024))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_ipad1024x768, "iPad (1024x768px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<1024, 768>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::ipad1024x768))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_ipad1536x2048, "iPad (1536x2048px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<1536, 2048>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::ipad1536x2048))
->DataElement(Handlers::ImagePreview, &IosLaunchscreens::m_ipad2048x1536, "iPad (2048x1536px)", "")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PngImageSetSizeOrEmpty<2048, 1536>))
->Attribute(Attributes::DefaultPath, GenDefaultImagePath(ImageGroup::IosLaunchScreens, Launchscreens::ipad2048x1536))
;
}
}
}
void IosOrientations::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<IosOrientations>()
->Version(1)
->Field("UIInterfaceOrientationLandscapeRight", &IosOrientations::m_landscapeRight)
->Field("UIInterfaceOrientationLandscapeLeft", &IosOrientations::m_landscapeLeft)
->Field("UIInterfaceOrientationPortrait", &IosOrientations::m_portraitBottom)
->Field("UIInterfaceOrientationPortraitUpsideDown", &IosOrientations::m_portraitTop)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<IosOrientations>("Orientations", "All supported orientations for iOS.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosOrientations::m_landscapeRight, "Landscape (right home button)", "Enable landscape orientation with home button on right side of device.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosOrientations::m_landscapeLeft, "Landscape (left home button)", "Enable landscape orientation with home button on left side of device.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosOrientations::m_portraitBottom, "Portrait (bottom home button)", "Enable portrait orientation with home button on bottom of device.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosOrientations::m_portraitTop, "Portrait (top home button)", "Enable portrait orientation with home button on top of device.")
;
}
}
}
void IosSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
IosOrientations::Reflect(context);
IosIcons::Reflect(context);
IosLaunchscreens::Reflect(context);
serialize->Class<IosSettings>()
->Version(1)
->Field("CFBundleName", &IosSettings::m_bundleName)
->Field("CFBundleDisplayName", &IosSettings::m_bundleDisplayName)
->Field("CFBundleExecutable", &IosSettings::m_executableName)
->Field("CFBundleIdentifier", &IosSettings::m_bundleIdentifier)
->Field("CFBundleShortVersionString", &IosSettings::m_versionName)
->Field("CFBundleVersion", &IosSettings::m_versionNumber)
->Field("CFBundleDevelopmentRegion", &IosSettings::m_developmentRegion)
->Field("UIRequiresFullScreen", &IosSettings::m_requiresFullscreen)
->Field("UIStatusBarHidden", &IosSettings::m_hideStatusBar)
->Field("UISupportedInterfaceOrientations", &IosSettings::m_iphoneOrientations)
->Field("UISupportedInterfaceOrientations~ipad", &IosSettings::m_ipadOrientations)
->Field("icons", &IosSettings::m_icons)
->Field("launchscreens", &IosSettings::m_launchscreens)
;
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<IosSettings>("Ios Settings", "All settings iOS settings.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_bundleName, "Bundle Name", "The name of the bundle.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName))
->Attribute(Attributes::PropertyIdentfier, Identfiers::IosBundleName)
->Attribute(Attributes::LinkedProperty, Identfiers::ProjectName)
->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_bundleDisplayName, "Display Name", "The user visible name of the bundle.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IsNotEmpty))
->Attribute(Attributes::PropertyIdentfier, Identfiers::IosDisplayName)
->Attribute(Attributes::LinkedProperty, Identfiers::ProductName)
->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_executableName, "Executable Name", "Name of the bundle's executable file.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName))
->Attribute(Attributes::PropertyIdentfier, Identfiers::IosExecutableName)
->Attribute(Attributes::LinkedProperty, Identfiers::ExecutableName)
->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_bundleIdentifier, "Bundle Identifier", "Uniquely identifies the bundle. Should be in reverse-DNS format.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PackageName))
->Attribute(Attributes::LinkOptional, true)
->Attribute(Attributes::PropertyIdentfier, Identfiers::IosBundleIdentifer)
->Attribute(Attributes::LinkedProperty, Identfiers::AndroidPackageName)
->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_versionName, "Version Name", "The release version number string for the app. Displayed in the app store.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSVersionNumber))
->Attribute(Attributes::LinkOptional, true)
->Attribute(Attributes::PropertyIdentfier, Identfiers::IosVersionName)
->Attribute(Attributes::LinkedProperty, Identfiers::AndroidVersionName)
->DataElement(Handlers::QValidatedLineEdit, &IosSettings::m_versionNumber, "Version Number", "The build version number string for the bundle.")
->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSVersionNumber))
->DataElement(AZ::Edit::UIHandlers::ComboBox, &IosSettings::m_developmentRegion, "Development Region", "The default language and region for the app.")
->Attribute(AZ::Edit::Attributes::StringList, AZStd::vector<AZStd::string>
{
"en_US",
"en_CA",
"fr_CA",
"zh_CN",
"fr_FR",
"de_DE",
"it_IT",
"ja_JP",
"ko_KR",
"zh_TW",
"en_GB"
})
->DataElement(AZ::Edit::UIHandlers::Default, &IosSettings::m_requiresFullscreen, "Requires Fullscreen", "Specifies whether the app is required to run in fullscreen mode.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosSettings::m_hideStatusBar, "Hide Status Bar", "Specifies whether the status bar is initially hidden when the app launches.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosSettings::m_iphoneOrientations, "iPhone Orientations", "Enable support for iPhone orientations.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosSettings::m_ipadOrientations, "iPad Orientations", "Enable support for iPad orientations.")
->DataElement(AZ::Edit::UIHandlers::Default, &IosSettings::m_icons)
->DataElement(AZ::Edit::UIHandlers::Default, &IosSettings::m_launchscreens)
;
}
}
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,157 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Serialization/EditContext.h>
namespace ProjectSettingsTool
{
class IosIcons
{
public:
AZ_TYPE_INFO(IosIcons, "{A57F9F23-36F5-4425-B40A-56D60119E5C9}");
AZ_CLASS_ALLOCATOR(IosIcons, AZ::SystemAllocator, 0);
IosIcons()
: m_appStore("")
, m_iphoneApp120("")
, m_iphoneApp180("")
, m_iphoneNotification40("")
, m_iphoneNotification60("")
, m_iphoneSettings58("")
, m_iphoneSettings87("")
, m_iphoneSpotlight80("")
, m_iphoneSpotlight120("")
, m_ipadApp76("")
, m_ipadApp152("")
, m_ipadProApp("")
, m_ipadNotification20("")
, m_ipadNotification40("")
, m_ipadSettings29("")
, m_ipadSettings58("")
, m_ipadSpotlight40("")
, m_ipadSpotlight80("")
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_appStore;
AZStd::string m_iphoneApp120;
AZStd::string m_iphoneApp180;
AZStd::string m_iphoneNotification40;
AZStd::string m_iphoneNotification60;
AZStd::string m_iphoneSettings58;
AZStd::string m_iphoneSettings87;
AZStd::string m_iphoneSpotlight80;
AZStd::string m_iphoneSpotlight120;
AZStd::string m_ipadApp76;
AZStd::string m_ipadApp152;
AZStd::string m_ipadProApp;
AZStd::string m_ipadNotification20;
AZStd::string m_ipadNotification40;
AZStd::string m_ipadSettings29;
AZStd::string m_ipadSettings58;
AZStd::string m_ipadSpotlight40;
AZStd::string m_ipadSpotlight80;
};
class IosLaunchscreens
{
public:
AZ_TYPE_INFO(IosLaunchscreens, "{1A34706F-9558-4081-9898-33758B026629}");
AZ_CLASS_ALLOCATOR(IosLaunchscreens, AZ::SystemAllocator, 0);
IosLaunchscreens()
: m_iphone640x960("")
, m_iphone640x1136("")
, m_iphone750x1334("")
, m_iphone1125x2436("")
, m_iphone2436x1125("")
, m_iphone1242x2208("")
, m_iphone2208x1242("")
, m_ipad768x1024("")
, m_ipad1024x768("")
, m_ipad1536x2048("")
, m_ipad2048x1536("")
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_iphone640x960;
AZStd::string m_iphone640x1136;
AZStd::string m_iphone750x1334;
AZStd::string m_iphone1125x2436;
AZStd::string m_iphone2436x1125;
AZStd::string m_iphone1242x2208;
AZStd::string m_iphone2208x1242;
AZStd::string m_ipad768x1024;
AZStd::string m_ipad1024x768;
AZStd::string m_ipad1536x2048;
AZStd::string m_ipad2048x1536;
};
class IosOrientations
{
public:
AZ_TYPE_INFO(IosOrientations, "{A42CDF2E-CCE1-4D93-9D4E-2270CFC0F2ED}");
AZ_CLASS_ALLOCATOR(IosOrientations, AZ::SystemAllocator, 0);
IosOrientations()
: m_landscapeRight(false)
, m_landscapeLeft(false)
, m_portraitBottom(false)
, m_portraitTop(false)
{}
static void Reflect(AZ::ReflectContext* context);
bool m_landscapeRight;
bool m_landscapeLeft;
bool m_portraitBottom;
bool m_portraitTop;
};
class IosSettings
{
public:
AZ_TYPE_INFO(IosSettings, "{9EDF051E-0158-4ADE-92A3-B7AC230E0114}");
AZ_CLASS_ALLOCATOR(IosSettings, AZ::SystemAllocator, 0);
IosSettings()
: m_bundleName("")
, m_bundleDisplayName("")
, m_executableName("")
, m_bundleIdentifier("com.amazon.o3de.UnknownProject")
, m_versionName("1.0.0")
, m_versionNumber("1.0.0")
, m_developmentRegion("en_US")
, m_requiresFullscreen(false)
, m_hideStatusBar(false)
, m_iphoneOrientations()
, m_ipadOrientations()
, m_icons()
, m_launchscreens()
{}
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_bundleName;
AZStd::string m_bundleDisplayName;
AZStd::string m_executableName;
AZStd::string m_bundleIdentifier;
AZStd::string m_versionName;
AZStd::string m_versionNumber;
AZStd::string m_developmentRegion;
bool m_requiresFullscreen;
bool m_hideStatusBar;
IosOrientations m_iphoneOrientations;
IosOrientations m_ipadOrientations;
IosIcons m_icons;
IosLaunchscreens m_launchscreens;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,59 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include "Utils.h"
namespace ProjectSettingsTool
{
namespace Attributes
{
static const AZ::Crc32 FuncValidator = AZ_CRC("FuncValidator");
static const AZ::Crc32 SelectFunction = AZ_CRC("SelectFunction");
static const AZ::Crc32 LinkOptional = AZ_CRC("LinkOptional");
static const AZ::Crc32 Obfuscated = AZ_CRC("ObfuscatedText");
// Used as a tooltip and for distinguising linked properties
static const AZ::Crc32 PropertyIdentfier = AZ_CRC("PropertyIdentfier");
static const AZ::Crc32 LinkedProperty = AZ_CRC("LinkedProperty");
static const AZ::Crc32 DefaultPath = AZ_CRC("DefaultPath");
static const AZ::Crc32 DefaultImagePreview = AZ_CRC("DefaultImagePreview");
static const AZ::Crc32 ObfuscatedText = AZ_CRC("ObfuscatedText");
static const AZ::Crc32 ClearButton = AZ_CRC("ClearButton");
static const AZ::Crc32 RemovableReadOnly = AZ_CRC("RemovableReadOnly");
} // namespace Attributes
namespace Handlers
{
static const AZ::Crc32 ImagePreview = AZ_CRC("ImagePreview");
static const AZ::Crc32 LinkedLineEdit = AZ_CRC("LinkedLineEdit");
static const AZ::Crc32 FileSelect = AZ_CRC("FileSelect");
static const AZ::Crc32 QValidatedLineEdit = AZ_CRC("QValLineEdit");
static const AZ::Crc32 QValidatedBrowseEdit = AZ_CRC("QValBrowseEdit");
} // namespace Handlers
namespace Identfiers
{
static const char* ProjectName = "Base - Project Name";
static const char* ProductName = "Base - Product Name";
static const char* ExecutableName = "Base - Executable Name";
static const char* AndroidPackageName = "Android - Package Name";
static const char* AndroidVersionName = "Android - Version Name";
static const char* AndroidIconDefault = "Android - Icon Default";
static const char* AndroidLandDefault = "Android - Land Default";
static const char* AndroidPortDefault = "Android - Port Default";
static const char* IosBundleName = "iOS - Bundle Name";
static const char* IosDisplayName = "iOS - Display Name";
static const char* IosExecutableName = "iOS - Executable Name";
static const char* IosBundleIdentifer = "iOS - Bundle Identifer";
static const char* IosVersionName = "iOS - Version Name";
} // namespace Identfiers
} // namespace ProjectSettingsTool
@@ -0,0 +1,42 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace ProjectSettingsTool
{
enum class PlatformId
{
Base,
Android,
Ios,
NumPlatformIds
};
enum class PlatformDataType
{
ProjectJson,
Plist,
NumPlatformDataTypes
};
struct Platform
{
PlatformId m_id;
PlatformDataType m_type;
};
const Platform Platforms[static_cast<unsigned>(PlatformId::NumPlatformIds)]
{
Platform{ PlatformId::Base, PlatformDataType::ProjectJson },
Platform{ PlatformId::Android, PlatformDataType::ProjectJson },
Platform{ PlatformId::Ios, PlatformDataType::Plist }
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,162 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PlistDictionary.h"
namespace ProjectSettingsTool
{
using namespace AZ;
using XmlDocument = rapidxml::xml_document<char>;
using XmlNode = rapidxml::xml_node<char>;
PlistDictionary::PlistDictionary(XmlDocument* plist)
: m_document(plist)
, m_dict(plist->first_node("plist")->first_node("dict"))
{
}
XmlNode* PlistDictionary::MakeNode(const char* name, const char* value)
{
return m_document->allocate_node
(
rapidxml::node_element,
m_document->allocate_string(name),
m_document->allocate_string(value)
);
}
XmlNode* PlistDictionary::MakeNode()
{
return m_document->allocate_node(rapidxml::node_element);
}
XmlNode* PlistDictionary::GetPropertyKeyNode(const char* key)
{
XmlNode* keyNode = m_dict->first_node("key");
// Look for the key in pList's interesting structure
while (keyNode)
{
if (strcmp(keyNode->value(), key) == 0)
{
break;
}
keyNode = keyNode->next_sibling("key");
}
// Returns key if found otherwise nullptr
return keyNode;
}
XmlNode* PlistDictionary::GetPropertyValueNode(const char* key)
{
XmlNode* keyNode = GetPropertyKeyNode(key);
// Key found return data node
if (keyNode != nullptr)
{
return keyNode->next_sibling();
}
// Failed to find key return nullptr
return nullptr;
}
XmlNode* PlistDictionary::AddProperty(const char* key)
{
m_dict->append_node(MakeNode("key", key));
XmlNode* data = MakeNode();
m_dict->append_node(data);
return data;
}
void PlistDictionary::RemoveProperty(const char* key)
{
XmlNode* keyNode = GetPropertyKeyNode(key);
if (keyNode != nullptr)
{
XmlNode* dataNode = keyNode->next_sibling();
m_dict->remove_node(keyNode);
m_dict->remove_node(dataNode);
}
}
const char* PlistDictionary::GetPropertyValue(const char* key)
{
XmlNode* valueNode = GetPropertyValueNode(key);
return valueNode != nullptr ? GetPropertyValue(valueNode) : nullptr;
}
const char* PlistDictionary::GetPropertyValue(XmlNode* node)
{
return node->value_size() > 0 ? node->value() : nullptr;
}
const char* PlistDictionary::GetPropertyValueName(const char* key)
{
XmlNode* valueNode = GetPropertyValueNode(key);
return valueNode != nullptr ? GetPropertyValueName(valueNode) : nullptr;
}
const char* PlistDictionary::GetPropertyValueName(XmlNode* node)
{
return node->name_size() > 0 ? node->name() : nullptr;
}
XmlNode* PlistDictionary::SetPropertyValue(const char* key, const char* newValue)
{
XmlNode* dataNode = GetPropertyValueNode(key);
if (dataNode == nullptr)
{
dataNode = AddProperty(key);
SetPropertyValueName(dataNode, "string");
}
SetPropertyValue(dataNode, newValue);
return dataNode;
}
void PlistDictionary::SetPropertyValue(XmlNode* node, const char* newValue)
{
node->value(m_document->allocate_string(newValue));
}
XmlNode* PlistDictionary::SetPropertyValueName(const char* key, const char* newName)
{
XmlNode* dataNode = GetPropertyValueNode(key);
if (dataNode == nullptr)
{
dataNode = AddProperty(key);
}
SetPropertyValueName(dataNode, newName);
return dataNode;
}
void PlistDictionary::SetPropertyValueName(XmlNode* node, const char* newName)
{
node->name(m_document->allocate_string(newName));
}
bool PlistDictionary::ContainsValidDict(XmlDocument* plist)
{
XmlNode* node = plist->first_node("plist");
if (node != nullptr)
{
node = node->first_node("dict");
if (node != nullptr)
{
return true;
}
}
return false;
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,62 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/XML/rapidxml.h>
namespace ProjectSettingsTool
{
// Wraps a plist dict node with a friendly api to allow lookups and other actions in it
class PlistDictionary
{
public:
// Constructs the dictionary wrapper
PlistDictionary(AZ::rapidxml::xml_document<char>* plist);
// Allocates a new node with given name and value
AZ::rapidxml::xml_node<char>* MakeNode(const char* name, const char* value);
AZ::rapidxml::xml_node<char>* MakeNode();
// Returns pointer to property data node
AZ::rapidxml::xml_node<char>* GetPropertyValueNode(const char* key);
// Creates a new property and returns pointer to its value node
AZ::rapidxml::xml_node<char>* AddProperty(const char* key);
// Removes property from dictionary
void RemoveProperty(const char* key);
// Returns pointer to property data value or nullptr if it is empty
const char* GetPropertyValue(const char* key);
const char* GetPropertyValue(AZ::rapidxml::xml_node<char>* node);
// Returns pointer to property data name or nullptr if it is empty
const char* GetPropertyValueName(const char* key);
const char* GetPropertyValueName(AZ::rapidxml::xml_node<char>* node);
// Changes value of property data and creates it if it doesn't exist
AZ::rapidxml::xml_node<char>* SetPropertyValue(const char* key, const char* newValue);
void SetPropertyValue(AZ::rapidxml::xml_node<char>* node, const char* newValue);
// Changes name of property data and creates it if it doesn't exist
AZ::rapidxml::xml_node<char>* SetPropertyValueName(const char* key, const char* newName);
void SetPropertyValueName(AZ::rapidxml::xml_node<char>* node, const char* newName);
// Checks to make sure a plist file has a valid dictionary
static bool ContainsValidDict(AZ::rapidxml::xml_document<char>* plist);
protected:
// Returns pointer to property key node
AZ::rapidxml::xml_node<char>* GetPropertyKeyNode(const char* key);
// pList dictionary is found in
AZ::rapidxml::xml_document<char>* m_document;
// The dictionary of properties
AZ::rapidxml::xml_node<char>* m_dict;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,324 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "ProjectSettingsContainer.h"
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/stringbuffer.h>
#include <AzCore/XML/rapidxml_print.h>
#include <Util/FileUtil.h>
namespace ProjectSettingsTool
{
using namespace AZ;
using StringOutcome = Outcome<void, AZStd::string>;
using XmlDocument = rapidxml::xml_document<char>;
using XmlNode = rapidxml::xml_node<char>;
const int xmlFlags = rapidxml::parse_doctype_node | rapidxml::parse_declaration_node | rapidxml::parse_no_data_nodes;
SettingsError::SettingsError(const AZStd::string& error, const AZStd::string& reason)
{
m_error = error;
m_reason = reason;
}
StringOutcome WriteConfigFile(const AZStd::string& fileName, const AZStd::string& fileContents)
{
using AZ::IO::SystemFile;
const char* filePath = fileName.c_str();
// Attempt to make file writable or check it out in source control
if (CFileUtil::OverwriteFile(filePath))
{
if (!CFileUtil::CreateDirectory(fileName.substr(0, fileName.find_last_of('/')).data()))
{
return AZ::Failure(AZStd::string::format("Could not create the directory for file \"%s\".", filePath));
}
SystemFile settingsFile;
if (!settingsFile.Open(filePath, SystemFile::SF_OPEN_WRITE_ONLY | SystemFile::SF_OPEN_CREATE))
{
return AZ::Failure(AZStd::string::format("Failed to open settings file %s for write.", filePath));
}
if (settingsFile.Write(fileContents.c_str(), fileContents.size()) != fileContents.size())
{
return AZ::Failure(AZStd::string::format("Failed to write to file %s.", filePath));
}
settingsFile.Close();
return AZ::Success();
}
return AZ::Failure(AZStd::string::format("Could not check out or make file writable: \"%s\".", filePath));
}
StringOutcome ReadConfigFile(const AZStd::string& fileName, AZStd::string& fileContents)
{
using AZ::IO::SystemFile;
const char* filePath = fileName.c_str();
if (!SystemFile::Exists(filePath))
{
return AZ::Failure(AZStd::string::format("%s file doesn't exist.", filePath));
}
SystemFile settingsFile;
if (!settingsFile.Open(filePath, SystemFile::SF_OPEN_READ_ONLY))
{
return AZ::Failure(AZStd::string::format("Failed to open settings file %s.", filePath));
}
fileContents = AZStd::string(settingsFile.Length(), '\0');
settingsFile.Read(fileContents.size(), fileContents.data());
settingsFile.Close();
return AZ::Success();
}
ProjectSettingsContainer::ProjectSettingsContainer(const AZStd::string& projectJsonFileName, PlistInitVector& plistPaths)
: m_projectJson(JsonSettings{ projectJsonFileName, "", AZStd::make_unique<rapidjson::Document>() })
{
LoadProjectJsonData();
for (PlatformAndPath& plistInfo : plistPaths)
{
m_pListsMap.insert(AZStd::pair<PlatformId, PlistSettings>(
plistInfo.first,
PlistSettings{ plistInfo.second, "", AZStd::make_unique<XmlDocument>() }));
// We will have to find it then because unique_ptrs are not copy constructible
// And a move constructor would copy large strings
LoadPlist(m_pListsMap.find(plistInfo.first)->second);
}
}
ProjectSettingsContainer::~ProjectSettingsContainer()
{
}
ProjectSettingsContainer::PlistSettings* ProjectSettingsContainer::GetPlistSettingsForPlatform(const Platform& plat)
{
PlistSettings* result = nullptr;
if (plat.m_type == PlatformDataType::Plist)
{
auto iter = m_pListsMap.find(plat.m_id);
if (iter != m_pListsMap.end())
{
result = &iter->second;
}
else
{
AZ_Assert(false, "Failed to find pList for platform.");
}
}
return result;
}
bool ProjectSettingsContainer::IsPlistPlatform(const Platform& plat)
{
if (plat.m_type == PlatformDataType::Plist)
{
auto iter = m_pListsMap.find(plat.m_id);
if (iter != m_pListsMap.end())
{
return true;
}
}
return false;
}
AZ::Outcome<void, SettingsError> ProjectSettingsContainer::GetError()
{
if (!m_errors.empty())
{
SettingsError error = m_errors.front();
m_errors.pop();
return AZ::Failure(error);
}
return AZ::Success();
}
void ProjectSettingsContainer::SavePlatformData(const Platform& plat)
{
PlistSettings* plistSettings = GetPlistSettingsForPlatform(plat);
if (plistSettings != nullptr)
{
SavePlist(*plistSettings);
}
else
{
SaveProjectJsonData();
}
}
void ProjectSettingsContainer::SaveProjectJsonData()
{
// Needed to write a document out to a string
rapidjson::StringBuffer jsonDataBuffer;
// Use pretty writer so it can be read easier
rapidjson::PrettyWriter<rapidjson::StringBuffer> jsonDatawriter(jsonDataBuffer);
m_projectJson.m_document->Accept(jsonDatawriter);
const AZStd::string jsonDataString = jsonDataBuffer.GetString();
StringOutcome outcome = WriteConfigFile(m_projectJson.m_path, jsonDataString);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError("Failed to save project.json", outcome.GetError()));
}
}
void ProjectSettingsContainer::ReloadProjectJsonData()
{
m_projectJson.m_document.reset(new rapidjson::Document);
LoadProjectJsonData();
}
void ProjectSettingsContainer::SavePlistsData()
{
for (AZStd::pair<PlatformId, PlistSettings>& plist : m_pListsMap)
{
LoadPlist(plist.second);
}
}
void ProjectSettingsContainer::SavePlistData(const Platform& plat)
{
PlistSettings* settings = GetPlistSettingsForPlatform(plat);
if (settings != nullptr)
{
SavePlist(*settings);
}
}
void ProjectSettingsContainer::ReloadPlistData()
{
for (AZStd::pair<PlatformId, PlistSettings>& plist : m_pListsMap)
{
PlistSettings& plistSettings = plist.second;
plistSettings.m_document.reset(new XmlDocument());
LoadPlist(plistSettings);
}
}
rapidjson::Document& ProjectSettingsContainer::GetProjectJsonDocument()
{
return *m_projectJson.m_document.get();
}
AZ::Outcome<rapidjson::Value*, void> ProjectSettingsContainer::GetProjectJsonValue(const char* key)
{
// Try to find member
rapidjson::Document& settings = *m_projectJson.m_document;
rapidjson::Value::MemberIterator memberIterator = settings.FindMember(key);
if (memberIterator != settings.MemberEnd())
{
return AZ::Success(&memberIterator->value);
}
else
{
settings.AddMember(rapidjson::Value(key, settings.GetAllocator()),
rapidjson::Value(rapidjson::kNullType), settings.GetAllocator());
return AZ::Success(&settings.FindMember(key)->value);
}
}
AZStd::unique_ptr<PlistDictionary> ProjectSettingsContainer::GetPlistDictionary(const Platform& plat)
{
if (plat.m_type == PlatformDataType::Plist)
{
PlistSettings* settings = GetPlistSettingsForPlatform(plat);
if (settings != nullptr)
{
if (PlistDictionary::ContainsValidDict(settings->m_document.get()))
{
return AZStd::make_unique<PlistDictionary>(settings->m_document.get());
}
else
{
//TODO: Query user if they would like to remake a valid plist then do it
AZStd::string platformName;
switch (plat.m_id)
{
case PlatformId::Ios:
platformName = "iOS";
break;
default:
platformName = "unknown";
break;
}
AZ_Assert(false, "%s pList is in invalid state.", platformName.c_str());
return nullptr;
}
}
}
AZ_Assert(false, "This platform does not use pLists to store data.");
return nullptr;
}
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& ProjectSettingsContainer::GetProjectJsonAllocator()
{
return m_projectJson.m_document->GetAllocator();
}
const char* ProjectSettingsContainer::GetFailedLoadingPlistText()
{
return "Failed to load info.plist";
}
void ProjectSettingsContainer::LoadProjectJsonData()
{
StringOutcome outcome = ReadConfigFile(m_projectJson.m_path, m_projectJson.m_rawData);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError("Failed to load project.json", outcome.GetError()));
}
m_projectJson.m_document->Parse(m_projectJson.m_rawData.c_str());
}
// Loads info.plist for iOS from disk
void ProjectSettingsContainer::LoadPlist(PlistSettings& plistSettings)
{
StringOutcome outcome = ReadConfigFile(plistSettings.m_path, plistSettings.m_rawData);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError(GetFailedLoadingPlistText(), outcome.GetError()));
}
plistSettings.m_document->parse<xmlFlags>(plistSettings.m_rawData.data());
}
void ProjectSettingsContainer::SavePlist(PlistSettings& plistSettings)
{
// Needed to write a document out to a string
AZStd::string xmlDocString;
rapidxml::print(std::back_inserter(xmlDocString), *plistSettings.m_document);
StringOutcome outcome = WriteConfigFile(plistSettings.m_path, xmlDocString);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError("Failed to save info.pList", outcome.GetError()));
}
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,113 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "Platforms.h"
#include "PlistDictionary.h"
#include <AzCore/JSON/document.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace ProjectSettingsTool
{
struct SettingsError
{
SettingsError(const AZStd::string& error, const AZStd::string& reason);
// The error that occurred
AZStd::string m_error;
// The reason the error occurred
AZStd::string m_reason;
};
// Loads, saves, and provides access to all of the project settings files
class ProjectSettingsContainer
{
public:
typedef AZStd::pair<PlatformId, AZStd::string> PlatformAndPath;
typedef AZStd::vector<PlatformAndPath> PlistInitVector;
template<class DocType>
struct PlatformSettings
{
// File path to document
AZStd::string m_path;
// Raw string loaded from file
AZStd::string m_rawData;
// The document itself
AZStd::unique_ptr<DocType> m_document;
};
using JsonSettings = PlatformSettings<rapidjson::Document>;
using PlistSettings = PlatformSettings<AZ::rapidxml::xml_document<char>>;
// Constructs the main manager of a document
ProjectSettingsContainer(const AZStd::string& projectJsonFileName, PlistInitVector& plistPaths);
// Used to destroy valueDoesNotExist
~ProjectSettingsContainer();
// Returns the PlistSettings for given platform
PlistSettings* GetPlistSettingsForPlatform(const Platform& plat);
// Returns true if PlistSettings are found for platform
bool IsPlistPlatform(const Platform& plat);
// Gets the earliest error not seen
AZ::Outcome<void, SettingsError> GetError();
// Save settings for given platform
void SavePlatformData(const Platform& plat);
// Saves project.json to disk
void SaveProjectJsonData();
// Reloads Project.json from disk
void ReloadProjectJsonData();
// Save all pLists back to disk
void SavePlistsData();
// Save platform's plist data back to disk
void SavePlistData(const Platform& plat);
// Reloads all plists from disk
void ReloadPlistData();
// Returns a reference to the project.json Document
rapidjson::Document& GetProjectJsonDocument();
// Gets reference to value in project.json
// returns null type if not found
AZ::Outcome<rapidjson::Value*, void> GetProjectJsonValue(const char* key);
AZStd::unique_ptr<PlistDictionary> GetPlistDictionary(const Platform& plat);
// Returns the allocator used by ProjectJson
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& GetProjectJsonAllocator();
static const char* GetFailedLoadingPlistText();
protected:
// Loads project.json from disk
void LoadProjectJsonData();
// Loads info.plist from filePath into given document
void LoadPlist(PlistSettings& plistSettings);
void SavePlist(PlistSettings& plistSettings);
// Errors that have occurred
AZStd::queue<SettingsError> m_errors;
// The project.json document
JsonSettings m_projectJson;
// A map to all of the loaded pLists
AZStd::unordered_map<PlatformId, PlistSettings> m_pListsMap;
private:
AZ_DISABLE_COPY_MOVE(ProjectSettingsContainer);
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,919 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "ProjectSettingsSerialization.h"
#include "PlatformSettings_common.h"
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
// Needed to overwrite ios icons and launchscreens
#include <Util/FileUtil.h>
namespace ProjectSettingsTool
{
using XmlNode = AZ::rapidxml::xml_node<char>;
const char* stringStr = "string";
const char* arrayStr = "array";
const char* trueStr = "true";
const char* noDocumentError = "No json or xml document to use for Project Settings Tool serialization.";
Serializer::Serializer(AzToolsFramework::InstanceDataNode* root)
: m_root(root)
, m_jsonDoc(nullptr)
, m_jsonRoot(nullptr)
, m_idString(AZ::SerializeTypeInfo<AZStd::string>::GetUuid())
, m_idInt(AZ::SerializeTypeInfo<int>::GetUuid())
, m_idBool(AZ::SerializeTypeInfo<bool>::GetUuid())
{}
Serializer::Serializer(AzToolsFramework::InstanceDataNode* root, rapidjson::Document* doc, rapidjson::Value* jsonRoot)
: Serializer(root)
{
SetDocumentRoot(doc);
if (jsonRoot != nullptr)
{
SetJsonRoot(jsonRoot);
}
else
{
SetJsonRoot(doc);
}
}
Serializer::Serializer(AzToolsFramework::InstanceDataNode* root, AZStd::unique_ptr<PlistDictionary> dict)
: Serializer(root)
{
SetDocumentRoot(AZStd::move(dict));
}
void Serializer::SetDocumentRoot(rapidjson::Document* doc)
{
m_jsonDoc = doc;
}
void Serializer::SetJsonRoot(rapidjson::Value* jsonRoot)
{
m_jsonRoot = jsonRoot;
}
void Serializer::SetDocumentRoot(AZStd::unique_ptr<PlistDictionary> dict)
{
m_plistDict = AZStd::move(dict);
}
bool Serializer::UiEqualToSettings() const
{
if (m_jsonRoot != nullptr)
{
return UiEqualToJson(m_jsonRoot);
}
else if (m_plistDict)
{
return UiEqualToPlist(m_root);
}
else
{
AZ_Assert(false, noDocumentError);
return false;
}
}
void Serializer::LoadFromSettings()
{
if (m_jsonRoot != nullptr)
{
LoadFromSettings(m_jsonRoot);
}
else if (m_plistDict)
{
LoadFromSettings(m_root);
}
else
{
AZ_Assert(false, noDocumentError);
}
}
void Serializer::SaveToSettings()
{
if (m_jsonRoot != nullptr)
{
SaveToSettings(m_jsonRoot);
}
else if (m_plistDict)
{
SaveToSettings(m_root);
}
else
{
AZ_Assert(false, noDocumentError);
}
}
bool Serializer::UiEqualToJson(rapidjson::Value* root) const
{
return UiEqualToJson(root, m_root);
}
void Serializer::LoadFromSettings(rapidjson::Value* root)
{
LoadFromSettings(root, m_root);
}
void Serializer::SaveToSettings(rapidjson::Value* root)
{
// This value needs to be an object to add members
if (!root->IsObject())
{
root->SetObject();
}
SaveToSettings(root, m_root);
}
bool Serializer::UiEqualToJson(rapidjson::Value* root, AzToolsFramework::InstanceDataNode* node) const
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
bool noDocElement = true;
rapidjson::Value::MemberIterator jsonMember;
const char* propertyName = childMeta->m_name;
// If there is a json object try to find the member
if (root != nullptr)
{
jsonMember = root->FindMember(propertyName);
if (jsonMember != root->MemberEnd())
{
noDocElement = false;
}
}
AZ::Uuid type = childMeta->m_typeId;
if (m_idString == type)
{
AZStd::string uiValue;
childNode.Read(uiValue);
if (!uiValue.empty() && !noDocElement)
{
if (jsonMember->value.IsString())
{
if (uiValue != jsonMember->value.GetString())
{
return false;
}
}
else
{
return false;
}
}
else if (uiValue.empty() != noDocElement)
{
return false;
}
}
else if (m_idInt == type)
{
int uiValue;
childNode.Read(uiValue);
if (!noDocElement)
{
if (jsonMember->value.IsInt())
{
if (uiValue != jsonMember->value.GetInt())
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else if (m_idBool == type)
{
bool uiValue;
childNode.Read(uiValue);
if (uiValue)
{
if (!noDocElement)
{
if (jsonMember->value.IsString())
{
AZStd::string expectedJsonValue = trueStr;
if (expectedJsonValue != jsonMember->value.GetString())
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else if (!noDocElement)
{
return false;
}
}
// Should be a class with members instead of base data type
else
{
rapidjson::Value* jsonNode = nullptr;
if (!noDocElement)
{
jsonNode = &jsonMember->value;
}
// Drill into class
if (!UiEqualToJson(jsonNode, &childNode))
{
return false;
}
}
}
}
}
return true;
}
bool Serializer::UiEqualToPlist(AzToolsFramework::InstanceDataNode* node) const
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
bool noDocElement = true;
const char* propertyName = childMeta->m_name;
XmlNode* plistNode = m_plistDict->GetPropertyValueNode(propertyName);
if (plistNode != nullptr)
{
noDocElement = false;
}
AZ::Uuid type = childMeta->m_typeId;
if (m_idString == type)
{
AZStd::string uiValue;
childNode.Read(uiValue);
if (!uiValue.empty() && !noDocElement)
{
if (AZStd::string(plistNode->name()) == stringStr)
{
if (uiValue != plistNode->value())
{
return false;
}
}
else
{
return false;
}
}
else if (uiValue.empty() != noDocElement)
{
return false;
}
}
else if (m_idBool == type)
{
bool uiValue;
childNode.Read(uiValue);
if (uiValue)
{
if (!noDocElement)
{
if (AZStd::string(plistNode->name()) != trueStr)
{
return false;
}
}
else
{
return false;
}
}
else if (!noDocElement)
{
return false;
}
}
else if (AZStd::string(childNode.GetClassMetadata()->m_name) == "IosOrientations")
{
// Enter even if nullptr to make sure all values should be false
if (plistNode == nullptr || AZStd::string(plistNode->name()) == arrayStr)
{
if (!UiEqualToPlistArray(plistNode, &childNode))
{
return false;
}
}
else
{
return false;
}
}
else
{
if (!UiEqualToPlistImages(&childNode))
{
return false;
}
}
}
}
}
return true;
}
bool Serializer::UiEqualToPlistArray(AZ::rapidxml::xml_node<char>* array, AzToolsFramework::InstanceDataNode* node) const
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
const AZ::SerializeContext::ClassElement* childMeta = childNode.GetElementMetadata();
if (childMeta)
{
AZ::Uuid type = childMeta->m_typeId;
if (m_idBool == type)
{
const char* propertyName = childMeta->m_name;
bool uiValue;
childNode.Read(uiValue);
bool found = false;
if (array != nullptr)
{
for (XmlNode* plistNode = array->first_node(); plistNode != nullptr; plistNode = plistNode->next_sibling())
{
if (AZStd::string(plistNode->value()) == propertyName)
{
if (!uiValue)
{
return false;
}
else
{
found = true;
}
break;
}
}
}
if (uiValue && !found)
{
return false;
}
}
}
}
}
return true;
}
bool Serializer::UiEqualToPlistImages(AzToolsFramework::InstanceDataNode* node) const
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
AZ::Uuid type = childMeta->m_typeId;
if (m_idString == type)
{
AZStd::string uiValue;
childNode.Read(uiValue);
if (!uiValue.empty())
{
return false;
}
}
}
}
}
return true;
}
void Serializer::LoadFromSettings(rapidjson::Value* root, AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
const char* propertyName = childMeta->m_name;
AZ::Uuid type = childMeta->m_typeId;
if (root != nullptr)
{
rapidjson::Value::MemberIterator jsonMember = root->FindMember(propertyName);
if (jsonMember != root->MemberEnd())
{
rapidjson::Value& jsonNode = jsonMember->value;
if (m_idString == type)
{
if (jsonNode.IsString())
{
AZStd::string jsonValue = jsonNode.GetString();
childNode.Write(jsonValue);
}
}
else if (m_idInt == type)
{
if (jsonNode.IsInt())
{
int jsonValue = jsonNode.GetInt();
childNode.Write(jsonValue);
}
}
else if (m_idBool == type)
{
if (jsonNode.IsString())
{
bool value = false;
AZStd::string jsonValue = jsonNode.GetString();
if (jsonValue == trueStr)
{
value = true;
}
childNode.Write(value);
}
}
// Should be a class with members instead of base data type
else
{
if (jsonNode.IsObject())
{
// Drill into class
LoadFromSettings(&jsonNode, &childNode);
}
}
}
else
{
SetDefaults(childNode, type);
}
}
else
{
SetDefaults(childNode, type);
}
}
}
}
}
void Serializer::SaveToSettings(rapidjson::Value* root, AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
const char* propertyName = childMeta->m_name;
rapidjson::Value::MemberIterator jsonMember = root->FindMember(propertyName);
if (jsonMember == root->MemberEnd())
{
root->AddMember(rapidjson::Value(propertyName, m_jsonDoc->GetAllocator())
, rapidjson::Value(rapidjson::Type::kNullType)
, m_jsonDoc->GetAllocator());
jsonMember = root->FindMember(propertyName);
}
rapidjson::Value& jsonNode = jsonMember->value;
AZ::Uuid type = childMeta->m_typeId;
if (m_idString == type)
{
AZStd::string uiValue;
childNode.Read(uiValue);
if (!uiValue.empty())
{
jsonNode.SetString(uiValue.data(), m_jsonDoc->GetAllocator());
}
else
{
root->RemoveMember(propertyName);
}
}
else if (m_idInt == type)
{
int uiValue;
childNode.Read(uiValue);
jsonNode.SetInt(uiValue);
}
else if (m_idBool == type)
{
bool uiValue;
childNode.Read(uiValue);
if (uiValue)
{
jsonNode.SetString(trueStr, m_jsonDoc->GetAllocator());
}
else
{
root->RemoveMember(propertyName);
}
}
// Should be a class with members instead of base data type
else
{
// This value needs to be an object to add members
if (!jsonNode.IsObject())
{
jsonNode.SetObject();
}
// Drill into class
SaveToSettings(&jsonNode, &childNode);
if (jsonNode.ObjectEmpty())
{
root->RemoveMember(propertyName);
}
}
}
}
}
}
void Serializer::LoadFromSettings(AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
const AZ::SerializeContext::ClassElement* childMeta = childNode.GetElementMetadata();
if (childMeta)
{
const char* propertyName = childMeta->m_name;
XmlNode* plistNode = m_plistDict->GetPropertyValueNode(propertyName);
AZ::Uuid type = childMeta->m_typeId;
if (plistNode != nullptr)
{
if (m_idString == type)
{
if (AZStd::string(plistNode->name()) == stringStr)
{
AZStd::string plistValue = plistNode->value();
childNode.Write(plistValue);
}
}
else if (m_idBool == type)
{
if (AZStd::string(plistNode->name()) == trueStr)
{
childNode.Write(true);
}
}
else if (AZStd::string(childNode.GetClassMetadata()->m_name) == "IosOrientations")
{
// Make sure it seems like an array in plist as well
if (AZStd::string(plistNode->name()) == arrayStr)
{
LoadOrientations(plistNode, &childNode);
}
}
else
{
SetClassToDefaults(&childNode);
}
}
else
{
SetDefaults(childNode, type);
}
}
}
}
}
void Serializer::LoadOrientations(XmlNode* array, AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
const AZ::SerializeContext::ClassElement* childMeta = childNode.GetElementMetadata();
if (childMeta)
{
const char* propertyName = childMeta->m_name;
bool found = false;
if (array != nullptr)
{
for (XmlNode* plistNode = array->first_node(); plistNode != nullptr; plistNode = plistNode->next_sibling())
{
if (AZStd::string(plistNode->value()) == propertyName)
{
AZ::Uuid type = childMeta->m_typeId;
if (m_idBool == type)
{
if (AZStd::string(plistNode->name()) == stringStr)
{
childNode.Write(true);
found = true;
}
}
else
{
AZ_Assert(false, "Unsupported type \"%s\" found in array.", childMeta->m_editData->m_name)
}
break;
}
}
}
if (!found)
{
childNode.Write(false);
}
}
}
}
}
void Serializer::SetClassToDefaults(AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
AZ::Uuid type = childMeta->m_typeId;
SetDefaults(childNode, type);
}
}
}
}
void Serializer::SetDefaults(AzToolsFramework::InstanceDataNode& node, const AZ::Uuid& type)
{
if (m_idString == type)
{
node.Write(AZStd::string(""));
return;
}
else if (m_idBool == type)
{
node.Write(false);
return;
}
else if (m_idInt == type)
{
node.Write(0);
return;
}
if (AZStd::string(node.GetClassMetadata()->m_name) == "IosOrientations")
{
LoadOrientations(nullptr, &node);
}
else
{
SetClassToDefaults(&node);
}
}
void Serializer::SaveToSettings(AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
const char* propertyName = childMeta->m_name;
AZ::Uuid type = childMeta->m_typeId;
if (m_idString == type)
{
AZStd::string uiValue;
childNode.Read(uiValue);
if (!uiValue.empty())
{
m_plistDict->SetPropertyValue(propertyName, uiValue.data());
}
else
{
m_plistDict->RemoveProperty(propertyName);
}
}
else if (m_idBool == type)
{
bool uiValue;
childNode.Read(uiValue);
if (uiValue)
{
m_plistDict->SetPropertyValueName(propertyName, trueStr);
m_plistDict->SetPropertyValue(propertyName, "");
}
else
{
m_plistDict->RemoveProperty(propertyName);
}
}
else if (AZStd::string(childNode.GetClassMetadata()->m_name) == "IosOrientations")
{
XmlNode* plistNode = m_plistDict->GetPropertyValueNode(propertyName);
if (plistNode == nullptr)
{
plistNode = m_plistDict->SetPropertyValueName(propertyName, arrayStr);
}
// Make sure the plist says this is an array type
if (AZStd::string(plistNode->name()) == arrayStr)
{
if (!SaveOrientations(plistNode, &childNode))
{
m_plistDict->RemoveProperty(propertyName);
}
}
}
//Assume this is a class with image overrides
else
{
OverwriteImages(&childNode);
}
}
}
}
}
bool Serializer::SaveOrientations(XmlNode* array, AzToolsFramework::InstanceDataNode* node)
{
bool anyEnabled = false;
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
const AZ::SerializeContext::ClassElement* childMeta = childNode.GetElementMetadata();
if (childMeta)
{
AZ::Uuid type = childMeta->m_typeId;
if (m_idBool == type)
{
const char* propertyName = childMeta->m_name;
bool uiValue;
childNode.Read(uiValue);
if (uiValue)
{
anyEnabled = true;
}
bool found = false;
for (XmlNode* plistNode = array->first_node(); plistNode != nullptr; plistNode = plistNode->next_sibling())
{
if (AZStd::string(plistNode->value()) == propertyName)
{
if (!uiValue)
{
array->remove_node(plistNode);
}
else
{
found = true;
}
break;
}
}
if (uiValue && !found)
{
array->append_node(m_plistDict->MakeNode(stringStr, propertyName));
}
}
}
}
}
return anyEnabled;
}
void Serializer::OverwriteImages(AzToolsFramework::InstanceDataNode* node)
{
const AZ::SerializeContext::ClassData* baseMeta = node->GetClassMetadata();
if (baseMeta)
{
for (AzToolsFramework::InstanceDataNode& childNode : node->GetChildren())
{
auto childMeta = childNode.GetElementMetadata();
if (childMeta)
{
AZ::Uuid type = childMeta->m_typeId;
if (m_idString == type)
{
AZStd::string uiValue;
childNode.Read(uiValue);
if (!uiValue.empty())
{
AZ::Edit::ElementData* childEditMeta = childMeta->m_editData;
if (childEditMeta != nullptr)
{
AZ::AttributeId handlerType = childEditMeta->m_elementId;
// Special handling for iOS image overrides, the source images must be overwritten.
if (handlerType == Handlers::ImagePreview)
{
AZ::Edit::Attribute* defaultPathAttr = childEditMeta->FindAttribute(Attributes::DefaultPath);
if (defaultPathAttr != nullptr)
{
AzToolsFramework::PropertyAttributeReader reader(defaultPathAttr->GetContextData(), defaultPathAttr);
AZStd::string defaultPath;
if (reader.Read<AZStd::string>(defaultPath))
{
QString defaultPathQString = defaultPath.data();
CFileUtil::OverwriteFile(defaultPathQString);
CFileUtil::CopyFile(QString(uiValue.data()), defaultPathQString);
// Clear the property so this isn't overwritten again for no reason
childNode.Write(AZStd::string(""));
}
}
else
{
AZ_Assert(false, "Could not find default path for \"%s\". Cannot override image.", childMeta->m_name)
}
}
}
}
}
else
{
AZ_Assert(false, "Unsupported type \"%s\" found in what should be image overrides.", childMeta->m_editData->m_name);
}
}
}
}
}
} // ProjectSettingsTool
@@ -0,0 +1,78 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "PlistDictionary.h"
#include <AzCore/JSON/document.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzToolsFramework
{
class InstanceDataNode;
}
namespace ProjectSettingsTool
{
class Serializer
{
public:
Serializer(AzToolsFramework::InstanceDataNode* root, rapidjson::Document* doc, rapidjson::Value* jsonRoot = nullptr);
Serializer(AzToolsFramework::InstanceDataNode* root, AZStd::unique_ptr<PlistDictionary> dict);
// Sets Json document
void SetDocumentRoot(rapidjson::Document* doc);
// Sets Json root
void SetJsonRoot(rapidjson::Value* jsonRoot);
// Sets pList dictionary
void SetDocumentRoot(AZStd::unique_ptr<PlistDictionary> dict);
// Returns true if all properties in ui are equal to settings
bool UiEqualToSettings() const;
// Loads properties into the ui from the settings
void LoadFromSettings();
// Saves properties from the ui to the settings
void SaveToSettings();
bool UiEqualToJson(rapidjson::Value* root) const;
void LoadFromSettings(rapidjson::Value* root);
void SaveToSettings(rapidjson::Value* root);
protected:
bool UiEqualToJson(rapidjson::Value* root, AzToolsFramework::InstanceDataNode* node) const;
bool UiEqualToPlist(AzToolsFramework::InstanceDataNode* node) const;
bool UiEqualToPlistArray(AZ::rapidxml::xml_node<char>* array, AzToolsFramework::InstanceDataNode* node) const;
bool UiEqualToPlistImages(AzToolsFramework::InstanceDataNode* node) const;
void LoadFromSettings(rapidjson::Value* root, AzToolsFramework::InstanceDataNode* node);
void LoadFromSettings(AzToolsFramework::InstanceDataNode* node);
void LoadOrientations(AZ::rapidxml::xml_node<char>* array, AzToolsFramework::InstanceDataNode* node);
void SetDefaults(AzToolsFramework::InstanceDataNode& node, const AZ::Uuid& type);
void SetClassToDefaults(AzToolsFramework::InstanceDataNode* node);
void SaveToSettings(rapidjson::Value* root, AzToolsFramework::InstanceDataNode* node);
void SaveToSettings(AzToolsFramework::InstanceDataNode* node);
bool SaveOrientations(AZ::rapidxml::xml_node<char>* array, AzToolsFramework::InstanceDataNode* node);
void OverwriteImages(AzToolsFramework::InstanceDataNode* node);
// The RPE root relative to the document's root
AzToolsFramework::InstanceDataNode* m_root;
// The Json document if using Json for this RPE
rapidjson::Document* m_jsonDoc;
// The root of the json for this serializer
rapidjson::Value* m_jsonRoot;
// The pList Dictionary wrapper if using a pList for this RPE
AZStd::unique_ptr<PlistDictionary> m_plistDict;
private:
Serializer(AzToolsFramework::InstanceDataNode* root);
// Uuid for AZStd::string
const AZ::Uuid m_idString;
// Uuid for int
const AZ::Uuid m_idInt;
// Uuid for bool
const AZ::Uuid m_idBool;
};
} // ProjectSettingsTool
@@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file alias="group_closed.png">icons/group_closed.png</file>
<file alias="group_open.png">icons/group_open.png</file>
<file alias="link.svg">icons/link.svg</file>
<file alias="broken_link.svg">icons/broken_link.svg</file>
</qresource>
</RCC>
@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProjectSettingsToolWidget</class>
<widget class="QWidget" name="ProjectSettingsToolWidget">
<property name="modal" stdset="0">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>853</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>640</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>Project Settings Tool</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="mainContentLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="0">
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>616</width>
<height>612</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_7">
<item row="1" column="0">
<widget class="QGroupBox" name="verticalGroupBox_3">
<property name="title">
<string>Platform Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="AzQtComponents::TabWidget" name="platformTabs">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="usesScrollButtons">
<bool>true</bool>
</property>
<property name="documentMode">
<bool>false</bool>
</property>
<property name="tabBarAutoHide">
<bool>false</bool>
</property>
<widget class="QWidget" name="androidTab">
<attribute name="title">
<string>Android</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_8"/>
</widget>
<widget class="QWidget" name="iosTab">
<attribute name="title">
<string> iOS </string>
</attribute>
<layout class="QGridLayout" name="gridLayout_9"/>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="baseSettingsGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Base Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout"/>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reloadButton">
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="reconfigureLog">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="ProjectSettingsTool.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,700 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "ProjectSettingsToolWindow.h"
#include "ui_ProjectSettingsToolWidget.h"
#include "DefaultImageValidator.h"
#include "PlatformSettings.h"
#include "ProjectSettingsContainer.h"
#include "ProjectSettingsValidator.h"
#include "PropertyImagePreview.h"
#include "PropertyFileSelect.h"
#include "PropertyLinked.h"
#include "Utils.h"
#include "ValidationHandler.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include "AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h"
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <Util/FileUtil.h>
#include <QMessageBox>
#include <QCloseEvent>
#include <QScrollBar>
#include <QTimer>
// The object name in json for android
const static char* androidSettings = "android_settings";
static bool g_serializeRegistered = false;
namespace ProjectSettingsTool
{
using XmlNode = AZ::rapidxml::xml_node<char>;
ProjectSettingsToolWindow::ProjectSettingsToolWindow(QWidget* parent)
: QWidget(parent)
, LastPathBus::Handler()
, m_ui(new Ui::ProjectSettingsToolWidget())
, m_reconfigureProcess()
, m_devRoot(GetDevRoot())
, m_projectRoot(GetProjectRoot())
, m_projectName(GetProjectName())
, m_plistsInitVector(
PlatformEnabled(PlatformId::Ios) ?
ProjectSettingsContainer::PlistInitVector({
ProjectSettingsContainer::PlatformAndPath
{ PlatformId::Ios, m_projectRoot + PlatformResourcesFolder(PlatformId::Ios) }
})
:
ProjectSettingsContainer::PlistInitVector())
, m_settingsContainer(AZStd::make_unique<ProjectSettingsContainer>(m_projectRoot + "/project.json", m_plistsInitVector))
, m_validator(AZStd::make_unique<Validator>())
, m_platformProperties()
, m_platformPropertyEditors()
, m_propertyHandlers()
, m_validationHandler(AZStd::make_unique<ValidationHandler>())
, m_linkHandler(nullptr)
// The default path to select images at
, m_lastImagesPath(QStringLiteral("%1Code%2/Resources")
.arg(m_projectRoot.c_str()
, m_projectName.c_str()))
, m_invalidState(false)
{
// Shows any and all errors that occurred during serialization with option to quit out on each one.
ShowAllErrorsThenExitIfInvalid();
if (!g_serializeRegistered)
{
ReflectPlatformClasses();
g_serializeRegistered = true;
}
InitializeUi();
RegisterHandlersAndBusses();
AddAllPlatformsToUi();
MakeSerializers();
if (m_invalidState)
{
// Exit for safety
return;
}
LoadPropertiesFromSettings();
m_linkHandler->LinkAllProperties();
// Hide the iOS tab if that platform is not enabled.
if (!PlatformEnabled(PlatformId::Ios))
{
m_ui->platformTabs->removeTab(m_ui->platformTabs->indexOf(m_ui->iosTab));
}
}
ProjectSettingsToolWindow::~ProjectSettingsToolWindow()
{
UnregisterHandlersAndBusses();
}
QString ProjectSettingsToolWindow::GetLastImagePath()
{
return m_lastImagesPath;
}
void ProjectSettingsToolWindow::SetLastImagePath(const QString& path)
{
m_lastImagesPath = path;
}
FunctorValidator* ProjectSettingsToolWindow::GetValidator(FunctorValidator::FunctorType functor)
{
return m_validator->GetQValidator(functor);
}
void ProjectSettingsToolWindow::TrackValidator(FunctorValidator* validator)
{
m_validator->TrackThisValidator(validator);
}
void ProjectSettingsToolWindow::ReflectPlatformClasses()
{
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
BaseSettings::Reflect(context);
AndroidSettings::Reflect(context);
IosSettings::Reflect(context);
}
void ProjectSettingsToolWindow::RegisterHandlersAndBusses()
{
m_propertyHandlers.push_back(PropertyFuncValLineEditHandler::Register(m_validationHandler.get()));
m_propertyHandlers.push_back(PropertyFuncValBrowseEditHandler::Register(m_validationHandler.get()));
m_propertyHandlers.push_back(PropertyFileSelectHandler::Register(m_validationHandler.get()));
m_propertyHandlers.push_back(PropertyImagePreviewHandler::Register(m_validationHandler.get()));
m_linkHandler = PropertyLinkedHandler::Register(m_validationHandler.get());
m_propertyHandlers.push_back(m_linkHandler);
LastPathBus::Handler::BusConnect();
ValidatorBus::Handler::BusConnect();
}
void ProjectSettingsToolWindow::UnregisterHandlersAndBusses()
{
ValidatorBus::Handler::BusDisconnect();
LastPathBus::Handler::BusDisconnect();
for (AzToolsFramework::PropertyHandlerBase* handler : m_propertyHandlers)
{
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
&AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::UnregisterPropertyType,
handler);
delete handler;
}
}
void ProjectSettingsToolWindow::MakeSerializerJson(const Platform& plat, AzToolsFramework::InstanceDataHierarchy& hierarchy, rapidjson::Document* doc)
{
m_platformSerializers[static_cast<unsigned>(plat.m_id)] = AZStd::make_unique<Serializer>(hierarchy.GetRoot(), doc);
}
void ProjectSettingsToolWindow::MakeSerializerJsonNonRoot(const Platform& plat, AzToolsFramework::InstanceDataHierarchy& hierarchy, rapidjson::Document* doc, rapidjson::Value* jsonRoot)
{
m_platformSerializers[static_cast<unsigned>(plat.m_id)] = AZStd::make_unique<Serializer>(hierarchy.GetRoot(), doc, jsonRoot);
}
void ProjectSettingsToolWindow::MakeSerializerPlist(const Platform& plat, AzToolsFramework::InstanceDataHierarchy& hierarchy, PlistDictionary* dict)
{
m_platformSerializers[static_cast<unsigned>(plat.m_id)] = AZStd::make_unique<Serializer>(hierarchy.GetRoot(), AZStd::unique_ptr<PlistDictionary>(dict));
}
void ProjectSettingsToolWindow::closeEvent(QCloseEvent* event)
{
if (!m_invalidState)
{
// Check if ui is loaded
if (m_ui->saveButton != nullptr)
{
// Save button is used as an inverse bool to tell if configure is being run or settings are being saved
if (m_ui->saveButton->isEnabled())
{
if (!UiEqualToSettings())
{
int result = QMessageBox::question
(
this,
tr("Warning"),
tr("There are currently unsaved changes. Are you sure you want to cancel?"),
QMessageBox::Yes,
QMessageBox::No
);
if (QMessageBox::Yes == result)
{
QWidget::closeEvent(event);
}
else
{
event->setAccepted(false);
}
}
else
{
QWidget::closeEvent(event);
}
}
else
{
QMessageBox::information
(
this,
tr("Info"),
tr("Cannot close until settings have been reconfigured."),
QMessageBox::Ok
);
event->setAccepted(false);
}
}
else
{
QWidget::closeEvent(event);
}
}
else
{
QWidget::closeEvent(event);
}
}
void ProjectSettingsToolWindow::ForceClose()
{
m_invalidState = true;
// Potentially called from the constructor, when the widget/window aren't properly set up, so delay this until after it's all setup
QTimer::singleShot(0, this, [this]() {window()->close();} );
}
bool ProjectSettingsToolWindow::IfErrorShowThenExit()
{
// Grabs the earliest unseen error popping it off the error queue
AZ::Outcome<void, SettingsError> error = m_settingsContainer->GetError();
if (!error.IsSuccess())
{
bool shouldAbort = error.GetError().m_error == m_settingsContainer->GetFailedLoadingPlistText();
QMessageBox::StandardButton result = QMessageBox::critical
(
this,
error.GetError().m_error.c_str(),
error.GetError().m_reason.c_str(),
shouldAbort ? QMessageBox::Abort : QMessageBox::StandardButtons(QMessageBox::Ok | QMessageBox::Abort),
shouldAbort ? QMessageBox::Abort : QMessageBox::Ok
);
if (result == QMessageBox::Abort)
{
ForceClose();
}
return true;
}
return false;
}
void ProjectSettingsToolWindow::ShowAllErrorsThenExitIfInvalid()
{
while (IfErrorShowThenExit())
{
if (m_invalidState)
{
// Exit for safety
return;
}
}
}
void ProjectSettingsToolWindow::InitializeUi()
{
// setup
m_ui->setupUi(this);
AzQtComponents::TabWidget::applySecondaryStyle(m_ui->platformTabs, false);
ResizeTabs(m_ui->platformTabs->currentIndex());
m_ui->reconfigureLog->hide();
// connects
connect
(
&m_reconfigureProcess,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this,
[this]()
{
m_ui->saveButton->setEnabled(true);
m_ui->reloadButton->setEnabled(true);
m_ui->reconfigureLog->insertPlainText(tr("\n Reconfiguration Finished"));
QScrollBar* scrollbar = m_ui->reconfigureLog->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
);
connect(&m_reconfigureProcess, &QProcess::readyReadStandardOutput, this,
[this]()
{
m_ui->reconfigureLog->insertPlainText(m_reconfigureProcess.readAllStandardOutput());
if (!m_ui->reconfigureLog->textCursor().hasSelection())
{
QScrollBar* scrollbar = m_ui->reconfigureLog->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
});
connect(&m_reconfigureProcess, &QProcess::readyReadStandardError, this,
[this]()
{
m_ui->reconfigureLog->insertPlainText(m_reconfigureProcess.readAllStandardError());
if (!m_ui->reconfigureLog->textCursor().hasSelection())
{
QScrollBar* scrollbar = m_ui->reconfigureLog->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
});
connect(m_ui->platformTabs, &QTabWidget::currentChanged,
this, &ProjectSettingsToolWindow::ResizeTabs);
connect(m_ui->saveButton, &QPushButton::clicked, this,
&ProjectSettingsToolWindow::SaveSettingsFromUi);
connect(m_ui->reloadButton, &QPushButton::clicked, this,
&ProjectSettingsToolWindow::ReloadUiFromSettings);
}
void ProjectSettingsToolWindow::ResizeTabs(int index)
{
for (int i = 0; i < m_ui->platformTabs->count(); i++)
{
if (i != index)
{
m_ui->platformTabs->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
}
// resize for current tab
m_ui->platformTabs->widget(index)->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_ui->platformTabs->widget(index)->resize(m_ui->platformTabs->widget(index)->minimumSizeHint());
m_ui->platformTabs->widget(index)->adjustSize();
}
void ProjectSettingsToolWindow::AddAllPlatformsToUi()
{
for (int plat = 0; plat < static_cast<unsigned long long>(PlatformId::NumPlatformIds); ++plat)
{
AddPlatformToUi(Platforms[plat]);
}
}
void ProjectSettingsToolWindow::AddPlatformToUi(const Platform& plat)
{
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
QWidget* parent = nullptr;
void* dataForPropertyEditor = nullptr;
AZ::TypeId dataTypeID;
switch (plat.m_id)
{
case PlatformId::Base:
parent = m_ui->baseSettingsGroupBox;
dataForPropertyEditor = &m_platformProperties.base;
dataTypeID = m_platformProperties.base.TYPEINFO_Uuid();
break;
case PlatformId::Android:
parent = m_ui->androidTab;
dataForPropertyEditor = &m_platformProperties.android;
dataTypeID = m_platformProperties.android.TYPEINFO_Uuid();
break;
case PlatformId::Ios:
parent = m_ui->iosTab;
dataForPropertyEditor = &m_platformProperties.ios;
dataTypeID = m_platformProperties.ios.TYPEINFO_Uuid();
break;
default:
AZ_Assert(false, "Cannot add unknown platform to ui.");
}
unsigned platIdValue = static_cast<unsigned>(plat.m_id);
m_platformPropertyEditors[platIdValue] = aznew AzToolsFramework::ReflectedPropertyEditor(parent);
parent->layout()->addWidget(m_platformPropertyEditors[platIdValue]);
m_platformPropertyEditors[platIdValue]->Setup(context, nullptr, false);
m_platformPropertyEditors[platIdValue]->AddInstance(dataForPropertyEditor, dataTypeID);
m_platformPropertyEditors[platIdValue]->setVisible(true);
m_platformPropertyEditors[platIdValue]->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_platformPropertyEditors[platIdValue]->SetHideRootProperties(false);
m_platformPropertyEditors[platIdValue]->SetDynamicEditDataProvider(nullptr);
m_platformPropertyEditors[platIdValue]->ExpandAll();
m_platformPropertyEditors[platIdValue]->InvalidateAll();
}
const char* GetPlatformKey(const Platform& plat)
{
switch (plat.m_id)
{
case PlatformId::Android:
return androidSettings;
default:
return "";
}
}
void ProjectSettingsToolWindow::MakeSerializers()
{
for (int plat = 0; plat < static_cast<unsigned long long>(PlatformId::NumPlatformIds); ++plat)
{
if (PlatformEnabled(static_cast<PlatformId>(plat)))
{
MakePlatformSerializer(Platforms[plat]);
}
}
}
void ProjectSettingsToolWindow::MakePlatformSerializer(const Platform& plat)
{
unsigned platIdValue = static_cast<unsigned>(plat.m_id);
switch (plat.m_id)
{
case PlatformId::Base:
m_platformPropertyEditors[platIdValue]->EnumerateInstances(AZStd::bind
(
&ProjectSettingsToolWindow::MakeSerializerJson,
this,
plat,
AZStd::placeholders::_1,
&m_settingsContainer->GetProjectJsonDocument()
));
break;
case PlatformId::Android:
m_platformPropertyEditors[platIdValue]->EnumerateInstances(AZStd::bind
(
&ProjectSettingsToolWindow::MakeSerializerJsonNonRoot,
this,
plat,
AZStd::placeholders::_1,
&m_settingsContainer->GetProjectJsonDocument(),
m_settingsContainer->GetProjectJsonValue(GetPlatformKey(plat)).GetValue()
));
break;
case PlatformId::Ios:
{
PlistDictionary* dict = m_settingsContainer->GetPlistDictionary(plat).release();
if (dict == nullptr)
{
QMessageBox::critical
(
this,
"Critical",
"Ios pList is invalid. Project Settings Tool must close.",
QMessageBox::Abort
);
ForceClose();
}
m_platformPropertyEditors[platIdValue]->EnumerateInstances(AZStd::bind
(
&ProjectSettingsToolWindow::MakeSerializerPlist,
this,
plat,
AZStd::placeholders::_1,
// All arguments must be copy constructible so this must be released
dict
));
break;
}
default:
AZ_Assert(false, "Cannot make serializer for unknown platform.");
break;
}
}
void ProjectSettingsToolWindow::LoadPropertiesFromSettings()
{
// Disable all fields links
for (int plat = 0; plat < static_cast<unsigned long long>(PlatformId::NumPlatformIds); ++plat)
{
if (PlatformEnabled(static_cast<PlatformId>(plat)))
{
LoadPropertiesFromPlatformSettings(Platforms[plat]);
}
}
}
void ProjectSettingsToolWindow::LoadPropertiesFromPlatformSettings(const Platform& plat)
{
unsigned platIdValue = static_cast<unsigned>(plat.m_id);
m_platformSerializers[platIdValue]->LoadFromSettings();
m_platformPropertyEditors[platIdValue]->InvalidateValues();
}
bool ProjectSettingsToolWindow::UiEqualToSettings()
{
for (int plat = 0; plat < static_cast<unsigned long long>(PlatformId::NumPlatformIds); ++plat)
{
if (PlatformEnabled(static_cast<PlatformId>(plat)))
{
if (!UiEqualToPlatformSettings(Platforms[plat]))
{
return false;
}
}
}
return true;
}
bool ProjectSettingsToolWindow::UiEqualToPlatformSettings(const Platform& plat)
{
return m_platformSerializers[static_cast<unsigned>(plat.m_id)]->UiEqualToSettings();
}
bool ProjectSettingsToolWindow::ValidateAllProperties()
{
return m_validationHandler->AllValid();
}
void ProjectSettingsToolWindow::SaveSettingsFromUi()
{
bool anySaves = false;
const unsigned long long numPlatforms = static_cast<unsigned long long>(PlatformId::NumPlatformIds);
bool needToSavePlat[numPlatforms] = {false};
for (int plat = 0; plat < numPlatforms; ++plat)
{
if (PlatformEnabled(static_cast<PlatformId>(plat)))
{
const Platform& platform = Platforms[plat];
if (!UiEqualToPlatformSettings(platform))
{
needToSavePlat[plat] = true;
anySaves = true;
}
}
}
if (anySaves)
{
// Keeps queued button presses from getting in
if (m_ui->saveButton->isEnabled())
{
m_ui->saveButton->setEnabled(false);
m_ui->reloadButton->setEnabled(false);
if (ValidateAllProperties())
{
bool projectJsonChanged = false;
for (int plat = 0; plat < numPlatforms; ++plat)
{
const Platform& platform = Platforms[plat];
if (needToSavePlat[plat])
{
m_platformSerializers[plat]->SaveToSettings();
if (m_settingsContainer->IsPlistPlatform(platform))
{
m_settingsContainer->SavePlistData(platform);
}
else
{
projectJsonChanged = true;
}
}
}
if (projectJsonChanged)
{
m_settingsContainer->SaveProjectJsonData();
}
ShowAllErrorsThenExitIfInvalid();
m_ui->reconfigureLog->setText("");
int result = QMessageBox::question
(
this,
tr("Reconfigure Project"),
tr("For new settings to be applied the project must be reconfigured. Would you like run configure now?"),
QMessageBox::Yes,
QMessageBox::No
);
if (QMessageBox::Yes == result)
{
m_ui->reconfigureLog->show();
#if defined(AZ_PLATFORM_WINDOWS)
m_reconfigureProcess.start("cmd.exe", { QString("/C %1").arg("lmbr_waf.bat configure") });
#elif defined(AZ_PLATFORM_MAC) || defined(AZ_PLATFORM_LINUX)
m_reconfigureProcess.start("/bin/sh", { QString("%1").arg("lmbr_waf.sh configure") });
#else
#error "Needs to be implemented"
#endif
}
else
{
m_ui->reloadButton->setEnabled(true);
m_ui->saveButton->setEnabled(true);
}
}
// Show a message box telling user settings failed to save
else
{
QMessageBox::critical(this, tr("Failed To Save"), tr("Failed to save due to invalid settings."));
m_ui->reloadButton->setEnabled(true);
m_ui->saveButton->setEnabled(true);
}
}
}
}
void ProjectSettingsToolWindow::SaveSettingsFromPlatformUi(const Platform& plat)
{
m_platformSerializers[static_cast<unsigned>(plat.m_id)]->SaveToSettings();
m_settingsContainer->SavePlatformData(plat);
ShowAllErrorsThenExitIfInvalid();
}
void ProjectSettingsToolWindow::ReloadUiFromSettings()
{
if (!UiEqualToSettings())
{
int result = QMessageBox::warning
(
this,
tr("Reload Settings"),
tr("Are you sure you would like to reload settings from file? All changes will be lost."),
QMessageBox::Reset,
QMessageBox::Cancel);
if (result == QMessageBox::Reset)
{
m_settingsContainer->ReloadProjectJsonData();
m_settingsContainer->ReloadPlistData();
MakeSerializers();
// Disable links to avoid overwriting values while loading
m_linkHandler->DisableAllPropertyLinks();
LoadPropertiesFromSettings();
// Re-enable them then mirror
m_linkHandler->EnableAllPropertyLinks();
m_linkHandler->EnableOptionalLinksIfAllPropertiesEqual();
m_linkHandler->MirrorAllLinkedProperties();
// Mark any invalid fields loaded from file
ValidateAllProperties();
}
}
}
bool ProjectSettingsToolWindow::PlatformEnabled(PlatformId platformId)
{
// iOS can be disabled if the plist file is missing
if (platformId == PlatformId::Ios)
{
const AZStd::string filename = m_projectRoot + PlatformResourcesFolder(platformId);
return CFileUtil::FileExists(filename.c_str());
}
return true;
}
const char* ProjectSettingsToolWindow::PlatformResourcesFolder(PlatformId platformId)
{
if (platformId == PlatformId::Ios)
{
const AZStd::string firstfilename = m_projectRoot + "/Gem/Resources/Platform/iOS/Info.plist";
if (CFileUtil::FileExists(firstfilename.c_str()))
{
return "/Gem/Resources/Platform/iOS/Info.plist";
}
else
{
const AZStd::string filename = m_projectRoot + "/Gem/Resources/IOSLauncher/Info.plist";
if (CFileUtil::FileExists(filename.c_str()))
{
return "/Gem/Resources/IOSLauncher/Info.plist";
}
}
}
return nullptr;
}
#include <moc_ProjectSettingsToolWindow.cpp>
} // namespace ProjectSettingsTool
@@ -0,0 +1,174 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "LastPathBus.h"
#include "Platforms.h"
#include "PlatformSettings.h"
#include "ProjectSettingsContainer.h"
#include "ProjectSettingsSerialization.h"
#include "ValidatorBus.h"
#include <QProcess>
#include <QScopedPointer>
#include <QWidget>
#endif
// Forward Declares
namespace Ui
{
class ProjectSettingsToolWidget;
}
namespace AzToolsFramework
{
class InstanceDataHierarchy;
class PropertyHandlerBase;
class ReflectedPropertyEditor;
}
namespace ProjectSettingsTool
{
//Forward Declares
class Validator;
class ValidationHandler;
class PropertyLinkedHandler;
struct Properties
{
BaseSettings base;
AndroidSettings android;
IosSettings ios;
};
// Main window for Project Settings tool
class ProjectSettingsToolWindow
: public QWidget
, public LastPathBus::Handler
, public ValidatorBus::Handler
{
Q_OBJECT
public:
static const GUID& GetClassID()
{
// {0DC1B7D9-B660-41C3-91F1-A643EE65AADF}
static const GUID guid = {
0x0dc1b7d9, 0xb660, 0x41c3, { 0x91, 0xf1, 0xa6, 0x43, 0xee, 0x65, 0xaa, 0xdf }
};
return guid;
}
ProjectSettingsToolWindow(QWidget* parent = nullptr);
~ProjectSettingsToolWindow();
// Ebuses
QString GetLastImagePath() override;
void SetLastImagePath(const QString& path) override;
FunctorValidator* GetValidator(FunctorValidator::FunctorType) override;
void TrackValidator(FunctorValidator*) override;
static void ReflectPlatformClasses();
private:
AZ_DISABLE_COPY_MOVE(ProjectSettingsToolWindow);
void closeEvent(QCloseEvent* event) override;
// Close the window now because an error occurred
void ForceClose();
// Un/Registers and dis/connect handlers and buses
void RegisterHandlersAndBusses();
void UnregisterHandlersAndBusses();
void InitializeUi();
// Used to make the serializers for settings
void MakeSerializerJson(const Platform& plat, AzToolsFramework::InstanceDataHierarchy& hierarchy, rapidjson::Document* doc);
void MakeSerializerJsonNonRoot(const Platform& plat, AzToolsFramework::InstanceDataHierarchy& hierarchy, rapidjson::Document* doc, rapidjson::Value* jsonRoot);
void MakeSerializerPlist(const Platform& plat, AzToolsFramework::InstanceDataHierarchy& hierarchy, PlistDictionary* dict);
// Shows an error dialog if an error has occurred while loading settings then exits if users chooses
// Returns true if there was an error
bool IfErrorShowThenExit();
// Loop through all errors then exit if the user chooses to abort or window is in invalid state
void ShowAllErrorsThenExitIfInvalid();
// Resizes TabWidget to size of current tab instead of largest tab
void ResizeTabs(int index);
// Add all platforms into the ui
void AddAllPlatformsToUi();
// Add given platform into the ui
void AddPlatformToUi(const Platform& plat);
// Makes all serializers
void MakeSerializers();
// Makes the serializer for specified platform
void MakePlatformSerializer(const Platform& plat);
// Replace values in ui with those read from settings
void LoadPropertiesFromSettings();
// Load properties for specified platform from file
void LoadPropertiesFromPlatformSettings(const Platform& plat);
// Checks if ui is the same as all settings
bool UiEqualToSettings();
// Checks if platform is the same as settings
bool UiEqualToPlatformSettings(const Platform& plat);
// Checks if all properties are valid, if any are not returns false, also sets warnings on those properties
bool ValidateAllProperties();
// Replace values in settings with those from ui and save to file
void SaveSettingsFromUi();
// Saves all properties for specified platform from to file
void SaveSettingsFromPlatformUi(const Platform& plat);
// Reload settings files and replace the values in ui with them
void ReloadUiFromSettings();
// returns true if the platform is enabled
bool PlatformEnabled(PlatformId platformId);
// returns the resource folder
const char* PlatformResourcesFolder(PlatformId platformId);
// The ui for the window
QScopedPointer<Ui::ProjectSettingsToolWidget> m_ui;
// The process used to reconfigure settings
QProcess m_reconfigureProcess;
AZStd::string m_devRoot;
AZStd::string m_projectRoot;
AZStd::string m_projectName;
// Used to initialize the settings container's pLists
ProjectSettingsContainer::PlistInitVector m_plistsInitVector;
// Container to manage settings files per platform
AZStd::unique_ptr<ProjectSettingsContainer> m_settingsContainer;
// Allows lookup and contains all allocated QValidators
AZStd::unique_ptr<Validator> m_validator;
Properties m_platformProperties;
AzToolsFramework::ReflectedPropertyEditor* m_platformPropertyEditors[static_cast<unsigned>(PlatformId::NumPlatformIds)];
AZStd::unique_ptr<Serializer> m_platformSerializers[static_cast<unsigned>(PlatformId::NumPlatformIds)];
// Pointers to all handlers to they can be unregistered and deleted
AZStd::vector<AzToolsFramework::PropertyHandlerBase*> m_propertyHandlers;
AZStd::unique_ptr<ValidationHandler> m_validationHandler;
PropertyLinkedHandler* m_linkHandler;
// Last path used when browsing for images in icons or splash
QString m_lastImagesPath;
bool m_invalidState;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,16 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/PlatformDef.h>
/////////////////////////////////////////////////////////////////////////////
// Engine
/////////////////////////////////////////////////////////////////////////////
#include <ISystem.h>
#include <Cry_Math.h>
@@ -0,0 +1,59 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "ProjectSettingsValidator.h"
#include "Validators.h"
namespace ProjectSettingsTool
{
Validator::Validator()
{}
Validator::~Validator()
{
for (AZStd::pair<FunctorValidator::FunctorType, FunctorValidator*>& pair : m_validatorToQValidator)
{
delete pair.second;
}
for (FunctorValidator* qValidator : m_otherValidators)
{
delete qValidator;
}
}
FunctorValidator* Validator::GetQValidator(FunctorValidator::FunctorType validator)
{
if (validator != nullptr)
{
auto iter = m_validatorToQValidator.find(validator);
if (iter != m_validatorToQValidator.end())
{
return iter->second;
}
else
{
FunctorValidator* qValidator = new FunctorValidator(validator);
m_validatorToQValidator.insert(
AZStd::pair<FunctorValidator::FunctorType, FunctorValidator*>(validator, qValidator));
return qValidator;
}
}
else
{
return nullptr;
}
}
void Validator::TrackThisValidator(FunctorValidator* validator)
{
m_otherValidators.push_back(validator);
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,43 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "FunctorValidator.h"
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_map.h>
namespace ProjectSettingsTool
{
class Validator
{
public:
typedef FunctorValidator::ReturnType ValidatorReturnType;
typedef FunctorValidator::FunctorType ValidatorType;
Validator();
~Validator();
// Finds the QValidator for a given validator or makes one and returns it
FunctorValidator* GetQValidator(FunctorValidator::FunctorType validator);
// Tracks this QValidator and deletes it in the destructor
void TrackThisValidator(FunctorValidator* validator);
private:
typedef AZStd::unordered_map<FunctorValidator::FunctorType, FunctorValidator*> ValidatorToQValidatorType;
typedef AZStd::list<FunctorValidator*> QValidatorList;
AZ_DISABLE_COPY_MOVE(Validator);
// Maps validator functions to QValidators
ValidatorToQValidatorType m_validatorToQValidator;
// Tracks allocations of other QValidators so they don't leak
QValidatorList m_otherValidators;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,109 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PropertyFileSelect.h"
#include "PlatformSettings_common.h"
#include "ValidationHandler.h"
#include <QLayout>
#include <QPushButton>
#include <QLineEdit>
namespace ProjectSettingsTool
{
PropertyFileSelectCtrl::PropertyFileSelectCtrl(QWidget* pParent)
: PropertyFuncValBrowseEditCtrl(pParent)
, m_selectFunctor(nullptr)
{
// Turn on clear button by default
browseEdit()->setClearButtonEnabled(true);
connect(browseEdit(), &AzQtComponents::BrowseEdit::attachedButtonTriggered, this, &PropertyFileSelectCtrl::SelectFile);
}
void PropertyFileSelectCtrl::SelectFile()
{
if (m_selectFunctor != nullptr)
{
QString path = m_selectFunctor(browseEdit()->text());
if (!path.isEmpty())
{
SetValueUser(path);
}
}
else
{
AZ_Assert(false, "No file select functor set.");
}
}
void PropertyFileSelectCtrl::ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
if (attrib == Attributes::SelectFunction)
{
void* functor = nullptr;
if (attrValue->Read<void*>(functor))
{
// This is guaranteed type safe elsewhere so this is safe
m_selectFunctor = reinterpret_cast<FileSelectFuncType>(functor);
}
}
else
{
PropertyFuncValBrowseEditCtrl::ConsumeAttribute(attrib, attrValue, debugName);
}
}
// Handler ///////////////////////////////////////////////////////////////////
PropertyFileSelectHandler::PropertyFileSelectHandler(ValidationHandler* valHdlr)
: AzToolsFramework::PropertyHandler<AZStd::string, PropertyFileSelectCtrl>()
, m_validationHandler(valHdlr)
{}
AZ::u32 PropertyFileSelectHandler::GetHandlerName(void) const
{
return Handlers::FileSelect;
}
QWidget* PropertyFileSelectHandler::CreateGUI(QWidget* pParent)
{
PropertyFileSelectCtrl* ctrl = aznew PropertyFileSelectCtrl(pParent);
m_validationHandler->AddValidatorCtrl(ctrl);
return ctrl;
}
void PropertyFileSelectHandler::ConsumeAttribute(PropertyFileSelectCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
GUI->ConsumeAttribute(attrib, attrValue, debugName);
}
void PropertyFileSelectHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, PropertyFileSelectCtrl* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetValue().toUtf8().data();
}
bool PropertyFileSelectHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, PropertyFileSelectCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetValue(instance.data());
GUI->ForceValidate();
return true;
}
PropertyFileSelectHandler* PropertyFileSelectHandler::Register(ValidationHandler* valHdlr)
{
PropertyFileSelectHandler* handler = aznew PropertyFileSelectHandler(valHdlr);
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
&AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType,
handler);
return handler;
}
} // namespace ProjectSettingsTool
#include <moc_PropertyFileSelect.cpp>
@@ -0,0 +1,62 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "PropertyFuncValBrowseEdit.h"
#endif
// Forward declare
class QPushButton;
namespace ProjectSettingsTool
{
// Forward declare
class ValidationHandler;
class PropertyFileSelectCtrl
: public PropertyFuncValBrowseEditCtrl
{
Q_OBJECT
public:
typedef QString(* FileSelectFuncType)(const QString&);
PropertyFileSelectCtrl(QWidget* pParent = nullptr);
virtual void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
protected:
void SelectFile();
QPushButton* m_selectButton;
FileSelectFuncType m_selectFunctor;
};
class PropertyFileSelectHandler
: public AzToolsFramework::PropertyHandler<AZStd::string, PropertyFileSelectCtrl>
{
AZ_CLASS_ALLOCATOR(PropertyFileSelectHandler, AZ::SystemAllocator, 0);
public:
PropertyFileSelectHandler(ValidationHandler* valHdlr);
AZ::u32 GetHandlerName(void) const override;
// Need to unregister ourselves
bool AutoDelete() const override { return false; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyFileSelectCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, PropertyFileSelectCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, PropertyFileSelectCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
static PropertyFileSelectHandler* Register(ValidationHandler* valHdlr);
private:
ValidationHandler* m_validationHandler;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,210 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PropertyFuncValBrowseEdit.h"
#include "PlatformSettings_common.h"
#include "ValidationHandler.h"
#include "ValidatorBus.h"
#include <AzToolsFramework/UI/PropertyEditor/PropertyQTConstants.h>
#include <QLineEdit>
#include <QHBoxLayout>
namespace ProjectSettingsTool
{
PropertyFuncValBrowseEditCtrl::PropertyFuncValBrowseEditCtrl(QWidget* pParent)
: QWidget(pParent)
, m_validator(nullptr)
{
QHBoxLayout* layout = new QHBoxLayout(this);
m_browseEdit = new AzQtComponents::BrowseEdit(this);
layout->setSpacing(4);
layout->setContentsMargins(1, 0, 1, 0);
layout->addWidget(m_browseEdit);
browseEdit()->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
browseEdit()->setMinimumWidth(AzToolsFramework::PropertyQTConstant_MinimumWidth);
browseEdit()->setFixedHeight(AzToolsFramework::PropertyQTConstant_DefaultHeight);
browseEdit()->setFocusPolicy(Qt::StrongFocus);
setLayout(layout);
setFocusProxy(browseEdit());
setFocusPolicy(browseEdit()->focusPolicy());
m_browseEdit->setClearButtonEnabled(true);
connect(m_browseEdit, &AzQtComponents::BrowseEdit::textChanged, this, &PropertyFuncValBrowseEditCtrl::ValueChangedByUser);
connect(m_browseEdit, &AzQtComponents::BrowseEdit::textChanged, this, &PropertyFuncValBrowseEditCtrl::ValidateAndShowErrors);
connect(m_browseEdit, &AzQtComponents::BrowseEdit::textChanged, this, [this]([[maybe_unused]] const QString& text)
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, this);
});
}
QString PropertyFuncValBrowseEditCtrl::GetValue() const
{
return m_browseEdit->text();
}
void PropertyFuncValBrowseEditCtrl::SetValue(const QString& value)
{
m_browseEdit->setText(value);
}
void PropertyFuncValBrowseEditCtrl::SetValueUser(const QString& value)
{
SetValue(value);
emit ValueChangedByUser();
}
FunctorValidator* PropertyFuncValBrowseEditCtrl::GetValidator()
{
return m_validator;
}
void PropertyFuncValBrowseEditCtrl::SetValidator(FunctorValidator* validator)
{
m_browseEdit->lineEdit()->setValidator(validator);
m_validator = validator;
}
void PropertyFuncValBrowseEditCtrl::SetValidator(FunctorValidator::FunctorType validator)
{
FunctorValidator* val = nullptr;
ValidatorBus::BroadcastResult(
val,
&ValidatorBus::Handler::GetValidator,
validator);
SetValidator(val);
}
bool PropertyFuncValBrowseEditCtrl::ValidateAndShowErrors()
{
if (m_validator)
{
FunctorValidator::ReturnType result = m_validator->ValidateWithErrors(m_browseEdit->text());
if (result.first == QValidator::Acceptable)
{
m_browseEdit->lineEdit()->setToolTip("");
return true;
}
else
{
m_browseEdit->lineEdit()->setToolTip(result.second);
m_browseEdit->lineEdit()->setReadOnly(false);
return false;
}
}
else
{
return true;
}
}
void PropertyFuncValBrowseEditCtrl::ForceValidate()
{
// Triggers update for errors
m_browseEdit->lineEdit()->textChanged(m_browseEdit->text());
}
void PropertyFuncValBrowseEditCtrl::ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
{
if (attrib == Attributes::FuncValidator)
{
void* validator = nullptr;
if (attrValue->Read<void*>(validator))
{
if (validator != nullptr)
{
SetValidator(reinterpret_cast<FunctorValidator::FunctorType>(validator));
}
}
}
else if (attrib == Attributes::ClearButton)
{
bool enable = false;
if (attrValue->Read<bool>(enable))
{
m_browseEdit->lineEdit()->setClearButtonEnabled(enable);
}
}
else if (attrib == Attributes::RemovableReadOnly)
{
bool readOnly = false;
if (attrValue->Read<bool>(readOnly))
{
m_browseEdit->lineEdit()->setReadOnly(readOnly);
}
}
else if (attrib == Attributes::ObfuscatedText)
{
bool obfus = false;
if (attrValue->Read<bool>(obfus) && obfus)
{
m_browseEdit->lineEdit()->setEchoMode(QLineEdit::Password);
}
}
}
AzQtComponents::BrowseEdit* PropertyFuncValBrowseEditCtrl::browseEdit()
{
return m_browseEdit;
}
// Handler ///////////////////////////////////////////////////////////////////
PropertyFuncValBrowseEditHandler::PropertyFuncValBrowseEditHandler(ValidationHandler* valHdlr)
: AzToolsFramework::PropertyHandler <AZStd::string, PropertyFuncValBrowseEditCtrl>()
, m_validationHandler(valHdlr
)
{}
AZ::u32 PropertyFuncValBrowseEditHandler::GetHandlerName(void) const
{
return Handlers::QValidatedBrowseEdit;
}
QWidget* PropertyFuncValBrowseEditHandler::CreateGUI(QWidget* pParent)
{
PropertyFuncValBrowseEditCtrl* ctrl = aznew PropertyFuncValBrowseEditCtrl(pParent);
m_validationHandler->AddValidatorCtrl(ctrl);
return ctrl;
}
void PropertyFuncValBrowseEditHandler::ConsumeAttribute(PropertyFuncValBrowseEditCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
GUI->ConsumeAttribute(attrib, attrValue, debugName);
}
void PropertyFuncValBrowseEditHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, PropertyFuncValBrowseEditCtrl* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetValue().toUtf8().data();
}
bool PropertyFuncValBrowseEditHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, PropertyFuncValBrowseEditCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetValue(instance.data());
GUI->ForceValidate();
return true;
}
PropertyFuncValBrowseEditHandler* PropertyFuncValBrowseEditHandler::Register(ValidationHandler* valHdlr)
{
PropertyFuncValBrowseEditHandler* handler = aznew PropertyFuncValBrowseEditHandler(valHdlr);
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
&AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType,
handler);
return handler;
}
} // namespace ProjectSettingsTool
#include <moc_PropertyFuncValBrowseEdit.cpp>
@@ -0,0 +1,85 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "FunctorValidator.h"
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzQtComponents/Components/Widgets/BrowseEdit.h>
#endif
namespace ProjectSettingsTool
{
// Forward Declare
class ValidationHandler;
class PropertyFuncValBrowseEditCtrl
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(PropertyFuncValBrowseEditCtrl, AZ::SystemAllocator, 0);
PropertyFuncValBrowseEditCtrl(QWidget* pParent = nullptr);
virtual QString GetValue() const;
// Sets value programmtically and triggers validation
virtual void SetValue(const QString& value);
// Sets value as if user set it
void SetValueUser(const QString& value);
// Returns pointer to the validator used
FunctorValidator* GetValidator();
// Sets the validator for the lineedit
void SetValidator(FunctorValidator* validator);
// Sets the validator for the linedit
void SetValidator(FunctorValidator::FunctorType validator);
// Returns false if invalid and returns shows error as tooltip
bool ValidateAndShowErrors();
// Forces the values to up validated and style updated
void ForceValidate();
// Returns a pointer to the BrowseEdit object.
AzQtComponents::BrowseEdit* browseEdit();
virtual void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName);
signals:
void ValueChangedByUser();
protected:
// Keeps track of the validator so no const_casts must be done
FunctorValidator* m_validator;
AzQtComponents::BrowseEdit* m_browseEdit = nullptr;
};
class PropertyFuncValBrowseEditHandler
: public AzToolsFramework::PropertyHandler <AZStd::string, PropertyFuncValBrowseEditCtrl>
{
AZ_CLASS_ALLOCATOR(PropertyFuncValBrowseEditHandler, AZ::SystemAllocator, 0);
public:
PropertyFuncValBrowseEditHandler(ValidationHandler* valHdlr);
AZ::u32 GetHandlerName(void) const override;
// Need to unregister ourselves
bool AutoDelete() const override { return false; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyFuncValBrowseEditCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, PropertyFuncValBrowseEditCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, PropertyFuncValBrowseEditCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
static PropertyFuncValBrowseEditHandler* Register(ValidationHandler* valHdlr);
private:
ValidationHandler* m_validationHandler;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,185 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PropertyFuncValLineEdit.h"
#include "PlatformSettings_common.h"
#include "ValidationHandler.h"
#include "ValidatorBus.h"
#include <QLineEdit>
namespace ProjectSettingsTool
{
PropertyFuncValLineEditCtrl::PropertyFuncValLineEditCtrl(QWidget* pParent)
: AzToolsFramework::PropertyStringLineEditCtrl(pParent)
, m_validator(nullptr)
{
connect(m_pLineEdit, &QLineEdit::textEdited, this, &PropertyFuncValLineEditCtrl::ValueChangedByUser);
connect(m_pLineEdit, &QLineEdit::textChanged, this, &PropertyFuncValLineEditCtrl::ValidateAndShowErrors);
connect(m_pLineEdit, &QLineEdit::textChanged, this, [this]([[maybe_unused]] const QString& text)
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, this);
});
}
QString PropertyFuncValLineEditCtrl::GetValue() const
{
return m_pLineEdit->text();
}
void PropertyFuncValLineEditCtrl::SetValue(const QString& value)
{
m_pLineEdit->setText(value);
}
void PropertyFuncValLineEditCtrl::SetValueUser(const QString& value)
{
SetValue(value);
emit ValueChangedByUser();
}
FunctorValidator* PropertyFuncValLineEditCtrl::GetValidator()
{
return m_validator;
}
void PropertyFuncValLineEditCtrl::SetValidator(FunctorValidator* validator)
{
m_pLineEdit->setValidator(validator);
m_validator = validator;
}
void PropertyFuncValLineEditCtrl::SetValidator(FunctorValidator::FunctorType validator)
{
FunctorValidator* val = nullptr;
ValidatorBus::BroadcastResult(
val,
&ValidatorBus::Handler::GetValidator,
validator);
SetValidator(val);
}
bool PropertyFuncValLineEditCtrl::ValidateAndShowErrors()
{
if (m_validator)
{
FunctorValidator::ReturnType result = m_validator->ValidateWithErrors(m_pLineEdit->text());
if (result.first == QValidator::Acceptable)
{
m_pLineEdit->setToolTip("");
return true;
}
else
{
m_pLineEdit->setToolTip(result.second);
m_pLineEdit->setReadOnly(false);
return false;
}
}
else
{
return true;
}
}
void PropertyFuncValLineEditCtrl::ForceValidate()
{
// Triggers update for errors
m_pLineEdit->textChanged(m_pLineEdit->text());
}
void PropertyFuncValLineEditCtrl::ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
{
if (attrib == Attributes::FuncValidator)
{
void* validator = nullptr;
if (attrValue->Read<void*>(validator))
{
if (validator != nullptr)
{
SetValidator(reinterpret_cast<FunctorValidator::FunctorType>(validator));
}
}
}
else if (attrib == Attributes::ClearButton)
{
bool enable = false;
if (attrValue->Read<bool>(enable))
{
m_pLineEdit->setClearButtonEnabled(enable);
}
}
else if (attrib == Attributes::RemovableReadOnly)
{
bool readOnly = false;
if (attrValue->Read<bool>(readOnly))
{
m_pLineEdit->setReadOnly(readOnly);
}
}
else if (attrib == Attributes::ObfuscatedText)
{
bool obfus = false;
if (attrValue->Read<bool>(obfus) && obfus)
{
m_pLineEdit->setEchoMode(QLineEdit::Password);
}
}
}
// Handler ///////////////////////////////////////////////////////////////////
PropertyFuncValLineEditHandler::PropertyFuncValLineEditHandler(ValidationHandler* valHdlr)
: AzToolsFramework::PropertyHandler <AZStd::string, PropertyFuncValLineEditCtrl>()
, m_validationHandler(valHdlr
)
{}
AZ::u32 PropertyFuncValLineEditHandler::GetHandlerName(void) const
{
return Handlers::QValidatedLineEdit;
}
QWidget* PropertyFuncValLineEditHandler::CreateGUI(QWidget* pParent)
{
PropertyFuncValLineEditCtrl* ctrl = aznew PropertyFuncValLineEditCtrl(pParent);
m_validationHandler->AddValidatorCtrl(ctrl);
return ctrl;
}
void PropertyFuncValLineEditHandler::ConsumeAttribute(PropertyFuncValLineEditCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
GUI->ConsumeAttribute(attrib, attrValue, debugName);
}
void PropertyFuncValLineEditHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, PropertyFuncValLineEditCtrl* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetValue().toUtf8().data();
}
bool PropertyFuncValLineEditHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, PropertyFuncValLineEditCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetValue(instance.data());
GUI->ForceValidate();
return true;
}
PropertyFuncValLineEditHandler* PropertyFuncValLineEditHandler::Register(ValidationHandler* valHdlr)
{
PropertyFuncValLineEditHandler* handler = aznew PropertyFuncValLineEditHandler(valHdlr);
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
&AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType,
handler);
return handler;
}
} // namespace ProjectSettingsTool
#include <moc_PropertyFuncValLineEdit.cpp>
@@ -0,0 +1,76 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "FunctorValidator.h"
#include <AzToolsFramework/UI/PropertyEditor/PropertyStringLineEditCtrl.hxx>
#endif
namespace ProjectSettingsTool
{
// Forward Declare
class ValidationHandler;
class PropertyFuncValLineEditCtrl
: public AzToolsFramework::PropertyStringLineEditCtrl
{
Q_OBJECT
public:
PropertyFuncValLineEditCtrl(QWidget* pParent = nullptr);
virtual QString GetValue() const;
// Sets value programmtically and triggers validation
virtual void SetValue(const QString& value);
// Sets value as if user set it
void SetValueUser(const QString& value);
// Returns pointer to the validator used
FunctorValidator* GetValidator();
// Sets the validator for the lineedit
void SetValidator(FunctorValidator* validator);
// Sets the validator for the linedit
void SetValidator(FunctorValidator::FunctorType validator);
// Returns false if invalid and returns shows error as tooltip
bool ValidateAndShowErrors();
// Forces the values to up validated and style updated
void ForceValidate();
virtual void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName);
signals:
void ValueChangedByUser();
protected:
// Keeps track of the validator so no const_casts must be done
FunctorValidator* m_validator;
};
class PropertyFuncValLineEditHandler
: public AzToolsFramework::PropertyHandler <AZStd::string, PropertyFuncValLineEditCtrl>
{
AZ_CLASS_ALLOCATOR(PropertyFuncValLineEditHandler, AZ::SystemAllocator, 0);
public:
PropertyFuncValLineEditHandler(ValidationHandler* valHdlr);
AZ::u32 GetHandlerName(void) const override;
// Need to unregister ourselves
bool AutoDelete() const override { return false; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyFuncValLineEditCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, PropertyFuncValLineEditCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, PropertyFuncValLineEditCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
static PropertyFuncValLineEditHandler* Register(ValidationHandler* valHdlr);
private:
ValidationHandler* m_validationHandler;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,271 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PropertyImagePreview.h"
#include "DefaultImageValidator.h"
#include "PlatformSettings_common.h"
#include "Utils.h"
#include "ValidationHandler.h"
#include "ValidatorBus.h"
#include <QBoxLayout>
#include <QDir>
#include <QLineEdit>
namespace ProjectSettingsTool
{
// Max width/length of preview in pixels
static const int maxPreviewDim = 96;
PropertyImagePreviewCtrl::PropertyImagePreviewCtrl(QWidget* parent)
: PropertyFuncValBrowseEditCtrl(parent)
, m_defaultImagePreview(nullptr)
, m_defaultPath("")
{
QLayout* myLayout = layout();
QBoxLayout* boxLayout = qobject_cast<QBoxLayout*>(myLayout);
m_preview = new QLabel(this);
m_preview->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_preview->setFixedSize(QSize(maxPreviewDim, maxPreviewDim));
if (boxLayout != nullptr)
{
boxLayout->insertWidget(0, m_preview);
}
else
{
AZ_Assert(false, "Expected QBoxLayout type not found in lineedit control.");
myLayout->addWidget(m_preview);
}
connect(this->m_browseEdit, &AzQtComponents::BrowseEdit::attachedButtonTriggered, this, [this]()
{
QString path = SelectImageFromFileDialog(browseEdit()->text());
if (!path.isEmpty())
{
SetValueUser(path);
}
});
connect(browseEdit(), &AzQtComponents::BrowseEdit::textChanged, this, &PropertyImagePreviewCtrl::LoadPreview);
}
void PropertyImagePreviewCtrl::SetValue(const QString& path)
{
browseEdit()->setText(path);
// Preview will not be shown if path is set to empty because it has not changed so call it here
LoadPreview();
}
const QString& PropertyImagePreviewCtrl::DefaultImagePath() const
{
return m_defaultPath;
}
void PropertyImagePreviewCtrl::SetDefaultImagePath(const QString& newPath)
{
m_defaultPath = newPath;
}
void PropertyImagePreviewCtrl::SetDefaultImagePreview(PropertyImagePreviewCtrl* imageSelect)
{
if (m_defaultImagePreview == nullptr)
{
m_defaultImagePreview = imageSelect;
connect(imageSelect, &PropertyFuncValBrowseEditCtrl::ValueChangedByUser, this, &PropertyImagePreviewCtrl::LoadPreview);
}
else
{
AZ_Assert(false, "Default image preview already set.");
}
}
PropertyImagePreviewCtrl* PropertyImagePreviewCtrl::DefaultImagePreview() const
{
return m_defaultImagePreview;
}
void PropertyImagePreviewCtrl::AddOverrideToValidator(PropertyImagePreviewCtrl* preview)
{
qobject_cast<DefaultImageValidator*>(m_validator)->AddOverride(preview);
connect(browseEdit(), &AzQtComponents::BrowseEdit::textChanged, this, &PropertyImagePreviewCtrl::ForceValidate);
}
void PropertyImagePreviewCtrl::LoadPreview()
{
QString currentPath = browseEdit()->text();
const QString* imagePath = nullptr;
if (!currentPath.isEmpty())
{
imagePath = &currentPath;
}
else
{
if (m_defaultImagePreview != nullptr)
{
currentPath = browseEdit()->text();
}
if (!currentPath.isEmpty())
{
imagePath = &currentPath;
}
else
{
imagePath = &m_defaultPath;
}
}
QDir dirPath(*imagePath);
// Keeps image from showing when no extension is given
if (!imagePath->isEmpty() && dirPath.isReadable())
{
// Image loaded from file
QImage originalImage;
// Scaled image
QImage processedImage;
// The image to be displayed
QImage* finalImage;
bool validImage = originalImage.load(*imagePath);
if (validImage)
{
//Scale down any images larger than 128 on either dimension
if (originalImage.height() > maxPreviewDim || originalImage.width() > maxPreviewDim)
{
processedImage = originalImage.scaled(maxPreviewDim, maxPreviewDim,
Qt::KeepAspectRatio, Qt::SmoothTransformation);
finalImage = &processedImage;
}
else
{
finalImage = &originalImage;
}
m_preview->setPixmap(QPixmap::fromImage(*finalImage));
}
// Failed to load image so set the preview to nothing
else
{
m_preview->setPixmap(QPixmap());
}
}
// Nothing valid set preview to nothing
else
{
m_preview->setPixmap(QPixmap());
}
}
void PropertyImagePreviewCtrl::UpgradeToDefaultValidator()
{
DefaultImageValidator* newValidator = new DefaultImageValidator(*m_validator);
SetValidator(newValidator);
// Track the memory allocated for this validator so its deleted
ValidatorBus::Broadcast(
&ValidatorBus::Handler::TrackValidator,
newValidator);
}
void PropertyImagePreviewCtrl::ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
if (attrib == Attributes::DefaultPath)
{
AZStd::string path;
if (attrValue->Read<AZStd::string>(path))
{
m_defaultPath = path.data();
}
}
else
{
PropertyFuncValBrowseEditCtrl::ConsumeAttribute(attrib, attrValue, debugName);
}
}
// Handler ///////////////////////////////////////////////////////////////////
PropertyImagePreviewHandler::PropertyImagePreviewHandler(ValidationHandler* valHdlr)
: AzToolsFramework::PropertyHandler<AZStd::string, PropertyImagePreviewCtrl>()
, m_validationHandler(valHdlr)
{}
AZ::u32 PropertyImagePreviewHandler::GetHandlerName(void) const
{
return Handlers::ImagePreview;
}
QWidget* PropertyImagePreviewHandler::CreateGUI(QWidget* pParent)
{
PropertyImagePreviewCtrl* ctrl = aznew PropertyImagePreviewCtrl(pParent);
m_validationHandler->AddValidatorCtrl(ctrl);
return ctrl;
}
void PropertyImagePreviewHandler::ConsumeAttribute(PropertyImagePreviewCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
//This means we are using this as a default image preview
if (attrib == Attributes::PropertyIdentfier)
{
AZStd::string ident;
if (attrValue->Read<AZStd::string>(ident))
{
GUI->UpgradeToDefaultValidator();
m_identToCtrl.insert(AZStd::pair<AZStd::string, PropertyImagePreviewCtrl*>(ident, GUI));
}
}
else if (attrib == Attributes::DefaultImagePreview)
{
AZStd::string ident;
if (attrValue->Read<AZStd::string>(ident))
{
auto defaultPreview = m_identToCtrl.find(ident);
if (defaultPreview != m_identToCtrl.end())
{
defaultPreview->second->AddOverrideToValidator(GUI);
GUI->SetDefaultImagePreview(defaultPreview->second);
}
else
{
AZ_Assert(false, "Default image select \"%s\" not found.", ident.data());
}
}
}
else
{
GUI->ConsumeAttribute(attrib, attrValue, debugName);
}
}
void PropertyImagePreviewHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, PropertyImagePreviewCtrl* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetValue().toUtf8().data();
}
bool PropertyImagePreviewHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, PropertyImagePreviewCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetValue(instance.data());
GUI->ForceValidate();
return true;
}
PropertyImagePreviewHandler* PropertyImagePreviewHandler::Register(ValidationHandler* valHdlr)
{
PropertyImagePreviewHandler* handler = aznew PropertyImagePreviewHandler(valHdlr);
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
&AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType,
handler);
return handler;
}
} // namespace ProjectSettingsTool
#include <moc_PropertyImagePreview.cpp>
@@ -0,0 +1,90 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "PropertyFileSelect.h"
#include <AzCore/base.h>
#include <AzCore/std/containers/unordered_map.h>
#include <QLabel>
#endif
// Forward Declaration
class QValidator;
namespace ProjectSettingsTool
{
// Forward Declaration
class ValidationHandler;
// Used to select a png image from a file dialog then display it
class PropertyImagePreviewCtrl
: public PropertyFuncValBrowseEditCtrl
{
Q_OBJECT
public:
PropertyImagePreviewCtrl(QWidget* parent = nullptr);
// Sets path for the image preview
void SetValue(const QString& path) override;
// Returns path for the default image preview
const QString& DefaultImagePath() const;
// Sets path for the default image preview
void SetDefaultImagePath(const QString& newPath);
// Sets the default image preview control to use for image previews
void SetDefaultImagePreview(PropertyImagePreviewCtrl* imageSelect);
// Returns current default image select control pointer
PropertyImagePreviewCtrl* DefaultImagePreview() const;
// Add a specific image override to the default image selects validator
void AddOverrideToValidator(PropertyImagePreviewCtrl* preview);
// Loads a preview of the image at the current path or default if path isEmpty
void LoadPreview();
// Upgrades the Validator to a DefaultImageValidator
void UpgradeToDefaultValidator();
virtual void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
protected:
AZ_DISABLE_COPY_MOVE(PropertyImagePreviewCtrl);
void emitSelectClicked();
// Default image select to use
PropertyImagePreviewCtrl* m_defaultImagePreview;
// Full Path to default image preview
QString m_defaultPath;
// Displays the image preview
QLabel* m_preview;
};
class PropertyImagePreviewHandler
: public AzToolsFramework::PropertyHandler<AZStd::string, PropertyImagePreviewCtrl>
{
AZ_CLASS_ALLOCATOR(PropertyImagePreviewHandler, AZ::SystemAllocator, 0);
public:
PropertyImagePreviewHandler(ValidationHandler* valHdlr);
AZ::u32 GetHandlerName(void) const override;
// Need to unregister ourselves
bool AutoDelete() const override { return false; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyImagePreviewCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, PropertyImagePreviewCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, PropertyImagePreviewCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
static PropertyImagePreviewHandler* Register(ValidationHandler* valHdlr);
private:
AZStd::unordered_map<AZStd::string, PropertyImagePreviewCtrl*> m_identToCtrl;
ValidationHandler* m_validationHandler;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,333 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "PropertyLinked.h"
#include "PlatformSettings_common.h"
#include "ValidationHandler.h"
#include <QLayout>
#include <QPushButton>
#include <QLineEdit>
namespace ProjectSettingsTool
{
PropertyLinkedCtrl::PropertyLinkedCtrl(QWidget* pParent)
: PropertyFuncValLineEditCtrl(pParent)
, m_linkButton(nullptr)
, m_linkedProperty(nullptr)
, m_linkEnabled(false)
{
connect(this, &PropertyFuncValLineEditCtrl::ValueChangedByUser, this, &PropertyLinkedCtrl::MirrorToLinkedProperty);
}
void PropertyLinkedCtrl::SetLinkedProperty(PropertyLinkedCtrl* property)
{
m_linkedProperty = property;
m_linkEnabled = true;
}
void PropertyLinkedCtrl::SetLinkTooltip(const QString& tip)
{
if (m_linkButton != nullptr)
{
m_linkButton->setToolTip("Linked to " + tip);
}
}
void PropertyLinkedCtrl::MakeLinkButton()
{
QLayout* myLayout = layout();
QIcon icon;
icon.addFile("://link.svg", QSize(), QIcon::Normal, QIcon::On);
icon.addFile("://broken_link.svg", QSize(), QIcon::Normal, QIcon::Off);
m_linkButton = new QPushButton(this);
m_linkButton->setIcon(icon);
m_linkButton->setCheckable(true);
m_linkButton->setFlat(true);
m_linkButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_linkButton->setFixedSize(QSize(16, 16));
m_linkButton->setContentsMargins(0, 0, 0, 0);
m_linkButton->setToolTip("Linked to...");
myLayout->addWidget(m_linkButton);
connect(m_linkButton, &QPushButton::clicked, this, &PropertyLinkedCtrl::MirrorLinkButtonState);
}
bool PropertyLinkedCtrl::LinkIsOptional()
{
return m_linkButton;
}
void PropertyLinkedCtrl::SetOptionalLink(bool linked)
{
m_linkButton->setChecked(linked);
}
void PropertyLinkedCtrl::MirrorToLinkedProperty()
{
if (m_linkEnabled && m_linkedProperty != nullptr)
{
m_linkedProperty->MirrorToLinkedPropertyRecursive(this, m_pLineEdit->text());
}
}
void PropertyLinkedCtrl::MirrorToLinkedPropertyRecursive(PropertyLinkedCtrl* caller, const QString& value)
{
if (caller != this)
{
if (m_linkButton == nullptr || m_linkButton->isChecked())
{
// Stop Property from mirroring again
m_linkEnabled = false;
m_pLineEdit->setText(value);
m_linkEnabled = true;
}
if (m_linkedProperty != nullptr)
{
m_linkedProperty->MirrorToLinkedPropertyRecursive(caller, value);
}
}
}
void PropertyLinkedCtrl::MirrorLinkButtonState(bool checked)
{
if (m_linkedProperty != nullptr)
{
m_linkedProperty->MirrorLinkButtonStateRecursive(this, checked);
// Mirror value of property linked was enabled on to all linked fields
if (checked)
{
MirrorToLinkedProperty();
}
}
}
void PropertyLinkedCtrl::MirrorLinkButtonStateRecursive(PropertyLinkedCtrl* caller, bool state)
{
if (caller != this)
{
if (m_linkButton != nullptr)
{
m_linkButton->setChecked(state);
}
if (m_linkedProperty != nullptr)
{
m_linkedProperty->MirrorLinkButtonStateRecursive(caller, state);
}
}
}
bool PropertyLinkedCtrl::AllLinkedPropertiesEqual()
{
if (m_linkedProperty != nullptr)
{
return m_linkedProperty->AllLinkedPropertiesEqual(this, m_pLineEdit->text());
}
// No linked property so must be equal
else
{
return true;
}
}
bool PropertyLinkedCtrl::AllLinkedPropertiesEqual(PropertyLinkedCtrl* caller, const QString& value)
{
if (caller != this)
{
// Not equal
if (m_pLineEdit->text() != value)
{
return false;
}
if (m_linkedProperty != nullptr)
{
return m_linkedProperty->AllLinkedPropertiesEqual(caller, value);
}
// All checked properties were equal
else
{
return true;
}
}
// All properties were equal
else
{
return true;
}
}
void PropertyLinkedCtrl::SetLinkEnabled(bool enabled)
{
m_linkEnabled = enabled;
}
void PropertyLinkedCtrl::ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
if (attrib == Attributes::LinkOptional && m_linkButton == nullptr)
{
bool optional = false;
if (attrValue->Read<bool>(optional) && optional)
{
MakeLinkButton();
}
}
else
{
PropertyFuncValLineEditCtrl::ConsumeAttribute(attrib, attrValue, debugName);
}
}
// Handler ///////////////////////////////////////////////////////////////////
PropertyLinkedHandler::PropertyLinkedHandler(ValidationHandler* valHdlr)
: AzToolsFramework::PropertyHandler<AZStd::string, PropertyLinkedCtrl>()
, m_validationHandler(valHdlr)
{}
AZ::u32 PropertyLinkedHandler::GetHandlerName(void) const
{
return Handlers::LinkedLineEdit;
}
QWidget* PropertyLinkedHandler::CreateGUI(QWidget* pParent)
{
PropertyLinkedCtrl* ctrl = aznew PropertyLinkedCtrl(pParent);
m_validationHandler->AddValidatorCtrl(ctrl);
return ctrl;
}
void PropertyLinkedHandler::ConsumeAttribute(PropertyLinkedCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
if (attrib == Attributes::PropertyIdentfier)
{
AZStd::string ident;
if (attrValue->Read<AZStd::string>(ident))
{
m_identToCtrl.insert(AZStd::pair<AZStd::string, PropertyLinkedCtrl*>(ident, GUI));
auto result = m_ctrlToIdentAndLink.find(GUI);
if (result != m_ctrlToIdentAndLink.end())
{
result->second.identifier = ident;
}
else
{
m_ctrlToIdentAndLink.insert(AZStd::pair<PropertyLinkedCtrl*, IdentAndLink>(GUI, IdentAndLink{ ident, "" }));
m_ctrlInitOrder.push_back(GUI);
}
}
}
else if (attrib == Attributes::LinkedProperty)
{
AZStd::string linked;
if (attrValue->Read<AZStd::string>(linked))
{
auto result = m_ctrlToIdentAndLink.find(GUI);
if (result != m_ctrlToIdentAndLink.end())
{
result->second.linkedIdentifier = linked;
}
else
{
m_ctrlToIdentAndLink.insert(AZStd::pair<PropertyLinkedCtrl*, IdentAndLink>(GUI, IdentAndLink{ "", linked }));
m_ctrlInitOrder.push_back(GUI);
}
GUI->SetLinkTooltip(linked.data());
}
}
else
{
GUI->ConsumeAttribute(attrib, attrValue, debugName);
}
}
void PropertyLinkedHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, PropertyLinkedCtrl* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
instance = GUI->GetValue().toUtf8().data();
}
bool PropertyLinkedHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, PropertyLinkedCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetValue(instance.data());
GUI->ForceValidate();
return true;
}
PropertyLinkedHandler* PropertyLinkedHandler::Register(ValidationHandler* valHdlr)
{
PropertyLinkedHandler* handler = aznew PropertyLinkedHandler(valHdlr);
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
&AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType,
handler);
return handler;
}
void PropertyLinkedHandler::LinkAllProperties()
{
// Link all the properties
for (PropertyLinkedCtrl* ctrlPointer : m_ctrlInitOrder)
{
auto property = m_ctrlToIdentAndLink.find(ctrlPointer);
auto link = m_identToCtrl.find(property->second.linkedIdentifier);
if (link != m_identToCtrl.end())
{
property->first->SetLinkedProperty(link->second);
//Force mirror non-optional links.
property->first->MirrorToLinkedProperty();
}
else
{
AZ_Assert(false, "Property \"%s\" not found while linking to \"%s\".", property->second.linkedIdentifier.data(), property->second.identifier.data());
}
}
// Enable optional links if all properties in link chain are the same value
EnableOptionalLinksIfAllPropertiesEqual();
}
void PropertyLinkedHandler::EnableOptionalLinksIfAllPropertiesEqual()
{
// Enable optional links if all properties in link chain are the same value
for (const AZStd::pair<PropertyLinkedCtrl*, IdentAndLink>& property : m_ctrlToIdentAndLink)
{
if (property.first->LinkIsOptional())
{
property.first->SetOptionalLink(property.first->AllLinkedPropertiesEqual());
}
}
}
void PropertyLinkedHandler::MirrorAllLinkedProperties()
{
for (PropertyLinkedCtrl* ctrlPointer : m_ctrlInitOrder)
{
ctrlPointer->MirrorToLinkedProperty();
}
}
void PropertyLinkedHandler::DisableAllPropertyLinks()
{
for (PropertyLinkedCtrl* ctrlPointer : m_ctrlInitOrder)
{
ctrlPointer->SetLinkEnabled(false);
}
}
void PropertyLinkedHandler::EnableAllPropertyLinks()
{
for (PropertyLinkedCtrl* ctrlPointer : m_ctrlInitOrder)
{
ctrlPointer->SetLinkEnabled(true);
}
}
} // namespace ProjectSettingsTool
#include <moc_PropertyLinked.cpp>
@@ -0,0 +1,104 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "PropertyFuncValLineEdit.h"
#include <AzCore/std/containers/unordered_map.h>
#endif
// Forward declares
class QPushButton;
namespace ProjectSettingsTool
{
// Forward declare
class ValidationHandler;
class PropertyLinkedCtrl
: public PropertyFuncValLineEditCtrl
{
Q_OBJECT
public:
typedef QString(* FileSelectFuncType)(const QString&);
PropertyLinkedCtrl(QWidget* pParent = nullptr);
// Set property this will mirror its values to
void SetLinkedProperty(PropertyLinkedCtrl* property);
// Set the tooltip on the link button so the user can see what property this is linked to
void SetLinkTooltip(const QString& tip);
// Returns true if all linked properties are equal
bool AllLinkedPropertiesEqual();
// Returns true if links are optional on this
bool LinkIsOptional();
// Set the option link state to given bool
void SetOptionalLink(bool linked);
// Tries to mirror the value to all linked properties
void MirrorToLinkedProperty();
// Enabled/disables link regardless of type
void SetLinkEnabled(bool enabled);
void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
protected:
void MakeLinkButton();
void MirrorToLinkedPropertyRecursive(PropertyLinkedCtrl* caller, const QString& value);
// Tries to mirror the link button state to all linked properties
void MirrorLinkButtonState(bool checked);
void MirrorLinkButtonStateRecursive(PropertyLinkedCtrl* caller, bool state);
// Returns true if all linked properties are equal
bool AllLinkedPropertiesEqual(PropertyLinkedCtrl* caller, const QString& value);
QPushButton* m_linkButton;
PropertyLinkedCtrl* m_linkedProperty;
bool m_linkEnabled;
};
class PropertyLinkedHandler
: public AzToolsFramework::PropertyHandler<AZStd::string, PropertyLinkedCtrl>
{
AZ_CLASS_ALLOCATOR(PropertyLinkedHandler, AZ::SystemAllocator, 0);
public:
PropertyLinkedHandler(ValidationHandler* valHdlr);
AZ::u32 GetHandlerName(void) const override;
// Need to unregister ourselves
bool AutoDelete() const override { return false; }
QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyLinkedCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, PropertyLinkedCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, PropertyLinkedCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
void LinkAllProperties();
void EnableOptionalLinksIfAllPropertiesEqual();
void MirrorAllLinkedProperties();
void DisableAllPropertyLinks();
void EnableAllPropertyLinks();
static PropertyLinkedHandler* Register(ValidationHandler* valHdlr);
private:
struct IdentAndLink
{
AZStd::string identifier;
AZStd::string linkedIdentifier;
};
// Map of identifiers to controls
AZStd::unordered_map<AZStd::string, PropertyLinkedCtrl*> m_identToCtrl;
// Map of controls to identifiers and their linked control's identifiers
AZStd::unordered_map<PropertyLinkedCtrl*, IdentAndLink> m_ctrlToIdentAndLink;
// Keeps track of the order ctrls were initialized
AZStd::vector<PropertyLinkedCtrl*> m_ctrlInitOrder;
// Tracks all validating properties
ValidationHandler* m_validationHandler;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,229 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "Utils.h"
#include "LastPathBus.h"
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <QFileDialog>
namespace
{
template<typename StringType>
void ToUnixPath(StringType& path)
{
AZStd::replace(path.begin(), path.end(), '\\', '/');
}
template<typename StringType>
StringType GetAbsoluteDevRoot()
{
const char* devRoot = nullptr;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
devRoot,
&AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevRootFolderPath);
if (!devRoot)
{
return "";
}
StringType devRootString(devRoot);
ToUnixPath(devRootString);
return devRootString;
}
template<typename StringType>
StringType GetAbsoluteProjectRoot()
{
const char* projectRoot = nullptr;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
projectRoot,
&AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevGameFolderPath);
if (!projectRoot)
{
return "";
}
StringType projectRootString(projectRoot);
ToUnixPath(projectRootString);
return projectRootString;
}
template<typename StringType>
StringType GetProjectName();
template<>
AZStd::string GetProjectName()
{
auto projectName = AZ::Utils::GetProjectName();
return AZStd::string{projectName.c_str()};
}
template<>
QString GetProjectName()
{
auto projectName = AZ::Utils::GetProjectName();
return QString::fromUtf8(projectName.c_str(), aznumeric_cast<int>(projectName.size()));
}
}
namespace ProjectSettingsTool
{
void* ConvertFunctorToVoid(AZStd::pair<QValidator::State, const QString> (*func)(const QString&))
{
return reinterpret_cast<void*>(func);
}
AZStd::string GetDevRoot()
{
return GetAbsoluteDevRoot<AZStd::string>();
}
AZStd::string GetProjectRoot()
{
return GetAbsoluteProjectRoot<AZStd::string>();
}
AZStd::string GetProjectName()
{
return ::GetProjectName<AZStd::string>();
}
QString SelectXmlFromFileDialog(const QString& currentFile)
{
// The selected file must be relative to this path
QString defaultPath = GetAbsoluteDevRoot<QString>();
QString startPath;
// Choose the starting path for file dialog
if (currentFile != "")
{
if (currentFile.contains(defaultPath))
{
startPath = currentFile;
}
else
{
startPath = defaultPath + currentFile;
}
}
else
{
startPath = defaultPath;
}
QString pickedPath = QFileDialog::getOpenFileName(nullptr, QObject::tr("Select Override"),
startPath, QObject::tr("Extensible Markup Language file (*.xml)"));
ToUnixPath(pickedPath);
// Remove the default relative path
if (pickedPath.contains(defaultPath))
{
pickedPath = pickedPath.mid(defaultPath.length());
}
return pickedPath;
}
QString SelectImageFromFileDialog(const QString& currentFile)
{
QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteDevRoot<QString>(), ::GetProjectName<QString>());
QString startPath;
// Choose the starting path for file dialog
if (currentFile != "")
{
if (QDir::isAbsolutePath(currentFile))
{
startPath = currentFile;
}
else
{
startPath = defaultPath + currentFile;
}
}
else
{
LastPathBus::BroadcastResult(
startPath,
&LastPathBus::Handler::GetLastImagePath);
}
QString pickedPath = QFileDialog::getOpenFileName(nullptr, QObject::tr("Select Image"),
startPath, QObject::tr("Image file (*.png)"));
ToUnixPath(pickedPath);
if (!pickedPath.isEmpty())
{
LastPathBus::Broadcast(
&LastPathBus::Handler::SetLastImagePath,
pickedPath.left(pickedPath.lastIndexOf('/')));
}
// Remove the default relative path if it is used
if (pickedPath.contains(defaultPath))
{
pickedPath = pickedPath.mid(defaultPath.length());
}
return pickedPath;
}
AZStd::string GenDefaultImagePath(ImageGroup group, AZStd::string size)
{
AZStd::string root;
// Android
if (group <= ImageGroup::AndroidPortrait)
{
root = GetDevRoot() + "/Code/Tools/Android/ProjectBuilder/app_";
}
//Ios
else
{
using AZ::IO::SystemFile;
root = GetProjectRoot() + "/Gem/Resources/Platform/iOS/Images.xcassets/";
if (!SystemFile::Exists(root.c_str()))
{
root = GetProjectRoot() + "/Gem/Resources/IOSLauncher/Images.xcassets/";
}
}
AZStd::string groupStr;
switch (group)
{
case ImageGroup::AndroidIcons:
groupStr = "icon-";
break;
case ImageGroup::AndroidLandscape:
groupStr = "splash-land-";
break;
case ImageGroup::AndroidPortrait:
groupStr = "splash-port-";
break;
case ImageGroup::IosIcons:
groupStr = "AppIcon.appiconset/";
break;
case ImageGroup::IosLaunchScreens:
groupStr = "LaunchImage.launchimage/";
break;
default:
AZ_Assert(false, "Unknown ImageGroup.");
break;
}
return root + groupStr + size + ".png";
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,38 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/string/string.h>
#include <QString>
#include <QValidator>
namespace ProjectSettingsTool
{
void* ConvertFunctorToVoid(AZStd::pair<QValidator::State, const QString>(*func)(const QString&));
AZStd::string GetDevRoot();
AZStd::string GetProjectRoot();
AZStd::string GetProjectName();
// Open file dialogs for each file type and return the result
// CurrentFile is where the dialog opens
QString SelectXmlFromFileDialog(const QString& currentFile);
QString SelectImageFromFileDialog(const QString& currentFile);
enum class ImageGroup
{
AndroidIcons,
AndroidLandscape,
AndroidPortrait,
IosIcons,
IosLaunchScreens
};
AZStd::string GenDefaultImagePath(ImageGroup group, AZStd::string size);
} // namespace ProjectSettingsTool
@@ -0,0 +1,47 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "ValidationHandler.h"
#include "PropertyFuncValLineEdit.h"
#include "PropertyFuncValBrowseEdit.h"
namespace ProjectSettingsTool
{
void ValidationHandler::AddValidatorCtrl(PropertyFuncValLineEditCtrl* ctrl)
{
m_validators.push_back(ctrl);
}
void ValidationHandler::AddValidatorCtrl(PropertyFuncValBrowseEditCtrl* ctrl)
{
m_browseEditValidators.push_back(ctrl);
}
bool ValidationHandler::AllValid()
{
for (PropertyFuncValLineEditCtrl* ctrl : m_validators)
{
if (!ctrl->ValidateAndShowErrors())
{
return false;
}
}
for (PropertyFuncValBrowseEditCtrl* ctrl : m_browseEditValidators)
{
if (!ctrl->ValidateAndShowErrors())
{
return false;
}
}
return true;
}
} // namespace ProjectSettingsTool
@@ -0,0 +1,28 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
namespace ProjectSettingsTool
{
// Forward Declare
class PropertyFuncValLineEditCtrl;
class PropertyFuncValBrowseEditCtrl;
class ValidationHandler
{
public:
void AddValidatorCtrl(PropertyFuncValLineEditCtrl* ctrl);
void AddValidatorCtrl(PropertyFuncValBrowseEditCtrl* ctrl);
bool AllValid();
private:
AZStd::vector<PropertyFuncValLineEditCtrl*> m_validators;
AZStd::vector<PropertyFuncValBrowseEditCtrl*> m_browseEditValidators;
};
} // namespace ProjectSettingsTool
@@ -0,0 +1,30 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "FunctorValidator.h"
#include <AzCore/EBus/EBus.h>
namespace ProjectSettingsTool
{
class ValidatorTraits
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<ValidatorTraits>;
// Bus Configuration
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual FunctorValidator* GetValidator(FunctorValidator::FunctorType) = 0;
virtual void TrackValidator(FunctorValidator*) = 0;
};
typedef AZ::EBus<ValidatorTraits> ValidatorBus;
} // namespace ProjectSettingsTool
@@ -0,0 +1,222 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include "Validators.h"
#include <QDir>
#include <QMimeDatabase>
#include <QRegularExpression>
#define STANDARD_SUCCESS ProjectSettingsTool::FunctorValidator::ReturnType(QValidator::Acceptable, "");
namespace
{
typedef ProjectSettingsTool::FunctorValidator::ReturnType RetType;
static const int noMaxLength = -1;
static const int maxIosVersionLength = 18;
static const int androidPublicAppKeyLength = 392;
static const char* xmlRelativePath = "Code/Tools/RC/Config/rc/";
static const char* xmlMimeType = "application/xml";
static const char* stringEmpty = "String is empty";
// Returns true if string is valid android package and apple bundle identifier
RetType RegularExpressionValidator(const QString& pattern, const QString& name, int maxLength = noMaxLength)
{
if (maxLength != noMaxLength && name.length() > maxLength)
{
return RetType(QValidator::Invalid, QObject::tr("Cannot be longer than %1 characters.")
.arg(QString::number(maxLength)));
}
else if (name.isEmpty())
{
return RetType(QValidator::Intermediate, QObject::tr(stringEmpty));
}
QRegularExpression regex(pattern);
QRegularExpressionMatch match = regex.match(name, 0, QRegularExpression::PartialPreferCompleteMatch);
if (match.hasMatch())
{
if (match.capturedLength(0) == name.length())
{
return STANDARD_SUCCESS;
}
else
{
return RetType(QValidator::Intermediate, "Input incorrect.");
}
}
if (match.hasPartialMatch())
{
return RetType(QValidator::Intermediate, QObject::tr("Partially matches requirements."));
}
else
{
return RetType(QValidator::Invalid, QObject::tr("Fails to match requirements at all."));
}
}
}
namespace ProjectSettingsTool
{
namespace Validators
{
namespace Internal
{
// Returns true if file is readable and the correct mime type
RetType FileReadableAndCorrectType(const QString& path, const QString& fileType)
{
QDir dirPath(path);
if (dirPath.isReadable())
{
QMimeDatabase mimeDB;
QMimeType mimeType = mimeDB.mimeTypeForFile(path);
if (mimeType.name() == fileType)
{
return STANDARD_SUCCESS;
}
else
{
return RetType(QValidator::Intermediate, QObject::tr("File type should be %1, but is %2.")
.arg(fileType).arg(mimeType.name()));
}
}
else
{
return RetType(QValidator::Intermediate, QObject::tr("File is not readable."));
}
}
} // namespace Internal
// Returns true if valid cross platform file or directory name
RetType FileName(const QString& name)
{
// There was a known issue on android with '.' used in directory names
// causing problems so it has been omitted from use
return RegularExpressionValidator("[\\w,-]+", name);
}
RetType FileNameOrEmpty(const QString& name)
{
if (IsNotEmpty(name).first == QValidator::Acceptable)
{
return FileName(name);
}
else
{
return STANDARD_SUCCESS;
}
}
// Returns true if string isn't empty
RetType IsNotEmpty(const QString& value)
{
if (!value.isEmpty())
{
return STANDARD_SUCCESS;
}
else
{
return RetType(QValidator::Intermediate, QObject::tr(stringEmpty));
}
}
// Returns true if string is valid as a boolean
RetType BoolString(const QString& value)
{
if (value == "true" || value == "false")
{
return STANDARD_SUCCESS;
}
return RetType(QValidator::Invalid, QObject::tr("Invalid bool string."));
}
// Returns true if string is valid android package and apple bundle identifier
RetType PackageName(const QString& name)
{
return RegularExpressionValidator("[a-zA-Z][A-Za-z0-9]*(\\.[a-zA-Z][A-Za-z0-9]*)+", name);
}
// Returns true if valid android version number
RetType VersionNumber(const QString& value)
{
// Error handling built in already
int ver = value.toInt();
if (0 >= ver)
{
return RetType(QValidator::Invalid, QObject::tr("Version must be greater than 0."));
}
else if (ver > maxAndroidVersion)
{
return RetType(QValidator::Invalid, QObject::tr("Version must be less than or equal to %1.")
.arg(QString::number(maxAndroidVersion)));
}
else
{
return STANDARD_SUCCESS;
}
}
// Returns true if valid ios version number
RetType IOSVersionNumber(const QString& value)
{
return RegularExpressionValidator
("(0|[1-9][0-9]{0,8}|[1-2][0-1][0-9]{0,8})(\\.(0|[1-9][0-9]{0,8}|[1-2][0-1][0-9]{0,8})){0,2}",
value,
maxIosVersionLength);
}
// Returns true if Public App Key is valid length
RetType PublicAppKeyOrEmpty(const QString& value)
{
// If anyone knows that public app keys are not always 392 chars long
// then this MUST be changed
if (value.isEmpty() || value.length() == androidPublicAppKeyLength)
{
return STANDARD_SUCCESS;
}
else
{
return RetType(QValidator::Intermediate, QObject::tr("App key should be %1 characters long.")
.arg(QString::number(androidPublicAppKeyLength)));
}
}
// Returns true if path is empty or a valid xml file relative to <build dir>
RetType ValidXmlOrEmpty(const QString& path)
{
if (IsNotEmpty(path).first == QValidator::Acceptable)
{
return Internal::FileReadableAndCorrectType(xmlRelativePath + path, xmlMimeType);
}
else
{
return STANDARD_SUCCESS;
}
}
// Returns true if path is empty or a valid png file
RetType ValidPngOrEmpty(const QString& path)
{
if (IsNotEmpty(path).first == QValidator::Acceptable)
{
return Internal::FileReadableAndCorrectType(path, pngMimeType);
}
else
{
return STANDARD_SUCCESS;
}
}
} // namespace Validators
} // namespace ProjectSettingsTool
@@ -0,0 +1,50 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "FunctorValidator.h"
namespace ProjectSettingsTool
{
namespace Validators
{
namespace Internal
{
// Returns true if file is readable and the correct mime type
FunctorValidator::ReturnType FileReadableAndCorrectType(const QString& path, const QString& fileType);
}
static const int maxAndroidVersion = 2100000000;
// Returns true if valid cross platform file or directory name
FunctorValidator::ReturnType FileName(const QString& name);
// Returns true if valid cross platform file or directory name or empty
FunctorValidator::ReturnType FileNameOrEmpty(const QString& name);
// Returns true if string isn't empty
FunctorValidator::ReturnType IsNotEmpty(const QString& value);
// Returns true if string is valid as a boolean
FunctorValidator::ReturnType BoolString(const QString& value);
// Returns true if string is valid android package and apple bundle identifier
FunctorValidator::ReturnType PackageName(const QString& name);
// Returns true if valid android version number
FunctorValidator::ReturnType VersionNumber(const QString& value);
// Returns true if valid ios version number
FunctorValidator::ReturnType IOSVersionNumber(const QString& value);
// Returns true if Public App Key is valid length
FunctorValidator::ReturnType PublicAppKeyOrEmpty(const QString& value);
// Returns true if path is empty or a valid xml file relative to <build dir>
FunctorValidator::ReturnType ValidXmlOrEmpty(const QString& path);
// Returns true if path is empty or a valid png file
FunctorValidator::ReturnType ValidPngOrEmpty(const QString& path);
// Returns true if path is empty or valid png file with specified dimensions
template <int imageWidth, int imageHeight = imageWidth>
FunctorValidator::ReturnType PngImageSetSizeOrEmpty(const QString& path);
} // namespace Validators
} // namespace ProjectSettingsTool
#include "Validators_impl.h"
@@ -0,0 +1,51 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <QImage>
namespace ProjectSettingsTool
{
namespace Validators
{
static const char* pngMimeType = "image/png";
// Returns true if path is empty or valid png file with specified dimensions
template <int imageWidth, int imageHeight>
FunctorValidator::ReturnType PngImageSetSizeOrEmpty(const QString& path)
{
using RetType = FunctorValidator::ReturnType;
if (IsNotEmpty(path).first != QValidator::Acceptable)
{
return FunctorValidator::ReturnType(QValidator::Acceptable, "");
}
else
{
RetType correct = Internal::FileReadableAndCorrectType(path, pngMimeType);
if (correct.first)
{
QImage image(path);
if (imageWidth == image.width() && imageHeight == image.height())
{
return FunctorValidator::ReturnType(QValidator::Acceptable, "");
}
else
{
return RetType(QValidator::Intermediate, QObject::tr("Image is not %1x%2 pixels.")
.arg(QString::number(imageWidth)).arg(QString::number(imageHeight)));
}
}
else
{
return correct;
}
}
}
} // namespace Validators
} // namespace ProjectSettingsTool
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="-4, -4, 28, 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / System / Unlink</title>
<g id="All-use-case" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Use-case---Main-page-" transform="translate(-1296.000000, -310.000000)">
<g id="Group" transform="translate(1294.000000, 308.240103)">
<rect id="Icon-Background" fill-opacity="0" fill="#D8D8D8" x="0" y="0" width="24" height="24"></rect>
<path d="M8.5,8.5 L3,8.5 L3,7 L7,7 L7,3 L8.5,3 L8.5,8.5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
<path d="M21,21 L15.5,21 L15.5,19.5 L19.5,19.5 L19.5,15.5 L21,15.5 L21,21 Z" id="Combined-Shape" fill="#FFFFFF" transform="translate(18.250000, 18.250000) rotate(180.000000) translate(-18.250000, -18.250000) "></path>
<path d="M21,8.75 L21.25,15 L14.75,15.25 L20.75,14.75 L20.75,9.25 L14.75,9.25 L14.75,8.75 L21,8.75 Z M9.25,8.75 L9.25,9.25 L3.25,9.25 L3.25,14.75 L9.25,14.75 L9.25,15.25 L3,15.25 L2.75,9 L9.25,8.75 Z" id="Link" stroke="#FFFFFF" stroke-width="1.5" transform="translate(12.000000, 12.000000) rotate(-45.000000) translate(-12.000000, -12.000000) "></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:403ee8a94ec2f0ff532c70d2f56e18d7d83cb335a0cc444b1ac88f2fadff45f7
size 308
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e4058789615838100562d84345a5b0fdd7ea5416052105ce0ae2e5a1db13b952
size 367
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="-4 -4 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / System / Link</title>
<g id="All-use-case" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Use-case---Main-page-" transform="translate(-1261.000000, -310.000000)">
<g id="Group" transform="translate(1259.000000, 308.240103)">
<rect id="Icon-Background" fill-opacity="0" fill="#D8D8D8" x="0" y="0" width="24" height="24"></rect>
<path d="M21,8.75 L21.25,15 L9.75,15.25 L9.75,12.75 L10.25,12.75 L10.25,14.75 L20.75,14.75 L20.75,9.25 L17.75,9.25 L17.75,8.75 L21,8.75 Z M14.25,8.75 L14.25,11.25 L13.75,11.25 L13.75,9.25 L3.25,9.25 L3.25,14.75 L6.25,14.75 L6.25,15.25 L3,15.25 L2.75,9 L14.25,8.75 Z" id="Link" stroke="#FFFFFF" stroke-width="1.5" transform="translate(12.000000, 12.000000) rotate(-45.000000) translate(-12.000000, -12.000000) "></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,64 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectSettingsTool_precompiled.h"
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <Include/IPlugin.h>
#include "ProjectSettingsToolWindow.h"
#include "../Editor/LyViewPaneNames.h"
class ProjectSettingsToolPlugin
: public IPlugin
{
public:
ProjectSettingsToolPlugin([[maybe_unused]] IEditor* editor)
{
AzToolsFramework::ViewPaneOptions options;
options.showInMenu = false;
AzToolsFramework::RegisterViewPane<ProjectSettingsTool::ProjectSettingsToolWindow>(LyViewPane::ProjectSettingsTool, LyViewPane::ProjectSettingsTool, options);
}
void Release() override
{
AzToolsFramework::UnregisterViewPane(LyViewPane::ProjectSettingsTool);
delete this;
}
void ShowAbout() override {}
const char* GetPluginGUID() override { return "{C5B96A1A-036A-46F9-B7F0-5DF93494F988}"; }
DWORD GetPluginVersion() override { return 1; }
const char* GetPluginName() override { return "ProjectSettingsTool"; }
bool CanExitNow() override { return true; }
void OnEditorNotify([[maybe_unused]] EEditorNotifyEvent aEventId) override {}
};
PLUGIN_API IPlugin* CreatePluginInstance(PLUGIN_INIT_PARAM* pInitParam)
{
ISystem* pSystem = pInitParam->pIEditorInterface->GetSystem();
ModuleInitISystem(pSystem, "ProjectSettingsTool");
// the above line initializes the gEnv global variable if necessary, and also makes GetIEditor() and other similar functions work correctly.
return new ProjectSettingsToolPlugin(GetIEditor());
}
#if defined(AZ_PLATFORM_WINDOWS)
HINSTANCE g_hInstance = 0;
BOOL __stdcall DllMain(HINSTANCE hinstDLL, ULONG fdwReason, [[maybe_unused]] LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
g_hInstance = hinstDLL;
}
return TRUE;
}
#endif
@@ -0,0 +1,55 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
set(FILES
main.cpp
ProjectSettingsTool_precompiled.h
DefaultImageValidator.cpp
DefaultImageValidator.h
FunctorValidator.cpp
FunctorValidator.h
LastPathBus.h
Platforms.h
PlatformSettings.h
PlatformSettings_Android.cpp
PlatformSettings_Android.h
PlatformSettings_Base.cpp
PlatformSettings_Base.h
PlatformSettings_common.h
PlatformSettings_Ios.cpp
PlatformSettings_Ios.h
PlistDictionary.cpp
PlistDictionary.h
ProjectSettingsContainer.cpp
ProjectSettingsContainer.h
ProjectSettingsSerialization.cpp
ProjectSettingsSerialization.h
ProjectSettingsTool.qrc
ProjectSettingsToolWidget.ui
ProjectSettingsToolWindow.cpp
ProjectSettingsToolWindow.h
ProjectSettingsValidator.cpp
ProjectSettingsValidator.h
PropertyFileSelect.cpp
PropertyFileSelect.h
PropertyFuncValBrowseEdit.cpp
PropertyFuncValBrowseEdit.h
PropertyFuncValLineEdit.cpp
PropertyFuncValLineEdit.h
PropertyImagePreview.cpp
PropertyImagePreview.h
PropertyLinked.cpp
PropertyLinked.h
Utils.cpp
Utils.h
ValidationHandler.cpp
ValidationHandler.h
ValidatorBus.h
Validators.cpp
Validators.h
Validators_impl.h
)