Merge branch 'development' into LYN-5265_state_tracker_impl
This commit is contained in:
@@ -77,9 +77,11 @@ namespace AZ
|
||||
public:
|
||||
/**
|
||||
* Allocator used by the EBus.
|
||||
* The default setting is AZStd::allocator, which uses AZ::SystemAllocator.
|
||||
* The default setting is Internal EBusEnvironmentAllocator
|
||||
* EBus code stores their Context instances in static memory
|
||||
* Therfore the configured allocator must last as long as the EBus in a module
|
||||
*/
|
||||
using AllocatorType = AZStd::allocator;
|
||||
using AllocatorType = AZ::Internal::EBusEnvironmentAllocator;
|
||||
|
||||
/**
|
||||
* Defines how many handlers can connect to an address on the EBus
|
||||
|
||||
@@ -34,7 +34,9 @@ namespace AZ
|
||||
friend IAllocator;
|
||||
friend class AllocatorBase;
|
||||
friend class Debug::AllocationRecords;
|
||||
friend class AZ::Internal::EnvironmentVariableHolder<AllocatorManager>;
|
||||
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
|
||||
->AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
|
||||
template<typename T> constexpr friend void AZStd::destroy_at(T*);
|
||||
|
||||
public:
|
||||
typedef AZStd::function<void (IAllocator* allocator, size_t /*byteSize*/, size_t /*alignment*/, int/* flags*/, const char* /*name*/, const char* /*fileName*/, int lineNum /*=0*/)> OutOfMemoryCBType;
|
||||
|
||||
@@ -251,16 +251,15 @@ namespace AZ
|
||||
class EnvironmentVariableHolder
|
||||
: public EnvironmentVariableHolderBase
|
||||
{
|
||||
void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor<T> */)
|
||||
{
|
||||
memset(&m_value, 0, sizeof(T));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor<T> */, Args&&... args)
|
||||
void ConstructImpl(Args&&... args)
|
||||
{
|
||||
// Construction of non-trivial types is left up to the type's constructor.
|
||||
new(&m_value) T(AZStd::forward<Args>(args)...);
|
||||
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
|
||||
#if __cpp_lib_launder
|
||||
AZStd::construct_at(std::launder(reinterpret_cast<T*>(&m_value)), AZStd::forward<Args>(args)...);
|
||||
#else
|
||||
AZStd::construct_at(reinterpret_cast<T*>(&m_value), AZStd::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct)
|
||||
{
|
||||
@@ -274,10 +273,12 @@ namespace AZ
|
||||
AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!");
|
||||
self->m_isConstructed = false;
|
||||
self->m_moduleOwner = nullptr;
|
||||
if constexpr(!AZStd::is_trivially_destructible_v<T>)
|
||||
{
|
||||
reinterpret_cast<T*>(&self->m_value)->~T();
|
||||
}
|
||||
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
|
||||
#if __cpp_lib_launder
|
||||
AZStd::destroy_at(std::launder(reinterpret_cast<T*>(&self->m_value)));
|
||||
#else
|
||||
AZStd::destroy_at(reinterpret_cast<T*>(&self->m_value));
|
||||
#endif
|
||||
}
|
||||
public:
|
||||
EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator)
|
||||
@@ -303,24 +304,13 @@ namespace AZ
|
||||
UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease);
|
||||
}
|
||||
|
||||
void Construct()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
|
||||
if (!m_isConstructed)
|
||||
{
|
||||
ConstructImpl(AZStd::is_trivially_constructible<T>{});
|
||||
m_isConstructed = true;
|
||||
m_moduleOwner = Environment::GetModuleId();
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void Construct(Args&&... args)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
|
||||
if (!m_isConstructed)
|
||||
{
|
||||
ConstructImpl(typename AZStd::false_type(), AZStd::forward<Args>(args)...);
|
||||
ConstructImpl(AZStd::forward<Args>(args)...);
|
||||
m_isConstructed = true;
|
||||
m_moduleOwner = Environment::GetModuleId();
|
||||
}
|
||||
@@ -333,7 +323,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// variable storage
|
||||
typename AZStd::aligned_storage<sizeof(T), AZStd::alignment_of<T>::value>::type m_value;
|
||||
AZStd::aligned_storage_for_t<T> m_value;
|
||||
static int s_moduleUseCount;
|
||||
};
|
||||
|
||||
@@ -468,6 +458,11 @@ namespace AZ
|
||||
Get() = value;
|
||||
}
|
||||
|
||||
void Set(T&& value)
|
||||
{
|
||||
Get() = AZStd::move(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return IsValid();
|
||||
|
||||
@@ -42,7 +42,10 @@ namespace AZ
|
||||
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
if (AZ::NameDictionary::IsReady())
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,23 +21,18 @@ namespace AZ
|
||||
|
||||
namespace NameDictionaryInternal
|
||||
{
|
||||
static AZ::EnvironmentVariable<NameDictionary*> s_instance = nullptr;
|
||||
static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
|
||||
}
|
||||
|
||||
void NameDictionary::Create()
|
||||
{
|
||||
using namespace NameDictionaryInternal;
|
||||
|
||||
AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!");
|
||||
AZ_Assert(!s_instance, "NameDictionary already created!");
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
if (!s_instance.Get())
|
||||
{
|
||||
s_instance.Set(aznew NameDictionary());
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +41,7 @@ namespace AZ
|
||||
using namespace NameDictionaryInternal;
|
||||
|
||||
AZ_Assert(s_instance, "NameDictionary not created!");
|
||||
delete (*s_instance);
|
||||
*s_instance = nullptr;
|
||||
s_instance.Reset();
|
||||
}
|
||||
|
||||
bool NameDictionary::IsReady()
|
||||
@@ -56,10 +50,10 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
return s_instance && *s_instance;
|
||||
return s_instance.IsConstructed();
|
||||
}
|
||||
|
||||
NameDictionary& NameDictionary::Instance()
|
||||
@@ -68,12 +62,12 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet.");
|
||||
AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
|
||||
|
||||
return *(*s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
NameDictionary::NameDictionary()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
|
||||
namespace MaterialEditor
|
||||
namespace MaterialEditor
|
||||
{
|
||||
class MaterialEditorCoreComponent;
|
||||
}
|
||||
@@ -34,14 +34,14 @@ namespace AZ
|
||||
{
|
||||
class NameData;
|
||||
};
|
||||
|
||||
|
||||
//! Maintains a list of unique strings for Name objects.
|
||||
//! The main benefit of the Name system is very fast string equality comparison, because every
|
||||
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
|
||||
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
|
||||
//! collide. It also saves memory by removing duplicate strings.
|
||||
//!
|
||||
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
|
||||
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
|
||||
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
|
||||
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
|
||||
//! that already exist.
|
||||
class NameDictionary final
|
||||
{
|
||||
@@ -51,7 +51,10 @@ namespace AZ
|
||||
friend Name;
|
||||
friend Internal::NameData;
|
||||
friend UnitTest::NameDictionaryTester;
|
||||
|
||||
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
|
||||
-> AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
|
||||
template<typename T> constexpr friend void AZStd::destroy_at(T*);
|
||||
|
||||
public:
|
||||
|
||||
static void Create();
|
||||
@@ -62,7 +65,7 @@ namespace AZ
|
||||
|
||||
//! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared.
|
||||
//! Otherwise, it is added to the internal dictionary.
|
||||
//!
|
||||
//!
|
||||
//! @param name The name to resolve against the dictionary.
|
||||
//! @return A Name instance holding a dictionary entry associated with the provided raw string.
|
||||
Name MakeName(AZStd::string_view name);
|
||||
@@ -84,13 +87,13 @@ namespace AZ
|
||||
// Attempts to release the name from the dictionary, but checks to make sure
|
||||
// a reference wasn't taken by another thread.
|
||||
void TryReleaseName(Name::Hash hash);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculates a hash for the provided name string.
|
||||
// Does not attempt to resolve hash collisions; that is handled elsewhere.
|
||||
Name::Hash CalcHash(AZStd::string_view name);
|
||||
|
||||
|
||||
AZStd::unordered_map<Name::Hash, Internal::NameData*> m_dictionary;
|
||||
mutable AZStd::shared_mutex m_sharedMutex;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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/allocator_stateless.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
stateless_allocator::stateless_allocator(const char* name)
|
||||
: m_name(name) {}
|
||||
|
||||
const char* stateless_allocator::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void stateless_allocator::set_name(const char* name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
auto stateless_allocator::allocate(size_type byteSize) -> pointer_type
|
||||
{
|
||||
return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0);
|
||||
}
|
||||
|
||||
auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type
|
||||
{
|
||||
pointer_type address = AZ_OS_MALLOC(byteSize, alignment);
|
||||
|
||||
if (address == nullptr)
|
||||
{
|
||||
AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n");
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
void stateless_allocator::deallocate(pointer_type ptr, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
auto stateless_allocator::max_size() const -> size_type
|
||||
{
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
stateless_allocator stateless_allocator::select_on_container_copy_construction() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto stateless_allocator::resize(pointer_type, size_type) -> size_type
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_lock_free()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_stale_read_allowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_delayed_recycling()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
bool operator==(const stateless_allocator&, const stateless_allocator&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const stateless_allocator&, const stateless_allocator&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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/std/base.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/RTTI/TypeInfoSimple.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
class stateless_allocator
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}");
|
||||
|
||||
using value_type = uint8_t;
|
||||
using pointer_type = void*;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using allow_memory_leaks = AZStd::true_type;
|
||||
|
||||
stateless_allocator(const char* name = "AZStd::stateless_allocator");
|
||||
stateless_allocator(const stateless_allocator& rhs) = default;
|
||||
|
||||
stateless_allocator& operator=(const stateless_allocator& rhs) = default;
|
||||
|
||||
const char* get_name() const;
|
||||
void set_name(const char* name);
|
||||
|
||||
pointer_type allocate(size_type byteSize);
|
||||
pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0);
|
||||
void deallocate(pointer_type ptr, size_type alignment);
|
||||
void deallocate(pointer_type ptr, size_type byteSize, size_type alignment);
|
||||
|
||||
// max_size actually returns the true maximum size of a single allocation
|
||||
size_type max_size() const;
|
||||
|
||||
// Returns a copy of the allocator
|
||||
stateless_allocator select_on_container_copy_construction() const;
|
||||
|
||||
//! extensions
|
||||
size_type resize(pointer_type ptr, size_type newSize);
|
||||
|
||||
bool is_lock_free();
|
||||
bool is_stale_read_allowed();
|
||||
bool is_delayed_recycling();
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
};
|
||||
|
||||
bool operator==(const stateless_allocator& left, const stateless_allocator& right);
|
||||
bool operator!=(const stateless_allocator& left, const stateless_allocator& right);
|
||||
}
|
||||
@@ -12,6 +12,8 @@ set(FILES
|
||||
allocator.h
|
||||
allocator_ref.h
|
||||
allocator_stack.h
|
||||
allocator_stateless.cpp
|
||||
allocator_stateless.h
|
||||
allocator_static.h
|
||||
allocator_traits.h
|
||||
any.h
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// alias std::pointer_traits into the AZStd::namespace
|
||||
// alias std::pointer_traits into the AZStd::namespace
|
||||
using std::pointer_traits;
|
||||
|
||||
//! Bring the names of uninitialized_default_construct and
|
||||
@@ -229,7 +229,7 @@ namespace AZStd
|
||||
//! `new (declval<void*>()) T(declval<Args>()...)` is well-formed
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto construct_at(T* ptr, Args&&... args)
|
||||
-> enable_if_t<is_void_v<void_t<decltype(new (declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
|
||||
-> enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
|
||||
{
|
||||
return ::new (ptr) T(AZStd::forward<Args>(args)...);
|
||||
}
|
||||
@@ -487,7 +487,7 @@ namespace AZStd
|
||||
{
|
||||
//! Implements the C++17 uninitialized_move function
|
||||
//! The functions accepts two input iterators and an output iterator
|
||||
//! It performs an AZStd::move on each in in the range of the input iterator
|
||||
//! It performs an AZStd::move on each in in the range of the input iterator
|
||||
//! and stores the result in location pointed by the output iterator
|
||||
template <typename InputIt, typename ForwardIt>
|
||||
ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
|
||||
+13
-1
@@ -11,6 +11,8 @@
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Outcome object that returns an error message in case of failure to allow caller to handle internal errors.
|
||||
@@ -43,7 +45,7 @@ namespace AzToolsFramework
|
||||
//! @param entityId The entityId whose open state will be set.
|
||||
//! @param open True if the container should be opened, false if it should be closed.
|
||||
//! @return An error message if the operation was invalid, success otherwise.
|
||||
virtual ContainerEntityOperationResult SetContainerOpenState(AZ::EntityId entityId, bool open) = 0;
|
||||
virtual ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) = 0;
|
||||
|
||||
//! If the entity id provided is registered as a container, it returns whether it's open.
|
||||
//! @note the default value for non-containers is true, so this function can be called without
|
||||
@@ -56,6 +58,16 @@ namespace AzToolsFramework
|
||||
//! @return The highest closed entity container id if any, or entityId otherwise.
|
||||
virtual AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const = 0;
|
||||
|
||||
//! Clears all open state information for Container Entities for the EntityContextId provided.
|
||||
//! Used when context is switched, for example in the case of a new root prefab being loaded
|
||||
//! in place of an old one.
|
||||
//! @note Clear is meant to be called when no container is registered for the context provided.
|
||||
//! @return An error message if any container was registered for the context, success otherwise.
|
||||
virtual ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) = 0;
|
||||
|
||||
//! Returns true if one of the ancestors of entityId is a closed container entity.
|
||||
virtual bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+82
-2
@@ -10,16 +10,19 @@
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ContainerEntitySystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<ContainerEntityInterface>::Register(this);
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void ContainerEntitySystemComponent::Deactivate()
|
||||
{
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
AZ::Interface<ContainerEntityInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
@@ -63,7 +66,7 @@ namespace AzToolsFramework
|
||||
return m_containers.contains(entityId);
|
||||
}
|
||||
|
||||
ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpenState(AZ::EntityId entityId, bool open)
|
||||
ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpen(AZ::EntityId entityId, bool open)
|
||||
{
|
||||
if (!IsContainer(entityId))
|
||||
{
|
||||
@@ -87,7 +90,7 @@ namespace AzToolsFramework
|
||||
|
||||
bool ContainerEntitySystemComponent::IsContainerOpen(AZ::EntityId entityId) const
|
||||
{
|
||||
// If the entity is not a container, it should behave as open.
|
||||
// Non-container entities behave the same as open containers. This saves the caller an additional check.
|
||||
if(!m_containers.contains(entityId))
|
||||
{
|
||||
return true;
|
||||
@@ -99,8 +102,17 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId ContainerEntitySystemComponent::FindHighestSelectableEntity(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
return entityId;
|
||||
}
|
||||
|
||||
// Return the highest closed container, or the entity if none is found.
|
||||
AZ::EntityId highestSelectableEntityId = entityId;
|
||||
|
||||
// Skip the queried entity, as we only want to check its ancestors.
|
||||
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
// Go up the hierarchy until you hit the root
|
||||
while (entityId.IsValid())
|
||||
{
|
||||
@@ -117,4 +129,72 @@ namespace AzToolsFramework
|
||||
return highestSelectableEntityId;
|
||||
}
|
||||
|
||||
void ContainerEntitySystemComponent::OnEntityStreamLoadSuccess()
|
||||
{
|
||||
// We don't yet support multiple entity contexts, so just use the default.
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
Clear(editorEntityContextId);
|
||||
}
|
||||
|
||||
ContainerEntityOperationResult ContainerEntitySystemComponent::Clear(AzFramework::EntityContextId entityContextId)
|
||||
{
|
||||
// We don't yet support multiple entity contexts, so only clear the default.
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
if (entityContextId != editorEntityContextId)
|
||||
{
|
||||
return AZ::Failure(AZStd::string(
|
||||
"Error in ContainerEntitySystemComponent::Clear - cannot clear non-default Entity Context!"));
|
||||
}
|
||||
|
||||
if (!m_containers.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(
|
||||
"Error in ContainerEntitySystemComponent::Clear - cannot clear container states if entities are still registered!"));
|
||||
}
|
||||
|
||||
m_openContainers.clear();
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
bool ContainerEntitySystemComponent::IsUnderClosedContainerEntity(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip the queried entity, as we only want to check its ancestors.
|
||||
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
// Go up the hierarchy until you hit the root.
|
||||
while (entityId.IsValid())
|
||||
{
|
||||
if (!IsContainerOpen(entityId))
|
||||
{
|
||||
// One of the ancestors is a container and it's closed.
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
if (parentId == entityId)
|
||||
{
|
||||
// In some circumstances, querying a root level entity with GetParentId will return
|
||||
// the entity itself instead of an invalid entityId.
|
||||
break;
|
||||
}
|
||||
|
||||
entityId = parentId;
|
||||
}
|
||||
|
||||
// All ancestors are either regular entities or open containers.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+8
-1
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace AzToolsFramework
|
||||
class ContainerEntitySystemComponent final
|
||||
: public AZ::Component
|
||||
, private ContainerEntityInterface
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ContainerEntitySystemComponent, "{74349759-B36B-44A6-B89F-F45D7111DD11}");
|
||||
@@ -42,9 +44,14 @@ namespace AzToolsFramework
|
||||
ContainerEntityOperationResult RegisterEntityAsContainer(AZ::EntityId entityId) override;
|
||||
ContainerEntityOperationResult UnregisterEntityAsContainer(AZ::EntityId entityId) override;
|
||||
bool IsContainer(AZ::EntityId entityId) const override;
|
||||
ContainerEntityOperationResult SetContainerOpenState(AZ::EntityId entityId, bool open) override;
|
||||
ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) override;
|
||||
bool IsContainerOpen(AZ::EntityId entityId) const override;
|
||||
AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override;
|
||||
ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override;
|
||||
bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override;
|
||||
|
||||
// EditorEntityContextNotificationBus overrides ...
|
||||
void OnEntityStreamLoadSuccess() override;
|
||||
|
||||
private:
|
||||
AZStd::unordered_set<AZ::EntityId> m_containers; //!< All entities in this set are containers.
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Commands/EntityStateCommand.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Slice/SliceMetadataEntityContextBus.h>
|
||||
#include <AzToolsFramework/Slice/SliceUtilities.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
|
||||
@@ -588,15 +590,40 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
// Detect if the Entity is Visible
|
||||
bool visible = false;
|
||||
EditorEntityInfoRequestBus::EventResult(
|
||||
visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible);
|
||||
|
||||
bool locked = false;
|
||||
EditorEntityInfoRequestBus::EventResult(
|
||||
locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
|
||||
if (!visible)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return visible && !locked;
|
||||
// Detect if the Entity is Locked
|
||||
bool locked = false;
|
||||
EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
|
||||
|
||||
if (locked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect if the Entity is part of the Editor Focus
|
||||
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
!focusModeInterface->IsInFocusSubTree(entityId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect if the Entity is a descendant of a closed container
|
||||
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
containerEntityInterface->IsUnderClosedContainerEntity(entityId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SetEntityLockStateRecursively(
|
||||
|
||||
@@ -196,6 +196,9 @@ namespace AzToolsFramework::Prefab
|
||||
Initialize();
|
||||
}
|
||||
|
||||
// Clear the old focus vector
|
||||
m_instanceFocusVector.clear();
|
||||
|
||||
// Focus on the root prefab (AZ::EntityId() will default to it)
|
||||
FocusOnOwningPrefab(AZ::EntityId());
|
||||
}
|
||||
@@ -230,7 +233,7 @@ namespace AzToolsFramework::Prefab
|
||||
{
|
||||
if (instance.has_value())
|
||||
{
|
||||
m_containerEntityInterface->SetContainerOpenState(instance->get().GetContainerEntityId(), true);
|
||||
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,7 +244,7 @@ namespace AzToolsFramework::Prefab
|
||||
{
|
||||
if (instance.has_value())
|
||||
{
|
||||
m_containerEntityInterface->SetContainerOpenState(instance->get().GetContainerEntityId(), false);
|
||||
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,9 +257,10 @@ namespace AzToolsFramework
|
||||
|
||||
// Select Container Entity
|
||||
{
|
||||
auto selectionUndo = aznew SelectionCommand({ containerEntityId }, "Select Prefab Container Entity");
|
||||
const EntityIdList selectedEntities = EntityIdList{ containerEntityId };
|
||||
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Select Prefab Container Entity");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1097,7 +1098,7 @@ namespace AzToolsFramework
|
||||
// Select the duplicated entities/instances
|
||||
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace AzToolsFramework
|
||||
typedef SharedThumbnailKey BusIdType;
|
||||
|
||||
//! notify product thumbnail that the data is ready
|
||||
virtual void ThumbnailRendered(QPixmap& thumbnailImage) = 0;
|
||||
virtual void ThumbnailRendered(const QPixmap& thumbnailImage) = 0;
|
||||
//! notify product thumbnail that the thumbnail failed to render
|
||||
virtual void ThumbnailFailedToRender() = 0;
|
||||
};
|
||||
|
||||
+8
-1
@@ -137,6 +137,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
EditorContextMenuBus::Handler::BusConnect();
|
||||
EditorEventsBus::Handler::BusConnect();
|
||||
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabIntegrationInterface>::Register(this);
|
||||
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension);
|
||||
@@ -147,6 +148,7 @@ namespace AzToolsFramework
|
||||
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect();
|
||||
AZ::Interface<PrefabIntegrationInterface>::Unregister(this);
|
||||
PrefabInstanceContainerNotificationBus::Handler::BusDisconnect();
|
||||
EditorEventsBus::Handler::BusDisconnect();
|
||||
EditorContextMenuBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -313,6 +315,11 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::OnEscape()
|
||||
{
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
|
||||
{
|
||||
auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position);
|
||||
@@ -1393,7 +1400,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<AzQtComponents::Card> PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId)
|
||||
{
|
||||
FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow());
|
||||
FlowLayout* unsavedPrefabsLayout = new FlowLayout(nullptr);
|
||||
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId);
|
||||
|
||||
|
||||
+8
-4
@@ -51,6 +51,7 @@ namespace AzToolsFramework
|
||||
|
||||
class PrefabIntegrationManager final
|
||||
: public EditorContextMenuBus::Handler
|
||||
, public EditorEventsBus::Handler
|
||||
, public AssetBrowser::AssetBrowserSourceDropBus::Handler
|
||||
, public PrefabInstanceContainerNotificationBus::Handler
|
||||
, public PrefabIntegrationInterface
|
||||
@@ -64,19 +65,22 @@ namespace AzToolsFramework
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// EditorContextMenuBus...
|
||||
// EditorContextMenuBus overrides ...
|
||||
int GetMenuPosition() const override;
|
||||
AZStd::string GetMenuIdentifier() const override;
|
||||
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
|
||||
|
||||
// EntityOutlinerSourceDropHandlingBus...
|
||||
// EditorEventsBus overrides ...
|
||||
void OnEscape();
|
||||
|
||||
// EntityOutlinerSourceDropHandlingBus overrides ...
|
||||
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;
|
||||
|
||||
// PrefabInstanceContainerNotificationBus...
|
||||
// PrefabInstanceContainerNotificationBus overrides ...
|
||||
void OnPrefabComponentActivate(AZ::EntityId entityId) override;
|
||||
void OnPrefabComponentDeactivate(AZ::EntityId entityId) override;
|
||||
|
||||
// PrefabIntegrationInterface...
|
||||
// PrefabIntegrationInterface overrides ...
|
||||
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override;
|
||||
int ExecuteClosePrefabDialog(TemplateId templateId) override;
|
||||
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
|
||||
|
||||
+53
-4
@@ -28,6 +28,9 @@ AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
|
||||
#include <QTableView>
|
||||
#include <QHeaderView>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QByteArray>
|
||||
#include <QDataStream>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
@@ -1230,6 +1233,16 @@ namespace AzToolsFramework
|
||||
return m_showThumbnailDropDownButton;
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetCustomThumbnailEnabled(bool enabled)
|
||||
{
|
||||
m_thumbnail->SetCustomThumbnailEnabled(enabled);
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap)
|
||||
{
|
||||
m_thumbnail->SetCustomThumbnailPixmap(pixmap);
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetThumbnailCallback(EditCallbackType* editNotifyCallback)
|
||||
{
|
||||
m_thumbnailCallback = editNotifyCallback;
|
||||
@@ -1356,15 +1369,27 @@ namespace AzToolsFramework
|
||||
GUI->SetClearNotifyCallback(nullptr);
|
||||
}
|
||||
}
|
||||
else if (attrib == AZ_CRC("BrowseIcon", 0x507d7a4f))
|
||||
else if (attrib == AZ_CRC_CE("BrowseIcon"))
|
||||
{
|
||||
AZStd::string iconPath;
|
||||
attrValue->Read<AZStd::string>(iconPath);
|
||||
|
||||
if (!iconPath.empty())
|
||||
if (attrValue->Read<AZStd::string>(iconPath) && !iconPath.empty())
|
||||
{
|
||||
GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// A QPixmap object can't be assigned directly via an attribute.
|
||||
// This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap.
|
||||
AZStd::vector<char> pixmapBuffer;
|
||||
if (attrValue->Read<AZStd::vector<char>>(pixmapBuffer) && !pixmapBuffer.empty())
|
||||
{
|
||||
QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast<int>(pixmapBuffer.size()));
|
||||
QDataStream stream(&pixmapBytes, QIODevice::ReadOnly);
|
||||
QPixmap pixmap;
|
||||
stream >> pixmap;
|
||||
GUI->SetBrowseButtonIcon(pixmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attrib == AZ_CRC_CE("BrowseButtonEnabled"))
|
||||
{
|
||||
@@ -1390,6 +1415,30 @@ namespace AzToolsFramework
|
||||
GUI->SetShowThumbnail(showThumbnail);
|
||||
}
|
||||
}
|
||||
else if (attrib == AZ_CRC_CE("ThumbnailIcon"))
|
||||
{
|
||||
AZStd::string iconPath;
|
||||
if (attrValue->Read<AZStd::string>(iconPath) && !iconPath.empty())
|
||||
{
|
||||
GUI->SetCustomThumbnailEnabled(true);
|
||||
GUI->SetCustomThumbnailPixmap(QPixmap::fromImage(QImage(iconPath.c_str())));
|
||||
}
|
||||
else
|
||||
{
|
||||
// A QPixmap object can't be assigned directly via an attribute.
|
||||
// This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap.
|
||||
AZStd::vector<char> pixmapBuffer;
|
||||
if (attrValue->Read<AZStd::vector<char>>(pixmapBuffer) && !pixmapBuffer.empty())
|
||||
{
|
||||
QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast<int>(pixmapBuffer.size()));
|
||||
QDataStream stream(&pixmapBytes, QIODevice::ReadOnly);
|
||||
QPixmap pixmap;
|
||||
stream >> pixmap;
|
||||
GUI->SetCustomThumbnailEnabled(true);
|
||||
GUI->SetCustomThumbnailPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attrib == AZ_CRC_CE("ThumbnailCallback"))
|
||||
{
|
||||
PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast<PropertyAssetCtrl::EditCallbackType*>(attrValue->GetAttribute());
|
||||
|
||||
+5
@@ -217,12 +217,17 @@ namespace AzToolsFramework
|
||||
void SetHideProductFilesInAssetPicker(bool hide);
|
||||
bool GetHideProductFilesInAssetPicker() const;
|
||||
|
||||
// Enable and configure a thumbnail widget that displays an asset preview and dropdown arrow for a dropdown menu
|
||||
void SetShowThumbnail(bool enable);
|
||||
bool GetShowThumbnail() const;
|
||||
void SetShowThumbnailDropDownButton(bool enable);
|
||||
bool GetShowThumbnailDropDownButton() const;
|
||||
void SetThumbnailCallback(EditCallbackType* editNotifyCallback);
|
||||
|
||||
// If enabled, replaces the thumbnail widget content with a custom pixmap
|
||||
void SetCustomThumbnailEnabled(bool enabled);
|
||||
void SetCustomThumbnailPixmap(const QPixmap& pixmap);
|
||||
|
||||
void SetSelectedAssetID(const AZ::Data::AssetId& newID);
|
||||
void SetCurrentAssetType(const AZ::Data::AssetType& newType);
|
||||
void SetSelectedAssetID(const AZ::Data::AssetId& newID, const AZ::Data::AssetType& newType);
|
||||
|
||||
+2
-3
@@ -38,7 +38,7 @@ namespace AzToolsFramework
|
||||
static bool UnsignedToolTip(QWidget* widget, QString& toolTipString);
|
||||
};
|
||||
|
||||
//! Base class for integer widget handlers to provide functionality independant
|
||||
//! Base class for integer widget handlers to provide functionality independent
|
||||
//! of widget type.
|
||||
//! @tparam ValueType The integer primitive type of the handler.
|
||||
//! @tparam PropertyControl The widget type of the handler.
|
||||
@@ -167,8 +167,7 @@ namespace AzToolsFramework
|
||||
PropertyControl* newCtrl = aznew PropertyControl(pParent);
|
||||
this->connect(newCtrl, &PropertyControl::valueChanged, this, [newCtrl]()
|
||||
{
|
||||
EBUS_EVENT(PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl);
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Events::RequestWrite, newCtrl);
|
||||
});
|
||||
// note: Qt automatically disconnects objects from each other when either end is destroyed, no need to worry about delete.
|
||||
|
||||
|
||||
-5
@@ -98,11 +98,6 @@ namespace AzToolsFramework
|
||||
QWidget* IntSpinBoxHandler<ValueType>::CreateGUI(QWidget* parent)
|
||||
{
|
||||
PropertyIntSpinCtrl* newCtrl = static_cast<PropertyIntSpinCtrl*>(BaseHandler::CreateGUI(parent));
|
||||
this->connect(newCtrl, &PropertyIntSpinCtrl::valueChanged, [newCtrl]()
|
||||
{
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl);
|
||||
});
|
||||
|
||||
return newCtrl;
|
||||
}
|
||||
|
||||
|
||||
+84
-47
@@ -7,75 +7,117 @@
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer<QRawFontPrivate>' needs to have dll-interface to be used by clients of class 'QRawFont'
|
||||
// 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning)
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
#include <QEvent>
|
||||
#include <QPainter>
|
||||
#include <UI/UICore/AspectRatioAwarePixmapWidget.hxx>
|
||||
#include <Thumbnails/ThumbnailWidget.h>
|
||||
|
||||
// 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer<QRawFontPrivate>' needs to have dll-interface to be used by clients of class
|
||||
// 'QRawFont' 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning)
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <Thumbnails/ThumbnailWidget.h>
|
||||
#include <UI/UICore/AspectRatioAwarePixmapWidget.hxx>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
#include "ThumbnailPropertyCtrl.h"
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
ThumbnailPropertyCtrl::ThumbnailPropertyCtrl(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout* pLayout = new QHBoxLayout();
|
||||
pLayout->setContentsMargins(0, 0, 0, 0);
|
||||
pLayout->setSpacing(0);
|
||||
|
||||
m_thumbnail = new Thumbnailer::ThumbnailWidget(this);
|
||||
m_thumbnail->setFixedSize(QSize(24, 24));
|
||||
|
||||
m_thumbnailEnlarged = new Thumbnailer::ThumbnailWidget(this);
|
||||
m_thumbnailEnlarged->setFixedSize(QSize(180, 180));
|
||||
m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
|
||||
|
||||
m_customThumbnail = new QLabel(this);
|
||||
m_customThumbnail->setFixedSize(QSize(24, 24));
|
||||
m_customThumbnail->setScaledContents(true);
|
||||
|
||||
m_customThumbnailEnlarged = new QLabel(this);
|
||||
m_customThumbnailEnlarged->setFixedSize(QSize(180, 180));
|
||||
m_customThumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
|
||||
m_customThumbnailEnlarged->setScaledContents(true);
|
||||
|
||||
m_dropDownArrow = new AspectRatioAwarePixmapWidget(this);
|
||||
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png"));
|
||||
m_dropDownArrow->setFixedSize(QSize(8, 24));
|
||||
ShowDropDownArrow(false);
|
||||
|
||||
m_emptyThumbnail = new QLabel(this);
|
||||
m_emptyThumbnail->setPixmap(QPixmap(":/stylesheet/img/line.png"));
|
||||
m_emptyThumbnail->setFixedSize(QSize(24, 24));
|
||||
|
||||
pLayout->addWidget(m_emptyThumbnail);
|
||||
QHBoxLayout* pLayout = new QHBoxLayout();
|
||||
pLayout->setContentsMargins(0, 0, 0, 0);
|
||||
pLayout->setSpacing(0);
|
||||
pLayout->addWidget(m_thumbnail);
|
||||
pLayout->addWidget(m_customThumbnail);
|
||||
pLayout->addWidget(m_emptyThumbnail);
|
||||
pLayout->addSpacing(4);
|
||||
pLayout->addWidget(m_dropDownArrow);
|
||||
pLayout->addSpacing(4);
|
||||
|
||||
setLayout(pLayout);
|
||||
|
||||
ShowDropDownArrow(false);
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName)
|
||||
{
|
||||
m_key = key;
|
||||
m_emptyThumbnail->setVisible(false);
|
||||
m_thumbnail->SetThumbnailKey(key, contextName);
|
||||
if (m_customThumbnailEnabled)
|
||||
{
|
||||
ClearThumbnail();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_key = key;
|
||||
m_thumbnail->SetThumbnailKey(m_key, contextName);
|
||||
m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName);
|
||||
}
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::ClearThumbnail()
|
||||
{
|
||||
m_emptyThumbnail->setVisible(true);
|
||||
m_key.clear();
|
||||
m_thumbnail->ClearThumbnail();
|
||||
m_thumbnailEnlarged->ClearThumbnail();
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::ShowDropDownArrow(bool visible)
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
setFixedSize(QSize(40, 24));
|
||||
}
|
||||
else
|
||||
{
|
||||
setFixedSize(QSize(24, 24));
|
||||
}
|
||||
setFixedSize(QSize(visible ? 40 : 24, 24));
|
||||
m_dropDownArrow->setVisible(visible);
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::SetCustomThumbnailEnabled(bool enabled)
|
||||
{
|
||||
m_customThumbnailEnabled = enabled;
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap)
|
||||
{
|
||||
m_customThumbnail->setPixmap(pixmap);
|
||||
m_customThumbnailEnlarged->setPixmap(pixmap);
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::UpdateVisibility()
|
||||
{
|
||||
m_thumbnail->setVisible(m_key && !m_customThumbnailEnabled);
|
||||
m_thumbnailEnlarged->setVisible(false);
|
||||
|
||||
m_customThumbnail->setVisible(m_customThumbnailEnabled);
|
||||
m_customThumbnailEnlarged->setVisible(false);
|
||||
|
||||
m_emptyThumbnail->setVisible(!m_key && !m_customThumbnailEnabled);
|
||||
}
|
||||
|
||||
bool ThumbnailPropertyCtrl::event(QEvent* e)
|
||||
{
|
||||
if (isEnabled())
|
||||
@@ -83,7 +125,7 @@ namespace AzToolsFramework
|
||||
if (e->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
emit clicked();
|
||||
return true; //ignore
|
||||
return true; // ignore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,37 +136,32 @@ namespace AzToolsFramework
|
||||
{
|
||||
QPainter p(this);
|
||||
QRect targetRect(QPoint(), QSize(40, 24));
|
||||
p.fillRect(targetRect, QColor(17, 17, 17)); // #111111
|
||||
p.fillRect(targetRect, QColor("#111111"));
|
||||
QWidget::paintEvent(e);
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::enterEvent(QEvent* e)
|
||||
{
|
||||
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0_highlighted.png"));
|
||||
if (!m_thumbnailEnlarged && m_key)
|
||||
{
|
||||
QPoint position = mapToGlobal(pos() - QPoint(185, 0));
|
||||
QSize size(180, 180);
|
||||
m_thumbnailEnlarged.reset(new Thumbnailer::ThumbnailWidget());
|
||||
m_thumbnailEnlarged->setFixedSize(size);
|
||||
m_thumbnailEnlarged->move(position);
|
||||
m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
|
||||
m_thumbnailEnlarged->SetThumbnailKey(m_key);
|
||||
m_thumbnailEnlarged->raise();
|
||||
m_thumbnailEnlarged->show();
|
||||
}
|
||||
const QPoint offset(-m_thumbnailEnlarged->width() - 5, -m_thumbnailEnlarged->height() / 2 + m_thumbnail->height() / 2);
|
||||
|
||||
m_thumbnailEnlarged->move(mapToGlobal(pos()) + offset);
|
||||
m_thumbnailEnlarged->raise();
|
||||
m_thumbnailEnlarged->setVisible(m_key && !m_customThumbnailEnabled);
|
||||
|
||||
m_customThumbnailEnlarged->move(mapToGlobal(pos()) + offset);
|
||||
m_customThumbnailEnlarged->raise();
|
||||
m_customThumbnailEnlarged->setVisible(m_customThumbnailEnabled);
|
||||
QWidget::enterEvent(e);
|
||||
}
|
||||
|
||||
void ThumbnailPropertyCtrl::leaveEvent(QEvent* e)
|
||||
{
|
||||
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png"));
|
||||
if (m_thumbnailEnlarged)
|
||||
{
|
||||
m_thumbnailEnlarged.reset();
|
||||
}
|
||||
m_thumbnailEnlarged->setVisible(false);
|
||||
m_customThumbnailEnlarged->setVisible(false);
|
||||
QWidget::leaveEvent(e);
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
#include "UI/PropertyEditor/moc_ThumbnailPropertyCtrl.cpp"
|
||||
|
||||
+19
-6
@@ -1,5 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -8,6 +6,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/PlatformDef.h>
|
||||
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
|
||||
@@ -35,25 +35,38 @@ namespace AzToolsFramework
|
||||
|
||||
//! Call this to set what thumbnail widget will display
|
||||
void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName = "Default");
|
||||
|
||||
//! Remove current thumbnail
|
||||
void ClearThumbnail();
|
||||
|
||||
//! Display a clickable dropdown arrow next to the thumbnail
|
||||
void ShowDropDownArrow(bool visible);
|
||||
|
||||
bool event(QEvent* e) override;
|
||||
//! Override the thumbnail widget with a custom image
|
||||
void SetCustomThumbnailEnabled(bool enabled);
|
||||
|
||||
//! Assign a custom image to display in place of thumbnail
|
||||
void SetCustomThumbnailPixmap(const QPixmap& pixmap);
|
||||
|
||||
Q_SIGNALS:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
private:
|
||||
void UpdateVisibility();
|
||||
|
||||
bool event(QEvent* e) override;
|
||||
void paintEvent(QPaintEvent* e) override;
|
||||
void enterEvent(QEvent* e) override;
|
||||
void leaveEvent(QEvent* e) override;
|
||||
|
||||
private:
|
||||
Thumbnailer::SharedThumbnailKey m_key;
|
||||
Thumbnailer::ThumbnailWidget* m_thumbnail = nullptr;
|
||||
QScopedPointer<Thumbnailer::ThumbnailWidget> m_thumbnailEnlarged;
|
||||
Thumbnailer::ThumbnailWidget* m_thumbnailEnlarged = nullptr;
|
||||
|
||||
QLabel* m_customThumbnail = nullptr;
|
||||
QLabel* m_customThumbnailEnlarged = nullptr;
|
||||
bool m_customThumbnailEnabled = false;
|
||||
|
||||
QLabel* m_emptyThumbnail = nullptr;
|
||||
AspectRatioAwarePixmapWidget* m_dropDownArrow = nullptr;
|
||||
};
|
||||
|
||||
+23
-4
@@ -215,15 +215,14 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// Verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
|
||||
if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor))
|
||||
if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor))
|
||||
{
|
||||
return EntityIdUnderCursor(AZ::EntityId());
|
||||
}
|
||||
|
||||
// Container Entity support - if the entity that is being selected is part of a closed container,
|
||||
// change the selection to the container instead.
|
||||
ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
if (containerEntityInterface)
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
{
|
||||
const auto highestSelectableEntity = containerEntityInterface->FindHighestSelectableEntity(entityIdUnderCursor);
|
||||
return EntityIdUnderCursor(entityIdUnderCursor, highestSelectableEntity);
|
||||
@@ -246,7 +245,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
|
||||
|
||||
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex))
|
||||
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -292,4 +291,24 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId)
|
||||
{
|
||||
return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId);
|
||||
}
|
||||
|
||||
bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId)
|
||||
{
|
||||
return m_focusModeInterface->IsInFocusSubTree(entityId);
|
||||
}
|
||||
|
||||
bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId)
|
||||
{
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
{
|
||||
return !containerEntityInterface->IsUnderClosedContainerEntity(entityId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -74,7 +74,19 @@ namespace AzToolsFramework
|
||||
AzFramework::DebugDisplayRequests& debugDisplay,
|
||||
const AZStd::function<bool(AZ::EntityId)>& showIconCheck);
|
||||
|
||||
//! Returns whether the entityId can be selected in the viewport according
|
||||
//! to the current Editor Focus Mode and Container Entity setup.
|
||||
bool IsSelectableInViewport(AZ::EntityId entityId);
|
||||
|
||||
private:
|
||||
//! Returns whether the entityId can be selected in the viewport according
|
||||
//! to the current Editor Focus Mode setup.
|
||||
bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId);
|
||||
|
||||
//! Returns whether the entityId can be selected in the viewport according
|
||||
//! to the current Container Entityu setup.
|
||||
bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId);
|
||||
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
|
||||
const FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
};
|
||||
|
||||
+17
-24
@@ -1114,7 +1114,7 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_boxSelect.InstallLeftMouseUp(
|
||||
[this, entityBoxSelectData]()
|
||||
[this, entityBoxSelectData]
|
||||
{
|
||||
entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds));
|
||||
|
||||
@@ -2186,7 +2186,7 @@ namespace AzToolsFramework
|
||||
// lock selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_L) }, LockSelection, LockSelectionTitle, LockSelectionDesc,
|
||||
[lockUnlock]()
|
||||
[lockUnlock]
|
||||
{
|
||||
lockUnlock(true);
|
||||
});
|
||||
@@ -2194,7 +2194,7 @@ namespace AzToolsFramework
|
||||
// unlock selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, UnlockSelection, LockSelectionTitle, LockSelectionDesc,
|
||||
[lockUnlock]()
|
||||
[lockUnlock]
|
||||
{
|
||||
lockUnlock(false);
|
||||
});
|
||||
@@ -2224,7 +2224,7 @@ namespace AzToolsFramework
|
||||
// hide selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_H) }, HideSelection, HideSelectionTitle, HideSelectionDesc,
|
||||
[showHide]()
|
||||
[showHide]
|
||||
{
|
||||
showHide(false);
|
||||
});
|
||||
@@ -2232,7 +2232,7 @@ namespace AzToolsFramework
|
||||
// show selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, ShowSelection, HideSelectionTitle, HideSelectionDesc,
|
||||
[showHide]()
|
||||
[showHide]
|
||||
{
|
||||
showHide(true);
|
||||
});
|
||||
@@ -2240,7 +2240,7 @@ namespace AzToolsFramework
|
||||
// unlock all entities in the level/scene
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, UnlockAll, UnlockAllTitle, UnlockAllDesc,
|
||||
[]()
|
||||
[]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2257,14 +2257,14 @@ namespace AzToolsFramework
|
||||
// show all entities in the level/scene
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, ShowAll, ShowAllTitle, ShowAllDesc,
|
||||
[]()
|
||||
[]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
ScopedUndoBatch undoBatch(ShowAllEntitiesUndoRedoDesc);
|
||||
|
||||
EnumerateEditorEntities(
|
||||
[](AZ::EntityId entityId)
|
||||
[](const AZ::EntityId entityId)
|
||||
{
|
||||
ScopedUndoBatch::MarkEntityDirty(entityId);
|
||||
SetEntityVisibility(entityId, true);
|
||||
@@ -2274,7 +2274,7 @@ namespace AzToolsFramework
|
||||
// select all entities in the level/scene
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, SelectAll, SelectAllTitle, SelectAllDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2314,7 +2314,7 @@ namespace AzToolsFramework
|
||||
// invert current selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, InvertSelect, InvertSelectionTitle, InvertSelectionDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2361,17 +2361,10 @@ namespace AzToolsFramework
|
||||
// duplicate selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_D) }, DuplicateSelect, DuplicateTitle, DuplicateDesc,
|
||||
[]()
|
||||
[]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
// Clear Widget selection - Prevents issues caused by cloning entities while a property in the Reflected Property Editor
|
||||
// is being edited.
|
||||
if (QApplication::focusWidget())
|
||||
{
|
||||
QApplication::focusWidget()->clearFocus();
|
||||
}
|
||||
|
||||
ScopedUndoBatch undoBatch(DuplicateUndoRedoDesc);
|
||||
auto selectionCommand = AZStd::make_unique<SelectionCommand>(EntityIdList(), DuplicateUndoRedoDesc);
|
||||
selectionCommand->SetParent(undoBatch.GetUndoBatch());
|
||||
@@ -2386,7 +2379,7 @@ namespace AzToolsFramework
|
||||
// delete selection
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_Delete) }, DeleteSelect, DeleteTitle, DeleteDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -2403,21 +2396,21 @@ namespace AzToolsFramework
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_Space) }, EditEscaspe, "", "",
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
DeselectEntities();
|
||||
});
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_P) }, EditPivot, TogglePivotTitleEditMenu, TogglePivotDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_R) }, EditReset, ResetEntityTransformTitle, ResetEntityTransformDesc,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
switch (m_mode)
|
||||
{
|
||||
@@ -2442,7 +2435,7 @@ namespace AzToolsFramework
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_U) }, ViewportUiVisible, "Toggle Viewport UI", "Hide/Show Viewport UI",
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
SetAllViewportUiVisible(!m_viewportUiVisible);
|
||||
});
|
||||
@@ -3251,7 +3244,7 @@ namespace AzToolsFramework
|
||||
QAction* action = menu->addAction(QObject::tr(TogglePivotTitleRightClick));
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action,
|
||||
[this]()
|
||||
[this]
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithNoContainers)
|
||||
{
|
||||
// When no containers are in the way, the function will just return the entityId of the entity that was clicked.
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithClosedContainer)
|
||||
{
|
||||
// If a closed container is an ancestor of the queried entity, the closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithOpenContainer)
|
||||
{
|
||||
// If a closed container is an ancestor of the queried entity, the closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleClosedContainers)
|
||||
{
|
||||
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CityEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleContainers)
|
||||
{
|
||||
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
}
|
||||
@@ -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 <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Register)
|
||||
{
|
||||
// Registering an entity is successful.
|
||||
auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_RegisterTwice)
|
||||
{
|
||||
// Registering an entity twice fails.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Unregister)
|
||||
{
|
||||
// Unregistering a container entity is successful.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterRegularEntity)
|
||||
{
|
||||
// Unregistering an entity that was not previously registered fails.
|
||||
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterTwice)
|
||||
{
|
||||
// Unregistering a container entity twice fails.
|
||||
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegularEntity)
|
||||
{
|
||||
// If a regular entity is passed, IsContainer returns false.
|
||||
// Note that we use a different entity than the tests above to validate a completely new EntityId.
|
||||
bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]);
|
||||
EXPECT_FALSE(isContainer);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegisteredContainer)
|
||||
{
|
||||
// If a container entity is passed, IsContainer returns true.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]);
|
||||
EXPECT_TRUE(isContainer);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnUnRegisteredContainer)
|
||||
{
|
||||
// If an entity that was previously a container but was then unregistered is passed, IsContainer returns false.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
|
||||
bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]);
|
||||
EXPECT_FALSE(isContainer);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenOnRegularEntity)
|
||||
{
|
||||
// Setting a regular entity to open should return a failure.
|
||||
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpen)
|
||||
{
|
||||
// Set a container entity to open, and verify the operation was successful.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenTwice)
|
||||
{
|
||||
// Set a container entity to open twice, and verify that does not cause a failure (as intended).
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerClosed)
|
||||
{
|
||||
// Set a container entity to closed, and verify the operation was successful.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnRegularEntity)
|
||||
{
|
||||
// Query open state on a regular entity, and verify it returns true.
|
||||
// Open containers behave exactly as regular entities, so this is the expected return value.
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
|
||||
EXPECT_TRUE(isOpen);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnDefaultContainerEntity)
|
||||
{
|
||||
// Query open state on a newly registered container entity, and verify it returns false.
|
||||
// Containers are registered closed by default.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
|
||||
EXPECT_FALSE(isOpen);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnOpenContainerEntity)
|
||||
{
|
||||
// Query open state on a container entity that was opened, and verify it returns true.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
|
||||
EXPECT_TRUE(isOpen);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnClosedContainerEntity)
|
||||
{
|
||||
// Query open state on a container entity that was opened and then closed, and verify it returns false.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], false);
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
|
||||
EXPECT_FALSE(isOpen);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ContainerOpenStateIsPreserved)
|
||||
{
|
||||
// Register an entity as container, open it, then unregister it.
|
||||
// When the entity is registered again, the open state should be preserved.
|
||||
// This behavior is necessary for the system to work alongside Prefab propagation refreshes.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
|
||||
EXPECT_TRUE(isOpen);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceeds)
|
||||
{
|
||||
// The Clear function works if no container is registered.
|
||||
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearFailsIfContainersAreStillRegistered)
|
||||
{
|
||||
// The Clear function fails if a container is registered.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceedsIfContainersAreUnregistered)
|
||||
{
|
||||
// The Clear function fails if a container is registered.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearDeletesPreservedOpenStates)
|
||||
{
|
||||
// Register an entity as container, open it, unregister it, then call clear.
|
||||
// When the entity is registered again, the open state should not be preserved.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[Passenger1EntityName], true);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
|
||||
m_containerEntityInterface->Clear(m_editorEntityContextId);
|
||||
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[Passenger1EntityName]);
|
||||
EXPECT_FALSE(isOpen);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithNoContainers)
|
||||
{
|
||||
// When no containers are in the way, the function will just return the entityId that was passed to it.
|
||||
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
|
||||
EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithClosedContainer)
|
||||
{
|
||||
// If a closed container is an ancestor of the queried entity, the closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); // Containers are closed by default
|
||||
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
|
||||
EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithOpenContainer)
|
||||
{
|
||||
// If an open container is an ancestor of the queried entity, it is ignored.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[SportsCarEntityName], true);
|
||||
|
||||
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
|
||||
EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleClosedContainers)
|
||||
{
|
||||
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
|
||||
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
|
||||
EXPECT_EQ(selectedEntityId, m_entityMap[StreetEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleContainers)
|
||||
{
|
||||
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
|
||||
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
|
||||
EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]);
|
||||
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,20 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ClearSelectedEntities()
|
||||
{
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList());
|
||||
}
|
||||
|
||||
AzToolsFramework::EntityIdList EditorFocusModeFixture::GetSelectedEntities()
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
return selectedEntities;
|
||||
}
|
||||
|
||||
void EditorFocusModeFixture::SetUpEditorFixtureImpl()
|
||||
{
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
@@ -21,6 +35,9 @@ namespace AzToolsFramework
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
ASSERT_TRUE(m_containerEntityInterface != nullptr);
|
||||
|
||||
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
ASSERT_TRUE(m_focusModeInterface != nullptr);
|
||||
|
||||
@@ -31,6 +48,24 @@ namespace AzToolsFramework
|
||||
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
GenerateTestHierarchy();
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
}
|
||||
|
||||
void EditorFocusModeFixture::TearDownEditorFixtureImpl()
|
||||
{
|
||||
// Clear Container Entity preserved open states
|
||||
m_containerEntityInterface->Clear(m_editorEntityContextId);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
}
|
||||
|
||||
void EditorFocusModeFixture::GenerateTestHierarchy()
|
||||
@@ -59,7 +94,7 @@ namespace AzToolsFramework
|
||||
entity->Activate();
|
||||
|
||||
// Move the CarEntity so it's out of the way.
|
||||
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, CarEntityPosition);
|
||||
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, WorldCarEntityPosition);
|
||||
|
||||
// Setup the camera so the Car entity is in view.
|
||||
AzFramework::SetCameraTransform(
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
|
||||
@@ -24,16 +25,20 @@ namespace AzToolsFramework
|
||||
{
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
void TearDownEditorFixtureImpl() override;
|
||||
|
||||
void GenerateTestHierarchy();
|
||||
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId);
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
|
||||
|
||||
ContainerEntityInterface* m_containerEntityInterface = nullptr;
|
||||
FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
|
||||
public:
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzToolsFramework::EntityIdList GetSelectedEntities();
|
||||
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::CameraState m_cameraState;
|
||||
|
||||
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
|
||||
@@ -45,6 +50,7 @@ namespace AzToolsFramework
|
||||
inline static const char* Passenger1EntityName = "Passenger1";
|
||||
inline static const char* Passenger2EntityName = "Passenger2";
|
||||
|
||||
inline static AZ::Vector3 CarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
|
||||
inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
|
||||
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorFocusModeSelectionFixture : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
|
||||
{
|
||||
public:
|
||||
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
// Calculate the world position in screen space
|
||||
const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState);
|
||||
|
||||
// Click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
|
||||
}
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
@@ -6,64 +6,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
|
||||
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorFocusModeSelectionFixture
|
||||
: public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
|
||||
{
|
||||
public:
|
||||
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
// Calculate the world position in screen space
|
||||
const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState);
|
||||
|
||||
// Click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
|
||||
}
|
||||
};
|
||||
|
||||
void ClearSelectedEntities()
|
||||
{
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList());
|
||||
}
|
||||
|
||||
AzToolsFramework::EntityIdList GetSelectedEntities()
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
return selectedEntities;
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel)
|
||||
{
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -75,73 +25,53 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Set the focus on the Street Entity (parent of the test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself)
|
||||
{
|
||||
// Set the focus on the Car Entity (test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling)
|
||||
{
|
||||
// Set the focus on the SportsCar Entity (sibling of the test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant)
|
||||
{
|
||||
// Set the focus on the Passenger1 Entity (child of the entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,55 +33,40 @@ namespace AzToolsFramework
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants)
|
||||
{
|
||||
// When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't.
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings)
|
||||
{
|
||||
// If the root entity has siblings, they are also outside of the focus subtree.
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
|
||||
}
|
||||
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf)
|
||||
{
|
||||
// If the root is a leaf, then the focus subtree will consists of just that entity.
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
|
||||
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear)
|
||||
@@ -90,15 +75,13 @@ namespace AzToolsFramework
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
|
||||
// When the focus is cleared, the whole level is in the focus subtree; so we expect all entities to return true.
|
||||
{
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,11 @@ set(FILES
|
||||
EntityTestbed.h
|
||||
FileFunc.cpp
|
||||
FingerprintingTests.cpp
|
||||
FocusMode/ContainerEntitySelectionTests.cpp
|
||||
FocusMode/ContainerEntityTests.cpp
|
||||
FocusMode/EditorFocusModeFixture.cpp
|
||||
FocusMode/EditorFocusModeFixture.h
|
||||
FocusMode/EditorFocusModeSelectionFixture.h
|
||||
FocusMode/EditorFocusModeSelectionTests.cpp
|
||||
FocusMode/EditorFocusModeTests.cpp
|
||||
GenericComponentWrapperTest.cpp
|
||||
|
||||
Reference in New Issue
Block a user