Compare commits
140 Commits
master_fix
...
d0cc2b67a7
| Author | SHA1 | Date | |
|---|---|---|---|
| d0cc2b67a7 | |||
| 9b93bef983 | |||
| ee95a292a6 | |||
| a336b4966f | |||
| da1c112c92 | |||
| 9ffe206b81 | |||
| a87a6898ae | |||
| ebf57ef9b0 | |||
| 668a4dbb4d | |||
| 1d8f221f1b | |||
| d70a003617 | |||
| b443b74d18 | |||
| fc341a86e7 | |||
| 68da28ffe8 | |||
|
|
427e76e76e | ||
| b183b5bcfc | |||
| 51feaa0730 | |||
| 8e69aa8bd6 | |||
| 90b2fded48 | |||
| 9125ffeb9e | |||
| 28980e5fbf | |||
| aab0d772a4 | |||
| 6296e1a9eb | |||
| 1586bb0702 | |||
| edeaf6af09 | |||
| 951edd95db | |||
| 9951211596 | |||
| 6d337464f7 | |||
| d0b552d74a | |||
| 83512822b1 | |||
| ccb78103ec | |||
| 6e79ffea34 | |||
| 4f03d37a17 | |||
| 6fb8419b3c | |||
| a4272d6ca9 | |||
| 4654117d5e | |||
| 6ff260d052 | |||
| 88d2b72822 | |||
| 29868531ad | |||
| 95dfb6fdc6 | |||
| 8df3999f85 | |||
| 149b189629 | |||
| b1fd86e6b5 | |||
| 5e8fdf879d | |||
| 84ada18299 | |||
| 635fe8017e | |||
| 834380ff05 | |||
| dd65fc2289 | |||
| 86125bb625 | |||
| 09a9d8b380 | |||
| 86b223ec93 | |||
| 78f6080321 | |||
| c7be6f6e0e | |||
| c40c31fbb7 | |||
| f5fbc1e32d | |||
| 0a20378acd | |||
| d4e87877b6 | |||
| 7eb7236b8a | |||
| dc0e4ffce2 | |||
| 86fe93943d | |||
| 3258113ef8 | |||
| ebf3999cfe | |||
| a1ccbbb5b9 | |||
| 35072c40d8 | |||
| 5c51021388 | |||
| 95fd527515 | |||
| 113d851cea | |||
| 72f0c460f9 | |||
| 13ddd15b44 | |||
| c9e24aaf5f | |||
| 68c0ac0ffc | |||
| bebda275a9 | |||
| 287eaae850 | |||
| 7e59c3b9a7 | |||
| 38658a5b8c | |||
| 88c75b8672 | |||
| 17ab1e6830 | |||
| 1f45110e5f | |||
| 5401166a07 | |||
| bfa8188782 | |||
| e777a71784 | |||
| c13e91a0d0 | |||
| aac5d57352 | |||
| ceca13c7b6 | |||
| 2905470330 | |||
| 2c17033822 | |||
| 0a4cb9e9b1 | |||
| 096651f4c1 | |||
| 275a08506b | |||
| 4cd61cb381 | |||
| 9ad1147581 | |||
| 35e09def1b | |||
| 0469607a71 | |||
| d115d22ee6 | |||
| 8120ae621d | |||
| f873c2fa9f | |||
| c3ffbe2f42 | |||
| 55a0a39881 | |||
| 592215dd30 | |||
| ef0c2a2785 | |||
| 37979e452a | |||
| e9671bb727 | |||
| aa328bd591 | |||
| 188e4313f9 | |||
| df02c70e31 | |||
| 257f54b323 | |||
| fb4b5b2575 | |||
| 4e1251276d | |||
| a7b200dc57 | |||
| 8cadbae80e | |||
| c3bae49aae | |||
|
|
6fa4fc6149 | ||
| f1ffe1acaa | |||
| 5c9ddf7f00 | |||
| 431767a150 | |||
| 8ba4e442a6 | |||
| 2f8d19c267 | |||
| f2fd98f742 | |||
| 3ce35d6e81 | |||
| 796dbaa836 | |||
| 84b209ec7f | |||
| fa976b34dc | |||
| e7cda362b7 | |||
| a41a09c162 | |||
| e82f84f0ab | |||
| f1d387ceea | |||
| 3893d4d1f8 | |||
| cf7ac50faf | |||
| 3f6bf15554 | |||
| dac74829b4 | |||
| 94d6f213a0 | |||
| 8f2550ef61 | |||
| b622a1cc5e | |||
| c83a3c32c7 | |||
| e7dcf7f7e7 | |||
| 398785a2be | |||
| 05dba0f1f5 | |||
| 39dbd32b2e | |||
| 23a34a455a | |||
| 4308328b48 |
2
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
2
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
@@ -31,7 +31,7 @@ body:
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- master branch
|
||||
default: 3
|
||||
default: 2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
4
.github/workflows/build_ios.yml
vendored
4
.github/workflows/build_ios.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Setup .NET Workload
|
||||
run: |
|
||||
dotnet workload install ios
|
||||
@@ -33,4 +33,4 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=9 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=8 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame
|
||||
|
||||
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
BIN
Content/Editor/Primitives/Cube.flax
(Stored with Git LFS)
BIN
Content/Editor/Primitives/Cube.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GlobalSignDistanceField.flax
(Stored with Git LFS)
BIN
Content/Shaders/GlobalSignDistanceField.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/MotionBlur.flax
(Stored with Git LFS)
BIN
Content/Shaders/MotionBlur.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/SDF.flax
(Stored with Git LFS)
BIN
Content/Shaders/SDF.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6805
|
||||
"Build": 6801
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Fedora: `sudo dnf install dotnet-sdk-8.0`
|
||||
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs)
|
||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||
* Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Install Git with LFS
|
||||
@@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Install Clang compiler (version 14 or later):
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
* Arch: `sudo pacman -S clang lldb lld`
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
|
||||
|
||||
if (data is DragDataFiles)
|
||||
return DragDropEffect.Copy;
|
||||
return _dragOverItems.Effect;
|
||||
return _dragOverItems?.Effect ?? DragDropEffect.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code";
|
||||
public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;
|
||||
|
||||
@@ -281,13 +281,6 @@ namespace FlaxEditor.Content
|
||||
|
||||
private void CacheData()
|
||||
{
|
||||
if (!_asset)
|
||||
{
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_attributes = Utils.GetEmptyArray<Attribute>();
|
||||
return;
|
||||
}
|
||||
if (_parameters != null)
|
||||
return;
|
||||
if (_asset.WaitForLoaded())
|
||||
@@ -351,13 +344,13 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null;
|
||||
public string Name => Path.GetFileNameWithoutExtension(_asset.Path);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Namespace => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null;
|
||||
public string TypeName => JsonSerializer.GetStringID(_asset.ID);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
@@ -130,11 +130,6 @@ namespace FlaxEditor.Content
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
|
||||
var antiAliasing = preview.PostFxVolume.AntiAliasing;
|
||||
antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing;
|
||||
antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode;
|
||||
preview.PostFxVolume.AntiAliasing = antiAliasing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,32 +15,26 @@
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
|
||||
String GetGDK()
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
{
|
||||
String gdk;
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
// Find GDK
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
{
|
||||
gdk.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
_gdkPath.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
{
|
||||
gdk.Clear();
|
||||
_gdkPath.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gdk.EndsWith(TEXT("GRDK\\")))
|
||||
gdk.Remove(gdk.Length() - 6);
|
||||
else if (gdk.EndsWith(TEXT("GRDK")))
|
||||
gdk.Remove(gdk.Length() - 5);
|
||||
if (_gdkPath.EndsWith(TEXT("GRDK\\")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 6);
|
||||
else if (_gdkPath.EndsWith(TEXT("GRDK")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 5);
|
||||
}
|
||||
}
|
||||
return gdk;
|
||||
}
|
||||
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
{
|
||||
_gdkPath = GetGDK();
|
||||
}
|
||||
|
||||
DotNetAOTModes GDKPlatformTools::UseAOT() const
|
||||
@@ -127,7 +121,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
|
||||
validName.Add('\0');
|
||||
|
||||
sb.Append(TEXT("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"1\">\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"0\">\n"));
|
||||
sb.AppendFormat(TEXT(" <Identity Name=\"{0}\" Publisher=\"{1}\" Version=\"{2}\"/>\n"),
|
||||
validName.Get(),
|
||||
platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName,
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Editor/Cooker/PlatformTools.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#if PLATFORM_MAC
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
@@ -128,7 +127,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c
|
||||
const String dst = dstPath / StringUtils::GetFileName(file);
|
||||
if (dst == file)
|
||||
continue;
|
||||
if (EditorUtilities::CopyFileIfNewer(dst, file))
|
||||
if (FileSystem::CopyFile(dst, file))
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst));
|
||||
return true;
|
||||
|
||||
@@ -526,7 +526,6 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
case BuildPlatform::XboxScarlett:
|
||||
{
|
||||
options.Platform = PlatformType::XboxScarlett;
|
||||
const char* platformDefineName = "PLATFORM_XBOX_SCARLETT";
|
||||
COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE);
|
||||
break;
|
||||
@@ -1368,10 +1367,7 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
typeName = e.TypeName;
|
||||
}
|
||||
if (e.Count == 1)
|
||||
LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize));
|
||||
else
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
}
|
||||
LOG(Info, "");
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (version.IsEmpty())
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName));
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
|
||||
data.StepProgress(infoMsg, 0);
|
||||
|
||||
// Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit)
|
||||
// TODO: remove it since EngineModule does properly reference AOT lib now
|
||||
EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll"));
|
||||
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml"));
|
||||
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb"));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -12,7 +11,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(EnvironmentProbe)), DefaultEditor]
|
||||
public class EnvironmentProbeEditor : ActorEditor
|
||||
{
|
||||
private Button _bake;
|
||||
private FlaxEngine.GUI.Button _bake;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -21,9 +20,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
_bake = group.Button("Bake").Button;
|
||||
layout.Space(10);
|
||||
_bake = layout.Button("Bake").Button;
|
||||
_bake.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +190,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var file in files)
|
||||
FindNewKeysCSharp(file, newKeys, allKeys);
|
||||
|
||||
// C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories);
|
||||
// C/C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.hpp", SearchOption.AllDirectories)).ToArray();;
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
|
||||
@@ -914,11 +914,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Remove drop down arrows and containment lines if no objects in the group
|
||||
if (group.Children.Count == 0)
|
||||
{
|
||||
group.Panel.Close();
|
||||
group.Panel.ArrowImageOpened = null;
|
||||
group.Panel.ArrowImageClosed = null;
|
||||
group.Panel.EnableContainmentLines = false;
|
||||
group.Panel.CanOpenClose = false;
|
||||
}
|
||||
|
||||
// Scripts arrange bar
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -20,9 +19,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
// Add 'Bake' button
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
var button = group.Button("Bake");
|
||||
layout.Space(10);
|
||||
var button = layout.Button("Bake");
|
||||
button.Button.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,8 +123,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (Picker == null)
|
||||
return;
|
||||
var differentValues = HasDifferentValues;
|
||||
Picker.DifferentValues = differentValues;
|
||||
if (!differentValues)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
panel.Panel.Size = new Float2(0, 18);
|
||||
panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0);
|
||||
|
||||
var removeButton = panel.Button("-", "Remove the last item.");
|
||||
var removeButton = panel.Button("-", "Remove the last item");
|
||||
removeButton.Button.Size = new Float2(16, 16);
|
||||
removeButton.Button.Enabled = size > _minCount;
|
||||
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
@@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
Resize(Count - 1);
|
||||
};
|
||||
|
||||
var addButton = panel.Button("+", "Add a new item.");
|
||||
var addButton = panel.Button("+", "Add a new item");
|
||||
addButton.Button.Size = new Float2(16, 16);
|
||||
addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount;
|
||||
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public event Action<TypePickerControl> TypePickerValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for types validation. Can be used to implement a rule for types to pick.
|
||||
/// The custom callback for types validation. Cane be used to implement a rule for types to pick.
|
||||
/// </summary>
|
||||
public Func<ScriptType, bool> CheckValid;
|
||||
|
||||
@@ -353,13 +353,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
if (!string.IsNullOrEmpty(typeReference.CheckMethod))
|
||||
{
|
||||
var parentEditor = ParentEditor;
|
||||
// Find actual parent editor if parent editor is collection editor
|
||||
while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor)))
|
||||
parentEditor = parentEditor.ParentEditor;
|
||||
|
||||
var parentType = parentEditor.Values[0].GetType();
|
||||
|
||||
var parentType = ParentEditor.Values[0].GetType();
|
||||
var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (method != null)
|
||||
{
|
||||
|
||||
@@ -1390,7 +1390,6 @@ namespace FlaxEditor
|
||||
public void BuildAllMeshesSDF()
|
||||
{
|
||||
var models = new List<Model>();
|
||||
var forceRebuild = Input.GetKey(KeyboardKeys.F);
|
||||
Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
|
||||
@@ -1400,7 +1399,7 @@ namespace FlaxEditor
|
||||
model != null &&
|
||||
!models.Contains(model) &&
|
||||
!model.IsVirtual &&
|
||||
(forceRebuild || model.SDF.Texture == null))
|
||||
model.SDF.Texture == null)
|
||||
{
|
||||
models.Add(model);
|
||||
}
|
||||
@@ -1413,17 +1412,7 @@ namespace FlaxEditor
|
||||
{
|
||||
var model = models[i];
|
||||
Log($"[{i}/{models.Count}] Generating SDF for {model}");
|
||||
float resolutionScale = 1.0f, backfacesThreshold = 0.6f;
|
||||
int lodIndex = 6;
|
||||
bool useGPU = true;
|
||||
var sdf = model.SDF;
|
||||
if (sdf.Texture != null)
|
||||
{
|
||||
// Preserve options set on this model
|
||||
resolutionScale = sdf.ResolutionScale;
|
||||
lodIndex = sdf.LOD;
|
||||
}
|
||||
if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU))
|
||||
if (!model.GenerateSDF())
|
||||
model.Save();
|
||||
}
|
||||
});
|
||||
@@ -1598,7 +1587,7 @@ namespace FlaxEditor
|
||||
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
|
||||
result = dockedTo.SelectedTab.Size;
|
||||
else
|
||||
result = gameWin.Viewport.ContentSize;
|
||||
result = gameWin.Viewport.Size;
|
||||
|
||||
result *= root.DpiScale;
|
||||
result = Float2.Round(result);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
#define USE_IS_FOREGROUND
|
||||
#else
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
#define USE_SDL_WORKAROUNDS
|
||||
#endif
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -122,7 +125,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the empty menu popup o na screen.
|
||||
/// Shows the empty menu popup on a screen.
|
||||
/// </summary>
|
||||
/// <param name="control">The target control.</param>
|
||||
/// <param name="area">The target control area to cover.</param>
|
||||
@@ -257,7 +260,9 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
desc.AllowMaximize = false;
|
||||
desc.AllowDragAndDrop = false;
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.Type = WindowType.Popup;
|
||||
desc.Parent = parentWin.Window;
|
||||
desc.Title = "ContextMenu";
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
@@ -266,6 +271,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
_window.GotFocus += OnWindowGotFocus;
|
||||
_window.LostFocus += OnWindowLostFocus;
|
||||
}
|
||||
|
||||
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
|
||||
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
|
||||
parentWin.Window.MouseDown += OnWindowMouseDown;
|
||||
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
|
||||
#endif
|
||||
|
||||
// Attach to the window
|
||||
_parentCM = parent as ContextMenuBase;
|
||||
@@ -441,6 +452,17 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
|
||||
{
|
||||
// The user clicked outside the popup window
|
||||
Hide();
|
||||
}
|
||||
#else
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
var child = _childCM;
|
||||
@@ -454,6 +476,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -552,7 +575,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && UseVisibilityControl && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -326,8 +326,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Update eye dropper tool
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
// Try reading the color under the cursor in realtime if supported by the platform
|
||||
Float2 mousePosition = Platform.MousePosition;
|
||||
SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
|
||||
Color color = ScreenUtilities.GetColorAt(mousePosition);
|
||||
if (color != Color.Transparent)
|
||||
SelectedColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,545 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class DockHintWindow
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Float2 _defaultWindowSize;
|
||||
private Rectangle _rectDock;
|
||||
private Rectangle _rectWindow;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private bool _lateDragOffsetUpdate;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
|
||||
private DockHintWindow(FloatWindowDockPanel toMove)
|
||||
{
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Remove focus from drag target
|
||||
_toMove.Focus();
|
||||
_toMove.Defocus();
|
||||
|
||||
// Focus window
|
||||
window.Focus();
|
||||
|
||||
// Check if window is maximized and restore window.
|
||||
if (window.IsMaximized)
|
||||
{
|
||||
// Restore window and set position to mouse.
|
||||
var mousePos = window.MousePosition;
|
||||
var previousSize = window.Size;
|
||||
window.Restore();
|
||||
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
|
||||
}
|
||||
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
var mouseScreenPosition = Platform.MousePosition;
|
||||
|
||||
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
|
||||
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
|
||||
if (mouseScreenPosition != Float2.Zero)
|
||||
CalculateDragOffset(mouseScreenPosition);
|
||||
else
|
||||
_lateDragOffsetUpdate = true;
|
||||
|
||||
// Get initial size
|
||||
_defaultWindowSize = window.Size;
|
||||
|
||||
// Init proxy window
|
||||
Proxy.Init(ref _defaultWindowSize);
|
||||
|
||||
// Bind events
|
||||
Proxy.Window.MouseUp += OnMouseUp;
|
||||
Proxy.Window.MouseMove += OnMouseMove;
|
||||
Proxy.Window.LostFocus += OnLostFocus;
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
|
||||
// Update window GUI
|
||||
Proxy.Window.GUI.PerformLayout();
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects();
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// End tracking mouse
|
||||
Proxy.Window.EndTrackingMouse();
|
||||
|
||||
// Disable rendering
|
||||
Proxy.Window.RenderingEnabled = false;
|
||||
|
||||
// Unbind events
|
||||
Proxy.Window.MouseUp -= OnMouseUp;
|
||||
Proxy.Window.MouseMove -= OnMouseMove;
|
||||
Proxy.Window.LostFocus -= OnLostFocus;
|
||||
|
||||
// Hide the proxy
|
||||
Proxy.Hide();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
var window = _toMove.Window?.Window;
|
||||
if (window == null)
|
||||
return;
|
||||
var mouse = Platform.MousePosition;
|
||||
|
||||
// Move base window
|
||||
window.Position = mouse - _dragOffset;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new DockHintWindow(toMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(DockWindow toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Show floating
|
||||
toMove.ShowFloating();
|
||||
|
||||
// Move window to the mouse position (with some offset for caption bar)
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var mouse = Platform.MousePosition;
|
||||
window.Window.Position = mouse - new Float2(8, 8);
|
||||
|
||||
// Get floating panel
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new DockHintWindow(floatingPanelToMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates window rectangle in the dock window.
|
||||
/// </summary>
|
||||
/// <param name="state">Window dock state.</param>
|
||||
/// <param name="rect">Dock panel rectangle.</param>
|
||||
/// <returns>Calculated window rectangle.</returns>
|
||||
public static Rectangle CalculateDockRect(DockState state, ref Rectangle rect)
|
||||
{
|
||||
Rectangle result = rect;
|
||||
switch (state)
|
||||
{
|
||||
case DockState.DockFill:
|
||||
result.Location.Y += Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
result.Size.Y -= Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
break;
|
||||
case DockState.DockTop:
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockLeft:
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockBottom:
|
||||
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockRight:
|
||||
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CalculateDragOffset(Float2 mouseScreenPosition)
|
||||
{
|
||||
var baseWinPos = _toMove.Window.Window.Position;
|
||||
_dragOffset = mouseScreenPosition - baseWinPos;
|
||||
}
|
||||
|
||||
private void UpdateRects()
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = Platform.MousePosition;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_mouse))
|
||||
toSet = DockState.DockTop;
|
||||
else if (_rBottom.Contains(_mouse))
|
||||
toSet = DockState.DockBottom;
|
||||
else if (_rLeft.Contains(_mouse))
|
||||
toSet = DockState.DockLeft;
|
||||
else if (_rRight.Contains(_mouse))
|
||||
toSet = DockState.DockRight;
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_mouse))
|
||||
toSet = DockState.DockFill;
|
||||
_toSet = toSet;
|
||||
|
||||
// Show proxy hint windows
|
||||
Proxy.Down.Position = _rBottom.Location;
|
||||
Proxy.Left.Position = _rLeft.Location;
|
||||
Proxy.Right.Position = _rRight.Location;
|
||||
Proxy.Up.Position = _rUpper.Location;
|
||||
Proxy.Center.Position = _rCenter.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update proxy hint windows visibility
|
||||
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
|
||||
|
||||
// Calculate proxy/dock/window rectangles
|
||||
if (_toDock == null)
|
||||
{
|
||||
// Floating window over nothing
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
// Floating window over dock panel
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use only part of the dock panel to show hint
|
||||
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
|
||||
}
|
||||
}
|
||||
|
||||
// Update proxy window
|
||||
Proxy.Window.ClientBounds = _rectWindow;
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(ref Float2 mousePos)
|
||||
{
|
||||
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
|
||||
if (_lateDragOffsetUpdate)
|
||||
{
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
CalculateDragOffset(mousePos);
|
||||
|
||||
// Reset state
|
||||
_lateDragOffsetUpdate = false;
|
||||
}
|
||||
|
||||
UpdateRects();
|
||||
}
|
||||
|
||||
private void OnLostFocus()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
|
||||
/// </summary>
|
||||
public static class Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The drag proxy window.
|
||||
/// </summary>
|
||||
public static Window Window;
|
||||
|
||||
/// <summary>
|
||||
/// The left hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Left;
|
||||
|
||||
/// <summary>
|
||||
/// The right hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Right;
|
||||
|
||||
/// <summary>
|
||||
/// The up hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Up;
|
||||
|
||||
/// <summary>
|
||||
/// The down hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Down;
|
||||
|
||||
/// <summary>
|
||||
/// The center hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Center;
|
||||
|
||||
/// <summary>
|
||||
/// The hint windows size.
|
||||
/// </summary>
|
||||
public const float HintWindowsSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
|
||||
/// </summary>
|
||||
public static void InitHitProxy()
|
||||
{
|
||||
CreateProxy(ref Left, "DockHint.Left");
|
||||
CreateProxy(ref Right, "DockHint.Right");
|
||||
CreateProxy(ref Up, "DockHint.Up");
|
||||
CreateProxy(ref Down, "DockHint.Down");
|
||||
CreateProxy(ref Center, "DockHint.Center");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hint window.
|
||||
/// </summary>
|
||||
/// <param name="initSize">Initial size of the proxy window.</param>
|
||||
public static void Init(ref Float2 initSize)
|
||||
{
|
||||
if (Window == null)
|
||||
{
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "DockHint.Window";
|
||||
settings.Size = initSize;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.IsTopmost = true;
|
||||
|
||||
Window = Platform.CreateWindow(ref settings);
|
||||
Window.Opacity = 0.6f;
|
||||
Window.GUI.BackgroundColor = Style.Current.Selection;
|
||||
Window.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize proxy
|
||||
Window.ClientSize = initSize;
|
||||
}
|
||||
|
||||
InitHitProxy();
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateProxy(ref Window win, string name)
|
||||
{
|
||||
if (win != null)
|
||||
return;
|
||||
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = name;
|
||||
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
|
||||
settings.AllowInput = false;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ActivateWhenFirstShown = false;
|
||||
settings.IsTopmost = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
|
||||
win = Platform.CreateWindow(ref settings);
|
||||
win.Opacity = 0.6f;
|
||||
win.GUI.BackgroundColor = Style.Current.Selection;
|
||||
win.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides proxy windows.
|
||||
/// </summary>
|
||||
public static void Hide()
|
||||
{
|
||||
HideProxy(ref Window);
|
||||
HideProxy(ref Left);
|
||||
HideProxy(ref Right);
|
||||
HideProxy(ref Up);
|
||||
HideProxy(ref Down);
|
||||
HideProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void HideProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases proxy data and windows.
|
||||
/// </summary>
|
||||
public static void Dispose()
|
||||
{
|
||||
DisposeProxy(ref Window);
|
||||
DisposeProxy(ref Left);
|
||||
DisposeProxy(ref Right);
|
||||
DisposeProxy(ref Up);
|
||||
DisposeProxy(ref Down);
|
||||
DisposeProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void DisposeProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Close(ClosingReason.User);
|
||||
win = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
private float _tabHeight = Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
private bool _useMinimumTabWidth = Editor.Instance.Options.Options.Interface.UseMinimumTabWidth;
|
||||
private float _minimumTabWidth = Editor.Instance.Options.Options.Interface.MinimumTabWidth;
|
||||
#if PLATFORM_WINDOWS
|
||||
private readonly bool _hideTabForSingleTab = Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#else
|
||||
private readonly bool _hideTabForSingleTab = false;
|
||||
#endif
|
||||
private readonly bool _hideTabForSingleTab = Utilities.Utils.HideSingleTabWindowTabBars();
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag (left button).
|
||||
@@ -54,6 +50,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MousePosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MouseStartPosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The start drag asynchronous window.
|
||||
@@ -196,7 +197,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
|
||||
{
|
||||
// Create docking hint window but in an async manner
|
||||
DockHintWindow.Create(_panel as FloatWindowDockPanel);
|
||||
WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -207,7 +208,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel.SelectTab(index - 1);
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(win);
|
||||
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,6 +394,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
return base.OnMouseDown(location, button);
|
||||
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
|
||||
MouseStartPosition = location;
|
||||
|
||||
// Check buttons
|
||||
if (button == MouseButton.Left)
|
||||
@@ -479,6 +481,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
// Check if single tab is tried to be moved
|
||||
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
|
||||
{
|
||||
if ((MousePosition - MouseStartPosition).Length > 3)
|
||||
{
|
||||
// Clear flag
|
||||
IsMouseLeftButtonDown = false;
|
||||
|
||||
// Check tab under the mouse
|
||||
if (!IsMouseDownOverCross && MouseDownWindow != null)
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
}
|
||||
// Check if has more than one tab to change order
|
||||
else if (MouseDownWindow != null && _panel.TabsCount > 1)
|
||||
{
|
||||
|
||||
@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
|
||||
{
|
||||
CreateFloating(location, size, position, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
public void CreateFloating()
|
||||
{
|
||||
CreateFloating(Float2.Zero, Float2.Zero);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
/// <param name="location">Window location.</param>
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
/// <param name="showWindow">Window visibility.</param>
|
||||
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
|
||||
{
|
||||
Undock();
|
||||
|
||||
@@ -199,14 +218,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
windowGUI.UnlockChildrenRecursive();
|
||||
windowGUI.PerformLayout();
|
||||
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
if (showWindow)
|
||||
{
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <seealso cref="DockPanel" />
|
||||
public class FloatWindowDockPanel : DockPanel
|
||||
{
|
||||
private class FloatWindowDecorations : WindowDecorations
|
||||
{
|
||||
private FloatWindowDockPanel _panel;
|
||||
|
||||
public FloatWindowDecorations(FloatWindowDockPanel panel)
|
||||
: base(panel.RootWindow)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_panel.BeginDrag();
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var hit = base.OnHitTest(ref mouse);
|
||||
if (hit == WindowHitCodes.Caption)
|
||||
{
|
||||
// Override the system behaviour when interacting with the caption area
|
||||
hit = WindowHitCodes.Client;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private MasterDockPanel _masterPanel;
|
||||
private WindowRootControl _window;
|
||||
|
||||
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
Parent = window;
|
||||
_window.Window.Closing += OnClosing;
|
||||
_window.Window.LeftButtonHit += OnLeftButtonHit;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
|
||||
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
{
|
||||
// Apply offset for the title bar
|
||||
foreach (var child in Children)
|
||||
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +108,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
return;
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
WindowDragHelper.StartDragging(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,22 +127,28 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Title = title;
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
settings.SupportsTransparency = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ActivateWhenFirstShown = true;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMinimize = true;
|
||||
settings.AllowMaximize = true;
|
||||
settings.AllowDragAndDrop = true;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = true;
|
||||
settings.Type = WindowType.Regular;
|
||||
settings.HasSizingFrame = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.ShowInTaskbar = true;
|
||||
settings.StartPosition = startPosition;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
//settings.HasSizingFrame = false;
|
||||
}
|
||||
|
||||
// Create window
|
||||
return Platform.CreateWindow(ref settings);
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
|
||||
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
|
||||
{
|
||||
// Check all floating windows
|
||||
// TODO: gather windows order and take it into account when performing test
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
return base.HitTest(ref position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs hit test over dock panel.
|
||||
/// </summary>
|
||||
/// <param name="position">Window space position to test.</param>
|
||||
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
|
||||
/// <param name="hitResults">Results of the hit test</param>
|
||||
/// <returns>True if any dock panels were hit, otherwise false.</returns>
|
||||
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
|
||||
{
|
||||
// Check all floating windows
|
||||
List<DockPanel> results = new(FloatingPanels.Count);
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
if (win.Visible && win != excluded)
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
var baseResult = base.HitTest(ref position);
|
||||
if (baseResult != null)
|
||||
results.Add(baseResult);
|
||||
|
||||
hitResults = results.ToArray();
|
||||
return hitResults.Length > 0;
|
||||
}
|
||||
|
||||
internal void LinkWindow(DockWindow window)
|
||||
{
|
||||
// Add to the windows list
|
||||
|
||||
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class WindowDragHelper
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Rectangle _rectDock;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private Window _dragSourceWindow;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
|
||||
|
||||
/// <summary>
|
||||
/// The hint control size.
|
||||
/// </summary>
|
||||
public const float HintControlSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity of the dragged window when hint controls are shown.
|
||||
/// </summary>
|
||||
public const float DragWindowOpacity = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any windows are being dragged.
|
||||
/// </summary>
|
||||
public static bool IsDragActive { get; private set; }
|
||||
|
||||
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
|
||||
{
|
||||
IsDragActive = true;
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Bind events
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
window.MouseUp += OnMouseUp;
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects(Platform.MousePosition);
|
||||
|
||||
// Ensure the dragged window stays on top of every other window
|
||||
window.IsAlwaysOnTop = true;
|
||||
|
||||
_dragSourceWindow = dragSourceWindow;
|
||||
if (_dragSourceWindow != null) // Detaching a tab from existing window
|
||||
{
|
||||
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
|
||||
|
||||
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
|
||||
|
||||
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
|
||||
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
|
||||
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragOffset = window.MousePosition;
|
||||
window.DoDragDrop(window.Title, _dragOffset, window);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IsDragActive = false;
|
||||
var window = _toMove?.Window?.Window;
|
||||
|
||||
// Unbind events
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (window != null)
|
||||
window.MouseUp -= OnMouseUp;
|
||||
if (_dragSourceWindow != null)
|
||||
_dragSourceWindow.MouseUp -= OnMouseUp;
|
||||
|
||||
RemoveDockHints();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
window.Opacity = 1.0f;
|
||||
window.IsAlwaysOnTop = false;
|
||||
window.BringToFront();
|
||||
}
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
if (_toMove.TabsCount > 0)
|
||||
{
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a floating dock panel.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new WindowDragHelper(toMove, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a docked panel into a floating window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started from.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Create floating window
|
||||
toMove.CreateFloating();
|
||||
|
||||
// Get floating panel
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
|
||||
|
||||
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
|
||||
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
|
||||
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
|
||||
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
|
||||
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
|
||||
|
||||
Control AddHintControl(Float2 pivot)
|
||||
{
|
||||
DragVisuals hintControl = _toDock.AddChild<DragVisuals>();
|
||||
hintControl.Size = new Float2(HintControlSize);
|
||||
hintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
hintControl.Pivot = pivot;
|
||||
hintControl.PivotRelative = true;
|
||||
return hintControl;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
|
||||
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
|
||||
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
|
||||
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
|
||||
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
|
||||
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
|
||||
}
|
||||
|
||||
private void UpdateRects(Float2 mousePos)
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = mousePos;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
DockPanel dockPanel = null;
|
||||
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
|
||||
{
|
||||
dockPanel = hitResults[0];
|
||||
|
||||
// Prefer panel which currently has focus
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow.Window.IsFocused)
|
||||
{
|
||||
dockPanel = hit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer panel in the same window we hit earlier
|
||||
if (dockPanel?.RootWindow != _toDock?.RootWindow)
|
||||
{
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow == _toDock?.RootWindow)
|
||||
{
|
||||
dockPanel = _toDock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dockPanel != _toDock)
|
||||
{
|
||||
RemoveDockHints();
|
||||
_toDock = dockPanel;
|
||||
AddDockHints();
|
||||
|
||||
// Make sure the all the dock hint areas are not under other windows
|
||||
_toDock?.RootWindow.Window.BringToFront();
|
||||
//_toDock?.RootWindow.Window.Focus();
|
||||
|
||||
// Make the dragged window transparent when dock hints are visible
|
||||
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
Control hoveredHintControl = null;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size / Platform.DpiScale;
|
||||
var offset = _toDock.PointFromScreen(_rectDock.Location);
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = HintControlSize;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 2, size.Y * 0.5f));
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test, and calculate the approximation for filled area when hovered over the hint
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
hoveredHintControl = _dockHintLeft;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockRight;
|
||||
hoveredHintControl = _dockHintRight;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockFill;
|
||||
hoveredHintControl = _dockHintCenter;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y);
|
||||
}
|
||||
|
||||
_toSet = toSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update sizes and opacity of hint controls
|
||||
if (_toDock != null)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(HintControlSize);
|
||||
_dockHintDown.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(HintControlSize);
|
||||
_dockHintLeft.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(HintControlSize);
|
||||
_dockHintRight.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(HintControlSize);
|
||||
_dockHintUp.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(HintControlSize);
|
||||
_dockHintCenter.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
|
||||
if (_toSet != DockState.Float)
|
||||
{
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(1.0f);
|
||||
hoveredHintControl.Size = hoveredSizeOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update hint controls visibility and location
|
||||
if (showProxyHints)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
_dockHintDown.Location = _rBottom.Location;
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
_dockHintLeft.Location = _rLeft.Location;
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
_dockHintRight.Location = _rRight.Location;
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
_dockHintUp.Location = _rUpper.Location;
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
_dockHintCenter.Location = _rCenter.Location;
|
||||
|
||||
_dockHintDown.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintLeft.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintRight.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintUp.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintCenter.Visible = showProxyHints & showCenterHint;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnUpdate()
|
||||
{
|
||||
var mousePos = Platform.MousePosition;
|
||||
|
||||
if (_mouse != mousePos)
|
||||
OnMouseMove(mousePos);
|
||||
}
|
||||
|
||||
private void OnMouseMove(Float2 mousePos)
|
||||
{
|
||||
if (_dragSourceWindow != null)
|
||||
_toMove.Window.Window.Position = mousePos - _dragOffset;
|
||||
|
||||
UpdateRects(mousePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
@@ -292,13 +293,45 @@ namespace FlaxEditor.GUI.Input
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMoveRelative(Float2 mouseMotion)
|
||||
{
|
||||
var location = Root.TrackingMouseOffset;
|
||||
if (_isSliding)
|
||||
{
|
||||
// Update sliding
|
||||
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cursor type so user knows they can slide value
|
||||
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.SizeWE;
|
||||
_cursorChanged = true;
|
||||
}
|
||||
else if (_cursorChanged && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
_cursorChanged = false;
|
||||
}
|
||||
|
||||
base.OnMouseMoveRelative(mouseMotion);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _isSliding)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
// End sliding and return mouse to original location
|
||||
RootWindow.MousePosition = _mouseClickedPosition;
|
||||
#endif
|
||||
EndSliding();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
#endif
|
||||
private MainMenuButton _selected;
|
||||
|
||||
/// <summary>
|
||||
@@ -60,200 +50,12 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">The main window.</param>
|
||||
public MainMenu(RootControl mainWindow)
|
||||
public MainMenu()
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Height = 28;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_window = mainWindow.RootWindow.Window;
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(6, 6, 6, 6),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
KeepAspectRatio = false,
|
||||
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration),
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest = null;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var dpiScale = _window.DpiScale;
|
||||
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var mousePos = PointFromScreen(mouse * dpiScale);
|
||||
var controlUnderMouse = GetChildAt(mousePos);
|
||||
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
|
||||
var rb = GetRightButton();
|
||||
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Return the rightmost button.
|
||||
/// </summary>
|
||||
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
|
||||
private MainMenuButton GetRightButton()
|
||||
{
|
||||
MainMenuButton b = null;
|
||||
foreach (var control in Children)
|
||||
{
|
||||
if (b == null && control is MainMenuButton)
|
||||
b = (MainMenuButton)control;
|
||||
|
||||
if (control is MainMenuButton && control.Right > b.Right)
|
||||
b = (MainMenuButton)control;
|
||||
}
|
||||
return b;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,26 +100,6 @@ namespace FlaxEditor.GUI
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
@@ -333,16 +115,8 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
#endif
|
||||
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
|
||||
x += decorations?.Icon?.Width ?? 0;
|
||||
|
||||
// Arrange controls
|
||||
MainMenuButton rightMostButton = null;
|
||||
@@ -361,37 +135,21 @@ namespace FlaxEditor.GUI
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = Width - _closeButton.Width;
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
|
||||
|
||||
// Title
|
||||
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
|
||||
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill the right side if title and buttons are not present
|
||||
if (decorations?.Title == null)
|
||||
Width = Parent.Width;
|
||||
else
|
||||
Width = x;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
|
||||
if (_selected != null)
|
||||
Selected = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
if (!Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
BackgroundColorMouseOverOpened = style.Background;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
|
||||
}
|
||||
|
||||
@@ -447,8 +447,8 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Select previous parent child
|
||||
var select = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
|
||||
// Get bottom most child node
|
||||
while (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
// Select last child if is valid and expanded and has any children
|
||||
if (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
{
|
||||
select = select.GetChild(select.ChildrenCount - 1) as TreeNode;
|
||||
}
|
||||
|
||||
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the title bar of the window with buttons.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class WindowDecorations : ContainerControl
|
||||
{
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
|
||||
/// <summary>
|
||||
/// The title label in the title bar.
|
||||
/// </summary>
|
||||
public Label Title => _title;
|
||||
|
||||
/// <summary>
|
||||
/// The icon used in the title bar.
|
||||
/// </summary>
|
||||
public Image Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip shown when hovering over the icon.
|
||||
/// </summary>
|
||||
public string IconTooltipText
|
||||
{
|
||||
get => _icon?.TooltipText ?? null;
|
||||
set
|
||||
{
|
||||
if (_icon != null)
|
||||
_icon.TooltipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
|
||||
/// </summary>
|
||||
/// <param name="window">The window.</param>
|
||||
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
|
||||
public WindowDecorations(RootControl window, bool iconOnly = false)
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
_window = window.RootWindow.Window;
|
||||
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Color.Transparent;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(4, 4, 4, 4),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
KeepAspectRatio = false,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
if (!iconOnly)
|
||||
{
|
||||
_icon.Margin = new Margin(6, 6, 6, 6);
|
||||
Height = 28;
|
||||
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
if (_maximizeButton.Text != maximizeText)
|
||||
_maximizeButton.Text = maximizeText;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest -= OnHitTest;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform hit test on the window.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position</param>
|
||||
/// <returns>The hit code for given position.</returns>
|
||||
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
var dpiScale = _window.DpiScale;
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var controlUnderMouse = GetChildAt(pos, control => control != _title);
|
||||
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
// These may not work with main window due to SDL not passing mouse events
|
||||
// when interacting with hit tests on caption area...
|
||||
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
return true;
|
||||
}
|
||||
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_window.Close(ClosingReason.User);
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
// Calculate extents for title bounds area excluding the icon and main menu area
|
||||
float x = 0;
|
||||
|
||||
// Icon
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
|
||||
// Main menu if present
|
||||
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
|
||||
{
|
||||
for (int i = 0; i < mainMenu.Children.Count; i++)
|
||||
{
|
||||
var c = mainMenu.Children[i];
|
||||
if (c is MainMenuButton b && c.Visible)
|
||||
{
|
||||
b.Bounds = new Rectangle(x, 0, b.Width, Height);
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
float rightMostButtonX = Width;
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = rightMostButtonX - _closeButton.Width;
|
||||
rightMostButtonX = _closeButton.X;
|
||||
}
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
|
||||
rightMostButtonX = _maximizeButton.X;
|
||||
}
|
||||
if (_minimizeButton != null)
|
||||
{
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
|
||||
rightMostButtonX = _minimizeButton.X;
|
||||
}
|
||||
|
||||
// Title
|
||||
if (_title != null)
|
||||
{
|
||||
_title.Text = _window.Title;
|
||||
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
DrawBorders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw borders around the window.
|
||||
/// </summary>
|
||||
public virtual void DrawBorders()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
if (Editor.Instance.UI.StatusBar == null)
|
||||
return;
|
||||
|
||||
const float thickness = 1.0f;
|
||||
Color color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
|
||||
Render2D.DrawRectangle(rect, color);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,10 +402,11 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
for (const auto& e : inputEvents)
|
||||
{
|
||||
auto window = e.Target ? e.Target : defaultWindow;
|
||||
if (!window)
|
||||
if (!window || window->IsClosed())
|
||||
continue;
|
||||
switch (e.Type)
|
||||
{
|
||||
@@ -435,12 +436,14 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
case InputDevice::EventType::MouseMove:
|
||||
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
|
||||
break;
|
||||
case InputDevice::EventType::MouseMoveRelative:
|
||||
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
|
||||
break;
|
||||
case InputDevice::EventType::MouseLeave:
|
||||
window->OnMouseLeave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
}
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
|
||||
@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
item = new CSharpScriptItem(path);
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h"))
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
|
||||
item = new CppScriptItem(path);
|
||||
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
|
||||
item = new ShaderSourceItem(path);
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
|
||||
outputExtension = extension;
|
||||
|
||||
// Check if can place source files here
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h"))
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
|
||||
{
|
||||
// Error
|
||||
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayBegin()
|
||||
public override void OnPlayBeginning()
|
||||
{
|
||||
Editor.Windows.FlashMainWindow();
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow;
|
||||
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Options;
|
||||
@@ -29,6 +28,40 @@ namespace FlaxEditor.Modules
|
||||
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
|
||||
public sealed class UIModule : EditorModule
|
||||
{
|
||||
private class MainWindowDecorations : WindowDecorations
|
||||
{
|
||||
public MainWindowDecorations(RootControl window, bool iconOnly)
|
||||
: base(window, iconOnly)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (base.OnKeyDown(key))
|
||||
return true;
|
||||
|
||||
// Fallback to the edit window for shortcuts
|
||||
var editor = Editor.Instance;
|
||||
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawBorders()
|
||||
{
|
||||
// Draw main window borders if using a custom style
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
var color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
|
||||
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
|
||||
}
|
||||
}
|
||||
|
||||
private Label _progressLabel;
|
||||
private ProgressBar _progressBar;
|
||||
private Button _outputLogButton;
|
||||
@@ -144,6 +177,11 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public MainMenu MainMenu;
|
||||
|
||||
/// <summary>
|
||||
/// The window decorations (title bar with buttons)
|
||||
/// </summary>
|
||||
public WindowDecorations WindowDecorations;
|
||||
|
||||
/// <summary>
|
||||
/// The tool strip control.
|
||||
/// </summary>
|
||||
@@ -426,19 +464,11 @@ namespace FlaxEditor.Modules
|
||||
InitToolstrip(mainWindow);
|
||||
InitStatusBar(mainWindow);
|
||||
InitDockPanel(mainWindow);
|
||||
InitWindowDecorations(mainWindow);
|
||||
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
// Add dummy control for drawing the main window borders if using a custom style
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
|
||||
#endif
|
||||
{
|
||||
mainWindow.AddChild(new CustomWindowBorderControl
|
||||
{
|
||||
Size = Float2.Zero,
|
||||
});
|
||||
}
|
||||
|
||||
mainWindow.PerformLayout(true);
|
||||
}
|
||||
|
||||
private void InitViewportScaleOptions()
|
||||
@@ -510,23 +540,6 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomWindowBorderControl : Control
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
var color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
|
||||
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndInit()
|
||||
{
|
||||
@@ -558,13 +571,6 @@ namespace FlaxEditor.Modules
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
// Cleanup dock panel hint proxy windows (Flax will destroy them by var but it's better to clear them earlier)
|
||||
DockHintWindow.Proxy.Dispose();
|
||||
}
|
||||
|
||||
private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing)
|
||||
{
|
||||
var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing);
|
||||
@@ -618,10 +624,12 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private void InitMainMenu(RootControl mainWindow)
|
||||
{
|
||||
MainMenu = new MainMenu(mainWindow)
|
||||
MainMenu = new MainMenu()
|
||||
{
|
||||
Parent = mainWindow
|
||||
};
|
||||
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
MainMenu.Height = 28;
|
||||
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
@@ -714,7 +722,6 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG);
|
||||
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh);
|
||||
_menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF);
|
||||
_menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one.");
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
|
||||
_menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel());
|
||||
@@ -763,6 +770,20 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
|
||||
}
|
||||
|
||||
private void InitWindowDecorations(RootControl mainWindow)
|
||||
{
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
var driver = Platform.DisplayServer;
|
||||
if (!string.IsNullOrEmpty(driver))
|
||||
driver = $" ({driver})";
|
||||
|
||||
WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
{
|
||||
Parent = mainWindow,
|
||||
IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}",
|
||||
};
|
||||
}
|
||||
|
||||
private void OnOptionsChanged(EditorOptions options)
|
||||
{
|
||||
var inputOptions = options.Input;
|
||||
@@ -1183,6 +1204,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
// Clear UI references (GUI cannot be used after window closing)
|
||||
MainMenu = null;
|
||||
WindowDecorations = null;
|
||||
ToolStrip = null;
|
||||
MasterPanel = null;
|
||||
StatusBar = null;
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEditor.Windows.Profiler;
|
||||
@@ -758,17 +757,19 @@ namespace FlaxEditor.Modules
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "Flax Editor";
|
||||
settings.Size = Platform.DesktopSize * 0.75f;
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.StartPosition = WindowStartPosition.CenterScreen;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
// Skip OS sizing frame and implement it using LeftButtonHit
|
||||
settings.HasSizingFrame = false;
|
||||
#endif
|
||||
}
|
||||
#elif PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX && !PLATFORM_SDL
|
||||
settings.HasBorder = false;
|
||||
#endif
|
||||
MainWindow = Platform.CreateWindow(ref settings);
|
||||
@@ -896,11 +897,9 @@ namespace FlaxEditor.Modules
|
||||
|
||||
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
|
||||
{
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
|
||||
var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
|
||||
var assetType = assetItem.GetType();
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType });
|
||||
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
|
||||
|
||||
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
{
|
||||
|
||||
@@ -179,6 +179,34 @@ namespace FlaxEditor.Options
|
||||
GameWindowThenRestore,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for type of window decorations to use.
|
||||
/// </summary>
|
||||
public enum WindowDecorationsType
|
||||
{
|
||||
/// <summary>
|
||||
/// Determined automatically based on the system and any known compatibility issues with native decorations.
|
||||
/// </summary>
|
||||
Auto,
|
||||
|
||||
/// <summary>
|
||||
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
|
||||
/// </summary>
|
||||
[EditorDisplay(Name = "Auto (Child Only)")]
|
||||
AutoChildOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Use native system window decorations on all windows.
|
||||
/// </summary>
|
||||
Native,
|
||||
|
||||
/// <summary>
|
||||
/// Use custom client-side window decorations on all windows.
|
||||
/// </summary>
|
||||
[EditorDisplay(Name = "Client-side")]
|
||||
ClientSide,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -268,7 +296,14 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(322)]
|
||||
public bool ScrollToScriptOnAdd { get; set; } = true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
|
||||
/// </summary>
|
||||
[DefaultValue(WindowDecorationsType.AutoChildOnly)]
|
||||
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
|
||||
public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly;
|
||||
#elif PLATFORM_WINDOWS
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -277,7 +312,7 @@ namespace FlaxEditor.Options
|
||||
public bool UseNativeWindowSystem { get; set; } = false;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL || PLATFORM_WINDOWS
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a window containing a single tabs hides the tab bar. Editor restart recommended.
|
||||
/// </summary>
|
||||
|
||||
@@ -121,9 +121,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
|
||||
// Discard non-source files or generated files
|
||||
if ((!path.EndsWith(TEXT(".cs")) &&
|
||||
!path.EndsWith(TEXT(".cpp")) &&
|
||||
!path.EndsWith(TEXT(".c")) &&
|
||||
!path.EndsWith(TEXT(".hpp")) &&
|
||||
!path.EndsWith(TEXT(".h"))) ||
|
||||
path.EndsWith(TEXT(".Gen.cs")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScopeLock scopeLock(_locker);
|
||||
_lastSourceCodeEdited = DateTime::Now();
|
||||
|
||||
@@ -406,8 +406,6 @@ namespace FlaxEngine.Utilities
|
||||
{
|
||||
if (type == ScriptType.Null)
|
||||
return null;
|
||||
if (type.BaseType == null)
|
||||
return type.Type;
|
||||
while (type.Type == null)
|
||||
type = type.BaseType;
|
||||
return type.Type;
|
||||
|
||||
@@ -726,7 +726,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
private void OnSurfaceMouseUp(ref Float2 mouse, MouseButton buttons, ref bool handled)
|
||||
{
|
||||
if (handled || Surface.Context != Context)
|
||||
if (handled)
|
||||
return;
|
||||
|
||||
// Check click over the connection
|
||||
@@ -751,7 +751,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
private void OnSurfaceMouseDoubleClick(ref Float2 mouse, MouseButton buttons, ref bool handled)
|
||||
{
|
||||
if (handled || Surface.Context != Context)
|
||||
if (handled)
|
||||
return;
|
||||
|
||||
// Check double click over the connection
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
@@ -15,7 +18,6 @@ namespace FlaxEditor.Surface
|
||||
class AttributesEditor : ContextMenuBase
|
||||
{
|
||||
private CustomEditorPresenter _presenter;
|
||||
private Proxy _proxy;
|
||||
private byte[] _oldData;
|
||||
|
||||
private class Proxy
|
||||
@@ -70,11 +72,11 @@ namespace FlaxEditor.Surface
|
||||
/// Initializes a new instance of the <see cref="AttributesEditor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The attributes list to edit.</param>
|
||||
/// <param name="attributeTypes">The allowed attribute types to use.</param>
|
||||
public AttributesEditor(Attribute[] attributes, IList<Type> attributeTypes)
|
||||
/// <param name="attributeType">The allowed attribute types to use.</param>
|
||||
public AttributesEditor(Attribute[] attributes, IList<Type> attributeType)
|
||||
{
|
||||
// Context menu dimensions
|
||||
const float width = 375.0f;
|
||||
const float width = 340.0f;
|
||||
const float height = 370.0f;
|
||||
Size = new Float2(width, height);
|
||||
|
||||
@@ -86,68 +88,61 @@ namespace FlaxEditor.Surface
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Ok and Cancel Buttons
|
||||
float buttonsWidth = (width - 12.0f) * 0.5f;
|
||||
// Buttons
|
||||
float buttonsWidth = (width - 16.0f) * 0.5f;
|
||||
float buttonsHeight = 20.0f;
|
||||
var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "Ok",
|
||||
Parent = this
|
||||
};
|
||||
okButton.Clicked += OnOkButtonClicked;
|
||||
var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight)
|
||||
var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "Cancel",
|
||||
Parent = this
|
||||
};
|
||||
cancelButton.Clicked += Hide;
|
||||
var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "OK",
|
||||
Parent = this
|
||||
};
|
||||
okButton.Clicked += OnOkButtonClicked;
|
||||
|
||||
// Actual panel used to display attributes
|
||||
// Actual panel
|
||||
var panel1 = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f),
|
||||
Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f),
|
||||
Parent = this
|
||||
};
|
||||
var editor = new CustomEditorPresenter(null);
|
||||
editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
editor.Panel.IsScrollable = true;
|
||||
editor.Panel.Parent = panel1;
|
||||
editor.Panel.Tag = attributeTypes;
|
||||
editor.Panel.Tag = attributeType;
|
||||
_presenter = editor;
|
||||
|
||||
// Cache 'previous' state to check if attributes were edited after operation
|
||||
_oldData = SurfaceMeta.GetAttributesData(attributes);
|
||||
|
||||
_proxy = new Proxy
|
||||
editor.Select(new Proxy
|
||||
{
|
||||
Value = attributes,
|
||||
};
|
||||
editor.Select(_proxy);
|
||||
|
||||
_presenter.Modified += OnPresenterModified;
|
||||
OnPresenterModified();
|
||||
}
|
||||
|
||||
private void OnPresenterModified()
|
||||
{
|
||||
if (_proxy.Value.Length == 0)
|
||||
{
|
||||
var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center);
|
||||
label.Label.Wrapping = TextWrapping.WrapWords;
|
||||
label.Control.Height = 35f;
|
||||
label.Label.Margin = new Margin(10f);
|
||||
label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnOkButtonClicked()
|
||||
{
|
||||
var newValue = ((Proxy)_presenter.Selection[0]).Value;
|
||||
newValue = newValue.Where(v => v != null).ToArray();
|
||||
for (int i = 0; i < newValue.Length; i++)
|
||||
{
|
||||
if (newValue[i] == null)
|
||||
{
|
||||
MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var newData = SurfaceMeta.GetAttributesData(newValue);
|
||||
if (!_oldData.SequenceEqual(newData))
|
||||
{
|
||||
Edited?.Invoke(newValue);
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
@@ -188,9 +183,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
_presenter = null;
|
||||
_oldData = null;
|
||||
_proxy = null;
|
||||
Edited = null;
|
||||
_presenter.Modified -= OnPresenterModified;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
group.UnlockChildrenRecursive();
|
||||
// TODO: Improve filtering to be based on boxes with the most common things instead of first box
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0 && _selectedBoxes[0] != null)
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null)
|
||||
UpdateFilters();
|
||||
else
|
||||
SortGroups();
|
||||
@@ -424,7 +424,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
OnSearchFilterChanged();
|
||||
}
|
||||
}
|
||||
else if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0)
|
||||
else if (_contextSensitiveSearchEnabled)
|
||||
{
|
||||
// TODO: Filtering could be improved here as well
|
||||
group.EvaluateVisibilityWithBox(_selectedBoxes[0]);
|
||||
@@ -462,7 +462,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
Parent = group
|
||||
};
|
||||
}
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0)
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
group.EvaluateVisibilityWithBox(_selectedBoxes[0]);
|
||||
group.SortChildren();
|
||||
if (ShowExpanded)
|
||||
@@ -476,7 +476,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
if (!isLayoutLocked)
|
||||
{
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count != 0 && _selectedBoxes[0] != null)
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null)
|
||||
UpdateFilters();
|
||||
else
|
||||
SortGroups();
|
||||
|
||||
@@ -214,26 +214,23 @@ namespace FlaxEditor.Surface
|
||||
if (!_isRenaming)
|
||||
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||
|
||||
if (Surface.CanEdit)
|
||||
// Close button
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Color button
|
||||
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if is resizing
|
||||
if (_isResizing)
|
||||
{
|
||||
// Close button
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Color button
|
||||
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if is resizing
|
||||
if (_isResizing)
|
||||
{
|
||||
// Draw overlay
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
|
||||
// Resize button
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
// Draw overlay
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
|
||||
// Resize button
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Selection outline
|
||||
if (_isSelected)
|
||||
{
|
||||
|
||||
@@ -431,6 +431,27 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public bool HasIndependentBoxes => Archetype.IndependentBoxes != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned.
|
||||
/// </summary>
|
||||
public bool HasDependentBoxesSetup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < Archetype.DependentBoxes.Length; i++)
|
||||
{
|
||||
var b = GetBox(Archetype.DependentBoxes[i]);
|
||||
if (b != null && b.CurrentType == b.DefaultType)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<SurfaceNode> UpdateStack = new List<SurfaceNode>();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -400,7 +400,7 @@ namespace FlaxEditor.Surface
|
||||
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
||||
}
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType)
|
||||
@@ -408,7 +408,7 @@ namespace FlaxEditor.Surface
|
||||
if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptTypeDescription(ScriptType type)
|
||||
|
||||
@@ -469,8 +469,7 @@ namespace FlaxEditor.Surface
|
||||
bool handled = base.OnMouseDown(location, button);
|
||||
if (!handled)
|
||||
CustomMouseDown?.Invoke(ref location, button, ref handled);
|
||||
var root = Root;
|
||||
if (handled || root == null)
|
||||
if (handled)
|
||||
{
|
||||
// Clear flags
|
||||
_isMovingSelection = false;
|
||||
@@ -524,11 +523,11 @@ namespace FlaxEditor.Surface
|
||||
if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation))
|
||||
{
|
||||
// Check if user is pressing control
|
||||
if (root.GetKey(KeyboardKeys.Control))
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
AddToSelection(controlUnderMouse);
|
||||
}
|
||||
else if (root.GetKey(KeyboardKeys.Shift))
|
||||
else if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
RemoveFromSelection(controlUnderMouse);
|
||||
}
|
||||
@@ -540,7 +539,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Start moving selected nodes
|
||||
if (!root.GetKey(KeyboardKeys.Shift))
|
||||
if (!Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
StartMouseCapture();
|
||||
_movingSelectionViewPos = _rootControl.Location;
|
||||
@@ -560,7 +559,7 @@ namespace FlaxEditor.Surface
|
||||
// Start selecting or commenting
|
||||
StartMouseCapture();
|
||||
|
||||
if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift))
|
||||
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
@@ -178,31 +178,19 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Update boxes types for nodes that dependant box types based on incoming connections
|
||||
{
|
||||
bool keepUpdating = true;
|
||||
int updatesMin = 2, updatesMax = 100;
|
||||
bool keepUpdating = false;
|
||||
int updateLimit = 100;
|
||||
do
|
||||
{
|
||||
keepUpdating = false;
|
||||
for (int i = 0; i < RootControl.Children.Count; i++)
|
||||
{
|
||||
if (RootControl.Children[i] is SurfaceNode node)
|
||||
if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup)
|
||||
{
|
||||
node.UpdateBoxesTypes();
|
||||
var arch = node.Archetype;
|
||||
if (arch.DependentBoxes != null && arch.IndependentBoxes != null)
|
||||
{
|
||||
foreach (var boxId in arch.DependentBoxes)
|
||||
{
|
||||
var b = node.GetBox(boxId);
|
||||
if (b != null && b.CurrentType == b.DefaultType)
|
||||
{
|
||||
keepUpdating = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
keepUpdating = true;
|
||||
}
|
||||
}
|
||||
} while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0);
|
||||
} while (keepUpdating && updateLimit-- > 0);
|
||||
}
|
||||
|
||||
Loaded?.Invoke(this);
|
||||
|
||||
@@ -74,6 +74,11 @@ struct TextureDataResult
|
||||
PixelFormat Format;
|
||||
Int2 Mip0Size;
|
||||
BytesContainer* Mip0DataPtr;
|
||||
|
||||
TextureDataResult()
|
||||
: Lock(FlaxStorage::LockData::Invalid)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ScreenUtilities.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
|
||||
Delegate<Color32> ScreenUtilities::PickColorDone;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#pragma comment(lib, "Gdi32.lib")
|
||||
|
||||
static HHOOK MouseCallbackHook;
|
||||
|
||||
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
||||
{
|
||||
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
|
||||
{
|
||||
UnhookWindowsHookEx(MouseCallbackHook);
|
||||
|
||||
// Push event with the picked color
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
return 1;
|
||||
}
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
HDC deviceContext = GetDC(NULL);
|
||||
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
|
||||
ReleaseDC(NULL, deviceContext);
|
||||
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
|
||||
if (MouseCallbackHook == NULL)
|
||||
{
|
||||
LOG(Warning, "Failed to set mouse hook.");
|
||||
LOG(Warning, "Error: {0}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
X11::XColor color;
|
||||
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
int defaultScreen = X11::XDefaultScreen(display);
|
||||
|
||||
X11::XImage* image;
|
||||
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
|
||||
color.pixel = XGetPixel(image, 0, 0);
|
||||
X11::XFree(image);
|
||||
|
||||
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
|
||||
|
||||
Color32 outputColor;
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
outputColor.A = 255;
|
||||
return outputColor;
|
||||
}
|
||||
|
||||
void OnScreenUtilsXEventCallback(void* eventPtr)
|
||||
{
|
||||
X11::XEvent* event = (X11::XEvent*) eventPtr;
|
||||
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
if (event->type == ButtonPress)
|
||||
{
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
X11::XUngrabPointer(display, CurrentTime);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
|
||||
|
||||
X11::Cursor cursor = XCreateFontCursor(display, 130);
|
||||
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
|
||||
if (grabbedPointer != GrabSuccess)
|
||||
{
|
||||
LOG(Error, "Failed to grab cursor for events.");
|
||||
X11::XFreeCursor(display, cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
X11::XFreeCursor(display, cursor);
|
||||
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
|
||||
#elif PLATFORM_MAC
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
// TODO: implement ScreenUtilities for macOS
|
||||
return { 0, 0, 0, 255 };
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
// This is what C# calls to start the color picking sequence
|
||||
// This should stop mouse clicks from working for one click, and that click is on the selected color
|
||||
// There is a class called NSColorSample that might implement that for you, but maybe not.
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,32 +2,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
|
||||
/// <summary>
|
||||
/// Platform-dependent screen utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pixel color at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="pos">Screen-space coordinate to read.</param>
|
||||
/// <returns>Pixel color at the specified coordinates.</returns>
|
||||
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
|
||||
|
||||
/// <summary>
|
||||
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PickColor();
|
||||
|
||||
/// <summary>
|
||||
/// Called when PickColor action is finished.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<Color32> PickColorDone;
|
||||
};
|
||||
#include "Engine/Platform/ScreenUtilities.h"
|
||||
|
||||
@@ -1583,5 +1583,31 @@ namespace FlaxEditor.Utilities
|
||||
c = c.Parent;
|
||||
return c as ISceneEditingContext;
|
||||
}
|
||||
|
||||
internal static bool UseCustomWindowDecorations(bool isMainWindow = false)
|
||||
{
|
||||
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
|
||||
{
|
||||
Options.InterfaceOptions.WindowDecorationsType.Auto => !Platform.SupportsNativeDecorations,
|
||||
Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !Platform.SupportsNativeDecorations : true,
|
||||
Options.InterfaceOptions.WindowDecorationsType.Native => false,
|
||||
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
internal static bool HideSingleTabWindowTabBars()
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
// We should not hide the tab bars if tab handle is the only way to dock the window
|
||||
bool clientSideDecorations = UseCustomWindowDecorations(false);
|
||||
bool draggableDecorations = clientSideDecorations || Platform.SupportsNativeDecorationDragging;
|
||||
return draggableDecorations && Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#elif PLATFORM_WINDOWS
|
||||
return Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
@@ -158,18 +159,22 @@ namespace FlaxEditor.Viewport
|
||||
private float _movementSpeed;
|
||||
private float _minMovementSpeed;
|
||||
private float _maxMovementSpeed;
|
||||
#if !PLATFORM_SDL
|
||||
private float _mouseAccelerationScale;
|
||||
private bool _useMouseFiltering;
|
||||
private bool _useMouseAcceleration;
|
||||
#endif
|
||||
|
||||
// Input
|
||||
|
||||
internal bool _disableInputUpdate;
|
||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2 _startPos;
|
||||
#if !PLATFORM_SDL
|
||||
private Float2 _mouseDeltaLast;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The previous input (from the previous update).
|
||||
@@ -522,10 +527,11 @@ namespace FlaxEditor.Viewport
|
||||
: base(task)
|
||||
{
|
||||
_editor = Editor.Instance;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
_mouseAccelerationScale = 0.1f;
|
||||
_useMouseFiltering = false;
|
||||
_useMouseAcceleration = false;
|
||||
#endif
|
||||
_camera = camera;
|
||||
if (_camera != null)
|
||||
_camera.Viewport = this;
|
||||
@@ -1525,7 +1531,9 @@ namespace FlaxEditor.Viewport
|
||||
// Hide cursor and start tracking mouse movement
|
||||
win.StartTrackingMouse(false);
|
||||
win.Cursor = CursorType.Hidden;
|
||||
win.MouseMoveRelative += OnMouseMoveRelative;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Center mouse position if it's too close to the edge
|
||||
var size = Size;
|
||||
var center = Float2.Round(size * 0.5f);
|
||||
@@ -1534,6 +1542,7 @@ namespace FlaxEditor.Viewport
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1545,6 +1554,7 @@ namespace FlaxEditor.Viewport
|
||||
// Restore cursor and stop tracking mouse movement
|
||||
win.Cursor = CursorType.Default;
|
||||
win.EndTrackingMouse();
|
||||
win.MouseMoveRelative -= OnMouseMoveRelative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1626,18 +1636,15 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Get parent window
|
||||
var win = (WindowRootControl)Root;
|
||||
|
||||
// Get current mouse position in the view
|
||||
if (win.IsFocused)
|
||||
{
|
||||
// When the window is not focused, the position in window does not return sane values
|
||||
Float2 pos = PointFromWindow(win.MousePosition);
|
||||
if (!float.IsInfinity(pos.LengthSquared))
|
||||
_viewMousePos = pos;
|
||||
// Get current mouse position in the view
|
||||
_viewMousePos = PointFromWindow(win.MousePosition);
|
||||
}
|
||||
|
||||
// Update input
|
||||
var window = win.Window;
|
||||
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow;
|
||||
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow && !WindowDragHelper.IsDragActive;
|
||||
{
|
||||
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
|
||||
var isViewportControllingMouse = canUseInput && IsControllingMouse;
|
||||
@@ -1649,9 +1656,17 @@ namespace FlaxEditor.Viewport
|
||||
else
|
||||
EndMouseCapture();
|
||||
}
|
||||
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
||||
|
||||
_prevInput = _input;
|
||||
#if PLATFORM_SDL
|
||||
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos) || _prevInput.IsControllingMouse;
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
if (_prevInput.IsControllingMouse)
|
||||
hit = null;
|
||||
#else
|
||||
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos);
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
#endif
|
||||
if (canUseInput && ContainsFocus && hit == null)
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
@@ -1764,6 +1779,10 @@ namespace FlaxEditor.Viewport
|
||||
if (_input.IsControlDown)
|
||||
moveDelta *= 0.3f;
|
||||
|
||||
#if PLATFORM_SDL
|
||||
var mouseDelta = _mouseDelta;
|
||||
_mouseDelta = Float2.Zero;
|
||||
#else
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
var offset = _viewMousePos - _startPos;
|
||||
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
|
||||
@@ -1805,6 +1824,7 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
@@ -1813,12 +1833,14 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta *= new Float2(1, -1);
|
||||
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Move mouse back to the root position
|
||||
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
|
||||
{
|
||||
var center = PointToWindow(_startPos);
|
||||
win.MousePosition = center;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Change Ortho size on mouse scroll
|
||||
if (_isOrtho && !rmbWheel)
|
||||
@@ -1830,6 +1852,8 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
#else
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
@@ -1844,6 +1868,7 @@ namespace FlaxEditor.Viewport
|
||||
_mouseDelta = Float2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Float2.Zero;
|
||||
#endif
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1893,6 +1918,12 @@ namespace FlaxEditor.Viewport
|
||||
_input.MouseWheelDelta = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnMouseMoveRelative(ref Float2 mouseMotion)
|
||||
{
|
||||
_mouseDelta += mouseMotion;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
@@ -1958,7 +1989,8 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
if (_isControllingMouse)
|
||||
{
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
if (RootWindow?.Window != null)
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
_isControllingMouse = false;
|
||||
_isVirtualMouseRightDown = false;
|
||||
}
|
||||
|
||||
@@ -611,16 +611,25 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
|
||||
bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
|
||||
Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse;
|
||||
Gizmos?.Active is TransformGizmo;
|
||||
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnControlMouseBegin(Window win)
|
||||
{
|
||||
_rubberBandSelector.ReleaseRubberBandSelection();
|
||||
|
||||
base.OnControlMouseBegin(win);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLeftMouseButtonDown()
|
||||
{
|
||||
base.OnLeftMouseButtonDown();
|
||||
|
||||
_rubberBandSelector.TryStartingRubberBandSelection();
|
||||
if (!IsAltKeyDown)
|
||||
_rubberBandSelector.TryStartingRubberBandSelection();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Viewport
|
||||
private PrefabUIEditorRoot _uiRoot;
|
||||
private bool _showUI = false;
|
||||
|
||||
private int _defaultScaleActiveIndex = -1;
|
||||
private int _defaultScaleActiveIndex = 0;
|
||||
private int _customScaleActiveIndex = -1;
|
||||
private ContextMenuButton _uiModeButton;
|
||||
private ContextMenuChildMenu _uiViewOptions;
|
||||
@@ -257,23 +257,16 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public void SaveActiveUIScalingOption()
|
||||
{
|
||||
if (!Prefab)
|
||||
return;
|
||||
var id = Prefab.ID;
|
||||
var defaultKey = $"{id}:DefaultViewportScalingIndex";
|
||||
var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex";
|
||||
Editor.Instance.ProjectCache.SetCustomData(defaultKey, _defaultScaleActiveIndex.ToString());
|
||||
var customKey = $"{id}:CustomViewportScalingIndex";
|
||||
var customKey = $"{Prefab.ID}:CustomViewportScalingIndex";
|
||||
Editor.Instance.ProjectCache.SetCustomData(customKey, _customScaleActiveIndex.ToString());
|
||||
}
|
||||
|
||||
private void LoadCustomUIScalingOption()
|
||||
{
|
||||
if (!Prefab)
|
||||
return;
|
||||
var id = Prefab.ID;
|
||||
Prefab.WaitForLoaded();
|
||||
|
||||
var defaultKey = $"{id}:DefaultViewportScalingIndex";
|
||||
var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex";
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData(defaultKey, out string defaultData))
|
||||
{
|
||||
if (int.TryParse(defaultData, out var index))
|
||||
@@ -293,7 +286,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
var customKey = $"{id}:CustomViewportScalingIndex";
|
||||
var customKey = $"{Prefab.ID}:CustomViewportScalingIndex";
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data))
|
||||
{
|
||||
if (int.TryParse(data, out var index))
|
||||
|
||||
@@ -130,6 +130,9 @@ namespace FlaxEditor.Windows
|
||||
"Mono Project - www.mono-project.com",
|
||||
#if USE_NETCORE
|
||||
".NET - www.dotnet.microsoft.com",
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
"Simple DirectMedia Layer - www.libsdl.org",
|
||||
#endif
|
||||
"FreeType Project - www.freetype.org",
|
||||
"Assimp - www.assimp.sourceforge.net",
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
|
||||
private string _cacheFolder;
|
||||
private AssetItem _item;
|
||||
private Guid _assetId;
|
||||
private Surface _surface;
|
||||
private Label _loadingLabel;
|
||||
private CancellationTokenSource _token;
|
||||
@@ -163,13 +163,13 @@ namespace FlaxEditor.Windows
|
||||
public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem)
|
||||
: base(editor, false, ScrollBars.None)
|
||||
{
|
||||
_item = assetItem;
|
||||
Title = _item.ShortName + " References";
|
||||
Title = assetItem.ShortName + " References";
|
||||
|
||||
_tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder));
|
||||
_cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References");
|
||||
if (!Directory.Exists(_cacheFolder))
|
||||
Directory.CreateDirectory(_cacheFolder);
|
||||
_assetId = assetItem.ID;
|
||||
_surface = new Surface(this)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
@@ -194,7 +194,6 @@ namespace FlaxEditor.Windows
|
||||
_nodesAssets.Add(assetId);
|
||||
var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId);
|
||||
_nodes.Add(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -393,7 +392,8 @@ namespace FlaxEditor.Windows
|
||||
_nodesAssets = new HashSet<Guid>();
|
||||
var searchLevel = 4; // TODO: make it as an option (somewhere in window UI)
|
||||
// TODO: add option to filter assets by type (eg. show only textures as leaf nodes)
|
||||
var assetNode = SpawnNode(_item.ID);
|
||||
var assetNode = SpawnNode(_assetId);
|
||||
// TODO: add some outline or tint color to the main node
|
||||
BuildGraph(assetNode, searchLevel, false);
|
||||
ArrangeGraph(assetNode, false);
|
||||
BuildGraph(assetNode, searchLevel, true);
|
||||
@@ -402,10 +402,6 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
_progress = 100.0f;
|
||||
|
||||
var commentRect = assetNode.EditorBounds;
|
||||
commentRect.Expand(80f);
|
||||
_surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green);
|
||||
|
||||
// Update UI
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ using FlaxEditor.Surface;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
@@ -429,7 +430,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var p = parameters[i];
|
||||
if (p.IsOverride && p.IsPublic)
|
||||
if (p.IsOverride)
|
||||
{
|
||||
p.IsOverride = false;
|
||||
actions.Add(new EditParamOverrideAction
|
||||
|
||||
@@ -90,15 +90,25 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
|
||||
gpu.CheckBox.Checked = sdfOptions.GPU;
|
||||
gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; };
|
||||
|
||||
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.");
|
||||
var backfacesThreshold = backfacesThresholdProp.FloatValue();
|
||||
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
|
||||
backfacesThreshold.ValueBox.MinValue = 0.001f;
|
||||
backfacesThreshold.ValueBox.MaxValue = 1.0f;
|
||||
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
|
||||
backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; };
|
||||
|
||||
// Toggle Backfaces Threshold visibility (CPU-only option)
|
||||
gpu.CheckBox.StateChanged += c =>
|
||||
{
|
||||
Window._sdfOptions.GPU = c.Checked;
|
||||
backfacesThresholdLabel.Visible = !c.Checked;
|
||||
backfacesThreshold.ValueBox.Visible = !c.Checked;
|
||||
};
|
||||
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
|
||||
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
|
||||
|
||||
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
|
||||
lodIndex.IntValue.MinValue = 0;
|
||||
lodIndex.IntValue.MaxValue = Asset.LODsCount - 1;
|
||||
|
||||
@@ -142,7 +142,6 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Title = "Content";
|
||||
Icon = editor.Icons.Folder32;
|
||||
var style = Style.Current;
|
||||
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
|
||||
@@ -165,8 +164,6 @@ namespace FlaxEditor.Windows
|
||||
_navigationBar = new NavigationBar
|
||||
{
|
||||
Parent = _toolStrip,
|
||||
ScrollbarTrackColor = style.Background,
|
||||
ScrollbarThumbColor = style.ForegroundGrey,
|
||||
};
|
||||
|
||||
// Split panel
|
||||
@@ -182,7 +179,7 @@ namespace FlaxEditor.Windows
|
||||
var headerPanel = new ContainerControl
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
BackgroundColor = style.Background,
|
||||
BackgroundColor = Style.Current.Background,
|
||||
IsScrollable = false,
|
||||
Offsets = new Margin(0, 0, 0, 18 + 6),
|
||||
};
|
||||
|
||||
@@ -10,117 +10,17 @@ using FlaxEditor.Modules;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
namespace FlaxEditor.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Render output control with content scaling support.
|
||||
/// </summary>
|
||||
public class ScaledRenderOutputControl : RenderOutputControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom scale.
|
||||
/// </summary>
|
||||
public float ContentScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Actual bounds size for content (incl. scale).
|
||||
/// </summary>
|
||||
public Float2 ContentSize => Size / ContentScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScaledRenderOutputControl(SceneRenderTask task)
|
||||
: base(task)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
DrawSelf();
|
||||
|
||||
// Draw children with scale
|
||||
var scaling = new Float3(ContentScale, ContentScale, 1);
|
||||
Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
|
||||
Render2D.PushTransform(scale);
|
||||
if (ClipChildren)
|
||||
{
|
||||
GetDesireClientArea(out var clientArea);
|
||||
Render2D.PushClip(ref clientArea);
|
||||
DrawChildren();
|
||||
Render2D.PopClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChildren();
|
||||
}
|
||||
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDesireClientArea(out Rectangle rect)
|
||||
{
|
||||
// Scale the area for the client controls
|
||||
rect = new Rectangle(Float2.Zero, Size / ContentScale);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
|
||||
{
|
||||
// Skip local PointFromParent but use base code
|
||||
location = base.PointFromParent(ref locationParent);
|
||||
return ContainsPoint(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
|
||||
{
|
||||
location /= ContentScale;
|
||||
return base.IntersectsChildContent(child, location, out childSpaceLocation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||
{
|
||||
if (precise) // Ignore as utility-only element
|
||||
return false;
|
||||
return base.ContainsPoint(ref location, precise);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCast(ref Float2 location, out Control hit)
|
||||
{
|
||||
var p = location / ContentScale;
|
||||
if (RayCastChildren(ref p, out hit))
|
||||
return true;
|
||||
return base.RayCast(ref location, out hit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointToParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointToParent(ref location);
|
||||
result *= ContentScale;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointFromParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointFromParent(ref location);
|
||||
result /= ContentScale;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides in-editor play mode simulation.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Windows.EditorWindow" />
|
||||
public class GameWindow : EditorWindow
|
||||
{
|
||||
private readonly ScaledRenderOutputControl _viewport;
|
||||
private readonly RenderOutputControl _viewport;
|
||||
private readonly GameRoot _guiRoot;
|
||||
private bool _showGUI = true, _editGUI = true;
|
||||
private bool _showDebugDraw = false;
|
||||
@@ -177,7 +77,7 @@ namespace FlaxEditor.Windows
|
||||
/// <summary>
|
||||
/// Gets the viewport.
|
||||
/// </summary>
|
||||
public ScaledRenderOutputControl Viewport => _viewport;
|
||||
public RenderOutputControl Viewport => _viewport;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show game GUI in the view or keep it hidden.
|
||||
@@ -395,7 +295,7 @@ namespace FlaxEditor.Windows
|
||||
var task = MainRenderTask.Instance;
|
||||
|
||||
// Setup viewport
|
||||
_viewport = new ScaledRenderOutputControl(task)
|
||||
_viewport = new RenderOutputControl(task)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
@@ -496,8 +396,11 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
if (v.Size.Y <= 0 || v.Size.X <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1))
|
||||
{
|
||||
@@ -545,7 +448,15 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private void ResizeViewport()
|
||||
{
|
||||
_windowAspectRatio = _freeAspect ? 1 : Width / Height;
|
||||
if (!_freeAspect)
|
||||
{
|
||||
_windowAspectRatio = Width / Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
_windowAspectRatio = 1;
|
||||
}
|
||||
|
||||
var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
|
||||
var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
|
||||
|
||||
@@ -557,24 +468,6 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
|
||||
}
|
||||
|
||||
if (_viewport.KeepAspectRatio)
|
||||
{
|
||||
var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size;
|
||||
if (scaleHeight < 1)
|
||||
{
|
||||
_viewport.ContentScale = _viewport.Width / resolution.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewport.ContentScale = _viewport.Height / resolution.Y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewport.ContentScale = 1;
|
||||
}
|
||||
|
||||
_viewport.SyncBackbufferSize();
|
||||
PerformLayout();
|
||||
}
|
||||
@@ -874,7 +767,7 @@ namespace FlaxEditor.Windows
|
||||
_cursorVisible = Screen.CursorVisible;
|
||||
_cursorLockMode = Screen.CursorLock;
|
||||
Screen.CursorVisible = true;
|
||||
if (Screen.CursorLock == CursorLockMode.Clipped)
|
||||
if (Screen.CursorLock == CursorLockMode.Clipped || Screen.CursorLock == CursorLockMode.Locked)
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
|
||||
// Defocus
|
||||
@@ -963,8 +856,11 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
|
||||
{
|
||||
// Make sure the cursor is always in the viewport when cursor is locked
|
||||
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
|
||||
|
||||
// Center mouse in play mode
|
||||
if (CenterMouseOnFocus)
|
||||
if (CenterMouseOnFocus || forceCenter)
|
||||
{
|
||||
var center = PointToWindow(Size * 0.5f);
|
||||
Root.MousePosition = center;
|
||||
@@ -989,15 +885,16 @@ namespace FlaxEditor.Windows
|
||||
_cursorVisible = Screen.CursorVisible;
|
||||
_cursorLockMode = Screen.CursorLock;
|
||||
|
||||
// Restore cursor visibility (could be hidden by the game)
|
||||
// Restore cursor state, could be hidden or locked by the game
|
||||
if (!_cursorVisible)
|
||||
Screen.CursorVisible = true;
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
}
|
||||
|
||||
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
|
||||
{
|
||||
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
|
||||
}
|
||||
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
|
||||
{
|
||||
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,7 +911,6 @@ namespace FlaxEditor.Windows
|
||||
return child.OnNavigate(direction, Float2.Zero, this, visited);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1065,7 +961,7 @@ namespace FlaxEditor.Windows
|
||||
else
|
||||
_defaultScaleActiveIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
if (_customScaleActiveIndex != -1)
|
||||
{
|
||||
var options = Editor.UI.CustomViewportScaleOptions;
|
||||
|
||||
@@ -296,15 +296,13 @@ namespace FlaxEditor.Windows.Profiler
|
||||
var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex);
|
||||
if (resources == null || resources.Length == 0)
|
||||
return;
|
||||
var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0);
|
||||
var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage);
|
||||
|
||||
// Add rows
|
||||
var rowColor2 = Style.Current.Background * 1.4f;
|
||||
int rowIndex = 0;
|
||||
foreach (var e in resourcesOrdered)
|
||||
{
|
||||
if (e == null)
|
||||
continue;
|
||||
ClickableRow row;
|
||||
if (_tableRowsCache.Count != 0)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace FlaxEditor.Windows
|
||||
private Tree _tree;
|
||||
private Panel _sceneTreePanel;
|
||||
private bool _isUpdatingSelection;
|
||||
private bool _isMouseDown;
|
||||
private bool _blockSceneTreeScroll = false;
|
||||
|
||||
private DragAssets _dragAssets;
|
||||
@@ -319,22 +318,22 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (assetItem.IsOfType<SceneAsset>())
|
||||
return true;
|
||||
return assetItem.OnEditorDrag(this) && Level.IsAnySceneLoaded;
|
||||
return assetItem.OnEditorDrag(this);
|
||||
}
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType) && Level.IsAnySceneLoaded;
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType) && Level.IsAnySceneLoaded;
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null && Level.IsAnySceneLoaded;
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -374,10 +373,7 @@ namespace FlaxEditor.Windows
|
||||
return true;
|
||||
|
||||
if (buttons == MouseButton.Right)
|
||||
{
|
||||
_isMouseDown = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -388,10 +384,8 @@ namespace FlaxEditor.Windows
|
||||
if (base.OnMouseUp(location, buttons))
|
||||
return true;
|
||||
|
||||
if (_isMouseDown && buttons == MouseButton.Right)
|
||||
if (buttons == MouseButton.Right)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
if (Editor.StateMachine.CurrentState.CanEditScene)
|
||||
{
|
||||
// Show context menu
|
||||
@@ -416,14 +410,6 @@ namespace FlaxEditor.Windows
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
base.OnLostFocus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
@@ -490,7 +476,6 @@ namespace FlaxEditor.Windows
|
||||
if (result == DragDropEffect.None)
|
||||
{
|
||||
_isDropping = true;
|
||||
|
||||
// Drag assets
|
||||
if (_dragAssets != null && _dragAssets.HasValidDrag)
|
||||
{
|
||||
@@ -505,7 +490,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
Level.SpawnActor(actor);
|
||||
var graphNode = Editor.Scene.GetActorNode(actor.ID);
|
||||
if (graphNode != null)
|
||||
graphNodes.Add(graphNode);
|
||||
@@ -528,7 +513,7 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
Level.SpawnActor(actor);
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
@@ -550,7 +535,7 @@ namespace FlaxEditor.Windows
|
||||
Control = control,
|
||||
Name = item.Name,
|
||||
};
|
||||
Editor.SceneEditing.Spawn(uiControl);
|
||||
Level.SpawnActor(uiControl);
|
||||
Editor.Scene.MarkSceneEdited(uiControl.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
@@ -572,7 +557,7 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
}
|
||||
actor.Name = actorType.Name;
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
Level.SpawnActor(actor);
|
||||
var graphNode = Editor.Scene.GetActorNode(actor.ID);
|
||||
if (graphNode != null)
|
||||
graphNodes.Add(graphNode);
|
||||
|
||||
@@ -29,7 +29,7 @@ const Char* SplashScreenQuotes[] =
|
||||
#elif PLATFORM_LINUX
|
||||
TEXT("Try it on a Raspberry"),
|
||||
TEXT("Trying to exit vim"),
|
||||
TEXT("Sudo flax --loadproject"),
|
||||
TEXT("sudo flax --project HelloWorld.flaxproj"),
|
||||
#elif PLATFORM_MAC
|
||||
TEXT("don't compare Macbooks to oranges."),
|
||||
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
|
||||
@@ -105,6 +105,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("You have my bow.\nAnd my axe!"),
|
||||
TEXT("To the bridge of Khazad-dum."),
|
||||
TEXT("One ring to rule them all.\nOne ring to find them."),
|
||||
TEXT("Where there's a whip, there's a way."),
|
||||
TEXT("That's what she said"),
|
||||
TEXT("We could be compiling shaders here"),
|
||||
TEXT("Hello There"),
|
||||
@@ -137,7 +138,6 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Good Luck Have Fun"),
|
||||
TEXT("GG Well Played"),
|
||||
TEXT("Now with documentation."),
|
||||
TEXT("We do this not because it is easy,\nbut because we thought it would be easy"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
@@ -166,7 +166,7 @@ void SplashScreen::Show()
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowDragAndDrop = false;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.Type = WindowType::Utility;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
settings.StartPosition = WindowStartPosition::CenterScreen;
|
||||
|
||||
@@ -246,19 +246,11 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
|
||||
const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f;
|
||||
#define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type })
|
||||
if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration
|
||||
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration())
|
||||
&& Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
|
||||
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
|
||||
// Handle the edge case of an event on 0 or on max animation duration during looping
|
||||
|| (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration())
|
||||
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|
||||
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|
||||
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f)
|
||||
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f)
|
||||
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|
||||
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|
||||
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f)
|
||||
)
|
||||
{
|
||||
int32 stateIndex = -1;
|
||||
@@ -2441,14 +2433,10 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
if (bucket.LoopsLeft == 0)
|
||||
{
|
||||
// End playing animation and reset bucket params
|
||||
// End playing animation
|
||||
value = tryGetValue(node->GetBox(1), Value::Null);
|
||||
bucket.Index = -1;
|
||||
slot.Animation = nullptr;
|
||||
bucket.TimePosition = 0.0f;
|
||||
bucket.BlendInPosition = 0.0f;
|
||||
bucket.BlendOutPosition = 0.0f;
|
||||
bucket.LoopsDone = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2557,15 +2545,9 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
|
||||
if (context.GraphStack.Count() < 2)
|
||||
{
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
AnimGraphNode* functionCallNode = nullptr;
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = context.CallStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -666,7 +666,7 @@ void Asset::onLoaded()
|
||||
{
|
||||
onLoaded_MainThread();
|
||||
}
|
||||
else if (OnLoaded.IsBinded() || _references.HasItems())
|
||||
else if (OnLoaded.IsBinded())
|
||||
{
|
||||
Function<void()> action;
|
||||
action.Bind<Asset, &Asset::onLoaded>(this);
|
||||
|
||||
@@ -218,14 +218,10 @@ Asset::LoadResult MaterialInstance::load()
|
||||
Guid baseMaterialId;
|
||||
headerStream.Read(baseMaterialId);
|
||||
auto baseMaterial = Content::LoadAsync<MaterialBase>(baseMaterialId);
|
||||
if (baseMaterial)
|
||||
baseMaterial->AddReference();
|
||||
|
||||
// Load parameters
|
||||
if (Params.Load(&headerStream))
|
||||
{
|
||||
if (baseMaterial)
|
||||
baseMaterial->RemoveReference();
|
||||
LOG(Warning, "Cannot load material parameters.");
|
||||
return LoadResult::CannotLoadData;
|
||||
}
|
||||
@@ -243,8 +239,6 @@ Asset::LoadResult MaterialInstance::load()
|
||||
ParamsChanged();
|
||||
}
|
||||
|
||||
if (baseMaterial)
|
||||
baseMaterial->RemoveReference();
|
||||
return LoadResult::Ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,6 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f
|
||||
LOG(Warning, "Cannot generate SDF for virtual models on a main thread.");
|
||||
return true;
|
||||
}
|
||||
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
|
||||
lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1);
|
||||
|
||||
// Generate SDF
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
model->GetLODData(_lodIndex, data);
|
||||
if (data.IsInvalid())
|
||||
{
|
||||
LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString());
|
||||
LOG(Warning, "Missing data chunk");
|
||||
return true;
|
||||
}
|
||||
MemoryReadStream stream(data.Get(), data.Length());
|
||||
@@ -234,7 +234,6 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
|
||||
return true;
|
||||
}
|
||||
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Use a temporary chunks for data storage for virtual assets
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
/// <param name="id">The asset id.</param>
|
||||
/// <returns>Loaded asset of null.</returns>
|
||||
template<typename T>
|
||||
T* Load(const Guid& id)
|
||||
T* LoadAsync(const Guid& id)
|
||||
{
|
||||
for (auto& e : *this)
|
||||
{
|
||||
@@ -26,10 +26,8 @@ public:
|
||||
return (T*)e.Get();
|
||||
}
|
||||
auto asset = (T*)::LoadAsset(id, T::TypeInitializer);
|
||||
if (asset && !asset->WaitForLoaded())
|
||||
if (asset)
|
||||
Add(asset);
|
||||
else
|
||||
asset = nullptr;
|
||||
return asset;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
@@ -347,21 +346,17 @@ int32 LoadingThread::Run()
|
||||
ContentLoadTask* task;
|
||||
ThisLoadThread = this;
|
||||
|
||||
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
|
||||
while (Platform::AtomicRead(&_exitFlag) == 0)
|
||||
{
|
||||
if (LoadTasks.try_dequeue(task))
|
||||
{
|
||||
Run(task);
|
||||
MONO_THREAD_INFO_GET(monoThreadInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
|
||||
LoadTasksMutex.Lock();
|
||||
LoadTasksSignal.Wait(LoadTasksMutex);
|
||||
LoadTasksMutex.Unlock();
|
||||
MONO_EXIT_GC_SAFE_WITH_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,7 +700,6 @@ Asset* Content::GetAsset(const StringView& outputPath)
|
||||
{
|
||||
if (outputPath.IsEmpty())
|
||||
return nullptr;
|
||||
PROFILE_CPU();
|
||||
ScopeLock lock(AssetsLocker);
|
||||
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
|
||||
@@ -87,10 +87,6 @@ public:
|
||||
/// </summary>
|
||||
double LastAccessTime = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads).
|
||||
/// </summary>
|
||||
int64 IsLoading = 0;
|
||||
/// <summary>
|
||||
/// The chunk data.
|
||||
/// </summary>
|
||||
@@ -150,7 +146,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsLoaded() const
|
||||
{
|
||||
return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0;
|
||||
return Data.IsValid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -158,7 +154,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsMissing() const
|
||||
{
|
||||
return !IsLoaded();
|
||||
return Data.IsInvalid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "FlaxPackage.h"
|
||||
#include "ContentStorageManager.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/ScopeExit.h"
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
@@ -75,6 +74,8 @@ FlaxChunk* FlaxChunk::Clone() const
|
||||
|
||||
const int32 FlaxStorage::MagicCode = 1180124739;
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
|
||||
|
||||
struct Header
|
||||
{
|
||||
int32 MagicCode;
|
||||
@@ -245,7 +246,6 @@ FlaxStorage::~FlaxStorage()
|
||||
ASSERT(IsDisposed());
|
||||
CHECK(_chunksLock == 0);
|
||||
CHECK(_refCount == 0);
|
||||
CHECK(_isUnloadingData == 0);
|
||||
ASSERT(_chunks.IsEmpty());
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -261,22 +261,6 @@ FlaxStorage::~FlaxStorage()
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlaxStorage::LockChunks()
|
||||
{
|
||||
RETRY:
|
||||
Platform::InterlockedIncrement(&_chunksLock);
|
||||
if (Platform::AtomicRead(&_isUnloadingData) != 0)
|
||||
{
|
||||
// Someone else is closing file handles or freeing chunks so wait for it to finish and retry
|
||||
Platform::InterlockedDecrement(&_chunksLock);
|
||||
do
|
||||
{
|
||||
Platform::Sleep(1);
|
||||
} while (Platform::AtomicRead(&_isUnloadingData) != 0);
|
||||
goto RETRY;
|
||||
}
|
||||
}
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockSafe()
|
||||
{
|
||||
auto lock = LockData(this);
|
||||
@@ -705,6 +689,7 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load header
|
||||
return LoadAssetHeader(e, data);
|
||||
}
|
||||
|
||||
@@ -714,10 +699,7 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
ASSERT(IsLoaded());
|
||||
ASSERT(chunk != nullptr && _chunks.Contains(chunk));
|
||||
|
||||
// Protect against loading the same chunk from multiple threads at once
|
||||
while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0)
|
||||
Platform::Sleep(1);
|
||||
SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); };
|
||||
// Check if already loaded
|
||||
if (chunk->IsLoaded())
|
||||
return false;
|
||||
|
||||
@@ -794,10 +776,12 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
// Raw data
|
||||
chunk->Data.Read(stream, size);
|
||||
}
|
||||
ASSERT(chunk->IsLoaded());
|
||||
chunk->RegisterUsage();
|
||||
}
|
||||
|
||||
UnlockChunks();
|
||||
|
||||
return failed;
|
||||
}
|
||||
|
||||
@@ -1436,12 +1420,10 @@ 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
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(ContentFiles);
|
||||
|
||||
@@ -1514,21 +1496,9 @@ void FlaxStorage::Tick(double time)
|
||||
{
|
||||
auto chunk = _chunks.Get()[i];
|
||||
const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime;
|
||||
if (!wasUsed &&
|
||||
chunk->IsLoaded() &&
|
||||
EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) &&
|
||||
Platform::AtomicRead(&chunk->IsLoading) == 0)
|
||||
if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory))
|
||||
{
|
||||
// Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0)
|
||||
{
|
||||
// Someone started loading so skip ticking
|
||||
Platform::InterlockedDecrement(&_isUnloadingData);
|
||||
return;
|
||||
}
|
||||
chunk->Unload();
|
||||
Platform::InterlockedDecrement(&_isUnloadingData);
|
||||
}
|
||||
wasAnyUsed |= wasUsed;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,6 @@ protected:
|
||||
int64 _refCount = 0;
|
||||
int64 _chunksLock = 0;
|
||||
int64 _files = 0;
|
||||
int64 _isUnloadingData = 0;
|
||||
double _lastRefLostTime;
|
||||
CriticalSection _loadLocker;
|
||||
|
||||
@@ -130,7 +129,10 @@ public:
|
||||
/// <summary>
|
||||
/// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked.
|
||||
/// </summary>
|
||||
void LockChunks();
|
||||
FORCE_INLINE void LockChunks()
|
||||
{
|
||||
Platform::InterlockedIncrement(&_chunksLock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks the storage chunks data.
|
||||
@@ -146,6 +148,7 @@ public:
|
||||
struct LockData
|
||||
{
|
||||
friend FlaxStorage;
|
||||
static LockData Invalid;
|
||||
|
||||
private:
|
||||
FlaxStorage* _storage;
|
||||
@@ -158,11 +161,6 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
LockData()
|
||||
: _storage(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
LockData(const LockData& other)
|
||||
: _storage(other._storage)
|
||||
{
|
||||
|
||||
@@ -658,10 +658,7 @@ public:
|
||||
--_count;
|
||||
T* data = _allocation.Get();
|
||||
if (index < _count)
|
||||
{
|
||||
for (int32 i = index; i < _count; i++)
|
||||
data[i] = MoveTemp(data[i + 1]);
|
||||
}
|
||||
Memory::MoveAssignItems(data + index, data + (index + 1), _count - index);
|
||||
Memory::DestructItems(data + _count, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -409,36 +409,27 @@ protected:
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
const int32 elementsCount = _elementsCount;
|
||||
const int32 oldSize = _size;
|
||||
AllocationData oldAllocation;
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, oldSize, oldSize);
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, _size, _size);
|
||||
_allocation.Allocate(_size);
|
||||
BucketType* data = _allocation.Get();
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
data[i]._state = HashSetBucketState::Empty;
|
||||
BucketType* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
{
|
||||
BucketType& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
if (pos.FreeSlotIndex == -1)
|
||||
{
|
||||
// Grow and retry to handle pathological cases (eg. heavy collisions)
|
||||
EnsureCapacity(_size + 1, true);
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
}
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket = MoveTemp(oldBucket);
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
oldData[i].Free();
|
||||
_elementsCount = elementsCount;
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -20,7 +19,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
|
||||
_Pragma("clang diagnostic push") \
|
||||
@@ -38,9 +37,8 @@
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -54,7 +52,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
@@ -69,7 +67,6 @@
|
||||
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#define DLLIMPORT __declspec(dllimport)
|
||||
#define USED
|
||||
#define THREADLOCAL __declspec(thread)
|
||||
#define STDCALL __stdcall
|
||||
#define CDECL __cdecl
|
||||
|
||||
@@ -620,9 +620,14 @@ bool Collision::RayIntersectsTriangle(const Ray& ray, const Vector3& a, const Ve
|
||||
Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z;
|
||||
rayDistance *= inverseDeterminant;
|
||||
|
||||
// Check if the triangle is in front the ray origin
|
||||
// Check if the triangle is behind the ray origin
|
||||
if (rayDistance < 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
distance = rayDistance;
|
||||
return rayDistance >= 0.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal)
|
||||
|
||||
@@ -12,6 +12,11 @@ String Ray::ToString() const
|
||||
return String::Format(TEXT("{}"), *this);
|
||||
}
|
||||
|
||||
Vector3 Ray::GetPoint(Real distance) const
|
||||
{
|
||||
return Position + Direction * distance;
|
||||
}
|
||||
|
||||
Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp)
|
||||
{
|
||||
Vector3 nearPoint(x, y, 0.0f);
|
||||
|
||||
@@ -79,10 +79,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="distance">The distance from ray origin.</param>
|
||||
/// <returns>The calculated point.</returns>
|
||||
FORCE_INLINE Vector3 GetPoint(Real distance) const
|
||||
{
|
||||
return Position + Direction * distance;
|
||||
}
|
||||
Vector3 GetPoint(Real distance) const;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between ray and a point.
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace AllocationUtils
|
||||
capacity |= capacity >> 8;
|
||||
capacity |= capacity >> 16;
|
||||
uint64 capacity64 = (uint64)(capacity + 1) * 2;
|
||||
return capacity64 >= MAX_int32 ? MAX_int32 : (int32)capacity64 / 2;
|
||||
if (capacity64 > MAX_int32)
|
||||
capacity64 = MAX_int32;
|
||||
return (int32)capacity64;
|
||||
}
|
||||
|
||||
// Aligns the input value to the next power of 2 to be used as bigger memory allocation block.
|
||||
@@ -206,7 +208,7 @@ public:
|
||||
typedef typename FallbackAllocation::template Data<T> FallbackData;
|
||||
|
||||
bool _useFallback = false;
|
||||
alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)];
|
||||
byte _data[Capacity * sizeof(T)];
|
||||
FallbackData _fallback;
|
||||
|
||||
public:
|
||||
|
||||
@@ -215,11 +215,6 @@ public:
|
||||
return String(_data.Get(), _data.Count());
|
||||
}
|
||||
|
||||
StringAnsi ToStringAnsi() const
|
||||
{
|
||||
return StringAnsi(_data.Get(), _data.Count());
|
||||
}
|
||||
|
||||
StringView ToStringView() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -145,6 +145,12 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
PARSE_BOOL_SWITCH("-monolog ", MonoLog);
|
||||
PARSE_BOOL_SWITCH("-mute ", Mute);
|
||||
PARSE_BOOL_SWITCH("-lowdpi ", LowDPI);
|
||||
|
||||
#if PLATFORM_LINUX && PLATFORM_SDL
|
||||
PARSE_BOOL_SWITCH("-wayland ", Wayland);
|
||||
PARSE_BOOL_SWITCH("-x11 ", X11);
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
|
||||
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
|
||||
|
||||
@@ -127,6 +127,20 @@ public:
|
||||
/// </summary>
|
||||
Nullable<bool> LowDPI;
|
||||
|
||||
#if PLATFORM_LINUX && PLATFORM_SDL
|
||||
|
||||
/// <summary>
|
||||
/// -wayland (prefer Wayland over X11 as display server)
|
||||
/// </summary>
|
||||
Nullable<bool> Wayland;
|
||||
|
||||
/// <summary>
|
||||
/// -x11 (prefer X11 over Wayland as display server)
|
||||
/// </summary>
|
||||
Nullable<bool> X11;
|
||||
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// -project !path! (Startup project path)
|
||||
|
||||
@@ -105,6 +105,8 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
CommandLine::Options.Std = true;
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
|
||||
if (Platform::Init())
|
||||
{
|
||||
Platform::Fatal(TEXT("Cannot init platform."));
|
||||
@@ -114,7 +116,6 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
InitProfilerMemory(cmdLine, 1);
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
Time::StartupTime = DateTime::Now();
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -79,11 +79,10 @@ namespace FlaxEngine.Interop
|
||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), NativeLibraryImportResolver);
|
||||
|
||||
// Change default culture to match with Mono runtime default culture
|
||||
var culture = CultureInfo.InvariantCulture;
|
||||
CultureInfo.DefaultThreadCurrentCulture = culture;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = culture;
|
||||
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
|
||||
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
|
||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
|
||||
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
|
||||
|
||||
}
|
||||
|
||||
@@ -426,8 +425,6 @@ namespace FlaxEngine.Interop
|
||||
var fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField
|
||||
fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags)
|
||||
return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync)
|
||||
#else
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
@@ -13,10 +15,14 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
namespace
|
||||
{
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
bool LastGameViewportFocus = false;
|
||||
}
|
||||
|
||||
class ScreenService : public EngineService
|
||||
{
|
||||
@@ -101,9 +107,9 @@ void Screen::SetCursorVisible(const bool value)
|
||||
const auto win = Engine::MainWindow;
|
||||
#endif
|
||||
if (win && Engine::HasGameViewportFocus())
|
||||
{
|
||||
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
|
||||
}
|
||||
else if (win)
|
||||
win->SetCursor(CursorType::Default);
|
||||
CursorVisible = value;
|
||||
}
|
||||
|
||||
@@ -116,21 +122,31 @@ void Screen::SetCursorLock(CursorLockMode mode)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
|
||||
if (win)
|
||||
bounds = Rectangle(win->ScreenToClient(bounds.GetTopLeft()), bounds.Size);
|
||||
#else
|
||||
const auto win = Engine::MainWindow;
|
||||
Rectangle bounds = win != nullptr ? win->GetClientBounds() : Rectangle();
|
||||
#endif
|
||||
if (win && mode == CursorLockMode::Clipped)
|
||||
if (win)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
|
||||
#else
|
||||
Rectangle bounds = win->GetClientBounds();
|
||||
#endif
|
||||
win->StartClippingCursor(bounds);
|
||||
}
|
||||
else if (win && CursorLock == CursorLockMode::Clipped)
|
||||
{
|
||||
win->EndClippingCursor();
|
||||
bool inRelativeMode = Input::Mouse->IsRelative();
|
||||
if (mode == CursorLockMode::Clipped)
|
||||
win->StartClippingCursor(bounds);
|
||||
else if (mode == CursorLockMode::Locked)
|
||||
{
|
||||
// Use mouse clip region to restrict the cursor in one spot
|
||||
win->StartClippingCursor(Rectangle(bounds.GetCenter(), Float2(1, 1)));
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::Locked || CursorLock == CursorLockMode::Clipped)
|
||||
win->EndClippingCursor();
|
||||
|
||||
// Enable relative mode when cursor is restricted
|
||||
if (mode != CursorLockMode::None)
|
||||
Input::Mouse->SetRelativeMode(true, win);
|
||||
else if (mode == CursorLockMode::None && inRelativeMode)
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
CursorLock = mode;
|
||||
}
|
||||
@@ -190,7 +206,11 @@ void ScreenService::Update()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Sync current cursor state in Editor (eg. when viewport focus can change)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
|
||||
if (gameViewportFocus != LastGameViewportFocus)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
LastGameViewportFocus = gameViewportFocus;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user