Merge remote-tracking branch 'origin/development' into component-doc-links
This commit is contained in:
@@ -140,9 +140,6 @@
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -201,6 +198,11 @@
|
||||
<header>AzToolsFramework/AssetBrowser/Search/SearchWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AzQtComponents::TableView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>AzQtComponents/Components/Widgets/TableView.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AzToolsFramework::AssetBrowser::AssetBrowserTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
@@ -214,7 +216,7 @@
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AzToolsFramework::AssetBrowser::AssetBrowserTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<extends>AzQtComponents::TableView</extends>
|
||||
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
|
||||
@@ -254,4 +254,35 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_googletest(
|
||||
NAME Legacy::EditorLib.Tests
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Legacy
|
||||
FILES_CMAKE
|
||||
Lib/Tests/Camera/editor_lib_camera_test_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
.
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzTest
|
||||
AZ::AzToolsFramework
|
||||
AZ::AzTestShared
|
||||
Legacy::EditorLib
|
||||
Gem::Camera.Editor
|
||||
Gem::AtomToolsFramework.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::EditorLib
|
||||
)
|
||||
|
||||
ly_add_source_properties(
|
||||
SOURCES Lib/Tests/Camera/test_EditorCamera.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES CAMERA_EDITOR_MODULE="$<TARGET_FILE_BASE_NAME:Camera.Editor>"
|
||||
)
|
||||
|
||||
ly_add_googletest(
|
||||
NAME Legacy::EditorLib.Camera.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -8,11 +8,21 @@
|
||||
|
||||
#include "QtEditorApplication.h"
|
||||
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
#include <AzFramework/API/ApplicationAPI_Linux.h>
|
||||
#endif
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
bool EditorQtApplication::nativeEventFilter(const QByteArray& , void* , long* )
|
||||
bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*)
|
||||
{
|
||||
// TODO_KDAB_LINUX
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
AzFramework::LinuxXcbEventHandlerBus::Broadcast(&AzFramework::LinuxXcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <EditorModularViewportCameraComposer.h>
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
@@ -34,10 +35,12 @@ namespace SandboxEditor
|
||||
: m_viewportId(viewportId)
|
||||
{
|
||||
EditorModularViewportCameraComposerNotificationBus::Handler::BusConnect(viewportId);
|
||||
Camera::EditorCameraNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EditorModularViewportCameraComposer::~EditorModularViewportCameraComposer()
|
||||
{
|
||||
Camera::EditorCameraNotificationBus::Handler::BusDisconnect();
|
||||
EditorModularViewportCameraComposerNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -283,4 +286,22 @@ namespace SandboxEditor
|
||||
m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
|
||||
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
|
||||
{
|
||||
if (viewEntityId.IsValid())
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame,
|
||||
worldFromLocal);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
}
|
||||
}
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -10,13 +10,16 @@
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzFramework/Viewport/CameraInput.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <EditorModularViewportCameraComposerBus.h>
|
||||
#include <SandboxAPI.h>
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
//! Type responsible for building the editor's modular viewport camera controller.
|
||||
class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
class EditorModularViewportCameraComposer
|
||||
: private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
, private Camera::EditorCameraNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId);
|
||||
@@ -32,6 +35,9 @@ namespace SandboxEditor
|
||||
// EditorModularViewportCameraComposerNotificationBus overrides ...
|
||||
void OnEditorModularViewportCameraComposerSettingsChanged() override;
|
||||
|
||||
// EditorCameraNotificationBus overrides ...
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
|
||||
|
||||
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
|
||||
AZStd::shared_ptr<AzFramework::PanCameraInput> m_firstPersonPanCamera;
|
||||
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
|
||||
|
||||
@@ -34,10 +34,14 @@ namespace EditorInternal
|
||||
: ToolsApplication(argc, argv)
|
||||
{
|
||||
EditorToolsApplicationRequests::Bus::Handler::BusConnect();
|
||||
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
|
||||
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EditorToolsApplication::~EditorToolsApplication()
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
|
||||
EditorToolsApplicationRequests::Bus::Handler::BusDisconnect();
|
||||
Stop();
|
||||
}
|
||||
@@ -48,7 +52,6 @@ namespace EditorInternal
|
||||
return m_StartupAborted;
|
||||
}
|
||||
|
||||
|
||||
void EditorToolsApplication::RegisterCoreComponents()
|
||||
{
|
||||
AzToolsFramework::ToolsApplication::RegisterCoreComponents();
|
||||
@@ -274,5 +277,14 @@ namespace EditorInternal
|
||||
Exit();
|
||||
}
|
||||
|
||||
}
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorToolsApplication::QueryKeyboardModifiers()
|
||||
{
|
||||
return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers());
|
||||
}
|
||||
|
||||
AZStd::chrono::milliseconds EditorToolsApplication::EditorViewportInputTimeNow()
|
||||
{
|
||||
const auto now = AZStd::chrono::high_resolution_clock::now();
|
||||
return AZStd::chrono::time_point_cast<AZStd::chrono::milliseconds>(now).time_since_epoch();
|
||||
}
|
||||
} // namespace EditorInternal
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include "Core/EditorMetricsPlainTextNameRegistration.h"
|
||||
#include "EditorToolsApplicationAPI.h"
|
||||
@@ -19,6 +21,8 @@ namespace EditorInternal
|
||||
class EditorToolsApplication
|
||||
: public AzToolsFramework::ToolsApplication
|
||||
, public EditorToolsApplicationRequests::Bus::Handler
|
||||
, public AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler
|
||||
, public AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
EditorToolsApplication(int* argc, char*** argv);
|
||||
@@ -44,6 +48,12 @@ namespace EditorInternal
|
||||
void CreateReflectionManager() override;
|
||||
void Reflect(AZ::ReflectContext* context) override;
|
||||
|
||||
// EditorModifierKeyRequestBus overrides ...
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override;
|
||||
|
||||
// EditorViewportInputTimeNowRequestBus overrides ...
|
||||
AZStd::chrono::milliseconds EditorViewportInputTimeNow() override;
|
||||
|
||||
protected:
|
||||
// From EditorToolsApplicationRequests
|
||||
bool OpenLevel(AZStd::string_view levelName) override;
|
||||
|
||||
@@ -744,11 +744,15 @@ void EditorViewportWidget::RenderAll()
|
||||
{
|
||||
namespace AztfVi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
AztfVi::KeyboardModifiers keyboardModifiers;
|
||||
AztfVi::EditorModifierKeyRequestBus::BroadcastResult(
|
||||
keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers);
|
||||
|
||||
m_debugDisplay->DepthTestOff();
|
||||
m_manipulatorManager->DrawManipulators(
|
||||
*m_debugDisplay, GetCameraState(),
|
||||
BuildMouseInteractionInternal(
|
||||
AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), QueryKeyboardModifiers(),
|
||||
AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), keyboardModifiers,
|
||||
BuildMousePick(WidgetToViewport(mapFromGlobal(QCursor::pos())))));
|
||||
m_debugDisplay->DepthTestOn();
|
||||
}
|
||||
@@ -959,12 +963,13 @@ QWidget* EditorViewportWidget::GetWidgetForViewportContextMenu()
|
||||
|
||||
bool EditorViewportWidget::ShowingWorldSpace()
|
||||
{
|
||||
return QueryKeyboardModifiers().Shift();
|
||||
}
|
||||
namespace AztfVi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorViewportWidget::QueryKeyboardModifiers()
|
||||
{
|
||||
return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers());
|
||||
AztfVi::KeyboardModifiers keyboardModifiers;
|
||||
AztfVi::EditorModifierKeyRequestBus::BroadcastResult(
|
||||
keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers);
|
||||
|
||||
return keyboardModifiers.Shift();
|
||||
}
|
||||
|
||||
void EditorViewportWidget::SetViewportId(int id)
|
||||
@@ -1039,7 +1044,6 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus()
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId());
|
||||
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId());
|
||||
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
|
||||
m_viewportUi.ConnectViewportUiBus(GetViewportId());
|
||||
|
||||
AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect();
|
||||
@@ -1050,7 +1054,6 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus()
|
||||
AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect();
|
||||
|
||||
m_viewportUi.DisconnectViewportUiBus();
|
||||
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ class SANDBOX_API EditorViewportWidget final
|
||||
, private AzFramework::InputSystemCursorConstraintRequestBus::Handler
|
||||
, private AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler
|
||||
, private AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler
|
||||
, private AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler
|
||||
, private AzFramework::AssetCatalogEventBus::Handler
|
||||
, private AZ::RPI::SceneNotificationBus::Handler
|
||||
{
|
||||
@@ -212,9 +211,6 @@ private:
|
||||
// EditorEntityViewportInteractionRequestBus overrides ...
|
||||
void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) override;
|
||||
|
||||
// EditorModifierKeyRequestBus overrides ...
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override;
|
||||
|
||||
// Camera::EditorCameraRequestBus overrides ...
|
||||
void SetViewFromEntityPerspective(const AZ::EntityId& entityId) override;
|
||||
void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
test_EditorCamera.cpp
|
||||
)
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 <AZTestShared/Math/MathTestHelpers.h>
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/ViewportControllerList.h>
|
||||
#include <AzTest/GemTestEnvironment.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <EditorModularViewportCameraComposer.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment
|
||||
{
|
||||
// AZ::Test::GemTestEnvironment overrides ...
|
||||
void AddGemsAndComponents() override;
|
||||
};
|
||||
|
||||
void EditorCameraTestEnvironment::AddGemsAndComponents()
|
||||
{
|
||||
AddDynamicModulePaths({ CAMERA_EDITOR_MODULE });
|
||||
AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() });
|
||||
}
|
||||
|
||||
class EditorCameraFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr;
|
||||
AZStd::unique_ptr<SandboxEditor::EditorModularViewportCameraComposer> m_editorModularViewportCameraComposer;
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> m_editorLibHandle;
|
||||
AzFramework::ViewportControllerListPtr m_controllerList;
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
|
||||
static const AzFramework::ViewportId TestViewportId;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib");
|
||||
[[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true);
|
||||
AZ_Assert(loaded, "EditorLib could not be loaded");
|
||||
|
||||
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
|
||||
m_controllerList->RegisterViewportContext(TestViewportId);
|
||||
|
||||
m_entity = AZStd::make_unique<AZ::Entity>();
|
||||
m_entity->Init();
|
||||
m_entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
m_entity->Activate();
|
||||
|
||||
m_editorModularViewportCameraComposer = AZStd::make_unique<SandboxEditor::EditorModularViewportCameraComposer>(TestViewportId);
|
||||
|
||||
auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController();
|
||||
// set some overrides for the test
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext) mutable
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::PlaceholderModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
m_controllerList->Add(controller);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_editorModularViewportCameraComposer.reset();
|
||||
m_cameraViewportContextView = nullptr;
|
||||
m_entity.reset();
|
||||
m_editorLibHandle = {};
|
||||
}
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337);
|
||||
|
||||
TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged)
|
||||
{
|
||||
// Given
|
||||
const auto entityTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 5.0f, -2.0f));
|
||||
AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, entityTransform);
|
||||
|
||||
// When
|
||||
// imitate viewport entity changing
|
||||
Camera::EditorCameraNotificationBus::Broadcast(
|
||||
&Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId());
|
||||
|
||||
// ensure the viewport updates after the viewport view entity change
|
||||
const float deltaTime = 1.0f / 60.0f;
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// retrieve updated camera transform
|
||||
const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
// camera transform matches that of the entity
|
||||
EXPECT_THAT(cameraTransform, IsClose(entityTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet)
|
||||
{
|
||||
// Given
|
||||
m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)));
|
||||
|
||||
// When
|
||||
AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
// reference frame is still the identity
|
||||
EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f));
|
||||
m_cameraViewportContextView->SetCameraTransform(nextTransform);
|
||||
|
||||
// When
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransform)
|
||||
{
|
||||
// When
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
transformToInterpolateTo, 0.0f);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransformWithReferenceSpaceSet)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
transformToInterpolateTo, 0.0f);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
// required to support running integration tests with the Camera Gem
|
||||
AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv);
|
||||
AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() });
|
||||
int result = RUN_ALL_TESTS();
|
||||
return result;
|
||||
}
|
||||
|
||||
IMPLEMENT_TEST_EXECUTABLE_MAIN();
|
||||
@@ -58,28 +58,6 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext
|
||||
{
|
||||
public:
|
||||
AZ::Transform GetCameraTransform() const override
|
||||
{
|
||||
return m_cameraTransform;
|
||||
}
|
||||
|
||||
void SetCameraTransform(const AZ::Transform& transform) override
|
||||
{
|
||||
m_cameraTransform = transform;
|
||||
}
|
||||
|
||||
void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity();
|
||||
};
|
||||
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
@@ -146,7 +124,7 @@ namespace UnitTest
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext)
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<TestModularCameraViewportContextImpl>();
|
||||
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::PlaceholderModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -12,28 +12,5 @@ set_target_properties(Editor PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/gui_info.plist
|
||||
RESOURCE ${CMAKE_CURRENT_LIST_DIR}/Images.xcassets
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME EditorAppIcon
|
||||
ENTITLEMENT_FILE_PATH ${CMAKE_CURRENT_LIST_DIR}/EditorEntitlements.plist
|
||||
)
|
||||
|
||||
# We cannot use ly_add_target here because we're already including this file from inside ly_add_target
|
||||
# So we need to setup target, dependencies and install logic manually.
|
||||
add_executable(EditorDummy Platform/Mac/main_dummy.cpp)
|
||||
add_executable(AZ::EditorDummy ALIAS EditorDummy)
|
||||
|
||||
ly_target_link_libraries(EditorDummy
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzFramework)
|
||||
|
||||
ly_add_dependencies(Editor EditorDummy)
|
||||
|
||||
# Store the aliased target into a DIRECTORY property
|
||||
set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS AZ::EditorDummy)
|
||||
|
||||
# Store the directory path in a GLOBAL property so that it can be accessed
|
||||
# in the layout install logic. Skip if the directory has already been added
|
||||
get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
|
||||
if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
|
||||
set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
ly_install_add_install_path_setreg(Editor)
|
||||
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>EditorDummy</string>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.O3DE.Editor</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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/ComponentApplication.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry
|
||||
AZ::ComponentApplication::Descriptor desc;
|
||||
AZ::ComponentApplication application;
|
||||
application.Create(desc);
|
||||
|
||||
AZStd::vector<AZStd::string> envVars;
|
||||
|
||||
const char* homePath = std::getenv("HOME");
|
||||
envVars.push_back(AZStd::string::format("HOME=%s", homePath));
|
||||
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH");
|
||||
AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig);
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
dyldSearchPath.append(":");
|
||||
dyldSearchPath.append(projectModulePath.c_str());
|
||||
}
|
||||
|
||||
if (AZ::IO::FixedMaxPath installedBinariesFolder;
|
||||
settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath engineRootFolder;
|
||||
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
installedBinariesFolder = engineRootFolder / installedBinariesFolder;
|
||||
dyldSearchPath.append(":");
|
||||
dyldSearchPath.append(installedBinariesFolder.c_str());
|
||||
}
|
||||
}
|
||||
envVars.push_back(dyldSearchPath);
|
||||
}
|
||||
|
||||
AZStd::string commandArgs;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
commandArgs.append(argv[i]);
|
||||
commandArgs.append(" ");
|
||||
}
|
||||
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) };
|
||||
processPath /= "Editor";
|
||||
processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native());
|
||||
processLaunchInfo.m_commandlineParameters = commandArgs;
|
||||
processLaunchInfo.m_environmentVariables = &envVars;
|
||||
processLaunchInfo.m_showWindow = true;
|
||||
|
||||
AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
|
||||
application.Destroy();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -51,4 +51,5 @@ ly_add_target(
|
||||
AZ::AzCore
|
||||
AZ::AzToolsFramework
|
||||
AZ::AzQtComponents
|
||||
Legacy::EditorCore
|
||||
)
|
||||
|
||||
@@ -207,12 +207,6 @@ namespace AZ
|
||||
ActivateComponent(**it);
|
||||
}
|
||||
|
||||
// Cache the transform interface to the transform interface
|
||||
// Generally this pattern is not recommended unless for component event buses
|
||||
// As we have a guarantee (by design) that components can't change during active state)
|
||||
// Even though technically they can connect disconnect from the bus.
|
||||
m_transform = TransformBus::FindFirstHandler(m_id);
|
||||
|
||||
SetState(State::Active);
|
||||
|
||||
EBUS_EVENT_ID(m_id, EntityBus, OnEntityActivated, m_id);
|
||||
@@ -1320,6 +1314,19 @@ namespace AZ
|
||||
return *processSignature;
|
||||
}
|
||||
|
||||
AZ::TransformInterface* Entity::GetTransform() const
|
||||
{
|
||||
// Lazy evaluation of the cached entity transform.
|
||||
if(!m_transform)
|
||||
{
|
||||
// Generally this pattern is not recommended unless for component event buses
|
||||
// As we have a guarantee (by design) that components can't change during active state)
|
||||
// Even though technically they can connect disconnect from the bus.
|
||||
m_transform = TransformBus::FindFirstHandler(m_id);
|
||||
}
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// MakeId
|
||||
// Ids must be unique across a project at authoring time. Runtime doesn't matter
|
||||
|
||||
@@ -354,10 +354,9 @@ namespace AZ
|
||||
//! @return The Process Signature of the local machine.
|
||||
static AZ::u32 GetProcessSignature();
|
||||
|
||||
/// @cond EXCLUDE_DOCS
|
||||
//! @deprecated Use the TransformBus to communicate with the TransformInterface.
|
||||
inline TransformInterface* GetTransform() const { return m_transform; }
|
||||
/// @endcond
|
||||
//! Gets the TransformInterface for the entity.
|
||||
//! @return The TransformInterface for the entity.
|
||||
TransformInterface* GetTransform() const;
|
||||
|
||||
//! Sorts an entity's components based on the dependencies between components.
|
||||
//! If all dependencies are met, the required services can be activated
|
||||
@@ -406,7 +405,7 @@ namespace AZ
|
||||
//! A cached pointer to the transform interface.
|
||||
//! We recommend using AZ::TransformBus and caching locally instead of accessing
|
||||
//! the transform interface directly through this pointer.
|
||||
TransformInterface* m_transform;
|
||||
mutable TransformInterface* m_transform;
|
||||
|
||||
//! A user-friendly name for the entity. This makes error messages easier to read.
|
||||
AZStd::string m_name;
|
||||
|
||||
@@ -59,6 +59,20 @@ namespace AZStd
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
// interface for externally defined profiler systems
|
||||
class Profiler
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(Profiler, "{3E5D6329-72D1-41BA-9158-68A349D1A4D5}");
|
||||
|
||||
Profiler() = default;
|
||||
virtual ~Profiler() = default;
|
||||
|
||||
// support for the extra macro args (e.g. format strings) will come in a later PR
|
||||
virtual void BeginRegion(const Budget* budget, const char* eventName) = 0;
|
||||
virtual void EndRegion(const Budget* budget) = 0;
|
||||
};
|
||||
|
||||
class ProfileScope
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
template<typename... T>
|
||||
@@ -22,9 +24,11 @@ namespace AZ::Debug
|
||||
PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...);
|
||||
#endif
|
||||
budget->BeginProfileRegion();
|
||||
// TODO: injecting instrumentation for other profilers
|
||||
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
|
||||
// will be introduced in a future PR
|
||||
|
||||
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
|
||||
{
|
||||
profiler->BeginRegion(budget, eventName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -39,6 +43,10 @@ namespace AZ::Debug
|
||||
#if defined(USE_PIX)
|
||||
PIXEndEvent();
|
||||
#endif
|
||||
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
|
||||
{
|
||||
profiler->EndRegion(budget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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/IO/FileReader.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
FileReader::FileReader() = default;
|
||||
|
||||
FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
|
||||
{
|
||||
Open(fileIoBase, filePath);
|
||||
}
|
||||
|
||||
FileReader::~FileReader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
FileReader::FileReader(FileReader&& other)
|
||||
{
|
||||
AZStd::swap(m_file, other.m_file);
|
||||
AZStd::swap(m_fileIoBase, other.m_fileIoBase);
|
||||
}
|
||||
|
||||
FileReader& FileReader::operator=(FileReader&& other)
|
||||
{
|
||||
// Close the current file and take over other file
|
||||
Close();
|
||||
m_file = AZStd::move(other.m_file);
|
||||
m_fileIoBase = AZStd::move(other.m_fileIoBase);
|
||||
other.m_file = AZStd::monostate{};
|
||||
other.m_fileIoBase = {};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
|
||||
{
|
||||
// Close file if the FileReader has an instance open
|
||||
Close();
|
||||
|
||||
if (fileIoBase != nullptr)
|
||||
{
|
||||
AZ::IO::HandleType fileHandle;
|
||||
if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
|
||||
{
|
||||
m_file = fileHandle;
|
||||
m_fileIoBase = fileIoBase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
|
||||
{
|
||||
m_file = AZStd::move(file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileReader::IsOpen() const
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
return *fileHandle != AZ::IO::InvalidHandle;
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->IsOpen();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileReader::Close()
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr)
|
||||
{
|
||||
fileIo->Close(*fileHandle);
|
||||
}
|
||||
}
|
||||
|
||||
m_file = AZStd::monostate{};
|
||||
m_fileIoBase = {};
|
||||
}
|
||||
|
||||
auto FileReader::Length() const -> SizeType
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize))
|
||||
{
|
||||
return fileSize;
|
||||
}
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->Length();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
|
||||
{
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->Read(byteSize, buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto FileReader::Tell() const -> SizeType
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset))
|
||||
{
|
||||
return fileOffset;
|
||||
}
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->Tell();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FileReader::Seek(AZ::s64 offset, SeekType type)
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
return m_fileIoBase->Seek(*fileHandle, offset, type);
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
systemFile->Seek(offset, static_cast<AZ::IO::SystemFile::SeekMode>(type));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileReader::Eof() const
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
return m_fileIoBase->Eof(*fileHandle);
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->Eof();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native();
|
||||
if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity()))
|
||||
{
|
||||
pathStringRef.resize_no_construct(AZStd::char_traits<char>::length(pathStringRef.data()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
filePath = systemFile->Name();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
class FileIOBase;
|
||||
enum class SeekType : AZ::u32;
|
||||
|
||||
//! Structure which encapsulates delegates File Read operations
|
||||
//! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied
|
||||
//! to the FileSystemReader class
|
||||
//! the SettingsRegistry option to use FileIO
|
||||
class FileReader
|
||||
{
|
||||
using HandleType = AZ::u32;
|
||||
using FileHandleType = AZStd::variant<AZStd::monostate, AZ::IO::SystemFile, HandleType>;
|
||||
public:
|
||||
using SizeType = AZ::u64;
|
||||
|
||||
//! Creates FileReader instance in the default state with no file opend
|
||||
FileReader();
|
||||
~FileReader();
|
||||
|
||||
//! Creates a new FileReader instance and attempts to open the file at the supplied path
|
||||
//! Uses the FileIOBase instance if supplied
|
||||
//! @param fileIOBase pointer to fileIOBase instance
|
||||
//! @param null-terminated filePath to open
|
||||
FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
|
||||
|
||||
//! Takes ownership of the supplied FileReader handle
|
||||
FileReader(FileReader&& other);
|
||||
|
||||
//! Moves ownership of FileReader handle to this instance
|
||||
FileReader& operator=(FileReader&& other);
|
||||
|
||||
//! Opens a File using the FileIOBase instance if non-nullptr
|
||||
//! Otherwise fall back to use SystemFile
|
||||
//! @param fileIOBase pointer to fileIOBase instance
|
||||
//! @param null-terminated filePath to open
|
||||
//! @return true if the File is opened successfully
|
||||
bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
|
||||
|
||||
//! Returns true if a file is currently open
|
||||
//! @return true if the file is open
|
||||
bool IsOpen() const;
|
||||
|
||||
//! Closes the File
|
||||
void Close();
|
||||
|
||||
//! Retrieve the length of the OpenFile
|
||||
SizeType Length() const;
|
||||
|
||||
//! Attempts to read up to byte size bytes into the supplied buffer
|
||||
//! @param byteSize - Maximum number of bytes to read
|
||||
//! @param buffer - Buffer to read bytes into
|
||||
//! @returns the number of bytes read if the file is open, otherwise 0
|
||||
SizeType Read(SizeType byteSize, void* buffer);
|
||||
|
||||
//! Returns the current file offset
|
||||
//! @returns file offset if the file is open, otherwise 0
|
||||
SizeType Tell() const;
|
||||
|
||||
//! Seeks within the open file to the offset supplied
|
||||
//! @param offset File offset to seek to
|
||||
//! @param type parameter to indicate the reference point to start the seek from
|
||||
//! @returns true if the file is open and the seek succeeded
|
||||
bool Seek(AZ::s64 offset, SeekType type);
|
||||
|
||||
//! Returns true if the file is open and in the EOF state
|
||||
bool Eof() const;
|
||||
|
||||
//! Store the file path of the open file into the output file path parameter
|
||||
//! The filePath reference is left unmodified, if the path was not stored
|
||||
//! @return true if the filePath was stored
|
||||
bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const;
|
||||
|
||||
private:
|
||||
|
||||
FileHandleType m_file;
|
||||
AZ::IO::FileIOBase* m_fileIoBase{};
|
||||
};
|
||||
}
|
||||
@@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
|
||||
Platform::Seek(m_handle, this, offset, mode);
|
||||
}
|
||||
|
||||
SystemFile::SizeType SystemFile::Tell()
|
||||
SystemFile::SizeType SystemFile::Tell() const
|
||||
{
|
||||
return Platform::Tell(m_handle, this);
|
||||
}
|
||||
|
||||
bool SystemFile::Eof()
|
||||
bool SystemFile::Eof() const
|
||||
{
|
||||
return Platform::Eof(m_handle, this);
|
||||
}
|
||||
|
||||
@@ -72,9 +72,9 @@ namespace AZ
|
||||
/// Seek in current file.
|
||||
void Seek(SeekSizeType offset, SeekMode mode);
|
||||
/// Get the cursor position in the current file.
|
||||
SizeType Tell();
|
||||
SizeType Tell() const;
|
||||
/// Is the cursor at the end of the file?
|
||||
bool Eof();
|
||||
bool Eof() const;
|
||||
/// Get the time the file was last modified.
|
||||
AZ::u64 ModificationTime();
|
||||
/// Read data from a file synchronous. Return number of bytes actually read in the buffer.
|
||||
|
||||
@@ -353,6 +353,7 @@ namespace AZ
|
||||
Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)->
|
||||
Method("CreateUniformScale", &Transform::CreateUniformScale)->
|
||||
Method("CreateTranslation", &Transform::CreateTranslation)->
|
||||
Method("CreateLookAt", &Transform::CreateLookAt)->
|
||||
Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <cerrno>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/FileReader.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
#include <AzCore/NativeUI//NativeUIRequests.h>
|
||||
@@ -1116,118 +1117,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
//! Structure which encapsulates Commands to either the FileIOBase or SystemFile classes based on
|
||||
//! the SettingsRegistry option to use FileIO
|
||||
struct SettingsRegistryFileReader
|
||||
{
|
||||
using FileHandleType = AZStd::variant<AZStd::monostate, AZ::IO::SystemFile, AZ::IO::HandleType>;
|
||||
|
||||
SettingsRegistryFileReader() = default;
|
||||
SettingsRegistryFileReader(bool useFileIo, const char* filePath)
|
||||
{
|
||||
Open(useFileIo, filePath);
|
||||
}
|
||||
|
||||
~SettingsRegistryFileReader()
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
|
||||
{
|
||||
fileIo->Close(*fileHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Open(bool useFileIo, const char* filePath)
|
||||
{
|
||||
Close();
|
||||
if (AZ::IO::FileIOBase* fileIo = useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr)
|
||||
{
|
||||
AZ::IO::HandleType fileHandle;
|
||||
if (fileIo->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
|
||||
{
|
||||
m_file = fileHandle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
|
||||
{
|
||||
m_file = AZStd::move(file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOpen() const
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
return *fileHandle != AZ::IO::InvalidHandle;
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->IsOpen();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
|
||||
{
|
||||
fileIo->Close(*fileHandle);
|
||||
}
|
||||
}
|
||||
|
||||
m_file = AZStd::monostate{};
|
||||
}
|
||||
|
||||
u64 Length() const
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize))
|
||||
{
|
||||
return fileSize;
|
||||
}
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->Length();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AZ::IO::SizeType Read(AZ::IO::SizeType byteSize, void* buffer)
|
||||
{
|
||||
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
|
||||
{
|
||||
if (AZ::u64 bytesRead{}; AZ::IO::FileIOBase::GetInstance()->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
|
||||
{
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
|
||||
{
|
||||
return systemFile->Read(byteSize, buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileHandleType m_file;
|
||||
};
|
||||
|
||||
bool SettingsRegistryImpl::MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey,
|
||||
AZStd::vector<char>& scratchBuffer)
|
||||
{
|
||||
@@ -1236,7 +1125,7 @@ namespace AZ
|
||||
|
||||
Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-");
|
||||
|
||||
SettingsRegistryFileReader fileReader(m_useFileIo, path);
|
||||
FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path);
|
||||
if (!fileReader.IsOpen())
|
||||
{
|
||||
AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/FileReader.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/TextStreamWriters.h>
|
||||
@@ -388,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
const ConfigParserSettings& configParserSettings)
|
||||
{
|
||||
auto configPath = FindEngineRoot(registry) / filePath;
|
||||
IO::SystemFile configFile;
|
||||
if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
|
||||
IO::FileReader configFile;
|
||||
bool configFileOpened{};
|
||||
switch (configParserSettings.m_fileReaderClass)
|
||||
{
|
||||
case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile:
|
||||
{
|
||||
auto fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
configFileOpened = configFile.Open(fileIo, configPath.c_str());
|
||||
break;
|
||||
}
|
||||
case ConfigParserSettings::FileReaderClass::UseSystemFileOnly:
|
||||
{
|
||||
configFileOpened = configFile.Open(nullptr, configPath.c_str());
|
||||
break;
|
||||
}
|
||||
case ConfigParserSettings::FileReaderClass::UseFileIOOnly:
|
||||
{
|
||||
auto fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
if (fileIo == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
configFileOpened = configFile.Open(fileIo, configPath.c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied");
|
||||
return false;
|
||||
}
|
||||
if (!configFileOpened)
|
||||
{
|
||||
AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str());
|
||||
return false;
|
||||
@@ -480,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
AZ_Error("SettingsRegistryMergeUtils", false,
|
||||
R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n"
|
||||
R"(Parsing will halt. The line content so far is:)" "\n"
|
||||
R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(),
|
||||
R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(),
|
||||
aznumeric_cast<int>(configBuffer.size()), configBuffer.data());
|
||||
configFileParsed = false;
|
||||
break;
|
||||
|
||||
@@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
//! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function
|
||||
//! The structure contains a functor which returns true if a character is a valid delimiter
|
||||
SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings;
|
||||
|
||||
//! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile
|
||||
enum class FileReaderClass
|
||||
{
|
||||
UseFileIOIfAvailableFallbackToSystemFile,
|
||||
UseSystemFileOnly,
|
||||
UseFileIOOnly
|
||||
};
|
||||
FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile;
|
||||
};
|
||||
//! Loads basic configuration files which have structures similar to Windows INI files
|
||||
//! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html
|
||||
|
||||
@@ -166,6 +166,8 @@ set(FILES
|
||||
IO/FileIO.cpp
|
||||
IO/FileIO.h
|
||||
IO/FileIOEventBus.h
|
||||
IO/FileReader.cpp
|
||||
IO/FileReader.h
|
||||
IO/IOUtils.h
|
||||
IO/IOUtils.cpp
|
||||
IO/IStreamer.h
|
||||
|
||||
+18
@@ -72,6 +72,7 @@ namespace AZ
|
||||
{
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
bool fileFound = false;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
@@ -79,6 +80,23 @@ namespace AZ
|
||||
if (AZ::IO::SystemFile::Exists(projectModulePath.c_str()))
|
||||
{
|
||||
m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size());
|
||||
fileFound = true;
|
||||
}
|
||||
}
|
||||
if (!fileFound)
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath installedBinariesPath;
|
||||
settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath engineRootFolder;
|
||||
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
installedBinariesPath = engineRootFolder / installedBinariesPath / fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(installedBinariesPath.c_str()))
|
||||
{
|
||||
m_fileName.assign(installedBinariesPath.c_str(), installedBinariesPath.Native().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <pwd.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -39,6 +40,14 @@ namespace AZ
|
||||
AZ::IO::FixedMaxPath path{homePath};
|
||||
return path.Native();
|
||||
}
|
||||
|
||||
struct passwd* pass = getpwuid(getuid());
|
||||
if (pass)
|
||||
{
|
||||
AZ::IO::FixedMaxPath path{pass->pw_dir};
|
||||
return path.Native();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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/IO/FileReader.h>
|
||||
#include <FileIOBaseTestTypes.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
template <typename FileIOType>
|
||||
class FileReaderTestFixture
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<FileIOType, TestFileIOBase>)
|
||||
{
|
||||
m_fileIo = AZStd::make_unique<TestFileIOBase>();
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_fileIo.reset();
|
||||
}
|
||||
|
||||
protected:
|
||||
AZStd::unique_ptr<AZ::IO::FileIOBase> m_fileIo{};
|
||||
};
|
||||
|
||||
using FileIOTypes = ::testing::Types<void, TestFileIOBase>;
|
||||
|
||||
TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes);
|
||||
|
||||
TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully)
|
||||
{
|
||||
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
|
||||
EXPECT_TRUE(fileReader.IsOpen());
|
||||
}
|
||||
|
||||
TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully)
|
||||
{
|
||||
AZ::IO::FileReader fileReader;
|
||||
fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
|
||||
EXPECT_TRUE(fileReader.IsOpen());
|
||||
}
|
||||
|
||||
TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds)
|
||||
{
|
||||
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
|
||||
EXPECT_TRUE(fileReader.Eof());
|
||||
}
|
||||
|
||||
TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds)
|
||||
{
|
||||
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
|
||||
AZ::IO::FixedMaxPath filePath;
|
||||
EXPECT_TRUE(fileReader.GetFilePath(filePath));
|
||||
AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() };
|
||||
if (this->m_fileIo)
|
||||
{
|
||||
EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename));
|
||||
}
|
||||
EXPECT_EQ(nulFilename, filePath);
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -37,6 +37,7 @@ set(FILES
|
||||
FileIOBaseTestTypes.h
|
||||
Geometry2DUtils.cpp
|
||||
Interface.cpp
|
||||
IO/FileReaderTests.cpp
|
||||
IO/Path/PathTests.cpp
|
||||
IPC.cpp
|
||||
Jobs.cpp
|
||||
|
||||
@@ -774,7 +774,7 @@ namespace AZ::IO::ZipDir
|
||||
return ZD_ERROR_INVALID_CALL;
|
||||
}
|
||||
|
||||
if (pFileEntry->nFileDataOffset != pFileEntry->INVALID_DATA_OFFSET)
|
||||
if (pFileEntry->nFileDataOffset != FileEntryBase::INVALID_DATA_OFFSET)
|
||||
{
|
||||
return ZD_ERROR_SUCCESS; // the data offset has been successfully read..
|
||||
}
|
||||
|
||||
@@ -553,7 +553,7 @@ namespace AZ::IO::ZipDir
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// give the CDR File Header entry, reads the local file header to validate
|
||||
// and determine where the actual file lies
|
||||
// and determine where the actual file resides
|
||||
void CacheFactory::AddFileEntry(char* strFilePath, const ZipFile::CDRFileHeader* pFileHeader, const SExtraZipFileData& extra)
|
||||
{
|
||||
if (pFileHeader->lLocalHeaderOffset > m_CDREnd.lCDROffset)
|
||||
@@ -600,8 +600,7 @@ namespace AZ::IO::ZipDir
|
||||
if (m_encryptedHeaders != ZipFile::HEADERS_NOT_ENCRYPTED)
|
||||
{
|
||||
// use CDR instead of local header
|
||||
// The pak encryption tool asserts that there is no extra data at the end of the local file header, so don't add any extra data from the CDR header.
|
||||
fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength;
|
||||
fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength + pFileHeader->nExtraFieldLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -187,8 +187,7 @@ namespace AZ::IO::ZipDir::ZipDirStructuresInternal
|
||||
|
||||
// If src/dst overlap (in place decompress), then inflate in chunks, copying src locally to ensure
|
||||
// pointers don't foul each other.
|
||||
bool bIndependantBlocks = ((pInput + nInputLen) <= pOutput) || (pInput >= (pOutput + nOutputLen));
|
||||
if (bIndependantBlocks)
|
||||
if ((pInput + nInputLen) <= pOutput || pInput >= (pOutput + nOutputLen))
|
||||
{
|
||||
pZStream->next_in = (Bytef*)pInput;
|
||||
pZStream->avail_in = aznumeric_cast<uint32_t>(nInputLen);
|
||||
@@ -260,8 +259,7 @@ namespace AZ::IO::ZipDir::ZipDirStructuresInternal
|
||||
|
||||
// If src/dst overlap (in place decompress), then inflate in chunks, copying src locally to ensure
|
||||
// pointers don't foul each other.
|
||||
bool bIndependantBlocks = ((pIn + nIn) <= stream.next_out) || (pIn >= (stream.next_out + stream.avail_out));
|
||||
if (bIndependantBlocks)
|
||||
if ((pIn + nIn) <= stream.next_out || pIn >= (stream.next_out + stream.avail_out))
|
||||
{
|
||||
stream.next_in = pIn;
|
||||
stream.avail_in = nIn;
|
||||
@@ -498,18 +496,18 @@ namespace AZ::IO::ZipDir
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
FileEntryBase::FileEntryBase(const ZipFile::CDRFileHeader& header, const SExtraZipFileData& extra)
|
||||
{
|
||||
this->desc = header.desc;
|
||||
this->nFileHeaderOffset = header.lLocalHeaderOffset;
|
||||
//this->nFileDataOffset = INVALID_DATA_OFFSET; // we don't know yet
|
||||
this->nMethod = header.nMethod;
|
||||
this->nNameOffset = 0; // we don't know yet
|
||||
this->nLastModTime = header.nLastModTime;
|
||||
this->nLastModDate = header.nLastModDate;
|
||||
this->nNTFS_LastModifyTime = extra.nLastModifyTime;
|
||||
desc = header.desc;
|
||||
nFileHeaderOffset = header.lLocalHeaderOffset;
|
||||
|
||||
nMethod = header.nMethod;
|
||||
nNameOffset = 0; // we don't know yet
|
||||
nLastModTime = header.nLastModTime;
|
||||
nLastModDate = header.nLastModDate;
|
||||
nNTFS_LastModifyTime = extra.nLastModifyTime;
|
||||
|
||||
// make an estimation (at least this offset should be there), but we don't actually know yet
|
||||
this->nFileDataOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength;
|
||||
this->nEOFOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength + header.desc.lSizeCompressed;
|
||||
nFileDataOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength + header.nExtraFieldLength;
|
||||
nEOFOffset = nFileDataOffset + header.desc.lSizeCompressed;
|
||||
}
|
||||
|
||||
// Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
|
||||
@@ -817,8 +815,6 @@ namespace AZ::IO::ZipDir
|
||||
header.nFileNameLength = aznumeric_cast<uint16_t>(nFileNameLength);
|
||||
header.nExtraFieldLength = 0;
|
||||
|
||||
pFileEntry->nFileDataOffset = pFileEntry->nFileHeaderOffset + sizeof(header) + header.nFileNameLength;
|
||||
pFileEntry->nEOFOffset = pFileEntry->nFileDataOffset + pFileEntry->desc.lSizeCompressed;
|
||||
if (!AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, &header, sizeof(header)))
|
||||
{
|
||||
return ZD_ERROR_IO_FAILED;
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace AZ::IO::ZipDir
|
||||
|
||||
inline static constexpr uint32_t INVALID_DATA_OFFSET = 0xFFFFFFFF;
|
||||
ZipFile::DataDescriptor desc{};
|
||||
uint32_t nFileDataOffset{}; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet!
|
||||
uint32_t nFileDataOffset{ INVALID_DATA_OFFSET }; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet!
|
||||
uint32_t nFileHeaderOffset{ INVALID_DATA_OFFSET }; // offset of the local file header
|
||||
uint32_t nNameOffset{}; // offset of the file name in the name pool for the directory
|
||||
|
||||
|
||||
@@ -9,8 +9,19 @@
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
#include <AzCore/std/chrono/clocks.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
ClickDetector::ClickDetector()
|
||||
{
|
||||
m_timeNowFn = []
|
||||
{
|
||||
const auto now = AZStd::chrono::high_resolution_clock::now();
|
||||
return AZStd::chrono::time_point_cast<AZStd::chrono::milliseconds>(now).time_since_epoch();
|
||||
};
|
||||
}
|
||||
|
||||
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
|
||||
{
|
||||
const auto previousDetectionState = m_detectionState;
|
||||
@@ -26,11 +37,13 @@ namespace AzFramework
|
||||
|
||||
if (clickEvent == ClickEvent::Down)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto now = m_timeNowFn();
|
||||
if (m_tryBeginTime)
|
||||
{
|
||||
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
|
||||
if (diff.count() < m_doubleClickInterval)
|
||||
using FloatingPointSeconds = AZStd::chrono::duration<float, AZStd::chrono::seconds::period>;
|
||||
|
||||
const auto diff = now - m_tryBeginTime.value();
|
||||
if (FloatingPointSeconds(diff).count() < m_doubleClickInterval)
|
||||
{
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
@@ -43,7 +56,8 @@ namespace AzFramework
|
||||
}
|
||||
else if (clickEvent == ClickEvent::Up)
|
||||
{
|
||||
const auto clickOutcome = [detectionState = m_detectionState] {
|
||||
const auto clickOutcome = [detectionState = m_detectionState]
|
||||
{
|
||||
if (detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
return ClickOutcome::Click;
|
||||
@@ -66,4 +80,9 @@ namespace AzFramework
|
||||
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
|
||||
void ClickDetector::OverrideTimeNowFn(AZStd::function<AZStd::chrono::milliseconds()> timeNowFn)
|
||||
{
|
||||
m_timeNowFn = AZStd::move(timeNowFn);
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
#include <chrono>
|
||||
@@ -21,10 +22,9 @@ namespace AzFramework
|
||||
//! (mouse down with movement and then mouse up).
|
||||
class ClickDetector
|
||||
{
|
||||
//! Alias for recording time of mouse down events
|
||||
using Time = std::chrono::time_point<std::chrono::steady_clock>;
|
||||
|
||||
public:
|
||||
ClickDetector();
|
||||
|
||||
//! Internal representation of click event (map from external event for this when
|
||||
//! calling DetectClick).
|
||||
enum class ClickEvent
|
||||
@@ -51,6 +51,10 @@ namespace AzFramework
|
||||
void SetDoubleClickInterval(float doubleClickInterval);
|
||||
//! Override the dead zone before a 'move' outcome will be triggered.
|
||||
void SetDeadZone(float deadZone);
|
||||
//! Override how the current time is retrieved.
|
||||
//! This is helpful to override when it comes to simulating different passages of
|
||||
//! time to avoid double click issues in tests for example.
|
||||
void OverrideTimeNowFn(AZStd::function<AZStd::chrono::milliseconds()> timeNowFn);
|
||||
|
||||
private:
|
||||
//! Internal state of ClickDetector based on incoming events.
|
||||
@@ -65,7 +69,9 @@ namespace AzFramework
|
||||
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
|
||||
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
|
||||
DetectionState m_detectionState; //!< Internal state of ClickDetector.
|
||||
AZStd::optional<Time> m_tryBeginTime; //!< Mouse down time (happens each mouse down, helps with double click handling).
|
||||
//! Mouse down time (happens each mouse down, helps with double click handling).
|
||||
AZStd::optional<AZStd::chrono::milliseconds> m_tryBeginTime;
|
||||
AZStd::function<AZStd::chrono::milliseconds()> m_timeNowFn; //!< Interface to query the current time.
|
||||
};
|
||||
|
||||
inline void ClickDetector::SetDoubleClickInterval(const float doubleClickInterval)
|
||||
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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/std/typetraits/integral_constant.h>
|
||||
#include <AzFramework/API/ApplicationAPI_Linux.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
|
||||
#define explicit ExplicitIsACXXKeyword
|
||||
#include <xcb/xkb.h>
|
||||
#undef explicit
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
class InputDeviceKeyboardXcb
|
||||
: public InputDeviceKeyboard::Implementation
|
||||
, public LinuxXcbEventHandlerBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(InputDeviceKeyboardXcb, AZ::SystemAllocator, 0);
|
||||
|
||||
using InputDeviceKeyboard::Implementation::Implementation;
|
||||
InputDeviceKeyboardXcb(InputDeviceKeyboard& inputDevice)
|
||||
: InputDeviceKeyboard::Implementation(inputDevice)
|
||||
{
|
||||
LinuxXcbEventHandlerBus::Handler::BusConnect();
|
||||
|
||||
auto* interface = AzFramework::LinuxXcbConnectionManagerInterface::Get();
|
||||
if (!interface)
|
||||
{
|
||||
AZ_Warning("ApplicationLinux", false, "XCB interface not available");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* connection = AzFramework::LinuxXcbConnectionManagerInterface::Get()->GetXcbConnection();
|
||||
if (!connection)
|
||||
{
|
||||
AZ_Warning("ApplicationLinux", false, "XCB connection not available");
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<xcb_xkb_use_extension_reply_t, DeleterForFreeFn<::std::free>> xkbUseExtensionReply{
|
||||
xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
|
||||
};
|
||||
if (!xkbUseExtensionReply)
|
||||
{
|
||||
AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
|
||||
return;
|
||||
}
|
||||
if (!xkbUseExtensionReply->supported)
|
||||
{
|
||||
AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
|
||||
return;
|
||||
}
|
||||
|
||||
m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
|
||||
|
||||
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
|
||||
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
|
||||
m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
bool IsConnected() const override
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
bool HasTextEntryStarted() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override
|
||||
{
|
||||
}
|
||||
|
||||
void TextEntryStop() override
|
||||
{
|
||||
}
|
||||
|
||||
void TickInputDevice() override
|
||||
{
|
||||
ProcessRawEventQueues();
|
||||
}
|
||||
|
||||
void HandleXcbEvent(xcb_generic_event_t* event) override
|
||||
{
|
||||
if (!IsConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->response_type & ~0x80)
|
||||
{
|
||||
case XCB_KEY_PRESS:
|
||||
{
|
||||
auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
|
||||
|
||||
const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
|
||||
if (key)
|
||||
{
|
||||
QueueRawKeyEvent(*key, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XCB_KEY_RELEASE:
|
||||
{
|
||||
auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
|
||||
|
||||
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
|
||||
if (key)
|
||||
{
|
||||
QueueRawKeyEvent(*key, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const
|
||||
{
|
||||
const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
|
||||
|
||||
switch(keysym)
|
||||
{
|
||||
case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
|
||||
case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
|
||||
case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
|
||||
case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
|
||||
case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
|
||||
case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
|
||||
case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
|
||||
case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
|
||||
case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
|
||||
case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
|
||||
case XKB_KEY_A:
|
||||
case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
|
||||
case XKB_KEY_B:
|
||||
case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
|
||||
case XKB_KEY_C:
|
||||
case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
|
||||
case XKB_KEY_D:
|
||||
case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
|
||||
case XKB_KEY_E:
|
||||
case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
|
||||
case XKB_KEY_F:
|
||||
case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
|
||||
case XKB_KEY_G:
|
||||
case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
|
||||
case XKB_KEY_H:
|
||||
case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
|
||||
case XKB_KEY_I:
|
||||
case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
|
||||
case XKB_KEY_J:
|
||||
case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
|
||||
case XKB_KEY_K:
|
||||
case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
|
||||
case XKB_KEY_L:
|
||||
case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
|
||||
case XKB_KEY_M:
|
||||
case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
|
||||
case XKB_KEY_N:
|
||||
case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
|
||||
case XKB_KEY_O:
|
||||
case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
|
||||
case XKB_KEY_P:
|
||||
case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
|
||||
case XKB_KEY_Q:
|
||||
case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
|
||||
case XKB_KEY_R:
|
||||
case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
|
||||
case XKB_KEY_S:
|
||||
case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
|
||||
case XKB_KEY_T:
|
||||
case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
|
||||
case XKB_KEY_U:
|
||||
case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
|
||||
case XKB_KEY_V:
|
||||
case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
|
||||
case XKB_KEY_W:
|
||||
case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
|
||||
case XKB_KEY_X:
|
||||
case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
|
||||
case XKB_KEY_Y:
|
||||
case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
|
||||
case XKB_KEY_Z:
|
||||
case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
|
||||
case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
|
||||
case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
|
||||
case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
|
||||
case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
|
||||
case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
|
||||
case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
|
||||
case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
|
||||
case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
|
||||
case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
|
||||
case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
|
||||
case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
|
||||
case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
|
||||
case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
|
||||
case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
|
||||
case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
|
||||
case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
|
||||
case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
|
||||
case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
|
||||
case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
|
||||
case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
|
||||
case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
|
||||
case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
|
||||
case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
|
||||
case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
|
||||
case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
|
||||
case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
|
||||
case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
|
||||
case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
|
||||
case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
|
||||
case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
|
||||
case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
|
||||
case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
|
||||
case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
|
||||
case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
|
||||
case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
|
||||
case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
|
||||
case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
|
||||
case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
|
||||
case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
|
||||
case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
|
||||
case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
|
||||
case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
|
||||
case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
|
||||
case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
|
||||
case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
|
||||
case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
|
||||
case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
|
||||
case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
|
||||
case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
|
||||
case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
|
||||
case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
|
||||
case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
|
||||
case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
|
||||
case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
|
||||
case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
|
||||
case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
|
||||
case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
|
||||
case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
|
||||
case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
|
||||
case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
|
||||
case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
|
||||
case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
|
||||
case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
|
||||
case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
|
||||
case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
|
||||
case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
|
||||
case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
|
||||
case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
|
||||
case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
|
||||
case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
|
||||
case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
|
||||
case XKB_KEY_grave:
|
||||
case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
|
||||
case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
|
||||
case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
|
||||
case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
|
||||
case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<auto freeFn>
|
||||
using DeleterForFreeFn = AZStd::integral_constant<decltype(freeFn), freeFn>;
|
||||
|
||||
AZStd::unique_ptr<xkb_context, DeleterForFreeFn<xkb_context_unref>> m_xkbContext;
|
||||
AZStd::unique_ptr<xkb_keymap, DeleterForFreeFn<xkb_keymap_unref>> m_xkbKeymap;
|
||||
AZStd::unique_ptr<xkb_state, DeleterForFreeFn<xkb_state_unref>> m_xkbState;
|
||||
int m_coreDeviceId{-1};
|
||||
bool m_initialized{false};
|
||||
};
|
||||
|
||||
InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
|
||||
{
|
||||
return aznew InputDeviceKeyboardXcb(inputDevice);
|
||||
}
|
||||
} // namespace AzFramework
|
||||
+9
-1
@@ -62,8 +62,16 @@ namespace AzFramework
|
||||
|
||||
uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
||||
|
||||
const uint32_t interestedEvents =
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||
| XCB_EVENT_MASK_BUTTON_PRESS
|
||||
| XCB_EVENT_MASK_BUTTON_RELEASE
|
||||
| XCB_EVENT_MASK_KEY_PRESS
|
||||
| XCB_EVENT_MASK_KEY_RELEASE
|
||||
| XCB_EVENT_MASK_POINTER_MOTION
|
||||
;
|
||||
uint32_t valueList[] = { xcbRootScreen->black_pixel,
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY };
|
||||
interestedEvents };
|
||||
|
||||
xcb_void_cookie_t xcbCheckResult;
|
||||
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
# Only 'xcb' and 'wayland' are recognized
|
||||
if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
|
||||
|
||||
find_library(XCB_LIBRARY xcb)
|
||||
|
||||
set(LY_BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
${XCB_LIBRARY}
|
||||
3rdParty::X11::xcb
|
||||
3rdParty::X11::xcb_xkb
|
||||
3rdParty::X11::xkbcommon
|
||||
3rdParty::X11::xkbcommon_X11
|
||||
)
|
||||
|
||||
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
|
||||
|
||||
@@ -25,7 +25,7 @@ set(FILES
|
||||
AzFramework/Windowing/NativeWindow_Linux_xcb.h
|
||||
AzFramework/Windowing/NativeWindow_Linux_xcb.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Unimplemented.cpp
|
||||
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp
|
||||
|
||||
+16
@@ -18,6 +18,7 @@ namespace AzManipulatorTestFramework
|
||||
class ImmediateModeActionDispatcher
|
||||
: public ActionDispatcher<ImmediateModeActionDispatcher>
|
||||
, public AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler
|
||||
, public AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler
|
||||
{
|
||||
using KeyboardModifier = AzToolsFramework::ViewportInteraction::KeyboardModifier;
|
||||
using KeyboardModifiers = AzToolsFramework::ViewportInteraction::KeyboardModifiers;
|
||||
@@ -50,6 +51,9 @@ namespace AzManipulatorTestFramework
|
||||
// EditorModifierKeyRequestBus overrides ...
|
||||
KeyboardModifiers QueryKeyboardModifiers() override;
|
||||
|
||||
// EditorViewportInputTimeNowRequestBus overrides ...
|
||||
AZStd::chrono::milliseconds EditorViewportInputTimeNow() override;
|
||||
|
||||
protected:
|
||||
// ActionDispatcher ...
|
||||
void SetSnapToGridImpl(bool enabled) override;
|
||||
@@ -79,6 +83,9 @@ namespace AzManipulatorTestFramework
|
||||
|
||||
mutable AZStd::unique_ptr<MouseInteractionEvent> m_event;
|
||||
ManipulatorViewportInteraction& m_viewportManipulatorInteraction;
|
||||
|
||||
//! Current time that ticks up after each call to EditorViewportInputTimeNow.
|
||||
AZStd::chrono::milliseconds m_timeNow = AZStd::chrono::milliseconds(0);
|
||||
};
|
||||
|
||||
template<typename ActualT, typename ExpectedT>
|
||||
@@ -106,4 +113,13 @@ namespace AzManipulatorTestFramework
|
||||
{
|
||||
return GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers;
|
||||
}
|
||||
|
||||
inline AZStd::chrono::milliseconds ImmediateModeActionDispatcher::EditorViewportInputTimeNow()
|
||||
{
|
||||
// step the time for each call to be greater than the minimum time required for a double click to register
|
||||
// note: the time increment is very high to ensure any potential system changes to settings such as double
|
||||
// click interval will not be impacted
|
||||
m_timeNow += AZStd::chrono::milliseconds(10000);
|
||||
return m_timeNow;
|
||||
}
|
||||
} // namespace AzManipulatorTestFramework
|
||||
|
||||
@@ -33,10 +33,12 @@ namespace AzManipulatorTestFramework
|
||||
: m_viewportManipulatorInteraction(viewportManipulatorInteraction)
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
|
||||
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
ImmediateModeActionDispatcher::~ImmediateModeActionDispatcher()
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! The AZ::Interface of the central editor mode tracker for all viewports.
|
||||
class ViewportEditorModeTrackerInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ViewportEditorModeTrackerInterface, "{7D72A4F7-2147-4ED9-A315-E456A3BE3CF6}");
|
||||
|
||||
virtual ~ViewportEditorModeTrackerInterface() = default;
|
||||
|
||||
//! Activates the specified editor mode for the specified viewport.
|
||||
virtual AZ::Outcome<void, AZStd::string> ActivateMode(
|
||||
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
|
||||
|
||||
//! Deactivates the specified editor mode for the specified viewport.
|
||||
virtual AZ::Outcome<void, AZStd::string> DeactivateMode(
|
||||
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
|
||||
|
||||
//! Attempts to retrieve the editor mode state for the specified viewport, otherwise returns nullptr.
|
||||
virtual const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
|
||||
|
||||
//! Returns the number of viewports currently being tracked.
|
||||
virtual size_t GetTrackedViewportCount() const = 0;
|
||||
|
||||
//! Returns true if the specified viewport is being tracked, otherwise false.
|
||||
virtual bool IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/EBus/Event.h>
|
||||
#include <AzFramework/Viewport/ViewportId.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Enumeration of each viewport editor mode.
|
||||
enum class ViewportEditorMode : AZ::u8
|
||||
{
|
||||
Default,
|
||||
Component,
|
||||
Focus,
|
||||
Pick
|
||||
};
|
||||
|
||||
//! Viewport identifier and other relevant viewport data.
|
||||
struct ViewportEditorModeInfo
|
||||
{
|
||||
using IdType = AzFramework::ViewportId;
|
||||
IdType m_id = ViewportUi::DefaultViewportId; //!< The unique identifier for a given viewport.
|
||||
};
|
||||
|
||||
//! Interface for the editor modes of a given viewport.
|
||||
class ViewportEditorModesInterface
|
||||
{
|
||||
public:
|
||||
virtual ~ViewportEditorModesInterface() = default;
|
||||
|
||||
//! Returns true if the specified editor mode is active, otherwise false.
|
||||
virtual bool IsModeActive(ViewportEditorMode mode) const = 0;
|
||||
};
|
||||
|
||||
//! Provides a bus to notify when the different editor modes are entered/exit.
|
||||
class ViewportEditorModeNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = ViewportEditorModeInfo::IdType;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Notifies subscribers of the a given viewport to the activation of the specified editor mode.
|
||||
virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
|
||||
{
|
||||
}
|
||||
|
||||
//! Notifies subscribers of the a given viewport to the deactivation of the specified editor mode.
|
||||
virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
|
||||
{
|
||||
}
|
||||
};
|
||||
using ViewportEditorModeNotificationsBus = AZ::EBus<ViewportEditorModeNotifications>;
|
||||
} // namespace AzToolsFramework
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceMetadataEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h>
|
||||
@@ -248,6 +249,7 @@ namespace AzToolsFramework
|
||||
components.insert(components.end(), {
|
||||
azrtti_typeid<EditorEntityContextComponent>(),
|
||||
azrtti_typeid<Components::EditorEntityUiSystemComponent>(),
|
||||
azrtti_typeid<FocusModeSystemComponent>(),
|
||||
azrtti_typeid<SliceMetadataEntityContextComponent>(),
|
||||
azrtti_typeid<Prefab::PrefabSystemComponent>(),
|
||||
azrtti_typeid<EditorEntityFixupComponent>(),
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ namespace AzToolsFramework
|
||||
|
||||
QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const
|
||||
{
|
||||
Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() != this);
|
||||
Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this);
|
||||
if (!proxyIndex.isValid() || !m_indexMap.contains(proxyIndex.row()))
|
||||
{
|
||||
return QModelIndex();
|
||||
|
||||
+10
-10
@@ -28,16 +28,16 @@ namespace AzToolsFramework
|
||||
namespace AssetBrowser
|
||||
{
|
||||
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
|
||||
: QTableView(parent)
|
||||
: AzQtComponents::TableView(parent)
|
||||
, m_delegate(new EntryDelegate(this))
|
||||
{
|
||||
setSortingEnabled(true);
|
||||
setItemDelegate(m_delegate);
|
||||
verticalHeader()->hide();
|
||||
setRootIsDecorated(false);
|
||||
|
||||
//Styling the header aligning text to the left and using a bold font.
|
||||
horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||
horizontalHeader()->setStyleSheet("QHeaderView { font-weight: bold; }");
|
||||
header()->setDefaultAlignment(Qt::AlignLeft);
|
||||
header()->setStyleSheet("QHeaderView { font-weight: bold; }");
|
||||
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace AzToolsFramework
|
||||
setSortingEnabled(false);
|
||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
||||
connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
|
||||
connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
|
||||
|
||||
AssetBrowserViewRequestBus::Handler::BusConnect();
|
||||
AssetBrowserComponentNotificationBus::Handler::BusConnect();
|
||||
@@ -62,11 +62,11 @@ namespace AzToolsFramework
|
||||
m_tableModel = qobject_cast<AssetBrowserTableModel*>(model);
|
||||
AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel");
|
||||
m_sourceFilterModel = qobject_cast<AssetBrowserFilterModel*>(m_tableModel->sourceModel());
|
||||
QTableView::setModel(model);
|
||||
AzQtComponents::TableView::setModel(model);
|
||||
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
|
||||
|
||||
horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
|
||||
horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
|
||||
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
|
||||
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::SetName(const QString& name)
|
||||
@@ -98,7 +98,7 @@ namespace AzToolsFramework
|
||||
|
||||
void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
|
||||
{
|
||||
QTableView::selectionChanged(selected, deselected);
|
||||
AzQtComponents::TableView::selectionChanged(selected, deselected);
|
||||
Q_EMIT selectionChangedSignal(selected, deselected);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace AzToolsFramework
|
||||
selectionModel()->clear();
|
||||
}
|
||||
}
|
||||
QTableView::rowsAboutToBeRemoved(parent, start, end);
|
||||
AzQtComponents::TableView::rowsAboutToBeRemoved(parent, start, end);
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::layoutChangedSlot(
|
||||
|
||||
+3
-2
@@ -13,9 +13,10 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
|
||||
|
||||
#include <AzQtComponents/Components/Widgets/TableView.h>
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QPointer>
|
||||
#include <QTableView>
|
||||
#endif
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -28,7 +29,7 @@ namespace AzToolsFramework
|
||||
class EntryDelegate;
|
||||
|
||||
class AssetBrowserTableView //! Table view that displays the asset browser entries in a list.
|
||||
: public QTableView
|
||||
: public AzQtComponents::TableView
|
||||
, public AssetBrowserViewRequestBus::Handler
|
||||
, public AssetBrowserComponentNotificationBus::Handler
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityModelComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySearchComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/PropertyTreeEditor/PropertyTreeEditorComponent.h>
|
||||
#include <AzToolsFramework/Render/EditorIntersectorComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceDependencyBrowserComponent.h>
|
||||
@@ -69,6 +70,7 @@ namespace AzToolsFramework
|
||||
Components::EditorSelectionAccentSystemComponent::CreateDescriptor(),
|
||||
EditorEntityContextComponent::CreateDescriptor(),
|
||||
EditorEntityFixupComponent::CreateDescriptor(),
|
||||
FocusModeSystemComponent::CreateDescriptor(),
|
||||
SliceMetadataEntityContextComponent::CreateDescriptor(),
|
||||
SliceRequestComponent::CreateDescriptor(),
|
||||
Prefab::PrefabSystemComponent::CreateDescriptor(),
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! FocusModeInterface
|
||||
//! Interface to handle the Editor Focus Mode.
|
||||
class FocusModeInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(FocusModeInterface, "{437243B0-F86B-422F-B7B8-4A21CC000702}");
|
||||
|
||||
//! Sets the root entity the Editor should focus on.
|
||||
//! The Editor will only allow the user to select entities that are descendants of the EntityId provided.
|
||||
//! @param entityId The entityId that will become the new focus root.
|
||||
virtual void SetFocusRoot(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Clears the Editor focus, allowing the user to select the whole level again.
|
||||
virtual void ClearFocusRoot() = 0;
|
||||
|
||||
//! Returns the entity id of the root of the current Editor focus.
|
||||
//! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set.
|
||||
virtual AZ::EntityId GetFocusRoot() = 0;
|
||||
|
||||
//! Returns whether the entity id provided is part of the focused sub-tree.
|
||||
virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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/TransformBus.h>
|
||||
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId)
|
||||
{
|
||||
if (entityId == AZ::EntityId())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entityId == focusRootId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId);
|
||||
|
||||
return IsInFocusSubTree(parentId, focusRootId);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Init()
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<FocusModeInterface>::Register(this);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Deactivate()
|
||||
{
|
||||
AZ::Interface<FocusModeInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("EditorFocusMode"));
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId)
|
||||
{
|
||||
m_focusRoot = entityId;
|
||||
|
||||
// TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::ClearFocusRoot()
|
||||
{
|
||||
SetFocusRoot(AZ::EntityId());
|
||||
}
|
||||
|
||||
AZ::EntityId FocusModeSystemComponent::GetFocusRoot()
|
||||
{
|
||||
return m_focusRoot;
|
||||
}
|
||||
|
||||
bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId)
|
||||
{
|
||||
if (m_focusRoot == AZ::EntityId())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return AzToolsFramework::IsInFocusSubTree(entityId, m_focusRoot);
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId);
|
||||
|
||||
//! System Component to handle the Editor Focus Mode system
|
||||
class FocusModeSystemComponent final
|
||||
: public AZ::Component
|
||||
, private FocusModeInterface
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(FocusModeSystemComponent, "{6CE522FE-2057-4794-BD05-61E04BD8EA30}");
|
||||
|
||||
FocusModeSystemComponent() = default;
|
||||
virtual ~FocusModeSystemComponent() = default;
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Init() override;
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
// FocusModeInterface overrides ...
|
||||
void SetFocusRoot(AZ::EntityId entityId) override;
|
||||
void ClearFocusRoot() override;
|
||||
AZ::EntityId GetFocusRoot() override;
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId) override;
|
||||
|
||||
private:
|
||||
AZ::EntityId m_focusRoot;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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/Prefab/PrefabFocusHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
PrefabFocusHandler::PrefabFocusHandler()
|
||||
{
|
||||
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.");
|
||||
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabFocusHandler::~PrefabFocusHandler()
|
||||
{
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
|
||||
{
|
||||
InstanceOptionalReference focusedInstance;
|
||||
|
||||
if (entityId == AZ::EntityId())
|
||||
{
|
||||
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
|
||||
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
|
||||
if(!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error "
|
||||
"(PrefabEditorEntityOwnershipInterface unavailable)."));
|
||||
}
|
||||
|
||||
focusedInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
}
|
||||
|
||||
if (!focusedInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(
|
||||
"Prefab Focus Handler: Couldn't find owning instance of entityId provided."));
|
||||
}
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
if (focusModeInterface)
|
||||
{
|
||||
focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId());
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId()
|
||||
{
|
||||
return m_focusedTemplateId;
|
||||
}
|
||||
|
||||
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance()
|
||||
{
|
||||
return m_focusedInstance;
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId)
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entityId == AZ::EntityId())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
return instance.has_value() && (&instance->get() == &m_focusedInstance->get());
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class InstanceEntityMapperInterface;
|
||||
|
||||
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0);
|
||||
|
||||
PrefabFocusHandler();
|
||||
~PrefabFocusHandler();
|
||||
|
||||
// PrefabFocusInterface override ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId() override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance() override;
|
||||
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override;
|
||||
|
||||
private:
|
||||
InstanceOptionalReference m_focusedInstance;
|
||||
TemplateId m_focusedTemplateId;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
//! Interface to handle operations related to the Prefab Focus system.
|
||||
class PrefabFocusInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}");
|
||||
|
||||
//! 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 FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Returns the template id of the instance the prefab system is focusing on.
|
||||
virtual TemplateId GetFocusedPrefabTemplateId() = 0;
|
||||
|
||||
//! Returns a reference to the instance the prefab system is focusing on.
|
||||
virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabUndoCache.h>
|
||||
@@ -189,6 +190,9 @@ namespace AzToolsFramework
|
||||
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
// Handles the Prefab Focus API that determines what prefab is being edited.
|
||||
PrefabFocusHandler m_prefabFocusHandler;
|
||||
|
||||
// Caches entity states for undo/redo purposes
|
||||
PrefabUndoCache m_prefabUndoCache;
|
||||
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace AzToolsFramework
|
||||
// A counter for generating unique Link Ids.
|
||||
AZStd::atomic<LinkId> m_linkIdCounter = 0u;
|
||||
|
||||
// Used for finding the owning instance of an arbitrary entity
|
||||
// Used for finding the owning instance of an arbitrary entity.
|
||||
InstanceEntityMapper m_instanceEntityMapper;
|
||||
|
||||
// Used for finding the Instances owned by an arbitrary Template.
|
||||
@@ -378,16 +378,16 @@ namespace AzToolsFramework
|
||||
// Used for loading/saving Prefab Template files.
|
||||
PrefabLoader m_prefabLoader;
|
||||
|
||||
// Handler the public Prefab API used by UI and scripting
|
||||
// Handles the public Prefab API used by UI and scripting.
|
||||
PrefabPublicHandler m_prefabPublicHandler;
|
||||
|
||||
// Used for updating Instances of Prefab Template.
|
||||
InstanceUpdateExecutor m_instanceUpdateExecutor;
|
||||
|
||||
// Used for updating Templates when Instances are modified
|
||||
// Used for updating Templates when Instances are modified.
|
||||
InstanceToTemplatePropagator m_instanceToTemplatePropagator;
|
||||
|
||||
// Handler of the public Prefab requests
|
||||
// Handler of the public Prefab requests.
|
||||
PrefabPublicRequestHandler m_prefabPublicRequestHandler;
|
||||
};
|
||||
} // namespace Prefab
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
@@ -24,14 +23,6 @@ namespace AzToolsFramework
|
||||
|
||||
LevelRootUiHandler::LevelRootUiHandler()
|
||||
{
|
||||
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
|
||||
|
||||
if (m_prefabEditInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "LevelRootUiHandler - could not get PrefabEditInterface on LevelRootUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
|
||||
if (m_prefabPublicInterface == nullptr)
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabEditInterface;
|
||||
class PrefabPublicInterface;
|
||||
};
|
||||
|
||||
@@ -36,7 +35,6 @@ namespace AzToolsFramework
|
||||
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
static constexpr int m_levelRootBorderThickness = 1;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
/*!
|
||||
* PrefabEditInterface
|
||||
* Interface to expose the API to Edit Prefabs in the Editor.
|
||||
*/
|
||||
class PrefabEditInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabEditInterface, "{DABB1D43-3760-420E-9F1E-5104F0AFF167}");
|
||||
|
||||
/**
|
||||
* Sets the prefab for the instance owning the entity provided as the prefab being edited.
|
||||
* @param entityId The entity whose owning prefab should be edited.
|
||||
*/
|
||||
virtual void EditOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
/**
|
||||
* Queries the Edit Manager to know if the provided entity is part of the prefab currently being edited.
|
||||
* @param entityId The entity whose prefab editing state we want to query.
|
||||
* @return True if the prefab owning this entity is being edited, false otherwise.
|
||||
*/
|
||||
virtual bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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/Prefab/PrefabEditManager.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
PrefabEditManager::PrefabEditManager()
|
||||
{
|
||||
m_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
|
||||
|
||||
if (m_prefabPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabEditManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Interface<PrefabEditInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabEditManager::~PrefabEditManager()
|
||||
{
|
||||
AZ::Interface<PrefabEditInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void PrefabEditManager::EditOwningPrefab(AZ::EntityId entityId)
|
||||
{
|
||||
m_instanceBeingEdited = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId);
|
||||
}
|
||||
|
||||
bool PrefabEditManager::IsOwningPrefabBeingEdited(AZ::EntityId entityId)
|
||||
{
|
||||
AZ::EntityId containerEntity = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId);
|
||||
return m_instanceBeingEdited == containerEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabEditManager final
|
||||
: private PrefabEditInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabEditManager, AZ::SystemAllocator, 0);
|
||||
|
||||
PrefabEditManager();
|
||||
~PrefabEditManager();
|
||||
|
||||
private:
|
||||
// PrefabEditInterface...
|
||||
void EditOwningPrefab(AZ::EntityId entityId) override;
|
||||
bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) override;
|
||||
|
||||
AZ::EntityId m_instanceBeingEdited;
|
||||
|
||||
PrefabPublicInterface* m_prefabPublicInterface;
|
||||
};
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -22,6 +22,7 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
@@ -57,9 +58,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
|
||||
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
|
||||
PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr;
|
||||
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
|
||||
@@ -102,13 +103,6 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabEditInterface = AZ::Interface<PrefabEditInterface>::Get();
|
||||
if (s_prefabEditInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabEditInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
if (s_prefabLoaderInterface == nullptr)
|
||||
{
|
||||
@@ -123,6 +117,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (s_prefabFocusInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorContextMenuBus::Handler::BusConnect();
|
||||
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabIntegrationInterface>::Register(this);
|
||||
@@ -224,7 +225,7 @@ namespace AzToolsFramework
|
||||
// Edit Prefab
|
||||
if (prefabWipFeaturesEnabled)
|
||||
{
|
||||
bool beingEdited = s_prefabEditInterface->IsOwningPrefabBeingEdited(selectedEntity);
|
||||
bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity);
|
||||
|
||||
if (!beingEdited)
|
||||
{
|
||||
@@ -428,7 +429,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
s_prefabEditInterface->EditOwningPrefab(containerEntity);
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
|
||||
|
||||
+4
-8
@@ -17,7 +17,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
|
||||
@@ -28,7 +28,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
|
||||
class PrefabFocusInterface;
|
||||
class PrefabLoaderInterface;
|
||||
|
||||
//! Structure for saving/retrieving user settings related to prefab workflows.
|
||||
@@ -80,9 +80,6 @@ namespace AzToolsFramework
|
||||
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
|
||||
|
||||
private:
|
||||
// Manages the Edit Mode UI for prefabs
|
||||
PrefabEditManager m_prefabEditManager;
|
||||
|
||||
// Used to handle the UI for the level root
|
||||
LevelRootUiHandler m_levelRootUiHandler;
|
||||
|
||||
@@ -135,13 +132,12 @@ namespace AzToolsFramework
|
||||
AZStd::unique_ptr<QDialog> ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference);
|
||||
void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog);
|
||||
|
||||
|
||||
static const AZStd::string s_prefabFileExtension;
|
||||
|
||||
static EditorEntityUiInterface* s_editorEntityUiInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabEditInterface* s_prefabEditInterface;
|
||||
static PrefabFocusInterface* s_prefabFocusInterface;
|
||||
static PrefabLoaderInterface* s_prefabLoaderInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
@@ -26,21 +26,19 @@ namespace AzToolsFramework
|
||||
|
||||
PrefabUiHandler::PrefabUiHandler()
|
||||
{
|
||||
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
|
||||
|
||||
if (m_prefabEditInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabEditInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
|
||||
if (m_prefabPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabPublicInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabFocusInterface = AZ::Interface<Prefab::PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
|
||||
@@ -83,7 +81,7 @@ namespace AzToolsFramework
|
||||
|
||||
QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
|
||||
{
|
||||
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
return QIcon(m_prefabEditIconPath);
|
||||
}
|
||||
@@ -105,7 +103,7 @@ namespace AzToolsFramework
|
||||
const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
|
||||
|
||||
QColor backgroundColor = m_prefabCapsuleColor;
|
||||
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
backgroundColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
@@ -191,7 +189,7 @@ namespace AzToolsFramework
|
||||
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
|
||||
|
||||
QColor borderColor = m_prefabCapsuleColor;
|
||||
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
borderColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabEditInterface;
|
||||
class PrefabFocusInterface;
|
||||
class PrefabPublicInterface;
|
||||
};
|
||||
|
||||
@@ -37,7 +38,7 @@ namespace AzToolsFramework
|
||||
const QModelIndex& descendantIndex) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
|
||||
Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
|
||||
|
||||
@@ -282,6 +282,24 @@ namespace AzToolsFramework
|
||||
return keyboardModifiers;
|
||||
}
|
||||
|
||||
//! An interface to deal with time requests relating to viewports.
|
||||
//! @note The bus is global and not per viewport.
|
||||
class EditorViewportInputTimeNowRequests : public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Returns the current time in seconds.
|
||||
//! This interface can be overridden for the purposes of testing to simplify viewport input requests.
|
||||
virtual AZStd::chrono::milliseconds EditorViewportInputTimeNow() = 0;
|
||||
|
||||
protected:
|
||||
~EditorViewportInputTimeNowRequests() = default;
|
||||
};
|
||||
|
||||
using EditorViewportInputTimeNowRequestBus = AZ::EBus<EditorViewportInputTimeNowRequests>;
|
||||
|
||||
//! Viewport requests for managing the viewport cursor state.
|
||||
class ViewportMouseCursorRequests
|
||||
{
|
||||
|
||||
+19
-8
@@ -253,10 +253,10 @@ namespace AzToolsFramework
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl());
|
||||
}
|
||||
|
||||
static bool ManipulatorDitto(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
static bool ManipulatorDitto(
|
||||
const AzFramework::ClickDetector::ClickOutcome clickOutcome, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
return mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
|
||||
mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
|
||||
}
|
||||
@@ -1054,6 +1054,17 @@ namespace AzToolsFramework
|
||||
RegisterActions();
|
||||
SetupBoxSelect();
|
||||
RefreshSelectedEntityIdsAndRegenerateManipulators();
|
||||
|
||||
// ensure the click detector uses the EditorViewportInputTimeNowRequests interface to retrieve elapsed time
|
||||
// note: this is to facilitate overriding this functionality for purposes such as testing
|
||||
m_clickDetector.OverrideTimeNowFn(
|
||||
[]
|
||||
{
|
||||
AZStd::chrono::milliseconds timeNow;
|
||||
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::BroadcastResult(
|
||||
timeNow, &AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Events::EditorViewportInputTimeNow);
|
||||
return timeNow;
|
||||
});
|
||||
}
|
||||
|
||||
EditorTransformComponentSelection::~EditorTransformComponentSelection()
|
||||
@@ -1883,7 +1894,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// set manipulator pivot override translation or orientation (update manipulators)
|
||||
if (Input::ManipulatorDitto(mouseInteraction))
|
||||
if (Input::ManipulatorDitto(clickOutcome, mouseInteraction))
|
||||
{
|
||||
PerformManipulatorDitto(entityIdUnderCursor);
|
||||
return false;
|
||||
@@ -3631,7 +3642,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& newViewId)
|
||||
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -3639,12 +3650,12 @@ namespace AzToolsFramework
|
||||
// match the editor camera translation/orientation), record the entity id if we have
|
||||
// a manipulator tracking it (entity id exists in m_entityIdManipulator lookups)
|
||||
// and remove it when recreating manipulators (see InitializeManipulators)
|
||||
if (newViewId.IsValid())
|
||||
if (viewEntityId.IsValid())
|
||||
{
|
||||
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(newViewId);
|
||||
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(viewEntityId);
|
||||
if (entityIdLookupIt != m_entityIdManipulators.m_lookups.end())
|
||||
{
|
||||
m_editorCameraComponentEntityId = newViewId;
|
||||
m_editorCameraComponentEntityId = viewEntityId;
|
||||
RegenerateManipulators();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ namespace AzToolsFramework
|
||||
void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override;
|
||||
|
||||
// Camera::EditorCameraNotificationBus overrides ...
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override;
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
|
||||
|
||||
// EditorContextVisibilityNotificationBus overrides ...
|
||||
void OnEntityVisibilityChanged(bool visibility) override;
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
AZ::Outcome<void, AZStd::string> ViewportEditorModes::ActivateMode(ViewportEditorMode mode)
|
||||
{
|
||||
if (const AZ::u32 modeIndex = static_cast<AZ::u32>(mode);
|
||||
modeIndex < NumEditorModes)
|
||||
{
|
||||
m_editorModes[modeIndex] = true;
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string::format("Cannot activate mode %u, mode is not recognized", modeIndex));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ViewportEditorModes::DeactivateMode(ViewportEditorMode mode)
|
||||
{
|
||||
if (const AZ::u32 modeIndex = static_cast<AZ::u32>(mode); modeIndex < NumEditorModes)
|
||||
{
|
||||
m_editorModes[modeIndex] = false;
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string::format("Cannot deactivate mode %u, mode is not recognized", modeIndex));
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewportEditorModes::IsModeActive(ViewportEditorMode mode) const
|
||||
{
|
||||
return m_editorModes[static_cast<AZ::u32>(mode)];
|
||||
}
|
||||
|
||||
void ViewportEditorModeTracker::RegisterInterface()
|
||||
{
|
||||
if (AZ::Interface<ViewportEditorModeTrackerInterface>::Get() == nullptr)
|
||||
{
|
||||
AZ::Interface<ViewportEditorModeTrackerInterface>::Register(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportEditorModeTracker::UnregisterInterface()
|
||||
{
|
||||
if (AZ::Interface<ViewportEditorModeTrackerInterface>::Get() != nullptr)
|
||||
{
|
||||
AZ::Interface<ViewportEditorModeTrackerInterface>::Unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ViewportEditorModeTracker::ActivateMode(
|
||||
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode)
|
||||
{
|
||||
auto& editorModes = m_viewportEditorModesMap[viewportEditorModeInfo.m_id];
|
||||
if (editorModes.IsModeActive(mode))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Duplicate call to ActivateMode for mode '%u' on id '%i'", static_cast<AZ::u32>(mode), viewportEditorModeInfo.m_id));
|
||||
}
|
||||
|
||||
if (const auto result = editorModes.ActivateMode(mode);
|
||||
!result.IsSuccess())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
ViewportEditorModeNotificationsBus::Event(
|
||||
viewportEditorModeInfo.m_id, &ViewportEditorModeNotificationsBus::Events::OnEditorModeActivated, editorModes, mode);
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ViewportEditorModeTracker::DeactivateMode(
|
||||
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode)
|
||||
{
|
||||
ViewportEditorModes* editorModes = nullptr;
|
||||
bool modeWasActive = true;
|
||||
if (m_viewportEditorModesMap.count(viewportEditorModeInfo.m_id))
|
||||
{
|
||||
editorModes = &m_viewportEditorModesMap.at(viewportEditorModeInfo.m_id);
|
||||
if (!editorModes->IsModeActive(mode))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Duplicate call to DeactivateMode for mode '%u' on id '%i'", static_cast<AZ::u32>(mode), viewportEditorModeInfo.m_id));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
modeWasActive = false;
|
||||
editorModes = &m_viewportEditorModesMap[viewportEditorModeInfo.m_id];
|
||||
}
|
||||
|
||||
if(const auto result = editorModes->DeactivateMode(mode);
|
||||
!result.IsSuccess())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
ViewportEditorModeNotificationsBus::Event(
|
||||
viewportEditorModeInfo.m_id, &ViewportEditorModeNotificationsBus::Events::OnEditorModeDeactivated, *editorModes, mode);
|
||||
|
||||
if (modeWasActive)
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Call to DeactivateMode for mode '%u' on id '%i' without precursor call to ActivateMode", static_cast<AZ::u32>(mode),
|
||||
viewportEditorModeInfo.m_id));
|
||||
}
|
||||
}
|
||||
|
||||
const ViewportEditorModesInterface* ViewportEditorModeTracker::GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const
|
||||
{
|
||||
if (auto editorModes = m_viewportEditorModesMap.find(viewportEditorModeInfo.m_id);
|
||||
editorModes != m_viewportEditorModesMap.end())
|
||||
{
|
||||
return &editorModes->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ViewportEditorModeTracker::GetTrackedViewportCount() const
|
||||
{
|
||||
return m_viewportEditorModesMap.size();
|
||||
}
|
||||
|
||||
bool ViewportEditorModeTracker::IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const
|
||||
{
|
||||
return m_viewportEditorModesMap.count(viewportEditorModeInfo.m_id) > 0;
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! The encapsulation of the editor modes for a given viewport.
|
||||
class ViewportEditorModes
|
||||
: public ViewportEditorModesInterface
|
||||
{
|
||||
public:
|
||||
//! The number of currently supported viewport editor modes.
|
||||
static constexpr AZ::u8 NumEditorModes = 4;
|
||||
|
||||
//! Sets the specified mode as active.
|
||||
AZ::Outcome<void, AZStd::string> ActivateMode(ViewportEditorMode mode);
|
||||
|
||||
// Sets the specified mode as inactive.
|
||||
AZ::Outcome<void, AZStd::string> DeactivateMode(ViewportEditorMode mode);
|
||||
|
||||
// ViewportEditorModesInterface ...
|
||||
bool IsModeActive(ViewportEditorMode mode) const override;
|
||||
private:
|
||||
AZStd::array<bool, NumEditorModes> m_editorModes{}; //!< State flags to track active/inactive status of viewport editor modes.
|
||||
};
|
||||
|
||||
//! The implementation of the central editor mode state tracker for all viewports.
|
||||
class ViewportEditorModeTracker
|
||||
: public ViewportEditorModeTrackerInterface
|
||||
{
|
||||
public:
|
||||
//! Registers this object with the AZ::Interface.
|
||||
void RegisterInterface();
|
||||
|
||||
//! Unregisters this object with the AZ::Interface.
|
||||
void UnregisterInterface();
|
||||
|
||||
// ViewportEditorModeTrackerInterface overrides ...
|
||||
AZ::Outcome<void, AZStd::string> ActivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override;
|
||||
AZ::Outcome<void, AZStd::string> DeactivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override;
|
||||
const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const override;
|
||||
size_t GetTrackedViewportCount() const override;
|
||||
bool IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const override;
|
||||
|
||||
private:
|
||||
using ViewportEditorModesMap = AZStd::unordered_map<typename ViewportEditorModeInfo::IdType, ViewportEditorModes>;
|
||||
ViewportEditorModesMap m_viewportEditorModesMap; //!< Editor mode state per viewport.
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
@@ -34,6 +34,7 @@ set(FILES
|
||||
API/EditorAnimationSystemRequestBus.h
|
||||
API/EditorEntityAPI.h
|
||||
API/EditorLevelNotificationBus.h
|
||||
API/ViewportEditorModeTrackerNotificationBus.h
|
||||
API/EditorVegetationRequestsBus.h
|
||||
API/EditorPythonConsoleBus.h
|
||||
API/EditorPythonRunnerRequestsBus.h
|
||||
@@ -44,6 +45,7 @@ set(FILES
|
||||
API/EntityCompositionNotificationBus.h
|
||||
API/EditorViewportIconDisplayInterface.h
|
||||
API/ViewPaneOptions.h
|
||||
API/ViewportEditorModeTrackerInterface.h
|
||||
Application/Ticker.h
|
||||
Application/Ticker.cpp
|
||||
Application/EditorEntityManager.cpp
|
||||
@@ -147,6 +149,9 @@ set(FILES
|
||||
Entity/SliceEditorEntityOwnershipServiceBus.h
|
||||
Fingerprinting/TypeFingerprinter.h
|
||||
Fingerprinting/TypeFingerprinter.cpp
|
||||
FocusMode/FocusModeInterface.h
|
||||
FocusMode/FocusModeSystemComponent.h
|
||||
FocusMode/FocusModeSystemComponent.cpp
|
||||
Logger/TraceLogger.cpp
|
||||
Logger/TraceLogger.h
|
||||
Manipulators/AngularManipulator.cpp
|
||||
@@ -538,6 +543,8 @@ set(FILES
|
||||
ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp
|
||||
ViewportSelection/EditorVisibleEntityDataCache.h
|
||||
ViewportSelection/EditorVisibleEntityDataCache.cpp
|
||||
ViewportSelection/ViewportEditorModeTracker.cpp
|
||||
ViewportSelection/ViewportEditorModeTracker.h
|
||||
ToolsFileUtils/ToolsFileUtils.h
|
||||
AssetBrowser/AssetBrowserBus.h
|
||||
AssetBrowser/AssetBrowserSourceDropBus.h
|
||||
@@ -625,6 +632,9 @@ set(FILES
|
||||
Prefab/PrefabDomTypes.h
|
||||
Prefab/PrefabDomUtils.h
|
||||
Prefab/PrefabDomUtils.cpp
|
||||
Prefab/PrefabFocusHandler.h
|
||||
Prefab/PrefabFocusHandler.cpp
|
||||
Prefab/PrefabFocusInterface.h
|
||||
Prefab/PrefabIdTypes.h
|
||||
Prefab/PrefabLoader.h
|
||||
Prefab/PrefabLoader.cpp
|
||||
@@ -717,9 +727,6 @@ set(FILES
|
||||
UI/Layer/LayerUiHandler.cpp
|
||||
UI/Prefab/LevelRootUiHandler.h
|
||||
UI/Prefab/LevelRootUiHandler.cpp
|
||||
UI/Prefab/PrefabEditInterface.h
|
||||
UI/Prefab/PrefabEditManager.h
|
||||
UI/Prefab/PrefabEditManager.cpp
|
||||
UI/Prefab/PrefabIntegrationBus.h
|
||||
UI/Prefab/PrefabIntegrationManager.h
|
||||
UI/Prefab/PrefabIntegrationManager.cpp
|
||||
|
||||
@@ -783,7 +783,8 @@ namespace UnitTest
|
||||
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->SetStickySelect(true)->CameraState(m_cameraState)
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(entity2ScreenPosition)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->MouseLButtonDown()
|
||||
@@ -1018,6 +1019,105 @@ namespace UnitTest
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
}
|
||||
|
||||
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
|
||||
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
|
||||
, public ::testing::WithParamInterface<bool>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam,
|
||||
StickyAndUnstickyDittoManipulatorToOtherEntityChangesManipulatorAndDoesNotChangeSelection)
|
||||
{
|
||||
PositionEntities();
|
||||
PositionCamera(m_cameraState);
|
||||
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// calculate the position in screen space of the second entity
|
||||
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
|
||||
|
||||
// single click select entity2
|
||||
m_actionDispatcher->SetStickySelect(GetParam())
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(entity2ScreenPosition)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
// entity1 is still selected
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
|
||||
AZStd::optional<AZ::Transform> manipulatorTransform;
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam,
|
||||
StickyAndUnstickyDittoManipulatorToOtherEntityChangesManipulatorAndClickOffResetsManipulator)
|
||||
{
|
||||
PositionEntities();
|
||||
PositionCamera(m_cameraState);
|
||||
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// calculate the position in screen space of the second entity
|
||||
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
|
||||
|
||||
// position in space above the entities
|
||||
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
|
||||
// calculate the screen space position of the click
|
||||
const auto clickOffPositionScreen = AzFramework::WorldToScreen(clickOffPositionWorld, m_cameraState);
|
||||
|
||||
using ::testing::UnorderedElementsAre;
|
||||
// single click select entity2, then click off
|
||||
m_actionDispatcher->SetStickySelect(GetParam())
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(entity2ScreenPosition)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp()
|
||||
->ExecuteBlock(
|
||||
[this]()
|
||||
{
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
|
||||
AZStd::optional<AZ::Transform> manipulatorTransform;
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
|
||||
})
|
||||
->MousePosition(clickOffPositionScreen)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
|
||||
AZStd::optional<AZ::Transform> manipulatorTransform;
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
|
||||
// manipulator transform is reset
|
||||
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity1WorldTranslation));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(All, EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam, testing::Values(true, false));
|
||||
|
||||
using EditorTransformComponentSelectionManipulatorTestFixture =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
|
||||
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using ViewportEditorMode = AzToolsFramework::ViewportEditorMode;
|
||||
using ViewportEditorModes = AzToolsFramework::ViewportEditorModes;
|
||||
using ViewportEditorModeTracker = AzToolsFramework::ViewportEditorModeTracker;
|
||||
using ViewportEditorModeInfo = AzToolsFramework::ViewportEditorModeInfo;
|
||||
using ViewportId = ViewportEditorModeInfo::IdType;
|
||||
using ViewportEditorModesInterface = AzToolsFramework::ViewportEditorModesInterface;
|
||||
|
||||
void ActivateModeAndExpectSuccess(ViewportEditorModes& editorModeState, ViewportEditorMode mode)
|
||||
{
|
||||
const auto result = editorModeState.ActivateMode(mode);
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
}
|
||||
|
||||
void DeactivateModeAndExpectSuccess(ViewportEditorModes& editorModeState, ViewportEditorMode mode)
|
||||
{
|
||||
const auto result = editorModeState.DeactivateMode(mode);
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
}
|
||||
|
||||
void SetAllModesActive(ViewportEditorModes& editorModeState)
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
ActivateModeAndExpectSuccess(editorModeState, static_cast<ViewportEditorMode>(mode));
|
||||
}
|
||||
}
|
||||
|
||||
void SetAllModesInactive(ViewportEditorModes& editorModeState)
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
DeactivateModeAndExpectSuccess(editorModeState, static_cast<ViewportEditorMode>(mode));
|
||||
}
|
||||
}
|
||||
|
||||
// Fixture for testing editor mode states
|
||||
class ViewportEditorModesTestsFixture
|
||||
: public ::testing::Test
|
||||
{
|
||||
public:
|
||||
ViewportEditorModes m_editorModes;
|
||||
};
|
||||
|
||||
// Fixture for testing editor mode states with parameterized test arguments
|
||||
class ViewportEditorModesTestsFixtureWithParams
|
||||
: public ViewportEditorModesTestsFixture
|
||||
, public ::testing::WithParamInterface<AzToolsFramework::ViewportEditorMode>
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
m_selectedEditorMode = GetParam();
|
||||
}
|
||||
|
||||
ViewportEditorMode m_selectedEditorMode;
|
||||
};
|
||||
|
||||
// Fixture for testing the viewport editor mode state tracker
|
||||
class ViewportEditorModeTrackerTestFixture
|
||||
: public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
ViewportEditorModeTracker m_viewportEditorModeTracker;
|
||||
};
|
||||
|
||||
// Subscriber of viewport editor mode notifications for a single viewport that expects a single mode to be activated/deactivated
|
||||
class ViewportEditorModeNotificationsBusHandler
|
||||
: private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
|
||||
{
|
||||
public:
|
||||
struct ReceivedEvents
|
||||
{
|
||||
bool m_onEnter = false;
|
||||
bool m_onExit = false;
|
||||
};
|
||||
|
||||
using EditModeTracker = AZStd::unordered_map<ViewportEditorMode, ReceivedEvents>;
|
||||
|
||||
ViewportEditorModeNotificationsBusHandler(ViewportId viewportId)
|
||||
: m_viewportSubscription(viewportId)
|
||||
{
|
||||
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(m_viewportSubscription);
|
||||
}
|
||||
|
||||
~ViewportEditorModeNotificationsBusHandler()
|
||||
{
|
||||
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
ViewportId GetViewportSubscription() const
|
||||
{
|
||||
return m_viewportSubscription;
|
||||
}
|
||||
|
||||
const EditModeTracker& GetEditorModes() const
|
||||
{
|
||||
return m_editorModes;
|
||||
}
|
||||
|
||||
void OnEditorModeActivated([[maybe_unused]]const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override
|
||||
{
|
||||
m_editorModes[mode].m_onEnter = true;
|
||||
}
|
||||
|
||||
virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override
|
||||
{
|
||||
m_editorModes[mode].m_onExit = true;
|
||||
}
|
||||
|
||||
private:
|
||||
ViewportId m_viewportSubscription;
|
||||
EditModeTracker m_editorModes;
|
||||
|
||||
};
|
||||
|
||||
// Fixture for testing viewport editor mode notifications publishing
|
||||
class ViewportEditorModePublisherTestFixture
|
||||
: public ViewportEditorModeTrackerTestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
m_editorModeHandlers[mode] = AZStd::make_unique<ViewportEditorModeNotificationsBusHandler>(mode);
|
||||
}
|
||||
}
|
||||
|
||||
void TearDownEditorFixtureImpl() override
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
m_editorModeHandlers[mode].reset();
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::array<AZStd::unique_ptr<ViewportEditorModeNotificationsBusHandler>, ViewportEditorModes::NumEditorModes> m_editorModeHandlers;
|
||||
};
|
||||
|
||||
TEST_F(ViewportEditorModesTestsFixture, NumberOfEditorModesIsEqualTo4)
|
||||
{
|
||||
EXPECT_EQ(ViewportEditorModes::NumEditorModes, 4);
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModesTestsFixture, InitialEditorModeStateHasAllInactiveModes)
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
EXPECT_FALSE(m_editorModes.IsModeActive(static_cast<ViewportEditorMode>(mode)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingModeActiveActivatesOnlyThatMode)
|
||||
{
|
||||
ActivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode);
|
||||
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
const auto editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
if (editorMode == m_selectedEditorMode)
|
||||
{
|
||||
EXPECT_TRUE(m_editorModes.IsModeActive(static_cast<ViewportEditorMode>(editorMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_FALSE(m_editorModes.IsModeActive(static_cast<ViewportEditorMode>(editorMode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingModeInactiveInactivatesOnlyThatMode)
|
||||
{
|
||||
SetAllModesActive(m_editorModes);
|
||||
DeactivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode);
|
||||
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
const auto editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
if (editorMode == m_selectedEditorMode)
|
||||
{
|
||||
EXPECT_FALSE(m_editorModes.IsModeActive(editorMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(m_editorModes.IsModeActive(editorMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingMultipleModesActiveActivatesAllThoseModesNonMutuallyExclusively)
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes - 1; mode++)
|
||||
{
|
||||
// Given only the selected mode active
|
||||
SetAllModesInactive(m_editorModes);
|
||||
{
|
||||
ActivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode);
|
||||
}
|
||||
|
||||
const auto editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
if (editorMode == m_selectedEditorMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// When other modes are activated
|
||||
ActivateModeAndExpectSuccess(m_editorModes, editorMode);
|
||||
|
||||
for (auto expectedMode = 0; expectedMode < ViewportEditorModes::NumEditorModes; expectedMode++)
|
||||
{
|
||||
const auto expectedEditorMode = static_cast<ViewportEditorMode>(expectedMode);
|
||||
if (expectedEditorMode == editorMode || expectedEditorMode == m_selectedEditorMode)
|
||||
{
|
||||
// Expect the activated modes to be active
|
||||
EXPECT_TRUE(m_editorModes.IsModeActive(expectedEditorMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expect the modes not active to be inactive
|
||||
EXPECT_FALSE(m_editorModes.IsModeActive(expectedEditorMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingMultipleModesInactiveInactivatesAllThoseModesNonMutuallyExclusively)
|
||||
{
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes - 1; mode++)
|
||||
{
|
||||
// Given only the selected mode inactive
|
||||
SetAllModesActive(m_editorModes);
|
||||
DeactivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode);
|
||||
|
||||
const auto editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
if (editorMode == m_selectedEditorMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// When other modes are deactivated
|
||||
DeactivateModeAndExpectSuccess(m_editorModes, editorMode);
|
||||
|
||||
for (auto expectedMode = 0; expectedMode < ViewportEditorModes::NumEditorModes; expectedMode++)
|
||||
{
|
||||
const auto expectedEditorMode = static_cast<ViewportEditorMode>(expectedMode);
|
||||
if (expectedEditorMode == editorMode || expectedEditorMode == m_selectedEditorMode)
|
||||
{
|
||||
// Expect the deactivated modes to be inactive
|
||||
EXPECT_FALSE(m_editorModes.IsModeActive(expectedEditorMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expects the modes not deactivated to still be active
|
||||
EXPECT_TRUE(m_editorModes.IsModeActive(expectedEditorMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AllEditorModes,
|
||||
ViewportEditorModesTestsFixtureWithParams,
|
||||
::testing::Values(
|
||||
AzToolsFramework::ViewportEditorMode::Default,
|
||||
AzToolsFramework::ViewportEditorMode::Component,
|
||||
AzToolsFramework::ViewportEditorMode::Focus,
|
||||
AzToolsFramework::ViewportEditorMode::Pick));
|
||||
|
||||
TEST_F(ViewportEditorModesTestsFixture, SettingOutOfBoundsModeActiveReturnsError)
|
||||
{
|
||||
const auto result = m_editorModes.ActivateMode(static_cast<ViewportEditorMode>(ViewportEditorModes::NumEditorModes));
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModesTestsFixture, SettingOutOfBoundsModeInactiveReturnsError)
|
||||
{
|
||||
const auto result = m_editorModes.DeactivateMode(static_cast<ViewportEditorMode>(ViewportEditorModes::NumEditorModes));
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModeTrackerTestFixture, InitialCentralStateTrackerHasNoViewportEditorModess)
|
||||
{
|
||||
EXPECT_EQ(m_viewportEditorModeTracker.GetTrackedViewportCount(), 0);
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModeTrackerTestFixture, RegisteringViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatId)
|
||||
{
|
||||
// Given a viewport not currently being tracked
|
||||
const ViewportId viewportid = 0;
|
||||
EXPECT_FALSE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr);
|
||||
|
||||
// When a mode is activated for that viewport
|
||||
const auto editorMode = ViewportEditorMode::Default;
|
||||
m_viewportEditorModeTracker.ActivateMode({ viewportid }, editorMode);
|
||||
const auto* viewportEditorModeState = m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid });
|
||||
|
||||
// Expect that viewport to now be tracked
|
||||
EXPECT_TRUE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_NE(viewportEditorModeState, nullptr);
|
||||
|
||||
// Expect the mode for that viewport to be active
|
||||
EXPECT_TRUE(viewportEditorModeState->IsModeActive(editorMode));
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModeTrackerTestFixture, UnregisteringViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatIdButReturnsError)
|
||||
{
|
||||
// Given a viewport not currently being tracked
|
||||
const ViewportId viewportid = 0;
|
||||
EXPECT_FALSE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr);
|
||||
|
||||
// When a mode is deactivated for that viewport
|
||||
const auto editorMode = ViewportEditorMode::Default;
|
||||
const auto expectedErrorMsg = AZStd::string::format(
|
||||
"Call to DeactivateMode for mode '%u' on id '%i' without precursor call to ActivateMode", static_cast<AZ::u32>(editorMode), viewportid);
|
||||
const auto result = m_viewportEditorModeTracker.DeactivateMode({ viewportid }, editorMode);
|
||||
|
||||
// Expect an error due to no precursor activation of that mode
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
EXPECT_EQ(result.GetError(), expectedErrorMsg);
|
||||
|
||||
// Expect that viewport to now be tracked
|
||||
const auto* viewportEditorModeState = m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid });
|
||||
EXPECT_TRUE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
|
||||
// Expect the mode for that viewport to be inactive
|
||||
EXPECT_NE(viewportEditorModeState, nullptr);
|
||||
EXPECT_FALSE(viewportEditorModeState->IsModeActive(editorMode));
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModeTrackerTestFixture, GettingNonExistentViewportEditorModesForIdReturnsNull)
|
||||
{
|
||||
const ViewportId viewportid = 0;
|
||||
EXPECT_FALSE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModeTrackerTestFixture, RegisteringViewportEditorModesForExistingIdInThatStateReturnsError)
|
||||
{
|
||||
// Given a viewport not currently tracked
|
||||
const ViewportId viewportid = 0;
|
||||
EXPECT_FALSE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr);
|
||||
|
||||
const auto editorMode = ViewportEditorMode::Default;
|
||||
{
|
||||
// When the mode is activated for the viewport
|
||||
const auto result = m_viewportEditorModeTracker.ActivateMode({ viewportid }, editorMode);
|
||||
|
||||
// Expect no error as there is no duplicate activation
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
|
||||
// Expect the mode to be active for the viewport
|
||||
const auto* viewportEditorModeState = m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid });
|
||||
EXPECT_TRUE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_NE(viewportEditorModeState, nullptr);
|
||||
EXPECT_TRUE(viewportEditorModeState->IsModeActive(editorMode));
|
||||
}
|
||||
{
|
||||
// When the mode is activated again for the viewport
|
||||
const auto result = m_viewportEditorModeTracker.ActivateMode({ viewportid }, editorMode);
|
||||
|
||||
// Expect an error for the duplicate activation
|
||||
const auto expectedErrorMsg = AZStd::string::format(
|
||||
"Duplicate call to ActivateMode for mode '%u' on id '%i'", static_cast<AZ::u32>(editorMode), viewportid);
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
EXPECT_EQ(result.GetError(), expectedErrorMsg);
|
||||
|
||||
// Expect the mode to still be active for the viewport
|
||||
const auto* viewportEditorModeState = m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid });
|
||||
EXPECT_TRUE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_NE(viewportEditorModeState, nullptr);
|
||||
EXPECT_TRUE(viewportEditorModeState->IsModeActive(editorMode));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ViewportEditorModeTrackerTestFixture, UnregisteringViewportEditorModesForExistingIdNotInThatStateReturnssError)
|
||||
{
|
||||
// Given a viewport not currently tracked
|
||||
const ViewportId viewportid = 0;
|
||||
EXPECT_FALSE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr);
|
||||
|
||||
const auto editorMode = ViewportEditorMode::Default;
|
||||
{
|
||||
// When the mode is activated and then deactivated for the viewport
|
||||
m_viewportEditorModeTracker.ActivateMode({ viewportid }, editorMode);
|
||||
const auto result = m_viewportEditorModeTracker.DeactivateMode({ viewportid }, editorMode);
|
||||
|
||||
// Expect no error as there is no duplicate deactivation
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
|
||||
// Expect the mode to be inctive for the viewport
|
||||
const auto* viewportEditorModeState = m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid });
|
||||
EXPECT_TRUE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_NE(viewportEditorModeState, nullptr);
|
||||
EXPECT_FALSE(viewportEditorModeState->IsModeActive(editorMode));
|
||||
}
|
||||
{
|
||||
// When the mode is deactivated again for the viewport
|
||||
const auto result = m_viewportEditorModeTracker.DeactivateMode({ viewportid }, editorMode);
|
||||
|
||||
// Expect an error for the duplicate deactivation
|
||||
const auto expectedErrorMsg = AZStd::string::format(
|
||||
"Duplicate call to DeactivateMode for mode '%u' on id '%i'", static_cast<AZ::u32>(editorMode), viewportid);
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
EXPECT_EQ(result.GetError(), expectedErrorMsg);
|
||||
|
||||
// Expect the mode to still be inactive for the viewport
|
||||
const auto* viewportEditorModeState = m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid });
|
||||
EXPECT_TRUE(m_viewportEditorModeTracker.IsViewportModeTracked({ viewportid }));
|
||||
EXPECT_NE(viewportEditorModeState, nullptr);
|
||||
EXPECT_FALSE(viewportEditorModeState->IsModeActive(editorMode));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
ViewportEditorModePublisherTestFixture,
|
||||
RegisteringViewportEditorModesForExistingIdPublishesOnViewportEditorModeRegisterEventForAllSubscribers)
|
||||
{
|
||||
// Given a set of subscribers tracking the editor modes for their exclusive viewport
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
// Expect each subscriber to have received no editor mode state changes
|
||||
EXPECT_EQ(m_editorModeHandlers[mode]->GetEditorModes().size(), 0);
|
||||
}
|
||||
|
||||
// When each editor mode is activated by the state tracker for a specific viewport
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
const ViewportId viewportId = mode;
|
||||
const ViewportEditorMode editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
m_viewportEditorModeTracker.ActivateMode({ viewportId }, editorMode);
|
||||
}
|
||||
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
// Expect only the subscribers of each viewport to have received the editor mode activated event
|
||||
const ViewportEditorMode editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
const auto& editorModes = m_editorModeHandlers[mode]->GetEditorModes();
|
||||
EXPECT_EQ(editorModes.size(), 1);
|
||||
EXPECT_EQ(editorModes.count(editorMode), 1);
|
||||
const auto& expectedEditorModeSet = editorModes.find(editorMode);
|
||||
EXPECT_NE(expectedEditorModeSet, editorModes.end());
|
||||
EXPECT_TRUE(expectedEditorModeSet->second.m_onEnter);
|
||||
EXPECT_FALSE(expectedEditorModeSet->second.m_onExit);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
ViewportEditorModePublisherTestFixture,
|
||||
UnregisteringViewportEditorModesForExistingIdPublishesOnViewportEditorModeUnregisterEventForAllSubscribers)
|
||||
{
|
||||
// Given a set of subscribers tracking the editor modes for their exclusive viewport
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
EXPECT_EQ(m_editorModeHandlers[mode]->GetEditorModes().size(), 0);
|
||||
}
|
||||
|
||||
// When each editor mode is activated deactivated by the state tracker for a specific viewport
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
const ViewportId viewportId = mode;
|
||||
const ViewportEditorMode editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
m_viewportEditorModeTracker.ActivateMode({ viewportId }, editorMode);
|
||||
m_viewportEditorModeTracker.DeactivateMode({ viewportId }, editorMode);
|
||||
}
|
||||
|
||||
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
|
||||
{
|
||||
// Expect only the subscribers of each viewport to have received the editor mode activated and deactivated event
|
||||
const ViewportEditorMode editorMode = static_cast<ViewportEditorMode>(mode);
|
||||
const auto& editorModes = m_editorModeHandlers[mode]->GetEditorModes();
|
||||
EXPECT_EQ(editorModes.size(), 1);
|
||||
EXPECT_EQ(editorModes.count(editorMode), 1);
|
||||
const auto& expectedEditorModeSet = editorModes.find(editorMode);
|
||||
EXPECT_NE(expectedEditorModeSet, editorModes.end());
|
||||
EXPECT_TRUE(expectedEditorModeSet->second.m_onEnter);
|
||||
EXPECT_TRUE(expectedEditorModeSet->second.m_onExit);
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -110,6 +110,7 @@ set(FILES
|
||||
UI/EntityPropertyEditorTests.cpp
|
||||
UndoStack.cpp
|
||||
Viewport/ClusterTests.cpp
|
||||
Viewport/ViewportEditorModeTests.cpp
|
||||
Viewport/ViewportScreenTests.cpp
|
||||
Viewport/ViewportUiClusterTests.cpp
|
||||
Viewport/ViewportUiDisplayTests.cpp
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -12,28 +12,5 @@ set_target_properties(AssetProcessor PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Platform/Mac/gui_info.plist
|
||||
RESOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Platform/Mac/Images.xcassets
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AssetProcessorAppIcon
|
||||
ENTITLEMENT_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Platform/Mac/AssetProcessorEntitlements.plist
|
||||
)
|
||||
|
||||
# We cannot use ly_add_target here because we're already including this file from inside ly_add_target
|
||||
# So we need to setup target, dependencies and install logic manually.
|
||||
add_executable(AssetProcessorDummy Platform/Mac/main_dummy.cpp)
|
||||
add_executable(AZ::AssetProcessorDummy ALIAS AssetProcessorDummy)
|
||||
|
||||
ly_target_link_libraries(AssetProcessorDummy
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzFramework)
|
||||
|
||||
ly_add_dependencies(AssetProcessor AssetProcessorDummy)
|
||||
|
||||
# Store the aliased target into a DIRECTORY property
|
||||
set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS AZ::AssetProcessorDummy)
|
||||
|
||||
# Store the directory path in a GLOBAL property so that it can be accessed
|
||||
# in the layout install logic. Skip if the directory has already been added
|
||||
get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
|
||||
if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
|
||||
set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
ly_install_add_install_path_setreg(AssetProcessor)
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>ASPR</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>AssetProcessorDummy</string>
|
||||
<string>AssetProcessor</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.Amazon.AssetProcessor</string>
|
||||
</dict>
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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/ComponentApplication.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry
|
||||
AZ::ComponentApplication::Descriptor desc;
|
||||
AZ::ComponentApplication application;
|
||||
application.Create(desc);
|
||||
|
||||
AZStd::vector<AZStd::string> envVars;
|
||||
|
||||
const char* homePath = std::getenv("HOME");
|
||||
envVars.push_back(AZStd::string::format("HOME=%s", homePath));
|
||||
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH");
|
||||
AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig);
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
dyldSearchPath.append(":");
|
||||
dyldSearchPath.append(projectModulePath.c_str());
|
||||
}
|
||||
|
||||
if (AZ::IO::FixedMaxPath installedBinariesFolder;
|
||||
settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath engineRootFolder;
|
||||
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
installedBinariesFolder = engineRootFolder / installedBinariesFolder;
|
||||
dyldSearchPath.append(":");
|
||||
dyldSearchPath.append(installedBinariesFolder.c_str());
|
||||
}
|
||||
}
|
||||
envVars.push_back(dyldSearchPath);
|
||||
}
|
||||
|
||||
AZStd::string commandArgs;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
commandArgs.append(argv[i]);
|
||||
commandArgs.append(" ");
|
||||
}
|
||||
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) };
|
||||
processPath /= "AssetProcessor";
|
||||
processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native());
|
||||
processLaunchInfo.m_commandlineParameters = commandArgs;
|
||||
processLaunchInfo.m_environmentVariables = &envVars;
|
||||
processLaunchInfo.m_showWindow = true;
|
||||
|
||||
AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
|
||||
application.Destroy();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
# This is the launcher that will be used by the O3DE_SDK.app bundle
|
||||
# generated by the cmake install process for Mac.
|
||||
if(NOT ${PAL_PLATFORM_NAME} STREQUAL Mac)
|
||||
return()
|
||||
endif()
|
||||
|
||||
ly_add_target(
|
||||
NAME O3DE_SDK EXECUTABLE
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
O3DE_SDK_files.cmake
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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/ComponentApplication.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// We need to pass in the engine path since we won't be able to find it by searching upwards.
|
||||
// We can't use any containers that use our custom allocator till after the call to ComponentApplication::Create()
|
||||
AZ::IO::FixedMaxPath processPath = AZ::Utils::GetExecutableDirectory();
|
||||
AZ::IO::FixedMaxPath enginePath = (processPath / "../Engine").LexicallyNormal();
|
||||
auto enginePathParam = AZ::SettingsRegistryInterface::FixedValueString::format(R"(--engine-path="%s")", enginePath.c_str());
|
||||
// Uses the fixed_vector deduction guide to determine the type is AZStd::fixed_vector<char*, 2>
|
||||
AZStd::fixed_vector commandLineParams{ processPath.Native().data(), enginePathParam.data() };
|
||||
|
||||
|
||||
// Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry
|
||||
AZ::ComponentApplication application(static_cast<int>(commandLineParams.size()), commandLineParams.data());
|
||||
application.Create(AZ::ComponentApplication::Descriptor());
|
||||
|
||||
AZ::IO::FixedMaxPath installedBinariesFolder;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
installedBinariesFolder = enginePath / installedBinariesFolder;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath shellPath = "/bin/sh";
|
||||
AZStd::string parameters = AZStd::string::format("-c \"export LY_CMAKE_PATH=/usr/local/bin && \"%s/python/get_python.sh\"\"", enginePath.c_str());
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo shellProcessLaunch;
|
||||
shellProcessLaunch.m_processExecutableString = AZStd::move(shellPath.Native());
|
||||
shellProcessLaunch.m_commandlineParameters = parameters;
|
||||
shellProcessLaunch.m_showWindow = true;
|
||||
shellProcessLaunch.m_workingDirectory = enginePath.String();
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE));
|
||||
shellProcess->WaitForProcessToExit(120);
|
||||
shellProcess.reset();
|
||||
|
||||
AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de";
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_processExecutableString = AZStd::move(projectManagerPath.Native());
|
||||
processLaunchInfo.m_showWindow = true;
|
||||
AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
|
||||
application.Destroy();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
O3DE_SDK_Launcher.cpp
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>O3DE_SDK</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.O3DE.O3DE_SDK</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright (c) Contributors to the Open 3D Engine Project.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -20,3 +20,4 @@ add_subdirectory(GridHub)
|
||||
add_subdirectory(Standalone)
|
||||
add_subdirectory(TestImpactFramework)
|
||||
add_subdirectory(ProjectManager)
|
||||
add_subdirectory(BundleLauncher)
|
||||
|
||||
@@ -7,14 +7,69 @@
|
||||
*/
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const
|
||||
{
|
||||
QString error = tr("Automatic building on Linux not currently supported!");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
// Attempt to use the Ninja build system if it is installed (described in the o3de documentation) if possible,
|
||||
// otherwise default to the the default for Linux (Unix Makefiles)
|
||||
auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment());
|
||||
QString cmakeGenerator = (whichNinjaResult.IsSuccess()) ? "Ninja Multi-Config" : "Unix Makefiles";
|
||||
bool compileProfileOnBuild = (whichNinjaResult.IsSuccess());
|
||||
|
||||
// On Linux the default compiler is gcc. For O3DE, it is clang, so we need to specify the version of clang that is detected
|
||||
// in order to get the compiler option.
|
||||
auto compilerOptionResult = ProjectUtils::FindSupportedCompilerForPlatform();
|
||||
if (!compilerOptionResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(compilerOptionResult.GetError());
|
||||
}
|
||||
auto clangCompilers = compilerOptionResult.GetValue().split('|');
|
||||
AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification");
|
||||
|
||||
QString clangCompilerOption = clangCompilers[0];
|
||||
QString clangPPCompilerOption = clangCompilers[1];
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QStringList generateProjectArgs = QStringList{ProjectCMakeCommand,
|
||||
"-B", ProjectBuildPathPostfix,
|
||||
"-S", ".",
|
||||
QString("-G%1").arg(cmakeGenerator),
|
||||
QString("-DCMAKE_C_COMPILER=").append(clangCompilerOption),
|
||||
QString("-DCMAKE_CXX_COMPILER=").append(clangPPCompilerOption),
|
||||
QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath)};
|
||||
if (!compileProfileOnBuild)
|
||||
{
|
||||
generateProjectArgs.append("-DCMAKE_BUILD_TYPE=profile");
|
||||
}
|
||||
return AZ::Success(generateProjectArgs);
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
{
|
||||
auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment());
|
||||
bool compileProfileOnBuild = (whichNinjaResult.IsSuccess());
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher";
|
||||
|
||||
QStringList buildProjectArgs = QStringList{ProjectCMakeCommand,
|
||||
"--build", ProjectBuildPathPostfix,
|
||||
"--target", launcherTargetName, ProjectCMakeBuildTargetEditor};
|
||||
if (compileProfileOnBuild)
|
||||
{
|
||||
buildProjectArgs.append(QStringList{"--config","profile"});
|
||||
}
|
||||
return AZ::Success(buildProjectArgs);
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const
|
||||
{
|
||||
return AZ::Success(QStringList{"kill", "-9", pidToKill});
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -6,15 +6,48 @@
|
||||
*/
|
||||
|
||||
#include <ProjectUtils.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||
// The list of clang C/C++ compiler command lines to validate on the host Linux system
|
||||
const QStringList SupportedClangCommands = {"clang-12|clang++-12"};
|
||||
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment()
|
||||
{
|
||||
// Compiler detection not supported on platform
|
||||
return AZ::Success();
|
||||
return AZ::Success(QProcessEnvironment(QProcessEnvironment::systemEnvironment()));
|
||||
}
|
||||
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform()
|
||||
{
|
||||
// Validate that cmake is installed and is in the command line
|
||||
auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment());
|
||||
if (!whichCMakeResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("CMake not found. \n\n"
|
||||
"Make sure that the minimum version of CMake is installed and available from the command prompt. "
|
||||
"Refer to the <a href='https://o3de.org/docs/welcome-guide/setup/requirements/#cmake'>O3DE requirements</a> page for more information."));
|
||||
}
|
||||
|
||||
// Look for the first compatible version of clang. The list below will contain the known clang compilers that have been tested for O3DE.
|
||||
for (const QString& supportClangCommand : SupportedClangCommands)
|
||||
{
|
||||
auto clangCompilers = supportClangCommand.split('|');
|
||||
AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification");
|
||||
|
||||
auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[0]}, QProcessEnvironment::systemEnvironment());
|
||||
auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[1]}, QProcessEnvironment::systemEnvironment());
|
||||
if (whichClangResult.IsSuccess() && whichClangPPResult.IsSuccess())
|
||||
{
|
||||
return AZ::Success(supportClangCommand);
|
||||
}
|
||||
}
|
||||
return AZ::Failure(QObject::tr("Clang not found. \n\n"
|
||||
"Make sure that the clang is installed and available from the command prompt. "
|
||||
"Refer to the <a href='https://o3de.org/docs/welcome-guide/setup/requirements/#cmake'>O3DE requirements</a> page for more information."));
|
||||
}
|
||||
|
||||
} // namespace ProjectUtils
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@@ -7,14 +7,82 @@
|
||||
*/
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
namespace Internal
|
||||
{
|
||||
QString error = tr("Automatic building on MacOS not currently supported!");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
AZ::Outcome<QString, QString> QueryInstalledCmakeFullPath()
|
||||
{
|
||||
auto environmentRequest = ProjectUtils::GetCommandLineProcessEnvironment();
|
||||
if (!environmentRequest.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(environmentRequest.GetError());
|
||||
}
|
||||
auto currentEnvironment = environmentRequest.GetValue();
|
||||
|
||||
auto queryCmakeInstalled = ProjectUtils::ExecuteCommandResult("which",
|
||||
QStringList{ProjectCMakeCommand},
|
||||
currentEnvironment);
|
||||
if (!queryCmakeInstalled.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to detect CMake on this host."));
|
||||
}
|
||||
QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0];
|
||||
return AZ::Success(cmakeInstalledPath);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const
|
||||
{
|
||||
// For Mac, we need to resolve the full path of cmake and use that in the process request. For
|
||||
// some reason, 'which' will resolve the full path, but when you just specify cmake with the same
|
||||
// environment, it is unable to resolve. To work around this, we will use 'which' to resolve the
|
||||
// full path and then use it as the command argument
|
||||
auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath();
|
||||
if (!cmakeInstalledPathQuery.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(cmakeInstalledPathQuery.GetError());
|
||||
}
|
||||
QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue();
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
|
||||
return AZ::Success(QStringList{cmakeInstalledPath,
|
||||
"-B", targetBuildPath,
|
||||
"-S", m_projectInfo.m_path,
|
||||
"-GXcode"});
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
{
|
||||
// For Mac, we need to resolve the full path of cmake and use that in the process request. For
|
||||
// some reason, 'which' will resolve the full path, but when you just specify cmake with the same
|
||||
// environment, it is unable to resolve. To work around this, we will use 'which' to resolve the
|
||||
// full path and then use it as the command argument
|
||||
auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath();
|
||||
if (!cmakeInstalledPathQuery.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(cmakeInstalledPathQuery.GetError());
|
||||
}
|
||||
|
||||
QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue();
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher";
|
||||
|
||||
return AZ::Success(QStringList{cmakeInstalledPath,
|
||||
"--build", targetBuildPath,
|
||||
"--config", "profile",
|
||||
"--target", launcherTargetName, ProjectCMakeBuildTargetEditor});
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const
|
||||
{
|
||||
return AZ::Success(QStringList{"kill", "-9", pidToKill});
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -7,15 +7,59 @@
|
||||
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment()
|
||||
{
|
||||
// Compiler detection not supported on platform
|
||||
return AZ::Success();
|
||||
// For CMake on Mac, if its installed through home-brew, then it will be installed
|
||||
// under /usr/local/bin, which may not be in the system PATH environment.
|
||||
// Add that path for the command line process so that it will be able to locate
|
||||
// a home-brew installed version of CMake
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ":/usr/local/bin";
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
return AZ::Success(currentEnvironment);
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform()
|
||||
{
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ":/usr/local/bin";
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
|
||||
// Validate that we have cmake installed first
|
||||
auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, currentEnvironment);
|
||||
if (!queryCmakeInstalled.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to detect CMake on this host."));
|
||||
}
|
||||
QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0];
|
||||
|
||||
// Query the version of the installed cmake
|
||||
auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}, currentEnvironment);
|
||||
if (!queryCmakeVersionQuery.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to determine the version of CMake on this host."));
|
||||
}
|
||||
AZ_TracePrintf("Project Manager", "Cmake version %s detected.", queryCmakeVersionQuery.GetValue().split("\n")[0].toUtf8().constData());
|
||||
|
||||
// Query for the version of xcodebuild (if installed)
|
||||
auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}, currentEnvironment);
|
||||
if (!queryCmakeInstalled.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to detect XCodeBuilder on this host."));
|
||||
}
|
||||
QString xcodeBuilderVersionNumber = queryXcodeBuildVersion.GetValue().split("\n")[0];
|
||||
AZ_TracePrintf("Project Manager", "XcodeBuilder version %s detected.", xcodeBuilderVersionNumber.toUtf8().constData());
|
||||
|
||||
|
||||
return AZ::Success(xcodeBuilderVersionNumber);
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -8,189 +8,37 @@
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const
|
||||
{
|
||||
// Check if we are trying to cancel task
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
|
||||
QFile logFile(GetLogFilePath());
|
||||
if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
|
||||
{
|
||||
QString error = tr("Failed to open log file.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
return AZ::Success(QStringList{ ProjectCMakeCommand,
|
||||
"-B", targetBuildPath,
|
||||
"-S", m_projectInfo.m_path,
|
||||
QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath),
|
||||
"-DLY_UNITY_BUILD=ON" } );
|
||||
}
|
||||
|
||||
EngineInfo engineInfo;
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
{
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher";
|
||||
|
||||
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (engineInfoResult.IsSuccess())
|
||||
{
|
||||
engineInfo = engineInfoResult.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString error = tr("Failed to get engine info.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
return AZ::Success(QStringList{ ProjectCMakeCommand,
|
||||
"--build", targetBuildPath,
|
||||
"--config", "profile",
|
||||
"--target", launcherTargetName, ProjectCMakeBuildTargetEditor });
|
||||
}
|
||||
|
||||
QTextStream logStream(&logFile);
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
logFile.close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
|
||||
// Show some kind of progress with very approximate estimates
|
||||
UpdateProgress(++m_progressEstimate);
|
||||
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
// Append cmake path to PATH incase it is missing
|
||||
QDir cmakePath(engineInfo.m_path);
|
||||
cmakePath.cd("cmake/runtime/bin");
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ";" + cmakePath.path();
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
|
||||
m_configProjectProcess = new QProcess(this);
|
||||
m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
|
||||
m_configProjectProcess->setProcessEnvironment(currentEnvironment);
|
||||
|
||||
m_configProjectProcess->start(
|
||||
"cmake",
|
||||
QStringList
|
||||
{
|
||||
"-B",
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"-S",
|
||||
m_projectInfo.m_path,
|
||||
"-G",
|
||||
"Visual Studio 16",
|
||||
"-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath,
|
||||
"-DLY_UNITY_BUILD=1"
|
||||
});
|
||||
|
||||
if (!m_configProjectProcess->waitForStarted())
|
||||
{
|
||||
QString error = tr("Configuring project failed to start.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
bool containsGeneratingDone = false;
|
||||
while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
|
||||
{
|
||||
QString configOutput = m_configProjectProcess->readAllStandardOutput();
|
||||
|
||||
if (configOutput.contains("Generating done"))
|
||||
{
|
||||
containsGeneratingDone = true;
|
||||
}
|
||||
|
||||
logStream << configOutput;
|
||||
logStream.flush();
|
||||
|
||||
UpdateProgress(qMin(++m_progressEstimate, 19));
|
||||
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
logFile.close();
|
||||
m_configProjectProcess->close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit
|
||||
|| m_configProjectProcess->exitCode() != 0
|
||||
|| !containsGeneratingDone)
|
||||
{
|
||||
QString error = tr("Configuring project failed. See log for details.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
UpdateProgress(++m_progressEstimate);
|
||||
|
||||
m_buildProjectProcess = new QProcess(this);
|
||||
m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
|
||||
m_buildProjectProcess->setProcessEnvironment(currentEnvironment);
|
||||
|
||||
m_buildProjectProcess->start(
|
||||
"cmake",
|
||||
QStringList
|
||||
{
|
||||
"--build",
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"--target",
|
||||
m_projectInfo.m_projectName + ".GameLauncher",
|
||||
"Editor",
|
||||
"--config",
|
||||
"profile"
|
||||
});
|
||||
|
||||
if (!m_buildProjectProcess->waitForStarted())
|
||||
{
|
||||
QString error = tr("Building project failed to start.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
// There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining
|
||||
m_progressEstimate = 200;
|
||||
while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
|
||||
{
|
||||
logStream << m_buildProjectProcess->readAllStandardOutput();
|
||||
logStream.flush();
|
||||
|
||||
// Show 1% progress for every 10 steps completed
|
||||
UpdateProgress(qMin(++m_progressEstimate / 10, 99));
|
||||
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
// QProcess is unable to kill its child processes so we need to ask the operating system to do that for us
|
||||
QProcess killBuildProcess;
|
||||
killBuildProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
killBuildProcess.start(
|
||||
"cmd.exe", QStringList{ "/C", "taskkill", "/pid", QString::number(m_buildProjectProcess->processId()), "/f", "/t" });
|
||||
killBuildProcess.waitForFinished();
|
||||
|
||||
logStream << "Killing Project Build.";
|
||||
logStream << killBuildProcess.readAllStandardOutput();
|
||||
m_buildProjectProcess->kill();
|
||||
logFile.close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit
|
||||
|| m_configProjectProcess->exitCode() != 0)
|
||||
{
|
||||
QString error = tr("Building project failed. See log for details.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const
|
||||
{
|
||||
return AZ::Success(QStringList { "cmd.exe", "/C", "taskkill", "/pid", pidToKill, "/f", "/t" } );
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
@@ -16,8 +18,44 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment()
|
||||
{
|
||||
// Use the engine path to insert a path for cmake
|
||||
auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (!engineInfoResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Failed to get engine info"));
|
||||
}
|
||||
auto engineInfo = engineInfoResult.GetValue();
|
||||
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
|
||||
// Append cmake path to PATH incase it is missing
|
||||
QDir cmakePath(engineInfo.m_path);
|
||||
cmakePath.cd("cmake/runtime/bin");
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ";" + cmakePath.path();
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
return AZ::Success(currentEnvironment);
|
||||
}
|
||||
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform()
|
||||
{
|
||||
// Validate that cmake is installed
|
||||
auto cmakeProcessEnvResult = GetCommandLineProcessEnvironment();
|
||||
if (!cmakeProcessEnvResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(cmakeProcessEnvResult.GetError());
|
||||
}
|
||||
auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}, cmakeProcessEnvResult.GetValue());
|
||||
if (!cmakeVersionQueryResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("CMake not found. \n\n"
|
||||
"Make sure that the minimum version of CMake is installed and available from the command prompt. "
|
||||
"Refer to the <a href='https://o3de.org/docs/welcome-guide/setup/requirements/#cmake'>O3DE requirements</a> for more information."));
|
||||
}
|
||||
|
||||
// Validate that the minimal version of visual studio is installed
|
||||
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
||||
QString programFilesPath = environment.value("ProgramFiles(x86)");
|
||||
QString vsWherePath = QDir(programFilesPath).filePath("Microsoft Visual Studio/Installer/vswhere.exe");
|
||||
@@ -25,27 +63,31 @@ namespace O3DE::ProjectManager
|
||||
QFileInfo vsWhereFile(vsWherePath);
|
||||
if (vsWhereFile.exists() && vsWhereFile.isFile())
|
||||
{
|
||||
QProcess vsWhereProcess;
|
||||
vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
QStringList vsWhereBaseArguments = QStringList{"-version",
|
||||
"16.9.2",
|
||||
"-latest",
|
||||
"-requires",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64"};
|
||||
|
||||
vsWhereProcess.start(
|
||||
vsWherePath,
|
||||
QStringList{
|
||||
"-version",
|
||||
"16.9.2",
|
||||
"-latest",
|
||||
"-requires",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"-property",
|
||||
"isComplete"
|
||||
});
|
||||
QProcess vsWhereIsCompleteProcess;
|
||||
vsWhereIsCompleteProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
if (vsWhereProcess.waitForStarted() && vsWhereProcess.waitForFinished())
|
||||
vsWhereIsCompleteProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{ "-property", "isComplete" });
|
||||
|
||||
if (vsWhereIsCompleteProcess.waitForStarted() && vsWhereIsCompleteProcess.waitForFinished())
|
||||
{
|
||||
QString vsWhereOutput(vsWhereProcess.readAllStandardOutput());
|
||||
if (vsWhereOutput.startsWith("1"))
|
||||
QString vsWhereIsCompleteOutput(vsWhereIsCompleteProcess.readAllStandardOutput());
|
||||
if (vsWhereIsCompleteOutput.startsWith("1"))
|
||||
{
|
||||
return AZ::Success();
|
||||
QProcess vsWhereCompilerVersionProcess;
|
||||
vsWhereCompilerVersionProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
vsWhereCompilerVersionProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{"-property", "catalog_productDisplayVersion"});
|
||||
|
||||
if (vsWhereCompilerVersionProcess.waitForStarted() && vsWhereCompilerVersionProcess.waitForFinished())
|
||||
{
|
||||
QString vsWhereCompilerVersionOutput(vsWhereCompilerVersionProcess.readAllStandardOutput());
|
||||
return AZ::Success(vsWhereCompilerVersionOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33301 6H12.6663V14.6667H3.33301V6ZM5.33301 7.33333H6.66634V13.3333H5.33301V7.33333ZM10.6663 7.33333H9.33301V13.3333H10.6663V7.33333Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6667 2H5.33333V3.33333H2V4.66667H14V3.33333H10.6667V2Z" fill="#E9E9E9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 430 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="2" y="2" width="1.33333" height="12" fill="white"/>
|
||||
<rect x="2" y="12.6666" width="12" height="1.33333" fill="white"/>
|
||||
<rect x="5.33301" y="9.20911" width="10.6667" height="2" transform="rotate(-45 5.33301 9.20911)" fill="white"/>
|
||||
<rect x="2" y="2" width="6.66667" height="1.33333" fill="white"/>
|
||||
<rect width="1.33333" height="6.66667" transform="matrix(-1 0 0 1 14 7.33337)" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 503 B |
@@ -35,5 +35,8 @@
|
||||
<file>Backgrounds/DefaultBackground.jpg</file>
|
||||
<file>Backgrounds/FtueBackground.jpg</file>
|
||||
<file>FeatureTagClose.svg</file>
|
||||
<file>Refresh.svg</file>
|
||||
<file>Edit.svg</file>
|
||||
<file>Delete.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -498,6 +498,18 @@ QProgressBar::chunk {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/************** Gems SubWidget **************/
|
||||
|
||||
#gemSubWidgetTitleLabel {
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#gemSubWidgetTextLabel {
|
||||
color: #DDDDDD;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/************** Gem Catalog (Inspector) **************/
|
||||
|
||||
#GemCatalogInspector {
|
||||
@@ -518,3 +530,99 @@ QProgressBar::chunk {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/************** Engine **************/
|
||||
|
||||
#engineTab::tab-bar {
|
||||
left: 60px;
|
||||
}
|
||||
|
||||
#engineTabBar::tab {
|
||||
height: 50px;
|
||||
background-color: transparent;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
#engineTabBar::tab:selected {
|
||||
border-bottom: 3px solid #94D2FF;
|
||||
color: #94D2FF;
|
||||
font-weight: 600;
|
||||
}
|
||||
#engineTabBar::tab:hover {
|
||||
color: #94D2FF;
|
||||
font-weight: 600;
|
||||
}
|
||||
#engineTabBar::tab:pressed {
|
||||
color: #66bcfa;
|
||||
}
|
||||
|
||||
#engineTopFrame {
|
||||
background-color:#1E252F;
|
||||
}
|
||||
|
||||
/************** Gem Repo **************/
|
||||
|
||||
#gemRepoHeaderLabel {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#gemRepoHeaderRefreshButton {
|
||||
background-color: transparent;
|
||||
qproperty-flat: true;
|
||||
qproperty-iconSize: 14px;
|
||||
}
|
||||
|
||||
#gemRepoHeaderAddButton {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #888888, stop: 1.0 #555555);
|
||||
qproperty-flat: true;
|
||||
margin-right:30px;
|
||||
min-width:120px;
|
||||
max-width:120px;
|
||||
min-height:24px;
|
||||
max-height:24px;
|
||||
border-radius: 3px;
|
||||
text-align:center;
|
||||
font-size:12px;
|
||||
font-weight:600;
|
||||
}
|
||||
#gemRepoHeaderAddButton:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #999999, stop: 1.0 #666666);
|
||||
}
|
||||
#gemRepoHeaderAddButton:pressed {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #555555, stop: 1.0 #777777);
|
||||
}
|
||||
|
||||
#gemRepoHeaderTable {
|
||||
background-color: transparent;
|
||||
max-height: 30px;
|
||||
}
|
||||
|
||||
#gemRepoListHeader {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#gemRepoInspector {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
/************** Gem Repo Inspector **************/
|
||||
|
||||
#gemRepoInspectorNameLabel {
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
#gemRepoInspectorBodyLabel {
|
||||
font-size: 12px;
|
||||
color: #DDDDDD;
|
||||
}
|
||||
|
||||
#gemRepoInspectorAddInfoTitleLabel {
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.046 5.85448C13.046 5.869 13.0436 5.66561 13.0436 5.68014H14.5278L12.3341 8.22252L10.1114 5.68014H11.6481C11.6481 5.66561 11.6513 5.869 11.6513 5.85448C11.6513 3.42833 9.77724 1.46707 7.46731 1.46707C6.42131 1.46707 5.46247 1.87385 4.73608 2.54213L3.8063 1.43801C4.79419 0.537286 6.07264 -0.000244141 7.46731 -0.000244141C10.5472 -0.000244141 13.046 2.6293 13.046 5.85448Z" fill="white"/>
|
||||
<path d="M1.48184 6.14503C1.48184 6.13051 1.48428 6.3339 1.48428 6.31937H0L2.1937 3.777L4.41646 6.31937H2.87975C2.87975 6.3339 2.87651 6.13051 2.87651 6.14503C2.87651 8.57118 4.7506 10.5324 7.06053 10.5324C8.10654 10.5324 9.06537 10.1257 9.79177 9.45738L10.7215 10.5615C9.73366 11.4622 8.4552 11.9998 7.06053 11.9998C3.98063 11.9998 1.48184 9.37022 1.48184 6.14503Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 886 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user