Merge branch 'stabilization/2106' into Helios_FixCacheCopyFailure
This commit is contained in:
@@ -57,7 +57,13 @@ namespace AZ
|
||||
//! Determines if a component can be created from the asset type
|
||||
//! This will be called before attempting to create a component from an asset (drag&drop, etc)
|
||||
//! You can use this to filter by subIds or do your own validation here if needed
|
||||
virtual bool CanCreateComponent(const AZ::Data::AssetId& /*assetId*/) const { return true; }
|
||||
virtual bool CanCreateComponent([[maybe_unused]] const AZ::Data::AssetId& assetId) const { return true; }
|
||||
|
||||
//! Determines if other products conflict with the given one when multiple are generated from a source asset.
|
||||
//! This will be called before attempting to create a component from an asset (drag&drop, etc)
|
||||
//! You can use this to filter by conflicting product types or in case you want to skip for UX reasons.
|
||||
//! @param[in] productAssetTypes Asset types of all generated products, including the one for our given type in this bus.
|
||||
virtual bool HasConflictingProducts([[maybe_unused]] const AZStd::vector<AZ::Data::AssetType>& productAssetTypes) const { return false; }
|
||||
};
|
||||
|
||||
using AssetTypeInfoBus = AZ::EBus<AssetTypeInfo>;
|
||||
|
||||
@@ -44,8 +44,7 @@ namespace AZ::IO
|
||||
//! make adjustments. For the most optimal performance align read buffers to the physicalSectorSize.
|
||||
u8 m_enableUnbufferedReads : 1;
|
||||
//! Globally enable file sharing. This allows files to used outside AZ::IO::Streamer, including other applications
|
||||
//! while in use by AZ::IO::Streamer. File sharing can negatively impact performance and is recommended for
|
||||
//! development only.
|
||||
//! while in use by AZ::IO::Streamer.
|
||||
u8 m_enableSharing : 1;
|
||||
//! If true, only information that's explicitly requested or issues are reported. If false, status information
|
||||
//! such as when drives are created and destroyed is reported as well.
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ namespace AZ::IO
|
||||
{
|
||||
if (reportHardware)
|
||||
{
|
||||
AZ_Printf("Streamer", "Skipping drive '%s' because to no paths make use of it.\n", driveIt);
|
||||
AZ_Printf("Streamer", "Skipping drive '%s' because no paths make use of it.\n", driveIt);
|
||||
}
|
||||
while (*driveIt++);
|
||||
continue;
|
||||
|
||||
+141
@@ -46,6 +46,7 @@ namespace AZ::IO
|
||||
options.m_hasSeekPenalty = HasSeekPenalty;
|
||||
options.m_enableUnbufferedReads = TestEnableUnbufferReads;
|
||||
options.m_enableSharing = TestEnableSharedReads;
|
||||
options.m_minimalReporting = true;
|
||||
|
||||
return StorageDriveWin({ "c:/" }, TestMaxFileHandles, TestMaxMetaDataEntries, TestPhysicalSectorSize,
|
||||
TestLogicalSectorSize, TestMaxIOChannels, TestOverCommit, options);
|
||||
@@ -151,6 +152,7 @@ namespace AZ::IO
|
||||
m_configurationOptions.m_hasSeekPenalty = HasSeekPenalty;
|
||||
m_configurationOptions.m_enableUnbufferedReads = TestEnableUnbufferReads;
|
||||
m_configurationOptions.m_enableSharing = TestEnableSharedReads;
|
||||
m_configurationOptions.m_minimalReporting = true;
|
||||
|
||||
m_storageDriveWin = AZStd::make_shared<AZ::IO::StorageDriveWin>(AZStd::vector<AZStd::string_view>{drive}, TestMaxFileHandles,
|
||||
TestMaxMetaDataEntries, TestPhysicalSectorSize, TestLogicalSectorSize, TestMaxIOChannels, overCommit, m_configurationOptions);
|
||||
@@ -1148,3 +1150,142 @@ namespace AZ::IO
|
||||
azfree(buffers[numRequests - 1]);
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
class StorageDriveWindowsFixture : public benchmark::Fixture
|
||||
{
|
||||
public:
|
||||
constexpr static char* TestFileName = "StreamerBenchmark.bin";
|
||||
constexpr static size_t FileSize = 64_mib;
|
||||
|
||||
void SetupStreamer(bool enableFileSharing)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
m_fileIO = new UnitTest::TestFileIOBase();
|
||||
m_previousFileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
AZ::IO::FileIOBase::SetInstance(m_fileIO);
|
||||
|
||||
SystemFile file;
|
||||
file.Open(TestFileName, SystemFile::OpenMode::SF_OPEN_CREATE | SystemFile::OpenMode::SF_OPEN_READ_WRITE);
|
||||
AZStd::unique_ptr<char[]> buffer(new char[FileSize]);
|
||||
::memset(buffer.get(), 'c', FileSize);
|
||||
|
||||
file.Write(buffer.get(), FileSize);
|
||||
file.Close();
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath(TestFileName);
|
||||
if (absolutePath.has_value())
|
||||
{
|
||||
AZStd::string drive;
|
||||
AZ::StringFunc::Path::GetDrive(absolutePath->c_str(), drive);
|
||||
|
||||
m_absolutePath = *absolutePath;
|
||||
|
||||
StorageDriveWin::ConstructionOptions options;
|
||||
options.m_hasSeekPenalty = false;
|
||||
options.m_enableUnbufferedReads = true; // Leave this on otherwise repeated loads will be using the Windows cache instead.
|
||||
options.m_enableSharing = enableFileSharing;
|
||||
options.m_minimalReporting = true;
|
||||
AZStd::shared_ptr<StreamStackEntry> storageDriveWin =
|
||||
AZStd::make_shared<StorageDriveWin>(AZStd::vector<AZStd::string_view>{ drive }, 32, 32, 4_kib, 512, 8, 0, options);
|
||||
|
||||
AZStd::unique_ptr<Scheduler> stack = AZStd::make_unique<Scheduler>(AZStd::move(storageDriveWin));
|
||||
m_streamer = aznew Streamer(AZStd::thread_desc{}, AZStd::move(stack));
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown([[maybe_unused]] const ::benchmark::State& state) override
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
AZStd::string temp;
|
||||
m_absolutePath.swap(temp);
|
||||
|
||||
delete m_streamer;
|
||||
|
||||
SystemFile::Delete(TestFileName);
|
||||
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
AZ::IO::FileIOBase::SetInstance(m_previousFileIO);
|
||||
delete m_fileIO;
|
||||
}
|
||||
|
||||
void RepeatedlyReadFile(benchmark::State& state)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
using namespace AZStd::chrono;
|
||||
|
||||
AZStd::unique_ptr<char[]> buffer(new char[FileSize]);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
AZStd::binary_semaphore waitForReads;
|
||||
AZStd::atomic<system_clock::time_point> end;
|
||||
auto callback = [&end, &waitForReads]([[maybe_unused]] FileRequestHandle request)
|
||||
{
|
||||
benchmark::DoNotOptimize(end = high_resolution_clock::now());
|
||||
waitForReads.release();
|
||||
};
|
||||
|
||||
FileRequestPtr request = m_streamer->Read(m_absolutePath, buffer.get(), state.range(0), state.range(0));
|
||||
m_streamer->SetRequestCompleteCallback(request, callback);
|
||||
|
||||
system_clock::time_point start;
|
||||
benchmark::DoNotOptimize(start = high_resolution_clock::now());
|
||||
m_streamer->QueueRequest(request);
|
||||
|
||||
waitForReads.try_acquire_for(AZStd::chrono::seconds(5));
|
||||
auto durationInSeconds = duration_cast<duration<double>>(end.load() - start);
|
||||
|
||||
state.SetIterationTime(durationInSeconds.count());
|
||||
|
||||
m_streamer->QueueRequest(m_streamer->FlushCaches());
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string m_absolutePath;
|
||||
AZ::IO::Streamer* m_streamer{};
|
||||
AZ::IO::FileIOBase* m_previousFileIO{};
|
||||
UnitTest::TestFileIOBase* m_fileIO{};
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(StorageDriveWindowsFixture, ReadsBaseline)(benchmark::State& state)
|
||||
{
|
||||
constexpr bool EnableFileSharing = false;
|
||||
SetupStreamer(EnableFileSharing);
|
||||
RepeatedlyReadFile(state);
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(StorageDriveWindowsFixture, ReadsWithFileReadSharingEnabled)(benchmark::State& state)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
constexpr bool EnableFileSharing = true;
|
||||
SetupStreamer(EnableFileSharing);
|
||||
RepeatedlyReadFile(state);
|
||||
}
|
||||
|
||||
// For these benchmarks the CPU stat doesn't provide useful information because it uses GetThreadTimes on Window but since the main
|
||||
// thread is mostly sleeping while waiting for the read on the Streamer thread to complete this will report values (close to) zero.
|
||||
|
||||
BENCHMARK_REGISTER_F(StorageDriveWindowsFixture, ReadsBaseline)
|
||||
->RangeMultiplier(8)
|
||||
->Range(1024, 64_mib)
|
||||
->UseManualTime()
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_REGISTER_F(StorageDriveWindowsFixture, ReadsWithFileReadSharingEnabled)
|
||||
->RangeMultiplier(8)
|
||||
->Range(1024, 64_mib)
|
||||
->UseManualTime()
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
} // namespace Benchmark
|
||||
#endif // HAVE_BENCHMARK
|
||||
|
||||
@@ -1206,7 +1206,7 @@ namespace JsonSerializationTests
|
||||
if (this->m_features.m_enableInitializationTest)
|
||||
{
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
typename TypeParam::Type compare = typename TypeParam::Type{};
|
||||
AZStd::remove_cvref_t<typename TypeParam::Type> compare;
|
||||
if (!this->m_description.AreEqual(*instance, compare))
|
||||
{
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
|
||||
@@ -198,11 +198,13 @@ namespace AzFramework
|
||||
AZ::SerializeContext* m_serializeContext{ nullptr };
|
||||
//! The priority at which this call will be executed.
|
||||
SpawnablePriority m_priority{ SpawnablePriority_Default };
|
||||
//! Entity references are resolved by referring to the last entity spawned from a template entity in the spawnable. If this
|
||||
//! is set to false entities from previous spawn calls are not taken into account. If set to true entity references may be
|
||||
//! resolved to a previously spawned entity. A lookup table has to be constructed when true, which may negatively impact
|
||||
//! performance, especially if a large number of entities are present on a ticket.
|
||||
bool m_referencePreviouslySpawnedEntities{ false };
|
||||
//! Entity references are resolved by referring to the most recent entity spawned from a template entity in the spawnable.
|
||||
//! If the entity referred to hasn't been spawned yet, the reference will be resolved to the first one that *will* be spawned.
|
||||
//! If this flag is set to "true", the id mappings will persist across SpawnEntites calls, and the entity references will resolve
|
||||
//! correctly across them.
|
||||
//! When "false", the entity id mappings will be reset on this call, so entity references will only work within this call, or
|
||||
//! potentially with any subsequent SpawnEntities call where the flag is true once again.
|
||||
bool m_referencePreviouslySpawnedEntities{ true };
|
||||
};
|
||||
|
||||
struct DespawnAllEntitiesOptionalArgs final
|
||||
|
||||
@@ -250,10 +250,47 @@ namespace AzFramework
|
||||
AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate,
|
||||
EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext)
|
||||
{
|
||||
return AZ::IdUtils::Remapper<AZ::EntityId, true>::CloneObjectAndGenerateNewIdsAndFixRefs(
|
||||
// If the same ID gets remapped more than once, preserve the original remapping instead of overwriting it.
|
||||
constexpr bool allowDuplicateIds = false;
|
||||
|
||||
return AZ::IdUtils::Remapper<AZ::EntityId, allowDuplicateIds>::CloneObjectAndGenerateNewIdsAndFixRefs(
|
||||
&entityTemplate, templateToCloneMap, &serializeContext);
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::InitializeEntityIdMappings(
|
||||
const Spawnable::EntityList& entities, EntityIdMap& idMap, AZStd::unordered_set<AZ::EntityId>& previouslySpawned)
|
||||
{
|
||||
// Make sure we don't have any previous data lingering around.
|
||||
idMap.clear();
|
||||
previouslySpawned.clear();
|
||||
|
||||
idMap.reserve(entities.size());
|
||||
previouslySpawned.reserve(entities.size());
|
||||
|
||||
for (auto& entity : entities)
|
||||
{
|
||||
idMap.emplace(entity->GetId(), AZ::Entity::MakeId());
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::RefreshEntityIdMapping(
|
||||
const AZ::EntityId& entityId, EntityIdMap& idMap, AZStd::unordered_set<AZ::EntityId>& previouslySpawned)
|
||||
{
|
||||
if (previouslySpawned.contains(entityId))
|
||||
{
|
||||
// This entity has already been spawned at least once before, so we need to generate a new id for it and
|
||||
// preserve the new id to fix up any future entity references to this entity.
|
||||
idMap[entityId] = AZ::Entity::MakeId();
|
||||
}
|
||||
else
|
||||
{
|
||||
// This entity hasn't been spawned yet, so use the first id we've already generated for this entity and mark
|
||||
// it as spawned so we know not to reuse this id next time.
|
||||
previouslySpawned.emplace(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
@@ -269,18 +306,24 @@ namespace AzFramework
|
||||
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
|
||||
size_t entitiesToSpawnSize = entitiesToSpawn.size();
|
||||
|
||||
// Map keeps track of ids from template (spawnable) to clone (instance)
|
||||
// Allowing patch ups of fields referring to entityIds outside of a given entity
|
||||
EntityIdMap templateToCloneEntityIdMap;
|
||||
|
||||
// Reserve buffers
|
||||
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
|
||||
spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize);
|
||||
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
|
||||
|
||||
// Pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below,
|
||||
// any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly.
|
||||
// We clear out and regenerate the set of IDs on every SpawnAllEntities call, because presumably every entity reference
|
||||
// in every entity we're about to instantiate is intended to point to an entity in our newly-instantiated batch, regardless
|
||||
// of spawn order. If we didn't clear out the map, it would be possible for some entities here to have references to
|
||||
// previously-spawned entities from a previous SpawnEntities or SpawnAllEntities call.
|
||||
InitializeEntityIdMappings(entitiesToSpawn, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
|
||||
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
|
||||
{
|
||||
AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], templateToCloneEntityIdMap, *request.m_serializeContext);
|
||||
// If this entity has previously been spawned, give it a new id in the reference map
|
||||
RefreshEntityIdMapping(entitiesToSpawn[i].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
|
||||
AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext);
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
|
||||
spawnedEntities.emplace_back(clone);
|
||||
@@ -337,21 +380,17 @@ namespace AzFramework
|
||||
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
|
||||
size_t entitiesToSpawnSize = request.m_entityIndices.size();
|
||||
|
||||
// Reconstruct the template to entity mapping.
|
||||
EntityIdMap templateToCloneEntityIdMap;
|
||||
if (!request.m_referencePreviouslySpawnedEntities)
|
||||
if (ticket.m_entityIdReferenceMap.empty() || !request.m_referencePreviouslySpawnedEntities)
|
||||
{
|
||||
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
templateToCloneEntityIdMap.reserve(spawnedEntitiesInitialCount + entitiesToSpawnSize);
|
||||
SpawnableConstIndexEntityContainerView indexEntityView(
|
||||
spawnedEntities.begin(), spawnedEntityIndices.begin(), spawnedEntities.size());
|
||||
for (auto& entry : indexEntityView)
|
||||
{
|
||||
templateToCloneEntityIdMap.insert_or_assign(entitiesToSpawn[entry.GetIndex()]->GetId(), entry.GetEntity()->GetId());
|
||||
}
|
||||
// This map keeps track of ids from template (spawnable) to clone (instance) allowing patch ups of fields referring
|
||||
// to entityIds outside of a given entity.
|
||||
// We pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below,
|
||||
// any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly.
|
||||
// By default, we only initialize this map once because it needs to persist across multiple SpawnEntities calls, so
|
||||
// that reference fixups work even when the entity being referenced is spawned in a different SpawnEntities
|
||||
// (or SpawnAllEntities) call.
|
||||
// However, the caller can also choose to reset the map by passing in "m_referencePreviouslySpawnedEntities = false".
|
||||
InitializeEntityIdMappings(entitiesToSpawn, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
}
|
||||
|
||||
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
|
||||
@@ -361,7 +400,12 @@ namespace AzFramework
|
||||
{
|
||||
if (index < entitiesToSpawn.size())
|
||||
{
|
||||
AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[index], templateToCloneEntityIdMap, *request.m_serializeContext);
|
||||
// If this entity has previously been spawned, give it a new id in the reference map
|
||||
RefreshEntityIdMapping(
|
||||
entitiesToSpawn[index].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
|
||||
AZ::Entity* clone =
|
||||
CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext);
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
|
||||
spawnedEntities.push_back(clone);
|
||||
@@ -451,9 +495,11 @@ namespace AzFramework
|
||||
ticket.m_spawnedEntities.clear();
|
||||
const Spawnable::EntityList& entities = request.m_spawnable->GetEntities();
|
||||
|
||||
// Map keeps track of ids from template (spawnable) to clone (instance)
|
||||
// Allowing patch ups of fields referring to entityIds outside of a given entity
|
||||
EntityIdMap templateToCloneEntityIdMap;
|
||||
// Pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below,
|
||||
// any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly.
|
||||
// This map is intentionally cleared out and regenerated here to ensure that we're starting fresh with mappings that
|
||||
// match the new set of template entities getting spawned.
|
||||
InitializeEntityIdMappings(entities, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
|
||||
if (ticket.m_loadAll)
|
||||
{
|
||||
@@ -461,11 +507,13 @@ namespace AzFramework
|
||||
// to spawn every entity, simply start over.
|
||||
ticket.m_spawnedEntityIndices.clear();
|
||||
size_t entitiesToSpawnSize = entities.size();
|
||||
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
|
||||
|
||||
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
|
||||
{
|
||||
AZ::Entity* clone = CloneSingleEntity(*entities[i], templateToCloneEntityIdMap, *request.m_serializeContext);
|
||||
// If this entity has previously been spawned, give it a new id in the reference map
|
||||
RefreshEntityIdMapping(entities[i].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
|
||||
AZ::Entity* clone = CloneSingleEntity(*entities[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext);
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
|
||||
ticket.m_spawnedEntities.push_back(clone);
|
||||
@@ -475,7 +523,7 @@ namespace AzFramework
|
||||
else
|
||||
{
|
||||
size_t entitiesSize = entities.size();
|
||||
templateToCloneEntityIdMap.reserve(entitiesSize);
|
||||
|
||||
for (size_t index : ticket.m_spawnedEntityIndices)
|
||||
{
|
||||
// It's possible for the new spawnable to have a different number of entities, so guard against this.
|
||||
@@ -483,7 +531,10 @@ namespace AzFramework
|
||||
// detected and will result in the incorrect entities being spawned.
|
||||
if (index < entitiesSize)
|
||||
{
|
||||
AZ::Entity* clone = CloneSingleEntity(*entities[index], templateToCloneEntityIdMap, *request.m_serializeContext);
|
||||
// If this entity has previously been spawned, give it a new id in the reference map
|
||||
RefreshEntityIdMapping(entities[index].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned);
|
||||
|
||||
AZ::Entity* clone = CloneSingleEntity(*entities[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext);
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
ticket.m_spawnedEntities.push_back(clone);
|
||||
}
|
||||
|
||||
@@ -85,6 +85,22 @@ namespace AzFramework
|
||||
AZ_CLASS_ALLOCATOR(Ticket, AZ::ThreadPoolAllocator, 0);
|
||||
static constexpr uint32_t Processing = AZStd::numeric_limits<uint32_t>::max();
|
||||
|
||||
//! Map of template entity ids to their associated instance ids.
|
||||
//! Tickets can be used to spawn the same template entities multiple times, in any order, across multiple calls.
|
||||
//! Since template entities can reference other entities, this map is used to fix up those references across calls
|
||||
//! using the following policy:
|
||||
//! - Entities referencing an entity that hasn't been spawned yet will get a reference to the id that *will* be used
|
||||
//! the first time that entity will be spawned. The reference will be invalid until that entity is spawned, but
|
||||
//! will be valid if/when it gets spawned.
|
||||
//! - Entities referencing an entity that *has* been spawned will get a reference to the id that was *last* used to
|
||||
//! spawn the entity.
|
||||
//! Note that this implies a certain level of non-determinism when spawning across calls, because the entity references
|
||||
//! will be based on the order in which the SpawnEntity calls occur, which can be affected by things like priority.
|
||||
EntityIdMap m_entityIdReferenceMap;
|
||||
//! For this to work, we also need to keep track of whether or not each entity has been spawned at least once, so we know
|
||||
//! whether or not to replace the id in the map when spawning a new instance of that entity.
|
||||
AZStd::unordered_set<AZ::EntityId> m_previouslySpawned;
|
||||
|
||||
AZStd::vector<AZ::Entity*> m_spawnedEntities;
|
||||
AZStd::vector<size_t> m_spawnedEntityIndices;
|
||||
AZ::Data::Asset<Spawnable> m_spawnable;
|
||||
@@ -194,6 +210,15 @@ namespace AzFramework
|
||||
bool ProcessRequest(BarrierCommand& request);
|
||||
bool ProcessRequest(DestroyTicketCommand& request);
|
||||
|
||||
//! Generate a base set of original-to-new entity ID mappings to use during spawning.
|
||||
//! Since Entity references get fixed up on an entity-by-entity basis while spawning, it's important to have the complete
|
||||
//! set of new IDs available right at the start. This way, entities that refer to other entities that haven't spawned yet
|
||||
//! will still get their references remapped correctly.
|
||||
void InitializeEntityIdMappings(
|
||||
const Spawnable::EntityList& entities, EntityIdMap& idMap, AZStd::unordered_set<AZ::EntityId>& previouslySpawned);
|
||||
void RefreshEntityIdMapping(
|
||||
const AZ::EntityId& entityId, EntityIdMap& idMap, AZStd::unordered_set<AZ::EntityId>& previouslySpawned);
|
||||
|
||||
Queue m_highPriorityQueue;
|
||||
Queue m_regularPriorityQueue;
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@ namespace AzFramework
|
||||
// Add a fullscreen button in the upper right of the title bar.
|
||||
[m_nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
|
||||
|
||||
m_nativeWindow.tabbingMode = NSWindowTabbingModeDisallowed;
|
||||
|
||||
// Make the window active
|
||||
[m_nativeWindow makeKeyAndOrderFront:nil];
|
||||
m_nativeWindow.title = m_windowTitle;
|
||||
|
||||
@@ -493,7 +493,30 @@ namespace AzQtComponents
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CE_MenuItem:
|
||||
{
|
||||
const QMenu* menu = qobject_cast<const QMenu*>(widget);
|
||||
QAction* action = menu->activeAction();
|
||||
if (action)
|
||||
{
|
||||
QMenu* subMenu = action->menu();
|
||||
if (subMenu)
|
||||
{
|
||||
QVariant noHover = subMenu->property("noHover");
|
||||
if (noHover.isValid() && noHover.toBool())
|
||||
{
|
||||
// First draw as standard to get the correct hover background for the complete control.
|
||||
QProxyStyle::drawControl(element, option, painter, widget);
|
||||
// Now draw the icon as non-hovered so control behaves as designed.
|
||||
QStyleOptionMenuItem myOpt = *qstyleoption_cast<const QStyleOptionMenuItem*>(option);
|
||||
myOpt.state &= ~QStyle::State_Selected;
|
||||
return QProxyStyle::drawControl(element, &myOpt, painter, widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return QProxyStyle::drawControl(element, option, painter, widget);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Helpers Icon</title>
|
||||
<defs>
|
||||
<circle id="path-1" cx="8" cy="8" r="8"></circle>
|
||||
<path d="M8.59736328,10.1821289 L8.59736328,9.79394531 C8.59736328,9.07617188 8.86835937,8.71728516 9.84248047,8.15332031 C10.8751953,7.54541016 11.4098633,6.77636719 11.4098633,5.67041016 C11.4098633,4.00048828 10.0255859,2.8359375 7.93085937,2.8359375 C5.68232422,2.8359375 4.40791016,4.09570312 4.37128906,5.89746094 L6.35615234,5.89746094 C6.40009766,5.06982422 6.95673828,4.53515625 7.79902344,4.53515625 C8.63398437,4.53515625 9.190625,5.04052734 9.190625,5.73632812 C9.190625,6.43212891 8.90498047,6.79101562 7.96015625,7.35498047 C6.94941406,7.94824219 6.54658203,8.60742188 6.64179687,9.75732422 L6.65644531,10.1821289 L8.59736328,10.1821289 Z M7.70380859,13.7124023 C8.52412109,13.7124023 9.02949219,13.2436523 9.02949219,12.4892578 C9.02949219,11.7275391 8.52412109,11.2587891 7.70380859,11.2587891 C6.88349609,11.2587891 6.37080078,11.7275391 6.37080078,12.4892578 C6.37080078,13.2436523 6.88349609,13.7124023 7.70380859,13.7124023 Z" id="path-3"></path>
|
||||
</defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="2nd-ToolBar-v2-(Perspective)-3" transform="translate(-1161.000000, -7.000000)">
|
||||
<g id="2nd-ToolBar-buttons-on-right" transform="translate(1076.000000, 5.000000)">
|
||||
<g id="Helpers-Icon" transform="translate(85.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Oval" fill="#FFFFFF" xlink:href="#path-1"></use>
|
||||
<g id="?" fill-rule="nonzero" mask="url(#mask-2)">
|
||||
<use fill="#909090" xlink:href="#path-3"></use>
|
||||
<use fill="#3F3F3F" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Buttons / Dropdown button with Icon / no arrow</title>
|
||||
<g id="Buttons-/-Dropdown-button-with-Icon-/-no-arrow" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Icons-/-System-/-Menu" fill="#FFFFFF">
|
||||
<rect id="Rectangle-11" x="1.33333333" y="2.66666667" width="13.3333333" height="1.33333333"></rect>
|
||||
<rect id="Rectangle-11" x="1.33333333" y="7.33333333" width="13.3333333" height="1.33333333"></rect>
|
||||
<rect id="Rectangle-11" x="1.33333333" y="12" width="13.3333333" height="1.33333333"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 786 B |
@@ -1,4 +1,4 @@
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.0708 0.87087L11.0708 0.87074C10.4286 0.371024 9.61107 0.13517 8.79265 0.213464C7.97422 0.291757 7.21961 0.678004 6.6897 1.28985L5.76756 2.36366C5.63378 2.51944 5.56909 2.72055 5.58771 2.92273C5.60634 3.12492 5.70675 3.31163 5.86686 3.44178C6.02698 3.57194 6.23368 3.63488 6.44149 3.61676C6.6493 3.59864 6.8412 3.50094 6.97498 3.34516L7.89716 2.27122C8.03184 2.11488 8.19736 1.98639 8.38395 1.89334C8.57053 1.8003 8.7744 1.74459 8.98349 1.7295C9.19258 1.71442 9.40266 1.74027 9.60131 1.80552C9.79996 1.87078 9.98315 1.97411 10.1401 2.10942C10.4422 2.38256 10.6244 2.75851 10.6488 3.15904C10.6732 3.55957 10.5379 3.95383 10.2711 4.25978L8.35381 6.49309L8.33616 6.51209C8.23375 6.62767 8.11374 6.72729 7.98031 6.80749C7.67189 6.99364 7.30661 7.06983 6.94686 7.02305C6.58711 6.97626 6.25522 6.8094 6.00789 6.55097C5.86556 6.40288 5.66865 6.31578 5.46039 6.30879C5.25213 6.30179 5.04952 6.37546 4.89703 6.51365C4.74453 6.65183 4.65462 6.84323 4.64701 7.04584C4.6394 7.24845 4.71472 7.44572 4.85645 7.59436C5.21222 7.96578 5.65746 8.24515 6.15198 8.40726C6.6465 8.56937 7.17473 8.60911 7.68898 8.52289C7.84983 8.49539 8.00828 8.45599 8.16298 8.40505C8.70939 8.22568 9.19408 7.90261 9.56335 7.47165L11.4758 5.24457C11.7478 4.92529 11.9523 4.55683 12.0773 4.16038C12.2024 3.76394 12.2457 3.34735 12.2046 2.93457C12.1671 2.53429 12.0475 2.1454 11.8527 1.79092C11.658 1.43644 11.3921 1.12358 11.0708 0.87087Z" fill="white"/>
|
||||
<path d="M5.40958 9.14055L4.58546 10.1003C4.32342 10.4101 3.94867 10.6097 3.53919 10.6576C3.12972 10.7054 2.71706 10.5977 2.3871 10.357C2.22234 10.2309 2.08525 10.0738 1.98392 9.89526C1.8826 9.71668 1.81911 9.52014 1.79719 9.31727C1.77528 9.1144 1.79539 8.90932 1.85634 8.71414C1.91729 8.51896 2.01784 8.33765 2.15204 8.18093L4.10231 5.90975L4.11666 5.89415C4.21908 5.77861 4.3391 5.67903 4.47254 5.59887C4.75216 5.42921 5.07976 5.34988 5.4085 5.37222C5.73725 5.39457 6.05033 5.51745 6.30299 5.72329C6.3635 5.77246 6.42015 5.82595 6.47246 5.88333C6.54738 5.9658 6.63971 6.03156 6.74316 6.07611C6.84661 6.12066 6.95872 6.14295 7.07184 6.14146C7.18361 6.13992 7.29372 6.11492 7.39465 6.06817C7.49558 6.02143 7.58495 5.95403 7.65666 5.8706L7.66607 5.85958C7.78641 5.72083 7.85132 5.54454 7.8489 5.36301C7.84647 5.18148 7.77687 5.00689 7.65286 4.87124C7.35341 4.54132 6.98422 4.27825 6.57057 4.10003C6.15692 3.92182 5.70858 3.83266 5.25623 3.83866C4.80388 3.84467 4.35821 3.94569 3.94971 4.13482C3.54122 4.32395 3.17954 4.59672 2.88945 4.93446L0.944626 7.19943C0.420065 7.81564 0.163643 8.60683 0.230025 9.40433C0.296406 10.2018 0.68034 10.9426 1.29998 11.4686C1.61272 11.7312 1.97644 11.9301 2.3696 12.0535C2.76277 12.1769 3.17738 12.2223 3.5889 12.187C3.687 12.1793 3.78434 12.1672 3.88093 12.1507C4.62877 12.0232 5.30663 11.6437 5.79562 11.0786L6.617 10.1221C6.75077 9.96628 6.81547 9.76517 6.79684 9.56299C6.77822 9.3608 6.6778 9.17409 6.51769 9.04394C6.35758 8.91378 6.15088 8.85084 5.94306 8.86896C5.73525 8.88708 5.54335 8.98477 5.40957 9.14055H5.40958Z" fill="white"/>
|
||||
<path d="M11.0708 0.87087L11.0708 0.87074C10.4286 0.371024 9.61107 0.13517 8.79265 0.213464C7.97422 0.291757 7.21961 0.678004 6.6897 1.28985L5.76756 2.36366C5.63378 2.51944 5.56909 2.72055 5.58771 2.92273C5.60634 3.12492 5.70675 3.31163 5.86686 3.44178C6.02698 3.57194 6.23368 3.63488 6.44149 3.61676C6.6493 3.59864 6.8412 3.50094 6.97498 3.34516L7.89716 2.27122C8.03184 2.11488 8.19736 1.98639 8.38395 1.89334C8.57053 1.8003 8.7744 1.74459 8.98349 1.7295C9.19258 1.71442 9.40266 1.74027 9.60131 1.80552C9.79996 1.87078 9.98315 1.97411 10.1401 2.10942C10.4422 2.38256 10.6244 2.75851 10.6488 3.15904C10.6732 3.55957 10.5379 3.95383 10.2711 4.25978L8.35381 6.49309L8.33616 6.51209C8.23375 6.62767 8.11374 6.72729 7.98031 6.80749C7.67189 6.99364 7.30661 7.06983 6.94686 7.02305C6.58711 6.97626 6.25522 6.8094 6.00789 6.55097C5.86556 6.40288 5.66865 6.31578 5.46039 6.30879C5.25213 6.30179 5.04952 6.37546 4.89703 6.51365C4.74453 6.65183 4.65462 6.84323 4.64701 7.04584C4.6394 7.24845 4.71472 7.44572 4.85645 7.59436C5.21222 7.96578 5.65746 8.24515 6.15198 8.40726C6.6465 8.56937 7.17473 8.60911 7.68898 8.52289C7.84983 8.49539 8.00828 8.45599 8.16298 8.40505C8.70939 8.22568 9.19408 7.90261 9.56335 7.47165L11.4758 5.24457C11.7478 4.92529 11.9523 4.55683 12.0773 4.16038C12.2024 3.76394 12.2457 3.34735 12.2046 2.93457C12.1671 2.53429 12.0475 2.1454 11.8527 1.79092C11.658 1.43644 11.3921 1.12358 11.0708 0.87087Z" fill="#3F3F3F"/>
|
||||
<path d="M5.40958 9.14055L4.58546 10.1003C4.32342 10.4101 3.94867 10.6097 3.53919 10.6576C3.12972 10.7054 2.71706 10.5977 2.3871 10.357C2.22234 10.2309 2.08525 10.0738 1.98392 9.89526C1.8826 9.71668 1.81911 9.52014 1.79719 9.31727C1.77528 9.1144 1.79539 8.90932 1.85634 8.71414C1.91729 8.51896 2.01784 8.33765 2.15204 8.18093L4.10231 5.90975L4.11666 5.89415C4.21908 5.77861 4.3391 5.67903 4.47254 5.59887C4.75216 5.42921 5.07976 5.34988 5.4085 5.37222C5.73725 5.39457 6.05033 5.51745 6.30299 5.72329C6.3635 5.77246 6.42015 5.82595 6.47246 5.88333C6.54738 5.9658 6.63971 6.03156 6.74316 6.07611C6.84661 6.12066 6.95872 6.14295 7.07184 6.14146C7.18361 6.13992 7.29372 6.11492 7.39465 6.06817C7.49558 6.02143 7.58495 5.95403 7.65666 5.8706L7.66607 5.85958C7.78641 5.72083 7.85132 5.54454 7.8489 5.36301C7.84647 5.18148 7.77687 5.00689 7.65286 4.87124C7.35341 4.54132 6.98422 4.27825 6.57057 4.10003C6.15692 3.92182 5.70858 3.83266 5.25623 3.83866C4.80388 3.84467 4.35821 3.94569 3.94971 4.13482C3.54122 4.32395 3.17954 4.59672 2.88945 4.93446L0.944626 7.19943C0.420065 7.81564 0.163643 8.60683 0.230025 9.40433C0.296406 10.2018 0.68034 10.9426 1.29998 11.4686C1.61272 11.7312 1.97644 11.9301 2.3696 12.0535C2.76277 12.1769 3.17738 12.2223 3.5889 12.187C3.687 12.1793 3.78434 12.1672 3.88093 12.1507C4.62877 12.0232 5.30663 11.6437 5.79562 11.0786L6.617 10.1221C6.75077 9.96628 6.81547 9.76517 6.79684 9.56299C6.77822 9.3608 6.6778 9.17409 6.51769 9.04394C6.35758 8.91378 6.15088 8.85084 5.94306 8.86896C5.73525 8.88708 5.54335 8.98477 5.40957 9.14055H5.40958Z" fill="#3F3F3F"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -28,5 +28,7 @@
|
||||
<file alias="script_canvas_editor.svg">Menu/script_canvas_editor.svg</file>
|
||||
<file alias="trackview_editor.svg">Menu/trackview_editor.svg</file>
|
||||
<file alias="ui_editor.svg">Menu/ui_editor.svg</file>
|
||||
<file alias="menu.svg">Menu/menu.svg</file>
|
||||
<file alias="helpers.svg">Menu/helpers.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
+13
-2
@@ -524,8 +524,6 @@ namespace AzToolsFramework
|
||||
rootSpawnableIndex = m_playInEditorData.m_assets.size();
|
||||
}
|
||||
|
||||
LoadReferencedAssets(product.GetReferencedAssets());
|
||||
|
||||
AZ::Data::AssetInfo info;
|
||||
info.m_assetId = product.GetAsset().GetId();
|
||||
info.m_assetType = product.GetAssetType();
|
||||
@@ -534,6 +532,19 @@ namespace AzToolsFramework
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(
|
||||
&AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
|
||||
m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
|
||||
|
||||
// Ensure the product asset is registered with the AssetManager
|
||||
// Hold on to the returned asset to keep ref count alive until we assign it the latest data
|
||||
AZ::Data::Asset<AZ::Data::AssetData> asset =
|
||||
AZ::Data::AssetManager::Instance().FindOrCreateAsset(info.m_assetId, info.m_assetType, AZ::Data::AssetLoadBehavior::Default);
|
||||
|
||||
// Update the asset registered in the AssetManager with the data of our product from the Prefab Processor
|
||||
AZ::Data::AssetManager::Instance().AssignAssetData(m_playInEditorData.m_assets.back());
|
||||
}
|
||||
|
||||
for (auto& product : context.GetProcessedObjects())
|
||||
{
|
||||
LoadReferencedAssets(product.GetReferencedAssets());
|
||||
}
|
||||
|
||||
// make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of
|
||||
|
||||
@@ -922,8 +922,8 @@ namespace AzToolsFramework
|
||||
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
|
||||
}
|
||||
|
||||
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
|
||||
// cannot duplicate an instance from itself.
|
||||
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance
|
||||
// This is because containers, despite representing the nested instance in the parent, are owned by the child.
|
||||
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
|
||||
{
|
||||
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
|
||||
@@ -967,17 +967,18 @@ namespace AzToolsFramework
|
||||
|
||||
// Duplicate any nested entities and instances as requested
|
||||
AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
|
||||
AZStd::unordered_map<EntityAlias, EntityAlias> duplicateEntityAliasMap;
|
||||
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
|
||||
entities, instanceDomAfter, duplicatedEntityAndInstanceIds);
|
||||
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
|
||||
instances, instanceDomAfter, duplicatedEntityAndInstanceIds,
|
||||
newInstanceAliasToOldInstanceMap);
|
||||
entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap);
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
|
||||
command->Redo();
|
||||
|
||||
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
|
||||
instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
|
||||
|
||||
// Create links for our duplicated instances (if any were duplicated)
|
||||
for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
|
||||
{
|
||||
@@ -995,8 +996,35 @@ namespace AzToolsFramework
|
||||
PrefabDom linkPatchesCopy;
|
||||
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
|
||||
|
||||
m_prefabSystemComponentInterface->CreateLink(
|
||||
commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy);
|
||||
// If the instance was duplicated as part of an ancestor's nested hierarchy, the container's parent patch
|
||||
// will need to be refreshed to point to the new duplicated parent entity
|
||||
auto oldInstanceContainerEntityId = oldInstance->GetContainerEntityId();
|
||||
AZ_Assert(oldInstanceContainerEntityId.IsValid(), "Instance returned invalid Container Entity Id");
|
||||
|
||||
AZ::EntityId previousParentEntityId;
|
||||
AZ::TransformBus::EventResult(previousParentEntityId, oldInstanceContainerEntityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
if (previousParentEntityId.IsValid() && AZStd::find(duplicatedEntityAndInstanceIds.begin(), duplicatedEntityAndInstanceIds.end(), previousParentEntityId))
|
||||
{
|
||||
auto oldParentAlias = commonOwningInstance->get().GetEntityAlias(previousParentEntityId);
|
||||
if (oldParentAlias.has_value() && duplicateEntityAliasMap.contains(oldParentAlias->get()))
|
||||
{
|
||||
// Get the dom into a QString for search/replace purposes
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
linkPatchesCopy.Accept(writer);
|
||||
|
||||
QString linkPatchesString(buffer.GetString());
|
||||
|
||||
ReplaceOldAliases(linkPatchesString, oldParentAlias->get(), duplicateEntityAliasMap[oldParentAlias->get()]);
|
||||
|
||||
linkPatchesCopy.Parse(linkPatchesString.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
PrefabUndoHelpers::CreateLink(
|
||||
oldInstance->GetTemplateId(), commonOwningInstance->get().GetTemplateId(),
|
||||
AZStd::move(linkPatchesCopy), newInstanceAlias, undoBatch.GetUndoBatch());
|
||||
}
|
||||
|
||||
// Select the duplicated entities/instances
|
||||
@@ -1507,14 +1535,13 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds)
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap)
|
||||
{
|
||||
if (entities.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
|
||||
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace AzToolsFramework
|
||||
*/
|
||||
void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds);
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap);
|
||||
/**
|
||||
* Duplicate a list of instances owned by a common owning instance by directly
|
||||
* copying/modifying their entries in the instance DOM
|
||||
|
||||
+8
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Math/ToString.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/Components/NonUniformScaleComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
|
||||
|
||||
@@ -57,6 +58,13 @@ namespace AzToolsFramework
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->ConstantProperty("EditorNonUniformScaleComponentTypeId", BehaviorConstant(EditorNonUniformScaleComponent::RTTI_Type()))
|
||||
->Attribute(AZ::Script::Attributes::Module, "editor")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNonUniformScaleComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
|
||||
@@ -210,6 +210,19 @@ namespace AzToolsFramework
|
||||
//! Type to inherit to implement ViewportInteractionRequests.
|
||||
using ViewportInteractionRequestBus = AZ::EBus<ViewportInteractionRequests, ViewportEBusTraits>;
|
||||
|
||||
//! An interface to notify when changes to viewport settings have happened.
|
||||
class ViewportSettingNotifications
|
||||
{
|
||||
public:
|
||||
virtual void OnGridSnappingChanged([[maybe_unused]] bool enabled) {}
|
||||
virtual void OnDrawHelpersChanged([[maybe_unused]] bool enabled) {}
|
||||
|
||||
protected:
|
||||
ViewportSettingNotifications() = default;
|
||||
};
|
||||
|
||||
using ViewportSettingsNotificationBus = AZ::EBus<ViewportSettingNotifications, ViewportEBusTraits>;
|
||||
|
||||
//! Requests to freeze the Viewport Input
|
||||
//! Added to prevent a bug with the legacy CryEngine Viewport code that would
|
||||
//! keep doing raycast tests even when no level is loaded, causing a crash.
|
||||
|
||||
@@ -32,6 +32,33 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
// Test component that has a reference to a different entity for use in validating per-instance entity id fixups.
|
||||
class ComponentWithEntityReference : public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ComponentWithEntityReference, "{CF5FDE59-86E5-40B6-9272-BBC1C4AFD061}");
|
||||
|
||||
void Activate() override
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
static void Reflect(AZ::ReflectContext* reflection)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
||||
{
|
||||
serializeContext->Class<ComponentWithEntityReference, AZ::Component>()
|
||||
->Field("EntityReference", &ComponentWithEntityReference::m_entityReference)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::EntityId m_entityReference;
|
||||
};
|
||||
|
||||
class SpawnableEntitiesManagerTest : public AllocatorsFixture
|
||||
{
|
||||
public:
|
||||
@@ -42,6 +69,8 @@ namespace UnitTest
|
||||
m_application = new TestApplication();
|
||||
AZ::ComponentApplication::Descriptor descriptor;
|
||||
m_application->Start(descriptor);
|
||||
m_application->RegisterComponentDescriptor(ComponentWithEntityReference::CreateDescriptor());
|
||||
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
@@ -80,6 +109,7 @@ namespace UnitTest
|
||||
void FillSpawnable(size_t numElements)
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities();
|
||||
entities.clear();
|
||||
entities.reserve(numElements);
|
||||
for (size_t i=0; i<numElements; ++i)
|
||||
{
|
||||
@@ -123,6 +153,94 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
enum class EntityReferenceScheme
|
||||
{
|
||||
AllReferenceFirst,
|
||||
AllReferenceLast,
|
||||
AllReferenceThemselves,
|
||||
AllReferenceNextCircular,
|
||||
AllReferencePreviousCircular
|
||||
};
|
||||
|
||||
void CreateEntityReferences(EntityReferenceScheme refScheme)
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities();
|
||||
size_t numElements = entities.size();
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
AZStd::unique_ptr<AZ::Entity>& entity = entities[i];
|
||||
auto component = entity->CreateComponent<ComponentWithEntityReference>();
|
||||
switch (refScheme)
|
||||
{
|
||||
case EntityReferenceScheme::AllReferenceFirst :
|
||||
component->m_entityReference = entities[0]->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferenceLast:
|
||||
component->m_entityReference = entities[numElements - 1]->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferenceThemselves:
|
||||
component->m_entityReference = entities[i]->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferenceNextCircular:
|
||||
component->m_entityReference = entities[(i + 1) % numElements]->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferencePreviousCircular:
|
||||
component->m_entityReference = entities[(i + numElements - 1) % numElements]->GetId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the entity references are pointing to the correct other entities within the same spawn batch.
|
||||
// A "spawn batch" is the set of entities produced for each SpawnAllEntities command.
|
||||
void ValidateEntityReferences(
|
||||
EntityReferenceScheme refScheme, size_t entitiesPerBatch, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
size_t numElements = entities.size();
|
||||
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
// Calculate the element offset that's the start of each batch of entities spawned.
|
||||
size_t curSpawnBatch = i / entitiesPerBatch;
|
||||
size_t curBatchOffset = curSpawnBatch * entitiesPerBatch;
|
||||
size_t curBatchIndex = i - curBatchOffset;
|
||||
|
||||
const AZ::Entity* const entity = *(entities.begin() + i);
|
||||
|
||||
auto component = entity->FindComponent<ComponentWithEntityReference>();
|
||||
ASSERT_NE(nullptr, component);
|
||||
AZ::EntityId comparisonId;
|
||||
// Ids should be local to a batch, so each of these will be compared within a batch of entities, not globally across
|
||||
// the entire set.
|
||||
switch (refScheme)
|
||||
{
|
||||
case EntityReferenceScheme::AllReferenceFirst:
|
||||
// Compare against the first entity in each batch
|
||||
comparisonId = (*(entities.begin() + curBatchOffset))->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferenceLast:
|
||||
// Compare against the last entity in each batch
|
||||
comparisonId = (*(entities.begin() + curBatchOffset + (entitiesPerBatch - 1)))->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferenceThemselves:
|
||||
// Compare against itself
|
||||
comparisonId = entity->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferenceNextCircular:
|
||||
// Compare against the next entity in each batch, looping around so that the last entity in the batch should refer
|
||||
// to the first entity in the batch.
|
||||
comparisonId = (*(entities.begin() + curBatchOffset + ((curBatchIndex + 1) % entitiesPerBatch)))->GetId();
|
||||
break;
|
||||
case EntityReferenceScheme::AllReferencePreviousCircular:
|
||||
// Compare against the previous entity in each batch, looping around so that the first entity in the batch should refer
|
||||
// to the last entity in the batch.
|
||||
comparisonId = (*(entities.begin() + curBatchOffset + ((curBatchIndex + numElements - 1) % entitiesPerBatch)))->GetId();
|
||||
break;
|
||||
}
|
||||
EXPECT_EQ(comparisonId, component->m_entityReference);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
AZ::Data::Asset<AzFramework::Spawnable>* m_spawnableAsset { nullptr };
|
||||
AzFramework::SpawnableEntitiesManager* m_manager { nullptr };
|
||||
@@ -185,6 +303,73 @@ namespace UnitTest
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllEntitiesReferenceOtherEntities_EntityIdsAreMappedCorrectly)
|
||||
{
|
||||
// This tests that entity id references get mapped correctly in a SpawnAllEntities call whether they're forward referencing
|
||||
// in the list, backwards referencing, or self-referencing. The circular tests are to ensure the implementation works regardless
|
||||
// of entity ordering.
|
||||
for (EntityReferenceScheme refScheme : {
|
||||
EntityReferenceScheme::AllReferenceFirst, EntityReferenceScheme::AllReferenceLast,
|
||||
EntityReferenceScheme::AllReferenceThemselves, EntityReferenceScheme::AllReferenceNextCircular,
|
||||
EntityReferenceScheme::AllReferencePreviousCircular })
|
||||
{
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback = [this, refScheme, NumEntities]
|
||||
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllEntitiesReferenceOtherEntities_EntityIdsOnlyReferWithinASingleCall)
|
||||
{
|
||||
// This tests that entity id references get mapped correctly with multiple SpawnAllEntities calls. Each call should only map
|
||||
// the entities to other entities within the same call, regardless of forward or backward mapping.
|
||||
// For example, suppose entities 1, 2, and 3 refer to 4. In the first SpawnAllEntities call, entities 1-3 will refer to 4.
|
||||
// In the second SpawnAllEntities call, entities 1-3 will refer to the second 4, not the previously-spawned 4.
|
||||
for (EntityReferenceScheme refScheme :
|
||||
{ EntityReferenceScheme::AllReferenceFirst, EntityReferenceScheme::AllReferenceLast,
|
||||
EntityReferenceScheme::AllReferenceThemselves, EntityReferenceScheme::AllReferenceNextCircular,
|
||||
EntityReferenceScheme::AllReferencePreviousCircular
|
||||
})
|
||||
{
|
||||
// Make sure we start with a fresh ticket each time, or else each iteration through this loop would continue to build up
|
||||
// more and more entities.
|
||||
delete m_ticket;
|
||||
m_ticket = new AzFramework::EntitySpawnTicket(*m_spawnableAsset);
|
||||
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback = [this, refScheme, NumEntities]
|
||||
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
// Spawn twice.
|
||||
constexpr size_t NumSpawnAllCalls = 2;
|
||||
for (int spawns = 0; spawns < NumSpawnAllCalls; spawns++)
|
||||
{
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
|
||||
}
|
||||
|
||||
m_manager->ListEntities(*m_ticket, callback);
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash)
|
||||
{
|
||||
{
|
||||
@@ -363,6 +548,180 @@ namespace UnitTest
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllEntitiesReferenceOtherEntities_ForwardReferencesWorkInSingleCall)
|
||||
{
|
||||
constexpr EntityReferenceScheme refScheme = EntityReferenceScheme::AllReferenceNextCircular;
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
// Verify that by default, entities that refer to other entities that haven't been spawned yet have the correct references
|
||||
// when the spawning all occurs in the same call
|
||||
m_manager->SpawnEntities(*m_ticket, { 0, 1, 2, 3 });
|
||||
m_manager->ListEntities(*m_ticket, callback);
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllEntitiesReferenceOtherEntities_ForwardReferencesWorkAcrossCalls)
|
||||
{
|
||||
constexpr EntityReferenceScheme refScheme = EntityReferenceScheme::AllReferenceNextCircular;
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
// Verify that by default, entities that refer to other entities that haven't been spawned yet have the correct references
|
||||
// even when the spawning is across multiple calls
|
||||
m_manager->SpawnEntities(*m_ticket, { 0 });
|
||||
m_manager->SpawnEntities(*m_ticket, { 1 });
|
||||
m_manager->SpawnEntities(*m_ticket, { 2 });
|
||||
m_manager->SpawnEntities(*m_ticket, { 3 });
|
||||
m_manager->ListEntities(*m_ticket, callback);
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllEntitiesReferenceOtherEntities_ReferencesPointToFirstOrLatest)
|
||||
{
|
||||
// With SpawnEntities, entity references should either refer to the first entity that *will* be spawned, or the last entity
|
||||
// that *has* been spawned. This test will create entities 0 1 2 3 that all refer to entity 3, and it will create two batches
|
||||
// of those. In the first batch, they'll forward-reference. In the second batch, they should backward-reference, except for
|
||||
// the second entity 3, which will now refer to itself as the last one that's been spawned.
|
||||
constexpr EntityReferenceScheme refScheme = EntityReferenceScheme::AllReferenceLast;
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
size_t numElements = entities.size();
|
||||
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
const AZ::Entity* const entity = *(entities.begin() + i);
|
||||
|
||||
auto component = entity->FindComponent<ComponentWithEntityReference>();
|
||||
ASSERT_NE(nullptr, component);
|
||||
AZ::EntityId comparisonId;
|
||||
if (i < (numElements - 1))
|
||||
{
|
||||
// There are two batches of NumEntities elements. Every entity should either forward-reference or backward-reference
|
||||
// to the last entity of the first batch, except for the very last entity of the second batch, which should reference
|
||||
// itself.
|
||||
comparisonId = (*(entities.begin() + (NumEntities- 1)))->GetId();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The very last entity of the second batch should reference itself because it's now the latest instance of that
|
||||
// entity to be spawned.
|
||||
comparisonId = entity->GetId();
|
||||
}
|
||||
|
||||
EXPECT_EQ(comparisonId, component->m_entityReference);
|
||||
}
|
||||
};
|
||||
|
||||
// Create 2 batches of forward references. In the first batch, entities 0 1 2 will point forward to 3. In the second batch,
|
||||
// entities 0 1 2 will point *backward* to the first 3, and the second entity 3 will point to itself.
|
||||
m_manager->SpawnEntities(*m_ticket, { 0, 1, 2, 3 });
|
||||
m_manager->SpawnEntities(*m_ticket, { 0, 1, 2, 3 });
|
||||
m_manager->ListEntities(*m_ticket, callback);
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllEntitiesReferenceOtherEntities_MultipleSpawnsInSameCallReferenceCorrectly)
|
||||
{
|
||||
// With SpawnEntities, entity references should either refer to the first entity that *will* be spawned, or the last entity
|
||||
// that *has* been spawned. This test will create entities 0 1 2 3 that all refer to entity 3, and it will create three sets
|
||||
// of those in the same call, with the following results:
|
||||
// - The first 0 1 2 will forward-reference to the first 3
|
||||
// - The first 3 will reference itself
|
||||
// - The second 0 1 2 will backwards-reference to the first 3
|
||||
// - The second 3 will reference itself
|
||||
// - The third 0 1 2 will backwards-reference to the second 3
|
||||
// - The third 3 will reference itself
|
||||
constexpr EntityReferenceScheme refScheme = EntityReferenceScheme::AllReferenceLast;
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
size_t numElements = entities.size();
|
||||
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
const AZ::Entity* const entity = *(entities.begin() + i);
|
||||
|
||||
auto component = entity->FindComponent<ComponentWithEntityReference>();
|
||||
ASSERT_NE(nullptr, component);
|
||||
AZ::EntityId comparisonId;
|
||||
|
||||
if (i < ((NumEntities * 2) - 1))
|
||||
{
|
||||
// The first 7 entities (0 1 2 3 0 1 2) will all refer to the 4th one (1st '3').
|
||||
comparisonId = (*(entities.begin() + (NumEntities - 1)))->GetId();
|
||||
}
|
||||
else if (i < (numElements - 1))
|
||||
{
|
||||
// The next 4 entities (3 0 1 2) will all refer to the 8th one (2nd '3').
|
||||
comparisonId = (*(entities.begin() + ((NumEntities * 2) - 1)))->GetId();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The very last entity (3) will reference itself (3rd '3').
|
||||
comparisonId = entity->GetId();
|
||||
}
|
||||
|
||||
EXPECT_EQ(comparisonId, component->m_entityReference);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the 3 batches of entities 0, 1, 2, 3. The entity references should work as described at the top of the test.
|
||||
m_manager->SpawnEntities(*m_ticket, { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 });
|
||||
m_manager->ListEntities(*m_ticket, callback);
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllEntitiesReferenceOtherEntities_OptionalFlagClearsReferenceMap)
|
||||
{
|
||||
constexpr EntityReferenceScheme refScheme = EntityReferenceScheme::AllReferenceLast;
|
||||
constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
// By setting the "referencePreviouslySpawnedEntities" flag to false, the map will get cleared on each call, so in both batches
|
||||
// the entities will forward-reference to the last entity in the batch. If the flag were true, entities 0 1 2 in the second
|
||||
// batch would refer backwards to the first entity 3.
|
||||
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgsSecondBatch;
|
||||
optionalArgsSecondBatch.m_completionCallback = AZStd::move(callback);
|
||||
optionalArgsSecondBatch.m_referencePreviouslySpawnedEntities = false;
|
||||
|
||||
m_manager->SpawnEntities(*m_ticket, { 0, 1, 2, 3 }, optionalArgsSecondBatch);
|
||||
m_manager->SpawnEntities(*m_ticket, { 0, 1, 2, 3 }, AZStd::move(optionalArgsSecondBatch));
|
||||
m_manager->ListEntities(*m_ticket, callback);
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash)
|
||||
{
|
||||
{
|
||||
|
||||
@@ -56,7 +56,8 @@ namespace AzAssetBrowserRequestHandlerPrivate
|
||||
using namespace AzToolsFramework;
|
||||
using namespace AzToolsFramework::AssetBrowser;
|
||||
// return true ONLY if we can handle the drop request in the viewport.
|
||||
bool CanSpawnEntityForProduct(const ProductAssetBrowserEntry* product)
|
||||
bool CanSpawnEntityForProduct(const ProductAssetBrowserEntry* product,
|
||||
AZStd::optional<const AZStd::vector<AZ::Data::AssetType>> optionalProductAssetTypes = AZStd::nullopt)
|
||||
{
|
||||
if (!product)
|
||||
{
|
||||
@@ -70,7 +71,6 @@ namespace AzAssetBrowserRequestHandlerPrivate
|
||||
|
||||
bool canCreateComponent = false;
|
||||
AZ::AssetTypeInfoBus::EventResult(canCreateComponent, product->GetAssetType(), &AZ::AssetTypeInfo::CanCreateComponent, product->GetAssetId());
|
||||
|
||||
if (!canCreateComponent)
|
||||
{
|
||||
return false;
|
||||
@@ -78,16 +78,25 @@ namespace AzAssetBrowserRequestHandlerPrivate
|
||||
|
||||
AZ::Uuid componentTypeId = AZ::Uuid::CreateNull();
|
||||
AZ::AssetTypeInfoBus::EventResult(componentTypeId, product->GetAssetType(), &AZ::AssetTypeInfo::GetComponentTypeId);
|
||||
|
||||
if (!componentTypeId.IsNull())
|
||||
if (componentTypeId.IsNull())
|
||||
{
|
||||
// we have a component type that handles this asset.
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (optionalProductAssetTypes.has_value())
|
||||
{
|
||||
bool hasConflictingProducts = false;
|
||||
AZ::AssetTypeInfoBus::EventResult(hasConflictingProducts, product->GetAssetType(), &AZ::AssetTypeInfo::HasConflictingProducts, optionalProductAssetTypes.value());
|
||||
if (hasConflictingProducts)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// additional operations can be added here.
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpawnEntityAtPoint(const ProductAssetBrowserEntry* product, AzQtComponents::ViewportDragContext* viewportDragContext, EntityIdList& spawnList, AzFramework::SliceInstantiationTicket& spawnTicket)
|
||||
@@ -511,9 +520,16 @@ void AzAssetBrowserRequestHandler::Drop(QDropEvent* event, AzQtComponents::DragA
|
||||
}
|
||||
|
||||
// Handle products
|
||||
AZStd::vector<AZ::Data::AssetType> productAssetTypes;
|
||||
productAssetTypes.reserve(products.size());
|
||||
for (const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* entry : products)
|
||||
{
|
||||
productAssetTypes.emplace_back(entry->GetAssetType());
|
||||
}
|
||||
|
||||
for (const ProductAssetBrowserEntry* product : products)
|
||||
{
|
||||
if (CanSpawnEntityForProduct(product))
|
||||
if (CanSpawnEntityForProduct(product, productAssetTypes))
|
||||
{
|
||||
SpawnEntityAtPoint(product, viewportDragContext, spawnedEntities, spawnTicket);
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ ly_add_target(
|
||||
Legacy::EditorCore
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::AtomViewportDisplayInfo
|
||||
Legacy::EditorCommon
|
||||
)
|
||||
ly_add_source_properties(
|
||||
SOURCES CryEdit.cpp
|
||||
|
||||
@@ -208,22 +208,9 @@ WelcomeScreenDialog QLabel
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QLabel#titleLabel
|
||||
WelcomeScreenDialog QLabel#currentProjectLabel
|
||||
{
|
||||
font-size: 22px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QLabel#bodyLabel
|
||||
{
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QLabel[fontStyle="sectionTitle"], QLabel#titleLabel[fontStyle="sectionTitle"], QLabel#documentationLink
|
||||
{
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QPushButton
|
||||
@@ -232,36 +219,20 @@ WelcomeScreenDialog QPushButton
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QFrame#viewContainer
|
||||
{
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QFrame#viewContainer[articleStyle="pinned"]
|
||||
{
|
||||
background: rgba(180,139,255,5%);
|
||||
border: 1px solid #B48BFF;
|
||||
box-shadow: 0 0 4px 0 rgba(0,0,0,50%);
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QWidget#articleViewContainerRoot
|
||||
{
|
||||
background: #111111;
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QScrollArea#previewArea
|
||||
WelcomeScreenDialog QWidget#levelViewFTUEContainer
|
||||
{
|
||||
background-color: transparent;
|
||||
background: #282828;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QWidget#articleViewContents
|
||||
{
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
WelcomeScreenDialog QFrame#imageFrame
|
||||
{
|
||||
background-color: transparent;
|
||||
QTableWidget#recentLevelTable::item {
|
||||
background-color: rgb(64,64,64);
|
||||
margin-bottom: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Particle Editor */
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
#include "ui_ViewportTitleDlg.h"
|
||||
@@ -57,13 +58,16 @@ inline namespace Helpers
|
||||
{
|
||||
void ToggleHelpers()
|
||||
{
|
||||
GetIEditor()->GetDisplaySettings()->DisplayHelpers(!GetIEditor()->GetDisplaySettings()->IsDisplayHelpers());
|
||||
const bool newValue = !GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
|
||||
GetIEditor()->GetDisplaySettings()->DisplayHelpers(newValue);
|
||||
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
|
||||
|
||||
if (GetIEditor()->GetDisplaySettings()->IsDisplayHelpers() == false)
|
||||
if (newValue == false)
|
||||
{
|
||||
GetIEditor()->GetObjectManager()->SendEvent(EVENT_HIDE_HELPER);
|
||||
}
|
||||
AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Broadcast(
|
||||
&AzToolsFramework::ViewportInteraction::ViewportSettingNotifications::OnDrawHelpersChanged, newValue);
|
||||
}
|
||||
|
||||
bool IsHelpersShown()
|
||||
@@ -126,6 +130,7 @@ CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
|
||||
SetupCameraDropdownMenu();
|
||||
SetupResolutionDropdownMenu();
|
||||
SetupViewportInformationMenu();
|
||||
SetupHelpersButton();
|
||||
SetupOverflowMenu();
|
||||
|
||||
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, gSettings.bMuteAudio ? m_oMuteAudioRequest : m_oUnmuteAudioRequest);
|
||||
@@ -207,15 +212,16 @@ void CViewportTitleDlg::SetupViewportInformationMenu()
|
||||
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::SetupHelpersButton()
|
||||
{
|
||||
connect(m_ui->m_helpers, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleHelpers);
|
||||
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::SetupOverflowMenu()
|
||||
{
|
||||
// Setup the overflow menu
|
||||
QMenu* overFlowMenu = new QMenu(this);
|
||||
m_debugHelpersAction = new QAction("Debug Helpers", overFlowMenu);
|
||||
m_debugHelpersAction->setCheckable(true);
|
||||
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
|
||||
connect(m_debugHelpersAction, &QAction::triggered, this, &CViewportTitleDlg::OnToggleHelpers);
|
||||
overFlowMenu->addAction(m_debugHelpersAction);
|
||||
|
||||
m_audioMuteAction = new QAction("Mute Audio", overFlowMenu);
|
||||
connect(m_audioMuteAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedMuteAudio);
|
||||
@@ -329,7 +335,7 @@ void CViewportTitleDlg::OnMaximize()
|
||||
void CViewportTitleDlg::OnToggleHelpers()
|
||||
{
|
||||
Helpers::ToggleHelpers();
|
||||
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
|
||||
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::SetNoViewportInfo()
|
||||
@@ -755,7 +761,7 @@ void CViewportTitleDlg::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
switch (event)
|
||||
{
|
||||
case eNotify_OnDisplayRenderUpdate:
|
||||
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
|
||||
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
|
||||
break;
|
||||
case eNotify_OnBeginGameMode:
|
||||
case eNotify_OnEndGameMode:
|
||||
|
||||
@@ -102,6 +102,7 @@ protected:
|
||||
void SetupResolutionDropdownMenu();
|
||||
void SetupViewportInformationMenu();
|
||||
void SetupOverflowMenu();
|
||||
void SetupHelpersButton();
|
||||
|
||||
QString m_title;
|
||||
|
||||
@@ -172,7 +173,6 @@ protected:
|
||||
QAction* m_normalInformationAction = nullptr;
|
||||
QAction* m_fullInformationAction = nullptr;
|
||||
QAction* m_compactInformationAction = nullptr;
|
||||
QAction* m_debugHelpersAction = nullptr;
|
||||
QAction* m_audioMuteAction = nullptr;
|
||||
QAction* m_enableVRAction = nullptr;
|
||||
QAction* m_enableGridSnappingAction = nullptr;
|
||||
|
||||
@@ -81,6 +81,18 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="m_helpers">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/Menu/helpers.svg</normaloff>:/Menu/helpers.svg
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="m_resolutionMenu">
|
||||
<property name="icon">
|
||||
@@ -94,7 +106,7 @@
|
||||
<widget class="QToolButton" name="m_overflowBtn">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/stylesheet/img/UI20/menu-centered.svg</normaloff>:/stylesheet/img/UI20/menu-centered.svg
|
||||
<normaloff>:/Menu/menu.svg</normaloff>:/Menu/menu.svg
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:263e95489560dac6e5944ef3caba13e598f83ddead324b943ad7735ba015e1a9
|
||||
size 70727
|
||||
@@ -15,7 +15,8 @@
|
||||
#include "WelcomeScreenDialog.h"
|
||||
|
||||
// Qt
|
||||
#include <QStringListModel>
|
||||
#include <QTableWidget>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QToolTip>
|
||||
#include <QMenu>
|
||||
#include <QDesktopServices>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <QScreen>
|
||||
#include <QDesktopWidget>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
@@ -74,65 +76,39 @@ static int GetSmallestScreenHeight()
|
||||
WelcomeScreenDialog::WelcomeScreenDialog(QWidget* pParent)
|
||||
: QDialog(new WindowDecorationWrapper(WindowDecorationWrapper::OptionAutoAttach | WindowDecorationWrapper::OptionAutoTitleBarButtons, pParent), Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint | Qt::WindowTitleHint)
|
||||
, ui(new Ui::WelcomeScreenDialog)
|
||||
, m_pRecentListModel(new QStringListModel(this))
|
||||
, m_pRecentList(nullptr)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Make our welcome screen checkboxes appear as toggle switches
|
||||
AzQtComponents::CheckBox::applyToggleSwitchStyle(ui->autoLoadLevel);
|
||||
AzQtComponents::CheckBox::applyToggleSwitchStyle(ui->showOnStartup);
|
||||
ui->recentLevelTable->setColumnCount(3);
|
||||
ui->recentLevelTable->setMouseTracking(true);
|
||||
ui->recentLevelTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
ui->recentLevelTable->horizontalHeader()->hide();
|
||||
ui->recentLevelTable->verticalHeader()->hide();
|
||||
ui->recentLevelTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
ui->recentLevelTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->recentLevelTable->setIconSize(QSize(20, 20));
|
||||
installEventFilter(this);
|
||||
|
||||
ui->autoLoadLevel->setChecked(gSettings.bAutoloadLastLevelAtStartup);
|
||||
ui->showOnStartup->setChecked(!gSettings.bShowDashboardAtStartup);
|
||||
|
||||
ui->recentLevelList->setModel(m_pRecentListModel);
|
||||
ui->recentLevelList->setMouseTracking(true);
|
||||
ui->recentLevelList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
auto currentProjectButtonMenu = new QMenu();
|
||||
|
||||
ui->currentProjectButton->setMenu(currentProjectButtonMenu);
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
ui->currentProjectButton->setText(projectName.c_str());
|
||||
ui->currentProjectButton->adjustSize();
|
||||
ui->currentProjectButton->setMinimumWidth(ui->currentProjectButton->width() + 40);
|
||||
ui->currentProjectName->setText(projectName.c_str());
|
||||
|
||||
ui->documentationLink->setCursor(Qt::PointingHandCursor);
|
||||
ui->documentationLink->installEventFilter(this);
|
||||
ui->newLevelButton->setDefault(true);
|
||||
|
||||
connect(ui->recentLevelList, &QWidget::customContextMenuRequested, this, &WelcomeScreenDialog::OnShowContextMenu);
|
||||
// Hide these buttons until the new functionality is added
|
||||
ui->gridButton->hide();
|
||||
ui->objectListButton->hide();
|
||||
ui->switchProjectButton->hide();
|
||||
|
||||
connect(ui->recentLevelList, &QListView::entered, this, &WelcomeScreenDialog::OnShowToolTip);
|
||||
connect(ui->recentLevelList, &QListView::clicked, this, &WelcomeScreenDialog::OnRecentLevelListItemClicked);
|
||||
connect(ui->recentLevelTable, &QWidget::customContextMenuRequested, this, &WelcomeScreenDialog::OnShowContextMenu);
|
||||
|
||||
connect(ui->recentLevelTable, &QTableWidget::entered, this, &WelcomeScreenDialog::OnShowToolTip);
|
||||
connect(ui->recentLevelTable, &QTableWidget::clicked, this, &WelcomeScreenDialog::OnRecentLevelTableItemClicked);
|
||||
|
||||
connect(ui->newLevelButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnNewLevelBtnClicked);
|
||||
connect(ui->levelFileLabel, &QLabel::linkActivated, this, &WelcomeScreenDialog::OnNewLevelLabelClicked);
|
||||
connect(ui->openLevelButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnOpenLevelBtnClicked);
|
||||
|
||||
connect(ui->newSliceButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnNewSliceBtnClicked);
|
||||
connect(ui->openSliceButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnOpenSliceBtnClicked);
|
||||
|
||||
connect(ui->documentationButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnDocumentationBtnClicked);
|
||||
connect(ui->showOnStartup, &QCheckBox::clicked, this, &WelcomeScreenDialog::OnShowOnStartupBtnClicked);
|
||||
connect(ui->autoLoadLevel, &QCheckBox::clicked, this, &WelcomeScreenDialog::OnAutoLoadLevelBtnClicked);
|
||||
|
||||
m_manifest = new News::ResourceManifest(
|
||||
std::bind(&WelcomeScreenDialog::SyncSuccess, this),
|
||||
std::bind(&WelcomeScreenDialog::SyncFail, this, std::placeholders::_1),
|
||||
std::bind(&WelcomeScreenDialog::SyncUpdate, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_articleViewContainer = new News::ArticleViewContainer(this, *m_manifest);
|
||||
connect(m_articleViewContainer, &News::ArticleViewContainer::scrolled,
|
||||
this, &WelcomeScreenDialog::previewAreaScrolled);
|
||||
ui->articleViewContainerRoot->layout()->addWidget(m_articleViewContainer);
|
||||
|
||||
m_manifest->Sync();
|
||||
|
||||
#ifndef ENABLE_SLICE_EDITOR
|
||||
ui->newSliceButton->hide();
|
||||
ui->openSliceButton->hide();
|
||||
#endif
|
||||
|
||||
// Adjust the height, if need be
|
||||
// Do it in the constructor so that the WindowDecoratorWrapper handles it correctly
|
||||
int smallestHeight = GetSmallestScreenHeight();
|
||||
@@ -153,16 +129,10 @@ WelcomeScreenDialog::WelcomeScreenDialog(QWidget* pParent)
|
||||
WelcomeScreenDialog::~WelcomeScreenDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete m_manifest;
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::done(int result)
|
||||
{
|
||||
if (m_waitingOnAsync)
|
||||
{
|
||||
m_manifest->Abort();
|
||||
}
|
||||
|
||||
QDialog::done(result);
|
||||
}
|
||||
|
||||
@@ -173,13 +143,11 @@ const QString& WelcomeScreenDialog::GetLevelPath()
|
||||
|
||||
bool WelcomeScreenDialog::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched == ui->documentationLink)
|
||||
if (event->type() == QEvent::Show)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonRelease)
|
||||
{
|
||||
OnDocumentationBtnClicked(false);
|
||||
return true;
|
||||
}
|
||||
ui->recentLevelTable->horizontalHeader()->resizeSection(0, ui->nameLabel->width());
|
||||
ui->recentLevelTable->horizontalHeader()->resizeSection(1, ui->modifiedLabel->width());
|
||||
ui->recentLevelTable->horizontalHeader()->resizeSection(2, ui->typeLabel->width());
|
||||
}
|
||||
|
||||
return QDialog::eventFilter(watched, event);
|
||||
@@ -207,7 +175,9 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
|
||||
int nCurDir = sCurDir.length();
|
||||
|
||||
int recentListSize = pList->GetSize();
|
||||
for (int i = 0; i < recentListSize; ++i)
|
||||
int currentRow = 0;
|
||||
ui->recentLevelTable->setRowCount(recentListSize);
|
||||
for (int i = 0; i < recentListSize; ++i)
|
||||
{
|
||||
const QString& recentFile = pList->m_arrNames[i];
|
||||
if (recentFile.endsWith(m_levelExtension))
|
||||
@@ -218,7 +188,7 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
|
||||
if (sCurEntryDir.compare(sCurDir, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
QString fullPath = recentFile;
|
||||
QString name = Path::GetFileName(fullPath);
|
||||
const QString name = Path::GetFile(fullPath);
|
||||
|
||||
Path::ConvertSlashToBackSlash(fullPath);
|
||||
fullPath = Path::ToUnixPath(fullPath.toLower());
|
||||
@@ -226,18 +196,34 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
|
||||
|
||||
if (fullPath.contains(gamePath))
|
||||
{
|
||||
m_pRecentListModel->setStringList(m_pRecentListModel->stringList() << QString(name));
|
||||
if (gSettings.prefabSystem)
|
||||
{
|
||||
QIcon icon;
|
||||
icon.addFile(QString::fromUtf8(":/Level/level.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
||||
ui->recentLevelTable->setItem(currentRow, 0, new QTableWidgetItem(icon, name));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->recentLevelTable->setItem(currentRow, 0, new QTableWidgetItem(name));
|
||||
}
|
||||
QFileInfo file(recentFile);
|
||||
QDateTime dateTime = file.lastModified();
|
||||
QString date = QLocale::system().toString(dateTime.date(), QLocale::ShortFormat) + " " +
|
||||
QLocale::system().toString(dateTime.time(), QLocale::LongFormat);
|
||||
ui->recentLevelTable->setItem(currentRow, 1, new QTableWidgetItem(date));
|
||||
ui->recentLevelTable->setItem(currentRow++, 2, new QTableWidgetItem(tr("Level")));
|
||||
m_levels.push_back(std::make_pair(name, recentFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ui->recentLevelTable->setRowCount(currentRow);
|
||||
ui->recentLevelTable->setMinimumHeight(currentRow * ui->recentLevelTable->verticalHeader()->defaultSectionSize());
|
||||
ui->recentLevelTable->setMaximumHeight(currentRow * ui->recentLevelTable->verticalHeader()->defaultSectionSize());
|
||||
ui->levelFileLabel->setVisible(currentRow ? false : true);
|
||||
|
||||
ui->recentLevelList->setCurrentIndex(QModelIndex());
|
||||
int rowSize = ui->recentLevelList->sizeHintForRow(0) + ui->recentLevelList->spacing() * 2;
|
||||
ui->recentLevelList->setMinimumHeight(m_pRecentListModel->rowCount() * rowSize);
|
||||
ui->recentLevelList->setMaximumHeight(m_pRecentListModel->rowCount() * rowSize);
|
||||
ui->recentLevelTable->setCurrentIndex(QModelIndex());
|
||||
}
|
||||
|
||||
|
||||
@@ -245,7 +231,7 @@ void WelcomeScreenDialog::RemoveLevelEntry(int index)
|
||||
{
|
||||
TNamePathPair levelPath = m_levels[index];
|
||||
|
||||
m_pRecentListModel->removeRow(index);
|
||||
ui->recentLevelTable->removeRow(index);
|
||||
m_levels.erase(m_levels.begin() + index);
|
||||
|
||||
|
||||
@@ -284,21 +270,18 @@ void WelcomeScreenDialog::OnShowToolTip(const QModelIndex& index)
|
||||
{
|
||||
const QString& fullPath = m_levels[index.row()].second;
|
||||
|
||||
//TEMPORARY:Begin This can be put back once the main window is in Qt
|
||||
//QRect itemRect = ui->recentLevelList->visualRect(index);
|
||||
QToolTip::showText(QCursor::pos(), QString("Open level: %1").arg(fullPath) /*, ui->recentLevelList, itemRect*/);
|
||||
//TEMPORARY:END
|
||||
QToolTip::showText(QCursor::pos(), QString("Open level: %1").arg(fullPath));
|
||||
}
|
||||
|
||||
|
||||
void WelcomeScreenDialog::OnShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
QModelIndex index = ui->recentLevelList->indexAt(pos);
|
||||
QModelIndex index = ui->recentLevelTable->indexAt(pos);
|
||||
if (index.isValid())
|
||||
{
|
||||
QString level = m_pRecentListModel->data(index, 0).toString();
|
||||
QString level = ui->recentLevelTable->itemAt(pos)->text();
|
||||
|
||||
QPoint globalPos = ui->recentLevelList->viewport()->mapToGlobal(pos);
|
||||
QPoint globalPos = ui->recentLevelTable->viewport()->mapToGlobal(pos);
|
||||
|
||||
QMenu contextMenu;
|
||||
contextMenu.addAction(QString("Remove " + level + " from recent list"));
|
||||
@@ -310,13 +293,16 @@ void WelcomeScreenDialog::OnShowContextMenu(const QPoint& pos)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WelcomeScreenDialog::OnNewLevelBtnClicked([[maybe_unused]] bool checked)
|
||||
{
|
||||
m_levelPath = "new";
|
||||
accept();
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnNewLevelLabelClicked([[maybe_unused]] const QString& path)
|
||||
{
|
||||
OnNewLevelBtnClicked(true);
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnOpenLevelBtnClicked([[maybe_unused]] bool checked)
|
||||
{
|
||||
@@ -329,27 +315,7 @@ void WelcomeScreenDialog::OnOpenLevelBtnClicked([[maybe_unused]] bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnNewSliceBtnClicked([[maybe_unused]] bool checked)
|
||||
{
|
||||
m_levelPath = "new slice";
|
||||
accept();
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnOpenSliceBtnClicked(bool)
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(MainWindow::instance(),
|
||||
tr("Open Slice"),
|
||||
Path::GetEditingGameDataFolder().c_str(),
|
||||
tr("Slice (*.slice)"));
|
||||
|
||||
if (!fileName.isEmpty())
|
||||
{
|
||||
m_levelPath = fileName;
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnRecentLevelListItemClicked(const QModelIndex& modelIndex)
|
||||
void WelcomeScreenDialog::OnRecentLevelTableItemClicked(const QModelIndex& modelIndex)
|
||||
{
|
||||
int index = modelIndex.row();
|
||||
|
||||
@@ -365,45 +331,6 @@ void WelcomeScreenDialog::OnCloseBtnClicked([[maybe_unused]] bool checked)
|
||||
accept();
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnAutoLoadLevelBtnClicked(bool checked)
|
||||
{
|
||||
gSettings.bAutoloadLastLevelAtStartup = checked;
|
||||
gSettings.Save();
|
||||
}
|
||||
|
||||
|
||||
void WelcomeScreenDialog::OnShowOnStartupBtnClicked(bool checked)
|
||||
{
|
||||
gSettings.bShowDashboardAtStartup = !checked;
|
||||
gSettings.Save();
|
||||
|
||||
if (gSettings.bShowDashboardAtStartup == false)
|
||||
{
|
||||
QMessageBox msgBox(AzToolsFramework::GetActiveWindow());
|
||||
msgBox.setWindowTitle(QObject::tr("Skip the Welcome dialog on startup"));
|
||||
msgBox.setText(QObject::tr("You may re-enable the Welcome dialog at any time by going to Edit > Editor Settings > Global Preferences in the menu bar."));
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::OnDocumentationBtnClicked([[maybe_unused]] bool checked)
|
||||
{
|
||||
QString webLink = tr("https://aws.amazon.com/lumberyard/support/");
|
||||
QDesktopServices::openUrl(QUrl(webLink));
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::SyncFail([[maybe_unused]] News::ErrorCode error)
|
||||
{
|
||||
m_articleViewContainer->AddErrorMessage();
|
||||
m_waitingOnAsync = false;
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::SyncSuccess()
|
||||
{
|
||||
m_articleViewContainer->PopulateArticles();
|
||||
m_waitingOnAsync = false;
|
||||
}
|
||||
|
||||
void WelcomeScreenDialog::previewAreaScrolled()
|
||||
{
|
||||
//this should only be reported once per session
|
||||
|
||||
@@ -52,13 +52,9 @@ private:
|
||||
Ui::WelcomeScreenDialog* ui;
|
||||
|
||||
QString m_levelPath;
|
||||
QStringListModel* m_pRecentListModel;
|
||||
TNameFullPathArray m_levels;
|
||||
RecentFileList* m_pRecentList;
|
||||
News::ResourceManifest* m_manifest = nullptr;
|
||||
News::ArticleViewContainer* m_articleViewContainer = nullptr;
|
||||
const char* m_levelExtension = nullptr;
|
||||
bool m_waitingOnAsync = true;
|
||||
bool m_messageScrollReported = false;
|
||||
|
||||
void RemoveLevelEntry(int index);
|
||||
@@ -66,19 +62,11 @@ private:
|
||||
void OnShowToolTip(const QModelIndex& index);
|
||||
void OnShowContextMenu(const QPoint& point);
|
||||
void OnNewLevelBtnClicked(bool checked);
|
||||
void OnNewLevelLabelClicked(const QString& checked);
|
||||
void OnOpenLevelBtnClicked(bool checked);
|
||||
void OnNewSliceBtnClicked(bool checked);
|
||||
void OnOpenSliceBtnClicked(bool checked);
|
||||
void OnRecentLevelListItemClicked(const QModelIndex& index);
|
||||
void OnGettingStartedBtnClicked(bool checked);
|
||||
void OnTutorialsBtnClicked(bool checked);
|
||||
void OnDocumentationBtnClicked(bool checked);
|
||||
void OnForumsBtnClicked(bool checked);
|
||||
void OnAutoLoadLevelBtnClicked(bool checked);
|
||||
void OnShowOnStartupBtnClicked(bool checked);
|
||||
void OnRecentLevelTableItemClicked(const QModelIndex& index);
|
||||
void OnCloseBtnClicked(bool checked);
|
||||
|
||||
void SyncUpdate(const QString& /* message */, News::LogType /* logType */) {}
|
||||
void SyncFail(News::ErrorCode error);
|
||||
void SyncSuccess();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="WelcomeScreenDialog">
|
||||
<file>WelcomeScreenDialogHeader.png</file>
|
||||
<file>DefaultActiveProject.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
<ui version="4.0">
|
||||
<class>WelcomeScreenDialog</class>
|
||||
<widget class="QWidget" name="WelcomeScreenDialog">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
<width>945</width>
|
||||
<height>639</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -18,21 +21,21 @@
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
<width>945</width>
|
||||
<height>639</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>16777215</height>
|
||||
<width>945</width>
|
||||
<height>639</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Welcome to Open 3D Engine</string>
|
||||
<string>Welcome to O3DE</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
@@ -53,100 +56,6 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="projectViewContainer" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="projectViewContainer_layout" stretch="0,0,1,0,0">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="currentProjectLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Current project:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="currentProjectButton">
|
||||
<property name="text">
|
||||
<string>Current Project Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: "black"</string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="bodyContainer">
|
||||
<property name="spacing">
|
||||
@@ -165,20 +74,26 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="levelViewFTUEContainer" native="true">
|
||||
<widget class="QWidget" name="articleViewContainerRoot" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<width>183</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<width>183</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<layout class="QVBoxLayout" name="newsContainerLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -191,6 +106,143 @@
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="currentProjectLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Active project</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="activeProjectIcon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>126</width>
|
||||
<height>167</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>126</width>
|
||||
<height>167</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="WelcomeScreenDialog.qrc">:/WelcomeScreenDialog/DefaultActiveProject.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="currentProjectName">
|
||||
<property name="text">
|
||||
<string>MyGame</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="switchProjectButton">
|
||||
<property name="text">
|
||||
<string>Switch project...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="levelViewFTUEContainer" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>762</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>762</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -227,7 +279,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open or create a level</string>
|
||||
<string>Recent Files</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>-1</number>
|
||||
@@ -255,16 +307,6 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="recentLevelList">
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -310,8 +352,26 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="newLevelButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>156</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>156</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New level...</string>
|
||||
<string>Create new...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -333,8 +393,67 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="openLevelButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>156</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>156</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open level...</string>
|
||||
<string>Open...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="objectListButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../Framework/AzQtComponents/AzQtComponents/Components/resources.qrc">
|
||||
<normaloff>:/stylesheet/img/UI20/toolbar/Object_list.svg</normaloff>:/stylesheet/img/UI20/toolbar/Object_list.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="gridButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../Framework/AzQtComponents/AzQtComponents/Components/resources.qrc">
|
||||
<normaloff>:/stylesheet/img/UI20/toolbar/Grid.svg</normaloff>:/stylesheet/img/UI20/toolbar/Grid.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -358,65 +477,130 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="horizontalWidget_2" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="2,2,1">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="sliceControlContainer">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="newSliceButton">
|
||||
<property name="text">
|
||||
<string>New slice...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="openSliceButton">
|
||||
<property name="text">
|
||||
<string>Open slice...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<item>
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="modifiedLabel">
|
||||
<property name="text">
|
||||
<string>Last modified</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="typeLabel">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7" stretch="0,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="levelFileLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No level file created yet for this project. <a href="#">Create one</a> now.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="recentLevelTable">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>1</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>48</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="maximumSize">
|
||||
@@ -439,205 +623,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QWidget" name="documentationLinkContainer" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="documentationButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>info</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/stylesheet/img/UI20/Info.svg</normaloff>:/stylesheet/img/UI20/Info.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="documentationLink">
|
||||
<property name="text">
|
||||
<string>Documentation and tutorials</string>
|
||||
</property>
|
||||
<property name="class" stdset="0">
|
||||
<string>link</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: "black"</string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="articleViewContainerRoot" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>480</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="newsContainerLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: "black"</string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignRight">
|
||||
<widget class="QWidget" name="optionsViewContainer" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="optionsViewContainer_layout">
|
||||
<property name="spacing">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoLoadLevel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto-load last opened level on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showOnStartup">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip this dialog on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="WelcomeScreenDialog.qrc"/>
|
||||
<include location="../../../Framework/AzQtComponents/AzQtComponents/Components/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:53b846352880d940621b14b1ea9514e0a4c95aa6ead4d00234a98684c061c04f
|
||||
size 29505
|
||||
@@ -31,5 +31,6 @@
|
||||
<file>CarrotArrowDown.svg</file>
|
||||
<file>Summary.svg</file>
|
||||
<file>WindowClose.svg</file>
|
||||
<file>Warning.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="24" height="24" fill="#444444"/>
|
||||
<rect x="10" y="6" width="4" height="15" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2L22 22H2L12 2ZM13 20V18H11V20H13ZM13 7H11V16.0862H13V7Z" fill="#F0C32D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 333 B |
@@ -206,7 +206,11 @@ namespace O3DE::ProjectManager
|
||||
PythonBindingsInterface::Get()->AddProject(projectInfo.m_path);
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path);
|
||||
if (!m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for template."));
|
||||
return;
|
||||
}
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
projectInfo.m_needsBuild = true;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <PathValidator.h>
|
||||
|
||||
@@ -46,28 +46,28 @@ namespace O3DE::ProjectManager
|
||||
m_engineVersion->lineEdit()->setReadOnly(true);
|
||||
layout->addWidget(m_engineVersion);
|
||||
|
||||
m_thirdParty = new FormBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this);
|
||||
m_thirdParty = new FormFolderBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this);
|
||||
m_thirdParty->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_thirdParty->lineEdit()->setReadOnly(true);
|
||||
m_thirdParty->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
connect(m_thirdParty->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_thirdParty);
|
||||
|
||||
m_defaultProjects = new FormBrowseEditWidget(tr("Default Projects Folder"), engineInfo.m_defaultProjectsFolder, this);
|
||||
m_defaultProjects = new FormFolderBrowseEditWidget(tr("Default Projects Folder"), engineInfo.m_defaultProjectsFolder, this);
|
||||
m_defaultProjects->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_defaultProjects->lineEdit()->setReadOnly(true);
|
||||
m_defaultProjects->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
connect(m_defaultProjects->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_defaultProjects);
|
||||
|
||||
m_defaultGems = new FormBrowseEditWidget(tr("Default Gems Folder"), engineInfo.m_defaultGemsFolder, this);
|
||||
m_defaultGems = new FormFolderBrowseEditWidget(tr("Default Gems Folder"), engineInfo.m_defaultGemsFolder, this);
|
||||
m_defaultGems->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_defaultGems->lineEdit()->setReadOnly(true);
|
||||
m_defaultGems->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
connect(m_defaultGems->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_defaultGems);
|
||||
|
||||
m_defaultProjectTemplates = new FormBrowseEditWidget(tr("Default Project Templates Folder"), engineInfo.m_defaultTemplatesFolder, this);
|
||||
m_defaultProjectTemplates = new FormFolderBrowseEditWidget(tr("Default Project Templates Folder"), engineInfo.m_defaultTemplatesFolder, this);
|
||||
m_defaultProjectTemplates->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_defaultProjectTemplates->lineEdit()->setReadOnly(true);
|
||||
m_defaultProjectTemplates->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
|
||||
@@ -11,13 +11,9 @@
|
||||
*/
|
||||
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <AzQtComponents/Components/StyledLineEdit.h>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QStandardPaths>
|
||||
#include <QIcon>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -30,20 +26,4 @@ namespace O3DE::ProjectManager
|
||||
connect(browseButton, &QPushButton::pressed, this, &FormBrowseEditWidget::HandleBrowseButton);
|
||||
m_frameLayout->addWidget(browseButton);
|
||||
}
|
||||
|
||||
void FormBrowseEditWidget::HandleBrowseButton()
|
||||
{
|
||||
QString defaultPath = m_lineEdit->text();
|
||||
if (defaultPath.isEmpty())
|
||||
{
|
||||
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
}
|
||||
|
||||
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
|
||||
if (!directory.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(directory);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace O3DE::ProjectManager
|
||||
explicit FormBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormBrowseEditWidget() = default;
|
||||
|
||||
private slots:
|
||||
void HandleBrowseButton();
|
||||
protected slots:
|
||||
virtual void HandleBrowseButton() = 0;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <AzQtComponents/Components/StyledLineEdit.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
FormFolderBrowseEditWidget::FormFolderBrowseEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
|
||||
: FormBrowseEditWidget(labelText, valueText, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void FormFolderBrowseEditWidget::HandleBrowseButton()
|
||||
{
|
||||
QString defaultPath = m_lineEdit->text();
|
||||
if (defaultPath.isEmpty())
|
||||
{
|
||||
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
}
|
||||
|
||||
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
|
||||
if (!directory.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(directory);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class FormFolderBrowseEditWidget
|
||||
: public FormBrowseEditWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FormFolderBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormFolderBrowseEditWidget() = default;
|
||||
|
||||
protected:
|
||||
void HandleBrowseButton() override;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <FormImageBrowseEditWidget.h>
|
||||
#include <AzQtComponents/Components/StyledLineEdit.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
FormImageBrowseEditWidget::FormImageBrowseEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
|
||||
: FormBrowseEditWidget(labelText, valueText, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void FormImageBrowseEditWidget::HandleBrowseButton()
|
||||
{
|
||||
QString file = QDir::toNativeSeparators(QFileDialog::getOpenFileName(
|
||||
this, tr("Select Image"), m_lineEdit->text(), tr("PNG (*.png)")));
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(file);
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class FormImageBrowseEditWidget
|
||||
: public FormBrowseEditWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FormImageBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormImageBrowseEditWidget() = default;
|
||||
|
||||
protected:
|
||||
void HandleBrowseButton() override;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <GemCatalog/GemListHeaderWidget.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <GemCatalog/GemRequirementDialog.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
@@ -144,12 +145,23 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath)
|
||||
bool GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath)
|
||||
{
|
||||
IPythonBindings* pythonBindings = PythonBindingsInterface::Get();
|
||||
QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
|
||||
QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
|
||||
|
||||
if (m_gemModel->DoGemsToBeAddedHaveRequirements())
|
||||
{
|
||||
GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, toBeAdded, this);
|
||||
confirmRequirementsDialog->exec();
|
||||
|
||||
if (confirmRequirementsDialog->GetButtonResult() != QDialogButtonBox::ApplyRole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const QModelIndex& modelIndex : toBeAdded)
|
||||
{
|
||||
const QString gemPath = GemModel::GetPath(modelIndex);
|
||||
@@ -158,6 +170,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +183,12 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ProjectManagerScreen GemCatalogScreen::GetScreenEnum()
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace O3DE::ProjectManager
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
void ReinitForProject(const QString& projectPath, bool isNewProject);
|
||||
void EnableDisableGemsForProject(const QString& projectPath);
|
||||
bool EnableDisableGemsForProject(const QString& projectPath);
|
||||
|
||||
private:
|
||||
void FillModel(const QString& projectPath, bool isNewProject);
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace O3DE::ProjectManager
|
||||
Platforms m_platforms;
|
||||
Types m_types; //! Asset and/or Code and/or Tool
|
||||
QStringList m_features;
|
||||
QString m_requirement;
|
||||
QString m_directoryLink;
|
||||
QString m_documentationLink;
|
||||
QString m_version = "Unknown Version";
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <QLabel>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QIcon>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -70,6 +71,22 @@ namespace O3DE::ProjectManager
|
||||
m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex));
|
||||
m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex));
|
||||
|
||||
if (m_model->HasRequirement(modelIndex))
|
||||
{
|
||||
m_reqirementsIconLabel->show();
|
||||
m_reqirementsTitleLabel->show();
|
||||
m_reqirementsTextLabel->show();
|
||||
|
||||
m_reqirementsTitleLabel->setText("Requirement");
|
||||
m_reqirementsTextLabel->setText(m_model->GetRequirement(modelIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reqirementsIconLabel->hide();
|
||||
m_reqirementsTitleLabel->hide();
|
||||
m_reqirementsTextLabel->hide();
|
||||
}
|
||||
|
||||
// Depending and conflicting gems
|
||||
m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex));
|
||||
m_conflictingGems->Update("Conflicting Gems", "The following Gems will be automatically disabled with this Gem.", m_model->GetConflictingGemNames(modelIndex));
|
||||
@@ -134,6 +151,28 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
// Requirements
|
||||
m_reqirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor);
|
||||
|
||||
QHBoxLayout* requrementsLayout = new QHBoxLayout();
|
||||
requrementsLayout->setAlignment(Qt::AlignTop);
|
||||
requrementsLayout->setMargin(0);
|
||||
requrementsLayout->setSpacing(0);
|
||||
|
||||
m_reqirementsIconLabel = new QLabel();
|
||||
m_reqirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24));
|
||||
requrementsLayout->addWidget(m_reqirementsIconLabel);
|
||||
|
||||
m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor);
|
||||
m_reqirementsTextLabel->setWordWrap(true);
|
||||
|
||||
QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
requrementsLayout->addSpacerItem(reqirementsSpacer);
|
||||
|
||||
m_mainLayout->addLayout(requrementsLayout);
|
||||
|
||||
m_mainLayout->addSpacing(20);
|
||||
|
||||
// Depending and conflicting gems
|
||||
m_dependingGems = new GemsSubWidget();
|
||||
m_mainLayout->addWidget(m_dependingGems);
|
||||
|
||||
@@ -76,6 +76,11 @@ namespace O3DE::ProjectManager
|
||||
LinkLabel* m_directoryLinkLabel = nullptr;
|
||||
LinkLabel* m_documentationLinkLabel = nullptr;
|
||||
|
||||
// Requirements
|
||||
QLabel* m_reqirementsTitleLabel = nullptr;
|
||||
QLabel* m_reqirementsIconLabel = nullptr;
|
||||
QLabel* m_reqirementsTextLabel = nullptr;
|
||||
|
||||
// Depending and conflicting gems
|
||||
GemsSubWidget* m_dependingGems = nullptr;
|
||||
GemsSubWidget* m_conflictingGems = nullptr;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
#include "GemModel.h"
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <QEvent>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace O3DE::ProjectManager
|
||||
inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2;
|
||||
inline constexpr static qreal s_buttonFontSize = 10.0;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const;
|
||||
QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const;
|
||||
QRect CalcButtonRect(const QRect& contentRect) const;
|
||||
@@ -71,6 +71,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QAbstractItemModel* m_model = nullptr;
|
||||
|
||||
private:
|
||||
// Platform icons
|
||||
void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath);
|
||||
inline constexpr static int s_platformIconSize = 12;
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "GemListView.h"
|
||||
#include "GemItemDelegate.h"
|
||||
#include <GemCatalog/GemListView.h>
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
#include <QStandardItemModel>
|
||||
#include <QDateTime>
|
||||
#include <QPalette>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "GemInfo.h"
|
||||
#include <QAbstractItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QListView>
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_binarySizeInKB, RoleBinarySize);
|
||||
item->setData(gemInfo.m_features, RoleFeatures);
|
||||
item->setData(gemInfo.m_path, RolePath);
|
||||
item->setData(gemInfo.m_requirement, RoleRequirement);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
@@ -183,6 +184,11 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RolePath).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetRequirement(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleRequirement).toString();
|
||||
}
|
||||
|
||||
bool GemModel::IsAdded(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleIsAdded).toBool();
|
||||
@@ -208,6 +214,24 @@ namespace O3DE::ProjectManager
|
||||
return (modelIndex.data(RoleWasPreviouslyAdded).toBool() && !modelIndex.data(RoleIsAdded).toBool());
|
||||
}
|
||||
|
||||
bool GemModel::HasRequirement(const QModelIndex& modelIndex)
|
||||
{
|
||||
return !modelIndex.data(RoleRequirement).toString().isEmpty();
|
||||
}
|
||||
|
||||
bool GemModel::DoGemsToBeAddedHaveRequirements() const
|
||||
{
|
||||
for (int row = 0; row < rowCount(); ++row)
|
||||
{
|
||||
const QModelIndex modelIndex = index(row, 0);
|
||||
if (NeedsToBeAdded(modelIndex) && HasRequirement(modelIndex))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<QModelIndex> GemModel::GatherGemsToBeAdded() const
|
||||
{
|
||||
QVector<QModelIndex> result;
|
||||
|
||||
@@ -53,12 +53,16 @@ namespace O3DE::ProjectManager
|
||||
static int GetBinarySizeInKB(const QModelIndex& modelIndex);
|
||||
static QStringList GetFeatures(const QModelIndex& modelIndex);
|
||||
static QString GetPath(const QModelIndex& modelIndex);
|
||||
static QString GetRequirement(const QModelIndex& modelIndex);
|
||||
|
||||
static bool IsAdded(const QModelIndex& modelIndex);
|
||||
static void SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded);
|
||||
static void SetWasPreviouslyAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded);
|
||||
static bool NeedsToBeAdded(const QModelIndex& modelIndex);
|
||||
static bool NeedsToBeRemoved(const QModelIndex& modelIndex);
|
||||
static bool HasRequirement(const QModelIndex& modelIndex);
|
||||
|
||||
bool DoGemsToBeAddedHaveRequirements() const;
|
||||
|
||||
QVector<QModelIndex> GatherGemsToBeAdded() const;
|
||||
QVector<QModelIndex> GatherGemsToBeRemoved() const;
|
||||
@@ -84,7 +88,8 @@ namespace O3DE::ProjectManager
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath
|
||||
RolePath,
|
||||
RoleRequirement
|
||||
};
|
||||
|
||||
QHash<QString, QModelIndex> m_nameToIndexMap;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementDelegate.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementDelegate::GemRequirementDelegate(QAbstractItemModel* model, QObject* parent)
|
||||
: GemItemDelegate(model, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void GemRequirementDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
|
||||
{
|
||||
if (!modelIndex.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QStyleOptionViewItem options(option);
|
||||
initStyleOption(&options, modelIndex);
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QRect fullRect, itemRect, contentRect;
|
||||
CalcRects(options, fullRect, itemRect, contentRect);
|
||||
|
||||
QFont standardFont(options.font);
|
||||
standardFont.setPixelSize(s_fontSize);
|
||||
QFontMetrics standardFontMetrics(standardFont);
|
||||
|
||||
painter->save();
|
||||
painter->setClipping(true);
|
||||
painter->setClipRect(fullRect);
|
||||
painter->setFont(options.font);
|
||||
|
||||
// Draw background
|
||||
painter->fillRect(fullRect, m_backgroundColor);
|
||||
|
||||
// Draw item background
|
||||
const QColor itemBackgroundColor = m_itemBackgroundColor;
|
||||
painter->fillRect(itemRect, itemBackgroundColor);
|
||||
|
||||
// Gem name
|
||||
QString gemName = GemModel::GetName(modelIndex);
|
||||
QFont gemNameFont(options.font);
|
||||
const int firstColumnMaxTextWidth = s_summaryStartX - 30;
|
||||
gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth);
|
||||
gemNameFont.setPixelSize(s_gemNameFontSize);
|
||||
gemNameFont.setBold(true);
|
||||
QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize);
|
||||
gemNameRect.moveTo(contentRect.left(), contentRect.center().y() - s_gemNameFontSize);
|
||||
|
||||
painter->setFont(gemNameFont);
|
||||
painter->setPen(m_textColor);
|
||||
painter->drawText(gemNameRect, Qt::TextSingleLine, gemName);
|
||||
|
||||
// Gem requirement
|
||||
const QSize requirementSize = QSize(contentRect.width() - s_summaryStartX - s_itemMargins.right(), contentRect.height());
|
||||
const QRect requirementRect = QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), requirementSize);
|
||||
|
||||
painter->setFont(standardFont);
|
||||
painter->setPen(m_textColor);
|
||||
|
||||
const QString requirement = GemModel::GetRequirement(modelIndex);
|
||||
painter->drawText(requirementRect, Qt::AlignLeft | Qt::TextWordWrap, requirement);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
bool GemRequirementDelegate::editorEvent(
|
||||
[[maybe_unused]] QEvent* event,
|
||||
[[maybe_unused]] QAbstractItemModel* model,
|
||||
[[maybe_unused]] const QStyleOptionViewItem& option,
|
||||
[[maybe_unused]] const QModelIndex& modelIndex)
|
||||
{
|
||||
// Do nothing here
|
||||
return false;
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class GemRequirementDelegate
|
||||
: public GemItemDelegate
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit GemRequirementDelegate(QAbstractItemModel* model, QObject* parent = nullptr);
|
||||
~GemRequirementDelegate() = default;
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override;
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override;
|
||||
|
||||
const QColor m_backgroundColor = QColor("#444444"); // Outside of the actual gem item
|
||||
const QColor m_itemBackgroundColor = QColor("#393939"); // Background color of the gem item
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementDialog.h>
|
||||
#include <GemCatalog/GemRequirementListView.h>
|
||||
#include <GemCatalog/GemRequirementFilterProxyModel.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSpacerItem>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementDialog::GemRequirementDialog(GemModel* model, const QVector<QModelIndex>& gemsToAdd, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Manual setup is required"));
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setMargin(0);
|
||||
vLayout->setContentsMargins(25, 10, 25, 10);
|
||||
vLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(vLayout);
|
||||
|
||||
QHBoxLayout* instructionLayout = new QHBoxLayout();
|
||||
instructionLayout->setMargin(0);
|
||||
|
||||
QLabel* instructionIconLabel = new QLabel();
|
||||
instructionIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(32, 32));
|
||||
instructionLayout->addWidget(instructionIconLabel);
|
||||
|
||||
instructionLayout->addSpacing(10);
|
||||
|
||||
QLabel* instructionLabel = new QLabel(tr("The following Gem(s) require manual setup before the project can be built successfully."));
|
||||
instructionLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
instructionLayout->addWidget(instructionLabel);
|
||||
|
||||
QSpacerItem* instructionSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
instructionLayout->addSpacerItem(instructionSpacer);
|
||||
|
||||
vLayout->addLayout(instructionLayout);
|
||||
|
||||
vLayout->addSpacing(20);
|
||||
|
||||
GemRequirementFilterProxyModel* proxModel = new GemRequirementFilterProxyModel(model, gemsToAdd, this);
|
||||
|
||||
GemRequirementListView* m_gemListView = new GemRequirementListView(proxModel, proxModel->GetSelectionModel(), this);
|
||||
vLayout->addWidget(m_gemListView);
|
||||
|
||||
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
|
||||
dialogButtons->setObjectName("footer");
|
||||
vLayout->addWidget(dialogButtons);
|
||||
|
||||
QPushButton* cancelButton = dialogButtons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
cancelButton->setProperty("secondary", true);
|
||||
QPushButton* continueButton = dialogButtons->addButton(tr("Continue"), QDialogButtonBox::ApplyRole);
|
||||
|
||||
connect(cancelButton, &QPushButton::clicked, this, &GemRequirementDialog::CancelButtonPressed);
|
||||
connect(continueButton, &QPushButton::clicked, this, &GemRequirementDialog::ContinueButtonPressed);
|
||||
}
|
||||
|
||||
QDialogButtonBox::ButtonRole GemRequirementDialog::GetButtonResult()
|
||||
{
|
||||
return m_buttonResult;
|
||||
}
|
||||
|
||||
void GemRequirementDialog::CancelButtonPressed()
|
||||
{
|
||||
m_buttonResult = QDialogButtonBox::RejectRole;
|
||||
close();
|
||||
}
|
||||
|
||||
void GemRequirementDialog::ContinueButtonPressed()
|
||||
{
|
||||
m_buttonResult = QDialogButtonBox::ApplyRole;
|
||||
close();
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QDialog>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(GemModel)
|
||||
|
||||
class GemRequirementDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
public:
|
||||
explicit GemRequirementDialog(GemModel* model, const QVector<QModelIndex>& gemsToAdd, QWidget *parent = nullptr);
|
||||
~GemRequirementDialog() = default;
|
||||
|
||||
QDialogButtonBox::ButtonRole GetButtonResult();
|
||||
|
||||
private:
|
||||
void CancelButtonPressed();
|
||||
void ContinueButtonPressed();
|
||||
|
||||
QDialogButtonBox::ButtonRole m_buttonResult = QDialogButtonBox::RejectRole;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementFilterProxyModel.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementFilterProxyModel::GemRequirementFilterProxyModel(GemModel* sourceModel, const QVector<QModelIndex>& addedGems, QObject* parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_sourceModel(sourceModel)
|
||||
, m_addedGems(addedGems)
|
||||
{
|
||||
setSourceModel(sourceModel);
|
||||
m_selectionProxyModel = new AzQtComponents::SelectionProxyModel(sourceModel->GetSelectionModel(), this, parent);
|
||||
}
|
||||
|
||||
bool GemRequirementFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
|
||||
{
|
||||
// Do not use sourceParent->child because an invalid parent does not produce valid children (which our index function does)
|
||||
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
if (!sourceIndex.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_addedGems.contains(sourceIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_sourceModel->HasRequirement(sourceIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzQtComponents/Utilities/SelectionProxyModel.h>
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(GemModel)
|
||||
|
||||
class GemRequirementFilterProxyModel
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
GemRequirementFilterProxyModel(GemModel* sourceModel, const QVector<QModelIndex>& addedGems, QObject* parent = nullptr);
|
||||
|
||||
AzQtComponents::SelectionProxyModel* GetSelectionModel() const { return m_selectionProxyModel; }
|
||||
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
|
||||
|
||||
private:
|
||||
GemModel* m_sourceModel = nullptr;
|
||||
AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr;
|
||||
|
||||
QVector<QModelIndex> m_addedGems;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementListView.h>
|
||||
#include <GemCatalog/GemRequirementDelegate.h>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementListView::GemRequirementListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent)
|
||||
: QListView(parent)
|
||||
{
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
setStyleSheet("background-color: #444444;");
|
||||
|
||||
setModel(model);
|
||||
setSelectionModel(selectionModel);
|
||||
setItemDelegate(new GemRequirementDelegate(model, this));
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QAbstractItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QListView>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class GemRequirementListView
|
||||
: public QListView
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit GemRequirementListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr);
|
||||
~GemRequirementListView() = default;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#include <NewProjectSettingsScreen.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <TemplateButtonWidget.h>
|
||||
#include <PathValidator.h>
|
||||
#include <EngineInfo.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectBuilder.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
@@ -30,8 +31,6 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
// 10 Minutes
|
||||
constexpr int MaxBuildTimeMSecs = 600000;
|
||||
static const QString BuildPathPostfix = "windows_vs2019";
|
||||
static const QString ErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log";
|
||||
|
||||
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
|
||||
: QObject()
|
||||
@@ -83,7 +82,7 @@ namespace O3DE::ProjectManager
|
||||
QStringList
|
||||
{
|
||||
"-B",
|
||||
QDir(m_projectInfo.m_path).filePath(BuildPathPostfix),
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"-S",
|
||||
m_projectInfo.m_path,
|
||||
"-G",
|
||||
@@ -123,7 +122,7 @@ namespace O3DE::ProjectManager
|
||||
QStringList
|
||||
{
|
||||
"--build",
|
||||
QDir(m_projectInfo.m_path).filePath(BuildPathPostfix),
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"--target",
|
||||
m_projectInfo.m_projectName + ".GameLauncher",
|
||||
"Editor",
|
||||
@@ -159,8 +158,8 @@ namespace O3DE::ProjectManager
|
||||
QString ProjectBuilderWorker::LogFilePath() const
|
||||
{
|
||||
QDir logFilePath(m_projectInfo.m_path);
|
||||
logFilePath.cd(BuildPathPostfix);
|
||||
return logFilePath.filePath(ErrorLogPathPostfix);
|
||||
logFilePath.cd(ProjectBuildPathPostfix);
|
||||
return logFilePath.filePath(ProjectBuildErrorLogPathPostfix);
|
||||
}
|
||||
|
||||
void ProjectBuilderWorker::WriteErrorLog(const QString& log)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
@@ -22,12 +23,11 @@
|
||||
#include <QMenu>
|
||||
#include <QSpacerItem>
|
||||
#include <QProgressBar>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
inline constexpr static int s_projectImageWidth = 210;
|
||||
inline constexpr static int s_projectImageHeight = 280;
|
||||
|
||||
LabelButton::LabelButton(QWidget* parent)
|
||||
: QLabel(parent)
|
||||
{
|
||||
@@ -92,11 +92,6 @@ namespace O3DE::ProjectManager
|
||||
: QFrame(parent)
|
||||
, m_projectInfo(projectInfo)
|
||||
{
|
||||
if (m_projectInfo.m_imagePath.isEmpty())
|
||||
{
|
||||
m_projectInfo.m_imagePath = ":/DefaultProjectImage.png";
|
||||
}
|
||||
|
||||
BaseSetup();
|
||||
if (processing)
|
||||
{
|
||||
@@ -118,20 +113,25 @@ namespace O3DE::ProjectManager
|
||||
setLayout(vLayout);
|
||||
|
||||
m_projectImageLabel = new LabelButton(this);
|
||||
m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight);
|
||||
m_projectImageLabel->setFixedSize(ProjectPreviewImageWidth, ProjectPreviewImageHeight);
|
||||
m_projectImageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); });
|
||||
vLayout->addWidget(m_projectImageLabel);
|
||||
|
||||
m_projectImageLabel->setPixmap(
|
||||
QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
QString projectPreviewPath = QDir(m_projectInfo.m_path).filePath(m_projectInfo.m_iconPath);
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (!doesPreviewExist.exists() || !doesPreviewExist.isFile())
|
||||
{
|
||||
projectPreviewPath = ":/DefaultProjectImage.png";
|
||||
}
|
||||
m_projectImageLabel->setPixmap(QPixmap(projectPreviewPath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
|
||||
m_projectFooter = new QFrame(this);
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_projectFooter->setLayout(hLayout);
|
||||
{
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this);
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.GetProjectDisplayName(), this);
|
||||
hLayout->addWidget(projectNameLabel);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,33 +10,74 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ProjectInfo.h"
|
||||
#include <ProjectInfo.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& displayName,
|
||||
const QString& origin, const QString& summary, const QString& imagePath, const QString& backgroundImagePath,
|
||||
ProjectInfo::ProjectInfo(
|
||||
const QString& path,
|
||||
const QString& projectName,
|
||||
const QString& displayName,
|
||||
const QString& origin,
|
||||
const QString& summary,
|
||||
const QString& iconPath,
|
||||
const QString& newPreviewImagePath,
|
||||
const QString& newBackgroundImagePath,
|
||||
bool needsBuild)
|
||||
: m_path(path)
|
||||
, m_projectName(projectName)
|
||||
, m_displayName(displayName)
|
||||
, m_origin(origin)
|
||||
, m_summary(summary)
|
||||
, m_imagePath(imagePath)
|
||||
, m_backgroundImagePath(backgroundImagePath)
|
||||
, m_iconPath(iconPath)
|
||||
, m_newPreviewImagePath(newPreviewImagePath)
|
||||
, m_newBackgroundImagePath(newBackgroundImagePath)
|
||||
, m_needsBuild(needsBuild)
|
||||
{
|
||||
}
|
||||
|
||||
bool ProjectInfo::operator==(const ProjectInfo& rhs)
|
||||
bool ProjectInfo::operator==(const ProjectInfo& rhs) const
|
||||
{
|
||||
return m_path == rhs.m_path
|
||||
&& m_projectName == rhs.m_projectName
|
||||
&& m_imagePath == rhs.m_imagePath
|
||||
&& m_backgroundImagePath == rhs.m_backgroundImagePath;
|
||||
if (m_path != rhs.m_path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_projectName != rhs.m_projectName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_displayName != rhs.m_displayName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_origin != rhs.m_origin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_summary != rhs.m_summary)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_iconPath != rhs.m_iconPath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_newPreviewImagePath != rhs.m_newPreviewImagePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_newBackgroundImagePath != rhs.m_newBackgroundImagePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectInfo::operator!=(const ProjectInfo& rhs)
|
||||
bool ProjectInfo::operator!=(const ProjectInfo& rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
@@ -45,4 +86,16 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
return !m_path.isEmpty() && !m_projectName.isEmpty();
|
||||
}
|
||||
|
||||
const QString& ProjectInfo::GetProjectDisplayName() const
|
||||
{
|
||||
if (!m_displayName.isEmpty())
|
||||
{
|
||||
return m_displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_projectName;
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -31,14 +31,16 @@ namespace O3DE::ProjectManager
|
||||
const QString& displayName,
|
||||
const QString& origin,
|
||||
const QString& summary,
|
||||
const QString& imagePath,
|
||||
const QString& backgroundImagePath,
|
||||
const QString& iconPath,
|
||||
const QString& newPreviewImagePath,
|
||||
const QString& newBackgroundImagePath,
|
||||
bool needsBuild);
|
||||
|
||||
bool operator==(const ProjectInfo& rhs);
|
||||
bool operator!=(const ProjectInfo& rhs);
|
||||
bool operator==(const ProjectInfo& rhs) const;
|
||||
bool operator!=(const ProjectInfo& rhs) const;
|
||||
|
||||
bool IsValid() const;
|
||||
const QString& GetProjectDisplayName() const;
|
||||
|
||||
// from o3de_manifest.json and o3de_projects.json
|
||||
QString m_path;
|
||||
@@ -48,14 +50,14 @@ namespace O3DE::ProjectManager
|
||||
QString m_displayName;
|
||||
QString m_origin;
|
||||
QString m_summary;
|
||||
QString m_iconPath;
|
||||
QStringList m_userTags;
|
||||
|
||||
// Used on projects home screen
|
||||
QString m_imagePath;
|
||||
QString m_backgroundImagePath;
|
||||
// Used as temp variable for replace images
|
||||
QString m_newPreviewImagePath;
|
||||
QString m_newBackgroundImagePath;
|
||||
|
||||
// Used in project creation
|
||||
|
||||
bool m_needsBuild = false; //! Does this project need to be built
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
inline constexpr static int ProjectPreviewImageWidth = 210;
|
||||
inline constexpr static int ProjectPreviewImageHeight = 280;
|
||||
|
||||
static const QString ProjectBuildPathPostfix = "Windows_VS2019";
|
||||
static const QString ProjectBuildErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log";
|
||||
static const QString ProjectPreviewImagePath = "preview.png";
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectSettingsScreen.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <PathValidator.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
@@ -47,7 +47,7 @@ namespace O3DE::ProjectManager
|
||||
connect(m_projectName->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::ValidateProjectName);
|
||||
m_verticalLayout->addWidget(m_projectName);
|
||||
|
||||
m_projectPath = new FormBrowseEditWidget(tr("Project Location"), "", this);
|
||||
m_projectPath = new FormFolderBrowseEditWidget(tr("Project Location"), "", this);
|
||||
m_projectPath->lineEdit()->setReadOnly(true);
|
||||
connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::Validate);
|
||||
m_verticalLayout->addWidget(m_projectPath);
|
||||
|
||||
@@ -32,9 +32,9 @@ namespace O3DE::ProjectManager
|
||||
~ProjectSettingsScreen() = default;
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
ProjectInfo GetProjectInfo();
|
||||
virtual ProjectInfo GetProjectInfo();
|
||||
|
||||
bool Validate();
|
||||
virtual bool Validate();
|
||||
|
||||
protected slots:
|
||||
virtual bool ValidateProjectName();
|
||||
|
||||
@@ -29,11 +29,8 @@ namespace O3DE::ProjectManager
|
||||
if (!QDir(path).isEmpty())
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
parent,
|
||||
QObject::tr("Overwrite Directory"),
|
||||
QObject::tr("Directory is not empty! Are you sure you want to overwrite it?"),
|
||||
QMessageBox::No | QMessageBox::Yes
|
||||
);
|
||||
parent, QObject::tr("Overwrite Directory"),
|
||||
QObject::tr("Directory is not empty! Are you sure you want to overwrite it?"), QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult != QMessageBox::Yes)
|
||||
{
|
||||
@@ -53,14 +50,13 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (ancestor == descendent)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
descendent.cdUp();
|
||||
}
|
||||
while (!descendent.isRoot());
|
||||
} while (!descendent.isRoot());
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CopyDirectory(const QString& origPath, const QString& newPath)
|
||||
@@ -138,7 +134,7 @@ namespace O3DE::ProjectManager
|
||||
bool CopyProject(const QString& origPath, const QString& newPath)
|
||||
{
|
||||
// Disallow copying from or into subdirectory
|
||||
if (!IsDirectoryDescedent(origPath, newPath) || !IsDirectoryDescedent(newPath, origPath))
|
||||
if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -173,20 +169,66 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent)
|
||||
bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool ignoreRegister)
|
||||
{
|
||||
if (!WarnDirectoryOverwrite(newPath, parent) || !UnregisterProject(origPath))
|
||||
origPath = QDir::toNativeSeparators(origPath);
|
||||
newPath = QDir::toNativeSeparators(newPath);
|
||||
|
||||
if (!WarnDirectoryOverwrite(newPath, parent) || (!ignoreRegister && !UnregisterProject(origPath)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir directory;
|
||||
if (directory.rename(origPath, newPath))
|
||||
QDir newDirectory(newPath);
|
||||
if (!newDirectory.removeRecursively())
|
||||
{
|
||||
return directory.rename(origPath, newPath);
|
||||
return false;
|
||||
}
|
||||
if (!newDirectory.rename(origPath, newPath))
|
||||
{
|
||||
// Likely failed because trying to move to another partition, try copying
|
||||
if (!CopyProject(origPath, newPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DeleteProjectFiles(origPath, true);
|
||||
}
|
||||
|
||||
if (!RegisterProject(newPath))
|
||||
if (!ignoreRegister && !RegisterProject(newPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent, bool interactive)
|
||||
{
|
||||
QFileInfo original(origFile);
|
||||
if (original.exists())
|
||||
{
|
||||
if (interactive)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
parent,
|
||||
QObject::tr("Overwrite File?"),
|
||||
QObject::tr("Replacing this will overwrite the current file on disk. Are you sure?"),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult == QMessageBox::No)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::remove(origFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::copy(newFile, origFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace O3DE::ProjectManager
|
||||
bool CopyProjectDialog(const QString& origPath, QWidget* parent = nullptr);
|
||||
bool CopyProject(const QString& origPath, const QString& newPath);
|
||||
bool DeleteProjectFiles(const QString& path, bool force = false);
|
||||
bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent = nullptr);
|
||||
bool MoveProject(QString origPath, QString newPath, QWidget* parent = nullptr, bool ignoreRegister = false);
|
||||
|
||||
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true);
|
||||
|
||||
bool IsVS2019Installed();
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <ProjectsScreen.h>
|
||||
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ProjectUtils.h>
|
||||
@@ -35,7 +36,6 @@
|
||||
#include <QSpacerItem>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QFileInfo>
|
||||
#include <QScrollArea>
|
||||
#include <QStackedWidget>
|
||||
#include <QFrame>
|
||||
@@ -218,16 +218,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing)
|
||||
{
|
||||
ProjectButton* projectButton;
|
||||
|
||||
QString projectPreviewPath = project.m_path + m_projectPreviewImagePath;
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (doesPreviewExist.exists() && doesPreviewExist.isFile())
|
||||
{
|
||||
project.m_imagePath = projectPreviewPath;
|
||||
}
|
||||
|
||||
projectButton = new ProjectButton(project, this, processing);
|
||||
ProjectButton* projectButton = new ProjectButton(project, this, processing);
|
||||
|
||||
flowLayout->addWidget(projectButton);
|
||||
|
||||
@@ -438,7 +429,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::information(this,
|
||||
tr("Project Should be rebuilt."),
|
||||
projectInfo.m_projectName + tr(" project likely needs to be rebuilt."));
|
||||
projectInfo.GetProjectDisplayName() + tr(" project likely needs to be rebuilt."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,8 +490,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::StandardButton buildProject = QMessageBox::information(
|
||||
this,
|
||||
tr("Building \"%1\"").arg(projectInfo.m_projectName),
|
||||
tr("Ready to build \"%1\"?").arg(projectInfo.m_projectName),
|
||||
tr("Building \"%1\"").arg(projectInfo.GetProjectDisplayName()),
|
||||
tr("Ready to build \"%1\"?").arg(projectInfo.GetProjectDisplayName()),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (buildProject == QMessageBox::Yes)
|
||||
|
||||
@@ -80,8 +80,6 @@ namespace O3DE::ProjectManager
|
||||
QQueue<ProjectInfo> m_buildQueue;
|
||||
ProjectBuilderController* m_currentBuilder = nullptr;
|
||||
|
||||
const QString m_projectPreviewImagePath = "/preview.png";
|
||||
|
||||
inline constexpr static int s_contentMargins = 80;
|
||||
inline constexpr static int s_spacerSize = 20;
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <PythonBindings.h>
|
||||
|
||||
#include <ProjectManagerDefs.h>
|
||||
|
||||
// Qt defines slots, which interferes with the use here.
|
||||
#pragma push_macro("slots")
|
||||
@@ -660,6 +661,7 @@ namespace O3DE::ProjectManager
|
||||
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
|
||||
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
|
||||
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
|
||||
gemInfo.m_requirement = Py_To_String_Optional(data, "Requirements", "");
|
||||
|
||||
if (data.contains("Tags"))
|
||||
{
|
||||
@@ -693,6 +695,7 @@ namespace O3DE::ProjectManager
|
||||
projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName);
|
||||
projectInfo.m_origin = Py_To_String_Optional(projectData, "origin", projectInfo.m_origin);
|
||||
projectInfo.m_summary = Py_To_String_Optional(projectData, "summary", projectInfo.m_summary);
|
||||
projectInfo.m_iconPath = Py_To_String_Optional(projectData, "icon", ProjectPreviewImagePath);
|
||||
if (projectData.contains("user_tags"))
|
||||
{
|
||||
for (auto tag : projectData["user_tags"])
|
||||
@@ -786,7 +789,7 @@ namespace O3DE::ProjectManager
|
||||
pybind11::str(projectInfo.m_origin.toStdString()), // new_origin
|
||||
pybind11::str(projectInfo.m_displayName.toStdString()), // new_display
|
||||
pybind11::str(projectInfo.m_summary.toStdString()), // new_summary
|
||||
pybind11::str(projectInfo.m_imagePath.toStdString()), // new_icon
|
||||
pybind11::str(projectInfo.m_iconPath.toStdString()), // new_icon
|
||||
pybind11::none(), // add_tags not used
|
||||
pybind11::none(), // remove_tags not used
|
||||
pybind11::list(pybind11::cast(newTags))); // replace_tags
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <ScreensCtrl.h>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -101,8 +103,11 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void UpdateProjectCtrl::HandleGemsButton()
|
||||
{
|
||||
m_stack->setCurrentWidget(m_gemCatalogScreen);
|
||||
Update();
|
||||
if (UpdateProjectSettings(true))
|
||||
{
|
||||
m_stack->setCurrentWidget(m_gemCatalogScreen);
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::HandleBackButton()
|
||||
@@ -114,7 +119,10 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else
|
||||
{
|
||||
emit GotoPreviousScreenRequest();
|
||||
if (UpdateProjectSettings(true))
|
||||
{
|
||||
emit GotoPreviousScreenRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,44 +132,19 @@ namespace O3DE::ProjectManager
|
||||
|
||||
if (m_stack->currentIndex() == ScreenOrder::Settings && m_updateSettingsScreen)
|
||||
{
|
||||
if (m_updateSettingsScreen)
|
||||
if (!UpdateProjectSettings())
|
||||
{
|
||||
if (!m_updateSettingsScreen->Validate())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectInfo newProjectSettings = m_updateSettingsScreen->GetProjectInfo();
|
||||
|
||||
// Update project if settings changed
|
||||
if (m_projectInfo != newProjectSettings)
|
||||
{
|
||||
auto result = PythonBindingsInterface::Get()->UpdateProject(newProjectSettings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project update failed"), tr(result.GetError().c_str()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if project path has changed and move it
|
||||
if (newProjectSettings.m_path != m_projectInfo.m_path)
|
||||
{
|
||||
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_projectInfo = newProjectSettings;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen)
|
||||
{
|
||||
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
if (!m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for project."));
|
||||
return;
|
||||
}
|
||||
|
||||
shouldRebuild = true;
|
||||
}
|
||||
@@ -190,14 +173,15 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
{
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_header->setSubTitle(QString(tr("Configure Gems")));
|
||||
m_nextButton->setText(tr("Finalize"));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setTitle("");
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
}
|
||||
}
|
||||
@@ -207,4 +191,70 @@ namespace O3DE::ProjectManager
|
||||
m_updateSettingsScreen->SetProjectInfo(m_projectInfo);
|
||||
}
|
||||
|
||||
bool UpdateProjectCtrl::UpdateProjectSettings(bool shouldConfirm)
|
||||
{
|
||||
AZ_Assert(m_updateSettingsScreen, "Update settings screen is nullptr.")
|
||||
|
||||
ProjectInfo newProjectSettings = m_updateSettingsScreen->GetProjectInfo();
|
||||
|
||||
if (m_projectInfo != newProjectSettings)
|
||||
{
|
||||
if (shouldConfirm)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
this,
|
||||
QObject::tr("Unsaved Changes!"),
|
||||
QObject::tr("Would you like to save your changes to project settings?"),
|
||||
QMessageBox::No | QMessageBox::Yes
|
||||
);
|
||||
|
||||
if (warningResult == QMessageBox::No)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_updateSettingsScreen->Validate())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update project if settings changed
|
||||
{
|
||||
auto result = PythonBindingsInterface::Get()->UpdateProject(newProjectSettings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project update failed"), tr(result.GetError().c_str()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if project path has changed and move it
|
||||
if (newProjectSettings.m_path != m_projectInfo.m_path)
|
||||
{
|
||||
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!newProjectSettings.m_newPreviewImagePath.isEmpty())
|
||||
{
|
||||
if (!ProjectUtils::ReplaceFile(
|
||||
QDir(newProjectSettings.m_path).filePath(newProjectSettings.m_iconPath), newProjectSettings.m_newPreviewImagePath))
|
||||
{
|
||||
QMessageBox::critical(this, tr("File replace failed"), tr("Failed to replace project preview image."));
|
||||
return false;
|
||||
}
|
||||
m_updateSettingsScreen->ResetProjectPreviewPath();
|
||||
}
|
||||
|
||||
m_projectInfo = newProjectSettings;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace O3DE::ProjectManager
|
||||
private:
|
||||
void Update();
|
||||
void UpdateSettingsScreen();
|
||||
bool UpdateProjectSettings(bool shouldConfirm = false);
|
||||
|
||||
enum ScreenOrder
|
||||
{
|
||||
|
||||
@@ -11,17 +11,43 @@
|
||||
*/
|
||||
|
||||
#include <UpdateProjectSettingsScreen.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <FormImageBrowseEditWidget.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QDir>
|
||||
#include <QLabel>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
UpdateProjectSettingsScreen::UpdateProjectSettingsScreen(QWidget* parent)
|
||||
: ProjectSettingsScreen(parent)
|
||||
, m_userChangedPreview(false)
|
||||
{
|
||||
m_projectPreview = new FormImageBrowseEditWidget(tr("Project Preview"), "", this);
|
||||
m_projectPreview->lineEdit()->setReadOnly(true);
|
||||
connect(m_projectPreview->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::Validate);
|
||||
connect(m_projectPreview->lineEdit(), &QLineEdit::textChanged, this, &UpdateProjectSettingsScreen::PreviewPathChanged);
|
||||
connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &UpdateProjectSettingsScreen::UpdateProjectPreviewPath);
|
||||
m_verticalLayout->addWidget(m_projectPreview);
|
||||
|
||||
QVBoxLayout* previewExtrasLayout = new QVBoxLayout(this);
|
||||
previewExtrasLayout->setAlignment(Qt::AlignLeft);
|
||||
previewExtrasLayout->setContentsMargins(50, 0, 0, 0);
|
||||
|
||||
QLabel* projectPreviewLabel = new QLabel(tr("Select an image (PNG). Minimum %1 x %2 pixels.")
|
||||
.arg(QString::number(ProjectPreviewImageWidth), QString::number(ProjectPreviewImageHeight)));
|
||||
previewExtrasLayout->addWidget(projectPreviewLabel);
|
||||
|
||||
m_projectPreviewImage = new QLabel(this);
|
||||
m_projectPreviewImage->setFixedSize(ProjectPreviewImageWidth, ProjectPreviewImageHeight);
|
||||
m_projectPreviewImage->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
previewExtrasLayout->addWidget(m_projectPreviewImage);
|
||||
|
||||
m_verticalLayout->addLayout(previewExtrasLayout);
|
||||
}
|
||||
|
||||
ProjectManagerScreen UpdateProjectSettingsScreen::GetScreenEnum()
|
||||
@@ -29,10 +55,58 @@ namespace O3DE::ProjectManager
|
||||
return ProjectManagerScreen::UpdateProjectSettings;
|
||||
}
|
||||
|
||||
ProjectInfo UpdateProjectSettingsScreen::GetProjectInfo()
|
||||
{
|
||||
m_projectInfo.m_displayName = m_projectName->lineEdit()->text();
|
||||
m_projectInfo.m_path = m_projectPath->lineEdit()->text();
|
||||
|
||||
if (m_userChangedPreview)
|
||||
{
|
||||
m_projectInfo.m_iconPath = ProjectPreviewImagePath;
|
||||
m_projectInfo.m_newPreviewImagePath = m_projectPreview->lineEdit()->text();
|
||||
}
|
||||
return m_projectInfo;
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::SetProjectInfo(const ProjectInfo& projectInfo)
|
||||
{
|
||||
m_projectName->lineEdit()->setText(projectInfo.m_projectName);
|
||||
m_projectInfo = projectInfo;
|
||||
|
||||
m_projectName->lineEdit()->setText(projectInfo.GetProjectDisplayName());
|
||||
|
||||
m_projectPath->lineEdit()->setText(projectInfo.m_path);
|
||||
UpdateProjectPreviewPath();
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::UpdateProjectPreviewPath()
|
||||
{
|
||||
if (!m_userChangedPreview)
|
||||
{
|
||||
m_projectPreview->lineEdit()->setText(QDir(m_projectPath->lineEdit()->text()).filePath(m_projectInfo.m_iconPath));
|
||||
// Setting the text sets m_userChangedPreview to true
|
||||
// Set it back to false because it should only be true when changed by user
|
||||
m_userChangedPreview = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateProjectSettingsScreen::Validate()
|
||||
{
|
||||
return ProjectSettingsScreen::Validate() && ValidateProjectPreview();
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::ResetProjectPreviewPath()
|
||||
{
|
||||
m_userChangedPreview = false;
|
||||
UpdateProjectPreviewPath();
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::PreviewPathChanged()
|
||||
{
|
||||
m_userChangedPreview = true;
|
||||
|
||||
// Update with latest image
|
||||
m_projectPreviewImage->setPixmap(
|
||||
QPixmap(m_projectPreview->lineEdit()->text()).scaled(m_projectPreviewImage->size(), Qt::KeepAspectRatioByExpanding));
|
||||
}
|
||||
|
||||
bool UpdateProjectSettingsScreen::ValidateProjectPath()
|
||||
@@ -48,4 +122,39 @@ namespace O3DE::ProjectManager
|
||||
return projectPathIsValid;
|
||||
}
|
||||
|
||||
bool UpdateProjectSettingsScreen::ValidateProjectPreview()
|
||||
{
|
||||
bool projectPreviewIsValid = true;
|
||||
|
||||
if (m_projectPreview->lineEdit()->text().isEmpty())
|
||||
{
|
||||
projectPreviewIsValid = false;
|
||||
m_projectPreview->setErrorLabelText(tr("Please select a file."));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_userChangedPreview)
|
||||
{
|
||||
QFileInfo previewFile(m_projectPreview->lineEdit()->text());
|
||||
if (!previewFile.exists() || !previewFile.isFile())
|
||||
{
|
||||
projectPreviewIsValid = false;
|
||||
m_projectPreview->setErrorLabelText(tr("Please select a valid png file."));
|
||||
}
|
||||
else
|
||||
{
|
||||
QString fileType = previewFile.completeSuffix().toLower();
|
||||
if (fileType != "png")
|
||||
{
|
||||
projectPreviewIsValid = false;
|
||||
m_projectPreview->setErrorLabelText(tr("Please select a png image."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_projectPreview->setErrorLabelVisible(!projectPreviewIsValid);
|
||||
return projectPreviewIsValid;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <ProjectSettingsScreen.h>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class UpdateProjectSettingsScreen
|
||||
@@ -25,10 +27,26 @@ namespace O3DE::ProjectManager
|
||||
~UpdateProjectSettingsScreen() = default;
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
ProjectInfo GetProjectInfo() override;
|
||||
void SetProjectInfo(const ProjectInfo& projectInfo);
|
||||
|
||||
bool Validate() override;
|
||||
|
||||
void ResetProjectPreviewPath();
|
||||
|
||||
public slots:
|
||||
void UpdateProjectPreviewPath();
|
||||
void PreviewPathChanged();
|
||||
|
||||
protected:
|
||||
bool ValidateProjectPath() override;
|
||||
virtual bool ValidateProjectPreview();
|
||||
|
||||
FormBrowseEditWidget* m_projectPreview;
|
||||
QLabel* m_projectPreviewImage;
|
||||
|
||||
ProjectInfo m_projectInfo;
|
||||
bool m_userChangedPreview; //! Did the user change the project preview path
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
set(FILES
|
||||
Source/Application.h
|
||||
Source/Application.cpp
|
||||
Source/ProjectManagerDefs.h
|
||||
Source/ScreenDefs.h
|
||||
Source/ScreenFactory.h
|
||||
Source/ScreenFactory.cpp
|
||||
@@ -25,6 +26,10 @@ set(FILES
|
||||
Source/FormLineEditWidget.cpp
|
||||
Source/FormBrowseEditWidget.h
|
||||
Source/FormBrowseEditWidget.cpp
|
||||
Source/FormFolderBrowseEditWidget.h
|
||||
Source/FormFolderBrowseEditWidget.cpp
|
||||
Source/FormImageBrowseEditWidget.h
|
||||
Source/FormImageBrowseEditWidget.cpp
|
||||
Source/PathValidator.h
|
||||
Source/PathValidator.cpp
|
||||
Source/ProjectManagerWindow.h
|
||||
@@ -82,6 +87,14 @@ set(FILES
|
||||
Source/GemCatalog/GemListHeaderWidget.cpp
|
||||
Source/GemCatalog/GemModel.h
|
||||
Source/GemCatalog/GemModel.cpp
|
||||
Source/GemCatalog/GemRequirementDialog.h
|
||||
Source/GemCatalog/GemRequirementDialog.cpp
|
||||
Source/GemCatalog/GemRequirementDelegate.h
|
||||
Source/GemCatalog/GemRequirementDelegate.cpp
|
||||
Source/GemCatalog/GemRequirementFilterProxyModel.h
|
||||
Source/GemCatalog/GemRequirementFilterProxyModel.cpp
|
||||
Source/GemCatalog/GemRequirementListView.h
|
||||
Source/GemCatalog/GemRequirementListView.cpp
|
||||
Source/GemCatalog/GemSortFilterProxyModel.h
|
||||
Source/GemCatalog/GemSortFilterProxyModel.cpp
|
||||
)
|
||||
|
||||
@@ -14,4 +14,5 @@ set(FILES
|
||||
Resources/ProjectManager.qss
|
||||
tests/ApplicationTests.cpp
|
||||
tests/main.cpp
|
||||
tests/UtilsTests.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <Application.h>
|
||||
#include <ProjectUtils.h>
|
||||
#include <ProjectManager_Test_Traits_Platform.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
class ProjectManagerUtilsTests
|
||||
: public ::UnitTest::ScopedAllocatorSetupFixture
|
||||
{
|
||||
public:
|
||||
ProjectManagerUtilsTests()
|
||||
{
|
||||
m_application = AZStd::make_unique<ProjectManager::Application>();
|
||||
m_application->Init(false);
|
||||
|
||||
QDir dir;
|
||||
dir.mkdir("ProjectA");
|
||||
dir.mkdir("ProjectB");
|
||||
|
||||
QFile origFile("ProjectA/origFile.txt");
|
||||
if (origFile.open(QIODevice::ReadWrite))
|
||||
{
|
||||
QTextStream stream(&origFile);
|
||||
stream << "orig" << Qt::endl;
|
||||
origFile.close();
|
||||
}
|
||||
|
||||
QFile replaceFile("ProjectA/replaceFile.txt");
|
||||
if (replaceFile.open(QIODevice::ReadWrite))
|
||||
{
|
||||
QTextStream stream(&replaceFile);
|
||||
stream << "replace" << Qt::endl;
|
||||
replaceFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
~ProjectManagerUtilsTests()
|
||||
{
|
||||
QDir dirA("ProjectA");
|
||||
dirA.removeRecursively();
|
||||
|
||||
QDir dirB("ProjectB");
|
||||
dirB.removeRecursively();
|
||||
|
||||
m_application.reset();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ProjectManager::Application> m_application;
|
||||
};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_Succeeds)
|
||||
#else
|
||||
TEST_F(ProjectManagerUtilsTests, MoveProject_Succeeds)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
{
|
||||
EXPECT_TRUE(MoveProject(
|
||||
QDir::currentPath() + QDir::separator() + "ProjectA",
|
||||
QDir::currentPath() + QDir::separator() + "ProjectB",
|
||||
nullptr, true));
|
||||
|
||||
QFileInfo origFile("ProjectA/origFile.txt");
|
||||
EXPECT_TRUE(!origFile.exists());
|
||||
|
||||
QFileInfo replaceFile("ProjectA/replaceFile.txt");
|
||||
EXPECT_TRUE(!replaceFile.exists());
|
||||
|
||||
QFileInfo origFileMoved("ProjectB/origFile.txt");
|
||||
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
||||
|
||||
QFileInfo replaceFileMoved("ProjectB/replaceFile.txt");
|
||||
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds)
|
||||
#else
|
||||
TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
{
|
||||
EXPECT_TRUE(ReplaceFile("ProjectA/origFile.txt", "ProjectA/replaceFile.txt", nullptr, false));
|
||||
|
||||
QFile origFile("ProjectA/origFile.txt");
|
||||
if (origFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QTextStream stream(&origFile);
|
||||
QString line = stream.readLine();
|
||||
EXPECT_EQ(line, "replace");
|
||||
|
||||
origFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -255,7 +255,6 @@ namespace AZ
|
||||
{
|
||||
return AZStd::make_pair(animation, anim);
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpAnimationImporter::ImportAnimation(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Animation");
|
||||
@@ -447,7 +446,22 @@ namespace AZ
|
||||
|
||||
return combinedAnimationResult.GetResult();
|
||||
}
|
||||
decltype(boneAnimations) parentFillerAnimations;
|
||||
|
||||
AZStd::unordered_set<AZStd::string> boneList;
|
||||
|
||||
for (int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
|
||||
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
{
|
||||
aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
boneList.insert(bone->mName.C_Str());
|
||||
}
|
||||
}
|
||||
|
||||
decltype(boneAnimations) fillerAnimations;
|
||||
|
||||
// Go through all the animations and make sure we create animations for bones who's parents don't have an animation
|
||||
for (auto&& anim : boneAnimations)
|
||||
@@ -459,8 +473,8 @@ namespace AZ
|
||||
{
|
||||
if (!IsPivotNode(parent->mName))
|
||||
{
|
||||
if (boneAnimations.find(parent->mName.C_Str()) == boneAnimations.end() &&
|
||||
parentFillerAnimations.find(parent->mName.C_Str()) == parentFillerAnimations.end())
|
||||
if (!boneAnimations.contains(parent->mName.C_Str()) &&
|
||||
!fillerAnimations.contains(parent->mName.C_Str()))
|
||||
{
|
||||
// Create 1 key for each type that just copies the current transform
|
||||
ConsolidatedNodeAnim emptyAnimation;
|
||||
@@ -472,7 +486,7 @@ namespace AZ
|
||||
globalTransform.Decompose(scale, rotation, position);
|
||||
|
||||
emptyAnimation.mNumRotationKeys = emptyAnimation.mNumPositionKeys = emptyAnimation.mNumScalingKeys = 1;
|
||||
|
||||
|
||||
emptyAnimation.m_ownedPositionKeys.emplace_back(0, position);
|
||||
emptyAnimation.mPositionKeys = emptyAnimation.m_ownedPositionKeys.data();
|
||||
|
||||
@@ -481,9 +495,9 @@ namespace AZ
|
||||
|
||||
emptyAnimation.m_ownedScalingKeys.emplace_back(0, scale);
|
||||
emptyAnimation.mScalingKeys = emptyAnimation.m_ownedScalingKeys.data();
|
||||
|
||||
parentFillerAnimations.insert(
|
||||
AZStd::make_pair(parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
|
||||
|
||||
fillerAnimations.insert(AZStd::make_pair(
|
||||
parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +505,7 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
boneAnimations.insert(AZStd::make_move_iterator(parentFillerAnimations.begin()), AZStd::make_move_iterator(parentFillerAnimations.end()));
|
||||
boneAnimations.insert(AZStd::make_move_iterator(fillerAnimations.begin()), AZStd::make_move_iterator(fillerAnimations.end()));
|
||||
|
||||
auto animItr = boneAnimations.equal_range(currentNode->mName.C_Str());
|
||||
|
||||
|
||||
@@ -98,6 +98,20 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
aiMatrix4x4 CalculateWorldTransform(const aiNode* currentNode)
|
||||
{
|
||||
aiMatrix4x4 transform = {};
|
||||
const aiNode* iteratingNode = currentNode;
|
||||
|
||||
while (iteratingNode)
|
||||
{
|
||||
transform = iteratingNode->mTransformation * transform;
|
||||
iteratingNode = iteratingNode->mParent;
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpBoneImporter::ImportBone(AssImpNodeEncounteredContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Bone");
|
||||
@@ -111,12 +125,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
bool isBone = false;
|
||||
|
||||
if (NodeParentIsOfType(context.m_scene.GetGraph(), context.m_currentGraphPosition, DataTypes::IBoneData::TYPEINFO_Uuid()))
|
||||
{
|
||||
isBone = true;
|
||||
}
|
||||
else
|
||||
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, const aiNode*> mainBoneList;
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
|
||||
@@ -170,15 +179,8 @@ namespace AZ
|
||||
{
|
||||
createdBoneData = AZStd::make_shared<SceneData::GraphData::RootBoneData>();
|
||||
}
|
||||
|
||||
aiMatrix4x4 transform = currentNode->mTransformation;
|
||||
const aiNode* parent = currentNode->mParent;
|
||||
|
||||
while (parent)
|
||||
{
|
||||
transform = parent->mTransformation * transform;
|
||||
parent = parent->mParent;
|
||||
}
|
||||
aiMatrix4x4 transform = CalculateWorldTransform(currentNode);
|
||||
|
||||
SceneAPI::DataTypes::MatrixType globalTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace AZ
|
||||
~AssImpBoneImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
|
||||
Events::ProcessingResult ImportBone(AssImpNodeEncounteredContext& context);
|
||||
};
|
||||
} // namespace FbxSceneBuilder
|
||||
|
||||
@@ -46,8 +46,9 @@ namespace AZ
|
||||
serializeContext->Class<AssImpTransformImporter, SceneCore::LoadingComponent>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void GetAllBones(const aiScene* scene, AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
|
||||
|
||||
void GetAllBones(
|
||||
const aiScene* scene, AZStd::unordered_multimap<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
@@ -57,7 +58,7 @@ namespace AZ
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
boneLookup[bone->mName.C_Str()] = bone;
|
||||
boneLookup.emplace(bone->mName.C_Str(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,41 +74,53 @@ namespace AZ
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
|
||||
AZStd::unordered_multimap<AZStd::string, const aiBone*> boneLookup;
|
||||
GetAllBones(scene, boneLookup);
|
||||
|
||||
auto boneIterator = boneLookup.find(currentNode->mName.C_Str());
|
||||
const bool isBone = boneIterator != boneLookup.end();
|
||||
|
||||
aiMatrix4x4 combinedTransform;
|
||||
|
||||
DataTypes::MatrixType localTransform;
|
||||
|
||||
if (isBone)
|
||||
{
|
||||
auto parentNode = currentNode->mParent;
|
||||
AZStd::vector<DataTypes::MatrixType> offsets, inverseOffsets;
|
||||
auto iteratingNode = currentNode;
|
||||
|
||||
aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix;
|
||||
aiMatrix4x4 parentOffset {};
|
||||
|
||||
auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str());
|
||||
|
||||
if (parentNode && parentBoneIterator != boneLookup.end())
|
||||
while (iteratingNode && boneLookup.count(iteratingNode->mName.C_Str()))
|
||||
{
|
||||
const auto& parentBone = parentBoneIterator->second;
|
||||
AZStd::string name = iteratingNode->mName.C_Str();
|
||||
|
||||
parentOffset = parentBone->mOffsetMatrix;
|
||||
auto range = boneLookup.equal_range(name);
|
||||
|
||||
if (range.first != range.second)
|
||||
{
|
||||
// There can be multiple offsetMatrices for a given bone, we're only interested in grabbing the first one
|
||||
auto boneFirstOffsetMatrix = range.first->second->mOffsetMatrix;
|
||||
auto azMat = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(boneFirstOffsetMatrix);
|
||||
offsets.push_back(azMat);
|
||||
inverseOffsets.push_back(azMat.GetInverseFull());
|
||||
}
|
||||
|
||||
iteratingNode = iteratingNode->mParent;
|
||||
}
|
||||
|
||||
auto inverseOffset = offsetMatrix;
|
||||
inverseOffset.Inverse();
|
||||
|
||||
combinedTransform = parentOffset * inverseOffset;
|
||||
|
||||
localTransform =
|
||||
offsets.at(AZ::GetMin(offsets.size()-1, static_cast<decltype(offsets.size())>(1))) // parent bone offset, or if there is no parent, then current node offset
|
||||
* inverseOffsets.at(inverseOffsets.size() - 1) // Inverse of root bone offset
|
||||
* offsets.at(offsets.size() - 1) // Root bone offset
|
||||
* inverseOffsets.at(0); // Inverse of current node offset
|
||||
}
|
||||
else
|
||||
{
|
||||
combinedTransform = GetConcatenatedLocalTransform(currentNode);
|
||||
localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(GetConcatenatedLocalTransform(currentNode));
|
||||
}
|
||||
|
||||
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
|
||||
// Don't bother adding a node with the identity matrix
|
||||
if (localTransform == DataTypes::MatrixType::Identity())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
|
||||
context.m_sourceSceneSystem.ConvertUnit(localTransform);
|
||||
|
||||
@@ -1765,16 +1765,8 @@ namespace LUAEditor
|
||||
return false;
|
||||
}
|
||||
|
||||
//name has the full path in it, we need to convert it to an asset name
|
||||
AZStd::string projectRoot, databaseRoot, databasePath, databaseFile, fileExtension;
|
||||
if (!AzFramework::StringFunc::AssetDatabasePath::Split(name.toUtf8().data(), &projectRoot, &databaseRoot, &databasePath, &databaseFile, &fileExtension))
|
||||
{
|
||||
AZ_Warning("LUAEditorMainWindow", false, AZStd::string::format("<span severity=\"err\">Path is invalid: '%s'</span>", name.toUtf8().data()).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
AzFramework::StringFunc::Path::Split(name.toUtf8().data(), nullptr, &m_lastOpenFilePath);
|
||||
AzFramework::StringFunc::AssetDatabasePath::Join(databasePath.c_str(), databaseFile.c_str(), newAssetName);
|
||||
newAssetName = name.toUtf8().data();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user