diff --git a/.gitignore b/.gitignore
index 54907892f..b7e11e554 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ Source/*.csproj
/Package_*/
!Source/Engine/Debug
/Source/Platforms/Editor/Linux/Mono/etc/mono/registry
+PackageEditor_Cert.command
PackageEditor_Cert.bat
PackagePlatforms_Cert.bat
diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax
index 18f653f86..0e94c5c06 100644
--- a/Content/Shaders/GlobalSignDistanceField.flax
+++ b/Content/Shaders/GlobalSignDistanceField.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2c8aa181a814d69b15ffec6493a71a6f42ae816ce04f7803cff2d5073b4b3c4f
-size 11790
+oid sha256:e075583620e62407503c73f52487c204ddcad421e80fa621aebd55af1cfb08d5
+size 11798
diff --git a/Flax.flaxproj b/Flax.flaxproj
index fbf48cc2b..6b3014e94 100644
--- a/Flax.flaxproj
+++ b/Flax.flaxproj
@@ -2,8 +2,8 @@
"Name": "Flax",
"Version": {
"Major": 1,
- "Minor": 6,
- "Build": 6345
+ "Minor": 7,
+ "Build": 6402
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings
index 6a22f211b..ff396d824 100644
--- a/Flax.sln.DotSettings
+++ b/Flax.sln.DotSettings
@@ -256,6 +256,8 @@
True
True
True
+ True
+ True
True
True
True
@@ -321,6 +323,7 @@
True
True
True
+ True
True
True
True
diff --git a/GenerateProjectFiles.bat b/GenerateProjectFiles.bat
index 622939c34..28970a203 100644
--- a/GenerateProjectFiles.bat
+++ b/GenerateProjectFiles.bat
@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
:: Build bindings for all editor configurations
echo Building C# bindings...
-Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
+Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor
popd
echo Done!
diff --git a/GenerateProjectFiles.command b/GenerateProjectFiles.command
index 5ee5c0783..a42121252 100755
--- a/GenerateProjectFiles.command
+++ b/GenerateProjectFiles.command
@@ -14,4 +14,4 @@ bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations
echo Building C# bindings...
# TODO: Detect the correct architecture here
-Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor,FlaxGame
+Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor
diff --git a/GenerateProjectFiles.sh b/GenerateProjectFiles.sh
index dceb8abe8..76d96c7ef 100755
--- a/GenerateProjectFiles.sh
+++ b/GenerateProjectFiles.sh
@@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations
echo Building C# bindings...
# TODO: Detect the correct architecture here
-Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor,FlaxGame
+Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor
diff --git a/README.md b/README.md
index fac631a6a..d6688bd03 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Flax Engine is a high quality modern 3D game engine written in C++ and C#.
-From stunning graphics to powerful scripts - Flax can give everything for your games. Designed for fast workflow with many ready to use features waiting for you right now. To learn more see the website ([www.flaxengine.com](https://flaxengine.com)).
+From stunning graphics to powerful scripts, it's designed for fast workflow with many ready-to-use features waiting for you right now. To learn more see the website ([www.flaxengine.com](https://flaxengine.com)).
This repository contains full source code of the Flax Engine (excluding NDA-protected platforms support). Anyone is welcome to contribute or use the modified source in Flax-based games.
diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs
index 5e054e5c6..259be104b 100644
--- a/Source/Editor/Content/GUI/ContentView.cs
+++ b/Source/Editor/Content/GUI/ContentView.cs
@@ -220,8 +220,9 @@ namespace FlaxEditor.Content.GUI
// Remove references and unlink items
for (int i = 0; i < _items.Count; i++)
{
- _items[i].Parent = null;
- _items[i].RemoveReference(this);
+ var item = _items[i];
+ item.Parent = null;
+ item.RemoveReference(this);
}
_items.Clear();
@@ -263,11 +264,12 @@ namespace FlaxEditor.Content.GUI
// Add references and link items
for (int i = 0; i < items.Count; i++)
{
- if (items[i].Visible)
+ var item = items[i];
+ if (item.Visible && !_items.Contains(item))
{
- items[i].Parent = this;
- items[i].AddReference(this);
- _items.Add(items[i]);
+ item.Parent = this;
+ item.AddReference(this);
+ _items.Add(item);
}
}
if (selection != null)
@@ -279,6 +281,8 @@ namespace FlaxEditor.Content.GUI
// Sort items depending on sortMethod parameter
_children.Sort(((control, control1) =>
{
+ if (control == null || control1 == null)
+ return 0;
if (sortType == SortType.AlphabeticReverse)
{
if (control.CompareTo(control1) > 0)
@@ -520,8 +524,8 @@ namespace FlaxEditor.Content.GUI
{
int min = _selection.Min(x => x.IndexInParent);
int max = _selection.Max(x => x.IndexInParent);
- min = Mathf.Min(min, item.IndexInParent);
- max = Mathf.Max(max, item.IndexInParent);
+ min = Mathf.Max(Mathf.Min(min, item.IndexInParent), 0);
+ max = Mathf.Min(Mathf.Max(max, item.IndexInParent), _children.Count - 1);
var selection = new List(_selection);
for (int i = min; i <= max; i++)
{
diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs
index 6bbdc0a51..604caa704 100644
--- a/Source/Editor/Content/Items/ContentItem.cs
+++ b/Source/Editor/Content/Items/ContentItem.cs
@@ -323,8 +323,6 @@ namespace FlaxEditor.Content
/// The new path.
internal virtual void UpdatePath(string value)
{
- Assert.AreNotEqual(Path, value);
-
// Set path
Path = StringUtils.NormalizePath(value);
FileName = System.IO.Path.GetFileName(value);
diff --git a/Source/Editor/Content/Proxy/BehaviorTreeProxy.cs b/Source/Editor/Content/Proxy/BehaviorTreeProxy.cs
new file mode 100644
index 000000000..33ad0862f
--- /dev/null
+++ b/Source/Editor/Content/Proxy/BehaviorTreeProxy.cs
@@ -0,0 +1,66 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+using FlaxEditor.Content.Thumbnails;
+using FlaxEditor.Windows;
+using FlaxEditor.Windows.Assets;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.Content
+{
+ ///
+ /// A asset proxy object.
+ ///
+ ///
+ [ContentContextMenu("New/AI/Behavior Tree")]
+ public class BehaviorTreeProxy : BinaryAssetProxy
+ {
+ ///
+ public override string Name => "Behavior Tree";
+
+ ///
+ public override bool CanReimport(ContentItem item)
+ {
+ return true;
+ }
+
+ ///
+ public override EditorWindow Open(Editor editor, ContentItem item)
+ {
+ return new BehaviorTreeWindow(editor, item as BinaryAssetItem);
+ }
+
+ ///
+ public override Color AccentColor => Color.FromRGB(0x3256A8);
+
+ ///
+ public override Type AssetType => typeof(BehaviorTree);
+
+ ///
+ public override bool CanCreate(ContentFolder targetLocation)
+ {
+ return targetLocation.CanHaveAssets;
+ }
+
+ ///
+ public override void Create(string outputPath, object arg)
+ {
+ if (Editor.CreateAsset(Editor.NewAssetType.BehaviorTree, outputPath))
+ throw new Exception("Failed to create new asset.");
+ }
+
+ ///
+ public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
+ {
+ guiRoot.AddChild(new Label
+ {
+ Text = Path.GetFileNameWithoutExtension(request.Asset.Path),
+ Offsets = Margin.Zero,
+ AnchorPreset = AnchorPresets.StretchAll,
+ Wrapping = TextWrapping.WrapWords
+ });
+ }
+ }
+}
diff --git a/Source/Editor/Content/Proxy/CubeTextureProxy.cs b/Source/Editor/Content/Proxy/CubeTextureProxy.cs
index 691e4ac53..2dccc9e47 100644
--- a/Source/Editor/Content/Proxy/CubeTextureProxy.cs
+++ b/Source/Editor/Content/Proxy/CubeTextureProxy.cs
@@ -54,12 +54,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- if (!_preview.HasLoadedAssets)
- return false;
-
- // Check if all mip maps are streamed
- var asset = (CubeTexture)request.Asset;
- return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((CubeTexture)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs b/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
index dcc3e3ec4..331ff81c3 100644
--- a/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
+++ b/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
@@ -62,7 +62,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- return _preview.HasLoadedAssets;
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((MaterialInstance)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/MaterialProxy.cs b/Source/Editor/Content/Proxy/MaterialProxy.cs
index cf7c8b77e..a7fcfecc8 100644
--- a/Source/Editor/Content/Proxy/MaterialProxy.cs
+++ b/Source/Editor/Content/Proxy/MaterialProxy.cs
@@ -106,7 +106,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- return _preview.HasLoadedAssets;
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Material)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/ModelProxy.cs b/Source/Editor/Content/Proxy/ModelProxy.cs
index 845cbc80b..0cf16850d 100644
--- a/Source/Editor/Content/Proxy/ModelProxy.cs
+++ b/Source/Editor/Content/Proxy/ModelProxy.cs
@@ -82,12 +82,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- if (!_preview.HasLoadedAssets)
- return false;
-
- // Check if asset is streamed enough
- var asset = (Model)request.Asset;
- return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Model)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/SceneProxy.cs b/Source/Editor/Content/Proxy/SceneProxy.cs
index 78d88b440..004c2aed7 100644
--- a/Source/Editor/Content/Proxy/SceneProxy.cs
+++ b/Source/Editor/Content/Proxy/SceneProxy.cs
@@ -30,6 +30,12 @@ namespace FlaxEditor.Content
return item is SceneItem;
}
+ ///
+ public override bool AcceptsAsset(string typeName, string path)
+ {
+ return (typeName == Scene.AssetTypename || typeName == Scene.EditorPickerTypename) && path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
+ }
+
///
public override bool CanCreate(ContentFolder targetLocation)
{
diff --git a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs
index f0193d0d0..6e228aa86 100644
--- a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs
+++ b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs
@@ -54,15 +54,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- if (!_preview.HasLoadedAssets)
- return false;
-
- // Check if asset is streamed enough
- var asset = (SkinnedModel)request.Asset;
- var lods = asset.LODs.Length;
- if (asset.IsLoaded && lods == 0)
- return true; // Skeleton-only model
- return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((SkinnedModel)request.Asset);
}
///
diff --git a/Source/Editor/Content/Proxy/TextureProxy.cs b/Source/Editor/Content/Proxy/TextureProxy.cs
index 48bfcc9e9..709ca4dbf 100644
--- a/Source/Editor/Content/Proxy/TextureProxy.cs
+++ b/Source/Editor/Content/Proxy/TextureProxy.cs
@@ -57,11 +57,7 @@ namespace FlaxEditor.Content
///
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
- // Check if asset is streamed enough
- var asset = (Texture)request.Asset;
- var mipLevels = asset.MipLevels;
- var minMipLevels = Mathf.Min(mipLevels, 7);
- return asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
+ return ThumbnailsModule.HasMinimumQuality((Texture)request.Asset);
}
///
diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
index a6996310e..1282e4daa 100644
--- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
+++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
@@ -125,6 +125,74 @@ namespace FlaxEditor.Content.Thumbnails
}
}
+ internal static bool HasMinimumQuality(TextureBase asset)
+ {
+ var mipLevels = asset.MipLevels;
+ var minMipLevels = Mathf.Min(mipLevels, 7);
+ return asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality));
+ }
+
+ internal static bool HasMinimumQuality(Model asset)
+ {
+ if (!asset.IsLoaded)
+ return false;
+ var lods = asset.LODs.Length;
+ var slots = asset.MaterialSlots;
+ foreach (var slot in slots)
+ {
+ if (slot.Material && !HasMinimumQuality(slot.Material))
+ return false;
+ }
+ return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
+ }
+
+ internal static bool HasMinimumQuality(SkinnedModel asset)
+ {
+ var lods = asset.LODs.Length;
+ if (asset.IsLoaded && lods == 0)
+ return true; // Skeleton-only model
+ var slots = asset.MaterialSlots;
+ foreach (var slot in slots)
+ {
+ if (slot.Material && !HasMinimumQuality(slot.Material))
+ return false;
+ }
+ return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
+ }
+
+ internal static bool HasMinimumQuality(MaterialBase asset)
+ {
+ if (asset is MaterialInstance asInstance)
+ return HasMinimumQuality(asInstance);
+ return HasMinimumQualityInternal(asset);
+ }
+
+ internal static bool HasMinimumQuality(Material asset)
+ {
+ return HasMinimumQualityInternal(asset);
+ }
+
+ internal static bool HasMinimumQuality(MaterialInstance asset)
+ {
+ if (!HasMinimumQualityInternal(asset))
+ return false;
+ var baseMaterial = asset.BaseMaterial;
+ return baseMaterial == null || HasMinimumQualityInternal(baseMaterial);
+ }
+
+ private static bool HasMinimumQualityInternal(MaterialBase asset)
+ {
+ if (!asset.IsLoaded)
+ return false;
+ var parameters = asset.Parameters;
+ foreach (var parameter in parameters)
+ {
+ if (parameter.Value is TextureBase asTexture && !HasMinimumQuality(asTexture))
+ return false;
+ }
+ return true;
+ }
+
#region IContentItemOwner
///
@@ -338,18 +406,16 @@ namespace FlaxEditor.Content.Thumbnails
for (int i = 0; i < maxChecks; i++)
{
var request = _requests[i];
-
try
{
if (request.IsReady)
- {
return request;
- }
}
catch (Exception ex)
{
- Editor.LogWarning(ex);
Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}.");
+ Editor.LogWarning(ex);
+ _requests.RemoveAt(i--);
}
}
@@ -368,7 +434,6 @@ namespace FlaxEditor.Content.Thumbnails
// Create atlas
if (PreviewsCache.Create(path))
{
- // Error
Editor.LogError("Failed to create thumbnails atlas.");
return null;
}
@@ -377,7 +442,6 @@ namespace FlaxEditor.Content.Thumbnails
var atlas = FlaxEngine.Content.LoadAsync(path);
if (atlas == null)
{
- // Error
Editor.LogError("Failed to load thumbnails atlas.");
return null;
}
@@ -449,7 +513,6 @@ namespace FlaxEditor.Content.Thumbnails
for (int i = 0; i < checks; i++)
{
var request = _requests[i];
-
try
{
if (request.IsReady)
@@ -463,8 +526,9 @@ namespace FlaxEditor.Content.Thumbnails
}
catch (Exception ex)
{
- Editor.LogWarning(ex);
Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}.");
+ Editor.LogWarning(ex);
+ _requests.RemoveAt(i--);
}
}
diff --git a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp
index ec7b8e7e1..42ef6fcdf 100644
--- a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp
@@ -104,4 +104,19 @@ bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
return false;
}
+void LinuxPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir)
+{
+ // Pick the first executable file
+ Array files;
+ FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
+ for (auto& file : files)
+ {
+ if (FileSystem::GetExtension(file).IsEmpty())
+ {
+ executableFile = file;
+ break;
+ }
+ }
+}
+
#endif
diff --git a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.h b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.h
index 562b38962..432240d00 100644
--- a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.h
+++ b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.h
@@ -20,6 +20,7 @@ public:
ArchitectureType GetArchitecture() const override;
bool UseSystemDotnet() const override;
bool OnDeployBinaries(CookingData& data) override;
+ void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override;
};
#endif
diff --git a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp
index 0008228f7..aa56a6b95 100644
--- a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.cpp
@@ -17,9 +17,7 @@
#include "Editor/ProjectInfo.h"
#include "Editor/Cooker/GameCooker.h"
#include "Editor/Utilities/EditorUtilities.h"
-
-#include "pugixml/pugixml_extra.hpp"
-
+#include
using namespace pugi;
IMPLEMENT_SETTINGS_GETTER(MacPlatformSettings, MacPlatform);
@@ -251,4 +249,19 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
return false;
}
+void MacPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir)
+{
+ // Pick the first executable file
+ Array files;
+ FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
+ for (auto& file : files)
+ {
+ if (FileSystem::GetExtension(file).IsEmpty())
+ {
+ executableFile = file;
+ break;
+ }
+ }
+}
+
#endif
diff --git a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.h b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.h
index 21d9141e3..efdd0b733 100644
--- a/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.h
+++ b/Source/Editor/Cooker/Platform/Mac/MacPlatformTools.h
@@ -27,6 +27,7 @@ public:
bool IsNativeCodeFile(CookingData& data, const String& file) override;
void OnBuildStarted(CookingData& data) override;
bool OnPostProcess(CookingData& data) override;
+ void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override;
};
#endif
diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
index c9bbc016e..8306475d1 100644
--- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp
+++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
@@ -131,7 +131,8 @@ bool DeployDataStep::Perform(CookingData& data)
if (FileSystem::DirectoryExists(dstDotnet))
{
String cachedData;
- File::ReadAllText(dotnetCacheFilePath, cachedData);
+ if (FileSystem::FileExists(dotnetCacheFilePath))
+ File::ReadAllText(dotnetCacheFilePath, cachedData);
if (cachedData != dotnetCachedValue)
{
FileSystem::DeleteDirectory(dstDotnet);
@@ -360,7 +361,7 @@ bool DeployDataStep::Perform(CookingData& data)
data.AddRootEngineAsset(PRE_INTEGRATED_GF_ASSET_NAME);
data.AddRootEngineAsset(SMAA_AREA_TEX);
data.AddRootEngineAsset(SMAA_SEARCH_TEX);
- if (data.Configuration != BuildConfiguration::Release)
+ if (!buildSettings.SkipDefaultFonts)
data.AddRootEngineAsset(TEXT("Editor/Fonts/Roboto-Regular"));
// Register custom assets (eg. plugins)
diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
index 73e2ede0f..0c4ac4106 100644
--- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs
+++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
@@ -37,6 +37,23 @@ namespace FlaxEditor.CustomEditors
UseDefault = 1 << 2,
}
+ ///
+ /// The interface for Editor context that owns the presenter. Can be or or other window/panel - custom editor scan use it for more specific features.
+ ///
+ public interface IPresenterOwner
+ {
+ ///
+ /// Gets the viewport linked with properties presenter (optional, null if unused).
+ ///
+ public Viewport.EditorViewport PresenterViewport { get; }
+
+ ///
+ /// Selects the scene objects.
+ ///
+ /// The nodes to select
+ public void Select(List nodes);
+ }
+
///
/// Main class for Custom Editors used to present selected objects properties and allow to modify them.
///
@@ -68,8 +85,15 @@ namespace FlaxEditor.CustomEditors
///
public override void Update(float deltaTime)
{
- // Update editors
- _presenter.Update();
+ try
+ {
+ // Update editors
+ _presenter.Update();
+ }
+ catch (Exception ex)
+ {
+ FlaxEditor.Editor.LogWarning(ex);
+ }
base.Update(deltaTime);
}
@@ -254,7 +278,7 @@ namespace FlaxEditor.CustomEditors
///
/// The Editor context that owns this presenter. Can be or or other window/panel - custom editor scan use it for more specific features.
///
- public object Owner;
+ public IPresenterOwner Owner;
///
/// Gets or sets the text to show when no object is selected.
@@ -270,7 +294,24 @@ namespace FlaxEditor.CustomEditors
}
}
+ ///
+ /// Gets or sets the value indicating whether properties are read-only.
+ ///
+ public bool ReadOnly
+ {
+ get => _readOnly;
+ set
+ {
+ if (_readOnly != value)
+ {
+ _readOnly = value;
+ UpdateReadOnly();
+ }
+ }
+ }
+
private bool _buildOnUpdate;
+ private bool _readOnly;
///
/// Initializes a new instance of the class.
@@ -278,7 +319,7 @@ namespace FlaxEditor.CustomEditors
/// The undo. It's optional.
/// The custom text to display when no object is selected. Default is No selection.
/// The owner of the presenter.
- public CustomEditorPresenter(Undo undo, string noSelectionText = null, object owner = null)
+ public CustomEditorPresenter(Undo undo, string noSelectionText = null, IPresenterOwner owner = null)
{
Undo = undo;
Owner = owner;
@@ -364,6 +405,8 @@ namespace FlaxEditor.CustomEditors
// Restore scroll value
if (parentScrollV > -1)
panel.VScrollBar.Value = parentScrollV;
+ if (_readOnly)
+ UpdateReadOnly();
}
///
@@ -374,6 +417,16 @@ namespace FlaxEditor.CustomEditors
_buildOnUpdate = true;
}
+ private void UpdateReadOnly()
+ {
+ // Only scrollbars are enabled
+ foreach (var child in Panel.Children)
+ {
+ if (!(child is ScrollBar))
+ child.Enabled = !_readOnly;
+ }
+ }
+
private void ExpandGroups(LayoutElementsContainer c, bool open)
{
if (c is Elements.GroupElement group)
diff --git a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
new file mode 100644
index 000000000..4053c6e16
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
@@ -0,0 +1,97 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.Gizmo;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+using FlaxEngine.Json;
+using FlaxEngine.Tools;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ ///
+ [CustomEditor(typeof(Cloth)), DefaultEditor]
+ class ClothEditor : ActorEditor
+ {
+ private ClothPaintingGizmoMode _gizmoMode;
+ private Viewport.Modes.EditorGizmoMode _prevMode;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ base.Initialize(layout);
+
+ if (Values.Count != 1)
+ return;
+
+ // Add gizmo painting mode to the viewport
+ var owner = Presenter.Owner;
+ if (owner == null)
+ return;
+ var gizmoOwner = owner as IGizmoOwner ?? owner.PresenterViewport as IGizmoOwner;
+ if (gizmoOwner == null)
+ return;
+ var gizmos = gizmoOwner.Gizmos;
+ _gizmoMode = new ClothPaintingGizmoMode();
+
+ var projectCache = Editor.Instance.ProjectCache;
+ if (projectCache.TryGetCustomData("ClothGizmoPaintValue", out var cachedPaintValue))
+ _gizmoMode.PaintValue = JsonSerializer.Deserialize(cachedPaintValue);
+ if (projectCache.TryGetCustomData("ClothGizmoContinuousPaint", out var cachedContinuousPaint))
+ _gizmoMode.ContinuousPaint = JsonSerializer.Deserialize(cachedContinuousPaint);
+ if (projectCache.TryGetCustomData("ClothGizmoBrushFalloff", out var cachedBrushFalloff))
+ _gizmoMode.BrushFalloff = JsonSerializer.Deserialize(cachedBrushFalloff);
+ if (projectCache.TryGetCustomData("ClothGizmoBrushSize", out var cachedBrushSize))
+ _gizmoMode.BrushSize = JsonSerializer.Deserialize(cachedBrushSize);
+ if (projectCache.TryGetCustomData("ClothGizmoBrushStrength", out var cachedBrushStrength))
+ _gizmoMode.BrushStrength = JsonSerializer.Deserialize(cachedBrushStrength);
+
+ gizmos.AddMode(_gizmoMode);
+ _prevMode = gizmos.ActiveMode;
+ gizmos.ActiveMode = _gizmoMode;
+ _gizmoMode.Gizmo.SetPaintCloth((Cloth)Values[0]);
+
+ // Insert gizmo mode options to properties editing
+ var paintGroup = layout.Group("Cloth Painting");
+ var paintValue = new ReadOnlyValueContainer(new ScriptType(typeof(ClothPaintingGizmoMode)), _gizmoMode);
+ paintGroup.Object(paintValue);
+ {
+ var grid = paintGroup.CustomContainer();
+ var gridControl = grid.CustomControl;
+ gridControl.ClipChildren = false;
+ gridControl.Height = Button.DefaultHeight;
+ gridControl.SlotsHorizontally = 2;
+ gridControl.SlotsVertically = 1;
+ grid.Button("Fill", "Fills the cloth particles with given paint value.").Button.Clicked += _gizmoMode.Gizmo.Fill;
+ grid.Button("Reset", "Clears the cloth particles paint.").Button.Clicked += _gizmoMode.Gizmo.Reset;
+ }
+ }
+
+ ///
+ protected override void Deinitialize()
+ {
+ // Cleanup gizmos
+ if (_gizmoMode != null)
+ {
+ var gizmos = _gizmoMode.Owner.Gizmos;
+ if (gizmos.ActiveMode == _gizmoMode)
+ gizmos.ActiveMode = _prevMode;
+ gizmos.RemoveMode(_gizmoMode);
+ var projectCache = Editor.Instance.ProjectCache;
+ projectCache.SetCustomData("ClothGizmoPaintValue", JsonSerializer.Serialize(_gizmoMode.PaintValue, typeof(float)));
+ projectCache.SetCustomData("ClothGizmoContinuousPaint", JsonSerializer.Serialize(_gizmoMode.ContinuousPaint, typeof(bool)));
+ projectCache.SetCustomData("ClothGizmoBrushFalloff", JsonSerializer.Serialize(_gizmoMode.BrushFalloff, typeof(float)));
+ projectCache.SetCustomData("ClothGizmoBrushSize", JsonSerializer.Serialize(_gizmoMode.BrushSize, typeof(float)));
+ projectCache.SetCustomData("ClothGizmoBrushStrength", JsonSerializer.Serialize(_gizmoMode.BrushStrength, typeof(float)));
+ _gizmoMode.Dispose();
+ _gizmoMode = null;
+ }
+ _prevMode = null;
+
+ base.Deinitialize();
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs
new file mode 100644
index 000000000..71c5f6daf
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs
@@ -0,0 +1,336 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+using System.Linq;
+using FlaxEditor.CustomEditors.Editors;
+using FlaxEditor.CustomEditors.Elements;
+using FlaxEditor.GUI;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ ///
+ [CustomEditor(typeof(ModelInstanceActor.MeshReference)), DefaultEditor]
+ public class MeshReferenceEditor : CustomEditor
+ {
+ private class MeshRefPickerControl : Control
+ {
+ private ModelInstanceActor.MeshReference _value = new ModelInstanceActor.MeshReference { LODIndex = -1, MeshIndex = -1 };
+ private string _valueName;
+ private Float2 _mousePos;
+
+ public string[][] MeshNames;
+ public event Action ValueChanged;
+
+ public ModelInstanceActor.MeshReference Value
+ {
+ get => _value;
+ set
+ {
+ if (_value.LODIndex == value.LODIndex && _value.MeshIndex == value.MeshIndex)
+ return;
+ _value = value;
+ if (value.LODIndex == -1 || value.MeshIndex == -1)
+ _valueName = null;
+ else if (MeshNames.Length == 1)
+ _valueName = MeshNames[value.LODIndex][value.MeshIndex];
+ else
+ _valueName = $"LOD{value.LODIndex} - {MeshNames[value.LODIndex][value.MeshIndex]}";
+ ValueChanged?.Invoke();
+ }
+ }
+
+ public MeshRefPickerControl()
+ : base(0, 0, 50, 16)
+ {
+ }
+
+ private void ShowDropDownMenu()
+ {
+ // Show context menu with tree structure of LODs and meshes
+ Focus();
+ var cm = new ItemsListContextMenu(200);
+ var meshNames = MeshNames;
+ var actor = _value.Actor;
+ for (int lodIndex = 0; lodIndex < meshNames.Length; lodIndex++)
+ {
+ var item = new ItemsListContextMenu.Item
+ {
+ Name = "LOD" + lodIndex,
+ Tag = new ModelInstanceActor.MeshReference { Actor = actor, LODIndex = lodIndex, MeshIndex = 0 },
+ TintColor = new Color(0.8f, 0.8f, 1.0f, 0.8f),
+ };
+ cm.AddItem(item);
+
+ for (int meshIndex = 0; meshIndex < meshNames[lodIndex].Length; meshIndex++)
+ {
+ item = new ItemsListContextMenu.Item
+ {
+ Name = " " + meshNames[lodIndex][meshIndex],
+ Tag = new ModelInstanceActor.MeshReference { Actor = actor, LODIndex = lodIndex, MeshIndex = meshIndex },
+ };
+ if (_value.LODIndex == lodIndex && _value.MeshIndex == meshIndex)
+ item.BackgroundColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
+ cm.AddItem(item);
+ }
+ }
+ cm.ItemClicked += item => Value = (ModelInstanceActor.MeshReference)item.Tag;
+ cm.Show(Parent, BottomLeft);
+ }
+
+ ///
+ public override void Draw()
+ {
+ base.Draw();
+
+ // Cache data
+ var style = FlaxEngine.GUI.Style.Current;
+ bool isSelected = _valueName != null;
+ bool isEnabled = EnabledInHierarchy;
+ var frameRect = new Rectangle(0, 0, Width, 16);
+ if (isSelected)
+ frameRect.Width -= 16;
+ frameRect.Width -= 16;
+ var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14);
+ var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14);
+ var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14);
+
+ // Draw frame
+ Render2D.DrawRectangle(frameRect, isEnabled && (IsMouseOver || IsNavFocused) ? style.BorderHighlighted : style.BorderNormal);
+
+ // Check if has item selected
+ if (isSelected)
+ {
+ // Draw name
+ Render2D.PushClip(nameRect);
+ Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center);
+ Render2D.PopClip();
+
+ // Draw deselect button
+ Render2D.DrawSprite(style.Cross, button1Rect, isEnabled && button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
+ }
+ else
+ {
+ // Draw info
+ Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center);
+ }
+
+ // Draw picker button
+ var pickerRect = isSelected ? button2Rect : button1Rect;
+ Render2D.DrawSprite(style.ArrowDown, pickerRect, isEnabled && pickerRect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
+ }
+
+ ///
+ public override void OnMouseEnter(Float2 location)
+ {
+ _mousePos = location;
+
+ base.OnMouseEnter(location);
+ }
+
+ ///
+ public override void OnMouseLeave()
+ {
+ _mousePos = Float2.Minimum;
+
+ base.OnMouseLeave();
+ }
+
+ ///
+ public override void OnMouseMove(Float2 location)
+ {
+ _mousePos = location;
+
+ base.OnMouseMove(location);
+ }
+
+ ///
+ public override bool OnMouseUp(Float2 location, MouseButton button)
+ {
+ // Cache data
+ bool isSelected = _valueName != null;
+ var frameRect = new Rectangle(0, 0, Width, 16);
+ if (isSelected)
+ frameRect.Width -= 16;
+ frameRect.Width -= 16;
+ var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14);
+ var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14);
+ var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14);
+
+ // Deselect
+ if (isSelected && button1Rect.Contains(ref location))
+ Value = new ModelInstanceActor.MeshReference { Actor = null, LODIndex = -1, MeshIndex = -1 };
+
+ // Picker dropdown menu
+ if ((isSelected ? button2Rect : button1Rect).Contains(ref location))
+ ShowDropDownMenu();
+
+ return base.OnMouseUp(location, button);
+ }
+
+ ///
+ public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
+ {
+ Focus();
+
+ // Open model editor window
+ if (_value.Actor is StaticModel staticModel)
+ Editor.Instance.ContentEditing.Open(staticModel.Model);
+ else if (_value.Actor is AnimatedModel animatedModel)
+ Editor.Instance.ContentEditing.Open(animatedModel.SkinnedModel);
+
+ return base.OnMouseDoubleClick(location, button);
+ }
+
+ ///
+ public override void OnSubmit()
+ {
+ base.OnSubmit();
+
+ ShowDropDownMenu();
+ }
+
+ ///
+ public override void OnDestroy()
+ {
+ MeshNames = null;
+ _valueName = null;
+
+ base.OnDestroy();
+ }
+ }
+
+ private ModelInstanceActor _actor;
+ private CustomElement _actorPicker;
+ private CustomElement _meshPicker;
+
+ ///
+ public override DisplayStyle Style => DisplayStyle.Inline;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ // Get the context actor to pick the mesh from it
+ if (GetActor(out var actor))
+ {
+ // TODO: support editing multiple values
+ layout.Label("Different values");
+ return;
+ }
+ _actor = actor;
+
+ var showActorPicker = actor == null || ParentEditor.Values.All(x => x is not Cloth);
+ if (showActorPicker)
+ {
+ // Actor reference picker
+ _actorPicker = layout.Custom();
+ _actorPicker.CustomControl.Type = new ScriptType(typeof(ModelInstanceActor));
+ _actorPicker.CustomControl.ValueChanged += () => SetValue(new ModelInstanceActor.MeshReference { Actor = (ModelInstanceActor)_actorPicker.CustomControl.Value });
+ }
+
+ if (actor != null)
+ {
+ // Get mesh names hierarchy
+ string[][] meshNames;
+ if (actor is StaticModel staticModel)
+ {
+ var model = staticModel.Model;
+ if (model == null || model.WaitForLoaded())
+ return;
+ var materials = model.MaterialSlots;
+ var lods = model.LODs;
+ meshNames = new string[lods.Length][];
+ for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
+ {
+ var lodMeshes = lods[lodIndex].Meshes;
+ meshNames[lodIndex] = new string[lodMeshes.Length];
+ for (int meshIndex = 0; meshIndex < lodMeshes.Length; meshIndex++)
+ {
+ var mesh = lodMeshes[meshIndex];
+ var materialName = materials[mesh.MaterialSlotIndex].Name;
+ if (string.IsNullOrEmpty(materialName) && materials[mesh.MaterialSlotIndex].Material)
+ materialName = Path.GetFileNameWithoutExtension(materials[mesh.MaterialSlotIndex].Material.Path);
+ if (string.IsNullOrEmpty(materialName))
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex}";
+ else
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex} ({materialName})";
+ }
+ }
+ }
+ else if (actor is AnimatedModel animatedModel)
+ {
+ var skinnedModel = animatedModel.SkinnedModel;
+ if (skinnedModel == null || skinnedModel.WaitForLoaded())
+ return;
+ var materials = skinnedModel.MaterialSlots;
+ var lods = skinnedModel.LODs;
+ meshNames = new string[lods.Length][];
+ for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
+ {
+ var lodMeshes = lods[lodIndex].Meshes;
+ meshNames[lodIndex] = new string[lodMeshes.Length];
+ for (int meshIndex = 0; meshIndex < lodMeshes.Length; meshIndex++)
+ {
+ var mesh = lodMeshes[meshIndex];
+ var materialName = materials[mesh.MaterialSlotIndex].Name;
+ if (string.IsNullOrEmpty(materialName) && materials[mesh.MaterialSlotIndex].Material)
+ materialName = Path.GetFileNameWithoutExtension(materials[mesh.MaterialSlotIndex].Material.Path);
+ if (string.IsNullOrEmpty(materialName))
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex}";
+ else
+ meshNames[lodIndex][meshIndex] = $"Mesh {meshIndex} ({materialName})";
+ }
+ }
+ }
+ else
+ return; // Not supported model type
+
+ // Mesh reference picker
+ _meshPicker = layout.Custom();
+ _meshPicker.CustomControl.MeshNames = meshNames;
+ _meshPicker.CustomControl.Value = (ModelInstanceActor.MeshReference)Values[0];
+ _meshPicker.CustomControl.ValueChanged += () => SetValue(_meshPicker.CustomControl.Value);
+ }
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ if (_actorPicker != null)
+ {
+ GetActor(out var actor);
+ _actorPicker.CustomControl.Value = actor;
+ if (actor != _actor)
+ {
+ RebuildLayout();
+ return;
+ }
+ }
+ if (_meshPicker != null)
+ {
+ _meshPicker.CustomControl.Value = (ModelInstanceActor.MeshReference)Values[0];
+ }
+ }
+
+ private bool GetActor(out ModelInstanceActor actor)
+ {
+ actor = null;
+ foreach (ModelInstanceActor.MeshReference value in Values)
+ {
+ if (actor == null)
+ actor = value.Actor;
+ else if (actor != value.Actor)
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
index a6c4e6623..8fb742b5e 100644
--- a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
@@ -1,6 +1,13 @@
-using FlaxEditor.CustomEditors.Editors;
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.Actions;
+using FlaxEditor.CustomEditors.Editors;
+using FlaxEditor.GUI;
+using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
+using System.Collections.Generic;
namespace FlaxEditor.CustomEditors.Dedicated;
@@ -10,6 +17,10 @@ namespace FlaxEditor.CustomEditors.Dedicated;
[CustomEditor(typeof(MissingScript)), DefaultEditor]
public class MissingScriptEditor : GenericEditor
{
+ private DropPanel _dropPanel;
+ private Button _replaceScriptButton;
+ private CheckBox _shouldReplaceAllCheckbox;
+
///
public override void Initialize(LayoutElementsContainer layout)
{
@@ -18,9 +29,137 @@ public class MissingScriptEditor : GenericEditor
base.Initialize(layout);
return;
}
+ _dropPanel = dropPanel;
+ _dropPanel.HeaderTextColor = Color.OrangeRed;
- dropPanel.HeaderTextColor = Color.OrangeRed;
+ var replaceScriptPanel = new Panel
+ {
+ Parent = _dropPanel,
+ Height = 64,
+ };
+
+ _replaceScriptButton = new Button
+ {
+ Text = "Replace Script",
+ TooltipText = "Replaces the missing script with a given script type",
+ AnchorPreset = AnchorPresets.TopCenter,
+ Width = 240,
+ Height = 24,
+ X = -120,
+ Y = 0,
+ Parent = replaceScriptPanel,
+ };
+ _replaceScriptButton.Clicked += OnReplaceScriptButtonClicked;
+
+ var replaceAllLabel = new Label
+ {
+ Text = "Replace all matching missing scripts",
+ TooltipText = "Whether or not to apply this script change to all scripts missing the same type.",
+ AnchorPreset = AnchorPresets.BottomCenter,
+ Y = -34,
+ Parent = replaceScriptPanel,
+ };
+ replaceAllLabel.X -= FlaxEngine.GUI.Style.Current.FontSmall.MeasureText(replaceAllLabel.Text).X;
+
+ _shouldReplaceAllCheckbox = new CheckBox
+ {
+ TooltipText = replaceAllLabel.TooltipText,
+ AnchorPreset = AnchorPresets.BottomCenter,
+ Y = -34,
+ Parent = replaceScriptPanel,
+ };
+
+ float centerDifference = (_shouldReplaceAllCheckbox.Right - replaceAllLabel.Left) / 2;
+ replaceAllLabel.X += centerDifference;
+ _shouldReplaceAllCheckbox.X += centerDifference;
base.Initialize(layout);
}
+
+ private void FindActorsWithMatchingMissingScript(List missingScripts)
+ {
+ foreach (Actor actor in Level.GetActors(typeof(Actor)))
+ {
+ for (int scriptIndex = 0; scriptIndex < actor.ScriptsCount; scriptIndex++)
+ {
+ Script actorScript = actor.Scripts[scriptIndex];
+ if (actorScript is not MissingScript missingActorScript)
+ continue;
+
+ MissingScript currentMissing = Values[0] as MissingScript;
+ if (missingActorScript.MissingTypeName != currentMissing.MissingTypeName)
+ continue;
+
+ missingScripts.Add(missingActorScript);
+ }
+ }
+ }
+
+ private void RunReplacementMultiCast(List actions)
+ {
+ if (actions.Count == 0)
+ {
+ Editor.LogWarning("Failed to replace scripts!");
+ return;
+ }
+
+ var multiAction = new MultiUndoAction(actions);
+ multiAction.Do();
+ var presenter = ParentEditor.Presenter;
+ if (presenter != null)
+ {
+ presenter.Undo.AddAction(multiAction);
+ presenter.Control.Focus();
+ }
+ }
+
+ private void ReplaceScript(ScriptType script, bool replaceAllInScene)
+ {
+ var actions = new List(4);
+
+ var missingScripts = new List();
+ if (!replaceAllInScene)
+ missingScripts.Add((MissingScript)Values[0]);
+ else
+ FindActorsWithMatchingMissingScript(missingScripts);
+
+ foreach (var missingScript in missingScripts)
+ actions.Add(AddRemoveScript.Add(missingScript.Actor, script));
+ RunReplacementMultiCast(actions);
+
+ for (int actionIdx = 0; actionIdx < actions.Count; actionIdx++)
+ {
+ AddRemoveScript addRemoveScriptAction = (AddRemoveScript)actions[actionIdx];
+ int orderInParent = addRemoveScriptAction.GetOrderInParent();
+
+ Script newScript = missingScripts[actionIdx].Actor.Scripts[orderInParent];
+ missingScripts[actionIdx].ReferenceScript = newScript;
+ }
+ actions.Clear();
+
+ foreach (var missingScript in missingScripts)
+ actions.Add(AddRemoveScript.Remove(missingScript));
+ RunReplacementMultiCast(actions);
+ }
+
+ private void OnReplaceScriptButtonClicked()
+ {
+ var scripts = Editor.Instance.CodeEditing.Scripts.Get();
+ if (scripts.Count == 0)
+ {
+ // No scripts
+ var cm1 = new ContextMenu();
+ cm1.AddButton("No scripts in project");
+ cm1.Show(_dropPanel, _replaceScriptButton.BottomLeft);
+ return;
+ }
+
+ // Show context menu with list of scripts to add
+ var cm = new ItemsListContextMenu(180);
+ for (int i = 0; i < scripts.Count; i++)
+ cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i]));
+ cm.ItemClicked += item => ReplaceScript((ScriptType)item.Tag, _shouldReplaceAllCheckbox.Checked);
+ cm.SortItems();
+ cm.Show(_dropPanel, _replaceScriptButton.BottomLeft - new Float2((cm.Width - _replaceScriptButton.Width) / 2, 0));
+ }
}
diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
index 5a10b9a52..c4b334b3a 100644
--- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
@@ -50,7 +50,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
grid.Button("Remove bone").Button.ButtonClicked += OnRemoveBone;
}
- if (Presenter.Owner is Windows.PropertiesWindow || Presenter.Owner is Windows.Assets.PrefabWindow)
+ if (Presenter.Owner != null)
{
// Selection
var grid = editorGroup.CustomContainer();
@@ -309,10 +309,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (node != null)
selection.Add(node);
}
- if (Presenter.Owner is Windows.PropertiesWindow propertiesWindow)
- propertiesWindow.Editor.SceneEditing.Select(selection);
- else if (Presenter.Owner is Windows.Assets.PrefabWindow prefabWindow)
- prefabWindow.Select(selection);
+ Presenter.Owner.Select(selection);
}
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
index aaf45bc52..d7bfbbad7 100644
--- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
@@ -258,27 +258,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// Small image control added per script group that allows to drag and drop a reference to it. Also used to reorder the scripts.
///
///
- internal class ScriptDragIcon : Image
+ internal class DragImage : Image
{
- private ScriptsEditor _editor;
private bool _isMouseDown;
private Float2 _mouseDownPos;
///
- /// Gets the target script.
+ /// Action called when drag event should start.
///
- public Script Script => (Script)Tag;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The script editor.
- /// The target script.
- public ScriptDragIcon(ScriptsEditor editor, Script script)
- {
- Tag = script;
- _editor = editor;
- }
+ public Action Drag;
///
public override void OnMouseEnter(Float2 location)
@@ -291,11 +279,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
///
public override void OnMouseLeave()
{
- // Check if start drag drop
if (_isMouseDown)
{
- DoDrag();
_isMouseDown = false;
+ Drag(this);
}
base.OnMouseLeave();
@@ -304,11 +291,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
///
public override void OnMouseMove(Float2 location)
{
- // Check if start drag drop
if (_isMouseDown && Float2.Distance(location, _mouseDownPos) > 10.0f)
{
- DoDrag();
_isMouseDown = false;
+ Drag(this);
}
base.OnMouseMove(location);
@@ -319,8 +305,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (button == MouseButton.Left)
{
- // Clear flag
_isMouseDown = false;
+ return true;
}
return base.OnMouseUp(location, button);
@@ -331,21 +317,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (button == MouseButton.Left)
{
- // Set flag
_isMouseDown = true;
_mouseDownPos = location;
+ return true;
}
return base.OnMouseDown(location, button);
}
-
- private void DoDrag()
- {
- var script = Script;
- _editor.OnScriptDragChange(true, script);
- DoDragDrop(DragScripts.GetDragData(script));
- _editor.OnScriptDragChange(false, script);
- }
}
internal class ScriptArrangeBar : Control
@@ -643,7 +621,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_scriptToggles[i] = scriptToggle;
// Add drag button to the group
- var scriptDrag = new ScriptDragIcon(this, script)
+ var scriptDrag = new DragImage
{
TooltipText = "Script reference",
AutoFocus = true,
@@ -654,6 +632,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
Margin = new Margin(1),
Brush = new SpriteBrush(Editor.Instance.Icons.DragBar12),
Tag = script,
+ Drag = img =>
+ {
+ var s = (Script)img.Tag;
+ OnScriptDragChange(true, s);
+ img.DoDragDrop(DragScripts.GetDragData(s));
+ OnScriptDragChange(false, s);
+ }
};
// Add settings button to the group
diff --git a/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs b/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
new file mode 100644
index 000000000..8ac6a51cb
--- /dev/null
+++ b/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
@@ -0,0 +1,196 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FlaxEditor.GUI;
+using FlaxEditor.GUI.Tree;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+using FlaxEngine.Utilities;
+
+namespace FlaxEditor.CustomEditors.Editors
+{
+ ///
+ /// Custom editor for and .
+ ///
+ public sealed class BehaviorKnowledgeSelectorEditor : CustomEditor
+ {
+ private ClickableLabel _label;
+
+ ///
+ public override DisplayStyle Style => DisplayStyle.Inline;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ _label = layout.ClickableLabel(Path).CustomControl;
+ _label.RightClick += ShowPicker;
+ var button = new Button
+ {
+ Size = new Float2(16.0f),
+ Text = "...",
+ TooltipText = "Edit...",
+ Parent = _label,
+ };
+ button.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
+ button.Clicked += ShowPicker;
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ // Update label
+ _label.Text = _label.TooltipText = Path;
+ }
+
+ private string Path
+ {
+ get
+ {
+ var v = Values[0];
+ if (v is BehaviorKnowledgeSelectorAny any)
+ return any.Path;
+ if (v is string str)
+ return str;
+ var pathField = v.GetType().GetField("Path");
+ return pathField.GetValue(v) as string;
+ }
+ set
+ {
+ if (string.Equals(Path, value, StringComparison.Ordinal))
+ return;
+ var v = Values[0];
+ if (v is BehaviorKnowledgeSelectorAny)
+ v = new BehaviorKnowledgeSelectorAny(value);
+ else if (v is string)
+ v = value;
+ else
+ {
+ var pathField = v.GetType().GetField("Path");
+ pathField.SetValue(v, value);
+ }
+ SetValue(v);
+ }
+ }
+
+ private void ShowPicker()
+ {
+ // Get Behavior Knowledge to select from
+ var behaviorTreeWindow = Presenter.Owner as Windows.Assets.BehaviorTreeWindow;
+ var rootNode = behaviorTreeWindow?.RootNode;
+ if (rootNode == null)
+ return;
+ var typed = ScriptType.Null;
+ var valueType = Values[0].GetType();
+ if (valueType.Name == "BehaviorKnowledgeSelector`1")
+ {
+ // Get typed selector type to show only assignable items
+ typed = new ScriptType(valueType.GenericTypeArguments[0]);
+ }
+
+ // Get customization options
+ var attributes = Values.GetAttributes();
+ var attribute = (BehaviorKnowledgeSelectorAttribute)attributes?.FirstOrDefault(x => x is BehaviorKnowledgeSelectorAttribute);
+ bool isGoalSelector = false;
+ if (attribute != null)
+ {
+ isGoalSelector = attribute.IsGoalSelector;
+ }
+
+ // Create menu with tree-like structure and search box
+ var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 0, true);
+ var selected = Path;
+
+ // Empty
+ var noneNode = new TreeNode
+ {
+ Text = "",
+ TooltipText = "Deselect value",
+ Parent = tree,
+ };
+ if (string.IsNullOrEmpty(selected))
+ tree.Select(noneNode);
+
+ if (!isGoalSelector)
+ {
+ // Blackboard
+ SetupPickerTypeItems(tree, typed, selected, "Blackboard", "Blackboard/", rootNode.BlackboardType);
+ }
+
+ // Goals
+ var goalTypes = rootNode.GoalTypes;
+ if (goalTypes?.Length != 0)
+ {
+ var goalsNode = new TreeNode
+ {
+ Text = "Goal",
+ TooltipText = "List of goal types defined in Blackboard Tree",
+ Parent = tree,
+ };
+ foreach (var goalTypeName in goalTypes)
+ {
+ var goalType = TypeUtils.GetType(goalTypeName);
+ if (goalType == null)
+ continue;
+ var goalTypeNode = SetupPickerTypeItems(tree, typed, selected, goalType.Name, "Goal/" + goalTypeName + "/", goalTypeName, !isGoalSelector);
+ goalTypeNode.Parent = goalsNode;
+ }
+ goalsNode.ExpandAll(true);
+ }
+
+ tree.SelectedChanged += delegate(List before, List after)
+ {
+ if (after.Count == 1)
+ {
+ menu.Hide();
+ Path = after[0].Tag as string;
+ }
+ };
+ menu.Show(_label, new Float2(0, _label.Height));
+ }
+
+ private TreeNode SetupPickerTypeItems(Tree tree, ScriptType typed, string selected, string text, string typePath, string typeName, bool addItems = true)
+ {
+ var type = TypeUtils.GetType(typeName);
+ if (type == null)
+ return null;
+ var typeNode = new TreeNode
+ {
+ Text = text,
+ TooltipText = type.TypeName,
+ Tag = typePath, // Ability to select whole item type data (eg. whole blackboard value)
+ Parent = tree,
+ };
+ if (typed && !typed.IsAssignableFrom(type))
+ typeNode.Tag = null;
+ if (string.Equals(selected, (string)typeNode.Tag, StringComparison.Ordinal))
+ tree.Select(typeNode);
+ if (addItems)
+ {
+ var items = GenericEditor.GetItemsForType(type, type.IsClass, true);
+ foreach (var item in items)
+ {
+ if (typed && !typed.IsAssignableFrom(item.Info.ValueType))
+ continue;
+ var itemPath = typePath + item.Info.Name;
+ var node = new TreeNode
+ {
+ Text = item.DisplayName,
+ TooltipText = item.TooltipText,
+ Tag = itemPath,
+ Parent = typeNode,
+ };
+ if (string.Equals(selected, itemPath, StringComparison.Ordinal))
+ tree.Select(node);
+ // TODO: add support for nested items (eg. field from blackboard structure field)
+ }
+ typeNode.Expand(true);
+ }
+ return typeNode;
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
index cba2b5a5a..55ac453a9 100644
--- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
@@ -26,7 +26,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// Describes object property/field information for custom editors pipeline.
///
///
- protected class ItemInfo : IComparable
+ public class ItemInfo : IComparable
{
private Options.GeneralOptions.MembersOrder _membersOrder;
@@ -248,7 +248,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// True if use type properties.
/// True if use type fields.
/// The items.
- protected List GetItemsForType(ScriptType type, bool useProperties, bool useFields)
+ public static List GetItemsForType(ScriptType type, bool useProperties, bool useFields)
{
var items = new List();
diff --git a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
index 2ef993e75..0369d679d 100644
--- a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
@@ -125,7 +125,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
// Value
- var values = new CustomValueContainer(type, (instance, index) => instance, (instance, index, value) => { });
+ var values = new CustomValueContainer(type, (instance, index) => instance);
values.AddRange(Values);
var editor = CustomEditorsUtil.CreateEditor(type);
var style = editor.Style;
diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
index ab17c4ae1..38800a738 100644
--- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
@@ -464,6 +464,11 @@ namespace FlaxEditor.CustomEditors.Editors
///
public class TypeNameEditor : TypeEditorBase
{
+ ///
+ /// Prevents spamming log if Value contains missing type to skip research in subsequential Refresh ticks.
+ ///
+ private string _lastTypeNameError;
+
///
public override void Initialize(LayoutElementsContainer layout)
{
@@ -484,8 +489,19 @@ namespace FlaxEditor.CustomEditors.Editors
{
base.Refresh();
- if (!HasDifferentValues && Values[0] is string asTypename)
- _element.CustomControl.Value = TypeUtils.GetType(asTypename);
+ if (!HasDifferentValues && Values[0] is string asTypename &&
+ !string.Equals(asTypename, _lastTypeNameError, StringComparison.Ordinal))
+ {
+ try
+ {
+ _element.CustomControl.Value = TypeUtils.GetType(asTypename);
+ }
+ finally
+ {
+ if (_element.CustomControl.Value == null && asTypename.Length != 0)
+ _lastTypeNameError = asTypename;
+ }
+ }
}
}
}
diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs
index e46040a42..07af5e991 100644
--- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs
+++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs
@@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements
///
/// [Deprecated on 26.05.2022, expires on 26.05.2024]
///
- [System.Obsolete("Deprecated in 1.4")]
+ [System.Obsolete("Deprecated in 1.4, use ValueBox instead")]
public DoubleValueBox DoubleValue => ValueBox;
///
diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs
index 552e9d125..789d8966e 100644
--- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs
+++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs
@@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements
///
/// [Deprecated on 26.05.2022, expires on 26.05.2024]
///
- [System.Obsolete("Deprecated in 1.4, ValueBox instead")]
+ [System.Obsolete("Deprecated in 1.4, use ValueBox instead")]
public FloatValueBox FloatValue => ValueBox;
///
diff --git a/Source/Editor/CustomEditors/Values/CustomValueContainer.cs b/Source/Editor/CustomEditors/Values/CustomValueContainer.cs
index 5be61399b..9a09c4cc2 100644
--- a/Source/Editor/CustomEditors/Values/CustomValueContainer.cs
+++ b/Source/Editor/CustomEditors/Values/CustomValueContainer.cs
@@ -38,15 +38,12 @@ namespace FlaxEditor.CustomEditors
///
/// Type of the value.
/// The value getter.
- /// The value setter.
+ /// The value setter (can be null if value is read-only).
/// The custom type attributes used to override the value editor logic or appearance (eg. instance of ).
- public CustomValueContainer(ScriptType valueType, GetDelegate getter, SetDelegate setter, object[] attributes = null)
+ public CustomValueContainer(ScriptType valueType, GetDelegate getter, SetDelegate setter = null, object[] attributes = null)
: base(ScriptMemberInfo.Null, valueType)
{
- if (getter == null || setter == null)
- throw new ArgumentNullException();
-
- _getter = getter;
+ _getter = getter ?? throw new ArgumentNullException();
_setter = setter;
_attributes = attributes;
}
@@ -57,9 +54,9 @@ namespace FlaxEditor.CustomEditors
/// Type of the value.
/// The initial value.
/// The value getter.
- /// The value setter.
+ /// The value setter (can be null if value is read-only).
/// The custom type attributes used to override the value editor logic or appearance (eg. instance of ).
- public CustomValueContainer(ScriptType valueType, object initialValue, GetDelegate getter, SetDelegate setter, object[] attributes = null)
+ public CustomValueContainer(ScriptType valueType, object initialValue, GetDelegate getter, SetDelegate setter = null, object[] attributes = null)
: this(valueType, getter, setter, attributes)
{
Add(initialValue);
@@ -89,6 +86,8 @@ namespace FlaxEditor.CustomEditors
{
if (instanceValues == null || instanceValues.Count != Count)
throw new ArgumentException();
+ if (_setter == null)
+ return;
for (int i = 0; i < Count; i++)
{
@@ -105,6 +104,8 @@ namespace FlaxEditor.CustomEditors
throw new ArgumentException();
if (values == null || values.Count != Count)
throw new ArgumentException();
+ if (_setter == null)
+ return;
for (int i = 0; i < Count; i++)
{
@@ -120,6 +121,8 @@ namespace FlaxEditor.CustomEditors
{
if (instanceValues == null || instanceValues.Count != Count)
throw new ArgumentException();
+ if (_setter == null)
+ return;
for (int i = 0; i < Count; i++)
{
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index e0c460d0c..b384b6515 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -8,7 +8,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEditor.Content;
-using FlaxEditor.Content.Import;
using FlaxEditor.Content.Settings;
using FlaxEditor.Content.Thumbnails;
using FlaxEditor.Modules;
@@ -154,12 +153,12 @@ namespace FlaxEditor
public ContentFindingModule ContentFinding;
///
- /// The scripts editing
+ /// The scripts editing.
///
public CodeEditingModule CodeEditing;
///
- /// The scripts documentation
+ /// The scripts documentation.
///
public CodeDocsModule CodeDocs;
@@ -179,7 +178,7 @@ namespace FlaxEditor
public ProjectCacheModule ProjectCache;
///
- /// The undo/redo
+ /// The undo/redo.
///
public EditorUndo Undo;
@@ -365,7 +364,7 @@ namespace FlaxEditor
{
foreach (var preview in activePreviews)
{
- if (preview == loadingPreview ||
+ if (preview == loadingPreview ||
(preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control))))
{
// Link it to the prefab preview to see it in the editor
@@ -726,8 +725,8 @@ namespace FlaxEditor
// Cleanup
Undo.Dispose();
- Surface.VisualScriptSurface.NodesCache.Clear();
- Surface.AnimGraphSurface.NodesCache.Clear();
+ foreach (var cache in Surface.VisjectSurface.NodesCache.Caches.ToArray())
+ cache.Clear();
Instance = null;
// Invoke new instance if need to open a project
@@ -797,7 +796,6 @@ namespace FlaxEditor
{
if (projectFilePath == null || !File.Exists(projectFilePath))
{
- // Error
MessageBox.Show("Missing project");
return;
}
@@ -933,6 +931,11 @@ namespace FlaxEditor
/// The .
///
Animation = 11,
+
+ ///
+ /// The .
+ ///
+ BehaviorTree = 12,
}
///
diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs
index 3e5d22eb0..8d6b0f9e2 100644
--- a/Source/Editor/GUI/AssetPicker.cs
+++ b/Source/Editor/GUI/AssetPicker.cs
@@ -482,8 +482,8 @@ namespace FlaxEditor.GUI
Focus();
});
if (_selected != null)
- {
- var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
+ {
+ var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
popup.ScrollToAndHighlightItemByName(selectedAssetName);
}
}
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs
index 7af36fae0..49a60a04e 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs
@@ -72,7 +72,7 @@ namespace FlaxEditor.GUI.ContextMenu
// Hide parent CM popups and set itself as child
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
}
-
+
///
public override bool OnMouseUp(Float2 location, MouseButton button)
{
diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
index 161b3f4ae..27878a763 100644
--- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
+++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
@@ -319,7 +319,9 @@ namespace FlaxEditor.GUI.Dialogs
protected override void OnShow()
{
// Auto cancel on lost focus
+#if !PLATFORM_LINUX
((WindowRootControl)Root).Window.LostFocus += OnCancel;
+#endif
base.OnShow();
}
diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs
index 07fc3ff0d..5054aee98 100644
--- a/Source/Editor/GUI/Dialogs/Dialog.cs
+++ b/Source/Editor/GUI/Dialogs/Dialog.cs
@@ -293,7 +293,7 @@ namespace FlaxEditor.GUI.Dialogs
if (Root != null)
{
bool shiftDown = Root.GetKey(KeyboardKeys.Shift);
- Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next);
+ Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next);
}
return true;
}
diff --git a/Source/Editor/GUI/Docking/DockHintWindow.cs b/Source/Editor/GUI/Docking/DockHintWindow.cs
index 52c5dcd3c..6e2353441 100644
--- a/Source/Editor/GUI/Docking/DockHintWindow.cs
+++ b/Source/Editor/GUI/Docking/DockHintWindow.cs
@@ -44,11 +44,11 @@ namespace FlaxEditor.GUI.Docking
var mousePos = window.MousePosition;
var previousSize = window.Size;
window.Restore();
- window.Position = FlaxEngine.Input.MouseScreenPosition - mousePos * window.Size / previousSize;
+ window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
}
// Calculate dragging offset and move window to the destination position
- var mouseScreenPosition = FlaxEngine.Input.MouseScreenPosition;
+ 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.
@@ -83,6 +83,7 @@ namespace FlaxEditor.GUI.Docking
// Enable hit window presentation
Proxy.Window.RenderingEnabled = true;
Proxy.Window.Show();
+ Proxy.Window.Focus();
}
///
@@ -113,7 +114,7 @@ namespace FlaxEditor.GUI.Docking
var window = _toMove.Window?.Window;
if (window == null)
return;
- var mouse = FlaxEngine.Input.MouseScreenPosition;
+ var mouse = Platform.MousePosition;
// Move base window
window.Position = mouse - _dragOffset;
@@ -193,7 +194,7 @@ namespace FlaxEditor.GUI.Docking
// Move window to the mouse position (with some offset for caption bar)
var window = (WindowRootControl)toMove.Root;
- var mouse = FlaxEngine.Input.MouseScreenPosition;
+ var mouse = Platform.MousePosition;
window.Window.Position = mouse - new Float2(8, 8);
// Get floating panel
@@ -244,7 +245,7 @@ namespace FlaxEditor.GUI.Docking
private void UpdateRects()
{
// Cache mouse position
- _mouse = FlaxEngine.Input.MouseScreenPosition;
+ _mouse = Platform.MousePosition;
// Check intersection with any dock panel
var uiMouse = _mouse;
@@ -270,15 +271,16 @@ namespace FlaxEditor.GUI.Docking
// Cache dock rectangles
var size = _rectDock.Size;
var offset = _rectDock.Location;
- float BorderMargin = 4.0f;
- float ProxyHintWindowsSize2 = Proxy.HintWindowsSize * 0.5f;
- float centerX = size.X * 0.5f;
- float centerY = size.Y * 0.5f;
- _rUpper = new Rectangle(centerX - ProxyHintWindowsSize2, BorderMargin, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
- _rBottom = new Rectangle(centerX - ProxyHintWindowsSize2, size.Y - Proxy.HintWindowsSize - BorderMargin, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
- _rLeft = new Rectangle(BorderMargin, centerY - ProxyHintWindowsSize2, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
- _rRight = new Rectangle(size.X - Proxy.HintWindowsSize - BorderMargin, centerY - ProxyHintWindowsSize2, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
- _rCenter = new Rectangle(centerX - ProxyHintWindowsSize2, centerY - ProxyHintWindowsSize2, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
+ 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;
@@ -428,7 +430,6 @@ namespace FlaxEditor.GUI.Docking
{
if (Window == null)
{
- // Create proxy window
var settings = CreateWindowSettings.Default;
settings.Title = "DockHint.Window";
settings.Size = initSize;
@@ -440,12 +441,10 @@ namespace FlaxEditor.GUI.Docking
settings.IsRegularWindow = false;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
- settings.ShowAfterFirstPaint = true;
+ settings.ShowAfterFirstPaint = false;
settings.IsTopmost = true;
Window = Platform.CreateWindow(ref settings);
-
- // Set opacity and background color
Window.Opacity = 0.6f;
Window.GUI.BackgroundColor = Style.Current.DragWindow;
}
@@ -465,7 +464,7 @@ namespace FlaxEditor.GUI.Docking
var settings = CreateWindowSettings.Default;
settings.Title = name;
- settings.Size = new Float2(HintWindowsSize);
+ settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
settings.AllowInput = false;
settings.AllowMaximize = false;
settings.AllowMinimize = false;
@@ -479,7 +478,6 @@ namespace FlaxEditor.GUI.Docking
settings.ShowAfterFirstPaint = false;
win = Platform.CreateWindow(ref settings);
-
win.Opacity = 0.6f;
win.GUI.BackgroundColor = Style.Current.DragWindow;
}
diff --git a/Source/Editor/GUI/Drag/DragScripts.cs b/Source/Editor/GUI/Drag/DragScripts.cs
index e72d28479..875875ef3 100644
--- a/Source/Editor/GUI/Drag/DragScripts.cs
+++ b/Source/Editor/GUI/Drag/DragScripts.cs
@@ -58,7 +58,6 @@ namespace FlaxEditor.GUI.Drag
{
if (item == null)
throw new ArgumentNullException();
-
return new DragDataText(DragPrefix + item.ID.ToString("N"));
}
@@ -71,11 +70,9 @@ namespace FlaxEditor.GUI.Drag
{
if (items == null)
throw new ArgumentNullException();
-
string text = DragPrefix;
foreach (var item in items)
text += item.ID.ToString("N") + '\n';
-
return new DragDataText(text);
}
@@ -83,9 +80,7 @@ namespace FlaxEditor.GUI.Drag
/// Tries to parse the drag data.
///
/// The data.
- ///
- /// Gathered objects or empty IEnumerable if cannot get any valid.
- ///
+ /// Gathered objects or empty IEnumerable if cannot get any valid.
public override IEnumerable