Merge branch 'development' of https://github.com/o3de/o3de into daimini/FocusMode/setup

This commit is contained in:
Danilo Aimini
2021-09-20 11:58:39 -07:00
185 changed files with 3498 additions and 809 deletions
@@ -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
@@ -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,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(
@@ -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
{
@@ -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
{
@@ -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;
@@ -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
@@ -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
@@ -541,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
@@ -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