Merge remote-tracking branch 'origin/master' into 1.13
# Conflicts: # Content/Shaders/Editor/Grid.flax # Content/Shaders/GBuffer.flax # Content/Shaders/GlobalSignDistanceField.flax # Content/Shaders/ProbesFilter.flax # Content/Shaders/SSR.flax # Content/Shaders/VolumetricFog.flax # Development/Documentation/mono.md # Source/Editor/Modules/ContentDatabaseModule.cs
This commit is contained in:
@@ -122,11 +122,8 @@ FlaxStorageReference ContentStorageManager::EnsureAccess(const StringView& path)
|
||||
// Note: because we want to create new storage package it may exists.
|
||||
// So let's check if any storage container is referencing that location and try to close it.
|
||||
auto storage = TryGetStorage(path);
|
||||
if (storage && storage->IsLoaded())
|
||||
{
|
||||
LOG(Info, "File \'{0}\' is in use. Trying to release handle to it.", path);
|
||||
storage->CloseFileHandles();
|
||||
}
|
||||
if (storage && storage->IsLoaded() && storage->CloseFileHandles())
|
||||
LOG(Warning, "Cannot release content storage handle for '{0}'.", path);
|
||||
return storage;
|
||||
}
|
||||
|
||||
|
||||
@@ -1438,10 +1438,6 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
|
||||
bool FlaxStorage::CloseFileHandles()
|
||||
{
|
||||
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
|
||||
|
||||
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
|
||||
return false; // Early out when no files are opened
|
||||
PROFILE_CPU();
|
||||
@@ -1469,7 +1465,13 @@ bool FlaxStorage::CloseFileHandles()
|
||||
}
|
||||
}
|
||||
}
|
||||
waitTime = 100;
|
||||
|
||||
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
|
||||
|
||||
// Wait for chunks lock again (with larger timeout for longer tasks)
|
||||
waitTime = 1000;
|
||||
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
||||
Platform::Sleep(1);
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0)
|
||||
|
||||
@@ -901,20 +901,21 @@ namespace FlaxEngine.Interop
|
||||
|
||||
Assembly assembly;
|
||||
#if FLAX_EDITOR
|
||||
// Load assembly from loaded bytes to prevent file locking in Editor
|
||||
var assemblyBytes = File.ReadAllBytes(assemblyPath);
|
||||
using MemoryStream stream = new MemoryStream(assemblyBytes);
|
||||
// Load assembly with stream to prevent runtime from locking the assembly file
|
||||
using FileStream stream = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read);
|
||||
var pdbPath = Path.ChangeExtension(assemblyPath, "pdb");
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
// Load including debug symbols
|
||||
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open);
|
||||
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open, FileAccess.Read);
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream);
|
||||
}
|
||||
|
||||
// TODO: Use new .NET 11 AssemblyLoadContext.SetAssemblyLocationOverride to specify correct Assembly.Location
|
||||
#else
|
||||
// Load assembly from file
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||
|
||||
@@ -197,7 +197,7 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* ve
|
||||
|
||||
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
|
||||
{
|
||||
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R16_UInt, vertices, triangles, normals, tangents, uvs, colors);
|
||||
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R32_UInt, vertices, triangles, normals, tangents, uvs, colors);
|
||||
}
|
||||
|
||||
bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer)
|
||||
|
||||
@@ -305,7 +305,7 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Flo
|
||||
|
||||
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Int4* blendIndices, const Float4* blendWeights, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
|
||||
{
|
||||
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R16_UInt, vertices, triangles, blendIndices, blendWeights, normals, tangents, uvs, colors);
|
||||
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R32_UInt, vertices, triangles, blendIndices, blendWeights, normals, tangents, uvs, colors);
|
||||
}
|
||||
|
||||
void SkinnedMesh::Draw(const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const
|
||||
|
||||
@@ -28,6 +28,11 @@ API_ENUM() enum class ClosingReason
|
||||
/// The close event.
|
||||
/// </summary>
|
||||
CloseEvent,
|
||||
|
||||
/// <summary>
|
||||
/// The scripts reload event.
|
||||
/// </summary>
|
||||
ScriptsReload,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#if USE_EDITOR
|
||||
#define COMPILE_WITH_ASSETS_IMPORTER 1 // Hack to use shaders importing in this module
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Content/Storage/ContentStorageManager.h"
|
||||
#include "Engine/Utilities/Encryption.h"
|
||||
#include "Engine/Platform/FileSystemWatcher.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
@@ -499,6 +501,37 @@ String ShadersCompilation::CompactShaderPath(StringView path)
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool ShadersCompilation::IsShaderSourceAssetUpToDate(const StringView& sourcePath, const StringView& assetPath)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
StringAnsi source;
|
||||
if (File::ReadAllText(sourcePath, source))
|
||||
return false;
|
||||
if (!source.HasChars() || source[source.Length() - 1] != '\n')
|
||||
source.Append('\n');
|
||||
|
||||
const auto storage = ContentStorageManager::GetStorage(assetPath);
|
||||
AssetInitData data;
|
||||
if (!storage
|
||||
|| storage->GetEntriesCount() != 1
|
||||
|| storage->GetEntry(0).TypeName != Shader::TypeName
|
||||
|| storage->LoadAssetHeader(0, data)
|
||||
|| data.SerializedVersion != Shader::SerializedVersion)
|
||||
return false;
|
||||
|
||||
FlaxChunk* sourceChunk = data.Header.Chunks[SHADER_FILE_CHUNK_SOURCE];
|
||||
if (!sourceChunk || storage->LoadAssetChunk(sourceChunk) || !sourceChunk->Data.IsValid())
|
||||
return false;
|
||||
|
||||
BytesContainer embeddedSource;
|
||||
embeddedSource.Copy(sourceChunk->Data);
|
||||
if (embeddedSource.Length() != source.Length() + 1)
|
||||
return false;
|
||||
Encryption::DecryptBytes(embeddedSource.Get(), embeddedSource.Length());
|
||||
embeddedSource.Get()[embeddedSource.Length() - 1] = 0;
|
||||
return Platform::MemoryCompare(embeddedSource.Get(), source.Get(), source.Length()) == 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
Array<FileSystemWatcher*> ShadersSourcesWatchers;
|
||||
@@ -515,6 +548,13 @@ namespace
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ImportShaderIfChanged(const StringView& sourcePath, const StringView& assetPath, Guid& assetId)
|
||||
{
|
||||
if (ShadersCompilation::IsShaderSourceAssetUpToDate(sourcePath, assetPath))
|
||||
return false;
|
||||
return AssetsImportingManager::Import(sourcePath, assetPath, assetId);
|
||||
}
|
||||
|
||||
void OnWatcherShadersEvent(const String& path, FileSystemAction action)
|
||||
{
|
||||
if (action == FileSystemAction::Delete || !path.EndsWith(TEXT(".shader")))
|
||||
@@ -538,7 +578,7 @@ namespace
|
||||
const String name = StringUtils::GetPathWithoutExtension(localPath);
|
||||
const String outputPath = shadersAssetsPath / name + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
Guid id = GetShaderAssetId(name);
|
||||
AssetsImportingManager::ImportIfEdited(path, outputPath, id);
|
||||
ImportShaderIfChanged(path, outputPath, id);
|
||||
}
|
||||
|
||||
void RegisterShaderWatchers(const ProjectInfo* project, HashSet<const ProjectInfo*>& projects)
|
||||
@@ -567,7 +607,7 @@ namespace
|
||||
const String name = StringUtils::GetPathWithoutExtension(localPath);
|
||||
const String outputPath = shadersAssetsPath / name + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
Guid id = GetShaderAssetId(name);
|
||||
AssetsImportingManager::ImportIfEdited(path, outputPath, id);
|
||||
ImportShaderIfChanged(path, outputPath, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,16 @@ public:
|
||||
// Compacts the full shader file path into portable format with project name prefix such as './<ProjectName>/ShaderFile.hlsl'.
|
||||
static String CompactShaderPath(StringView path);
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Checks whether a shader asset embeds the current source file contents.
|
||||
/// </summary>
|
||||
/// <param name="sourcePath">The shader source file path.</param>
|
||||
/// <param name="assetPath">The shader asset file path.</param>
|
||||
/// <returns>True when the embedded source matches, otherwise false.</returns>
|
||||
static bool IsShaderSourceAssetUpToDate(const StringView& sourcePath, const StringView& assetPath);
|
||||
#endif
|
||||
|
||||
private:
|
||||
static ShaderCompiler* RequestCompiler(ShaderProfile profile, PlatformType platform);
|
||||
static void FreeCompiler(ShaderCompiler* compiler);
|
||||
|
||||
@@ -26,6 +26,29 @@ namespace FlaxEngine.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RemovingControl : MyControl
|
||||
{
|
||||
private readonly Control[] _controlsToRemove;
|
||||
|
||||
public RemovingControl(float x, float y, float width, float height, params Control[] controlsToRemove)
|
||||
: base(x, y, width, height)
|
||||
{
|
||||
_controlsToRemove = controlsToRemove;
|
||||
}
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
for (int i = 0; i < _controlsToRemove.Length; i++)
|
||||
{
|
||||
var control = _controlsToRemove[i];
|
||||
if (control.Parent == Parent)
|
||||
control.Parent = null;
|
||||
}
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChildren()
|
||||
{
|
||||
@@ -53,6 +76,25 @@ namespace FlaxEngine.Tests
|
||||
Assert.AreEqual(cc1.GetChildAt(new Vector2(15, 5)), cc2);
|
||||
Assert.AreEqual(cc1.GetChildAtRecursive(new Vector2(35, 25)), c3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMouseMoveAllowsChildrenRemoval()
|
||||
{
|
||||
var container = new MyContainerControl(0, 0, 100, 100);
|
||||
var first = new MyControl(0, 0, 100, 100);
|
||||
var second = new MyControl(0, 0, 100, 100);
|
||||
var removing = new RemovingControl(0, 0, 100, 100, first, second);
|
||||
container.AddChild(first);
|
||||
container.AddChild(second);
|
||||
container.AddChild(removing);
|
||||
|
||||
// The top-most child removes multiple siblings during input dispatch.
|
||||
// Traversal must not use the now-stale next index.
|
||||
container.OnMouseMove(new Float2(50, 50));
|
||||
|
||||
Assert.AreEqual(1, container.ChildrenCount);
|
||||
Assert.AreEqual(removing, container.GetChild(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Engine/Core/ScopeExit.h"
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/ShadersCompilation/ShadersCompilation.h"
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER && USE_EDITOR
|
||||
|
||||
TEST_CASE("Shader source asset synchronization ignores timestamps")
|
||||
{
|
||||
const String sourcePath = Globals::StartupFolder / TEXT("Source/Shaders/VolumetricFog.shader");
|
||||
const String assetPath = Globals::EngineContentFolder / TEXT("Shaders/VolumetricFog.flax");
|
||||
REQUIRE(FileSystem::FileExists(sourcePath));
|
||||
REQUIRE(FileSystem::FileExists(assetPath));
|
||||
CHECK(ShadersCompilation::IsShaderSourceAssetUpToDate(sourcePath, assetPath));
|
||||
|
||||
const String tempRoot = Globals::TemporaryFolder / (TEXT("ShaderSourceSync-") + Guid::New().ToString(Guid::FormatType::N));
|
||||
REQUIRE(!FileSystem::CreateDirectory(tempRoot));
|
||||
SCOPE_EXIT
|
||||
{
|
||||
FileSystem::DeleteDirectory(tempRoot, true);
|
||||
};
|
||||
|
||||
DataContainer<byte> assetData;
|
||||
StringAnsi modifiedSource;
|
||||
REQUIRE(!File::ReadAllBytes(assetPath, assetData));
|
||||
REQUIRE(!File::ReadAllText(sourcePath, modifiedSource));
|
||||
modifiedSource.Append("// Deliberately different source\n");
|
||||
|
||||
const String tempSourcePath = tempRoot / TEXT("VolumetricFog.shader");
|
||||
const String tempAssetPath = tempRoot / TEXT("VolumetricFog.flax");
|
||||
REQUIRE(!File::WriteAllBytes(tempSourcePath, modifiedSource.Get(), modifiedSource.Length()));
|
||||
REQUIRE(!File::WriteAllBytes(tempAssetPath, assetData.Get(), assetData.Length()));
|
||||
CHECK_FALSE(FileSystem::GetFileLastEditTime(tempSourcePath) > FileSystem::GetFileLastEditTime(tempAssetPath));
|
||||
CHECK_FALSE(ShadersCompilation::IsShaderSourceAssetUpToDate(tempSourcePath, tempAssetPath));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -21,6 +21,7 @@ public class Tests : EngineModule
|
||||
base.Setup(options);
|
||||
|
||||
options.PrivateDependencies.Add("ModelTool");
|
||||
options.PrivateDependencies.Add("ShadersCompilation");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -20,26 +20,29 @@
|
||||
#include <ThirdParty/assimp/scene.h>
|
||||
#include <ThirdParty/assimp/version.h>
|
||||
#include <ThirdParty/assimp/postprocess.h>
|
||||
#include <ThirdParty/assimp/LogStream.hpp>
|
||||
#include <ThirdParty/assimp/DefaultLogger.hpp>
|
||||
#include <ThirdParty/assimp/Logger.hpp>
|
||||
|
||||
class AssimpLogStream : public Assimp::LogStream
|
||||
class AssimpLogger final : public Assimp::Logger
|
||||
{
|
||||
public:
|
||||
AssimpLogStream()
|
||||
AssimpLogger()
|
||||
: Logger(NORMAL)
|
||||
{
|
||||
Assimp::DefaultLogger::create("");
|
||||
Assimp::DefaultLogger::get()->attachStream(this);
|
||||
}
|
||||
|
||||
~AssimpLogStream()
|
||||
bool attachStream(Assimp::LogStream*, unsigned int) override
|
||||
{
|
||||
Assimp::DefaultLogger::get()->detachStream(this);
|
||||
Assimp::DefaultLogger::kill();
|
||||
return false;
|
||||
}
|
||||
|
||||
void write(const char* message) override
|
||||
bool detachStream(Assimp::LogStream*, unsigned int) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
static void Write(const Char* type, const char* message)
|
||||
{
|
||||
String s(message);
|
||||
if (s.Length() <= 0)
|
||||
@@ -52,10 +55,49 @@ public:
|
||||
else if (c >= 255)
|
||||
c = '?';
|
||||
}
|
||||
LOG(Info, "[Assimp]: {0}", s);
|
||||
LOG(Info, "[Assimp]: {0}: {1}", type, s);
|
||||
}
|
||||
|
||||
void OnDebug(const char* message) override
|
||||
{
|
||||
if (m_Severity >= DEBUGGING)
|
||||
Write(TEXT("Debug"), message);
|
||||
}
|
||||
|
||||
void OnVerboseDebug(const char* message) override
|
||||
{
|
||||
if (m_Severity >= VERBOSE)
|
||||
Write(TEXT("Debug"), message);
|
||||
}
|
||||
|
||||
void OnInfo(const char* message) override
|
||||
{
|
||||
Write(TEXT("Info"), message);
|
||||
}
|
||||
|
||||
void OnWarn(const char* message) override
|
||||
{
|
||||
Write(TEXT("Warn"), message);
|
||||
}
|
||||
|
||||
void OnError(const char* message) override
|
||||
{
|
||||
Write(TEXT("Error"), message);
|
||||
}
|
||||
};
|
||||
|
||||
Assimp::Logger* GetAssimpLogger()
|
||||
{
|
||||
static Assimp::Logger* logger = []()
|
||||
{
|
||||
auto result = new AssimpLogger();
|
||||
Assimp::DefaultLogger::set(result);
|
||||
LOG(Info, "Assimp {0}.{1}.{2}", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
|
||||
return result;
|
||||
}();
|
||||
return logger;
|
||||
}
|
||||
|
||||
Float2 ToFloat2(const aiVector2D& v)
|
||||
{
|
||||
return Float2(v.x, v.y);
|
||||
@@ -148,7 +190,6 @@ struct AssimpBone
|
||||
struct AssimpImporterData
|
||||
{
|
||||
Assimp::Importer AssimpImporter;
|
||||
AssimpLogStream AssimpLogStream;
|
||||
const String Path;
|
||||
const aiScene* Scene = nullptr;
|
||||
const ModelTool::Options& Options;
|
||||
@@ -705,12 +746,7 @@ void ImportAnimation(int32 index, ModelData& data, AssimpImporterData& importerD
|
||||
|
||||
bool ModelTool::ImportDataAssimp(const String& path, ModelData& data, Options& options, String& errorMsg)
|
||||
{
|
||||
static bool AssimpInited = false;
|
||||
if (!AssimpInited)
|
||||
{
|
||||
AssimpInited = true;
|
||||
LOG(Info, "Assimp {0}.{1}.{2}", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
|
||||
}
|
||||
GetAssimpLogger();
|
||||
bool importMeshes = EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry);
|
||||
bool importAnimations = EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Animations);
|
||||
AssimpImporterData context(path, options);
|
||||
|
||||
@@ -910,7 +910,7 @@ namespace FlaxEngine.GUI
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible)
|
||||
@@ -928,7 +928,7 @@ namespace FlaxEngine.GUI
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -948,7 +948,7 @@ namespace FlaxEngine.GUI
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -998,7 +998,7 @@ namespace FlaxEngine.GUI
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1019,7 +1019,7 @@ namespace FlaxEngine.GUI
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1040,7 +1040,7 @@ namespace FlaxEngine.GUI
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1061,7 +1061,7 @@ namespace FlaxEngine.GUI
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1096,7 +1096,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnTouchEnter(Float2 location, int pointerId)
|
||||
{
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled && !child.IsTouchPointerOver(pointerId))
|
||||
@@ -1114,7 +1114,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override bool OnTouchDown(Float2 location, int pointerId)
|
||||
{
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1139,7 +1139,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnTouchMove(Float2 location, int pointerId)
|
||||
{
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1168,7 +1168,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override bool OnTouchUp(Float2 location, int pointerId)
|
||||
{
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled && child.IsTouchPointerOver(pointerId))
|
||||
@@ -1250,7 +1250,7 @@ namespace FlaxEngine.GUI
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1275,7 +1275,7 @@ namespace FlaxEngine.GUI
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
@@ -1333,7 +1333,7 @@ namespace FlaxEngine.GUI
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
|
||||
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled)
|
||||
|
||||
Reference in New Issue
Block a user