Merge remote-tracking branch 'upstream/development' into GemUIDownload
This commit is contained in:
+18
-16
@@ -287,21 +287,22 @@ bool CCryDocManager::DoPromptFileName(QString& fileName, [[maybe_unused]] UINT n
|
||||
|
||||
return false;
|
||||
}
|
||||
CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAddToMRU)
|
||||
CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
|
||||
{
|
||||
assert(lpszFileName != nullptr);
|
||||
assert(filename != nullptr);
|
||||
|
||||
const bool reopenIfSame = openSameLevelOptions == COpenSameLevelOptions::ReopenLevelIfSame;
|
||||
// find the highest confidence
|
||||
auto pos = m_templateList.begin();
|
||||
CCrySingleDocTemplate::Confidence bestMatch = CCrySingleDocTemplate::noAttempt;
|
||||
CCrySingleDocTemplate* pBestTemplate = nullptr;
|
||||
CCryEditDoc* pOpenDocument = nullptr;
|
||||
|
||||
if (lpszFileName[0] == '\"')
|
||||
if (filename[0] == '\"')
|
||||
{
|
||||
++lpszFileName;
|
||||
++filename;
|
||||
}
|
||||
QString szPath = QString::fromUtf8(lpszFileName);
|
||||
QString szPath = QString::fromUtf8(filename);
|
||||
if (szPath.endsWith('"'))
|
||||
{
|
||||
szPath.remove(szPath.length() - 1, 1);
|
||||
@@ -325,7 +326,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd
|
||||
}
|
||||
}
|
||||
|
||||
if (pOpenDocument != nullptr)
|
||||
if (!reopenIfSame && pOpenDocument != nullptr)
|
||||
{
|
||||
return pOpenDocument;
|
||||
}
|
||||
@@ -336,7 +337,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), bAddToMRU, false);
|
||||
return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), addToMostRecentFileList, false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -513,7 +514,7 @@ public:
|
||||
QString m_appRoot;
|
||||
QString m_logFile;
|
||||
QString m_pythonArgs;
|
||||
QString m_pythontTestCase;
|
||||
QString m_pythonTestCase;
|
||||
QString m_execFile;
|
||||
QString m_execLineCmd;
|
||||
|
||||
@@ -562,7 +563,7 @@ public:
|
||||
const std::vector<std::pair<CommandLineStringOption, QString&> > stringOptions = {
|
||||
{{"logfile", "File name of the log file to write out to.", "logfile"}, m_logFile},
|
||||
{{"runpythonargs", "Command-line argument string to pass to the python script if --runpython or --runpythontest was used.", "runpythonargs"}, m_pythonArgs},
|
||||
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythontTestCase},
|
||||
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythonTestCase},
|
||||
{{"exec", "cfg file to run on startup, used for systems like automation", "exec"}, m_execFile},
|
||||
{{"rhi", "Command-line argument to force which rhi to use", "dummyString"}, dummyString },
|
||||
{{"rhi-device-validation", "Command-line argument to configure rhi validation", "dummyString"}, dummyString },
|
||||
@@ -818,7 +819,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b
|
||||
return OpenDocumentFile(lpszPathName, true, bMakeVisible);
|
||||
}
|
||||
|
||||
CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, [[maybe_unused]] bool bMakeVisible)
|
||||
CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, [[maybe_unused]] bool bMakeVisible)
|
||||
{
|
||||
CCryEditDoc* pCurDoc = GetIEditor()->GetDocument();
|
||||
|
||||
@@ -848,7 +849,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b
|
||||
{
|
||||
pCurDoc->OnOpenDocument(lpszPathName);
|
||||
pCurDoc->SetPathName(lpszPathName);
|
||||
if (bAddToMRU)
|
||||
if (addToMostRecentFileList)
|
||||
{
|
||||
CCryEditApp::instance()->AddToRecentFileList(lpszPathName);
|
||||
}
|
||||
@@ -1535,11 +1536,12 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
|
||||
{
|
||||
// Multiple testcases can be specified them with ';', these should match the files to run
|
||||
AZStd::vector<AZStd::string_view> testcaseList;
|
||||
QByteArray pythonTestCase = cmdInfo.m_pythonTestCase.toUtf8();
|
||||
testcaseList.resize(fileList.size());
|
||||
{
|
||||
int i = 0;
|
||||
AzFramework::StringFunc::TokenizeVisitor(
|
||||
fileStr.constData(),
|
||||
pythonTestCase.constData(),
|
||||
[&i, &testcaseList](AZStd::string_view elem)
|
||||
{
|
||||
testcaseList[i++] = (elem);
|
||||
@@ -2630,7 +2632,7 @@ void CCryEditApp::OnShowHelpers()
|
||||
void CCryEditApp::OnEditLevelData()
|
||||
{
|
||||
auto dir = QFileInfo(GetIEditor()->GetDocument()->GetLevelPathName()).dir();
|
||||
CFileUtil::EditTextFile(dir.absoluteFilePath("LevelData.xml").toUtf8().data());
|
||||
CFileUtil::EditTextFile(dir.absoluteFilePath("leveldata.xml").toUtf8().data());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -3364,7 +3366,7 @@ void CCryEditApp::OnOpenSlice()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName)
|
||||
CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
|
||||
{
|
||||
if (m_openingLevel)
|
||||
{
|
||||
@@ -3404,9 +3406,9 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName)
|
||||
openDocTraceHandler.SetShowWindow(false);
|
||||
}
|
||||
|
||||
// in this case, we set bAddToMRU to always be true because adding files to the MRU list
|
||||
// in this case, we set addToMostRecentFileList to always be true because adding files to the MRU list
|
||||
// automatically culls duplicate and normalizes paths anyway
|
||||
m_pDocManager->OpenDocumentFile(lpszFileName, true);
|
||||
m_pDocManager->OpenDocumentFile(filename, addToMostRecentFileList, openSameLevelOptions);
|
||||
|
||||
if (openDocTraceHandler.HasAnyErrors())
|
||||
{
|
||||
|
||||
+11
-3
@@ -85,6 +85,12 @@ public:
|
||||
|
||||
using EditorIdleProcessingBus = AZ::EBus<EditorIdleProcessing>;
|
||||
|
||||
enum class COpenSameLevelOptions
|
||||
{
|
||||
ReopenLevelIfSame,
|
||||
NotReopenIfSame
|
||||
};
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
class SANDBOX_API CCryEditApp
|
||||
@@ -174,7 +180,9 @@ public:
|
||||
virtual bool InitInstance();
|
||||
virtual int ExitInstance(int exitCode = 0);
|
||||
virtual bool OnIdle(LONG lCount);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* filename,
|
||||
bool addToMostRecentFileList=true,
|
||||
COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame);
|
||||
|
||||
CCryDocManager* GetDocManager() { return m_pDocManager; }
|
||||
|
||||
@@ -448,7 +456,7 @@ public:
|
||||
~CCrySingleDocTemplate() {};
|
||||
// avoid creating another CMainFrame
|
||||
// close other type docs before opening any things
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, bool bMakeVisible);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, bool bMakeVisible);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bMakeVisible = TRUE);
|
||||
virtual Confidence MatchDocType(const char* lpszPathName, CCryEditDoc*& rpDocMatch);
|
||||
|
||||
@@ -468,7 +476,7 @@ public:
|
||||
virtual void OnFileNew();
|
||||
virtual bool DoPromptFileName(QString& fileName, UINT nIDSTitle,
|
||||
DWORD lFlags, bool bOpenFileDialog, CDocTemplate* pTemplate);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName, bool bAddToMRU);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame);
|
||||
|
||||
QVector<CCrySingleDocTemplate*> m_templateList;
|
||||
};
|
||||
|
||||
+11
-13
@@ -143,20 +143,11 @@ namespace
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const bool addToMostRecentFileList = false;
|
||||
auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data(),
|
||||
addToMostRecentFileList, COpenSameLevelOptions::ReopenLevelIfSame);
|
||||
|
||||
auto previousDocument = GetIEditor()->GetDocument();
|
||||
QString previousPathName = (previousDocument != nullptr) ? previousDocument->GetLevelPathName() : "";
|
||||
auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data());
|
||||
|
||||
// the underlying document pointer doesn't change, so we can't check that; use the path name's instead
|
||||
|
||||
bool result = true;
|
||||
if (newDocument == nullptr || newDocument->IsLevelLoadFailed() || (newDocument->GetLevelPathName() == previousPathName))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
return newDocument != nullptr && !newDocument->IsLevelLoadFailed();
|
||||
}
|
||||
|
||||
bool PyOpenLevelNoPrompt(const char* pLevelName)
|
||||
@@ -407,6 +398,11 @@ inline namespace Commands
|
||||
{
|
||||
return AZ::Debug::Trace::WaitForDebugger(timeoutSeconds);
|
||||
}
|
||||
|
||||
AZStd::string PyGetFileAlias(AZStd::string alias)
|
||||
{
|
||||
return AZ::IO::FileIOBase::GetInstance()->GetAlias(alias.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -457,6 +453,8 @@ namespace AzToolsFramework
|
||||
addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger"));
|
||||
addLegacyGeneral(behaviorContext->Method("wait_for_debugger", PyWaitForDebugger, behaviorContext->MakeDefaultValues(-1.f), "Pauses this thread execution until the debugger has been attached"));
|
||||
|
||||
addLegacyGeneral(behaviorContext->Method("get_file_alias", PyGetFileAlias, nullptr, "Retrieves path for IO alias"));
|
||||
|
||||
// this will put these methods into the 'azlmbr.legacy.checkout_dialog' module
|
||||
auto addCheckoutDialog = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
|
||||
{
|
||||
|
||||
@@ -75,7 +75,10 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
|
||||
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
|
||||
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY)
|
||||
->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX)
|
||||
->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY)
|
||||
->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ);
|
||||
|
||||
serialize.Class<CameraInputSettings>()
|
||||
->Version(2)
|
||||
@@ -154,7 +157,16 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
"Invert direction of pan in local Y axis")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor",
|
||||
"Should the cursor be captured (hidden) while performing free look");
|
||||
"Should the cursor be captured (hidden) while performing free look")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position",
|
||||
"Default X Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position",
|
||||
"Default Y Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position",
|
||||
"Default Z Camera Position when a level is opened");
|
||||
|
||||
editContext->Class<CameraInputSettings>("Camera Input Settings", "")
|
||||
->DataElement(
|
||||
@@ -271,6 +283,12 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
|
||||
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
|
||||
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
|
||||
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
|
||||
SandboxEditor::SetDefaultCameraEditorPosition(
|
||||
AZ::Vector3(
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ
|
||||
));
|
||||
|
||||
SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId);
|
||||
SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId);
|
||||
@@ -308,6 +326,11 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
|
||||
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
|
||||
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
|
||||
|
||||
AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ();
|
||||
|
||||
m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateLeftChannelId = SandboxEditor::CameraTranslateLeftChannelId().GetName();
|
||||
|
||||
@@ -57,6 +57,9 @@ private:
|
||||
bool m_orbitYawRotationInverted;
|
||||
bool m_panInvertedX;
|
||||
bool m_panInvertedY;
|
||||
float m_defaultCameraPositionX;
|
||||
float m_defaultCameraPositionY;
|
||||
float m_defaultCameraPositionZ;
|
||||
|
||||
AZ::Crc32 RotateSmoothingVisibility() const
|
||||
{
|
||||
|
||||
@@ -52,6 +52,9 @@ namespace SandboxEditor
|
||||
constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId";
|
||||
constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId";
|
||||
constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionX = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/x";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionY = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/y";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionZ = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/z";
|
||||
|
||||
template<typename T>
|
||||
void SetRegistry(const AZStd::string_view setting, T&& value)
|
||||
@@ -111,6 +114,21 @@ namespace SandboxEditor
|
||||
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
|
||||
}
|
||||
|
||||
AZ::Vector3 DefaultEditorCameraPosition()
|
||||
{
|
||||
float xPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0));
|
||||
float yPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0));
|
||||
float zPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0));
|
||||
return AZ::Vector3(xPosition, yPosition, zPosition);
|
||||
}
|
||||
|
||||
void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition)
|
||||
{
|
||||
SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
|
||||
SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
|
||||
SetRegistry(CameraDefaultStartingPositionZ, defaultCameraPosition.GetZ());
|
||||
}
|
||||
|
||||
AZ::u64 MaxItemsShownInAssetBrowserSearch()
|
||||
{
|
||||
return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelId.h>
|
||||
|
||||
namespace SandboxEditor
|
||||
@@ -32,6 +33,9 @@ namespace SandboxEditor
|
||||
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
|
||||
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
|
||||
|
||||
SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition();
|
||||
SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition);
|
||||
|
||||
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
|
||||
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ void StartFixedCursorMode(QObject *viewport);
|
||||
|
||||
#define RENDER_MESH_TEST_DISTANCE (0.2f)
|
||||
#define CURSOR_FONT_HEIGHT 8.0f
|
||||
|
||||
namespace AZ::ViewportHelpers
|
||||
{
|
||||
static const char TextCantCreateCameraNoLevel[] = "Cannot create camera when no level is loaded.";
|
||||
@@ -623,16 +622,10 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
|
||||
float sx = terrainAabb.GetXExtent();
|
||||
float sy = terrainAabb.GetYExtent();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
// Initial camera will be at middle of the map at the height of 2
|
||||
// meters above the terrain (default terrain height is 32)
|
||||
viewTM.SetTranslation(Vec3(sx * 0.5f, sy * 0.5f, 34.0f));
|
||||
|
||||
viewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(viewTM);
|
||||
|
||||
UpdateScene();
|
||||
@@ -647,16 +640,10 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
|
||||
float sx = terrainAabb.GetXExtent();
|
||||
float sy = terrainAabb.GetYExtent();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
// Initial camera will be at middle of the map at the height of 2
|
||||
// meters above the terrain (default terrain height is 32)
|
||||
viewTM.SetTranslation(Vec3(sx * 0.5f, sy * 0.5f, 34.0f));
|
||||
|
||||
viewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(viewTM);
|
||||
}
|
||||
break;
|
||||
@@ -1345,10 +1332,6 @@ void EditorViewportWidget::keyPressEvent(QKeyEvent* event)
|
||||
|
||||
void EditorViewportWidget::SetViewTM(const Matrix34& tm)
|
||||
{
|
||||
if (m_viewSourceType == ViewSourceType::None)
|
||||
{
|
||||
m_defaultViewTM = tm;
|
||||
}
|
||||
SetViewTM(tm, false);
|
||||
}
|
||||
|
||||
@@ -1445,6 +1428,10 @@ void EditorViewportWidget::SetViewTM(const Matrix34& camMatrix, bool bMoveOnly)
|
||||
"Please report this as a bug."
|
||||
);
|
||||
}
|
||||
else if (shouldUpdateObject == ShouldUpdateObject::No)
|
||||
{
|
||||
GetCurrentAtomView()->SetCameraTransform(LYTransformToAZMatrix3x4(camMatrix));
|
||||
}
|
||||
|
||||
if (m_pressedKeyState == KeyPressedState::PressedThisFrame)
|
||||
{
|
||||
@@ -2028,6 +2015,9 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
m_viewSourceType = ViewSourceType::None;
|
||||
GetViewManager()->SetCameraObjectId(GUID_NULL);
|
||||
SetName(m_defaultViewName);
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
// Synchronize the configured editor viewport FOV to the default camera
|
||||
@@ -2530,6 +2520,11 @@ bool EditorViewportSettings::StickySelectEnabled() const
|
||||
return SandboxEditor::StickySelectEnabled();
|
||||
}
|
||||
|
||||
AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return SandboxEditor::DefaultEditorCameraPosition();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
bool EditorViewportWidget::ShouldPreviewFullscreen() const
|
||||
@@ -2641,5 +2636,4 @@ void EditorViewportWidget::StopFullscreenPreview()
|
||||
// Show the main window
|
||||
MainWindow::instance()->show();
|
||||
}
|
||||
|
||||
#include <moc_EditorViewportWidget.cpp>
|
||||
|
||||
@@ -78,6 +78,7 @@ struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::Vi
|
||||
float ManipulatorLineBoundWidth() const override;
|
||||
float ManipulatorCircleBoundWidth() const override;
|
||||
bool StickySelectEnabled() const override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
};
|
||||
|
||||
// EditorViewportWidget window
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml"
|
||||
#define MATERIAL_LEVEL_LIBRARY_FILE "Materials.xml"
|
||||
#define RESOURCE_LIST_FILE "ResourceList.txt"
|
||||
#define USED_RESOURCE_LIST_FILE "UsedResourceList.txt"
|
||||
#define SHADER_LIST_FILE "ShadersList.txt"
|
||||
#define MUSIC_LEVEL_LIBRARY_FILE "music.xml"
|
||||
#define MATERIAL_LEVEL_LIBRARY_FILE "materials.xml"
|
||||
#define RESOURCE_LIST_FILE "resourcelist.txt"
|
||||
#define USED_RESOURCE_LIST_FILE "usedresourcelist.txt"
|
||||
#define SHADER_LIST_FILE "shaderslist.txt"
|
||||
|
||||
#define GetAValue(rgb) ((BYTE)((rgb) >> 24))
|
||||
|
||||
@@ -185,9 +185,9 @@ bool CGameExporter::Export(unsigned int flags, [[maybe_unused]] EEndian eExportE
|
||||
ExportOcclusionMesh(sLevelPath.toUtf8().data());
|
||||
|
||||
//! Export Level data.
|
||||
CLogFile::WriteLine("Exporting LevelData.xml");
|
||||
CLogFile::WriteLine("Exporting leveldata.xml");
|
||||
ExportLevelData(sLevelPath);
|
||||
CLogFile::WriteLine("Exporting LevelData.xml done.");
|
||||
CLogFile::WriteLine("Exporting leveldata.xml done.");
|
||||
|
||||
ExportLevelInfo(sLevelPath);
|
||||
|
||||
@@ -266,26 +266,26 @@ void CGameExporter::ExportOcclusionMesh(const char* pszGamePath)
|
||||
void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/)
|
||||
{
|
||||
IEditor* pEditor = GetIEditor();
|
||||
pEditor->SetStatusText(QObject::tr("Exporting LevelData.xml..."));
|
||||
pEditor->SetStatusText(QObject::tr("Exporting leveldata.xml..."));
|
||||
|
||||
char versionString[256];
|
||||
pEditor->GetFileVersion().ToString(versionString);
|
||||
|
||||
XmlNodeRef root = XmlHelpers::CreateXmlNode("LevelData");
|
||||
XmlNodeRef root = XmlHelpers::CreateXmlNode("leveldata");
|
||||
root->setAttr("SandboxVersion", versionString);
|
||||
XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("LevelDataAction");
|
||||
XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("leveldataaction");
|
||||
rootAction->setAttr("SandboxVersion", versionString);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save Level Data XML
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString levelDataFile = path + "LevelData.xml";
|
||||
QString levelDataFile = path + "leveldata.xml";
|
||||
XmlString xmlData = root->getXML();
|
||||
CCryMemFile file;
|
||||
file.Write(xmlData.c_str(), static_cast<unsigned int>(xmlData.length()));
|
||||
m_levelPak.m_pakFile.UpdateFile(levelDataFile.toUtf8().data(), file);
|
||||
|
||||
QString levelDataActionFile = path + "LevelDataAction.xml";
|
||||
QString levelDataActionFile = path + "leveldataaction.xml";
|
||||
XmlString xmlDataAction = rootAction->getXML();
|
||||
CCryMemFile fileAction;
|
||||
fileAction.Write(xmlDataAction.c_str(), static_cast<unsigned int>(xmlDataAction.length()));
|
||||
@@ -298,7 +298,7 @@ void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/
|
||||
if (savedEntities)
|
||||
{
|
||||
QString entitiesFile;
|
||||
entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "Mission0");
|
||||
entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "mission0");
|
||||
m_levelPak.m_pakFile.UpdateFile(entitiesFile.toUtf8().data(), entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ void CGameExporter::ExportLevelInfo(const QString& path)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save LevelInfo file.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString filename = path + "LevelInfo.xml";
|
||||
QString filename = path + "levelinfo.xml";
|
||||
XmlString xmlData = root->getXML();
|
||||
|
||||
CCryMemFile file;
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace UnitTest
|
||||
void BeginCursorCapture() override;
|
||||
void EndCursorCapture() override;
|
||||
bool IsMouseOver() const override;
|
||||
void SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) override;
|
||||
void ClearOverrideCursor() override;
|
||||
|
||||
private:
|
||||
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
|
||||
@@ -58,6 +60,17 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::SetOverrideCursor(
|
||||
[[maybe_unused]] AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::ClearOverrideCursor()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace AZ
|
||||
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderIsDown)
|
||||
{
|
||||
message = "Failed to put entries/dependencies into source control as the provider is not available.\n";
|
||||
reportAsWarning = true;
|
||||
}
|
||||
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_CertificateInvalid)
|
||||
{
|
||||
|
||||
@@ -577,7 +577,9 @@ namespace AZ
|
||||
}
|
||||
|
||||
azstrcat(lines[i], AZ_ARRAY_SIZE(lines[i]), "\n");
|
||||
AZ_Printf(window, "%s", lines[i]); // feed back into the trace system so that listeners can get it.
|
||||
// Use Output instead of AZ_Printf to be consistent with the exception output code and avoid
|
||||
// this accidentally being suppressed as a normal message
|
||||
Output(window, lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace AzFramework
|
||||
class IMatchmakingAsyncRequests
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
|
||||
AZ_RTTI(IMatchmakingAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
|
||||
|
||||
IMatchmakingAsyncRequests() = default;
|
||||
virtual ~IMatchmakingAsyncRequests() = default;
|
||||
@@ -60,4 +60,31 @@ namespace AzFramework
|
||||
// @param stopMatchmakingRequest The request of StopMatchmaking operation
|
||||
virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
|
||||
};
|
||||
|
||||
//! MatchmakingAsyncRequestNotifications
|
||||
//! The notifications correspond to matchmaking async requests
|
||||
class MatchmakingAsyncRequestNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
// Safeguard handler for multi-threaded use case
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
|
||||
virtual void OnAcceptMatchAsyncComplete() = 0;
|
||||
|
||||
// OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
|
||||
// @param matchmakingTicketId The unique identifier for the matchmaking ticket
|
||||
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
|
||||
|
||||
// OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
|
||||
virtual void OnStopMatchmakingAsyncComplete() = 0;
|
||||
};
|
||||
using MatchmakingAsyncRequestNotificationBus = AZ::EBus<MatchmakingAsyncRequestNotifications>;
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -13,36 +13,10 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! MatchmakingAsyncRequestNotifications
|
||||
//! The notifications correspond to matchmaking async requests
|
||||
class MatchmakingAsyncRequestNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
// Safeguard handler for multi-threaded use case
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
|
||||
virtual void OnAcceptMatchAsyncComplete() = 0;
|
||||
|
||||
// OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
|
||||
// @param matchmakingTicketId The unique identifier for the matchmaking ticket
|
||||
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
|
||||
|
||||
// OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
|
||||
virtual void OnStopMatchmakingAsyncComplete() = 0;
|
||||
};
|
||||
using MatchmakingAsyncRequestNotificationBus = AZ::EBus<MatchmakingAsyncRequestNotifications>;
|
||||
|
||||
//! MatchmakingNotifications
|
||||
//! The matchmaking notifications to listen for performing required operations
|
||||
class MatchAcceptanceNotifications
|
||||
//! based on matchmaking ticket event
|
||||
class MatchmakingNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
@@ -55,8 +29,18 @@ namespace AzFramework
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE
|
||||
// OnMatchAcceptance is fired when match is found and pending on acceptance
|
||||
// Use this notification to accept found match
|
||||
virtual void OnMatchAcceptance() = 0;
|
||||
|
||||
// OnMatchComplete is fired when match is complete
|
||||
virtual void OnMatchComplete() = 0;
|
||||
|
||||
// OnMatchError is fired when match is processed with error
|
||||
virtual void OnMatchError() = 0;
|
||||
|
||||
// OnMatchFailure is fired when match is failed to complete
|
||||
virtual void OnMatchFailure() = 0;
|
||||
};
|
||||
using MatchAcceptanceNotificationBus = AZ::EBus<MatchAcceptanceNotifications>;
|
||||
using MatchmakingNotificationBus = AZ::EBus<MatchmakingNotifications>;
|
||||
} // namespace AzFramework
|
||||
|
||||
+2
@@ -46,6 +46,8 @@ namespace AzManipulatorTestFramework
|
||||
virtual void UpdateVisibility() = 0;
|
||||
//! Set if sticky select is enabled or not.
|
||||
virtual void SetStickySelect(bool enabled) = 0;
|
||||
//! Get default Editor Camera Position.
|
||||
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
|
||||
};
|
||||
|
||||
//! This interface is used to simulate the manipulator manager while the manipulators are under test.
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ namespace AzManipulatorTestFramework
|
||||
int GetViewportId() const override;
|
||||
void UpdateVisibility() override;
|
||||
void SetStickySelect(bool enabled) override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
|
||||
// ViewportInteractionRequestBus overrides ...
|
||||
AzFramework::CameraState GetCameraState() override;
|
||||
|
||||
@@ -120,6 +120,11 @@ namespace AzManipulatorTestFramework
|
||||
m_stickySelect = enabled;
|
||||
}
|
||||
|
||||
AZ::Vector3 ViewportInteraction::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ViewportInteraction::SetGridSize(float size)
|
||||
{
|
||||
m_gridSize = size;
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace AzNetworking
|
||||
: m_delta(delta)
|
||||
, m_dataSerializer(m_delta.GetBufferPtr(), m_delta.GetBufferCapacity())
|
||||
{
|
||||
m_namePrefix.reserve(128);
|
||||
;
|
||||
}
|
||||
|
||||
DeltaSerializerCreate::~DeltaSerializerCreate()
|
||||
@@ -73,7 +73,7 @@ namespace AzNetworking
|
||||
// Delete any left over records that might be hanging around
|
||||
for (auto iter : m_records)
|
||||
{
|
||||
delete iter.second;
|
||||
delete iter;
|
||||
}
|
||||
m_records.clear();
|
||||
}
|
||||
@@ -160,28 +160,13 @@ namespace AzNetworking
|
||||
return SerializeHelper(buffer, bufferCapacity, isString, outSize, name);
|
||||
}
|
||||
|
||||
AZStd::string DeltaSerializerCreate::GetNextObjectName(const char* name)
|
||||
bool DeltaSerializerCreate::BeginObject([[maybe_unused]] const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
AZStd::string objectName = name;
|
||||
objectName += ".";
|
||||
objectName += AZStd::to_string(m_objectCounter);
|
||||
++m_objectCounter;
|
||||
return objectName;
|
||||
}
|
||||
|
||||
bool DeltaSerializerCreate::BeginObject(const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
m_nameLengthStack.push_back(m_namePrefix.length());
|
||||
m_namePrefix += GetNextObjectName(name);
|
||||
m_namePrefix += ".";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeltaSerializerCreate::EndObject([[maybe_unused]] const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
const size_t prevLen = m_nameLengthStack.back();
|
||||
m_nameLengthStack.pop_back();
|
||||
m_namePrefix.resize(prevLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -205,25 +190,15 @@ namespace AzNetworking
|
||||
{
|
||||
typedef AbstractValue::ValueT<T> ValueType;
|
||||
|
||||
const size_t prevLen = m_namePrefix.length();
|
||||
m_namePrefix += GetNextObjectName(name);
|
||||
|
||||
const AZ::HashValue32 nameHash = AZ::TypeHash32(m_namePrefix.c_str());
|
||||
|
||||
m_namePrefix.resize(prevLen);
|
||||
|
||||
AbstractValue::BaseValue*& baseValue = m_records[nameHash];
|
||||
AbstractValue::BaseValue* baseValue = m_records.size() > m_objectCounter ? m_records[m_objectCounter] : nullptr;
|
||||
++m_objectCounter;
|
||||
|
||||
// If we are in the gather records phase, just save off the value records
|
||||
if (m_gatheringRecords)
|
||||
{
|
||||
if (baseValue != nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Duplicate name encountered in delta serializer. This will cause data to be serialized incorrectly.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_Assert(baseValue == nullptr, "Expected to create a new record but found a pre-existing one at index %d", m_objectCounter - 1);
|
||||
baseValue = new ValueType(value);
|
||||
m_records.push_back(baseValue);
|
||||
}
|
||||
else // If we are not gathering records, then we are comparing them
|
||||
{
|
||||
|
||||
@@ -90,8 +90,6 @@ namespace AzNetworking
|
||||
DeltaSerializerCreate(const DeltaSerializerCreate&) = delete;
|
||||
DeltaSerializerCreate& operator=(const DeltaSerializerCreate&) = delete;
|
||||
|
||||
AZStd::string GetNextObjectName(const char* name);
|
||||
|
||||
template <typename T>
|
||||
bool SerializeHelper(T& value, uint32_t bufferCapacity, bool isString, uint32_t& outSize, const char* name);
|
||||
|
||||
@@ -105,9 +103,7 @@ namespace AzNetworking
|
||||
|
||||
bool m_gatheringRecords = false;
|
||||
uint32_t m_objectCounter = 0;
|
||||
AZStd::string m_namePrefix;
|
||||
AZStd::vector<size_t> m_nameLengthStack;
|
||||
AZStd::unordered_map<AZ::HashValue32, AbstractValue::BaseValue*> m_records;
|
||||
AZStd::vector<AbstractValue::BaseValue*> m_records;
|
||||
NetworkInputSerializer m_dataSerializer;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,4 +11,213 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
struct DeltaDataElement
|
||||
{
|
||||
AzNetworking::PacketId m_packetId = AzNetworking::InvalidPacketId;
|
||||
uint32_t m_id = 0;
|
||||
AZ::TimeMs m_timeMs = AZ::TimeMs{ 0 };
|
||||
float m_blendFactor = 0.f;
|
||||
AZStd::vector<int> m_growVector, m_shrinkVector;
|
||||
|
||||
bool Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
if (!serializer.Serialize(m_packetId, "PacketId")
|
||||
|| !serializer.Serialize(m_id, "Id")
|
||||
|| !serializer.Serialize(m_timeMs, "TimeMs")
|
||||
|| !serializer.Serialize(m_blendFactor, "BlendFactor")
|
||||
|| !serializer.Serialize(m_growVector, "GrowVector")
|
||||
|| !serializer.Serialize(m_shrinkVector, "ShrinkVector"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct DeltaDataContainer
|
||||
{
|
||||
AZStd::string m_containerName;
|
||||
AZStd::array<DeltaDataElement, 32> m_container;
|
||||
|
||||
// This logic is modeled after NetworkInputArray serialization in the Multiplayer Gem
|
||||
bool Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
// Always serialize the full first element
|
||||
if(!m_container[0].Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < m_container.size(); ++i)
|
||||
{
|
||||
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
|
||||
{
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
// Read out the delta
|
||||
if (!deltaSerializer.Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start with previous value
|
||||
m_container[i] = m_container[i - 1];
|
||||
// Then apply delta
|
||||
AzNetworking::DeltaSerializerApply applySerializer(deltaSerializer);
|
||||
if (!applySerializer.ApplyDelta(m_container[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
// Create the delta
|
||||
AzNetworking::DeltaSerializerCreate createSerializer(deltaSerializer);
|
||||
if (!createSerializer.CreateDelta(m_container[i - 1], m_container[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then write out the delta
|
||||
if (!deltaSerializer.Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This logic is modeled after NetworkInputArray serialization in the Multiplayer Gem
|
||||
bool SerializeNoDelta(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_container.size(); ++i)
|
||||
{
|
||||
if(!m_container[i].Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class DeltaSerializerTests
|
||||
: public UnitTest::AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
UnitTest::AllocatorsTestFixture::SetUp();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
UnitTest::AllocatorsTestFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr float BLEND_FACTOR_SCALE = 1.1f;
|
||||
static constexpr uint32_t TIME_SCALE = 10;
|
||||
|
||||
DeltaDataContainer TestDeltaContainer()
|
||||
{
|
||||
DeltaDataContainer testContainer;
|
||||
AZStd::vector<int> growVector, shrinkVector;
|
||||
shrinkVector.resize(testContainer.m_container.array_size);
|
||||
|
||||
testContainer.m_containerName = "TestContainer";
|
||||
for (int i = 0; i < testContainer.m_container.array_size; ++i)
|
||||
{
|
||||
testContainer.m_container[i].m_packetId = AzNetworking::PacketId(i);
|
||||
testContainer.m_container[i].m_id = i;
|
||||
testContainer.m_container[i].m_timeMs = AZ::TimeMs(i * TIME_SCALE);
|
||||
testContainer.m_container[i].m_blendFactor = BLEND_FACTOR_SCALE * i;
|
||||
growVector.push_back(i);
|
||||
testContainer.m_container[i].m_growVector = growVector;
|
||||
shrinkVector.resize(testContainer.m_container.array_size - i);
|
||||
testContainer.m_container[i].m_shrinkVector = shrinkVector;
|
||||
}
|
||||
|
||||
return testContainer;
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaArray)
|
||||
{
|
||||
DeltaDataContainer inContainer = TestDeltaContainer();
|
||||
AZStd::array<uint8_t, 2048> buffer;
|
||||
AzNetworking::NetworkInputSerializer inSerializer(buffer.data(), static_cast<uint32_t>(buffer.size()));
|
||||
|
||||
// Always serialize the full first element
|
||||
EXPECT_TRUE(inContainer.Serialize(inSerializer));
|
||||
|
||||
DeltaDataContainer outContainer;
|
||||
AzNetworking::NetworkOutputSerializer outSerializer(buffer.data(), static_cast<uint32_t>(buffer.size()));
|
||||
|
||||
EXPECT_TRUE(outContainer.Serialize(outSerializer));
|
||||
|
||||
for (uint32_t i = 0; i > outContainer.m_container.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(inContainer.m_container[i].m_blendFactor, outContainer.m_container[i].m_blendFactor);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_id, outContainer.m_container[i].m_id);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_packetId, outContainer.m_container[i].m_packetId);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_timeMs, outContainer.m_container[i].m_timeMs);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_growVector[i], outContainer.m_container[i].m_growVector[i]);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_growVector.size(), outContainer.m_container[i].m_growVector.size());
|
||||
EXPECT_EQ(inContainer.m_container[i].m_shrinkVector.size(), outContainer.m_container[i].m_shrinkVector.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaSerializerCreateUnused)
|
||||
{
|
||||
// Every function here should return a constant value regardless of inputs
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
AzNetworking::DeltaSerializerCreate createSerializer(deltaSerializer);
|
||||
|
||||
EXPECT_EQ(createSerializer.GetCapacity(), 0);
|
||||
EXPECT_EQ(createSerializer.GetSize(), 0);
|
||||
EXPECT_EQ(createSerializer.GetBuffer(), nullptr);
|
||||
EXPECT_EQ(createSerializer.GetSerializerMode(), AzNetworking::SerializerMode::ReadFromObject);
|
||||
|
||||
createSerializer.ClearTrackedChangesFlag(); //NO-OP
|
||||
EXPECT_FALSE(createSerializer.GetTrackedChangesFlag());
|
||||
EXPECT_TRUE(createSerializer.BeginObject("CreateSerializer", "Begin"));
|
||||
EXPECT_TRUE(createSerializer.EndObject("CreateSerializer", "End"));
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaArraySize)
|
||||
{
|
||||
DeltaDataContainer deltaContainer = TestDeltaContainer();
|
||||
DeltaDataContainer noDeltaContainer = TestDeltaContainer();
|
||||
|
||||
AZStd::array<uint8_t, 2048> deltaBuffer;
|
||||
AzNetworking::NetworkInputSerializer deltaSerializer(deltaBuffer.data(), static_cast<uint32_t>(deltaBuffer.size()));
|
||||
AZStd::array<uint8_t, 2048> noDeltaBuffer;
|
||||
AzNetworking::NetworkInputSerializer noDeltaSerializer(noDeltaBuffer.data(), static_cast<uint32_t>(noDeltaBuffer.size()));
|
||||
|
||||
EXPECT_TRUE(deltaContainer.Serialize(deltaSerializer));
|
||||
EXPECT_FALSE(noDeltaContainer.SerializeNoDelta(noDeltaSerializer)); // Should run out of space
|
||||
EXPECT_EQ(noDeltaSerializer.GetCapacity(), noDeltaSerializer.GetSize()); // Verify that the serializer filled up
|
||||
EXPECT_FALSE(noDeltaSerializer.IsValid()); // and that it is no longer valid due to lack of space
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaSerializerApplyUnused)
|
||||
{
|
||||
// Every function here should return a constant value regardless of inputs
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
AzNetworking::DeltaSerializerApply applySerializer(deltaSerializer);
|
||||
|
||||
EXPECT_EQ(applySerializer.GetCapacity(), 0);
|
||||
EXPECT_EQ(applySerializer.GetSize(), 0);
|
||||
EXPECT_EQ(applySerializer.GetBuffer(), nullptr);
|
||||
EXPECT_EQ(applySerializer.GetSerializerMode(), AzNetworking::SerializerMode::WriteToObject);
|
||||
|
||||
applySerializer.ClearTrackedChangesFlag(); //NO-OP
|
||||
EXPECT_FALSE(applySerializer.GetTrackedChangesFlag());
|
||||
EXPECT_TRUE(applySerializer.BeginObject("CreateSerializer", "Begin"));
|
||||
EXPECT_TRUE(applySerializer.EndObject("CreateSerializer", "End"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzQtComponents/Components/ToastNotification.h>
|
||||
#include <AzQtComponents/Components/ui_ToastNotification.h>
|
||||
|
||||
#include <QCursor>
|
||||
#include <QIcon>
|
||||
#include <QToolButton>
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
ToastNotification::ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration)
|
||||
: QDialog(parent, Qt::FramelessWindowHint)
|
||||
, m_closeOnClick(true)
|
||||
, m_ui(new Ui::ToastNotification())
|
||||
, m_fadeAnimation(nullptr)
|
||||
{
|
||||
setProperty("HasNoWindowDecorations", true);
|
||||
|
||||
setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
QIcon toastIcon;
|
||||
|
||||
switch (toastConfiguration.m_toastType)
|
||||
{
|
||||
case ToastType::Error:
|
||||
toastIcon = QIcon(":/stylesheet/img/logging/error.svg");
|
||||
break;
|
||||
case ToastType::Warning:
|
||||
toastIcon = QIcon(":/stylesheet/img/logging/warning-yellow.svg");
|
||||
break;
|
||||
case ToastType::Information:
|
||||
toastIcon = QIcon(":/stylesheet/img/logging/information.svg");
|
||||
break;
|
||||
case ToastType::Custom:
|
||||
toastIcon = QIcon(toastConfiguration.m_customIconImage);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_ui->iconLabel->setPixmap(toastIcon.pixmap(64, 64));
|
||||
|
||||
m_ui->titleLabel->setText(toastConfiguration.m_title);
|
||||
m_ui->mainLabel->setText(toastConfiguration.m_description);
|
||||
|
||||
m_lifeSpan.setInterval(aznumeric_cast<int>(toastConfiguration.m_duration.count()));
|
||||
m_closeOnClick = toastConfiguration.m_closeOnClick;
|
||||
|
||||
m_ui->closeButton->setVisible(m_closeOnClick);
|
||||
QObject::connect(m_ui->closeButton, &QToolButton::clicked, this, &ToastNotification::accept);
|
||||
|
||||
m_fadeDuration = toastConfiguration.m_fadeDuration;
|
||||
|
||||
QObject::connect(&m_lifeSpan, &QTimer::timeout, this, &ToastNotification::FadeOut);
|
||||
}
|
||||
|
||||
ToastNotification::~ToastNotification()
|
||||
{
|
||||
}
|
||||
|
||||
void ToastNotification::ShowToastAtCursor()
|
||||
{
|
||||
QPoint globalCursorPos = QCursor::pos();
|
||||
|
||||
// Left/middle align it relative to the cursor.
|
||||
QPointF anchorPoint(0, 0.5);
|
||||
|
||||
// Magic offset to try to get it to not hide under the cursor.
|
||||
// No way to get this programatically from what I can tell.
|
||||
globalCursorPos.setX(globalCursorPos.x() + 16);
|
||||
|
||||
ShowToastAtPoint(globalCursorPos, anchorPoint);
|
||||
}
|
||||
|
||||
void ToastNotification::ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint)
|
||||
{
|
||||
show();
|
||||
updateGeometry();
|
||||
|
||||
UpdatePosition(screenPosition, anchorPoint);
|
||||
}
|
||||
|
||||
void ToastNotification::UpdatePosition(const QPoint& screenPosition, const QPointF& anchorPoint)
|
||||
{
|
||||
QRect dialogGeometry = geometry();
|
||||
|
||||
QPoint finalPosition;
|
||||
finalPosition.setX(aznumeric_cast<int>(screenPosition.x() - dialogGeometry.width() * anchorPoint.x()));
|
||||
finalPosition.setY(aznumeric_cast<int>(screenPosition.y() - dialogGeometry.height() * anchorPoint.y()));
|
||||
|
||||
move(finalPosition);
|
||||
}
|
||||
|
||||
void ToastNotification::showEvent(QShowEvent* showEvent)
|
||||
{
|
||||
QDialog::showEvent(showEvent);
|
||||
|
||||
if (m_fadeDuration.count() > 0)
|
||||
{
|
||||
m_fadeAnimation = new QPropertyAnimation(this, "windowOpacity", this);
|
||||
m_fadeAnimation->setKeyValueAt(0, 0);
|
||||
m_fadeAnimation->setKeyValueAt(1, 1);
|
||||
|
||||
m_fadeAnimation->setDuration(static_cast<int>(m_fadeDuration.count()));
|
||||
|
||||
m_fadeAnimation->start();
|
||||
|
||||
QObject::connect(m_fadeAnimation, &QPropertyAnimation::finished, this, &ToastNotification::StartTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void ToastNotification::hideEvent(QHideEvent* hideEvent)
|
||||
{
|
||||
QDialog::hideEvent(hideEvent);
|
||||
|
||||
m_lifeSpan.stop();
|
||||
|
||||
if (m_fadeAnimation)
|
||||
{
|
||||
m_fadeAnimation->stop();
|
||||
delete m_fadeAnimation;
|
||||
}
|
||||
|
||||
emit ToastNotificationHidden();
|
||||
}
|
||||
|
||||
void ToastNotification::mousePressEvent(QMouseEvent*)
|
||||
{
|
||||
if (m_closeOnClick)
|
||||
{
|
||||
emit ToastNotificationInteraction();
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
bool ToastNotification::eventFilter(QObject*, QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
|
||||
if (mouseEvent && mouseEvent->button() == Qt::MouseButton::LeftButton)
|
||||
{
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToastNotification::StartTimer()
|
||||
{
|
||||
delete m_fadeAnimation;
|
||||
m_fadeAnimation = nullptr;
|
||||
|
||||
if (m_lifeSpan.interval() != 0)
|
||||
{
|
||||
m_lifeSpan.start();
|
||||
}
|
||||
}
|
||||
|
||||
void ToastNotification::FadeOut()
|
||||
{
|
||||
if (m_fadeDuration.count() > 0)
|
||||
{
|
||||
m_fadeAnimation = new QPropertyAnimation(this, "windowOpacity", this);
|
||||
m_fadeAnimation->setKeyValueAt(0, windowOpacity());
|
||||
m_fadeAnimation->setKeyValueAt(1, 0);
|
||||
|
||||
m_fadeAnimation->setDuration(static_cast<int>(m_fadeDuration.count()));
|
||||
|
||||
m_fadeAnimation->start();
|
||||
|
||||
QObject::connect(m_fadeAnimation, &QPropertyAnimation::finished, this, &ToastNotification::accept);
|
||||
}
|
||||
else
|
||||
{
|
||||
accept();
|
||||
}
|
||||
}
|
||||
#include "Components/moc_ToastNotification.cpp"
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzQtComponents/AzQtComponentsAPI.h>
|
||||
#include <AzQtComponents/Components/ToastNotificationConfiguration.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <QEvent>
|
||||
#include <QDialog>
|
||||
#include <QMouseEvent>
|
||||
#include <QTimer>
|
||||
#endif
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ToastNotification;
|
||||
}
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QPropertyAnimation)
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
class AZ_QT_COMPONENTS_API ToastNotification
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ToastNotification, AZ::SystemAllocator, 0);
|
||||
|
||||
ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration);
|
||||
virtual ~ToastNotification();
|
||||
|
||||
// Shows the toast notification relative to the current cursor.
|
||||
void ShowToastAtCursor();
|
||||
|
||||
// Aligns the toast notification so that the specified anchor point on the notification lies on the specified screen position.
|
||||
// i.e. anchor point of 0,0 will align the top left position of the dialog with the screen position
|
||||
// anchor point of 1,1 will align the bottom right position of the dialog with the screen position
|
||||
void ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint);
|
||||
|
||||
void UpdatePosition(const QPoint& screenPosition, const QPointF& anchorPoint);
|
||||
|
||||
// QDialog
|
||||
void showEvent(QShowEvent* showEvent) override;
|
||||
void hideEvent(QHideEvent* hideEvent) override;
|
||||
void mousePressEvent(QMouseEvent* mouseEvent) override;
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
public slots:
|
||||
void StartTimer();
|
||||
void FadeOut();
|
||||
|
||||
signals:
|
||||
void ToastNotificationHidden();
|
||||
void ToastNotificationInteraction();
|
||||
|
||||
private:
|
||||
QPropertyAnimation* m_fadeAnimation;
|
||||
|
||||
bool m_closeOnClick;
|
||||
QTimer m_lifeSpan;
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
AZStd::chrono::milliseconds m_fadeDuration;
|
||||
AZStd::unique_ptr<Ui::ToastNotification> m_ui;
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
};
|
||||
} // namespace AzQtComponents
|
||||
@@ -0,0 +1,236 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ToastNotification</class>
|
||||
<widget class="QDialog" name="ToastNotification">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>225</width>
|
||||
<height>48</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>225</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="icon_frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgba(255, 255, 255, 20);</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgba(255, 255, 255, 0);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="resources.qrc">:/stylesheet/img/logging/information.svg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="text_frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="titleFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="titleLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Invalid Connection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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="QToolButton" name="closeButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/stylesheet/img/close_x.svg</normaloff>:/stylesheet/img/close_x.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="mainLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Types are not a match.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AzQtComponents/Components/ToastNotificationConfiguration.h>
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
ToastConfiguration::ToastConfiguration(ToastType toastType, const QString& title, const QString& description)
|
||||
: m_toastType(toastType)
|
||||
, m_title(title)
|
||||
, m_description(description)
|
||||
{
|
||||
}
|
||||
} // namespace AzQtComponents
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzQtComponents/AzQtComponentsAPI.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/chrono/chrono.h>
|
||||
#include <QString>
|
||||
#endif
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
enum class ToastType
|
||||
{
|
||||
Information,
|
||||
Warning,
|
||||
Error,
|
||||
Custom
|
||||
};
|
||||
|
||||
class AZ_QT_COMPONENTS_API ToastConfiguration
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ToastConfiguration, AZ::SystemAllocator, 0);
|
||||
ToastConfiguration(ToastType toastType, const QString& title, const QString& description);
|
||||
|
||||
bool m_closeOnClick = true;
|
||||
|
||||
ToastType m_toastType = ToastType::Information;
|
||||
|
||||
QString m_title;
|
||||
QString m_description;
|
||||
QString m_customIconImage;
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds(5000);
|
||||
AZStd::chrono::milliseconds m_fadeDuration = AZStd::chrono::milliseconds(250);
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
};
|
||||
} // namespace AzQtComponents
|
||||
@@ -49,6 +49,11 @@ set(FILES
|
||||
Components/Titlebar.h
|
||||
Components/TitleBarOverdrawHandler.cpp
|
||||
Components/TitleBarOverdrawHandler.h
|
||||
Components/ToastNotification.cpp
|
||||
Components/ToastNotification.h
|
||||
Components/ToastNotificationConfiguration.h
|
||||
Components/ToastNotificationConfiguration.cpp
|
||||
Components/ToastNotification.ui
|
||||
Components/ToolButtonComboBox.cpp
|
||||
Components/ToolButtonComboBox.h
|
||||
Components/ToolButtonLineEdit.cpp
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ namespace AzToolsFramework
|
||||
AZ_Assert(m_loaderInterface != nullptr,
|
||||
"Couldn't get prefab loader interface, it's a requirement for PrefabEntityOwnership system to work");
|
||||
|
||||
m_rootInstance = AZStd::unique_ptr<Prefab::Instance>(m_prefabSystemComponent->CreatePrefab({}, {}, "NewLevel.prefab"));
|
||||
m_rootInstance = AZStd::unique_ptr<Prefab::Instance>(m_prefabSystemComponent->CreatePrefab({}, {}, "newLevel.prefab"));
|
||||
m_sliceOwnershipService.BusConnect(m_entityContextId);
|
||||
m_sliceOwnershipService.m_shouldAssertForLegacySlicesUsage = m_shouldAssertForLegacySlicesUsage;
|
||||
m_editorSliceOwnershipService.BusConnect();
|
||||
|
||||
@@ -244,6 +244,17 @@ namespace AzToolsFramework
|
||||
|
||||
const auto eventType = event->type();
|
||||
|
||||
if (eventType == QEvent::Type::MouseMove)
|
||||
{
|
||||
// clear override cursor when moving outside of the viewport
|
||||
const auto* mouseEvent = static_cast<const QMouseEvent*>(event);
|
||||
if (m_overrideCursor && !m_sourceWidget->geometry().contains(m_sourceWidget->mapFromGlobal(mouseEvent->globalPos())))
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
m_overrideCursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only accept mouse & key release events that originate from an object that is not our target widget,
|
||||
// as we don't want to erroneously intercept user input meant for another component.
|
||||
if (object != m_sourceWidget && eventType != QEvent::Type::KeyRelease && eventType != QEvent::Type::MouseButtonRelease)
|
||||
@@ -262,7 +273,7 @@ namespace AzToolsFramework
|
||||
if (eventType == QEvent::FocusIn)
|
||||
{
|
||||
const auto globalCursorPosition = QCursor::pos();
|
||||
if (m_sourceWidget->geometry().contains(globalCursorPosition))
|
||||
if (m_sourceWidget->geometry().contains(m_sourceWidget->mapFromGlobal(globalCursorPosition)))
|
||||
{
|
||||
HandleMouseMoveEvent(globalCursorPosition);
|
||||
}
|
||||
@@ -452,4 +463,32 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Qt::CursorShape QtCursorFromAzCursor(const ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
switch (cursorStyleOverride)
|
||||
{
|
||||
case ViewportInteraction::CursorStyleOverride::Forbidden:
|
||||
return Qt::ForbiddenCursor;
|
||||
default:
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
ClearOverrideCursor();
|
||||
|
||||
qApp->setOverrideCursor(QtCursorFromAzCursor(cursorStyleOverride));
|
||||
m_overrideCursor = true;
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::ClearOverrideCursor()
|
||||
{
|
||||
if (m_overrideCursor)
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
m_overrideCursor = false;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
#include <AzFramework/Input/Channels/InputChannelDeltaWithSharedPosition2D.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedModifierKeyStates.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedPosition2D.h>
|
||||
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
@@ -55,6 +56,9 @@ namespace AzToolsFramework
|
||||
//! like a dolly or rotation, where mouse movement is important but cursor location is not.
|
||||
void SetCursorCaptureEnabled(bool enabled);
|
||||
|
||||
void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride);
|
||||
void ClearOverrideCursor();
|
||||
|
||||
// QObject overrides...
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
@@ -164,6 +168,8 @@ namespace AzToolsFramework
|
||||
bool m_enabled = true;
|
||||
// Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement).
|
||||
bool m_capturingCursor = false;
|
||||
// Flags whether the cursor has been overridden.
|
||||
bool m_overrideCursor = false;
|
||||
|
||||
// Our viewport-specific AZ devices. We control their internal input channel states.
|
||||
AZStd::unique_ptr<EditorQtMouseDevice> m_mouseDevice;
|
||||
|
||||
@@ -28,7 +28,9 @@ namespace AzToolsFramework::Prefab
|
||||
"Instance Entity Mapper Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
EditorEntityInfoNotificationBus::Handler::BusConnect();
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
PrefabPublicNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Register(this);
|
||||
}
|
||||
@@ -37,10 +39,12 @@ namespace AzToolsFramework::Prefab
|
||||
{
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Unregister(this);
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
PrefabPublicNotificationBus::Handler::BusDisconnect();
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::Initialize()
|
||||
void PrefabFocusHandler::InitializeEditorInterfaces()
|
||||
{
|
||||
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
AZ_Assert(
|
||||
@@ -55,13 +59,6 @@ namespace AzToolsFramework::Prefab
|
||||
"Prefab - PrefabFocusHandler - "
|
||||
"Focus Mode Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
|
||||
AZ_Assert(
|
||||
m_instanceEntityMapperInterface,
|
||||
"Prefab - PrefabFocusHandler - "
|
||||
"Instance Entity Mapper Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
|
||||
@@ -90,12 +87,12 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
|
||||
{
|
||||
if (index < 0 || index >= m_instanceFocusVector.size())
|
||||
if (index < 0 || index >= m_instanceFocusHierarchy.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusHierarchy[index];
|
||||
|
||||
FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
|
||||
|
||||
@@ -134,42 +131,38 @@ namespace AzToolsFramework::Prefab
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on."));
|
||||
}
|
||||
|
||||
if (!m_isInitialized)
|
||||
// Close all container entities in the old path.
|
||||
CloseInstanceContainers(m_instanceFocusHierarchy);
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
AZ::EntityId containerEntityId;
|
||||
|
||||
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
Initialize();
|
||||
containerEntityId = focusedInstance->get().GetContainerEntityId();
|
||||
}
|
||||
|
||||
if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get())
|
||||
else
|
||||
{
|
||||
// Close all container entities in the old path
|
||||
CloseInstanceContainers(m_instanceFocusVector);
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
AZ::EntityId containerEntityId;
|
||||
|
||||
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
containerEntityId = focusedInstance->get().GetContainerEntityId();
|
||||
}
|
||||
else
|
||||
{
|
||||
containerEntityId = AZ::EntityId();
|
||||
}
|
||||
containerEntityId = AZ::EntityId();
|
||||
}
|
||||
|
||||
// Focus on the descendants of the container entity
|
||||
// Focus on the descendants of the container entity in the Editor, if the interface is initialized.
|
||||
if (m_focusModeInterface)
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(containerEntityId);
|
||||
|
||||
// Refresh path variables
|
||||
RefreshInstanceFocusList();
|
||||
|
||||
// Open all container entities in the new path
|
||||
OpenInstanceContainers(m_instanceFocusVector);
|
||||
|
||||
PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged);
|
||||
}
|
||||
|
||||
// Refresh path variables.
|
||||
RefreshInstanceFocusList();
|
||||
RefreshInstanceFocusPath();
|
||||
|
||||
// Open all container entities in the new path.
|
||||
OpenInstanceContainers(m_instanceFocusHierarchy);
|
||||
|
||||
PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged);
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
@@ -220,49 +213,80 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
const int PrefabFocusHandler::GetPrefabFocusPathLength([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return aznumeric_cast<int>(m_instanceFocusVector.size());
|
||||
return aznumeric_cast<int>(m_instanceFocusHierarchy.size());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OnEntityStreamLoadSuccess()
|
||||
void PrefabFocusHandler::OnContextReset()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
// Clear the old focus vector
|
||||
m_instanceFocusVector.clear();
|
||||
m_instanceFocusHierarchy.clear();
|
||||
|
||||
// Focus on the root prefab (AZ::EntityId() will default to it)
|
||||
FocusOnPrefabInstanceOwningEntityId(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OnEntityInfoUpdatedName(AZ::EntityId entityId, [[maybe_unused]]const AZStd::string& name)
|
||||
{
|
||||
// Determine if the entityId is the container for any of the instances in the vector
|
||||
auto result = AZStd::find_if(
|
||||
m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(),
|
||||
[entityId](const InstanceOptionalReference& instance)
|
||||
{
|
||||
return (instance->get().GetContainerEntityId() == entityId);
|
||||
}
|
||||
);
|
||||
|
||||
if (result != m_instanceFocusHierarchy.end())
|
||||
{
|
||||
// Refresh the path and notify changes.
|
||||
RefreshInstanceFocusPath();
|
||||
PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OnPrefabInstancePropagationEnd()
|
||||
{
|
||||
// Refresh the path and notify changes in case propagation updated any container names.
|
||||
RefreshInstanceFocusPath();
|
||||
PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged);
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::RefreshInstanceFocusList()
|
||||
{
|
||||
m_instanceFocusVector.clear();
|
||||
m_instanceFocusPath.clear();
|
||||
m_instanceFocusHierarchy.clear();
|
||||
|
||||
AZStd::list<InstanceOptionalReference> instanceFocusList;
|
||||
|
||||
// Use a support list to easily push front while traversing the prefab hierarchy
|
||||
InstanceOptionalReference currentInstance = m_focusedInstance;
|
||||
while (currentInstance.has_value())
|
||||
{
|
||||
instanceFocusList.push_front(currentInstance);
|
||||
m_instanceFocusHierarchy.emplace_back(currentInstance);
|
||||
|
||||
currentInstance = currentInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
// Populate internals using the support list
|
||||
for (auto& instance : instanceFocusList)
|
||||
// Invert the vector, since we need the top instance to be at index 0
|
||||
AZStd::reverse(m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::RefreshInstanceFocusPath()
|
||||
{
|
||||
m_instanceFocusPath.clear();
|
||||
|
||||
for (const InstanceOptionalReference& instance : m_instanceFocusHierarchy)
|
||||
{
|
||||
m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName());
|
||||
m_instanceFocusVector.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
|
||||
{
|
||||
// If this is called outside the Editor, this interface won't be initialized.
|
||||
if (!m_containerEntityInterface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const InstanceOptionalReference& instance : instances)
|
||||
{
|
||||
if (instance.has_value())
|
||||
@@ -274,6 +298,12 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
|
||||
{
|
||||
// If this is called outside the Editor, this interface won't be initialized.
|
||||
if (!m_containerEntityInterface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const InstanceOptionalReference& instance : instances)
|
||||
{
|
||||
if (instance.has_value())
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -30,7 +32,9 @@ namespace AzToolsFramework::Prefab
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
, private PrefabFocusPublicInterface
|
||||
, private PrefabPublicNotificationBus::Handler
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
, private EditorEntityInfoNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0);
|
||||
@@ -38,9 +42,8 @@ namespace AzToolsFramework::Prefab
|
||||
PrefabFocusHandler();
|
||||
~PrefabFocusHandler();
|
||||
|
||||
void Initialize();
|
||||
|
||||
// PrefabFocusInterface overrides ...
|
||||
void InitializeEditorInterfaces() override;
|
||||
PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
|
||||
@@ -54,25 +57,34 @@ namespace AzToolsFramework::Prefab
|
||||
const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
// EditorEntityContextNotificationBus overrides ...
|
||||
void OnEntityStreamLoadSuccess() override;
|
||||
void OnContextReset() override;
|
||||
|
||||
// EditorEntityInfoNotificationBus overrides ...
|
||||
void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& name) override;
|
||||
|
||||
// PrefabPublicNotifications overrides ...
|
||||
void OnPrefabInstancePropagationEnd();
|
||||
|
||||
private:
|
||||
PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance);
|
||||
void RefreshInstanceFocusList();
|
||||
void RefreshInstanceFocusPath();
|
||||
|
||||
void OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
|
||||
void CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
|
||||
|
||||
//! The instance the editor is currently focusing on.
|
||||
InstanceOptionalReference m_focusedInstance;
|
||||
//! The templateId of the focused instance.
|
||||
TemplateId m_focusedTemplateId;
|
||||
AZStd::vector<InstanceOptionalReference> m_instanceFocusVector;
|
||||
//! The list of instances going from the root (index 0) to the focused instance.
|
||||
AZStd::vector<InstanceOptionalReference> m_instanceFocusHierarchy;
|
||||
//! A path containing the names of the containers in the instance focus hierarchy, separated with a /.
|
||||
AZ::IO::Path m_instanceFocusPath;
|
||||
|
||||
ContainerEntityInterface* m_containerEntityInterface = nullptr;
|
||||
FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
|
||||
bool m_isInitialized = false;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -26,6 +26,11 @@ namespace AzToolsFramework::Prefab
|
||||
public:
|
||||
AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}");
|
||||
|
||||
//! Initializes the editor interfaces for Prefab Focus mode.
|
||||
//! If this is not called on initialization, the Prefab Focus Mode functions will still work
|
||||
//! but won't trigger the Editor APIs to visualize focus mode on the UI.
|
||||
virtual void InitializeEditorInterfaces() = 0;
|
||||
|
||||
//! Set the focused prefab instance to the owning instance of the entityId provided.
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0;
|
||||
|
||||
@@ -66,7 +66,16 @@ namespace AzToolsFramework
|
||||
|
||||
bool Template::IsValid() const
|
||||
{
|
||||
return !m_prefabDom.IsNull() && !m_filePath.empty();
|
||||
if (m_prefabDom.IsNull() || m_filePath.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!m_prefabDom.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto source = m_prefabDom.FindMember(PrefabDomUtils::SourceName);
|
||||
return (source != m_prefabDom.MemberEnd());
|
||||
}
|
||||
|
||||
bool Template::IsLoadedWithErrors() const
|
||||
@@ -175,6 +184,26 @@ namespace AzToolsFramework
|
||||
return findInstancesResult->get();
|
||||
}
|
||||
|
||||
bool Template::IsProcedural() const
|
||||
{
|
||||
if (m_isProcedural.has_value())
|
||||
{
|
||||
return m_isProcedural.value();
|
||||
}
|
||||
else if (!IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto source = m_prefabDom.FindMember(PrefabDomUtils::SourceName);
|
||||
if (!source->value.IsString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AZ::IO::PathView path(source->value.GetString());
|
||||
m_isProcedural = AZStd::make_optional(path.Extension().Match(".procprefab"));
|
||||
return m_isProcedural.value();
|
||||
}
|
||||
|
||||
const AZ::IO::Path& Template::GetFilePath() const
|
||||
{
|
||||
return m_filePath;
|
||||
|
||||
@@ -65,6 +65,9 @@ namespace AzToolsFramework
|
||||
const AZ::IO::Path& GetFilePath() const;
|
||||
void SetFilePath(const AZ::IO::PathView& path);
|
||||
|
||||
// To tell if this Template was created from an product asset
|
||||
bool IsProcedural() const;
|
||||
|
||||
private:
|
||||
// Container for keeping links representing the Template's nested instances.
|
||||
Links m_links;
|
||||
@@ -80,6 +83,9 @@ namespace AzToolsFramework
|
||||
|
||||
// Flag to tell if this Template has changes that have yet to be saved to file.
|
||||
bool m_isDirty = false;
|
||||
|
||||
// Flag to tell if this Template was generated outside the Editor
|
||||
mutable AZStd::optional<bool> m_isProcedural;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+18
-5
@@ -20,6 +20,8 @@
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
#include <AzToolsFramework/SourceControl/PerforceConnection.h>
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace
|
||||
@@ -75,9 +77,17 @@ namespace AzToolsFramework
|
||||
m_resolveKey = true;
|
||||
m_testTrust = false;
|
||||
|
||||
|
||||
// set up signals before we start thread.
|
||||
m_shutdownThreadSignal = false;
|
||||
m_WorkerThread = AZStd::thread(AZStd::bind(&PerforceComponent::ThreadWorker, this));
|
||||
|
||||
// Check to see if the 'p4' command is available at the command line
|
||||
int p4VersionExitCode = QProcess::execute("p4", QStringList{ "-V" });
|
||||
m_p4ApplicationDetected = (p4VersionExitCode == 0);
|
||||
if (m_p4ApplicationDetected)
|
||||
{
|
||||
m_WorkerThread = AZStd::thread(AZStd::bind(&PerforceComponent::ThreadWorker, this));
|
||||
}
|
||||
|
||||
SourceControlConnectionRequestBus::Handler::BusConnect();
|
||||
SourceControlCommandBus::Handler::BusConnect();
|
||||
@@ -88,10 +98,13 @@ namespace AzToolsFramework
|
||||
SourceControlCommandBus::Handler::BusDisconnect();
|
||||
SourceControlConnectionRequestBus::Handler::BusDisconnect();
|
||||
|
||||
m_shutdownThreadSignal = true; // tell the thread to die.
|
||||
m_WorkerSemaphore.release(1); // wake up the thread so that it sees the signal
|
||||
m_WorkerThread.join(); // wait for the thread to finish.
|
||||
m_WorkerThread = AZStd::thread();
|
||||
if (m_p4ApplicationDetected)
|
||||
{
|
||||
m_shutdownThreadSignal = true; // tell the thread to die.
|
||||
m_WorkerSemaphore.release(1); // wake up the thread so that it sees the signal
|
||||
m_WorkerThread.join(); // wait for the thread to finish.
|
||||
m_WorkerThread = AZStd::thread();
|
||||
}
|
||||
|
||||
SetConnection(nullptr);
|
||||
}
|
||||
|
||||
@@ -260,5 +260,7 @@ namespace AzToolsFramework
|
||||
AZStd::atomic_bool m_validConnection;
|
||||
|
||||
SourceControlState m_connectionState;
|
||||
|
||||
bool m_p4ApplicationDetected { false };
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzQtComponents/Components/ToastNotificationConfiguration.h>
|
||||
|
||||
#include <QPoint>
|
||||
#endif
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
typedef AZ::EntityId ToastId;
|
||||
|
||||
/**
|
||||
* An EBus for receiving notifications when a user interacts with or dismisses
|
||||
* a toast notification.
|
||||
*/
|
||||
class ToastNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = ToastId;
|
||||
|
||||
virtual void OnToastInteraction() {}
|
||||
virtual void OnToastDismissed() {}
|
||||
};
|
||||
|
||||
using ToastNotificationBus = AZ::EBus<ToastNotifications>;
|
||||
|
||||
typedef AZ::u32 ToastRequestBusId;
|
||||
|
||||
/**
|
||||
* An EBus used to hide or show toast notifications. Generally, these request are handled by a
|
||||
* ToastNotificationsView that has been created with a specific ToastRequestBusId
|
||||
* e.g. AZ_CRC("ExampleToastNotificationView")
|
||||
*/
|
||||
class ToastRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = ToastRequestBusId; // bus is addressed by CRC of the view name
|
||||
|
||||
/**
|
||||
* Hide a toast notification widget.
|
||||
*
|
||||
* @param toastId The toast notification's ToastId
|
||||
*/
|
||||
virtual void HideToastNotification(const ToastId& toastId) = 0;
|
||||
|
||||
/**
|
||||
* Show a toast notification with the specified toast configuration. When handled by a ToastNotificationsView,
|
||||
* notifications are queued and presented to the user in sequence.
|
||||
*
|
||||
* @param toastConfiguration The toast configuration
|
||||
* @return a ToastId
|
||||
*/
|
||||
virtual ToastId ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) = 0;
|
||||
|
||||
/**
|
||||
* Show a toast notification with the specified toast configuration at the current moust cursor location.
|
||||
*
|
||||
* @param toastConfiguration The toast configuration
|
||||
* @return a ToastId
|
||||
*/
|
||||
virtual ToastId ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) = 0;
|
||||
|
||||
/**
|
||||
* Show a toast notification with the specified toast configuration at the specified location.
|
||||
*
|
||||
* @param screenPosition The screen position
|
||||
* @param anchorPoint The anchorPoint for the toast notification widget
|
||||
* @param toastConfiguration The toast configuration
|
||||
* @return a ToastId
|
||||
*/
|
||||
virtual ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration&) = 0;
|
||||
};
|
||||
|
||||
using ToastRequestBus = AZ::EBus<ToastRequests>;
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/UI/Notifications/ToastNotificationsView.h>
|
||||
#include <AzQtComponents/Components/ToastNotification.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
ToastNotificationsView::ToastNotificationsView(QWidget* parent, ToastRequestBusId busId)
|
||||
: QWidget(parent)
|
||||
{
|
||||
ToastRequestBus::Handler::BusConnect(busId);
|
||||
}
|
||||
|
||||
ToastNotificationsView::~ToastNotificationsView()
|
||||
{
|
||||
ToastRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void ToastNotificationsView::OnHide()
|
||||
{
|
||||
QWidget::hide();
|
||||
|
||||
if (m_activeNotification.IsValid())
|
||||
{
|
||||
auto notificationIter = m_notifications.find(m_activeNotification);
|
||||
if (notificationIter != m_notifications.end())
|
||||
{
|
||||
notificationIter->second->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToastNotificationsView::UpdateToastPosition()
|
||||
{
|
||||
if (m_activeNotification.IsValid())
|
||||
{
|
||||
auto notificationIter = m_notifications.find(m_activeNotification);
|
||||
if (notificationIter != m_notifications.end())
|
||||
{
|
||||
notificationIter->second->UpdatePosition(GetGlobalPoint(), m_anchorPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToastNotificationsView::OnShow()
|
||||
{
|
||||
QWidget::show();
|
||||
|
||||
if (m_activeNotification.IsValid() || !m_queuedNotifications.empty())
|
||||
{
|
||||
DisplayQueuedNotification();
|
||||
}
|
||||
}
|
||||
|
||||
ToastId ToastNotificationsView::ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration)
|
||||
{
|
||||
ToastId toastId = CreateToastNotification(toastConfiguration);
|
||||
m_queuedNotifications.emplace_back(toastId);
|
||||
|
||||
if (!m_activeNotification.IsValid())
|
||||
{
|
||||
DisplayQueuedNotification();
|
||||
}
|
||||
|
||||
return toastId;
|
||||
}
|
||||
|
||||
ToastId ToastNotificationsView::ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration)
|
||||
{
|
||||
ToastId toastId = CreateToastNotification(toastConfiguration);
|
||||
m_notifications[toastId]->ShowToastAtCursor();
|
||||
return toastId;
|
||||
}
|
||||
|
||||
ToastId ToastNotificationsView::ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration& toastConfiguration)
|
||||
{
|
||||
ToastId toastId = CreateToastNotification(toastConfiguration);
|
||||
m_notifications[toastId]->ShowToastAtPoint(screenPosition, anchorPoint);
|
||||
return toastId;
|
||||
}
|
||||
|
||||
void ToastNotificationsView::HideToastNotification(const ToastId& toastId)
|
||||
{
|
||||
auto notificationIter = m_notifications.find(toastId);
|
||||
if (notificationIter != m_notifications.end())
|
||||
{
|
||||
auto queuedIter = AZStd::find(m_queuedNotifications.begin(), m_queuedNotifications.end(), toastId);
|
||||
if (queuedIter != m_queuedNotifications.end())
|
||||
{
|
||||
m_queuedNotifications.erase(queuedIter);
|
||||
}
|
||||
|
||||
notificationIter->second->reject();
|
||||
}
|
||||
}
|
||||
|
||||
ToastId ToastNotificationsView::CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration)
|
||||
{
|
||||
AzQtComponents::ToastNotification* notification = aznew AzQtComponents::ToastNotification(parentWidget(), toastConfiguration);
|
||||
ToastId toastId = AZ::Entity::MakeId();
|
||||
m_notifications[toastId] = notification;
|
||||
|
||||
QObject::connect(
|
||||
m_notifications[toastId], &AzQtComponents::ToastNotification::ToastNotificationHidden,
|
||||
[toastId]()
|
||||
{
|
||||
ToastNotificationBus::Event(toastId, &ToastNotificationBus::Events::OnToastDismissed);
|
||||
});
|
||||
|
||||
QObject::connect(
|
||||
m_notifications[toastId], &AzQtComponents::ToastNotification::ToastNotificationInteraction,
|
||||
[toastId]()
|
||||
{
|
||||
ToastNotificationBus::Event(toastId, &ToastNotificationBus::Events::OnToastInteraction);
|
||||
});
|
||||
|
||||
return toastId;
|
||||
}
|
||||
|
||||
QPoint ToastNotificationsView::GetGlobalPoint()
|
||||
{
|
||||
QPoint relativePoint = m_offset;
|
||||
|
||||
AZ_Assert(parentWidget(), "ToastNotificationsView has invalid parent QWidget");
|
||||
if (m_anchorPoint.x() == 1.0)
|
||||
{
|
||||
relativePoint.setX(parentWidget()->width() - m_offset.x());
|
||||
}
|
||||
if (m_anchorPoint.y() == 1.0)
|
||||
{
|
||||
relativePoint.setY(parentWidget()->height() - m_offset.y());
|
||||
}
|
||||
|
||||
return parentWidget()->mapToGlobal(relativePoint);
|
||||
}
|
||||
|
||||
void ToastNotificationsView::DisplayQueuedNotification()
|
||||
{
|
||||
AZ_Assert(parentWidget(), "ToastNotificationsView has invalid parent QWidget");
|
||||
if (m_queuedNotifications.empty() || !parentWidget()->isVisible() || !isVisible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ToastId toastId = m_queuedNotifications.front();
|
||||
m_queuedNotifications.erase(m_queuedNotifications.begin());
|
||||
|
||||
auto notificationIter = m_notifications.find(toastId);
|
||||
if (notificationIter != m_notifications.end())
|
||||
{
|
||||
m_activeNotification = toastId;
|
||||
|
||||
notificationIter->second->ShowToastAtPoint(GetGlobalPoint(), m_anchorPoint);
|
||||
|
||||
QObject::connect(
|
||||
notificationIter->second, &AzQtComponents::ToastNotification::ToastNotificationHidden,
|
||||
[&]()
|
||||
{
|
||||
m_activeNotification.SetInvalid();
|
||||
DisplayQueuedNotification();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// If we didn't actually show something, recurse to avoid things getting stuck in the queue.
|
||||
if (!m_activeNotification.IsValid())
|
||||
{
|
||||
DisplayQueuedNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QWidget>
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzToolsFramework/UI/Notifications/ToastBus.h>
|
||||
#endif
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
class ToastNotification;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
/**
|
||||
* \brief A QWidget that displays and manages a queue of toast notifications.
|
||||
*
|
||||
* This view must be updated by its parent when the parent widget is show, hidden, moved
|
||||
* or resized because toast notifications are displayed on top of the parent and are not part
|
||||
* of the layout, so they must be manually moved.
|
||||
*/
|
||||
class ToastNotificationsView final
|
||||
: public QWidget
|
||||
, protected ToastRequestBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ToastNotificationsView(QWidget* parent, ToastRequestBusId busId);
|
||||
~ToastNotificationsView() override;
|
||||
|
||||
void HideToastNotification(const ToastId& toastId) override;
|
||||
|
||||
ToastId ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) override;
|
||||
ToastId ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) override;
|
||||
ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const AzQtComponents::ToastConfiguration&) override;
|
||||
|
||||
void OnHide();
|
||||
void OnShow();
|
||||
void UpdateToastPosition();
|
||||
|
||||
private:
|
||||
ToastId CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration);
|
||||
void DisplayQueuedNotification();
|
||||
QPoint GetGlobalPoint();
|
||||
|
||||
ToastId m_activeNotification;
|
||||
AZStd::unordered_map<ToastId, AzQtComponents::ToastNotification*> m_notifications;
|
||||
AZStd::vector<ToastId> m_queuedNotifications;
|
||||
|
||||
QPoint m_offset = QPoint(10, 10);
|
||||
QPointF m_anchorPoint = QPointF(1, 0);
|
||||
};
|
||||
} // AzToolsFramework
|
||||
+9
-15
@@ -27,6 +27,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||
@@ -135,6 +136,10 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize Editor functionality for the Prefab Focus Handler
|
||||
auto prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
prefabFocusInterface->InitializeEditorInterfaces();
|
||||
|
||||
EditorContextMenuBus::Handler::BusConnect();
|
||||
EditorEventsBus::Handler::BusConnect();
|
||||
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
|
||||
@@ -250,7 +255,7 @@ namespace AzToolsFramework
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
// Edit Prefab
|
||||
if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
|
||||
if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
|
||||
{
|
||||
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
|
||||
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
|
||||
@@ -1154,25 +1159,14 @@ namespace AzToolsFramework
|
||||
{
|
||||
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
|
||||
|
||||
bool prefabWipFeaturesEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
|
||||
|
||||
if (prefabWipFeaturesEnabled)
|
||||
{
|
||||
// Register entity as a container
|
||||
s_containerEntityInterface->RegisterEntityAsContainer(entityId);
|
||||
}
|
||||
// Register entity as a container
|
||||
s_containerEntityInterface->RegisterEntityAsContainer(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::OnPrefabComponentDeactivate(AZ::EntityId entityId)
|
||||
{
|
||||
bool prefabWipFeaturesEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
|
||||
|
||||
if (prefabWipFeaturesEnabled && !s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
|
||||
if (!s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
|
||||
{
|
||||
// Unregister entity as a container
|
||||
s_containerEntityInterface->UnregisterEntityAsContainer(entityId);
|
||||
|
||||
@@ -316,14 +316,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabUiHandler::OnDoubleClick(AZ::EntityId entityId) const
|
||||
{
|
||||
bool prefabWipFeaturesEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
|
||||
|
||||
if (prefabWipFeaturesEnabled)
|
||||
{
|
||||
// Focus on this prefab
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
|
||||
}
|
||||
// Focus on this prefab
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,8 @@ namespace AzToolsFramework
|
||||
virtual float ManipulatorCircleBoundWidth() const = 0;
|
||||
//! Returns if sticky select is enabled or not.
|
||||
virtual bool StickySelectEnabled() const = 0;
|
||||
//! Returns the default viewport camera position.
|
||||
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
|
||||
|
||||
protected:
|
||||
~ViewportSettingsRequests() = default;
|
||||
@@ -300,6 +302,12 @@ namespace AzToolsFramework
|
||||
|
||||
using EditorViewportInputTimeNowRequestBus = AZ::EBus<EditorViewportInputTimeNowRequests>;
|
||||
|
||||
//! The style of cursor override.
|
||||
enum class CursorStyleOverride
|
||||
{
|
||||
Forbidden
|
||||
};
|
||||
|
||||
//! Viewport requests for managing the viewport cursor state.
|
||||
class ViewportMouseCursorRequests
|
||||
{
|
||||
@@ -310,6 +318,10 @@ namespace AzToolsFramework
|
||||
virtual void EndCursorCapture() = 0;
|
||||
//! Is the mouse over the viewport.
|
||||
virtual bool IsMouseOver() const = 0;
|
||||
//! Set the cursor style override.
|
||||
virtual void SetOverrideCursor(CursorStyleOverride cursorStyleOverride) = 0;
|
||||
//! Clear the cursor style override.
|
||||
virtual void ClearOverrideCursor() = 0;
|
||||
|
||||
protected:
|
||||
~ViewportMouseCursorRequests() = default;
|
||||
|
||||
@@ -44,6 +44,13 @@ AZ_CVAR(
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Display the aggregate world bounds for a given entity (the union of all world component Aabbs)");
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_useCursorLockIconInFocusMode,
|
||||
false,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Use a lock icon when the cursor is over entities that cannot be interacted with");
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -222,6 +229,13 @@ namespace AzToolsFramework
|
||||
// verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
|
||||
if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor))
|
||||
{
|
||||
if (ed_useCursorLockIconInFocusMode)
|
||||
{
|
||||
ViewportInteraction::ViewportMouseCursorRequestBus::Event(
|
||||
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor,
|
||||
ViewportInteraction::CursorStyleOverride::Forbidden);
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down ||
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick)
|
||||
@@ -232,6 +246,9 @@ namespace AzToolsFramework
|
||||
return CursorEntityIdQuery(AZ::EntityId(), AZ::EntityId());
|
||||
}
|
||||
|
||||
ViewportInteraction::ViewportMouseCursorRequestBus::Event(
|
||||
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::ClearOverrideCursor);
|
||||
|
||||
// container entity support - if the entity that is being selected is part of a closed container,
|
||||
// change the selection to the container instead.
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
|
||||
@@ -759,6 +759,9 @@ set(FILES
|
||||
UI/Prefab/PrefabUiHandler.cpp
|
||||
UI/Prefab/PrefabViewportFocusPathHandler.h
|
||||
UI/Prefab/PrefabViewportFocusPathHandler.cpp
|
||||
UI/Notifications/ToastNotificationsView.cpp
|
||||
UI/Notifications/ToastNotificationsView.h
|
||||
UI/Notifications/ToastBus.h
|
||||
PythonTerminal/ScriptHelpDialog.cpp
|
||||
PythonTerminal/ScriptHelpDialog.h
|
||||
PythonTerminal/ScriptHelpDialog.ui
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <Prefab/PrefabTestFixture.h>
|
||||
#include <Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
@@ -132,4 +133,32 @@ namespace UnitTest
|
||||
EXPECT_TRUE(outputValue.HasMember("member"));
|
||||
EXPECT_STREQ(outputValue.FindMember("member")->value.GetString(), "value");
|
||||
}
|
||||
|
||||
TEST_F(ProceduralPrefabAssetTest, Template_IsProcPrefab_DefaultsToNotProcPrefab)
|
||||
{
|
||||
AzToolsFramework::Prefab::PrefabDom dom;
|
||||
dom.SetObject();
|
||||
dom.AddMember("Source", "foo.prefab", dom.GetAllocator());
|
||||
AzToolsFramework::Prefab::Template fooTemplate("foo", AZStd::move(dom));
|
||||
EXPECT_FALSE(fooTemplate.IsProcedural());
|
||||
}
|
||||
|
||||
TEST_F(ProceduralPrefabAssetTest, Template_IsProcPrefab_DomDrivesFlagToTrue)
|
||||
{
|
||||
AzToolsFramework::Prefab::PrefabDom dom;
|
||||
dom.SetObject();
|
||||
dom.AddMember("Source", "foo.procprefab", dom.GetAllocator());
|
||||
AzToolsFramework::Prefab::Template fooTemplate("foo", AZStd::move(dom));
|
||||
EXPECT_TRUE(fooTemplate.IsProcedural());
|
||||
// the second time should use the cached version of the flag
|
||||
EXPECT_TRUE(fooTemplate.IsProcedural());
|
||||
}
|
||||
|
||||
TEST_F(ProceduralPrefabAssetTest, Template_IsProcPrefab_FailsWithNoSource)
|
||||
{
|
||||
AzToolsFramework::Prefab::PrefabDom dom;
|
||||
dom.SetObject();
|
||||
AzToolsFramework::Prefab::Template fooTemplate("foo", AZStd::move(dom));
|
||||
EXPECT_FALSE(fooTemplate.IsProcedural());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,7 @@ namespace UnitTest
|
||||
// Update our undo cache entry to include the rename / reparent as one atomic operation.
|
||||
m_prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(entityId, m_undoStack->GetTop());
|
||||
|
||||
// Force a prefab propagation as updates are deferred to the next tick.
|
||||
m_prefabSystemComponent->OnSystemTick();
|
||||
ProcessDeferredUpdates();
|
||||
|
||||
return entityId;
|
||||
}
|
||||
@@ -125,6 +124,30 @@ namespace UnitTest
|
||||
return m_model->index(0, 0);
|
||||
}
|
||||
|
||||
// Kicks off any updates scheduled for the next tick
|
||||
void ProcessDeferredUpdates()
|
||||
{
|
||||
// Force a prefab propagation for updates that are deferred to the next tick.
|
||||
m_prefabSystemComponent->OnSystemTick();
|
||||
|
||||
// Ensure the model process its entity update queue
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
// Performs an undo operation and ensures the tick-scheduled updates happen
|
||||
void Undo()
|
||||
{
|
||||
m_undoStack->Undo();
|
||||
ProcessDeferredUpdates();
|
||||
}
|
||||
|
||||
// Performs a redo operation and ensures the tick-scheduled updates happen
|
||||
void Redo()
|
||||
{
|
||||
m_undoStack->Redo();
|
||||
ProcessDeferredUpdates();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::EntityOutlinerListModel> m_model;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTester;
|
||||
AzToolsFramework::UndoSystem::UndoStack* m_undoStack = nullptr;
|
||||
@@ -139,21 +162,18 @@ namespace UnitTest
|
||||
CreateNamedEntity(AZStd::string::format("Entity%zu", i));
|
||||
EXPECT_EQ(m_model->rowCount(GetRootIndex()), i + 1);
|
||||
}
|
||||
m_model->ProcessEntityUpdates();
|
||||
|
||||
for (int i = entityCount; i > 0; --i)
|
||||
{
|
||||
m_undoStack->Undo();
|
||||
Undo();
|
||||
EXPECT_EQ(m_model->rowCount(GetRootIndex()), i - 1);
|
||||
}
|
||||
m_model->ProcessEntityUpdates();
|
||||
|
||||
for (size_t i = 0; i < entityCount; ++i)
|
||||
{
|
||||
m_undoStack->Redo();
|
||||
Redo();
|
||||
EXPECT_EQ(m_model->rowCount(GetRootIndex()), i + 1);
|
||||
}
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
TEST_F(EntityOutlinerTest, TestCreateNestedHierarchyUndoAndRedoWorks)
|
||||
@@ -177,21 +197,18 @@ namespace UnitTest
|
||||
{
|
||||
parentId = CreateNamedEntity(AZStd::string::format("EntityDepth%i", i), parentId);
|
||||
EXPECT_EQ(modelDepth(), i + 1);
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
for (int i = depth - 1; i >= 0; --i)
|
||||
{
|
||||
m_undoStack->Undo();
|
||||
Undo();
|
||||
EXPECT_EQ(modelDepth(), i);
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
m_undoStack->Redo();
|
||||
Redo();
|
||||
EXPECT_EQ(modelDepth(), i + 1);
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -86,7 +86,7 @@ bool CLevelInfo::ReadInfo()
|
||||
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
|
||||
|
||||
// Set up a default game type for legacy code.
|
||||
m_defaultGameTypeName = "Mission0";
|
||||
m_defaultGameTypeName = "mission0";
|
||||
|
||||
if (usePrefabSystemForLevels)
|
||||
{
|
||||
@@ -96,17 +96,17 @@ bool CLevelInfo::ReadInfo()
|
||||
|
||||
AZStd::string levelPath(m_levelPath);
|
||||
AZStd::string xmlFile(levelPath);
|
||||
xmlFile += "/LevelInfo.xml";
|
||||
xmlFile += "/levelinfo.xml";
|
||||
XmlNodeRef rootNode = GetISystem()->LoadXmlFromFile(xmlFile.c_str());
|
||||
|
||||
if (rootNode)
|
||||
{
|
||||
AZStd::string dataFile(levelPath);
|
||||
dataFile += "/LevelDataAction.xml";
|
||||
dataFile += "/leveldataaction.xml";
|
||||
XmlNodeRef dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str());
|
||||
if (!dataNode)
|
||||
{
|
||||
dataFile = levelPath + "/LevelData.xml";
|
||||
dataFile = levelPath + "/leveldata.xml";
|
||||
dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str());
|
||||
}
|
||||
|
||||
@@ -614,7 +614,7 @@ ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName)
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::string missionXml("Mission_");
|
||||
AZStd::string missionXml("mission_");
|
||||
missionXml += pLevelInfo->m_defaultGameTypeName;
|
||||
missionXml += ".xml";
|
||||
AZStd::string xmlFile(pLevelInfo->GetPath());
|
||||
|
||||
@@ -3573,19 +3573,61 @@ namespace AssetProcessor
|
||||
QString knownPathBeforeWildcard = encodedFileData.left(slashBeforeWildcardIndex + 1); // include the slash
|
||||
QString relativeSearch = encodedFileData.mid(slashBeforeWildcardIndex + 1); // skip the slash
|
||||
|
||||
for (int i = 0; i < m_platformConfig->GetScanFolderCount(); ++i)
|
||||
// Absolute path, just check the 1 scan folder
|
||||
if (AZ::IO::PathView(encodedFileData.toUtf8().constData()).IsAbsolute())
|
||||
{
|
||||
const ScanFolderInfo* scanFolderInfo = &m_platformConfig->GetScanFolderAt(i);
|
||||
|
||||
if (!scanFolderInfo->RecurseSubFolders() && encodedFileData.contains("/"))
|
||||
QString scanFolderName;
|
||||
if (!m_platformConfig->ConvertToRelativePath(encodedFileData, resultDatabaseSourceName, scanFolderName))
|
||||
{
|
||||
continue;
|
||||
AZ_Warning(
|
||||
AssetProcessor::ConsoleChannel, false,
|
||||
"'%s' does not appear to be in any input folder. Use relative paths instead.",
|
||||
sourceDependency.m_sourceFileDependencyPath.c_str());
|
||||
}
|
||||
|
||||
QDir rooted(scanFolderInfo->ScanPath());
|
||||
QString absolutePath = rooted.absoluteFilePath(knownPathBeforeWildcard);
|
||||
auto scanFolderInfo = m_platformConfig->GetScanFolderByPath(scanFolderName);
|
||||
|
||||
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(absolutePath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
|
||||
// Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
|
||||
QDir rooted(scanFolderName);
|
||||
QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
|
||||
|
||||
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
|
||||
scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
|
||||
}
|
||||
else // Relative path, check every scan folder
|
||||
{
|
||||
for (int i = 0; i < m_platformConfig->GetScanFolderCount(); ++i)
|
||||
{
|
||||
const ScanFolderInfo* scanFolderInfo = &m_platformConfig->GetScanFolderAt(i);
|
||||
|
||||
if (!scanFolderInfo->RecurseSubFolders() && encodedFileData.contains("/"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QDir rooted(scanFolderInfo->ScanPath());
|
||||
QString absolutePath = rooted.absoluteFilePath(knownPathBeforeWildcard);
|
||||
|
||||
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
|
||||
absolutePath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to relative paths
|
||||
for (auto dependencyItr = resolvedDependencyList.begin(); dependencyItr != resolvedDependencyList.end();)
|
||||
{
|
||||
QString relativePath, scanFolder;
|
||||
if (m_platformConfig->ConvertToRelativePath(*dependencyItr, relativePath, scanFolder))
|
||||
{
|
||||
*dependencyItr = relativePath;
|
||||
++dependencyItr;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("AssetProcessor", false, "Failed to get relative path for wildcard dependency file %s. Is the file within a scan folder?",
|
||||
dependencyItr->toUtf8().constData());
|
||||
dependencyItr = resolvedDependencyList.erase(dependencyItr);
|
||||
}
|
||||
}
|
||||
|
||||
resultDatabaseSourceName = encodedFileData.replace('\\', '/');
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include <utilities/BatchApplicationManager.h>
|
||||
#include <utilities/ApplicationServer.h>
|
||||
#include <AzFramework/Asset/AssetSystemComponent.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <connection/connectionManager.h>
|
||||
#include <QCoreApplication>
|
||||
#include <QTemporaryDir>
|
||||
@@ -98,10 +100,26 @@ namespace AssetProcessorMessagesTests
|
||||
|
||||
int argC = 0;
|
||||
m_batchApplicationManager = AZStd::make_unique<UnitTestBatchApplicationManager>(&argC, nullptr, nullptr);
|
||||
m_batchApplicationManager->BeforeRun();
|
||||
|
||||
// Override Game Name to be "AutomatedTesting"
|
||||
AssetUtilities::ComputeProjectName("AutomatedTesting", true);
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
EXPECT_NE(registry, nullptr);
|
||||
constexpr AZ::SettingsRegistryInterface::FixedValueString bootstrapKey{
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey
|
||||
};
|
||||
constexpr AZ::SettingsRegistryInterface::FixedValueString projectPathKey{ bootstrapKey + "/project_path" };
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
// Force the branch token into settings registry before starting the application manager.
|
||||
// This avoids writing the asset_processor.setreg file which can cause fileIO errors.
|
||||
const AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath();
|
||||
constexpr AZ::SettingsRegistryInterface::FixedValueString branchTokenKey{ bootstrapKey + "/assetProcessor_branch_token" };
|
||||
AZStd::string token;
|
||||
AZ::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token);
|
||||
registry->Set(branchTokenKey, token.c_str());
|
||||
|
||||
auto status = m_batchApplicationManager->BeforeRun();
|
||||
ASSERT_EQ(status, ApplicationManager::BeforeRunStatus::Status_Success);
|
||||
|
||||
m_batchApplicationManager->m_platformConfiguration = new PlatformConfiguration();
|
||||
m_batchApplicationManager->InitAssetProcessorManager();
|
||||
@@ -159,21 +177,25 @@ namespace AssetProcessorMessagesTests
|
||||
|
||||
ASSERT_TRUE(result);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
QEventLoop eventLoop;
|
||||
if (m_batchApplicationManager->m_connectionManager)
|
||||
{
|
||||
QEventLoop eventLoop;
|
||||
|
||||
QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit);
|
||||
QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit);
|
||||
|
||||
m_batchApplicationManager->m_connectionManager->QuitRequested();
|
||||
m_batchApplicationManager->m_connectionManager->QuitRequested();
|
||||
|
||||
eventLoop.exec();
|
||||
eventLoop.exec();
|
||||
}
|
||||
|
||||
m_assetSystemComponent->Deactivate();
|
||||
if (m_assetSystemComponent)
|
||||
{
|
||||
m_assetSystemComponent->Deactivate();
|
||||
}
|
||||
m_batchApplicationManager->Destroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -505,6 +505,14 @@ bool ApplicationManager::StartAZFramework()
|
||||
AzFramework::Application::Descriptor appDescriptor;
|
||||
AZ::ComponentApplication::StartupParameters params;
|
||||
|
||||
QDir projectPath{ AssetUtilities::ComputeProjectPath() };
|
||||
if (!projectPath.exists("project.json"))
|
||||
{
|
||||
AZStd::string errorMsg = AZStd::string::format("Path '%s' is not a valid project path.", projectPath.path().toUtf8().constData());
|
||||
AssetProcessor::MessageInfoBus::Broadcast(&AssetProcessor::MessageInfoBus::Events::OnErrorMessage, errorMsg.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
QString projectName = AssetUtilities::ComputeProjectName();
|
||||
|
||||
// Prevent loading of gems in the Create method of the ComponentApplication
|
||||
@@ -520,7 +528,6 @@ bool ApplicationManager::StartAZFramework()
|
||||
//Registering all the Components
|
||||
m_frameworkApp.RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
|
||||
|
||||
|
||||
Reflect();
|
||||
|
||||
const AzFramework::CommandLine* commandLine = nullptr;
|
||||
|
||||
@@ -95,6 +95,8 @@ GUIApplicationManager::~GUIApplicationManager()
|
||||
|
||||
ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun()
|
||||
{
|
||||
AssetProcessor::MessageInfoBus::Handler::BusConnect();
|
||||
|
||||
ApplicationManager::BeforeRunStatus status = ApplicationManagerBase::BeforeRun();
|
||||
if (status != ApplicationManager::BeforeRunStatus::Status_Success)
|
||||
{
|
||||
@@ -109,7 +111,6 @@ ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun()
|
||||
#if defined(EXTERNAL_CRASH_REPORTING)
|
||||
CrashHandler::ToolsCrashHandler::InitCrashHandler("AssetProcessor", projectAssetRoot.absolutePath().toStdString());
|
||||
#endif
|
||||
AssetProcessor::MessageInfoBus::Handler::BusConnect();
|
||||
|
||||
// we have to monitor both the cache folder and the database file and restart AP if either of them gets deleted
|
||||
// It is important to note that we are monitoring the parent folder and not the actual cache folder itself since
|
||||
@@ -436,98 +437,7 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
|
||||
connection = Qt::QueuedConnection;
|
||||
}
|
||||
|
||||
if (m_isCurrentlyLoadingGems)
|
||||
{
|
||||
// if something goes wrong during gem initialization, this is a special case and we need to be extra helpful.
|
||||
const char* userSettingsFile = "_WAF_/user_settings.options";
|
||||
const char* defaultSettingsFile = "_WAF_/default_settings.json";
|
||||
|
||||
QDir engineRoot;
|
||||
AssetUtilities::ComputeEngineRoot(engineRoot);
|
||||
|
||||
QString settingsPath = engineRoot.absoluteFilePath(userSettingsFile);
|
||||
QString friendlyErrorMessage;
|
||||
bool usingDefaults = false;
|
||||
|
||||
if (QFile::exists(settingsPath))
|
||||
{
|
||||
QSettings loader(settingsPath, QSettings::IniFormat);
|
||||
QVariant settingValue = loader.value("Game Projects/enabled_game_projects");
|
||||
QStringList compiledProjects = settingValue.toStringList();
|
||||
|
||||
if (compiledProjects.isEmpty())
|
||||
{
|
||||
QByteArray byteArray;
|
||||
QFile jsonFile;
|
||||
jsonFile.setFileName(engineRoot.absoluteFilePath(defaultSettingsFile));
|
||||
jsonFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
byteArray = jsonFile.readAll();
|
||||
jsonFile.close();
|
||||
|
||||
QJsonObject settingsObject = QJsonDocument::fromJson(byteArray).object();
|
||||
QJsonArray projectsArray = settingsObject["Game Projects"].toArray();
|
||||
|
||||
if (!projectsArray.isEmpty())
|
||||
{
|
||||
auto projectObject = projectsArray[0].toObject();
|
||||
QString projects = projectObject["default_value"].toString();
|
||||
|
||||
if (!projects.isEmpty())
|
||||
{
|
||||
compiledProjects = projects.split(',');
|
||||
usingDefaults = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < compiledProjects.size(); ++i)
|
||||
{
|
||||
compiledProjects[i] = compiledProjects[i].trimmed();
|
||||
}
|
||||
|
||||
QString enabledProject = AssetUtilities::ComputeProjectName();
|
||||
|
||||
if (!compiledProjects.contains(enabledProject))
|
||||
{
|
||||
QString projectSourceLine;
|
||||
|
||||
if (usingDefaults)
|
||||
{
|
||||
projectSourceLine = QString("The currently compiled projects according to the defaults in %1 are '%2'").arg(defaultSettingsFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
projectSourceLine = QString("The currently compiled projects according to %1 are '%2'").arg(userSettingsFile);
|
||||
}
|
||||
|
||||
projectSourceLine = projectSourceLine.arg(compiledProjects.join(", "));
|
||||
friendlyErrorMessage = QString("An error occurred while loading gems.\n"
|
||||
"The enabled game project is not in the list of compiled projects.\n"
|
||||
"Please configure the enabled project to be compiled and rebuild or change the enabled project.\n"
|
||||
"The currently enabled game project (from bootstrap.cfg or /%4 command-line parameter) is '%1'.\n"
|
||||
"%2\n"
|
||||
"Full error text:\n"
|
||||
"%3"
|
||||
).arg(enabledProject).arg(projectSourceLine).arg(message).arg(AssetUtilities::ProjectPathOverrideParameter);
|
||||
}
|
||||
}
|
||||
|
||||
if (friendlyErrorMessage.isEmpty())
|
||||
{
|
||||
friendlyErrorMessage = QString("An error occurred while loading gems.\n"
|
||||
"This can happen when new gems are added to a project, but those gems need to be built in order to function.\n"
|
||||
"This can also happen when switching to a different project, one which uses gems which are not yet built.\n"
|
||||
"To continue, please build the current project before attempting to run Asset Processor again.\n\n"
|
||||
"Full error text:\n"
|
||||
"%1").arg(message);
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, friendlyErrorMessage), Q_ARG(bool, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, QString(message)), Q_ARG(bool, true));
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, QString(message)), Q_ARG(bool, true));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1435,32 +1435,35 @@ namespace AssetProcessor
|
||||
return QString();
|
||||
}
|
||||
|
||||
QStringList PlatformConfiguration::FindWildcardMatches(const QString& sourceFolder, QString relativeName, bool includeFolders, bool recursiveSearch) const
|
||||
QStringList PlatformConfiguration::FindWildcardMatches(
|
||||
const QString& sourceFolder, QString relativeName, bool includeFolders, bool recursiveSearch) const
|
||||
{
|
||||
if (relativeName.isEmpty())
|
||||
{
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
const int pathLen = sourceFolder.length() + 1;
|
||||
QDir sourceFolderDir(sourceFolder);
|
||||
|
||||
relativeName.replace('\\', '/');
|
||||
QString posixRelativeName = QDir::fromNativeSeparators(relativeName);
|
||||
|
||||
QStringList returnList;
|
||||
QRegExp nameMatch{ relativeName, Qt::CaseInsensitive, QRegExp::Wildcard };
|
||||
QDirIterator diretoryIterator(sourceFolder, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, recursiveSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
|
||||
QRegExp nameMatch{ posixRelativeName, Qt::CaseInsensitive, QRegExp::Wildcard };
|
||||
QDirIterator dirIterator(
|
||||
sourceFolderDir.path(), QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot,
|
||||
recursiveSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
|
||||
QStringList files;
|
||||
while (diretoryIterator.hasNext())
|
||||
while (dirIterator.hasNext())
|
||||
{
|
||||
diretoryIterator.next();
|
||||
if (!includeFolders && !diretoryIterator.fileInfo().isFile())
|
||||
dirIterator.next();
|
||||
if (!includeFolders && !dirIterator.fileInfo().isFile())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QString pathMatch{ diretoryIterator.filePath().mid(pathLen) };
|
||||
QString pathMatch{ sourceFolderDir.relativeFilePath(dirIterator.filePath()) };
|
||||
if (nameMatch.exactMatch(pathMatch))
|
||||
{
|
||||
returnList.append(AssetUtilities::NormalizeFilePath(diretoryIterator.filePath()));
|
||||
returnList.append(QDir::fromNativeSeparators(dirIterator.filePath()));
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
|
||||
Reference in New Issue
Block a user