Merge remote-tracking branch 'origin/master' into 1.12
# Conflicts: # Source/Engine/Platform/Windows/WindowsPlatform.h
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
name: Continuous Deployment
|
name: Continuous Deployment
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '15 6 * * *'
|
- cron: '15 4 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -513,20 +513,12 @@ namespace FlaxEditor.GUI.Docking
|
|||||||
if (TryCollapseSplitter(_tabsProxy?.Parent as Panel))
|
if (TryCollapseSplitter(_tabsProxy?.Parent as Panel))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!IsMaster)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (_childPanels.Count != 0)
|
else if (_childPanels.Count != 0)
|
||||||
{
|
{
|
||||||
if (TryCollapseSplitter(_tabsProxy?.Parent as Panel))
|
if (TryCollapseSplitter(_tabsProxy?.Parent as Panel))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!IsMaster)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool CollapseEmptyTabsProxy()
|
internal bool CollapseEmptyTabsProxy()
|
||||||
|
|||||||
@@ -548,9 +548,9 @@ namespace FlaxEditor.Options
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the curvature of the line connecting to connected visject nodes.
|
/// Gets or sets the curvature of the line connecting to connected visject nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DefaultValue(1.0f), Range(0.0f, 2.0f)]
|
[DefaultValue(0.25f), Range(0.0f, 2.0f)]
|
||||||
[EditorDisplay("Visject"), EditorOrder(550)]
|
[EditorDisplay("Visject"), EditorOrder(550)]
|
||||||
public float ConnectionCurvature { get; set; } = 1.0f;
|
public float ConnectionCurvature { get; set; } = 0.25f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value that indicates whether the context menu description panel is shown or not.
|
/// Gets or sets a value that indicates whether the context menu description panel is shown or not.
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ void EngineImpl::InitLog()
|
|||||||
|
|
||||||
// Log product info
|
// Log product info
|
||||||
LOG(Info, "Product: {0}, Company: {1}", Globals::ProductName, Globals::CompanyName);
|
LOG(Info, "Product: {0}, Company: {1}", Globals::ProductName, Globals::CompanyName);
|
||||||
LOG(Info, "Current culture: {0}", Platform::GetUserLocaleName());
|
LOG(Info, "Current language: {}, culture: {}", Platform::GetUserLanguage(), Platform::GetUserLocaleName());
|
||||||
LOG(Info, "Command line: {0}", CommandLine);
|
LOG(Info, "Command line: {0}", CommandLine);
|
||||||
LOG(Info, "Base folder: {0}", Globals::StartupFolder);
|
LOG(Info, "Base folder: {0}", Globals::StartupFolder);
|
||||||
LOG(Info, "Binaries folder: {0}", Globals::BinariesFolder);
|
LOG(Info, "Binaries folder: {0}", Globals::BinariesFolder);
|
||||||
|
|||||||
@@ -220,7 +220,11 @@ void PostFxMaterialsSettings::BlendWith(PostFxMaterialsSettings& other, float we
|
|||||||
while (Materials.Count() != POST_PROCESS_SETTINGS_MAX_MATERIALS && indexSrc < other.Materials.Count())
|
while (Materials.Count() != POST_PROCESS_SETTINGS_MAX_MATERIALS && indexSrc < other.Materials.Count())
|
||||||
{
|
{
|
||||||
if (!Materials.Contains(materialsSrc[indexSrc].GetID()))
|
if (!Materials.Contains(materialsSrc[indexSrc].GetID()))
|
||||||
Materials.Add(materialsSrc[indexSrc]);
|
{
|
||||||
|
auto& ref = materialsSrc[indexSrc];
|
||||||
|
ref.Get(); // Load asset
|
||||||
|
Materials.Add(ref);
|
||||||
|
}
|
||||||
indexSrc++;
|
indexSrc++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,6 +313,12 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
|||||||
Actor* oldTargetActor = instance.TargetActor;
|
Actor* oldTargetActor = instance.TargetActor;
|
||||||
if (!oldTargetActor || EnumHasAnyFlags(oldTargetActor->Flags, ObjectFlags::WasMarkedToDelete))
|
if (!oldTargetActor || EnumHasAnyFlags(oldTargetActor->Flags, ObjectFlags::WasMarkedToDelete))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Get scene objects in the prefab instance (use old target actor to maintain actors order matching the prefab data)
|
||||||
|
sceneObjects->Clear();
|
||||||
|
SceneQuery::GetAllSerializableSceneObjects(instance.TargetActor, *sceneObjects.Value);
|
||||||
|
|
||||||
|
// Fixup prefab root when it was changed
|
||||||
Actor* newTargetActor = FindActorWithPrefabObjectId(instance.TargetActor, defaultInstance->GetID());
|
Actor* newTargetActor = FindActorWithPrefabObjectId(instance.TargetActor, defaultInstance->GetID());
|
||||||
if (!newTargetActor)
|
if (!newTargetActor)
|
||||||
{
|
{
|
||||||
@@ -321,15 +327,17 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
|||||||
else if (oldTargetActor != newTargetActor)
|
else if (oldTargetActor != newTargetActor)
|
||||||
{
|
{
|
||||||
LOG(Info, "Changing root object of prefab instance from {0} to {1}", oldTargetActor->ToString(), newTargetActor->ToString());
|
LOG(Info, "Changing root object of prefab instance from {0} to {1}", oldTargetActor->ToString(), newTargetActor->ToString());
|
||||||
newTargetActor->SetParent(oldTargetActor->GetParent(), true, false);
|
Actor* oldTargetParent = oldTargetActor->GetParent();
|
||||||
|
if (newTargetActor == oldTargetParent || newTargetActor->GetParent() == oldTargetActor)
|
||||||
|
{
|
||||||
|
// Direct reparenting needs additional step to prevent loop in a hierarchy
|
||||||
|
oldTargetActor->SetParent(nullptr, true, false);
|
||||||
|
}
|
||||||
|
newTargetActor->SetParent(oldTargetParent, true, false);
|
||||||
oldTargetActor->SetParent(newTargetActor, true, false);
|
oldTargetActor->SetParent(newTargetActor, true, false);
|
||||||
instance.TargetActor = newTargetActor;
|
instance.TargetActor = newTargetActor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get scene objects in the prefab instance
|
|
||||||
sceneObjects->Clear();
|
|
||||||
SceneQuery::GetAllSerializableSceneObjects(instance.TargetActor, *sceneObjects.Value);
|
|
||||||
|
|
||||||
int32 existingObjectsCount = sceneObjects->Count();
|
int32 existingObjectsCount = sceneObjects->Count();
|
||||||
modifier->IdsMapping.EnsureCapacity((existingObjectsCount + newPrefabObjectIds.Count()));
|
modifier->IdsMapping.EnsureCapacity((existingObjectsCount + newPrefabObjectIds.Count()));
|
||||||
|
|
||||||
@@ -350,7 +358,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier.Value->IdsMapping[obj->GetPrefabObjectID()] = obj->GetSceneObjectId();
|
modifier.Value->IdsMapping[obj->GetPrefabObjectID()] = obj->GetID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,6 +828,8 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
|||||||
PROFILE_CPU_NAMED("Prefab.Apply");
|
PROFILE_CPU_NAMED("Prefab.Apply");
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
const auto prefabId = GetID();
|
const auto prefabId = GetID();
|
||||||
|
const auto oldRootId = GetRootObjectId();
|
||||||
|
const auto newRootId = targetActor->GetPrefabObjectID();
|
||||||
|
|
||||||
// Gather all scene objects in target instance (reused later)
|
// Gather all scene objects in target instance (reused later)
|
||||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache targetObjects = ActorsCache::SceneObjectsListCache.Get();
|
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache targetObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||||
@@ -1121,15 +1131,14 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
|||||||
|
|
||||||
// Find the prefab root object (the root is usually serialized first)
|
// Find the prefab root object (the root is usually serialized first)
|
||||||
auto root = dynamic_cast<Actor*>(sceneObjects.Value->At(0));
|
auto root = dynamic_cast<Actor*>(sceneObjects.Value->At(0));
|
||||||
if (root && root->_parent)
|
int32 targetActorIdx = oldObjectsIds.Find(newRootId);
|
||||||
|
if (newRootId != oldRootId && targetActorIdx > 0 && targetActorIdx < sceneObjects.Value->Count() && dynamic_cast<Actor*>(sceneObjects.Value->At(targetActorIdx)))
|
||||||
{
|
{
|
||||||
// When changing prefab root the target actor is a new root so try to find it in the objects
|
// When changing prefab root the target actor is a new root so try to find it in the objects
|
||||||
int32 targetActorIdx = oldObjectsIds.Find(targetActor->GetPrefabObjectID());
|
root = (Actor*)sceneObjects.Value->At(targetActorIdx);
|
||||||
if (targetActorIdx > 0 && targetActorIdx < sceneObjects.Value->Count() && dynamic_cast<Actor*>(sceneObjects.Value->At(targetActorIdx)))
|
|
||||||
{
|
|
||||||
root = dynamic_cast<Actor*>(sceneObjects.Value->At(targetActorIdx));
|
|
||||||
}
|
}
|
||||||
|
else if (root && root->_parent)
|
||||||
|
{
|
||||||
// Try using the first actor without a parent as a new root
|
// Try using the first actor without a parent as a new root
|
||||||
for (int32 i = 1; i < sceneObjects->Count(); i++)
|
for (int32 i = 1; i < sceneObjects->Count(); i++)
|
||||||
{
|
{
|
||||||
@@ -1141,13 +1150,13 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Keep root unlinked
|
if (root && root->_parent)
|
||||||
if (root->_parent)
|
|
||||||
{
|
{
|
||||||
|
// Keep root unlinked
|
||||||
root->_parent->Children.Remove(root);
|
root->_parent->Children.Remove(root);
|
||||||
root->_parent = nullptr;
|
root->_parent = nullptr;
|
||||||
}
|
root->OnParentChanged();
|
||||||
}
|
}
|
||||||
if (!root)
|
if (!root)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -584,6 +584,11 @@ bool PrefabManager::ApplyAll(Actor* instance)
|
|||||||
// Use the input object as fallback
|
// Use the input object as fallback
|
||||||
rootObjectInstance = instance;
|
rootObjectInstance = instance;
|
||||||
}
|
}
|
||||||
|
while (rootObjectInstance->GetParent() && rootObjectInstance->GetParent()->GetPrefabID() == rootObjectInstance->GetPrefabID())
|
||||||
|
{
|
||||||
|
// Move up to the root of the prefab instance (eg. in case of root change on instance to apply)
|
||||||
|
rootObjectInstance = rootObjectInstance->GetParent();
|
||||||
|
}
|
||||||
|
|
||||||
return prefab->ApplyAll(rootObjectInstance);
|
return prefab->ApplyAll(rootObjectInstance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,50 @@ String LocalizedString::ToStringPlural(int32 n) const
|
|||||||
return Localization::GetPluralString(Id, n, Value);
|
return Localization::GetPluralString(Id, n, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Serialization::Serialize(ISerializable::SerializeStream& stream, const LocalizedString& v, const void* otherObj)
|
||||||
|
{
|
||||||
|
if (v.Id.IsEmpty())
|
||||||
|
{
|
||||||
|
stream.String(v.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream.StartObject();
|
||||||
|
stream.JKEY("Id");
|
||||||
|
stream.String(v.Id);
|
||||||
|
if (v.Value.HasChars())
|
||||||
|
{
|
||||||
|
stream.JKEY("Value");
|
||||||
|
stream.String(v.Value);
|
||||||
|
}
|
||||||
|
stream.EndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serialization::Deserialize(ISerializable::DeserializeStream& stream, LocalizedString& v, ISerializeModifier* modifier)
|
||||||
|
{
|
||||||
|
if (stream.IsString())
|
||||||
|
{
|
||||||
|
v.Id = String::Empty;
|
||||||
|
v.Value = stream.GetText();
|
||||||
|
}
|
||||||
|
else if (stream.IsObject())
|
||||||
|
{
|
||||||
|
auto e = SERIALIZE_FIND_MEMBER(stream, "Id");
|
||||||
|
if (e != stream.MemberEnd())
|
||||||
|
v.Id.SetUTF8(e->value.GetString(), e->value.GetStringLength());
|
||||||
|
e = SERIALIZE_FIND_MEMBER(stream, "Value");
|
||||||
|
if (e != stream.MemberEnd())
|
||||||
|
v.Value.SetUTF8(e->value.GetString(), e->value.GetStringLength());
|
||||||
|
else if (v.Id.HasChars())
|
||||||
|
v.Value.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v = LocalizedString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LocalizationService::OnLocalizationChanged()
|
void LocalizationService::OnLocalizationChanged()
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
@@ -288,7 +332,8 @@ bool LocalizationService::Init()
|
|||||||
PROFILE_MEM(Localization);
|
PROFILE_MEM(Localization);
|
||||||
|
|
||||||
// Use system language as default
|
// Use system language as default
|
||||||
CurrentLanguage = CurrentCulture = CultureInfo(Platform::GetUserLocaleName());
|
CurrentLanguage = CultureInfo(Platform::GetUserLanguage());
|
||||||
|
CurrentCulture = CultureInfo(Platform::GetUserLocaleName());
|
||||||
|
|
||||||
// Setup localization
|
// Setup localization
|
||||||
Instance.OnLocalizationChanged();
|
Instance.OnLocalizationChanged();
|
||||||
@@ -350,3 +395,16 @@ String Localization::GetPluralString(const String& id, int32 n, const String& fa
|
|||||||
const String& format = Instance.Get(id, n - 1, fallback);
|
const String& format = Instance.Get(id, n - 1, fallback);
|
||||||
return String::Format(format.GetText(), n);
|
return String::Format(format.GetText(), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array<String> Localization::GetLocales()
|
||||||
|
{
|
||||||
|
Array<String> result;
|
||||||
|
auto& settings = *LocalizationSettings::Get();
|
||||||
|
for (auto& e : settings.LocalizedStringTables)
|
||||||
|
{
|
||||||
|
auto table = e.Get();
|
||||||
|
if (table && !table->WaitForLoaded())
|
||||||
|
result.AddUnique(table->Locale);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,4 +59,10 @@ public:
|
|||||||
/// <param name="fallback">The optional fallback string value to use if localized string is missing.</param>
|
/// <param name="fallback">The optional fallback string value to use if localized string is missing.</param>
|
||||||
/// <returns>The localized text.</returns>
|
/// <returns>The localized text.</returns>
|
||||||
API_FUNCTION() static String GetPluralString(const String& id, int32 n, const String& fallback = String::Empty);
|
API_FUNCTION() static String GetPluralString(const String& id, int32 n, const String& fallback = String::Empty);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of unique languages (locale names such as 'pl-PL') defined in project in Localized String Tables set in Localization Settings. Can be used to display all languages available in game.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The list of unique languages (locale names such as 'pl-PL').</returns>
|
||||||
|
API_FUNCTION() static Array<String, HeapAllocation> GetLocales();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,49 +71,8 @@ namespace Serialization
|
|||||||
return !otherObj || v != *(LocalizedString*)otherObj;
|
return !otherObj || v != *(LocalizedString*)otherObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Serialize(ISerializable::SerializeStream& stream, const LocalizedString& v, const void* otherObj)
|
FLAXENGINE_API void Serialize(ISerializable::SerializeStream& stream, const LocalizedString& v, const void* otherObj);
|
||||||
{
|
FLAXENGINE_API void Deserialize(ISerializable::DeserializeStream& stream, LocalizedString& v, ISerializeModifier* modifier);
|
||||||
if (v.Id.IsEmpty())
|
|
||||||
{
|
|
||||||
stream.String(v.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream.StartObject();
|
|
||||||
stream.JKEY("Id");
|
|
||||||
stream.String(v.Id);
|
|
||||||
if (v.Value.HasChars())
|
|
||||||
{
|
|
||||||
stream.JKEY("Value");
|
|
||||||
stream.String(v.Value);
|
|
||||||
}
|
|
||||||
stream.EndObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Deserialize(ISerializable::DeserializeStream& stream, LocalizedString& v, ISerializeModifier* modifier)
|
|
||||||
{
|
|
||||||
if (stream.IsString())
|
|
||||||
{
|
|
||||||
v.Id = String::Empty;
|
|
||||||
v.Value = stream.GetText();
|
|
||||||
}
|
|
||||||
else if (stream.IsObject())
|
|
||||||
{
|
|
||||||
auto e = SERIALIZE_FIND_MEMBER(stream, "Id");
|
|
||||||
if (e != stream.MemberEnd())
|
|
||||||
v.Id.SetUTF8(e->value.GetString(), e->value.GetStringLength());
|
|
||||||
e = SERIALIZE_FIND_MEMBER(stream, "Value");
|
|
||||||
if (e != stream.MemberEnd())
|
|
||||||
v.Value.SetUTF8(e->value.GetString(), e->value.GetStringLength());
|
|
||||||
else if (v.Id.HasChars())
|
|
||||||
v.Value.Clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v = LocalizedString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_DEFAULT_FORMATTING_VIA_TO_STRING(LocalizedString);
|
DEFINE_DEFAULT_FORMATTING_VIA_TO_STRING(LocalizedString);
|
||||||
|
|||||||
@@ -609,6 +609,11 @@ ScreenOrientationType PlatformBase::GetScreenOrientationType()
|
|||||||
return ScreenOrientationType::Unknown;
|
return ScreenOrientationType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String PlatformBase::GetUserLanguage()
|
||||||
|
{
|
||||||
|
return Platform::GetUserLocaleName();
|
||||||
|
}
|
||||||
|
|
||||||
String PlatformBase::GetUserName()
|
String PlatformBase::GetUserName()
|
||||||
{
|
{
|
||||||
return Users.Count() != 0 ? Users[0]->GetName() : String::Empty;
|
return Users.Count() != 0 ? Users[0]->GetName() : String::Empty;
|
||||||
|
|||||||
@@ -625,7 +625,12 @@ public:
|
|||||||
API_PROPERTY() static ScreenOrientationType GetScreenOrientationType();
|
API_PROPERTY() static ScreenOrientationType GetScreenOrientationType();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current locale culture (eg. "pl-PL" or "en-US").
|
/// Gets the current user display language used to localize texts. Returns name of the culture (eg. "pl-PL" or "en-US"), use CultureInfo for display name of the language.
|
||||||
|
/// </summary>
|
||||||
|
API_PROPERTY() static String GetUserLanguage();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current user locale culture used to localize numbers, currency and dates. Returns name of the culture (eg. "pl-PL" or "en-US"), use CultureInfo for display name of the language.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY() static String GetUserLocaleName() = delete;
|
API_PROPERTY() static String GetUserLocaleName() = delete;
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ void FlaxDbgHelpUnlock()
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
String UserLocale, ComputerName, WindowsName;
|
String UserLanguage, UserLocale, ComputerName, WindowsName;
|
||||||
HANDLE EngineMutex = nullptr;
|
HANDLE EngineMutex = nullptr;
|
||||||
Rectangle VirtualScreenBounds(0.0f, 0.0f, 0.0f, 0.0f);
|
Rectangle VirtualScreenBounds(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
int32 VersionMajor = 0;
|
int32 VersionMajor = 0;
|
||||||
@@ -774,12 +774,18 @@ bool WindowsPlatform::Init()
|
|||||||
|
|
||||||
DWORD tmp;
|
DWORD tmp;
|
||||||
Char buffer[256];
|
Char buffer[256];
|
||||||
|
ULONG bufferSize = ARRAY_COUNT(buffer), languagesCount = 0;
|
||||||
|
|
||||||
// Get user locale string
|
// Get user locale strings
|
||||||
if (GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH))
|
if (GetUserDefaultLocaleName(buffer, (int)bufferSize))
|
||||||
{
|
{
|
||||||
UserLocale = String(buffer);
|
UserLocale = String(buffer);
|
||||||
}
|
}
|
||||||
|
if (GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &languagesCount, buffer, &bufferSize))
|
||||||
|
{
|
||||||
|
// Get the first language
|
||||||
|
UserLanguage = String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
// Get computer name string
|
// Get computer name string
|
||||||
if (GetComputerNameW(buffer, &tmp))
|
if (GetComputerNameW(buffer, &tmp))
|
||||||
@@ -951,6 +957,11 @@ int32 WindowsPlatform::GetDpi()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
String WindowsPlatform::GetUserLanguage()
|
||||||
|
{
|
||||||
|
return UserLanguage.HasChars() ? UserLanguage : UserLocale;
|
||||||
|
}
|
||||||
|
|
||||||
String WindowsPlatform::GetUserLocaleName()
|
String WindowsPlatform::GetUserLocaleName()
|
||||||
{
|
{
|
||||||
return UserLocale;
|
return UserLocale;
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ public:
|
|||||||
#if !PLATFORM_SDL
|
#if !PLATFORM_SDL
|
||||||
static int32 GetDpi();
|
static int32 GetDpi();
|
||||||
#endif
|
#endif
|
||||||
|
static String GetUserLanguage();
|
||||||
static String GetUserLocaleName();
|
static String GetUserLocaleName();
|
||||||
static String GetComputerName();
|
static String GetComputerName();
|
||||||
static bool GetHasFocus();
|
static bool GetHasFocus();
|
||||||
|
|||||||
@@ -854,4 +854,55 @@ TEST_CASE("Prefabs")
|
|||||||
instanceB->DeleteObject();
|
instanceB->DeleteObject();
|
||||||
instanceC->DeleteObject();
|
instanceC->DeleteObject();
|
||||||
}
|
}
|
||||||
|
SECTION("Test Changing Prefab Root")
|
||||||
|
{
|
||||||
|
// https://github.com/FlaxEngine/FlaxEngine/issues/4034
|
||||||
|
|
||||||
|
// Create base prefab with 2 objects
|
||||||
|
AssetReference<Prefab> prefab = Content::CreateVirtualAsset<Prefab>();
|
||||||
|
REQUIRE(prefab);
|
||||||
|
SCOPE_EXIT{ Content::DeleteAsset(prefab); };
|
||||||
|
Guid id;
|
||||||
|
Guid::Parse("333334524c696dcfa93cabacd2a4f404", id);
|
||||||
|
prefab->ChangeID(id);
|
||||||
|
auto prefabInit = prefab->Init(Prefab::TypeName,
|
||||||
|
"["
|
||||||
|
"{"
|
||||||
|
"\"ID\": \"1111cfaa4bd1a53435129480e5bbdb3b\","
|
||||||
|
"\"TypeName\": \"FlaxEngine.Decal\","
|
||||||
|
"\"Name\": \"Old Root\""
|
||||||
|
"},"
|
||||||
|
"{"
|
||||||
|
"\"ID\": \"2222814f4d913e58eb35ab8b0b7e2eef\","
|
||||||
|
"\"TypeName\": \"FlaxEngine.Camera\","
|
||||||
|
"\"ParentID\": \"1111cfaa4bd1a53435129480e5bbdb3b\","
|
||||||
|
"\"Name\": \"New Root\""
|
||||||
|
"}"
|
||||||
|
"]");
|
||||||
|
REQUIRE(!prefabInit);
|
||||||
|
|
||||||
|
// Spawn test instances
|
||||||
|
ScriptingObjectReference<Actor> instance1 = PrefabManager::SpawnPrefab(prefab);
|
||||||
|
ScriptingObjectReference<Actor> instance2 = PrefabManager::SpawnPrefab(prefab);
|
||||||
|
|
||||||
|
// Change root
|
||||||
|
Actor* child = instance1->Children[0];
|
||||||
|
child->SetParent(nullptr);
|
||||||
|
instance1->SetParent(child);
|
||||||
|
bool applyResult = PrefabManager::ApplyAll(instance1);
|
||||||
|
REQUIRE(!applyResult);
|
||||||
|
|
||||||
|
// Verify scenario
|
||||||
|
REQUIRE(instance2);
|
||||||
|
REQUIRE(instance2->GetName() == TEXT("Old Root"));
|
||||||
|
REQUIRE(instance2->GetChildrenCount() == 0);
|
||||||
|
REQUIRE(instance2->GetParent());
|
||||||
|
REQUIRE(instance2->GetParent()->GetName() == TEXT("New Root"));
|
||||||
|
REQUIRE(instance2->GetParent()->GetChildrenCount() == 1);
|
||||||
|
REQUIRE(instance2->GetParent()->Children[0]->GetName() == TEXT("Old Root"));
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
instance1->DeleteObject();
|
||||||
|
instance2->DeleteObject();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user