Merge branch 'master' into visject_grid_snap
Merged master.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -33,4 +33,3 @@ public class %class% : Script
|
||||
// Here you can add code that needs to be called every frame
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -3,7 +3,8 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 7,
|
||||
"Build": 6401
|
||||
"Revision": 0,
|
||||
"Build": 6404
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<a href="https://flaxengine.com/discord"><img src="https://discordapp.com/api/guilds/437989205315158016/widget.png"/></a>
|
||||
|
||||
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.
|
||||
|
||||
|
||||
292
Source/Editor/Content/AssetPickerValidator.cs
Normal file
292
Source/Editor/Content/AssetPickerValidator.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Content;
|
||||
|
||||
/// <summary>
|
||||
/// Manages and converts the selected content item to the appropriate types. Useful for drag operations.
|
||||
/// </summary>
|
||||
public class AssetPickerValidator : IContentItemOwner
|
||||
{
|
||||
private Asset _selected;
|
||||
private ContentItem _selectedItem;
|
||||
private ScriptType _type;
|
||||
private string _fileExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item.
|
||||
/// </summary>
|
||||
public ContentItem SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
if (_selectedItem == value)
|
||||
return;
|
||||
if (value == null)
|
||||
{
|
||||
if (_selected == null && _selectedItem is SceneItem)
|
||||
{
|
||||
// Deselect scene reference
|
||||
_selectedItem.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deselect
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is SceneItem item)
|
||||
{
|
||||
if (_selectedItem == item)
|
||||
return;
|
||||
if (!IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value to scene reference (cannot load asset because scene can be already loaded - duplicated ID issue)
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = null;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is AssetItem assetItem)
|
||||
{
|
||||
SelectedAsset = FlaxEngine.Content.LoadAsync(assetItem.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = value;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset identifier.
|
||||
/// </summary>
|
||||
public Guid SelectedID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_selected != null)
|
||||
return _selected.ID;
|
||||
if (_selectedItem is AssetItem assetItem)
|
||||
return assetItem.ID;
|
||||
return Guid.Empty;
|
||||
}
|
||||
set => SelectedItem = Editor.Instance.ContentDatabase.FindAsset(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected content item path.
|
||||
/// </summary>
|
||||
public string SelectedPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string path = _selectedItem?.Path ?? _selected?.Path;
|
||||
if (path != null)
|
||||
{
|
||||
// Convert into path relative to the project (cross-platform)
|
||||
var projectFolder = Globals.ProjectFolder;
|
||||
if (path.StartsWith(projectFolder))
|
||||
path = path.Substring(projectFolder.Length + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = StringUtils.IsRelative(value) ? Path.Combine(Globals.ProjectFolder, value) : value;
|
||||
SelectedItem = Editor.Instance.ContentDatabase.Find(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset object.
|
||||
/// </summary>
|
||||
public Asset SelectedAsset
|
||||
{
|
||||
get => _selected;
|
||||
set
|
||||
{
|
||||
// Check if value won't change
|
||||
if (value == _selected)
|
||||
return;
|
||||
|
||||
// Find item from content database and check it
|
||||
var item = value ? Editor.Instance.ContentDatabase.FindAsset(value.ID) : null;
|
||||
if (item != null && !IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = value;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the assets types that this picker accepts (it supports types derived from the given type). Use <see cref="ScriptType.Null"/> for generic file picker.
|
||||
/// </summary>
|
||||
public ScriptType AssetType
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
if (_type != value)
|
||||
{
|
||||
_type = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content items extensions filter. Null if unused.
|
||||
/// </summary>
|
||||
public string FileExtension
|
||||
{
|
||||
get => _fileExtension;
|
||||
set
|
||||
{
|
||||
if (_fileExtension != value)
|
||||
{
|
||||
_fileExtension = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selected item gets changed.
|
||||
/// </summary>
|
||||
public event Action SelectedItemChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for assets validation. Cane be used to implement a rule for assets to pick.
|
||||
/// </summary>
|
||||
public Func<ContentItem, bool> CheckValid;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether item is valid.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsValid(ContentItem item)
|
||||
{
|
||||
if (_fileExtension != null && !item.Path.EndsWith(_fileExtension))
|
||||
return false;
|
||||
if (CheckValid != null && !CheckValid(item))
|
||||
return false;
|
||||
if (_type == ScriptType.Null)
|
||||
return true;
|
||||
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
// Faster path for binary items (in-built)
|
||||
if (assetItem is BinaryAssetItem binaryItem)
|
||||
return _type.IsAssignableFrom(new ScriptType(binaryItem.Type));
|
||||
|
||||
// Type filter
|
||||
var type = TypeUtils.GetType(assetItem.TypeName);
|
||||
if (_type.IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
// Json assets can contain any type of the object defined by the C# type (data oriented design)
|
||||
if (assetItem is JsonAssetItem && (_type.Type == typeof(JsonAsset) || _type.Type == typeof(Asset)))
|
||||
return true;
|
||||
|
||||
// Special case for scene asset references
|
||||
if (_type.Type == typeof(SceneReference) && assetItem is SceneItem)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPickerValidator"/> class.
|
||||
/// </summary>
|
||||
public AssetPickerValidator()
|
||||
: this(new ScriptType(typeof(Asset)))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPickerValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="assetType">The assets types that this picker accepts.</param>
|
||||
public AssetPickerValidator(ScriptType assetType)
|
||||
{
|
||||
_type = assetType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when selected item gets changed.
|
||||
/// </summary>
|
||||
protected virtual void OnSelectedItemChanged()
|
||||
{
|
||||
SelectedItemChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDeleted(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemRenamed(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemReimported(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDispose(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call to remove reference from the selected item.
|
||||
/// </summary>
|
||||
public void OnDestroy()
|
||||
{
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -323,8 +323,6 @@ namespace FlaxEditor.Content
|
||||
/// <param name="value">The new path.</param>
|
||||
internal virtual void UpdatePath(string value)
|
||||
{
|
||||
Assert.AreNotEqual(Path, value);
|
||||
|
||||
// Set path
|
||||
Path = StringUtils.NormalizePath(value);
|
||||
FileName = System.IO.Path.GetFileName(value);
|
||||
|
||||
@@ -30,6 +30,12 @@ namespace FlaxEditor.Content
|
||||
return item is SceneItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AcceptsAsset(string typeName, string path)
|
||||
{
|
||||
return (typeName == Scene.AssetTypename || typeName == Scene.EditorPickerTypename) && path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
|
||||
@@ -406,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--);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,7 +513,6 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
for (int i = 0; i < checks; i++)
|
||||
{
|
||||
var request = _requests[i];
|
||||
|
||||
try
|
||||
{
|
||||
if (request.IsReady)
|
||||
@@ -529,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--);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String> files;
|
||||
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
|
||||
for (auto& file : files)
|
||||
{
|
||||
if (FileSystem::GetExtension(file).IsEmpty())
|
||||
{
|
||||
executableFile = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -249,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<String> files;
|
||||
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
|
||||
for (auto& file : files)
|
||||
{
|
||||
if (FileSystem::GetExtension(file).IsEmpty())
|
||||
{
|
||||
executableFile = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -157,6 +157,12 @@ namespace FlaxEditor.CustomEditors
|
||||
var values = _values;
|
||||
var presenter = _presenter;
|
||||
var layout = _layout;
|
||||
if (layout.Editors.Count != 1)
|
||||
{
|
||||
// There are more editors using the same layout so rebuild parent editor to prevent removing others editors
|
||||
_parent?.RebuildLayout();
|
||||
return;
|
||||
}
|
||||
var control = layout.ContainerControl;
|
||||
var parent = _parent;
|
||||
var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1;
|
||||
|
||||
@@ -225,8 +225,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
_actor = actor;
|
||||
|
||||
var showActorPicker = actor == null || ParentEditor.Values.All(x => x is not Cloth);
|
||||
if (showActorPicker)
|
||||
if (ParentEditor.Values.Any(x => x is Cloth))
|
||||
{
|
||||
// Cloth always picks the parent model mesh
|
||||
if (actor == null)
|
||||
{
|
||||
layout.Label("Cloth needs to be added as a child to model actor.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Actor reference picker
|
||||
_actorPicker = layout.Custom<FlaxObjectRefPickerControl>();
|
||||
@@ -242,7 +249,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
var model = staticModel.Model;
|
||||
if (model == null || model.WaitForLoaded())
|
||||
{
|
||||
layout.Label("No model.");
|
||||
return;
|
||||
}
|
||||
var materials = model.MaterialSlots;
|
||||
var lods = model.LODs;
|
||||
meshNames = new string[lods.Length][];
|
||||
@@ -267,7 +277,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
var skinnedModel = animatedModel.SkinnedModel;
|
||||
if (skinnedModel == null || skinnedModel.WaitForLoaded())
|
||||
{
|
||||
layout.Label("No model.");
|
||||
return;
|
||||
}
|
||||
var materials = skinnedModel.MaterialSlots;
|
||||
var lods = skinnedModel.LODs;
|
||||
meshNames = new string[lods.Length][];
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
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<MissingScript> 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<IUndoAction> 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<IUndoAction>(4);
|
||||
|
||||
var missingScripts = new List<MissingScript>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +695,41 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
private void SetType(ref ScriptType controlType, UIControl uiControl)
|
||||
{
|
||||
string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl);
|
||||
uiControl.Control = (Control)controlType.CreateInstance();
|
||||
|
||||
var oldControlType = (Control)uiControl.Control;
|
||||
var newControlType = (Control)controlType.CreateInstance();
|
||||
|
||||
// copy old control data to new control
|
||||
if (oldControlType != null)
|
||||
{
|
||||
newControlType.Visible = oldControlType.Visible;
|
||||
newControlType.Enabled = oldControlType.Enabled;
|
||||
newControlType.AutoFocus = oldControlType.AutoFocus;
|
||||
|
||||
newControlType.AnchorMin = oldControlType.AnchorMin;
|
||||
newControlType.AnchorMax = oldControlType.AnchorMax;
|
||||
newControlType.Offsets = oldControlType.Offsets;
|
||||
|
||||
newControlType.LocalLocation = oldControlType.LocalLocation;
|
||||
newControlType.Scale = oldControlType.Scale;
|
||||
newControlType.Bounds = oldControlType.Bounds;
|
||||
newControlType.Width = oldControlType.Width;
|
||||
newControlType.Height = oldControlType.Height;
|
||||
newControlType.Center = oldControlType.Center;
|
||||
newControlType.PivotRelative = oldControlType.PivotRelative;
|
||||
|
||||
newControlType.Pivot = oldControlType.Pivot;
|
||||
newControlType.Shear = oldControlType.Shear;
|
||||
newControlType.Rotation = oldControlType.Rotation;
|
||||
}
|
||||
if (oldControlType is ContainerControl oldContainer && newControlType is ContainerControl newContainer)
|
||||
{
|
||||
newContainer.CullChildren = oldContainer.CullChildren;
|
||||
newContainer.ClipChildren = oldContainer.ClipChildren;
|
||||
}
|
||||
|
||||
uiControl.Control = newControlType;
|
||||
|
||||
if (uiControl.Name.StartsWith(previousName))
|
||||
{
|
||||
string newName = controlType.Name + uiControl.Name.Substring(previousName.Length);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
value = 0;
|
||||
|
||||
// If selected is single actor that has children, ask if apply layer to the sub objects as well
|
||||
if (Values.IsSingleObject && (int)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren)
|
||||
if (Values.IsSingleObject && (int)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren && !Editor.IsPlayMode)
|
||||
{
|
||||
var valueText = comboBox.SelectedItem;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
// Generic file picker
|
||||
assetType = ScriptType.Null;
|
||||
Picker.FileExtension = assetReference.TypeName;
|
||||
Picker.Validator.FileExtension = assetReference.TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -85,7 +85,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
Picker.AssetType = assetType;
|
||||
Picker.Validator.AssetType = assetType;
|
||||
Picker.Height = height;
|
||||
Picker.SelectedItemChanged += OnSelectedItemChanged;
|
||||
}
|
||||
@@ -95,15 +95,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (_isRefreshing)
|
||||
return;
|
||||
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
|
||||
SetValue(Picker.SelectedItem);
|
||||
SetValue(Picker.Validator.SelectedItem);
|
||||
else if (_valueType.Type == typeof(Guid))
|
||||
SetValue(Picker.SelectedID);
|
||||
SetValue(Picker.Validator.SelectedID);
|
||||
else if (_valueType.Type == typeof(SceneReference))
|
||||
SetValue(new SceneReference(Picker.SelectedID));
|
||||
SetValue(new SceneReference(Picker.Validator.SelectedID));
|
||||
else if (_valueType.Type == typeof(string))
|
||||
SetValue(Picker.SelectedPath);
|
||||
SetValue(Picker.Validator.SelectedPath);
|
||||
else
|
||||
SetValue(Picker.SelectedAsset);
|
||||
SetValue(Picker.Validator.SelectedAsset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -115,15 +115,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
_isRefreshing = true;
|
||||
if (Values[0] is AssetItem assetItem)
|
||||
Picker.SelectedItem = assetItem;
|
||||
Picker.Validator.SelectedItem = assetItem;
|
||||
else if (Values[0] is Guid guid)
|
||||
Picker.SelectedID = guid;
|
||||
Picker.Validator.SelectedID = guid;
|
||||
else if (Values[0] is SceneReference sceneAsset)
|
||||
Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
Picker.Validator.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
else if (Values[0] is string path)
|
||||
Picker.SelectedPath = path;
|
||||
Picker.Validator.SelectedPath = path;
|
||||
else
|
||||
Picker.SelectedAsset = Values[0] as Asset;
|
||||
Picker.Validator.SelectedAsset = Values[0] as Asset;
|
||||
_isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -110,7 +113,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
// No support for different collections for now
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
if (HasDifferentTypes)
|
||||
return;
|
||||
|
||||
var size = Count;
|
||||
@@ -135,14 +138,43 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
spacing = collection.Spacing;
|
||||
}
|
||||
|
||||
var dragArea = layout.CustomContainer<DragAreaControl>();
|
||||
dragArea.CustomControl.Editor = this;
|
||||
dragArea.CustomControl.ElementType = ElementType;
|
||||
|
||||
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter
|
||||
// which scripts can be dragged over and dropped on this collection editor.
|
||||
var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute);
|
||||
if (assetReference != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetReference.TypeName))
|
||||
{
|
||||
}
|
||||
else if (assetReference.TypeName.Length > 1 && assetReference.TypeName[0] == '.')
|
||||
{
|
||||
dragArea.CustomControl.ElementType = ScriptType.Null;
|
||||
dragArea.CustomControl.FileExtension = assetReference.TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
var customType = TypeUtils.GetType(assetReference.TypeName);
|
||||
if (customType != ScriptType.Null)
|
||||
dragArea.CustomControl.ElementType = customType;
|
||||
else if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(assetReference.TypeName))
|
||||
Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for drag and drop filter.", assetReference.TypeName));
|
||||
else
|
||||
dragArea.CustomControl.ElementType = ScriptType.Void;
|
||||
}
|
||||
}
|
||||
|
||||
// Size
|
||||
if (_readOnly || (NotNullItems && size == 0))
|
||||
{
|
||||
layout.Label("Size", size.ToString());
|
||||
dragArea.Label("Size", size.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = layout.IntegerValue("Size");
|
||||
_size = dragArea.IntegerValue("Size");
|
||||
_size.IntValue.MinValue = 0;
|
||||
_size.IntValue.MaxValue = ushort.MaxValue;
|
||||
_size.IntValue.Value = size;
|
||||
@@ -152,7 +184,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Elements
|
||||
if (size > 0)
|
||||
{
|
||||
var panel = layout.VerticalPanel();
|
||||
var panel = dragArea.VerticalPanel();
|
||||
panel.Panel.BackgroundColor = _background;
|
||||
var elementType = ElementType;
|
||||
|
||||
@@ -212,37 +244,33 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Add/Remove buttons
|
||||
if (!_readOnly)
|
||||
{
|
||||
var area = layout.Space(20);
|
||||
var addButton = new Button(area.ContainerControl.Width - (16 + 16 + 2 + 2), 2, 16, 16)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add new item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl,
|
||||
Enabled = !NotNullItems || size > 0,
|
||||
};
|
||||
addButton.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
var panel = dragArea.HorizontalPanel();
|
||||
panel.Panel.Size = new Float2(0, 20);
|
||||
panel.Panel.Margin = new Margin(2);
|
||||
|
||||
Resize(Count + 1);
|
||||
};
|
||||
var removeButton = new Button(addButton.Right + 2, addButton.Y, 16, 16)
|
||||
{
|
||||
Text = "-",
|
||||
TooltipText = "Remove last item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl,
|
||||
Enabled = size > 0,
|
||||
};
|
||||
removeButton.Clicked += () =>
|
||||
var removeButton = panel.Button("-", "Remove last item");
|
||||
removeButton.Button.Size = new Float2(16, 16);
|
||||
removeButton.Button.Enabled = size > 0;
|
||||
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
removeButton.Button.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count - 1);
|
||||
};
|
||||
|
||||
var addButton = panel.Button("+", "Add new item");
|
||||
addButton.Button.Size = new Float2(16, 16);
|
||||
addButton.Button.Enabled = !NotNullItems || size > 0;
|
||||
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
addButton.Button.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count + 1);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,5 +397,232 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
return base.OnDirty(editor, value, token);
|
||||
}
|
||||
|
||||
private class DragAreaControl : VerticalPanel
|
||||
{
|
||||
private DragItems _dragItems;
|
||||
private DragActors _dragActors;
|
||||
private DragHandlers _dragHandlers;
|
||||
private AssetPickerValidator _pickerValidator;
|
||||
|
||||
public ScriptType ElementType
|
||||
{
|
||||
get => _pickerValidator?.AssetType ?? ScriptType.Null;
|
||||
set => _pickerValidator = new AssetPickerValidator(value);
|
||||
}
|
||||
|
||||
public CollectionEditor Editor { get; set; }
|
||||
|
||||
public string FileExtension
|
||||
{
|
||||
set => _pickerValidator.FileExtension = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
if (_dragHandlers is { HasValidDrag: true })
|
||||
{
|
||||
var area = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.FillRectangle(area, Color.Orange * 0.5f);
|
||||
Render2D.DrawRectangle(area, Color.Black);
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_pickerValidator.OnDestroy();
|
||||
}
|
||||
|
||||
private bool ValidateActors(ActorNode node)
|
||||
{
|
||||
return node.Actor.GetScript(ElementType.Type) || ElementType.Type.IsAssignableTo(typeof(Actor));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
if (_dragHandlers == null)
|
||||
{
|
||||
_dragItems = new DragItems(_pickerValidator.IsValid);
|
||||
_dragActors = new DragActors(ValidateActors);
|
||||
_dragHandlers = new DragHandlers
|
||||
{
|
||||
_dragActors,
|
||||
_dragItems
|
||||
};
|
||||
}
|
||||
return _dragHandlers.OnDragEnter(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
return _dragHandlers.Effect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
{
|
||||
_dragHandlers.OnDragDrop(null);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_dragHandlers.HasValidDrag)
|
||||
{
|
||||
if (_dragItems.HasValidDrag)
|
||||
{
|
||||
var list = Editor.CloneValues();
|
||||
if (list == null)
|
||||
{
|
||||
if (Editor.Values.Type.IsArray)
|
||||
{
|
||||
list = TypeUtils.CreateArrayInstance(Editor.Values.Type.GetElementType(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
list = Editor.Values.Type.CreateInstance() as IList;
|
||||
}
|
||||
}
|
||||
if (list.IsFixedSize)
|
||||
{
|
||||
var oldSize = list.Count;
|
||||
var newSize = list.Count + _dragItems.Objects.Count;
|
||||
var type = Editor.Values.Type.GetElementType();
|
||||
var array = TypeUtils.CreateArrayInstance(type, newSize);
|
||||
list.CopyTo(array, 0);
|
||||
|
||||
for (var i = oldSize; i < newSize; i++)
|
||||
{
|
||||
var validator = new AssetPickerValidator
|
||||
{
|
||||
FileExtension = _pickerValidator.FileExtension,
|
||||
AssetType = _pickerValidator.AssetType,
|
||||
SelectedItem = _dragItems.Objects[i - oldSize],
|
||||
};
|
||||
|
||||
if (typeof(AssetItem).IsAssignableFrom(ElementType.Type))
|
||||
array.SetValue(validator.SelectedItem, i);
|
||||
else if (ElementType.Type == typeof(Guid))
|
||||
array.SetValue(validator.SelectedID, i);
|
||||
else if (ElementType.Type == typeof(SceneReference))
|
||||
array.SetValue(new SceneReference(validator.SelectedID), i);
|
||||
else if (ElementType.Type == typeof(string))
|
||||
array.SetValue(validator.SelectedPath, i);
|
||||
else
|
||||
array.SetValue(validator.SelectedAsset, i);
|
||||
|
||||
validator.OnDestroy();
|
||||
}
|
||||
Editor.SetValue(array);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in _dragItems.Objects)
|
||||
{
|
||||
var validator = new AssetPickerValidator
|
||||
{
|
||||
FileExtension = _pickerValidator.FileExtension,
|
||||
AssetType = _pickerValidator.AssetType,
|
||||
SelectedItem = item,
|
||||
};
|
||||
|
||||
if (typeof(AssetItem).IsAssignableFrom(ElementType.Type))
|
||||
list.Add(validator.SelectedItem);
|
||||
else if (ElementType.Type == typeof(Guid))
|
||||
list.Add(validator.SelectedID);
|
||||
else if (ElementType.Type == typeof(SceneReference))
|
||||
list.Add(new SceneReference(validator.SelectedID));
|
||||
else if (ElementType.Type == typeof(string))
|
||||
list.Add(validator.SelectedPath);
|
||||
else
|
||||
list.Add(validator.SelectedAsset);
|
||||
|
||||
validator.OnDestroy();
|
||||
}
|
||||
Editor.SetValue(list);
|
||||
}
|
||||
}
|
||||
else if (_dragActors.HasValidDrag)
|
||||
{
|
||||
var list = Editor.CloneValues();
|
||||
if (list == null)
|
||||
{
|
||||
if (Editor.Values.Type.IsArray)
|
||||
{
|
||||
list = TypeUtils.CreateArrayInstance(Editor.Values.Type.GetElementType(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
list = Editor.Values.Type.CreateInstance() as IList;
|
||||
}
|
||||
}
|
||||
|
||||
if (list.IsFixedSize)
|
||||
{
|
||||
var oldSize = list.Count;
|
||||
var newSize = list.Count + _dragActors.Objects.Count;
|
||||
var type = Editor.Values.Type.GetElementType();
|
||||
var array = TypeUtils.CreateArrayInstance(type, newSize);
|
||||
list.CopyTo(array, 0);
|
||||
|
||||
for (var i = oldSize; i < newSize; i++)
|
||||
{
|
||||
var actor = _dragActors.Objects[i - oldSize].Actor;
|
||||
if (ElementType.Type.IsAssignableTo(typeof(Actor)))
|
||||
{
|
||||
array.SetValue(actor, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
array.SetValue(actor.GetScript(ElementType.Type), i);
|
||||
}
|
||||
}
|
||||
Editor.SetValue(array);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var actorNode in _dragActors.Objects)
|
||||
{
|
||||
if (ElementType.Type.IsAssignableTo(typeof(Actor)))
|
||||
{
|
||||
list.Add(actorNode.Actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(actorNode.Actor.GetScript(ElementType.Type));
|
||||
}
|
||||
}
|
||||
Editor.SetValue(list);
|
||||
}
|
||||
}
|
||||
|
||||
_dragHandlers.OnDragDrop(null);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,16 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var group = layout.Group("Entry");
|
||||
_group = group;
|
||||
|
||||
if (ParentEditor == null)
|
||||
if (ParentEditor == null || HasDifferentTypes)
|
||||
return;
|
||||
var entry = (ModelInstanceEntry)Values[0];
|
||||
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
|
||||
var materialLabel = new PropertyNameLabel("Material");
|
||||
materialLabel.TooltipText = "The mesh surface material used for the rendering.";
|
||||
if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance)
|
||||
var parentEditorValues = ParentEditor.ParentEditor?.Values;
|
||||
if (parentEditorValues?[0] is ModelInstanceActor modelInstance)
|
||||
{
|
||||
// TODO: store _modelInstance and _material in array for each selected model instance actor
|
||||
_entryIndex = entryIndex;
|
||||
_modelInstance = modelInstance;
|
||||
var slots = modelInstance.MaterialSlots;
|
||||
@@ -56,6 +58,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Create material picker
|
||||
var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase);
|
||||
for (var i = 1; i < parentEditorValues.Count; i++)
|
||||
materialValue.Add(_material);
|
||||
var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue);
|
||||
materialEditor.Values.SetDefaultValue(defaultValue);
|
||||
materialEditor.RefreshDefaultValue();
|
||||
@@ -72,14 +76,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return;
|
||||
_isRefreshing = true;
|
||||
var slots = _modelInstance.MaterialSlots;
|
||||
var material = _materialEditor.Picker.SelectedAsset as MaterialBase;
|
||||
var material = _materialEditor.Picker.Validator.SelectedAsset as MaterialBase;
|
||||
var defaultMaterial = GPUDevice.Instance.DefaultMaterial;
|
||||
var value = (ModelInstanceEntry)Values[0];
|
||||
var prevMaterial = value.Material;
|
||||
if (!material)
|
||||
{
|
||||
// Fallback to default material
|
||||
_materialEditor.Picker.SelectedAsset = defaultMaterial;
|
||||
_materialEditor.Picker.Validator.SelectedAsset = defaultMaterial;
|
||||
value.Material = defaultMaterial;
|
||||
}
|
||||
else if (material == slots[_entryIndex].Material)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -17,189 +18,21 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="Control" />
|
||||
/// <seealso cref="IContentItemOwner" />
|
||||
[HideInEditor]
|
||||
public class AssetPicker : Control, IContentItemOwner
|
||||
public class AssetPicker : Control
|
||||
{
|
||||
private const float DefaultIconSize = 64;
|
||||
private const float ButtonsOffset = 2;
|
||||
private const float ButtonsSize = 12;
|
||||
|
||||
private Asset _selected;
|
||||
private ContentItem _selectedItem;
|
||||
private ScriptType _type;
|
||||
private string _fileExtension;
|
||||
|
||||
private bool _isMouseDown;
|
||||
private Float2 _mouseDownPos;
|
||||
private Float2 _mousePos;
|
||||
private DragItems _dragOverElement;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item.
|
||||
/// The asset validator. Used to ensure only appropriate items can be picked.
|
||||
/// </summary>
|
||||
public ContentItem SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
if (_selectedItem == value)
|
||||
return;
|
||||
if (value == null)
|
||||
{
|
||||
if (_selected == null && _selectedItem is SceneItem)
|
||||
{
|
||||
// Deselect scene reference
|
||||
_selectedItem.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deselect
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is SceneItem item)
|
||||
{
|
||||
if (_selectedItem == item)
|
||||
return;
|
||||
if (!IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value to scene reference (cannot load asset because scene can be already loaded - duplicated ID issue)
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = null;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
else if (value is AssetItem assetItem)
|
||||
{
|
||||
SelectedAsset = FlaxEngine.Content.LoadAsync(assetItem.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = value;
|
||||
_selected = null;
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset identifier.
|
||||
/// </summary>
|
||||
public Guid SelectedID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_selected != null)
|
||||
return _selected.ID;
|
||||
if (_selectedItem is AssetItem assetItem)
|
||||
return assetItem.ID;
|
||||
return Guid.Empty;
|
||||
}
|
||||
set => SelectedItem = Editor.Instance.ContentDatabase.FindAsset(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected content item path.
|
||||
/// </summary>
|
||||
public string SelectedPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string path = _selectedItem?.Path ?? _selected?.Path;
|
||||
if (path != null)
|
||||
{
|
||||
// Convert into path relative to the project (cross-platform)
|
||||
var projectFolder = Globals.ProjectFolder;
|
||||
if (path.StartsWith(projectFolder))
|
||||
path = path.Substring(projectFolder.Length + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = StringUtils.IsRelative(value) ? Path.Combine(Globals.ProjectFolder, value) : value;
|
||||
SelectedItem = Editor.Instance.ContentDatabase.Find(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected asset object.
|
||||
/// </summary>
|
||||
public Asset SelectedAsset
|
||||
{
|
||||
get => _selected;
|
||||
set
|
||||
{
|
||||
// Check if value won't change
|
||||
if (value == _selected)
|
||||
return;
|
||||
|
||||
// Find item from content database and check it
|
||||
var item = value ? Editor.Instance.ContentDatabase.FindAsset(value.ID) : null;
|
||||
if (item != null && !IsValid(item))
|
||||
item = null;
|
||||
|
||||
// Change value
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = item;
|
||||
_selected = value;
|
||||
_selectedItem?.AddReference(this);
|
||||
OnSelectedItemChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the assets types that this picker accepts (it supports types derived from the given type). Use <see cref="ScriptType.Null"/> for generic file picker.
|
||||
/// </summary>
|
||||
public ScriptType AssetType
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
if (_type != value)
|
||||
{
|
||||
_type = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content items extensions filter. Null if unused.
|
||||
/// </summary>
|
||||
public string FileExtension
|
||||
{
|
||||
get => _fileExtension;
|
||||
set
|
||||
{
|
||||
if (_fileExtension != value)
|
||||
{
|
||||
_fileExtension = value;
|
||||
|
||||
// Auto deselect if the current value is invalid
|
||||
if (_selectedItem != null && !IsValid(_selectedItem))
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public AssetPickerValidator Validator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selected item gets changed.
|
||||
@@ -216,38 +49,6 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
public bool CanEdit = true;
|
||||
|
||||
private bool IsValid(ContentItem item)
|
||||
{
|
||||
if (_fileExtension != null && !item.Path.EndsWith(_fileExtension))
|
||||
return false;
|
||||
if (CheckValid != null && !CheckValid(item))
|
||||
return false;
|
||||
if (_type == ScriptType.Null)
|
||||
return true;
|
||||
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
// Faster path for binary items (in-built)
|
||||
if (assetItem is BinaryAssetItem binaryItem)
|
||||
return _type.IsAssignableFrom(new ScriptType(binaryItem.Type));
|
||||
|
||||
// Type filter
|
||||
var type = TypeUtils.GetType(assetItem.TypeName);
|
||||
if (_type.IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
// Json assets can contain any type of the object defined by the C# type (data oriented design)
|
||||
if (assetItem is JsonAssetItem && (_type.Type == typeof(JsonAsset) || _type.Type == typeof(Asset)))
|
||||
return true;
|
||||
|
||||
// Special case for scene asset references
|
||||
if (_type.Type == typeof(SceneReference) && assetItem is SceneItem)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPicker"/> class.
|
||||
/// </summary>
|
||||
@@ -264,7 +65,8 @@ namespace FlaxEditor.GUI
|
||||
public AssetPicker(ScriptType assetType, Float2 location)
|
||||
: base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize))
|
||||
{
|
||||
_type = assetType;
|
||||
Validator = new AssetPickerValidator(assetType);
|
||||
Validator.SelectedItemChanged += OnSelectedItemChanged;
|
||||
_mousePos = Float2.Minimum;
|
||||
}
|
||||
|
||||
@@ -275,10 +77,10 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
// Update tooltip
|
||||
string tooltip;
|
||||
if (_selectedItem is AssetItem assetItem)
|
||||
if (Validator.SelectedItem is AssetItem assetItem)
|
||||
tooltip = assetItem.NamePath;
|
||||
else
|
||||
tooltip = SelectedPath;
|
||||
tooltip = Validator.SelectedPath;
|
||||
TooltipText = tooltip;
|
||||
|
||||
SelectedItemChanged?.Invoke();
|
||||
@@ -289,37 +91,13 @@ namespace FlaxEditor.GUI
|
||||
// Do the drag drop operation if has selected element
|
||||
if (new Rectangle(Float2.Zero, Size).Contains(ref _mouseDownPos))
|
||||
{
|
||||
if (_selected != null)
|
||||
DoDragDrop(DragAssets.GetDragData(_selected));
|
||||
else if (_selectedItem != null)
|
||||
DoDragDrop(DragItems.GetDragData(_selectedItem));
|
||||
if (Validator.SelectedAsset != null)
|
||||
DoDragDrop(DragAssets.GetDragData(Validator.SelectedAsset));
|
||||
else if (Validator.SelectedItem != null)
|
||||
DoDragDrop(DragItems.GetDragData(Validator.SelectedItem));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDeleted(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemRenamed(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemReimported(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnItemDispose(ContentItem item)
|
||||
{
|
||||
// Deselect item
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
private Rectangle IconRect => new Rectangle(0, 0, Height, Height);
|
||||
|
||||
private Rectangle Button1Rect => new Rectangle(Height + ButtonsOffset, 0, ButtonsSize, ButtonsSize);
|
||||
@@ -341,10 +119,10 @@ namespace FlaxEditor.GUI
|
||||
if (CanEdit)
|
||||
Render2D.DrawSprite(style.ArrowDown, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
if (_selectedItem != null)
|
||||
if (Validator.SelectedItem != null)
|
||||
{
|
||||
// Draw item preview
|
||||
_selectedItem.DrawThumbnail(ref iconRect);
|
||||
Validator.SelectedItem.DrawThumbnail(ref iconRect);
|
||||
|
||||
// Draw buttons
|
||||
if (CanEdit)
|
||||
@@ -363,7 +141,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
Render2D.DrawText(
|
||||
style.FontSmall,
|
||||
_selectedItem.ShortName,
|
||||
Validator.SelectedItem.ShortName,
|
||||
new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
@@ -371,7 +149,7 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
// Check if has no item but has an asset (eg. virtual asset)
|
||||
else if (_selected)
|
||||
else if (Validator.SelectedAsset)
|
||||
{
|
||||
// Draw remove button
|
||||
Render2D.DrawSprite(style.Cross, button3Rect, button3Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||
@@ -380,8 +158,8 @@ namespace FlaxEditor.GUI
|
||||
float sizeForTextLeft = Width - button1Rect.Right;
|
||||
if (sizeForTextLeft > 30)
|
||||
{
|
||||
var name = _selected.GetType().Name;
|
||||
if (_selected.IsVirtual)
|
||||
var name = Validator.SelectedAsset.GetType().Name;
|
||||
if (Validator.SelectedAsset.IsVirtual)
|
||||
name += " (virtual)";
|
||||
Render2D.DrawText(
|
||||
style.FontSmall,
|
||||
@@ -407,9 +185,7 @@ namespace FlaxEditor.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_selectedItem?.RemoveReference(this);
|
||||
_selectedItem = null;
|
||||
_selected = null;
|
||||
Validator.OnDestroy();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
@@ -463,57 +239,57 @@ namespace FlaxEditor.GUI
|
||||
// Buttons logic
|
||||
if (!CanEdit)
|
||||
{
|
||||
if (Button1Rect.Contains(location) && _selectedItem != null)
|
||||
if (Button1Rect.Contains(location) && Validator.SelectedItem != null)
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||
}
|
||||
}
|
||||
else if (Button1Rect.Contains(location))
|
||||
{
|
||||
Focus();
|
||||
if (_type != ScriptType.Null)
|
||||
if (Validator.AssetType != ScriptType.Null)
|
||||
{
|
||||
// Show asset picker popup
|
||||
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
|
||||
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
SelectedItem = item;
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (_selected != null)
|
||||
if (Validator.SelectedAsset != null)
|
||||
{
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(Validator.SelectedAsset.Path);
|
||||
popup.ScrollToAndHighlightItemByName(selectedAssetName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show content item picker popup
|
||||
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
|
||||
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
|
||||
{
|
||||
SelectedItem = item;
|
||||
Validator.SelectedItem = item;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
if (_selectedItem != null)
|
||||
if (Validator.SelectedItem != null)
|
||||
{
|
||||
popup.ScrollToAndHighlightItemByName(_selectedItem.ShortName);
|
||||
popup.ScrollToAndHighlightItemByName(Validator.SelectedItem.ShortName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_selected != null || _selectedItem != null)
|
||||
else if (Validator.SelectedAsset != null || Validator.SelectedItem != null)
|
||||
{
|
||||
if (Button2Rect.Contains(location) && _selectedItem != null)
|
||||
if (Button2Rect.Contains(location) && Validator.SelectedItem != null)
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||
}
|
||||
else if (Button3Rect.Contains(location))
|
||||
{
|
||||
// Deselect asset
|
||||
Focus();
|
||||
SelectedItem = null;
|
||||
Validator.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,10 +316,10 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
Focus();
|
||||
|
||||
if (_selectedItem != null && IconRect.Contains(location))
|
||||
if (Validator.SelectedItem != null && IconRect.Contains(location))
|
||||
{
|
||||
// Open it
|
||||
Editor.Instance.ContentEditing.Open(_selectedItem);
|
||||
Editor.Instance.ContentEditing.Open(Validator.SelectedItem);
|
||||
}
|
||||
|
||||
// Handled
|
||||
@@ -557,7 +333,7 @@ namespace FlaxEditor.GUI
|
||||
|
||||
// Check if drop asset
|
||||
if (_dragOverElement == null)
|
||||
_dragOverElement = new DragItems(IsValid);
|
||||
_dragOverElement = new DragItems(Validator.IsValid);
|
||||
if (CanEdit && _dragOverElement.OnDragEnter(data))
|
||||
{
|
||||
}
|
||||
@@ -590,7 +366,7 @@ namespace FlaxEditor.GUI
|
||||
if (CanEdit && _dragOverElement.HasValidDrag)
|
||||
{
|
||||
// Select element
|
||||
SelectedItem = _dragOverElement.Objects[0];
|
||||
Validator.SelectedItem = _dragOverElement.Objects[0];
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
/// </summary>
|
||||
public DialogResult Result => _result;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the size of the dialog.
|
||||
/// </summary>
|
||||
public Float2 DialogSize => _dialogSize;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dialog"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -465,36 +465,47 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
if (Parent.Parent is SplitPanel splitter)
|
||||
{
|
||||
// Check if has any child panels
|
||||
var childPanel = new List<DockPanel>(_childPanels);
|
||||
for (int i = 0; i < childPanel.Count; i++)
|
||||
// Check if there is another nested dock panel inside this dock panel and extract it here
|
||||
var childPanels = _childPanels.ToArray();
|
||||
if (childPanels.Length != 0)
|
||||
{
|
||||
// Undock all tabs
|
||||
var panel = childPanel[i];
|
||||
int count = panel.TabsCount;
|
||||
while (count-- > 0)
|
||||
// Move tabs from child panels into this one
|
||||
DockWindow selectedTab = null;
|
||||
foreach (var childPanel in childPanels)
|
||||
{
|
||||
panel.GetTab(0).Close();
|
||||
var childPanelTabs = childPanel.Tabs.ToArray();
|
||||
for (var i = 0; i < childPanelTabs.Length; i++)
|
||||
{
|
||||
var childPanelTab = childPanelTabs[i];
|
||||
if (selectedTab == null && childPanelTab.IsSelected)
|
||||
selectedTab = childPanelTab;
|
||||
childPanel.UndockWindow(childPanelTab);
|
||||
AddTab(childPanelTab, false);
|
||||
}
|
||||
}
|
||||
if (selectedTab != null)
|
||||
SelectTab(selectedTab);
|
||||
}
|
||||
|
||||
// Unlink splitter
|
||||
var splitterParent = splitter.Parent;
|
||||
Assert.IsNotNull(splitterParent);
|
||||
splitter.Parent = null;
|
||||
|
||||
// Move controls from second split panel to the split panel parent
|
||||
var scrPanel = Parent == splitter.Panel2 ? splitter.Panel1 : splitter.Panel2;
|
||||
var srcPanelChildrenCount = scrPanel.ChildrenCount;
|
||||
for (int i = srcPanelChildrenCount - 1; i >= 0 && scrPanel.ChildrenCount > 0; i--)
|
||||
else
|
||||
{
|
||||
scrPanel.GetChild(i).Parent = splitterParent;
|
||||
}
|
||||
Assert.IsTrue(scrPanel.ChildrenCount == 0);
|
||||
Assert.IsTrue(splitterParent.ChildrenCount == srcPanelChildrenCount);
|
||||
// Unlink splitter
|
||||
var splitterParent = splitter.Parent;
|
||||
Assert.IsNotNull(splitterParent);
|
||||
splitter.Parent = null;
|
||||
|
||||
// Delete
|
||||
splitter.Dispose();
|
||||
// Move controls from second split panel to the split panel parent
|
||||
var scrPanel = Parent == splitter.Panel2 ? splitter.Panel1 : splitter.Panel2;
|
||||
var srcPanelChildrenCount = scrPanel.ChildrenCount;
|
||||
for (int i = srcPanelChildrenCount - 1; i >= 0 && scrPanel.ChildrenCount > 0; i--)
|
||||
{
|
||||
scrPanel.GetChild(i).Parent = splitterParent;
|
||||
}
|
||||
Assert.IsTrue(scrPanel.ChildrenCount == 0);
|
||||
Assert.IsTrue(splitterParent.ChildrenCount == srcPanelChildrenCount);
|
||||
|
||||
// Delete
|
||||
splitter.Dispose();
|
||||
}
|
||||
}
|
||||
else if (!IsMaster)
|
||||
{
|
||||
@@ -582,19 +593,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// Adds the tab.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to insert as a tab.</param>
|
||||
protected virtual void AddTab(DockWindow window)
|
||||
/// <param name="autoSelect">True if auto-select newly added tab.</param>
|
||||
protected virtual void AddTab(DockWindow window, bool autoSelect = true)
|
||||
{
|
||||
// Dock
|
||||
_tabs.Add(window);
|
||||
window.ParentDockPanel = this;
|
||||
|
||||
// Select tab
|
||||
SelectTab(window);
|
||||
if (autoSelect)
|
||||
SelectTab(window);
|
||||
}
|
||||
|
||||
private void CreateTabsProxy()
|
||||
{
|
||||
// Check if has no tabs proxy created
|
||||
if (_tabsProxy == null)
|
||||
{
|
||||
// Create proxy and make set simple full dock
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MaximumSize = new Float2(4096);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
settings.SupportsTransparency = false;
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input
|
||||
[HideInEditor]
|
||||
public class ColorValueBox : Control
|
||||
{
|
||||
private bool _isMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate function used for the color picker events handling.
|
||||
/// </summary>
|
||||
@@ -134,11 +136,22 @@ namespace FlaxEditor.GUI.Input
|
||||
Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
_isMouseDown = true;
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
Focus();
|
||||
OnSubmit();
|
||||
if (_isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
Focus();
|
||||
OnSubmit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace FlaxEditor.GUI.Tabs
|
||||
[HideInEditor]
|
||||
public class Tab : ContainerControl
|
||||
{
|
||||
internal Tabs _selectedInTabs;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
@@ -86,5 +88,25 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
return new Tabs.TabHeader((Tabs)Parent, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnParentChangedInternal()
|
||||
{
|
||||
if (_selectedInTabs != null)
|
||||
_selectedInTabs.SelectedTab = null;
|
||||
|
||||
base.OnParentChangedInternal();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
if (_selectedInTabs != null)
|
||||
_selectedInTabs.SelectedTab = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,8 @@ namespace FlaxEditor.GUI.Tabs
|
||||
get => _autoTabsSizeAuto;
|
||||
set
|
||||
{
|
||||
if (_autoTabsSizeAuto == value)
|
||||
return;
|
||||
_autoTabsSizeAuto = value;
|
||||
PerformLayout();
|
||||
}
|
||||
@@ -204,11 +206,11 @@ namespace FlaxEditor.GUI.Tabs
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
if (_orientation == value)
|
||||
return;
|
||||
_orientation = value;
|
||||
|
||||
if (UseScroll)
|
||||
TabsPanel.ScrollBars = _orientation == Orientation.Horizontal ? ScrollBars.Horizontal : ScrollBars.Vertical;
|
||||
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
@@ -261,7 +263,12 @@ namespace FlaxEditor.GUI.Tabs
|
||||
// Check if index will change
|
||||
if (_selectedIndex != index)
|
||||
{
|
||||
SelectedTab?.OnDeselected();
|
||||
var prev = SelectedTab;
|
||||
if (prev != null)
|
||||
{
|
||||
prev._selectedInTabs = null;
|
||||
prev.OnDeselected();
|
||||
}
|
||||
_selectedIndex = index;
|
||||
PerformLayout();
|
||||
OnSelectedTabChanged();
|
||||
@@ -340,8 +347,13 @@ namespace FlaxEditor.GUI.Tabs
|
||||
/// </summary>
|
||||
protected virtual void OnSelectedTabChanged()
|
||||
{
|
||||
var selectedTab = SelectedTab;
|
||||
SelectedTabChanged?.Invoke(this);
|
||||
SelectedTab?.OnSelected();
|
||||
if (selectedTab != null)
|
||||
{
|
||||
selectedTab._selectedInTabs = this;
|
||||
selectedTab.OnSelected();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -402,6 +414,14 @@ namespace FlaxEditor.GUI.Tabs
|
||||
tabHeader.Size = tabsSize;
|
||||
}
|
||||
}
|
||||
else if (UseScroll)
|
||||
{
|
||||
// If scroll bar is visible it covers part of the tab header so include this in tab size to improve usability
|
||||
if (_orientation == Orientation.Horizontal && TabsPanel.HScrollBar.Visible)
|
||||
tabsSize.Y += TabsPanel.HScrollBar.Height;
|
||||
else if (_orientation == Orientation.Vertical && TabsPanel.VScrollBar.Visible)
|
||||
tabsSize.X += TabsPanel.VScrollBar.Width;
|
||||
}
|
||||
|
||||
// Fit the tabs panel
|
||||
TabsPanel.Size = _orientation == Orientation.Horizontal
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
if (AssetID == value?.ID)
|
||||
return;
|
||||
AssetID = value?.ID ?? Guid.Empty;
|
||||
_picker.SelectedAsset = value;
|
||||
_picker.Validator.SelectedAsset = value;
|
||||
OnAssetChanged();
|
||||
Timeline?.MarkAsEdited();
|
||||
}
|
||||
@@ -63,10 +63,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
|
||||
private void OnPickerSelectedItemChanged()
|
||||
{
|
||||
if (Asset == (TAsset)_picker.SelectedAsset)
|
||||
if (Asset == (TAsset)_picker.Validator.SelectedAsset)
|
||||
return;
|
||||
using (new TrackUndoBlock(this))
|
||||
Asset = (TAsset)_picker.SelectedAsset;
|
||||
Asset = (TAsset)_picker.Validator.SelectedAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -776,11 +776,20 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Check if mouse hits arrow
|
||||
if (_mouseOverArrow && HasAnyVisibleChild)
|
||||
{
|
||||
// Toggle open state
|
||||
if (_opened)
|
||||
Collapse();
|
||||
if (ParentTree.Root.GetKey(KeyboardKeys.Alt))
|
||||
{
|
||||
if (_opened)
|
||||
CollapseAll();
|
||||
else
|
||||
ExpandAll();
|
||||
}
|
||||
else
|
||||
Expand();
|
||||
{
|
||||
if (_opened)
|
||||
Collapse();
|
||||
else
|
||||
Expand();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if mouse hits bar
|
||||
|
||||
@@ -11,6 +11,11 @@ namespace FlaxEditor.Gizmo
|
||||
[HideInEditor]
|
||||
public interface IGizmoOwner
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the gizmos collection.
|
||||
/// </summary>
|
||||
FlaxEditor.Viewport.EditorViewport Viewport { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gizmos collection.
|
||||
/// </summary>
|
||||
|
||||
@@ -162,10 +162,23 @@ namespace FlaxEditor.Gizmo
|
||||
|
||||
// Scale gizmo to fit on-screen
|
||||
Vector3 position = Position;
|
||||
Vector3 vLength = Owner.ViewPosition - position;
|
||||
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
|
||||
_screenScale = (float)(vLength.Length / GizmoScaleFactor * gizmoSize);
|
||||
|
||||
if (Owner.Viewport.UseOrthographicProjection)
|
||||
{
|
||||
//[hack] this is far form ideal the View Position is in wrong location, any think using the View Position will have problem
|
||||
//the camera system needs rewrite the to be a camera on springarm, similar how the ArcBallCamera is handled
|
||||
//the ortho projection cannot exist with fps camera because there is no
|
||||
// - focus point to calculate correct View Position with Orthographic Scale as a reference and Orthographic Scale from View Position
|
||||
// with make the camera jump
|
||||
// - and deaph so w and s movment in orto mode moves the cliping plane now
|
||||
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
|
||||
_screenScale = gizmoSize * (50 * Owner.Viewport.OrthographicScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 vLength = Owner.ViewPosition - position;
|
||||
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
|
||||
_screenScale = (float)(vLength.Length / GizmoScaleFactor * gizmoSize);
|
||||
}
|
||||
// Setup world
|
||||
Quaternion orientation = GetSelectedObject(0).Orientation;
|
||||
_gizmoWorld = new Transform(position, orientation, new Float3(_screenScale));
|
||||
|
||||
@@ -513,7 +513,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
}
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (auto& win : WindowsManager::Windows)
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
windows.Add(WindowsManager::Windows);
|
||||
for (Window* win : windows)
|
||||
{
|
||||
if (win->IsVisible())
|
||||
win->OnUpdate(deltaTime);
|
||||
|
||||
@@ -330,14 +330,15 @@ bool ManagedEditor::CanReloadScripts()
|
||||
|
||||
bool ManagedEditor::CanAutoBuildCSG()
|
||||
{
|
||||
if (!ManagedEditorOptions.AutoRebuildCSG)
|
||||
return false;
|
||||
|
||||
// Skip calls from non-managed thread (eg. physics worker)
|
||||
if (!MCore::Thread::IsAttached())
|
||||
return false;
|
||||
|
||||
if (!HasManagedInstance())
|
||||
return false;
|
||||
if (!ManagedEditorOptions.AutoRebuildCSG)
|
||||
return false;
|
||||
if (Internal_CanAutoBuildCSG == nullptr)
|
||||
{
|
||||
Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG");
|
||||
@@ -348,14 +349,15 @@ bool ManagedEditor::CanAutoBuildCSG()
|
||||
|
||||
bool ManagedEditor::CanAutoBuildNavMesh()
|
||||
{
|
||||
if (!ManagedEditorOptions.AutoRebuildNavMesh)
|
||||
return false;
|
||||
|
||||
// Skip calls from non-managed thread (eg. physics worker)
|
||||
if (!MCore::Thread::IsAttached())
|
||||
return false;
|
||||
|
||||
if (!HasManagedInstance())
|
||||
return false;
|
||||
if (!ManagedEditorOptions.AutoRebuildNavMesh)
|
||||
return false;
|
||||
if (Internal_CanAutoBuildNavMesh == nullptr)
|
||||
{
|
||||
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh");
|
||||
|
||||
@@ -649,8 +649,6 @@ namespace FlaxEditor.Modules
|
||||
// Special case for folders
|
||||
if (item is ContentFolder folder)
|
||||
{
|
||||
// TODO: maybe don't remove folders recursive but at once?
|
||||
|
||||
// Delete all children
|
||||
if (folder.Children.Count > 0)
|
||||
{
|
||||
@@ -664,6 +662,9 @@ namespace FlaxEditor.Modules
|
||||
// Remove directory
|
||||
if (deletedByUser && Directory.Exists(path))
|
||||
{
|
||||
// Flush files removal before removing folder (loaded assets remove file during object destruction in Asset::OnDeleteObject)
|
||||
FlaxEngine.Scripting.FlushRemovedObjects();
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
@@ -810,10 +811,9 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
if (node == null)
|
||||
return;
|
||||
|
||||
// Temporary data
|
||||
var folder = node.Folder;
|
||||
var path = folder.Path;
|
||||
var canHaveAssets = node.CanHaveAssets;
|
||||
|
||||
if (_isDuringFastSetup)
|
||||
{
|
||||
@@ -832,20 +832,38 @@ namespace FlaxEditor.Modules
|
||||
var child = folder.Children[i];
|
||||
if (!child.Exists)
|
||||
{
|
||||
// Send info
|
||||
// Item doesn't exist anymore
|
||||
Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed"));
|
||||
|
||||
// Destroy it
|
||||
Delete(child, false);
|
||||
|
||||
i--;
|
||||
}
|
||||
else if (canHaveAssets && child is AssetItem childAsset)
|
||||
{
|
||||
// Check if asset type doesn't match the item proxy (eg. item reimported as Material Instance instead of Material)
|
||||
if (FlaxEngine.Content.GetAssetInfo(child.Path, out var assetInfo))
|
||||
{
|
||||
bool changed = assetInfo.ID != childAsset.ID;
|
||||
if (!changed && assetInfo.TypeName != childAsset.TypeName)
|
||||
{
|
||||
// Use proxy check (eg. scene asset might accept different typename than AssetInfo reports)
|
||||
var proxy = GetAssetProxy(childAsset.TypeName, child.Path);
|
||||
if (proxy == null)
|
||||
proxy = GetAssetProxy(assetInfo.TypeName, child.Path);
|
||||
changed = !proxy.AcceptsAsset(assetInfo.TypeName, child.Path);
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
OnAssetTypeInfoChanged(childAsset, ref assetInfo);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find files
|
||||
var files = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
if (node.CanHaveAssets)
|
||||
if (canHaveAssets)
|
||||
{
|
||||
LoadAssets(node, files);
|
||||
}
|
||||
@@ -1134,17 +1152,19 @@ namespace FlaxEditor.Modules
|
||||
|
||||
RebuildInternal();
|
||||
|
||||
Editor.ContentImporting.ImportFileEnd += ContentImporting_ImportFileDone;
|
||||
Editor.ContentImporting.ImportFileEnd += (obj, failed) =>
|
||||
{
|
||||
var path = obj.ResultUrl;
|
||||
if (!failed)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileDone(path));
|
||||
};
|
||||
_enableEvents = true;
|
||||
}
|
||||
|
||||
private void ContentImporting_ImportFileDone(IFileEntryAction obj, bool failed)
|
||||
private void OnImportFileDone(string path)
|
||||
{
|
||||
if (failed)
|
||||
return;
|
||||
|
||||
// Check if already has that element
|
||||
var item = Find(obj.ResultUrl);
|
||||
var item = Find(path);
|
||||
if (item is BinaryAssetItem binaryAssetItem)
|
||||
{
|
||||
// Get asset info from the registry (content layer will update cache it just after import)
|
||||
@@ -1154,19 +1174,8 @@ namespace FlaxEditor.Modules
|
||||
// For eg. change texture to sprite atlas on reimport
|
||||
if (binaryAssetItem.TypeName != assetInfo.TypeName)
|
||||
{
|
||||
// Asset type has been changed!
|
||||
Editor.LogWarning(string.Format("Asset \'{0}\' changed type from {1} to {2}", item.Path, binaryAssetItem.TypeName, assetInfo.TypeName));
|
||||
Editor.Windows.CloseAllEditors(item);
|
||||
|
||||
// Remove this item from the database and some related data
|
||||
var toRefresh = binaryAssetItem.ParentFolder;
|
||||
binaryAssetItem.Dispose();
|
||||
toRefresh.Children.Remove(binaryAssetItem);
|
||||
if (!binaryAssetItem.HasDefaultThumbnail)
|
||||
{
|
||||
// Delete old thumbnail and remove it from the cache
|
||||
Editor.Instance.Thumbnails.DeletePreview(binaryAssetItem);
|
||||
}
|
||||
OnAssetTypeInfoChanged(binaryAssetItem, ref assetInfo);
|
||||
|
||||
// Refresh the parent folder to find the new asset (it should have different type or some other format)
|
||||
RefreshFolder(toRefresh, false);
|
||||
@@ -1183,6 +1192,23 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAssetTypeInfoChanged(AssetItem assetItem, ref AssetInfo assetInfo)
|
||||
{
|
||||
// Asset type has been changed!
|
||||
Editor.LogWarning(string.Format("Asset \'{0}\' changed type from {1} to {2}", assetItem.Path, assetItem.TypeName, assetInfo.TypeName));
|
||||
Editor.Windows.CloseAllEditors(assetItem);
|
||||
|
||||
// Remove this item from the database and some related data
|
||||
assetItem.Dispose();
|
||||
assetItem.ParentFolder.Children.Remove(assetItem);
|
||||
|
||||
// Delete old thumbnail and remove it from the cache
|
||||
if (!assetItem.HasDefaultThumbnail)
|
||||
{
|
||||
Editor.Instance.Thumbnails.DeletePreview(assetItem);
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnDirectoryEvent(MainContentTreeNode node, FileSystemEventArgs e)
|
||||
{
|
||||
// Ensure to be ready for external events
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace FlaxEditor.Modules
|
||||
public event Action ImportingQueueBegin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when file is being imported.
|
||||
/// Occurs when file is being imported. Can be called on non-main thread.
|
||||
/// </summary>
|
||||
public event Action<IFileEntryAction> ImportFileBegin;
|
||||
|
||||
@@ -67,12 +67,12 @@ namespace FlaxEditor.Modules
|
||||
public delegate void ImportFileEndDelegate(IFileEntryAction entry, bool failed);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when file importing end.
|
||||
/// Occurs when file importing end. Can be called on non-main thread.
|
||||
/// </summary>
|
||||
public event ImportFileEndDelegate ImportFileEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when assets importing ends.
|
||||
/// Occurs when assets importing ends. Can be called on non-main thread.
|
||||
/// </summary>
|
||||
public event Action ImportingQueueEnd;
|
||||
|
||||
|
||||
@@ -266,6 +266,19 @@ namespace FlaxEditor.Modules
|
||||
Editor.StateMachine.ChangingScenesState.LoadScene(sceneId, additive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload all loaded scenes.
|
||||
/// </summary>
|
||||
public void ReloadScenes()
|
||||
{
|
||||
foreach (var scene in Level.Scenes)
|
||||
{
|
||||
var sceneId = scene.ID;
|
||||
if (!Level.UnloadScene(scene))
|
||||
Level.LoadScene(sceneId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes scene (async).
|
||||
/// </summary>
|
||||
|
||||
@@ -147,6 +147,17 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
}
|
||||
if (key != null)
|
||||
xml.TryGetValue(key, out text);
|
||||
|
||||
// Customize tooltips for properties to be more human-readable in UI
|
||||
if (text != null && memberType.HasFlag(MemberTypes.Property) && text.StartsWith("Gets or sets ", StringComparison.Ordinal))
|
||||
{
|
||||
text = text.Substring(13);
|
||||
unsafe
|
||||
{
|
||||
fixed (char* e = text)
|
||||
e[0] = char.ToUpper(e[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -410,9 +410,11 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
base.OnUpdate();
|
||||
|
||||
// Automatic project files generation after workspace modifications
|
||||
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty)
|
||||
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty && !ScriptsBuilder.IsCompiling)
|
||||
{
|
||||
Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
// Try to delay generation when a lot of files are added at once
|
||||
if (ScriptsBuilder.IsSourceDirtyFor(TimeSpan.FromMilliseconds(150)))
|
||||
Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI;
|
||||
@@ -11,7 +10,6 @@ using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Progress.Handlers;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Windows;
|
||||
@@ -41,6 +39,7 @@ namespace FlaxEditor.Modules
|
||||
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
|
||||
|
||||
private ContextMenuButton _menuFileSaveScenes;
|
||||
private ContextMenuButton _menuFileReloadScenes;
|
||||
private ContextMenuButton _menuFileCloseScenes;
|
||||
private ContextMenuButton _menuFileOpenScriptsProject;
|
||||
private ContextMenuButton _menuFileGenerateScriptsProjectFiles;
|
||||
@@ -208,6 +207,7 @@ namespace FlaxEditor.Modules
|
||||
_toolStripScale.Checked = gizmoMode == TransformGizmoBase.Mode.Scale;
|
||||
//
|
||||
_toolStripBuildScenes.Enabled = (canEditScene && !isPlayMode) || Editor.StateMachine.BuildingScenesState.IsActive;
|
||||
_toolStripBuildScenes.Visible = Editor.Options.Options.General.BuildActions?.Length != 0;
|
||||
_toolStripCook.Enabled = Editor.Windows.GameCookerWin.CanBuild(Platform.PlatformType) && !GameCooker.IsRunning;
|
||||
//
|
||||
var play = _toolStripPlay;
|
||||
@@ -471,13 +471,13 @@ namespace FlaxEditor.Modules
|
||||
// Place dialog nearby the target control
|
||||
var targetControlDesktopCenter = targetControl.PointToScreen(targetControl.Size * 0.5f);
|
||||
var desktopSize = Platform.GetMonitorBounds(targetControlDesktopCenter);
|
||||
var pos = targetControlDesktopCenter + new Float2(10.0f, -dialog.Height * 0.5f);
|
||||
var dialogEnd = pos + dialog.Size;
|
||||
var pos = targetControlDesktopCenter + new Float2(10.0f, -dialog.DialogSize.Y * 0.5f);
|
||||
var dialogEnd = pos + dialog.DialogSize;
|
||||
var desktopEnd = desktopSize.BottomRight - new Float2(10.0f);
|
||||
if (dialogEnd.X >= desktopEnd.X || dialogEnd.Y >= desktopEnd.Y)
|
||||
pos = targetControl.PointToScreen(Float2.Zero) - new Float2(10.0f + dialog.Width, dialog.Height);
|
||||
pos = targetControl.PointToScreen(Float2.Zero) - new Float2(10.0f + dialog.DialogSize.X, dialog.DialogSize.Y);
|
||||
var desktopBounds = Platform.VirtualDesktopBounds;
|
||||
pos = Float2.Clamp(pos, desktopBounds.UpperLeft, desktopBounds.BottomRight - dialog.Size);
|
||||
pos = Float2.Clamp(pos, desktopBounds.UpperLeft, desktopBounds.BottomRight - dialog.DialogSize);
|
||||
dialog.RootWindow.Window.Position = pos;
|
||||
|
||||
// Register for context menu (prevent auto-closing context menu when selecting color)
|
||||
@@ -528,6 +528,7 @@ namespace FlaxEditor.Modules
|
||||
_menuFileSaveAll = cm.AddButton("Save All", inputOptions.Save, Editor.SaveAll);
|
||||
_menuFileSaveScenes = cm.AddButton("Save scenes", inputOptions.SaveScenes, Editor.Scene.SaveScenes);
|
||||
_menuFileCloseScenes = cm.AddButton("Close scenes", inputOptions.CloseScenes, Editor.Scene.CloseAllScenes);
|
||||
_menuFileReloadScenes = cm.AddButton("Reload scenes", Editor.Scene.ReloadScenes);
|
||||
cm.AddSeparator();
|
||||
_menuFileOpenScriptsProject = cm.AddButton("Open scripts project", inputOptions.OpenScriptsProject, Editor.CodeEditing.OpenSolution);
|
||||
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
|
||||
@@ -653,7 +654,7 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
|
||||
}
|
||||
|
||||
private void OnOptionsChanged(FlaxEditor.Options.EditorOptions options)
|
||||
private void OnOptionsChanged(EditorOptions options)
|
||||
{
|
||||
var inputOptions = options.Input;
|
||||
|
||||
@@ -688,6 +689,8 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsTakeScreenshot.ShortKeys = inputOptions.TakeScreenshot.ToString();
|
||||
|
||||
MainMenuShortcutKeysUpdated?.Invoke();
|
||||
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
private void InitToolstrip(RootControl mainWindow)
|
||||
@@ -709,11 +712,11 @@ namespace FlaxEditor.Modules
|
||||
_toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})");
|
||||
ToolStrip.AddSeparator();
|
||||
|
||||
// Cook scenes
|
||||
// Build scenes
|
||||
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip($"Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options ({inputOptions.BuildScenesData})");
|
||||
|
||||
// Cook and run
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.Play})");
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.CookAndRun})");
|
||||
_toolStripCook.ContextMenu = new ContextMenu();
|
||||
_toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked);
|
||||
_toolStripCook.ContextMenu.AddSeparator();
|
||||
@@ -829,6 +832,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
_menuFileSaveScenes.Enabled = hasOpenedScene;
|
||||
_menuFileCloseScenes.Enabled = hasOpenedScene;
|
||||
_menuFileReloadScenes.Enabled = hasOpenedScene;
|
||||
_menuFileGenerateScriptsProjectFiles.Enabled = !Editor.ProgressReporting.GenerateScriptsProjectFiles.IsActive;
|
||||
|
||||
c.PerformLayout();
|
||||
|
||||
@@ -171,9 +171,13 @@ namespace FlaxEditor.Modules
|
||||
var mainWindow = MainWindow;
|
||||
if (mainWindow)
|
||||
{
|
||||
var projectPath = Globals.ProjectFolder.Replace('/', '\\');
|
||||
var platformBit = Platform.Is64BitApp ? "64" : "32";
|
||||
var title = string.Format("Flax Editor - \'{0}\' ({1}-bit)", projectPath, platformBit);
|
||||
var projectPath = Globals.ProjectFolder;
|
||||
#if PLATFORM_WINDOWS
|
||||
projectPath = projectPath.Replace('/', '\\');
|
||||
#endif
|
||||
var engineVersion = Editor.EngineProject.Version;
|
||||
var engineVersionText = engineVersion.Revision > 0 ? $"{engineVersion.Major}.{engineVersion.Minor}.{engineVersion.Revision}" : $"{engineVersion.Major}.{engineVersion.Minor}";
|
||||
var title = $"Flax Editor {engineVersionText} - \'{projectPath}\'";
|
||||
mainWindow.Title = title;
|
||||
}
|
||||
}
|
||||
@@ -237,7 +241,11 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public void LoadDefaultLayout()
|
||||
{
|
||||
LoadLayout(StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/LayoutDefault.xml"));
|
||||
var path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/LayoutDefault.xml");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
LoadLayout(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -276,9 +284,6 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Get metadata
|
||||
int version = int.Parse(root.Attributes["Version"].Value, CultureInfo.InvariantCulture);
|
||||
var virtualDesktopBounds = Platform.VirtualDesktopBounds;
|
||||
var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location;
|
||||
var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
@@ -288,31 +293,9 @@ namespace FlaxEditor.Modules
|
||||
if (MainWindow)
|
||||
{
|
||||
var mainWindowNode = root["MainWindow"];
|
||||
Rectangle bounds = LoadBounds(mainWindowNode["Bounds"]);
|
||||
bool isMaximized = bool.Parse(mainWindowNode.GetAttribute("IsMaximized"));
|
||||
|
||||
// Clamp position to match current desktop dimensions (if window was on desktop that is now inactive)
|
||||
if (bounds.X < virtualDesktopSafeLeftCorner.X || bounds.Y < virtualDesktopSafeLeftCorner.Y || bounds.X > virtualDesktopSafeRightCorner.X || bounds.Y > virtualDesktopSafeRightCorner.Y)
|
||||
bounds.Location = virtualDesktopSafeLeftCorner;
|
||||
|
||||
if (isMaximized)
|
||||
{
|
||||
if (MainWindow.IsMaximized)
|
||||
MainWindow.Restore();
|
||||
MainWindow.ClientPosition = bounds.Location;
|
||||
MainWindow.Maximize();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mathf.Min(bounds.Size.X, bounds.Size.Y) >= 1)
|
||||
{
|
||||
MainWindow.ClientBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.ClientPosition = bounds.Location;
|
||||
}
|
||||
}
|
||||
bool isMaximized = true, isMinimized = false;
|
||||
Rectangle bounds = LoadBounds(mainWindowNode, ref isMaximized, ref isMinimized);
|
||||
LoadWindow(MainWindow, ref bounds, isMaximized, false);
|
||||
}
|
||||
|
||||
// Load master panel structure
|
||||
@@ -332,11 +315,13 @@ namespace FlaxEditor.Modules
|
||||
continue;
|
||||
|
||||
// Get window properties
|
||||
Rectangle bounds = LoadBounds(child["Bounds"]);
|
||||
bool isMaximized = false, isMinimized = false;
|
||||
Rectangle bounds = LoadBounds(child, ref isMaximized, ref isMinimized);
|
||||
|
||||
// Create window and floating dock panel
|
||||
var window = FloatWindowDockPanel.CreateFloatWindow(MainWindow.GUI, bounds.Location, bounds.Size, WindowStartPosition.Manual, string.Empty);
|
||||
var panel = new FloatWindowDockPanel(masterPanel, window.GUI);
|
||||
LoadWindow(panel.Window.Window, ref bounds, isMaximized, isMinimized);
|
||||
|
||||
// Load structure
|
||||
LoadPanel(child, panel);
|
||||
@@ -493,23 +478,67 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private static void SaveBounds(XmlWriter writer, Window win)
|
||||
{
|
||||
var bounds = win.ClientBounds;
|
||||
|
||||
writer.WriteAttributeString("X", bounds.X.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Y", bounds.Y.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Width", bounds.Width.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Height", bounds.Height.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteStartElement("Bounds");
|
||||
{
|
||||
var bounds = win.ClientBounds;
|
||||
writer.WriteAttributeString("X", bounds.X.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Y", bounds.Y.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Width", bounds.Width.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Height", bounds.Height.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("IsMaximized", win.IsMaximized.ToString());
|
||||
writer.WriteAttributeString("IsMinimized", win.IsMinimized.ToString());
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
private static Rectangle LoadBounds(XmlElement node)
|
||||
private static Rectangle LoadBounds(XmlElement node, ref bool isMaximized, ref bool isMinimized)
|
||||
{
|
||||
float x = float.Parse(node.GetAttribute("X"), CultureInfo.InvariantCulture);
|
||||
float y = float.Parse(node.GetAttribute("Y"), CultureInfo.InvariantCulture);
|
||||
float width = float.Parse(node.GetAttribute("Width"), CultureInfo.InvariantCulture);
|
||||
float height = float.Parse(node.GetAttribute("Height"), CultureInfo.InvariantCulture);
|
||||
var bounds = node["Bounds"];
|
||||
var isMaximizedText = bounds.GetAttribute("IsMaximized");
|
||||
if (!string.IsNullOrEmpty(isMaximizedText))
|
||||
isMaximized = bool.Parse(isMaximizedText);
|
||||
var isMinimizedText = bounds.GetAttribute("IsMinimized");
|
||||
if (!string.IsNullOrEmpty(isMinimizedText))
|
||||
isMinimized = bool.Parse(isMinimizedText);
|
||||
float x = float.Parse(bounds.GetAttribute("X"), CultureInfo.InvariantCulture);
|
||||
float y = float.Parse(bounds.GetAttribute("Y"), CultureInfo.InvariantCulture);
|
||||
float width = float.Parse(bounds.GetAttribute("Width"), CultureInfo.InvariantCulture);
|
||||
float height = float.Parse(bounds.GetAttribute("Height"), CultureInfo.InvariantCulture);
|
||||
return new Rectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
private static void LoadWindow(Window win, ref Rectangle bounds, bool isMaximized, bool isMinimized)
|
||||
{
|
||||
var virtualDesktopBounds = Platform.VirtualDesktopBounds;
|
||||
var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location;
|
||||
var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight;
|
||||
|
||||
// Clamp position to match current desktop dimensions (if window was on desktop that is now inactive)
|
||||
if (bounds.X < virtualDesktopSafeLeftCorner.X || bounds.Y < virtualDesktopSafeLeftCorner.Y || bounds.X > virtualDesktopSafeRightCorner.X || bounds.Y > virtualDesktopSafeRightCorner.Y)
|
||||
bounds.Location = virtualDesktopSafeLeftCorner;
|
||||
|
||||
if (isMaximized)
|
||||
{
|
||||
if (win.IsMaximized)
|
||||
win.Restore();
|
||||
win.ClientPosition = bounds.Location;
|
||||
win.Maximize();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mathf.Min(bounds.Size.X, bounds.Size.Y) >= 1)
|
||||
{
|
||||
win.ClientBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
win.ClientPosition = bounds.Location;
|
||||
}
|
||||
if (isMinimized)
|
||||
win.Minimize();
|
||||
}
|
||||
}
|
||||
|
||||
private class LayoutNameDialog : Dialog
|
||||
{
|
||||
private TextBox _textbox;
|
||||
@@ -609,13 +638,8 @@ namespace FlaxEditor.Modules
|
||||
if (MainWindow)
|
||||
{
|
||||
writer.WriteStartElement("MainWindow");
|
||||
writer.WriteAttributeString("IsMaximized", MainWindow.IsMaximized.ToString());
|
||||
|
||||
writer.WriteStartElement("Bounds");
|
||||
SaveBounds(writer, MainWindow);
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
// Master panel structure
|
||||
@@ -628,22 +652,13 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
var panel = masterPanel.FloatingPanels[i];
|
||||
var window = panel.Window;
|
||||
|
||||
if (window == null)
|
||||
{
|
||||
Editor.LogWarning("Floating panel has missing window");
|
||||
continue;
|
||||
}
|
||||
|
||||
writer.WriteStartElement("Float");
|
||||
|
||||
SavePanel(writer, panel);
|
||||
|
||||
writer.WriteStartElement("Bounds");
|
||||
SaveBounds(writer, window.Window);
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
writer.WriteEndElement();
|
||||
@@ -724,7 +739,6 @@ namespace FlaxEditor.Modules
|
||||
settings.Size = Platform.DesktopSize * 0.75f;
|
||||
settings.StartPosition = WindowStartPosition.CenterScreen;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
{
|
||||
@@ -736,12 +750,9 @@ namespace FlaxEditor.Modules
|
||||
#elif PLATFORM_LINUX
|
||||
settings.HasBorder = false;
|
||||
#endif
|
||||
|
||||
MainWindow = Platform.CreateWindow(ref settings);
|
||||
|
||||
if (MainWindow == null)
|
||||
{
|
||||
// Error
|
||||
Editor.LogError("Failed to create editor main window!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// Gets or sets the sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(200), Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")]
|
||||
[EditorDisplay("General"), EditorOrder(200), ExpandGroups, Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")]
|
||||
public BuildAction[] BuildActions { get; set; } =
|
||||
{
|
||||
BuildAction.CSG,
|
||||
|
||||
@@ -259,10 +259,7 @@ namespace FlaxEditor.Options
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
@@ -270,9 +267,7 @@ namespace FlaxEditor.Options
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
@@ -284,7 +279,6 @@ namespace FlaxEditor.Options
|
||||
InputBinding.TryParse(str, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
@@ -295,7 +289,6 @@ namespace FlaxEditor.Options
|
||||
{
|
||||
return ((InputBinding)value).ToString();
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Common"), EditorOrder(230)]
|
||||
public InputBinding RotateSelection = new InputBinding(KeyboardKeys.R);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F11")]
|
||||
[EditorDisplay("Common"), EditorOrder(240)]
|
||||
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
@@ -208,16 +212,20 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Debugger", "Continue"), EditorOrder(810)]
|
||||
public InputBinding DebuggerContinue = new InputBinding(KeyboardKeys.F5);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+F11")]
|
||||
[EditorDisplay("Debugger", "Unlock mouse in Play Mode"), EditorOrder(820)]
|
||||
public InputBinding DebuggerUnlockMouse = new InputBinding(KeyboardKeys.F11, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F10")]
|
||||
[EditorDisplay("Debugger", "Step Over"), EditorOrder(820)]
|
||||
[EditorDisplay("Debugger", "Step Over"), EditorOrder(830)]
|
||||
public InputBinding DebuggerStepOver = new InputBinding(KeyboardKeys.F10);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "F11")]
|
||||
[EditorDisplay("Debugger", "Step Into"), EditorOrder(830)]
|
||||
[EditorDisplay("Debugger", "Step Into"), EditorOrder(840)]
|
||||
public InputBinding DebuggerStepInto = new InputBinding(KeyboardKeys.F11);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+F11")]
|
||||
[EditorDisplay("Debugger", "Step Out"), EditorOrder(840)]
|
||||
[EditorDisplay("Debugger", "Step Out"), EditorOrder(850)]
|
||||
public InputBinding DebuggerStepOut = new InputBinding(KeyboardKeys.F11, KeyboardKeys.Shift);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -244,11 +244,11 @@ namespace FlaxEditor.Options
|
||||
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
|
||||
ProgressNormal = Color.FromBgra(0xFF0ad328),
|
||||
|
||||
Statusbar = new Style.StatusbarStyle()
|
||||
Statusbar = new Style.StatusbarStyle
|
||||
{
|
||||
PlayMode = Color.FromBgra(0xFF2F9135),
|
||||
Failed = Color.FromBgra(0xFF9C2424),
|
||||
Loading = Color.FromBgra(0xFF2D2D30)
|
||||
Loading = Color.FromBgra(0xFF2D2D30),
|
||||
},
|
||||
|
||||
// Fonts
|
||||
@@ -271,7 +271,7 @@ namespace FlaxEditor.Options
|
||||
Scale = Editor.Icons.Scale32,
|
||||
Scalar = Editor.Icons.Scalar32,
|
||||
|
||||
SharedTooltip = new Tooltip()
|
||||
SharedTooltip = new Tooltip(),
|
||||
};
|
||||
style.DragWindow = style.BackgroundSelected * 0.7f;
|
||||
|
||||
|
||||
@@ -26,45 +26,108 @@ namespace FlaxEditor.Options
|
||||
public float MouseWheelSensitivity { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default movement speed for the viewport camera (must match the dropdown menu values in the viewport).
|
||||
/// Gets or sets the total amount of steps the camera needs to go from minimum to maximum speed.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0.01f, 100.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(110), Tooltip("The default movement speed for the viewport camera (must match the dropdown menu values in the viewport).")]
|
||||
public float DefaultMovementSpeed { get; set; } = 1.0f;
|
||||
[DefaultValue(64), Limit(1, 128)]
|
||||
[EditorDisplay("Camera"), EditorOrder(110), Tooltip("The total amount of steps the camera needs to go from minimum to maximum speed.")]
|
||||
public int TotalCameraSpeedSteps { get; set; } = 64;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the degree to which the camera will be eased when using camera flight in the editor window.
|
||||
/// </summary>
|
||||
[DefaultValue(3.0f), Limit(1.0f, 8.0f)]
|
||||
[EditorDisplay("Camera"), EditorOrder(111), Tooltip("The degree to which the camera will be eased when using camera flight in the editor window (ignored if camera easing degree is enabled).")]
|
||||
public float CameraEasingDegree { get; set; } = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default movement speed for the viewport camera (must be in range between minimum and maximum movement speed values).
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0.05f, 32.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(120), Tooltip("The default movement speed for the viewport camera (must be in range between minimum and maximum movement speed values).")]
|
||||
public float MovementSpeed { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default minimum camera movement speed.
|
||||
/// </summary>
|
||||
[DefaultValue(0.05f), Limit(0.05f, 32.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(121), Tooltip("The default minimum movement speed for the viewport camera.")]
|
||||
public float MinMovementSpeed { get; set; } = 0.05f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default maximum camera movement speed.
|
||||
/// </summary>
|
||||
[DefaultValue(32.0f), Limit(16.0f, 1000.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(122), Tooltip("The default maximum movement speed for the viewport camera.")]
|
||||
public float MaxMovementSpeed { get; set; } = 32f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default camera easing mode.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(130), Tooltip("The default camera easing mode.")]
|
||||
public bool UseCameraEasing { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default near clipping plane distance for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(10.0f), Limit(0.001f, 1000.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(120), Tooltip("The default near clipping plane distance for the viewport camera.")]
|
||||
public float DefaultNearPlane { get; set; } = 10.0f;
|
||||
[EditorDisplay("Defaults"), EditorOrder(140), Tooltip("The default near clipping plane distance for the viewport camera.")]
|
||||
public float NearPlane { get; set; } = 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default far clipping plane distance for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(40000.0f), Limit(10.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(130), Tooltip("The default far clipping plane distance for the viewport camera.")]
|
||||
public float DefaultFarPlane { get; set; } = 40000.0f;
|
||||
[EditorDisplay("Defaults"), EditorOrder(150), Tooltip("The default far clipping plane distance for the viewport camera.")]
|
||||
public float FarPlane { get; set; } = 40000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default field of view angle (in degrees) for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)]
|
||||
[EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")]
|
||||
public float DefaultFieldOfView { get; set; } = 60.0f;
|
||||
[EditorDisplay("Defaults"), EditorOrder(160), Tooltip("The default field of view angle (in degrees) for the viewport camera.")]
|
||||
public float FieldOfView { get; set; } = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the panning direction is inverted for the viewport camera.
|
||||
/// Gets or sets the default camera orthographic mode.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(150), Tooltip("Invert the panning direction for the viewport camera.")]
|
||||
public bool DefaultInvertPanning { get; set; } = false;
|
||||
[EditorDisplay("Defaults"), EditorOrder(170), Tooltip("The default camera orthographic mode.")]
|
||||
public bool UseOrthographicProjection { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Scales editor viewport grid.
|
||||
/// Gets or sets the default camera orthographic scale (if camera uses orthographic mode).
|
||||
/// </summary>
|
||||
[DefaultValue(5.0f), Limit(0.001f, 100000.0f, 0.1f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(180), Tooltip("The default camera orthographic scale (if camera uses orthographic mode).")]
|
||||
public float OrthographicScale { get; set; } = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default panning direction for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(190), Tooltip("The default panning direction for the viewport camera.")]
|
||||
public bool InvertPanning { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default relative panning mode.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(200), Tooltip("The default relative panning mode. Uses distance between camera and target to determine panning speed.")]
|
||||
public bool UseRelativePanning { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default panning speed (ignored if relative panning is speed enabled).
|
||||
/// </summary>
|
||||
[DefaultValue(0.8f), Limit(0.01f, 128.0f, 0.1f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(210), Tooltip("The default camera panning speed (ignored if relative panning is enabled).")]
|
||||
public float PanningSpeed { get; set; } = 0.8f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default editor viewport grid scale.
|
||||
/// </summary>
|
||||
[DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(160), Tooltip("Scales editor viewport grid.")]
|
||||
[EditorDisplay("Defaults"), EditorOrder(220), Tooltip("The default editor viewport grid scale.")]
|
||||
public float ViewportGridScale { get; set; } = 50.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,25 +19,25 @@ namespace FlaxEditor.Progress.Handlers
|
||||
public ImportAssetsProgress()
|
||||
{
|
||||
var importing = Editor.Instance.ContentImporting;
|
||||
importing.ImportingQueueBegin += OnStart;
|
||||
importing.ImportingQueueEnd += OnEnd;
|
||||
importing.ImportingQueueBegin += () => FlaxEngine.Scripting.InvokeOnUpdate(OnStart);
|
||||
importing.ImportingQueueEnd += () => FlaxEngine.Scripting.InvokeOnUpdate(OnEnd);
|
||||
importing.ImportFileBegin += OnImportFileBegin;
|
||||
}
|
||||
|
||||
private void OnImportFileBegin(IFileEntryAction importFileEntry)
|
||||
{
|
||||
string info;
|
||||
if (importFileEntry is ImportFileEntry)
|
||||
_currentInfo = string.Format("Importing \'{0}\'", System.IO.Path.GetFileName(importFileEntry.SourceUrl));
|
||||
info = string.Format("Importing \'{0}\'", System.IO.Path.GetFileName(importFileEntry.SourceUrl));
|
||||
else
|
||||
_currentInfo = string.Format("Creating \'{0}\'", importFileEntry.SourceUrl);
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
private void UpdateProgress()
|
||||
{
|
||||
var importing = Editor.Instance.ContentImporting;
|
||||
var info = string.Format("{0} ({1}/{2})...", _currentInfo, importing.ImportBatchDone, importing.ImportBatchSize);
|
||||
OnUpdate(importing.ImportingProgress, info);
|
||||
info = string.Format("Creating \'{0}\'", importFileEntry.SourceUrl);
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
_currentInfo = info;
|
||||
var importing = Editor.Instance.ContentImporting;
|
||||
var text = string.Format("{0} ({1}/{2})...", _currentInfo, importing.ImportBatchDone, importing.ImportBatchSize);
|
||||
OnUpdate(importing.ImportingProgress, text);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,8 @@ bool ProjectInfo::LoadProject(const String& projectPath)
|
||||
Version = ::Version(
|
||||
JsonTools::GetInt(version, "Major", 0),
|
||||
JsonTools::GetInt(version, "Minor", 0),
|
||||
JsonTools::GetInt(version, "Build", 0));
|
||||
JsonTools::GetInt(version, "Build", -1),
|
||||
JsonTools::GetInt(version, "Revision", -1));
|
||||
}
|
||||
}
|
||||
if (Version.Revision() == 0)
|
||||
|
||||
@@ -23,17 +23,11 @@ namespace FlaxEditor
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else if (value is Version)
|
||||
{
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new JsonSerializationException("Expected Version object value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,65 +41,60 @@ namespace FlaxEditor
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
|
||||
if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.StartObject)
|
||||
try
|
||||
{
|
||||
try
|
||||
reader.Read();
|
||||
var values = new Dictionary<string, int>();
|
||||
while (reader.TokenType == JsonToken.PropertyName)
|
||||
{
|
||||
var key = reader.Value as string;
|
||||
reader.Read();
|
||||
Dictionary<string, int> values = new Dictionary<string, int>();
|
||||
while (reader.TokenType == JsonToken.PropertyName)
|
||||
{
|
||||
var key = reader.Value as string;
|
||||
reader.Read();
|
||||
var val = (long)reader.Value;
|
||||
reader.Read();
|
||||
values.Add(key, (int)val);
|
||||
}
|
||||
var val = (long)reader.Value;
|
||||
reader.Read();
|
||||
values.Add(key, (int)val);
|
||||
}
|
||||
|
||||
int major = 0, minor = 0, build = 0;
|
||||
values.TryGetValue("Major", out major);
|
||||
values.TryGetValue("Minor", out minor);
|
||||
values.TryGetValue("Build", out build);
|
||||
values.TryGetValue("Major", out var major);
|
||||
values.TryGetValue("Minor", out var minor);
|
||||
if (!values.TryGetValue("Build", out var build))
|
||||
build = -1;
|
||||
if (!values.TryGetValue("Revision", out var revision))
|
||||
revision = -1;
|
||||
|
||||
Version v = new Version(major, minor, build);
|
||||
return v;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
|
||||
}
|
||||
if (build <= 0)
|
||||
return new Version(major, minor);
|
||||
if (revision <= 0)
|
||||
return new Version(major, minor, build);
|
||||
return new Version(major, minor, build, revision);
|
||||
}
|
||||
else if (reader.TokenType == JsonToken.String)
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
Version v = new Version((string)reader.Value!);
|
||||
return v;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.Value));
|
||||
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
|
||||
}
|
||||
}
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Version((string)reader.Value!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
|
||||
}
|
||||
}
|
||||
throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.Value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can convert the specified object type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.</returns>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(Version);
|
||||
|
||||
@@ -7,6 +7,7 @@ using Real = System.Single;
|
||||
#endif
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
@@ -30,6 +31,13 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
// Rotate to match the space (GUI uses upper left corner as a root)
|
||||
Actor.LocalOrientation = Quaternion.Euler(0, -180, -180);
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Name = "Canvas Scalar",
|
||||
Transform = Actor.Transform,
|
||||
Control = new CanvasScaler()
|
||||
};
|
||||
Root.Spawn(uiControl, Actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -66,7 +66,8 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
_orderInParent = actor.OrderInParent;
|
||||
Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
|
||||
var id = actor.ID;
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
if (Editor.Instance.ProjectCache.IsExpandedActor(ref id))
|
||||
{
|
||||
Expand(true);
|
||||
@@ -171,7 +172,8 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
// Restore cached state on query filter clear
|
||||
if (noFilter && actor != null)
|
||||
{
|
||||
var id = actor.ID;
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id);
|
||||
}
|
||||
|
||||
@@ -264,7 +266,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
/// <summary>
|
||||
/// Starts the actor renaming action.
|
||||
/// </summary>
|
||||
public void StartRenaming(EditorWindow window)
|
||||
public void StartRenaming(EditorWindow window, Panel treePanel = null)
|
||||
{
|
||||
// Block renaming during scripts reload
|
||||
if (Editor.Instance.ProgressReporting.CompileScripts.IsActive)
|
||||
@@ -279,7 +281,13 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
(window as PrefabWindow).ScrollingOnTreeView(false);
|
||||
|
||||
// Start renaming the actor
|
||||
var dialog = RenamePopup.Show(this, TextRect, _actorNode.Name, false);
|
||||
var rect = TextRect;
|
||||
if (treePanel != null)
|
||||
{
|
||||
treePanel.ScrollViewTo(this, true);
|
||||
rect.Size = new Float2(treePanel.Width - TextRect.Location.X, TextRect.Height);
|
||||
}
|
||||
var dialog = RenamePopup.Show(this, rect, _actorNode.Name, false);
|
||||
dialog.Renamed += OnRenamed;
|
||||
dialog.Closed += popup =>
|
||||
{
|
||||
@@ -301,10 +309,12 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
protected override void OnExpandedChanged()
|
||||
{
|
||||
base.OnExpandedChanged();
|
||||
var actor = Actor;
|
||||
|
||||
if (!IsLayoutLocked && Actor)
|
||||
if (!IsLayoutLocked && actor)
|
||||
{
|
||||
var id = Actor.ID;
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
Editor.Instance.ProjectCache.SetExpandedActor(ref id, IsExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,9 +207,9 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
FileSystem::GetChildDirectories(subDirectories, TEXT("/opt/"));
|
||||
|
||||
// Versions installed via JetBrains Toolbox
|
||||
SearchDirectory(&installations, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/rider/"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-0"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions
|
||||
SearchDirectory(&installations, localAppDataPath / TEXT("JetBrains/Toolbox/apps/rider/"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains/Toolbox/apps/Rider/ch-0"));
|
||||
FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions
|
||||
|
||||
// Detect Flatpak installations
|
||||
SearchDirectory(&installations,
|
||||
|
||||
@@ -286,81 +286,17 @@ namespace VisualStudio
|
||||
return "Visual Studio open timout";
|
||||
}
|
||||
|
||||
static ComPtr<EnvDTE::ProjectItem> FindItem(const ComPtr<EnvDTE::ProjectItems>& projectItems, BSTR filePath)
|
||||
{
|
||||
long count;
|
||||
projectItems->get_Count(&count);
|
||||
if (count == 0)
|
||||
return nullptr;
|
||||
|
||||
for (long i = 1; i <= count; i++) // They are counting from [1..Count]
|
||||
{
|
||||
ComPtr<EnvDTE::ProjectItem> projectItem;
|
||||
projectItems->Item(_variant_t(i), &projectItem);
|
||||
if (!projectItem)
|
||||
continue;
|
||||
|
||||
short fileCount = 0;
|
||||
projectItem->get_FileCount(&fileCount);
|
||||
for (short fileIndex = 1; fileIndex <= fileCount; fileIndex++)
|
||||
{
|
||||
_bstr_t filename;
|
||||
projectItem->get_FileNames(fileIndex, filename.GetAddress());
|
||||
|
||||
if (filename.GetBSTR() != nullptr && AreFilePathsEqual(filePath, filename))
|
||||
{
|
||||
return projectItem;
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<EnvDTE::ProjectItems> childProjectItems;
|
||||
projectItem->get_ProjectItems(&childProjectItems);
|
||||
if (childProjectItems)
|
||||
{
|
||||
ComPtr<EnvDTE::ProjectItem> result = FindItem(childProjectItems, filePath);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ComPtr<EnvDTE::ProjectItem> FindItem(const ComPtr<EnvDTE::_Solution>& solution, BSTR filePath)
|
||||
{
|
||||
static ComPtr<EnvDTE::ProjectItem> FindItem(const ComPtr<EnvDTE::_Solution>& solution, BSTR filePath)
|
||||
{
|
||||
HRESULT result;
|
||||
|
||||
ComPtr<EnvDTE::Projects> projects;
|
||||
result = solution->get_Projects(&projects);
|
||||
ComPtr<EnvDTE::ProjectItem> projectItem;
|
||||
result = solution->FindProjectItem(filePath, &projectItem);
|
||||
if (FAILED(result))
|
||||
return nullptr;
|
||||
|
||||
long projectsCount = 0;
|
||||
result = projects->get_Count(&projectsCount);
|
||||
if (FAILED(result))
|
||||
return nullptr;
|
||||
|
||||
for (long projectIndex = 1; projectIndex <= projectsCount; projectIndex++) // They are counting from [1..Count]
|
||||
{
|
||||
ComPtr<EnvDTE::Project> project;
|
||||
result = projects->Item(_variant_t(projectIndex), &project);
|
||||
if (FAILED(result) || !project)
|
||||
continue;
|
||||
|
||||
ComPtr<EnvDTE::ProjectItems> projectItems;
|
||||
result = project->get_ProjectItems(&projectItems);
|
||||
if (FAILED(result) || !projectItems)
|
||||
continue;
|
||||
|
||||
auto projectItem = FindItem(projectItems, filePath);
|
||||
if (projectItem)
|
||||
{
|
||||
return projectItem;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return projectItem;
|
||||
}
|
||||
|
||||
// Opens a file on a specific line in a running Visual Studio instance.
|
||||
//
|
||||
|
||||
@@ -170,7 +170,7 @@ bool ScriptsBuilder::IsSourceWorkspaceDirty()
|
||||
bool ScriptsBuilder::IsSourceDirtyFor(const TimeSpan& timeout)
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _lastSourceCodeEdited > (_lastCompileAction + timeout);
|
||||
return _lastSourceCodeEdited > _lastCompileAction && DateTime::Now() > _lastSourceCodeEdited + timeout;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsCompiling()
|
||||
@@ -266,7 +266,7 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args, const StringView& work
|
||||
|
||||
bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
|
||||
{
|
||||
String args(TEXT("-log -genproject "));
|
||||
String args(TEXT("-log -mutex -genproject "));
|
||||
args += customArgs;
|
||||
_wasProjectStructureChanged = false;
|
||||
return RunBuildTool(args);
|
||||
@@ -669,7 +669,7 @@ void ScriptsBuilderService::Update()
|
||||
}
|
||||
|
||||
// Check if compile code (if has been edited)
|
||||
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(50);
|
||||
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(150);
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (ScriptsBuilder::IsSourceDirtyFor(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to use for checking.</param>
|
||||
/// <returns>True if source code is dirty, otherwise false.</returns>
|
||||
static bool IsSourceDirtyFor(const TimeSpan& timeout);
|
||||
API_FUNCTION() static bool IsSourceDirtyFor(const TimeSpan& timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if scripts are being now compiled/reloaded.
|
||||
|
||||
@@ -56,12 +56,14 @@ namespace FlaxEditor.States
|
||||
else if (Editor.Options.Options.General.ForceScriptCompilationOnStartup && !skipCompile)
|
||||
{
|
||||
// Generate project files when Cache is missing or was cleared previously
|
||||
if (!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Intermediate")) ||
|
||||
!Directory.Exists(Path.Combine(Editor.GameProject?.ProjectFolderPath, "Cache", "Projects")))
|
||||
var projectFolderPath = Editor.GameProject?.ProjectFolderPath;
|
||||
if (!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Intermediate")) ||
|
||||
!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Projects")))
|
||||
{
|
||||
var customArgs = Editor.Instance.CodeEditing.SelectedEditor.GenerateProjectCustomArgs;
|
||||
var customArgs = Editor.CodeEditing.SelectedEditor?.GenerateProjectCustomArgs;
|
||||
ScriptsBuilder.GenerateProject(customArgs);
|
||||
}
|
||||
|
||||
// Compile scripts before loading any scenes, then we load them and can open scenes
|
||||
ScriptsBuilder.Compile();
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
if (selectedIndex != -1)
|
||||
{
|
||||
var index = 5 + selectedIndex * 2;
|
||||
SetValue(index, _animationPicker.SelectedID);
|
||||
SetValue(index, _animationPicker.Validator.SelectedID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
if (isValid)
|
||||
{
|
||||
_animationPicker.SelectedID = data1;
|
||||
_animationPicker.Validator.SelectedID = data1;
|
||||
_animationSpeed.Value = data0.W;
|
||||
|
||||
var path = string.Empty;
|
||||
@@ -505,7 +505,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationPicker.SelectedID = Guid.Empty;
|
||||
_animationPicker.Validator.SelectedID = Guid.Empty;
|
||||
_animationSpeed.Value = 1.0f;
|
||||
}
|
||||
_animationPicker.Enabled = isValid;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Op1(1, "Bitwise NOT", "Negates the value using bitwise operation", new[] { "!", "~" }),
|
||||
Op2(2, "Bitwise AND", "Performs a bitwise conjunction on two values", new[] { "&" }),
|
||||
Op2(3, "Bitwise OR", "", new[] { "|" }),
|
||||
Op2(4, "Bitwise XOR", ""),
|
||||
Op2(4, "Bitwise XOR", "", new[] { "^" }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Op1(1, "Boolean NOT", "Negates the boolean value", new[] { "!", "~" }),
|
||||
Op2(2, "Boolean AND", "Performs a logical conjunction on two values", new[] { "&&" }),
|
||||
Op2(3, "Boolean OR", "Returns true if either (or both) of its operands is true", new[] { "||" }),
|
||||
Op2(4, "Boolean XOR", ""),
|
||||
Op2(4, "Boolean XOR", "", new [] { "^" } ),
|
||||
Op2(5, "Boolean NOR", ""),
|
||||
Op2(6, "Boolean NAND", ""),
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[HideInEditor]
|
||||
public static class Comparisons
|
||||
{
|
||||
private static NodeArchetype Op(ushort id, string title, string desc)
|
||||
private static NodeArchetype Op(ushort id, string title, string desc, string[] altTitles = null)
|
||||
{
|
||||
return new NodeArchetype
|
||||
{
|
||||
@@ -22,6 +22,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = title,
|
||||
Description = desc,
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
AlternativeTitles = altTitles,
|
||||
ConnectionsHints = ConnectionsHint.Value,
|
||||
Size = new Float2(100, 40),
|
||||
IndependentBoxes = new[]
|
||||
@@ -170,12 +171,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public static NodeArchetype[] Nodes =
|
||||
{
|
||||
Op(1, "==", "Determines whether two values are equal"),
|
||||
Op(2, "!=", "Determines whether two values are not equal"),
|
||||
Op(3, ">", "Determines whether the first value is greater than the other"),
|
||||
Op(4, "<", "Determines whether the first value is less than the other"),
|
||||
Op(5, "<=", "Determines whether the first value is less or equal to the other"),
|
||||
Op(6, ">=", "Determines whether the first value is greater or equal to the other"),
|
||||
Op(1, "==", "Determines whether two values are equal", new[] { "equals" }),
|
||||
Op(2, "!=", "Determines whether two values are not equal", new[] { "not equals" }),
|
||||
Op(3, ">", "Determines whether the first value is greater than the other", new[] { "greater than", "larger than", "bigger than" }),
|
||||
Op(4, "<", "Determines whether the first value is less than the other", new[] { "less than", "smaller than" }),
|
||||
Op(5, "<=", "Determines whether the first value is less or equal to the other", new[] { "less equals than", "smaller equals than" }),
|
||||
Op(6, ">=", "Determines whether the first value is greater or equal to the other", new[] { "greater equals than", "larger equals than", "bigger equals than" }),
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 7,
|
||||
|
||||
@@ -7,11 +7,12 @@ using Real = System.Single;
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Surface.Undo;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -24,6 +25,109 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[HideInEditor]
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// A special type of node that adds the functionality to convert nodes to parameters.
|
||||
/// </summary>
|
||||
internal class ConvertToParameterNode : SurfaceNode
|
||||
{
|
||||
private readonly ScriptType _type;
|
||||
private readonly Func<object[], object> _convertFunction;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConvertToParameterNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch, ScriptType type, Func<object[], object> convertFunction = null)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
_type = type;
|
||||
_convertFunction = convertFunction;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location)
|
||||
{
|
||||
base.OnShowSecondaryContextMenu(menu, location);
|
||||
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Convert to Parameter", OnConvertToParameter);
|
||||
}
|
||||
|
||||
private void OnConvertToParameter()
|
||||
{
|
||||
if (Surface.Owner is not IVisjectSurfaceWindow window)
|
||||
throw new Exception("Surface owner is not a Visject Surface Window");
|
||||
|
||||
Asset asset = Surface.Owner.SurfaceAsset;
|
||||
if (asset == null || !asset.IsLoaded)
|
||||
{
|
||||
Editor.LogError("Asset is null or not loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add parameter to editor
|
||||
var paramIndex = Surface.Parameters.Count;
|
||||
var initValue = _convertFunction == null ? Values[0] : _convertFunction.Invoke(Values);
|
||||
var paramAction = new AddRemoveParamAction
|
||||
{
|
||||
Window = window,
|
||||
IsAdd = true,
|
||||
Name = Utilities.Utils.IncrementNameNumber("New parameter", OnParameterRenameValidate),
|
||||
Type = _type,
|
||||
Index = paramIndex,
|
||||
InitValue = initValue,
|
||||
};
|
||||
paramAction.Do();
|
||||
Surface.AddBatchedUndoAction(paramAction);
|
||||
|
||||
// Spawn Get Parameter Node based on the added parameter
|
||||
Guid parameterGuid = Surface.Parameters[paramIndex].ID;
|
||||
bool undoEnabled = Surface.Undo.Enabled;
|
||||
Surface.Undo.Enabled = false;
|
||||
NodeArchetype arch = Surface.GetParameterGetterNodeArchetype(out var groupId);
|
||||
SurfaceNode node = Surface.Context.SpawnNode(groupId, arch.TypeID, Location, new object[] { parameterGuid });
|
||||
Surface.Undo.Enabled = undoEnabled;
|
||||
if (node is not Parameters.SurfaceNodeParamsGet getNode)
|
||||
throw new Exception("Node is not a ParamsGet node!");
|
||||
Surface.AddBatchedUndoAction(new AddRemoveNodeAction(getNode, true));
|
||||
|
||||
// Recreate connections of constant node
|
||||
// Constant nodes and parameter nodes should have the same ports, so we can just iterate through the connections
|
||||
var editConnectionsAction1 = new EditNodeConnections(Context, this);
|
||||
var editConnectionsAction2 = new EditNodeConnections(Context, node);
|
||||
var boxes = GetBoxes();
|
||||
for (int i = 0; i < boxes.Count; i++)
|
||||
{
|
||||
Box box = boxes[i];
|
||||
if (!box.HasAnyConnection)
|
||||
continue;
|
||||
if (!getNode.TryGetBox(box.ID, out Box paramBox))
|
||||
continue;
|
||||
|
||||
// Iterating backwards, because the CreateConnection method deletes entries from the box connections when target box IsSingle is set to true
|
||||
for (int k = box.Connections.Count - 1; k >= 0; k--)
|
||||
{
|
||||
Box connectedBox = box.Connections[k];
|
||||
paramBox.CreateConnection(connectedBox);
|
||||
}
|
||||
}
|
||||
editConnectionsAction1.End();
|
||||
editConnectionsAction2.End();
|
||||
Surface.AddBatchedUndoAction(editConnectionsAction1);
|
||||
Surface.AddBatchedUndoAction(editConnectionsAction2);
|
||||
|
||||
// Add undo actions and remove constant node
|
||||
var removeConstantAction = new AddRemoveNodeAction(this, false);
|
||||
Surface.AddBatchedUndoAction(removeConstantAction);
|
||||
removeConstantAction.Do();
|
||||
Surface.MarkAsEdited();
|
||||
}
|
||||
|
||||
private bool OnParameterRenameValidate(string value)
|
||||
{
|
||||
if (Surface.Owner is not IVisjectSurfaceWindow window)
|
||||
throw new Exception("Surface owner is not a Visject Surface Window");
|
||||
return !string.IsNullOrWhiteSpace(value) && window.VisjectSurface.Parameters.All(x => x.Name != value);
|
||||
}
|
||||
}
|
||||
|
||||
private class EnumNode : SurfaceNode
|
||||
{
|
||||
private EnumComboBox _picker;
|
||||
@@ -356,6 +460,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 1,
|
||||
Title = "Bool",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(bool))),
|
||||
Description = "Constant boolean value",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -388,6 +493,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 2,
|
||||
Title = "Integer",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(int))),
|
||||
Description = "Constant integer value",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -415,6 +521,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 3,
|
||||
Title = "Float",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(float))),
|
||||
Description = "Constant floating point",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -442,6 +549,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 4,
|
||||
Title = "Float2",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float2))),
|
||||
Description = "Constant Float2",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 60),
|
||||
@@ -472,6 +580,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 5,
|
||||
Title = "Float3",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float3))),
|
||||
Description = "Constant Float3",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 80),
|
||||
@@ -504,6 +613,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 6,
|
||||
Title = "Float4",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float4))),
|
||||
Description = "Constant Float4",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 100),
|
||||
@@ -538,6 +648,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 7,
|
||||
Title = "Color",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Color))),
|
||||
Description = "RGBA color",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(70, 100),
|
||||
@@ -570,6 +681,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 8,
|
||||
Title = "Rotation",
|
||||
Create = (id, context, arch, groupArch) =>
|
||||
new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Quaternion)), values => Quaternion.Euler((float)values[0], (float)values[1], (float)values[2])),
|
||||
Description = "Euler angle rotation",
|
||||
Flags = NodeFlags.AnimGraph | NodeFlags.VisualScriptGraph | NodeFlags.ParticleEmitterGraph,
|
||||
Size = new Float2(110, 60),
|
||||
@@ -594,6 +707,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 9,
|
||||
Title = "String",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(string))),
|
||||
Description = "Text",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Float2(200, 20),
|
||||
@@ -644,6 +758,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 12,
|
||||
Title = "Unsigned Integer",
|
||||
AlternativeTitles = new[] { "UInt", "U Int" },
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(uint))),
|
||||
Description = "Constant unsigned integer value",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(170, 20),
|
||||
@@ -683,6 +799,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 15,
|
||||
Title = "Double",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(double))),
|
||||
Description = "Constant floating point",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -700,6 +817,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 16,
|
||||
Title = "Vector2",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector2))),
|
||||
Description = "Constant Vector2",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 60),
|
||||
@@ -720,6 +838,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 17,
|
||||
Title = "Vector3",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector3))),
|
||||
Description = "Constant Vector3",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 80),
|
||||
@@ -742,6 +861,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 18,
|
||||
Title = "Vector4",
|
||||
Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector4))),
|
||||
Description = "Constant Vector4",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(130, 100),
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
private void OnAssetPickerSelectedItemChanged()
|
||||
{
|
||||
SetValue(0, _assetPicker.SelectedID);
|
||||
SetValue(0, _assetPicker.Validator.SelectedID);
|
||||
}
|
||||
|
||||
private void TryRestoreConnections(Box box, Box[] prevBoxes, ref NodeElementArchetype arch)
|
||||
@@ -133,7 +133,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
var prevOutputs = _outputs;
|
||||
|
||||
// Extract function signature parameters (inputs and outputs packed)
|
||||
_asset = LoadSignature(_assetPicker.SelectedID, out var typeNames, out var names);
|
||||
_asset = LoadSignature(_assetPicker.Validator.SelectedID, out var typeNames, out var names);
|
||||
if (typeNames != null && names != null)
|
||||
{
|
||||
var types = new Type[typeNames.Length];
|
||||
@@ -174,7 +174,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_outputs[i] = box;
|
||||
}
|
||||
|
||||
Title = _assetPicker.SelectedItem.ShortName;
|
||||
Title = _assetPicker.Validator.SelectedItem.ShortName;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2470,6 +2470,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = string.Empty,
|
||||
Description = "Overrides the base class method with custom implementation",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste,
|
||||
SortScore = 10,
|
||||
IsInputCompatible = MethodOverrideNode.IsInputCompatible,
|
||||
IsOutputCompatible = MethodOverrideNode.IsOutputCompatible,
|
||||
Size = new Float2(240, 60),
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Size = new Float2(200, 100),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
0.0f,
|
||||
0.5f,
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
|
||||
@@ -882,6 +882,60 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(1, "Inv Size", typeof(Float2), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 40,
|
||||
Title = "Rectangle Mask",
|
||||
Description = "Creates a rectangle mask",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(150, 40),
|
||||
ConnectionsHints = ConnectionsHint.Vector,
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
new Float2(0.5f, 0.5f),
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Rectangle", true, typeof(Float2), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 41,
|
||||
Title = "FWidth",
|
||||
Description = "Creates a partial derivative (fwidth)",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(150, 20),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0 },
|
||||
DependentBoxes = new[] { 1 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 42,
|
||||
Title = "AA Step",
|
||||
Description = "Smooth version of step function with less aliasing",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(150, 40),
|
||||
ConnectionsHints = ConnectionsHint.Vector,
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
0.5f
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Gradient", true, typeof(float), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
public static class Math
|
||||
{
|
||||
private static NodeArchetype Op1(ushort id, string title, string desc, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null)
|
||||
{
|
||||
return Op1(id, title, desc, null, hints, type);
|
||||
}
|
||||
|
||||
private static NodeArchetype Op1(ushort id, string title, string desc, string[] altTitles, ConnectionsHint hints = ConnectionsHint.Numeric, Type type = null)
|
||||
{
|
||||
return new NodeArchetype
|
||||
{
|
||||
@@ -20,6 +25,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = title,
|
||||
Description = desc,
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
AlternativeTitles = altTitles,
|
||||
Size = new Float2(110, 20),
|
||||
DefaultType = new ScriptType(type),
|
||||
ConnectionsHints = hints,
|
||||
@@ -92,6 +98,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 11,
|
||||
Title = "Length",
|
||||
AlternativeTitles = new[] { "Magnitude", "Mag" },
|
||||
Description = "Returns the length of A vector",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(110, 20),
|
||||
@@ -107,10 +114,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Op1(13, "Round", "Rounds A to the nearest integer"),
|
||||
Op1(14, "Saturate", "Clamps A to the range [0, 1]"),
|
||||
Op1(15, "Sine", "Returns sine of A"),
|
||||
Op1(16, "Sqrt", "Returns square root of A"),
|
||||
Op1(16, "Sqrt", "Returns square root of A", new [] { "Square Root", "Square", "Root" }),
|
||||
Op1(17, "Tangent", "Returns tangent of A"),
|
||||
Op2(18, "Cross", "Returns the cross product of A and B", ConnectionsHint.None, typeof(Float3)),
|
||||
Op2(19, "Distance", "Returns a distance scalar between A and B", ConnectionsHint.Vector, null, typeof(float), false),
|
||||
Op2(19, "Distance", "Returns a distance scalar between A and B", new [] { "Magnitude", "Mag", "Length" }, ConnectionsHint.Vector, null, typeof(float), false),
|
||||
Op2(20, "Dot", "Returns the dot product of A and B", ConnectionsHint.Vector, null, typeof(float), false),
|
||||
Op2(21, "Max", "Selects the greater of A and B"),
|
||||
Op2(22, "Min", "Selects the lesser of A and B"),
|
||||
@@ -185,7 +192,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
},
|
||||
//
|
||||
Op1(27, "Negate", "Returns opposite value"),
|
||||
Op1(27, "Negate", "Returns opposite value", new [] { "Invert" }),
|
||||
Op1(28, "One Minus", "Returns 1 - value"),
|
||||
//
|
||||
new NodeArchetype
|
||||
@@ -225,6 +232,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 31,
|
||||
Title = "Mad",
|
||||
AlternativeTitles = new [] { "Multiply", "Add", "*+" },
|
||||
Description = "Performs value multiplication and addition at once",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(160, 60),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Surface.Archetypes
|
||||
@@ -95,6 +96,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 1,
|
||||
Title = "Texture",
|
||||
Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Texture))),
|
||||
Description = "Two dimensional texture object",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(140, 120),
|
||||
@@ -131,6 +133,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 3,
|
||||
Title = "Cube Texture",
|
||||
Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(CubeTexture))),
|
||||
Description = "Set of 6 textures arranged in a cube",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(140, 120),
|
||||
@@ -154,6 +157,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 4,
|
||||
Title = "Normal Map",
|
||||
Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(NormalMap))),
|
||||
Description = "Two dimensional texture object sampled as a normal map",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(140, 120),
|
||||
|
||||
@@ -1483,7 +1483,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 11,
|
||||
Title = "Comment",
|
||||
AlternativeTitles = new[] { "//" },
|
||||
AlternativeTitles = new[] { "//" , "Group" },
|
||||
TryParseText = (string filterText, out object[] data) =>
|
||||
{
|
||||
data = null;
|
||||
@@ -1510,6 +1510,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
"Comment", // Title
|
||||
new Color(1.0f, 1.0f, 1.0f, 0.2f), // Color
|
||||
new Float2(400.0f, 400.0f), // Size
|
||||
-1, // Order
|
||||
},
|
||||
},
|
||||
CurveNode<float>.GetArchetype(12, "Curve", typeof(float), 0.0f, 1.0f),
|
||||
@@ -1638,6 +1639,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 22,
|
||||
Title = "As",
|
||||
AlternativeTitles = new [] { "Cast" },
|
||||
Create = (id, context, arch, groupArch) => new AsNode(id, context, arch, groupArch),
|
||||
Description = "Casts the object to a different type. Returns null if cast fails.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
if (!Visible)
|
||||
return;
|
||||
|
||||
SortScore += _archetype.SortScore;
|
||||
if (selectedBox != null && CanConnectTo(selectedBox))
|
||||
SortScore += 1;
|
||||
if (Data != null)
|
||||
|
||||
@@ -38,13 +38,13 @@ namespace FlaxEditor.Surface.Elements
|
||||
|
||||
private void OnNodeValuesChanged()
|
||||
{
|
||||
SelectedID = (Guid)ParentNode.Values[Archetype.ValueIndex];
|
||||
Validator.SelectedID = (Guid)ParentNode.Values[Archetype.ValueIndex];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSelectedItemChanged()
|
||||
{
|
||||
var selectedId = SelectedID;
|
||||
var selectedId = Validator.SelectedID;
|
||||
if (ParentNode != null && (Guid)ParentNode.Values[Archetype.ValueIndex] != selectedId)
|
||||
{
|
||||
ParentNode.SetValue(Archetype.ValueIndex, selectedId);
|
||||
|
||||
@@ -160,6 +160,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
|
||||
/// <summary>
|
||||
/// Custom score value to use when sorting node archetypes in Editor. If positive (eg. 1, 2) can be used to add more importance for a specific node type.
|
||||
/// </summary>
|
||||
public float SortScore;
|
||||
|
||||
/// <summary>
|
||||
/// Default node values. This array supports types: bool, int, float, Vector2, Vector3, Vector4, Color, Rectangle, Guid, string, Matrix and byte[].
|
||||
/// </summary>
|
||||
@@ -215,14 +220,17 @@ namespace FlaxEditor.Surface
|
||||
Size = Size,
|
||||
Flags = Flags,
|
||||
Title = Title,
|
||||
Description = Title,
|
||||
SubTitle = SubTitle,
|
||||
Description = Description,
|
||||
AlternativeTitles = (string[])AlternativeTitles?.Clone(),
|
||||
Tag = Tag,
|
||||
SortScore = SortScore,
|
||||
DefaultValues = (object[])DefaultValues?.Clone(),
|
||||
DefaultType = DefaultType,
|
||||
ConnectionsHints = ConnectionsHints,
|
||||
IndependentBoxes = (int[])IndependentBoxes?.Clone(),
|
||||
DependentBoxes = (int[])DependentBoxes?.Clone(),
|
||||
DependentBoxFilter = DependentBoxFilter,
|
||||
Elements = (NodeElementArchetype[])Elements?.Clone(),
|
||||
TryParseText = TryParseText,
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
protected internal override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
{
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[1];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -52,6 +53,12 @@ namespace FlaxEditor.Surface
|
||||
set => SetValue(2, value, false);
|
||||
}
|
||||
|
||||
private int OrderValue
|
||||
{
|
||||
get => (int)Values[3];
|
||||
set => SetValue(3, value, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public SurfaceComment(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
@@ -67,6 +74,19 @@ namespace FlaxEditor.Surface
|
||||
Title = TitleValue;
|
||||
Color = ColorValue;
|
||||
Size = SizeValue;
|
||||
|
||||
// Order
|
||||
// Backwards compatibility - When opening with an older version send the old comments to the back
|
||||
if (Values.Length < 4)
|
||||
{
|
||||
if (IndexInParent > 0)
|
||||
IndexInParent = 0;
|
||||
OrderValue = IndexInParent;
|
||||
}
|
||||
else if(OrderValue != -1)
|
||||
{
|
||||
IndexInParent = OrderValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -76,6 +96,10 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Randomize color
|
||||
Color = ColorValue = Color.FromHSV(new Random().NextFloat(0, 360), 0.7f, 0.25f, 0.8f);
|
||||
|
||||
if(OrderValue == -1)
|
||||
OrderValue = Context.CommentCount - 1;
|
||||
IndexInParent = OrderValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -314,5 +338,38 @@ namespace FlaxEditor.Surface
|
||||
Color = ColorValue = color;
|
||||
Surface.MarkAsEdited(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location)
|
||||
{
|
||||
base.OnShowSecondaryContextMenu(menu, location);
|
||||
|
||||
menu.AddSeparator();
|
||||
ContextMenuChildMenu cmOrder = menu.AddChildMenu("Order");
|
||||
{
|
||||
cmOrder.ContextMenu.AddButton("Bring Forward", () =>
|
||||
{
|
||||
if(IndexInParent < Context.CommentCount-1)
|
||||
IndexInParent++;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
cmOrder.ContextMenu.AddButton("Bring to Front", () =>
|
||||
{
|
||||
IndexInParent = Context.CommentCount-1;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
cmOrder.ContextMenu.AddButton("Send Backward", () =>
|
||||
{
|
||||
if(IndexInParent > 0)
|
||||
IndexInParent--;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
cmOrder.ContextMenu.AddButton("Send to Back", () =>
|
||||
{
|
||||
IndexInParent = 0;
|
||||
OrderValue = IndexInParent;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using System;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
@@ -27,7 +26,7 @@ namespace FlaxEditor.Surface
|
||||
/// <summary>
|
||||
/// Parameter unique ID
|
||||
/// </summary>
|
||||
public Guid ID;
|
||||
public Guid ID = Guid.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Parameter name
|
||||
@@ -49,23 +48,5 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
[NoSerialize, HideInEditor]
|
||||
public readonly SurfaceMeta Meta = new SurfaceMeta();
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new parameter of the given type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>The created parameter.</returns>
|
||||
public static SurfaceParameter Create(ScriptType type, string name)
|
||||
{
|
||||
return new SurfaceParameter
|
||||
{
|
||||
ID = Guid.NewGuid(),
|
||||
IsPublic = true,
|
||||
Name = name,
|
||||
Type = type,
|
||||
Value = TypeUtils.GetDefaultValue(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,13 +541,15 @@ namespace FlaxEditor.Surface
|
||||
Action save, Action showWholeGraph, Action toggleGridSnap, InputActionsContainer actionsContainer,
|
||||
out ToolStripButton saveButton, out ToolStripButton undoButton, out ToolStripButton redoButton, out ToolStripButton gridSnapButton)
|
||||
{
|
||||
var inputOptions = editor.Options.Options.Input;
|
||||
|
||||
// Toolstrip
|
||||
saveButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Save64, save).LinkTooltip("Save");
|
||||
toolStrip.AddSeparator();
|
||||
undoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
redoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
undoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||
redoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||
toolStrip.AddSeparator();
|
||||
toolStrip.AddButton(editor.Icons.Search64, editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
||||
toolStrip.AddButton(editor.Icons.Search64, editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
||||
toolStrip.AddButton(editor.Icons.CenterView64, showWholeGraph).LinkTooltip("Show whole graph");
|
||||
gridSnapButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Stop64, toggleGridSnap).LinkTooltip("Toggle grid snapping for nodes.");
|
||||
gridSnapButton.BackgroundColor = Style.Current.Background; // Default color for grid snap button.
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
/// <param name="groupId">The group ID.</param>
|
||||
/// <returns>The node archetype.</returns>
|
||||
protected virtual NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
protected internal virtual NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
{
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[0];
|
||||
|
||||
@@ -726,7 +726,18 @@ namespace FlaxEditor.Surface
|
||||
return null;
|
||||
Rectangle surfaceArea = GetNodesBounds(selection).MakeExpanded(80.0f);
|
||||
|
||||
return _context.CreateComment(ref surfaceArea, string.IsNullOrEmpty(text) ? "Comment" : text, new Color(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
// Order below other selected comments
|
||||
bool hasCommentsSelected = false;
|
||||
int lowestCommentOrder = int.MaxValue;
|
||||
for (int i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (selection[i] is not SurfaceComment || selection[i].IndexInParent >= lowestCommentOrder)
|
||||
continue;
|
||||
hasCommentsSelected = true;
|
||||
lowestCommentOrder = selection[i].IndexInParent;
|
||||
}
|
||||
|
||||
return _context.CreateComment(ref surfaceArea, string.IsNullOrEmpty(text) ? "Comment" : text, new Color(1.0f, 1.0f, 1.0f, 0.2f), hasCommentsSelected ? lowestCommentOrder : -1);
|
||||
}
|
||||
|
||||
private static Rectangle GetNodesBounds(List<SurfaceNode> nodes)
|
||||
|
||||
@@ -920,12 +920,6 @@ namespace FlaxEditor.Surface
|
||||
// Link control
|
||||
control.OnLoaded(action);
|
||||
control.Parent = RootControl;
|
||||
|
||||
if (control is SurfaceComment)
|
||||
{
|
||||
// Move comments to the background
|
||||
control.IndexInParent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -85,6 +85,27 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of surface comments
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used as an alternative to <see cref="Comments"/>, if only the amount of comments is important.
|
||||
/// Is faster and doesn't allocate as much memory
|
||||
/// </remarks>
|
||||
public int CommentCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < RootControl.Children.Count; i++)
|
||||
{
|
||||
if (RootControl.Children[i] is SurfaceComment)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this context is modified (needs saving and flushing with surface data context source).
|
||||
/// </summary>
|
||||
@@ -285,14 +306,16 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="surfaceArea">The surface area to create comment.</param>
|
||||
/// <param name="title">The comment title.</param>
|
||||
/// <param name="color">The comment color.</param>
|
||||
/// <param name="customOrder">The comment order or -1 to use default.</param>
|
||||
/// <returns>The comment object</returns>
|
||||
public virtual SurfaceComment SpawnComment(ref Rectangle surfaceArea, string title, Color color)
|
||||
public virtual SurfaceComment SpawnComment(ref Rectangle surfaceArea, string title, Color color, int customOrder = -1)
|
||||
{
|
||||
var values = new object[]
|
||||
{
|
||||
title, // Title
|
||||
color, // Color
|
||||
surfaceArea.Size, // Size
|
||||
customOrder, // Order
|
||||
};
|
||||
return (SurfaceComment)SpawnNode(7, 11, surfaceArea.Location, values);
|
||||
}
|
||||
@@ -303,11 +326,12 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="surfaceArea">The surface area to create comment.</param>
|
||||
/// <param name="title">The comment title.</param>
|
||||
/// <param name="color">The comment color.</param>
|
||||
/// <param name="customOrder">The comment order or -1 to use default.</param>
|
||||
/// <returns>The comment object</returns>
|
||||
public SurfaceComment CreateComment(ref Rectangle surfaceArea, string title, Color color)
|
||||
public SurfaceComment CreateComment(ref Rectangle surfaceArea, string title, Color color, int customOrder = -1)
|
||||
{
|
||||
// Create comment
|
||||
var comment = SpawnComment(ref surfaceArea, title, color);
|
||||
var comment = SpawnComment(ref surfaceArea, title, color, customOrder);
|
||||
if (comment == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to create comment.");
|
||||
|
||||
@@ -17,6 +17,7 @@ using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
{
|
||||
@@ -258,6 +259,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public IVisjectSurfaceWindow Window;
|
||||
|
||||
/// <summary>
|
||||
/// The identifier of the parameter. Empty to auto generate it.
|
||||
/// </summary>
|
||||
public Guid Id = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// True if adding, false if removing parameter.
|
||||
/// </summary>
|
||||
@@ -278,6 +284,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public ScriptType Type;
|
||||
|
||||
/// <summary>
|
||||
/// The value to initialize the parameter with. Can be null to use default one for the parameter type.
|
||||
/// </summary>
|
||||
public object InitValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ActionString => IsAdd ? "Add parameter" : "Remove parameter";
|
||||
|
||||
@@ -304,7 +315,14 @@ namespace FlaxEditor.Surface
|
||||
var type = Type;
|
||||
if (IsAdd && type.Type == typeof(NormalMap))
|
||||
type = new ScriptType(typeof(Texture));
|
||||
var param = SurfaceParameter.Create(type, Name);
|
||||
var param = new SurfaceParameter
|
||||
{
|
||||
ID = Id,
|
||||
IsPublic = true,
|
||||
Name = Name,
|
||||
Type = type,
|
||||
Value = InitValue ?? TypeUtils.GetDefaultValue(type),
|
||||
};
|
||||
if (IsAdd && Type.Type == typeof(NormalMap))
|
||||
param.Value = FlaxEngine.Content.LoadAsyncInternal<Texture>("Engine/Textures/NormalTexture");
|
||||
Window.VisjectSurface.Parameters.Insert(Index, param);
|
||||
@@ -726,6 +744,8 @@ namespace FlaxEditor.Surface
|
||||
protected VisjectSurfaceWindow(Editor editor, AssetItem item, bool useTabs = false)
|
||||
: base(editor, item)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
// Undo
|
||||
_undo = new FlaxEditor.Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
@@ -1054,7 +1074,6 @@ namespace FlaxEditor.Surface
|
||||
public virtual void OnParamRemoveUndo()
|
||||
{
|
||||
_refreshPropertiesOnLoad = true;
|
||||
//_propertiesEditor.BuildLayoutOnUpdate();
|
||||
_propertiesEditor.BuildLayout();
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
protected internal override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId)
|
||||
{
|
||||
groupId = 6;
|
||||
return Archetypes.Parameters.Nodes[2];
|
||||
|
||||
@@ -225,6 +225,7 @@ namespace FlaxEngine.Tools
|
||||
var cloth = _cloth;
|
||||
if (cloth == null)
|
||||
return;
|
||||
var hasPaintInput = Owner.IsLeftMouseButtonDown && !Owner.IsAltKeyDown;
|
||||
|
||||
// Perform detailed tracing to find cursor location for the brush
|
||||
var ray = Owner.MouseRay;
|
||||
@@ -240,7 +241,7 @@ namespace FlaxEngine.Tools
|
||||
// Cursor hit other object or nothing
|
||||
PaintEnd();
|
||||
|
||||
if (Owner.IsLeftMouseButtonDown)
|
||||
if (hasPaintInput)
|
||||
{
|
||||
// Select something else
|
||||
var view = new Ray(Owner.ViewPosition, Owner.ViewDirection);
|
||||
@@ -253,7 +254,7 @@ namespace FlaxEngine.Tools
|
||||
}
|
||||
|
||||
// Handle painting
|
||||
if (Owner.IsLeftMouseButtonDown)
|
||||
if (hasPaintInput)
|
||||
PaintStart();
|
||||
else
|
||||
PaintEnd();
|
||||
|
||||
@@ -290,7 +290,7 @@ namespace FlaxEditor.Tools.Terrain
|
||||
|
||||
var patchCoord = Gizmo.SelectedPatchCoord;
|
||||
var chunkCoord = Gizmo.SelectedChunkCoord;
|
||||
var action = new EditChunkMaterialAction(CarveTab.SelectedTerrain, ref patchCoord, ref chunkCoord, _chunkOverrideMaterial.SelectedAsset as MaterialBase);
|
||||
var action = new EditChunkMaterialAction(CarveTab.SelectedTerrain, ref patchCoord, ref chunkCoord, _chunkOverrideMaterial.Validator.SelectedAsset as MaterialBase);
|
||||
action.Do();
|
||||
CarveTab.Editor.Undo.AddAction(action);
|
||||
}
|
||||
@@ -336,12 +336,12 @@ namespace FlaxEditor.Tools.Terrain
|
||||
_isUpdatingUI = true;
|
||||
if (terrain.HasPatch(ref patchCoord))
|
||||
{
|
||||
_chunkOverrideMaterial.SelectedAsset = terrain.GetChunkOverrideMaterial(ref patchCoord, ref chunkCoord);
|
||||
_chunkOverrideMaterial.Validator.SelectedAsset = terrain.GetChunkOverrideMaterial(ref patchCoord, ref chunkCoord);
|
||||
_chunkOverrideMaterial.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_chunkOverrideMaterial.SelectedAsset = null;
|
||||
_chunkOverrideMaterial.Validator.SelectedAsset = null;
|
||||
_chunkOverrideMaterial.Enabled = false;
|
||||
}
|
||||
_isUpdatingUI = false;
|
||||
|
||||
@@ -75,6 +75,11 @@ namespace FlaxEditor.Actions
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
public int GetOrderInParent()
|
||||
{
|
||||
return _orderInParent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new added script undo action.
|
||||
/// </summary>
|
||||
@@ -184,6 +189,7 @@ namespace FlaxEditor.Actions
|
||||
script.Parent = parentActor;
|
||||
if (_orderInParent != -1)
|
||||
script.OrderInParent = _orderInParent;
|
||||
_orderInParent = script.OrderInParent; // Ensure order is correct for script that want to use it later
|
||||
if (_prefabObjectId != Guid.Empty)
|
||||
SceneObject.Internal_LinkPrefab(Object.GetUnmanagedPtr(script), ref _prefabId, ref _prefabObjectId);
|
||||
Editor.Instance.Scene.MarkSceneEdited(parentActor.Scene);
|
||||
|
||||
@@ -394,8 +394,12 @@ bool EditorUtilities::UpdateExeIcon(const String& path, const TextureData& icon)
|
||||
// - icon/cursor/etc data
|
||||
|
||||
std::fstream stream;
|
||||
#if PLATFORM_WINDOWS
|
||||
stream.open(path.Get(), std::ios::in | std::ios::out | std::ios::binary);
|
||||
#else
|
||||
StringAsANSI<> pathAnsi(path.Get());
|
||||
stream.open(pathAnsi.Get(), std::ios::in | std::ios::out | std::ios::binary);
|
||||
#endif
|
||||
if (!stream.is_open())
|
||||
{
|
||||
LOG(Warning, "Cannot open file");
|
||||
|
||||
@@ -73,6 +73,7 @@ Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
outputColor.A = 255;
|
||||
return outputColor;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +259,10 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
// Pan
|
||||
if (input.IsPanning)
|
||||
{
|
||||
var panningSpeed = 0.8f;
|
||||
var panningSpeed = (Viewport.RelativePanning)
|
||||
? Mathf.Abs((position - TargetPoint).Length) * 0.005f
|
||||
: Viewport.PanningSpeed;
|
||||
|
||||
if (Viewport.InvertPanning)
|
||||
{
|
||||
position += up * (mouseDelta.Y * panningSpeed);
|
||||
|
||||
@@ -41,6 +41,8 @@ namespace FlaxEditor.Viewport
|
||||
Gizmos[i].Update(deltaTime);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GizmosCollection Gizmos { get; }
|
||||
|
||||
@@ -128,12 +128,26 @@ namespace FlaxEditor.Viewport
|
||||
public const int FpsCameraFilteringFrames = 3;
|
||||
|
||||
/// <summary>
|
||||
/// The speed widget button.
|
||||
/// The camera settings widget.
|
||||
/// </summary>
|
||||
protected ViewportWidgetButton _speedWidget;
|
||||
protected ViewportWidgetsContainer _cameraWidget;
|
||||
|
||||
/// <summary>
|
||||
/// The camera settings widget button.
|
||||
/// </summary>
|
||||
protected ViewportWidgetButton _cameraButton;
|
||||
|
||||
/// <summary>
|
||||
/// The orthographic mode widget button.
|
||||
/// </summary>
|
||||
protected ViewportWidgetButton _orthographicModeButton;
|
||||
|
||||
private readonly Editor _editor;
|
||||
|
||||
private float _mouseSensitivity;
|
||||
private float _movementSpeed;
|
||||
private float _minMovementSpeed;
|
||||
private float _maxMovementSpeed;
|
||||
private float _mouseAccelerationScale;
|
||||
private bool _useMouseFiltering;
|
||||
private bool _useMouseAcceleration;
|
||||
@@ -174,11 +188,17 @@ namespace FlaxEditor.Viewport
|
||||
private float _fieldOfView;
|
||||
private float _nearPlane;
|
||||
private float _farPlane;
|
||||
private float _orthoSize = 1.0f;
|
||||
private bool _isOrtho = false;
|
||||
private float _wheelMovementChangeDeltaSum = 0;
|
||||
private float _orthoSize;
|
||||
private bool _isOrtho;
|
||||
private bool _useCameraEasing;
|
||||
private float _cameraEasingDegree;
|
||||
private float _panningSpeed;
|
||||
private bool _relativePanning;
|
||||
private bool _invertPanning;
|
||||
|
||||
private int _speedStep;
|
||||
private int _maxSpeedSteps;
|
||||
|
||||
/// <summary>
|
||||
/// Speed of the mouse.
|
||||
/// </summary>
|
||||
@@ -189,6 +209,25 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public float MouseWheelZoomSpeedFactor = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Format of the text for the camera move speed.
|
||||
/// </summary>
|
||||
private string MovementSpeedTextFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon || Mathf.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon)
|
||||
return "{0:0.##}";
|
||||
|
||||
if (_movementSpeed < 10.0f)
|
||||
return "{0:0.00}";
|
||||
else if (_movementSpeed < 100.0f)
|
||||
return "{0:0.0}";
|
||||
else
|
||||
return "{0:#}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera movement speed.
|
||||
/// </summary>
|
||||
@@ -197,19 +236,40 @@ namespace FlaxEditor.Viewport
|
||||
get => _movementSpeed;
|
||||
set
|
||||
{
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
{
|
||||
if (Math.Abs(value - EditorViewportCameraSpeedValues[i]) < 0.001f)
|
||||
{
|
||||
_movementSpeed = EditorViewportCameraSpeedValues[i];
|
||||
if (_speedWidget != null)
|
||||
_speedWidget.Text = _movementSpeed.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
_movementSpeed = value;
|
||||
|
||||
if (_cameraButton != null)
|
||||
_cameraButton.Text = string.Format(MovementSpeedTextFormat, _movementSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum camera movement speed.
|
||||
/// </summary>
|
||||
public float MinMovementSpeed
|
||||
{
|
||||
get => _minMovementSpeed;
|
||||
set => _minMovementSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum camera movement speed.
|
||||
/// </summary>
|
||||
public float MaxMovementSpeed
|
||||
{
|
||||
get => _maxMovementSpeed;
|
||||
set => _maxMovementSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera easing mode.
|
||||
/// </summary>
|
||||
public bool UseCameraEasing
|
||||
{
|
||||
get => _useCameraEasing;
|
||||
set => _useCameraEasing = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse movement position delta (user press and move).
|
||||
/// </summary>
|
||||
@@ -396,6 +456,15 @@ namespace FlaxEditor.Viewport
|
||||
set => _isOrtho = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the panning speed should be relative to the camera target.
|
||||
/// </summary>
|
||||
public bool RelativePanning
|
||||
{
|
||||
get => _relativePanning;
|
||||
set => _relativePanning = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the panning direction is inverted.
|
||||
/// </summary>
|
||||
@@ -405,6 +474,15 @@ namespace FlaxEditor.Viewport
|
||||
set => _invertPanning = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera panning speed.
|
||||
/// </summary>
|
||||
public float PanningSpeed
|
||||
{
|
||||
get => _panningSpeed;
|
||||
set => _panningSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input actions collection to processed during user input.
|
||||
/// </summary>
|
||||
@@ -419,6 +497,8 @@ namespace FlaxEditor.Viewport
|
||||
public EditorViewport(SceneRenderTask task, ViewportCamera camera, bool useWidgets)
|
||||
: base(task)
|
||||
{
|
||||
_editor = Editor.Instance;
|
||||
|
||||
_mouseAccelerationScale = 0.1f;
|
||||
_useMouseFiltering = false;
|
||||
_useMouseAcceleration = false;
|
||||
@@ -431,43 +511,299 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Setup options
|
||||
{
|
||||
var options = Editor.Instance.Options.Options;
|
||||
_movementSpeed = options.Viewport.DefaultMovementSpeed;
|
||||
_nearPlane = options.Viewport.DefaultNearPlane;
|
||||
_farPlane = options.Viewport.DefaultFarPlane;
|
||||
_fieldOfView = options.Viewport.DefaultFieldOfView;
|
||||
_invertPanning = options.Viewport.DefaultInvertPanning;
|
||||
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
OnEditorOptionsChanged(options);
|
||||
SetupViewportOptions();
|
||||
}
|
||||
|
||||
// Initialize camera values from cache
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraMovementSpeedValue", out var cachedState))
|
||||
MovementSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraMinMovementSpeedValue", out cachedState))
|
||||
_minMovementSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraMaxMovementSpeedValue", out cachedState))
|
||||
_maxMovementSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("UseCameraEasingState", out cachedState))
|
||||
_useCameraEasing = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraPanningSpeedValue", out cachedState))
|
||||
_panningSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraInvertPanningState", out cachedState))
|
||||
_invertPanning = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraRelativePanningState", out cachedState))
|
||||
_relativePanning = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicState", out cachedState))
|
||||
_isOrtho = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicSizeValue", out cachedState))
|
||||
_orthoSize = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraFieldOfViewValue", out cachedState))
|
||||
_fieldOfView = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraNearPlaneValue", out cachedState))
|
||||
_nearPlane = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraFarPlaneValue", out cachedState))
|
||||
_farPlane = float.Parse(cachedState);
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
var largestText = "Invert Panning";
|
||||
#region Camera settings widget
|
||||
|
||||
var largestText = "Relative Panning";
|
||||
var textSize = Style.Current.FontMedium.MeasureText(largestText);
|
||||
var xLocationForExtras = textSize.X + 5;
|
||||
// Camera speed widget
|
||||
var camSpeed = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
var camSpeedCM = new ContextMenu();
|
||||
var camSpeedButton = new ViewportWidgetButton(_movementSpeed.ToString(), Editor.Instance.Icons.CamSpeed32, camSpeedCM)
|
||||
var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X;
|
||||
|
||||
// Camera Settings Widget
|
||||
_cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
|
||||
// Camera Settings Menu
|
||||
var cameraCM = new ContextMenu();
|
||||
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), Editor.Instance.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
|
||||
{
|
||||
Tag = this,
|
||||
TooltipText = "Camera speed scale"
|
||||
TooltipText = "Camera Settings",
|
||||
Parent = _cameraWidget
|
||||
};
|
||||
_speedWidget = camSpeedButton;
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
{
|
||||
var v = EditorViewportCameraSpeedValues[i];
|
||||
var button = camSpeedCM.AddButton(v.ToString());
|
||||
button.Tag = v;
|
||||
}
|
||||
camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag;
|
||||
camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide;
|
||||
camSpeedButton.Parent = camSpeed;
|
||||
camSpeed.Parent = this;
|
||||
_cameraWidget.Parent = this;
|
||||
|
||||
// Orthographic/Perspective Mode Widget
|
||||
_orthographicModeButton = new ViewportWidgetButton(string.Empty, Editor.Instance.Icons.CamSpeed32, null, true)
|
||||
{
|
||||
Checked = !_isOrtho,
|
||||
TooltipText = "Toggle Orthographic/Perspective Mode",
|
||||
Parent = _cameraWidget
|
||||
};
|
||||
_orthographicModeButton.Toggled += OnOrthographicModeToggled;
|
||||
|
||||
// Camera Speed
|
||||
var camSpeedButton = cameraCM.AddButton("Camera Speed");
|
||||
camSpeedButton.CloseMenuOnClick = false;
|
||||
var camSpeedValue = new FloatValueBox(_movementSpeed, xLocationForExtras, 2, 70.0f, _minMovementSpeed, _maxMovementSpeed, 0.5f)
|
||||
{
|
||||
Parent = camSpeedButton
|
||||
};
|
||||
|
||||
camSpeedValue.ValueChanged += () => OnMovementSpeedChanged(camSpeedValue);
|
||||
cameraCM.VisibleChanged += control => camSpeedValue.Value = _movementSpeed;
|
||||
|
||||
// Minimum & Maximum Camera Speed
|
||||
var minCamSpeedButton = cameraCM.AddButton("Min Cam Speed");
|
||||
minCamSpeedButton.CloseMenuOnClick = false;
|
||||
var minCamSpeedValue = new FloatValueBox(_minMovementSpeed, xLocationForExtras, 2, 70.0f, 0.05f, _maxMovementSpeed, 0.5f)
|
||||
{
|
||||
Parent = minCamSpeedButton
|
||||
};
|
||||
var maxCamSpeedButton = cameraCM.AddButton("Max Cam Speed");
|
||||
maxCamSpeedButton.CloseMenuOnClick = false;
|
||||
var maxCamSpeedValue = new FloatValueBox(_maxMovementSpeed, xLocationForExtras, 2, 70.0f, _minMovementSpeed, 1000.0f, 0.5f)
|
||||
{
|
||||
Parent = maxCamSpeedButton
|
||||
};
|
||||
|
||||
minCamSpeedValue.ValueChanged += () =>
|
||||
{
|
||||
OnMinMovementSpeedChanged(minCamSpeedValue);
|
||||
|
||||
maxCamSpeedValue.MinValue = minCamSpeedValue.Value;
|
||||
|
||||
if (Math.Abs(camSpeedValue.MinValue - minCamSpeedValue.Value) > Mathf.Epsilon)
|
||||
camSpeedValue.MinValue = minCamSpeedValue.Value;
|
||||
};
|
||||
cameraCM.VisibleChanged += control => minCamSpeedValue.Value = _minMovementSpeed;
|
||||
maxCamSpeedValue.ValueChanged += () =>
|
||||
{
|
||||
OnMaxMovementSpeedChanged(maxCamSpeedValue);
|
||||
|
||||
minCamSpeedValue.MaxValue = maxCamSpeedValue.Value;
|
||||
|
||||
if (Math.Abs(camSpeedValue.MaxValue - maxCamSpeedValue.Value) > Mathf.Epsilon)
|
||||
camSpeedValue.MaxValue = maxCamSpeedValue.Value;
|
||||
};
|
||||
cameraCM.VisibleChanged += control => maxCamSpeedValue.Value = _maxMovementSpeed;
|
||||
|
||||
// Camera Easing
|
||||
{
|
||||
var useCameraEasing = cameraCM.AddButton("Camera Easing");
|
||||
useCameraEasing.CloseMenuOnClick = false;
|
||||
var useCameraEasingValue = new CheckBox(xLocationForExtras, 2, _useCameraEasing)
|
||||
{
|
||||
Parent = useCameraEasing
|
||||
};
|
||||
|
||||
useCameraEasingValue.StateChanged += OnCameraEasingToggled;
|
||||
cameraCM.VisibleChanged += control => useCameraEasingValue.Checked = _useCameraEasing;
|
||||
}
|
||||
|
||||
// Panning Speed
|
||||
{
|
||||
var panningSpeed = cameraCM.AddButton("Panning Speed");
|
||||
panningSpeed.CloseMenuOnClick = false;
|
||||
var panningSpeedValue = new FloatValueBox(_panningSpeed, xLocationForExtras, 2, 70.0f, 0.01f, 128.0f, 0.1f)
|
||||
{
|
||||
Parent = panningSpeed
|
||||
};
|
||||
|
||||
panningSpeedValue.ValueChanged += () => OnPanningSpeedChanged(panningSpeedValue);
|
||||
cameraCM.VisibleChanged += control =>
|
||||
{
|
||||
panningSpeed.Visible = !_relativePanning;
|
||||
panningSpeedValue.Value = _panningSpeed;
|
||||
};
|
||||
}
|
||||
|
||||
// Relative Panning
|
||||
{
|
||||
var relativePanning = cameraCM.AddButton("Relative Panning");
|
||||
relativePanning.CloseMenuOnClick = false;
|
||||
var relativePanningValue = new CheckBox(xLocationForExtras, 2, _relativePanning)
|
||||
{
|
||||
Parent = relativePanning
|
||||
};
|
||||
|
||||
relativePanningValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _relativePanning)
|
||||
{
|
||||
OnRelativePanningToggled(checkBox);
|
||||
cameraCM.Hide();
|
||||
}
|
||||
};
|
||||
cameraCM.VisibleChanged += control => relativePanningValue.Checked = _relativePanning;
|
||||
}
|
||||
|
||||
// Invert Panning
|
||||
{
|
||||
var invertPanning = cameraCM.AddButton("Invert Panning");
|
||||
invertPanning.CloseMenuOnClick = false;
|
||||
var invertPanningValue = new CheckBox(xLocationForExtras, 2, _invertPanning)
|
||||
{
|
||||
Parent = invertPanning
|
||||
};
|
||||
|
||||
invertPanningValue.StateChanged += OnInvertPanningToggled;
|
||||
cameraCM.VisibleChanged += control => invertPanningValue.Checked = _invertPanning;
|
||||
}
|
||||
|
||||
cameraCM.AddSeparator();
|
||||
|
||||
// Camera Viewpoints
|
||||
{
|
||||
var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu;
|
||||
for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++)
|
||||
{
|
||||
var co = EditorViewportCameraViewpointValues[i];
|
||||
var button = cameraView.AddButton(co.Name);
|
||||
button.Tag = co.Orientation;
|
||||
}
|
||||
|
||||
cameraView.ButtonClicked += OnViewpointChanged;
|
||||
}
|
||||
|
||||
// Orthographic Mode
|
||||
{
|
||||
var ortho = cameraCM.AddButton("Orthographic");
|
||||
ortho.CloseMenuOnClick = false;
|
||||
var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho)
|
||||
{
|
||||
Parent = ortho
|
||||
};
|
||||
|
||||
orthoValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _isOrtho)
|
||||
{
|
||||
OnOrthographicModeToggled(checkBox);
|
||||
cameraCM.Hide();
|
||||
}
|
||||
};
|
||||
cameraCM.VisibleChanged += control => orthoValue.Checked = _isOrtho;
|
||||
}
|
||||
|
||||
// Field of View
|
||||
{
|
||||
var fov = cameraCM.AddButton("Field Of View");
|
||||
fov.CloseMenuOnClick = false;
|
||||
var fovValue = new FloatValueBox(_fieldOfView, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f)
|
||||
{
|
||||
Parent = fov
|
||||
};
|
||||
|
||||
fovValue.ValueChanged += () => OnFieldOfViewChanged(fovValue);
|
||||
cameraCM.VisibleChanged += control =>
|
||||
{
|
||||
fov.Visible = !_isOrtho;
|
||||
fovValue.Value = _fieldOfView;
|
||||
};
|
||||
}
|
||||
|
||||
// Orthographic Scale
|
||||
{
|
||||
var orthoSize = cameraCM.AddButton("Ortho Scale");
|
||||
orthoSize.CloseMenuOnClick = false;
|
||||
var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f)
|
||||
{
|
||||
Parent = orthoSize
|
||||
};
|
||||
|
||||
orthoSizeValue.ValueChanged += () => OnOrthographicSizeChanged(orthoSizeValue);
|
||||
cameraCM.VisibleChanged += control =>
|
||||
{
|
||||
orthoSize.Visible = _isOrtho;
|
||||
orthoSizeValue.Value = _orthoSize;
|
||||
};
|
||||
}
|
||||
|
||||
// Near Plane
|
||||
{
|
||||
var nearPlane = cameraCM.AddButton("Near Plane");
|
||||
nearPlane.CloseMenuOnClick = false;
|
||||
var nearPlaneValue = new FloatValueBox(_nearPlane, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f)
|
||||
{
|
||||
Parent = nearPlane
|
||||
};
|
||||
|
||||
nearPlaneValue.ValueChanged += () => OnNearPlaneChanged(nearPlaneValue);
|
||||
cameraCM.VisibleChanged += control => nearPlaneValue.Value = _nearPlane;
|
||||
}
|
||||
|
||||
// Far Plane
|
||||
{
|
||||
var farPlane = cameraCM.AddButton("Far Plane");
|
||||
farPlane.CloseMenuOnClick = false;
|
||||
var farPlaneValue = new FloatValueBox(_farPlane, xLocationForExtras, 2, 70.0f, 10.0f)
|
||||
{
|
||||
Parent = farPlane
|
||||
};
|
||||
|
||||
farPlaneValue.ValueChanged += () => OnFarPlaneChanged(farPlaneValue);
|
||||
cameraCM.VisibleChanged += control => farPlaneValue.Value = _farPlane;
|
||||
}
|
||||
|
||||
cameraCM.AddSeparator();
|
||||
|
||||
// Reset Button
|
||||
{
|
||||
var reset = cameraCM.AddButton("Reset to default");
|
||||
reset.ButtonClicked += button =>
|
||||
{
|
||||
SetupViewportOptions();
|
||||
|
||||
// if the context menu is opened without triggering the value changes beforehand,
|
||||
// the movement speed will not be correctly reset to its default value in certain cases
|
||||
// therefore, a UI update needs to be triggered here
|
||||
minCamSpeedValue.Value = _minMovementSpeed;
|
||||
camSpeedValue.Value = _movementSpeed;
|
||||
maxCamSpeedValue.Value = _maxMovementSpeed;
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Camera settings widget
|
||||
|
||||
#region View mode widget
|
||||
|
||||
largestText = "Brightness";
|
||||
textSize = Style.Current.FontMedium.MeasureText(largestText);
|
||||
xLocationForExtras = textSize.X + 5;
|
||||
|
||||
// View mode widget
|
||||
var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
ViewWidgetButtonMenu = new ContextMenu();
|
||||
var viewModeButton = new ViewportWidgetButton("View", SpriteHandle.Invalid, ViewWidgetButtonMenu)
|
||||
@@ -484,8 +820,8 @@ namespace FlaxEditor.Viewport
|
||||
// Show FPS
|
||||
{
|
||||
InitFpsCounter();
|
||||
_showFpsButon = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter);
|
||||
_showFpsButon.CloseMenuOnClick = false;
|
||||
_showFpsButton = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter);
|
||||
_showFpsButton.CloseMenuOnClick = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,12 +929,14 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
ref var vv = ref v.Options[j];
|
||||
var button = childMenu.AddButton(vv.Name);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Tag = vv.Mode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var button = debugView.AddButton(v.Name);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Tag = v.Mode;
|
||||
}
|
||||
}
|
||||
@@ -608,104 +946,6 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
|
||||
// Orthographic
|
||||
{
|
||||
var ortho = ViewWidgetButtonMenu.AddButton("Orthographic");
|
||||
ortho.CloseMenuOnClick = false;
|
||||
var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho)
|
||||
{
|
||||
Parent = ortho
|
||||
};
|
||||
orthoValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _isOrtho)
|
||||
{
|
||||
_isOrtho = checkBox.Checked;
|
||||
ViewWidgetButtonMenu.Hide();
|
||||
if (_isOrtho)
|
||||
{
|
||||
var orient = ViewOrientation;
|
||||
OrientViewport(ref orient);
|
||||
}
|
||||
}
|
||||
};
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => orthoValue.Checked = _isOrtho;
|
||||
}
|
||||
|
||||
// Camera Viewpoints
|
||||
{
|
||||
var cameraView = ViewWidgetButtonMenu.AddChildMenu("Viewpoints").ContextMenu;
|
||||
for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++)
|
||||
{
|
||||
var co = EditorViewportCameraViewpointValues[i];
|
||||
var button = cameraView.AddButton(co.Name);
|
||||
button.Tag = co.Orientation;
|
||||
}
|
||||
cameraView.ButtonClicked += button =>
|
||||
{
|
||||
var orient = Quaternion.Euler((Float3)button.Tag);
|
||||
OrientViewport(ref orient);
|
||||
};
|
||||
}
|
||||
|
||||
// Field of View
|
||||
{
|
||||
var fov = ViewWidgetButtonMenu.AddButton("Field Of View");
|
||||
fov.CloseMenuOnClick = false;
|
||||
var fovValue = new FloatValueBox(1, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f)
|
||||
{
|
||||
Parent = fov
|
||||
};
|
||||
|
||||
fovValue.ValueChanged += () => _fieldOfView = fovValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
fov.Visible = !_isOrtho;
|
||||
fovValue.Value = _fieldOfView;
|
||||
};
|
||||
}
|
||||
|
||||
// Ortho Scale
|
||||
{
|
||||
var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale");
|
||||
orthoSize.CloseMenuOnClick = false;
|
||||
var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f)
|
||||
{
|
||||
Parent = orthoSize
|
||||
};
|
||||
|
||||
orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
orthoSize.Visible = _isOrtho;
|
||||
orthoSizeValue.Value = _orthoSize;
|
||||
};
|
||||
}
|
||||
|
||||
// Near Plane
|
||||
{
|
||||
var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane");
|
||||
nearPlane.CloseMenuOnClick = false;
|
||||
var nearPlaneValue = new FloatValueBox(2.0f, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f)
|
||||
{
|
||||
Parent = nearPlane
|
||||
};
|
||||
nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane;
|
||||
}
|
||||
|
||||
// Far Plane
|
||||
{
|
||||
var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane");
|
||||
farPlane.CloseMenuOnClick = false;
|
||||
var farPlaneValue = new FloatValueBox(1000, xLocationForExtras, 2, 70.0f, 10.0f)
|
||||
{
|
||||
Parent = farPlane
|
||||
};
|
||||
farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane;
|
||||
}
|
||||
|
||||
// Brightness
|
||||
{
|
||||
var brightness = ViewWidgetButtonMenu.AddButton("Brightness");
|
||||
@@ -730,24 +970,7 @@ namespace FlaxEditor.Viewport
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale;
|
||||
}
|
||||
|
||||
// Invert Panning
|
||||
{
|
||||
var invert = ViewWidgetButtonMenu.AddButton("Invert Panning");
|
||||
invert.CloseMenuOnClick = false;
|
||||
var invertValue = new CheckBox(xLocationForExtras, 2, _invertPanning)
|
||||
{
|
||||
Parent = invert
|
||||
};
|
||||
|
||||
invertValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _invertPanning)
|
||||
{
|
||||
_invertPanning = checkBox.Checked;
|
||||
}
|
||||
};
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning;
|
||||
}
|
||||
#endregion View mode widget
|
||||
}
|
||||
|
||||
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
|
||||
@@ -764,6 +987,135 @@ namespace FlaxEditor.Viewport
|
||||
task.Begin += OnRenderBegin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the viewport options to the default values.
|
||||
/// </summary>
|
||||
private void SetupViewportOptions()
|
||||
{
|
||||
var options = Editor.Instance.Options.Options;
|
||||
_minMovementSpeed = options.Viewport.MinMovementSpeed;
|
||||
MovementSpeed = options.Viewport.MovementSpeed;
|
||||
_maxMovementSpeed = options.Viewport.MaxMovementSpeed;
|
||||
_useCameraEasing = options.Viewport.UseCameraEasing;
|
||||
_panningSpeed = options.Viewport.PanningSpeed;
|
||||
_invertPanning = options.Viewport.InvertPanning;
|
||||
_relativePanning = options.Viewport.UseRelativePanning;
|
||||
|
||||
_isOrtho = options.Viewport.UseOrthographicProjection;
|
||||
_orthoSize = options.Viewport.OrthographicScale;
|
||||
_fieldOfView = options.Viewport.FieldOfView;
|
||||
_nearPlane = options.Viewport.NearPlane;
|
||||
_farPlane = options.Viewport.FarPlane;
|
||||
|
||||
OnEditorOptionsChanged(options);
|
||||
}
|
||||
|
||||
private void OnMovementSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
var value = Mathf.Clamp(control.Value, _minMovementSpeed, _maxMovementSpeed);
|
||||
MovementSpeed = value;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnMinMovementSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
var value = Mathf.Clamp(control.Value, 0.05f, _maxMovementSpeed);
|
||||
_minMovementSpeed = value;
|
||||
|
||||
if (_movementSpeed < value)
|
||||
MovementSpeed = value;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("CameraMinMovementSpeedValue", _minMovementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnMaxMovementSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
var value = Mathf.Clamp(control.Value, _minMovementSpeed, 1000.0f);
|
||||
_maxMovementSpeed = value;
|
||||
|
||||
if (_movementSpeed > value)
|
||||
MovementSpeed = value;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("CameraMaxMovementSpeedValue", _maxMovementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnCameraEasingToggled(Control control)
|
||||
{
|
||||
_useCameraEasing = !_useCameraEasing;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("UseCameraEasingState", _useCameraEasing.ToString());
|
||||
}
|
||||
|
||||
private void OnPanningSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
_panningSpeed = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraPanningSpeedValue", _panningSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnRelativePanningToggled(Control control)
|
||||
{
|
||||
_relativePanning = !_relativePanning;
|
||||
_editor.ProjectCache.SetCustomData("CameraRelativePanningState", _relativePanning.ToString());
|
||||
}
|
||||
|
||||
private void OnInvertPanningToggled(Control control)
|
||||
{
|
||||
_invertPanning = !_invertPanning;
|
||||
_editor.ProjectCache.SetCustomData("CameraInvertPanningState", _invertPanning.ToString());
|
||||
}
|
||||
|
||||
|
||||
private void OnViewpointChanged(ContextMenuButton button)
|
||||
{
|
||||
var orient = Quaternion.Euler((Float3)button.Tag);
|
||||
OrientViewport(ref orient);
|
||||
}
|
||||
|
||||
private void OnFieldOfViewChanged(FloatValueBox control)
|
||||
{
|
||||
_fieldOfView = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraFieldOfViewValue", _fieldOfView.ToString());
|
||||
}
|
||||
|
||||
private void OnOrthographicModeToggled(Control control)
|
||||
{
|
||||
_isOrtho = !_isOrtho;
|
||||
|
||||
if (_orthographicModeButton != null)
|
||||
_orthographicModeButton.Checked = !_isOrtho;
|
||||
|
||||
if (_isOrtho)
|
||||
{
|
||||
var orient = ViewOrientation;
|
||||
OrientViewport(ref orient);
|
||||
}
|
||||
|
||||
_editor.ProjectCache.SetCustomData("CameraOrthographicState", _isOrtho.ToString());
|
||||
}
|
||||
|
||||
private void OnOrthographicSizeChanged(FloatValueBox control)
|
||||
{
|
||||
_orthoSize = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraOrthographicSizeValue", _orthoSize.ToString());
|
||||
}
|
||||
|
||||
private void OnNearPlaneChanged(FloatValueBox control)
|
||||
{
|
||||
_nearPlane = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _nearPlane.ToString());
|
||||
}
|
||||
|
||||
private void OnFarPlaneChanged(FloatValueBox control)
|
||||
{
|
||||
_farPlane = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _farPlane.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this viewport is using mouse currently (eg. user moving objects).
|
||||
/// </summary>
|
||||
@@ -796,33 +1148,59 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCameraMovementProgressChanged()
|
||||
{
|
||||
// prevent NaN
|
||||
if (Math.Abs(_minMovementSpeed - _maxMovementSpeed) < Mathf.Epsilon)
|
||||
{
|
||||
_speedStep = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon)
|
||||
{
|
||||
_speedStep = _maxSpeedSteps;
|
||||
return;
|
||||
}
|
||||
else if (Math.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon)
|
||||
{
|
||||
_speedStep = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate current linear/eased progress
|
||||
var progress = Mathf.Remap(_movementSpeed, _minMovementSpeed, _maxMovementSpeed, 0.0f, 1.0f);
|
||||
|
||||
if (_useCameraEasing)
|
||||
progress = Mathf.Pow(progress, 1.0f / _cameraEasingDegree);
|
||||
|
||||
_speedStep = Mathf.RoundToInt(progress * _maxSpeedSteps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases or decreases the camera movement speed.
|
||||
/// </summary>
|
||||
/// <param name="step">The stepping direction for speed adjustment.</param>
|
||||
protected void AdjustCameraMoveSpeed(int step)
|
||||
{
|
||||
int camValueIndex = -1;
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
{
|
||||
if (Mathf.NearEqual(EditorViewportCameraSpeedValues[i], _movementSpeed))
|
||||
{
|
||||
camValueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (camValueIndex == -1)
|
||||
return;
|
||||
_speedStep = Mathf.Clamp(_speedStep + step, 0, _maxSpeedSteps);
|
||||
|
||||
if (step > 0)
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Min(camValueIndex + 1, EditorViewportCameraSpeedValues.Length - 1)];
|
||||
else if (step < 0)
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Max(camValueIndex - 1, 0)];
|
||||
// calculate new linear/eased progress
|
||||
var progress = _useCameraEasing
|
||||
? Mathf.Pow((float)_speedStep / _maxSpeedSteps, _cameraEasingDegree)
|
||||
: (float)_speedStep / _maxSpeedSteps;
|
||||
|
||||
var speed = Mathf.Lerp(_minMovementSpeed, _maxMovementSpeed, progress);
|
||||
MovementSpeed = (float)Math.Round(speed, 3);
|
||||
_editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_mouseSensitivity = options.Viewport.MouseSensitivity;
|
||||
_maxSpeedSteps = options.Viewport.TotalCameraSpeedSteps;
|
||||
_cameraEasingDegree = options.Viewport.CameraEasingDegree;
|
||||
OnCameraMovementProgressChanged();
|
||||
}
|
||||
|
||||
private void OnRenderBegin(RenderTask task, GPUContext context)
|
||||
@@ -861,7 +1239,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
|
||||
private FpsCounter _fpsCounter;
|
||||
private ContextMenuButton _showFpsButon;
|
||||
private ContextMenuButton _showFpsButton;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show or hide FPS counter.
|
||||
@@ -873,7 +1251,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
_fpsCounter.Visible = value;
|
||||
_fpsCounter.Enabled = value;
|
||||
_showFpsButon.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
_showFpsButton.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,8 +1392,6 @@ namespace FlaxEditor.Viewport
|
||||
/// <param name="win">The parent window.</param>
|
||||
protected virtual void OnControlMouseBegin(Window win)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum = 0;
|
||||
|
||||
// Hide cursor and start tracking mouse movement
|
||||
win.StartTrackingMouse(false);
|
||||
win.Cursor = CursorType.Hidden;
|
||||
@@ -1111,8 +1487,8 @@ namespace FlaxEditor.Viewport
|
||||
_camera.Update(deltaTime);
|
||||
useMovementSpeed = _camera.UseMovementSpeed;
|
||||
|
||||
if (_speedWidget != null)
|
||||
_speedWidget.Parent.Visible = useMovementSpeed;
|
||||
if (_cameraButton != null)
|
||||
_cameraButton.Parent.Visible = useMovementSpeed;
|
||||
}
|
||||
|
||||
// Get parent window
|
||||
@@ -1215,18 +1591,8 @@ namespace FlaxEditor.Viewport
|
||||
rmbWheel = useMovementSpeed && (_input.IsMouseRightDown || _isVirtualMouseRightDown) && wheelInUse;
|
||||
if (rmbWheel)
|
||||
{
|
||||
const float step = 4.0f;
|
||||
_wheelMovementChangeDeltaSum += _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity;
|
||||
if (_wheelMovementChangeDeltaSum >= step)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum -= step;
|
||||
AdjustCameraMoveSpeed(1);
|
||||
}
|
||||
else if (_wheelMovementChangeDeltaSum <= -step)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum += step;
|
||||
AdjustCameraMoveSpeed(-1);
|
||||
}
|
||||
var step = _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity;
|
||||
AdjustCameraMoveSpeed(step > 0.0f ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,22 +1861,6 @@ namespace FlaxEditor.Viewport
|
||||
new CameraViewpoint("Bottom", new Float3(-90, 0, 0))
|
||||
};
|
||||
|
||||
private readonly float[] EditorViewportCameraSpeedValues =
|
||||
{
|
||||
0.05f,
|
||||
0.1f,
|
||||
0.25f,
|
||||
0.5f,
|
||||
1.0f,
|
||||
2.0f,
|
||||
4.0f,
|
||||
6.0f,
|
||||
8.0f,
|
||||
16.0f,
|
||||
32.0f,
|
||||
64.0f,
|
||||
};
|
||||
|
||||
private struct ViewModeOptions
|
||||
{
|
||||
public readonly string Name;
|
||||
@@ -1566,28 +1916,17 @@ namespace FlaxEditor.Viewport
|
||||
new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"),
|
||||
};
|
||||
|
||||
private void WidgetCamSpeedShowHide(Control cm)
|
||||
{
|
||||
if (cm.Visible == false)
|
||||
return;
|
||||
|
||||
var ccm = (ContextMenu)cm;
|
||||
foreach (var e in ccm.Items)
|
||||
{
|
||||
if (e is ContextMenuButton b)
|
||||
{
|
||||
var v = (float)b.Tag;
|
||||
b.Icon = Mathf.Abs(MovementSpeed - v) < 0.001f
|
||||
? Style.Current.CheckBoxTick
|
||||
: SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WidgetViewModeShowHideClicked(ContextMenuButton button)
|
||||
{
|
||||
if (button.Tag is ViewMode v)
|
||||
{
|
||||
Task.ViewMode = v;
|
||||
var cm = button.ParentContextMenu;
|
||||
WidgetViewModeShowHide(cm);
|
||||
var mainCM = ViewWidgetButtonMenu.GetChildMenu("Debug View").ContextMenu;
|
||||
if (mainCM != null && cm != mainCM)
|
||||
WidgetViewModeShowHide(mainCM);
|
||||
}
|
||||
}
|
||||
|
||||
private void WidgetViewModeShowHide(Control cm)
|
||||
@@ -1599,7 +1938,7 @@ namespace FlaxEditor.Viewport
|
||||
foreach (var e in ccm.Items)
|
||||
{
|
||||
if (e is ContextMenuButton b && b.Tag is ViewMode v)
|
||||
b.Icon = Task.View.Mode == v ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
b.Icon = Task.ViewMode == v ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -306,6 +306,8 @@ namespace FlaxEditor.Viewport
|
||||
var orient = ViewOrientation;
|
||||
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GizmosCollection Gizmos { get; }
|
||||
|
||||
@@ -7,6 +7,8 @@ using FlaxEngine.GUI;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using Object = FlaxEngine.Object;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
@@ -49,6 +51,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
private Image _guiMaterialControl;
|
||||
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
||||
private ContextMenu _modelWidgetButtonMenu;
|
||||
private AssetPicker _customModelPicker;
|
||||
private Model _customModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
||||
@@ -74,15 +78,66 @@ namespace FlaxEditor.Viewport.Previews
|
||||
get => _selectedModelIndex;
|
||||
set
|
||||
{
|
||||
if (value == -1) // Using Custom Model
|
||||
return;
|
||||
if (value < 0 || value > Models.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
if (_customModelPicker != null)
|
||||
_customModelPicker.Validator.SelectedAsset = null;
|
||||
_selectedModelIndex = value;
|
||||
_previewModel.Model = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/" + Models[value]);
|
||||
_previewModel.Transform = Transforms[value];
|
||||
}
|
||||
}
|
||||
|
||||
// Used to automatically update which entry is checked.
|
||||
// TODO: Maybe a better system with predicate bool checks could be used?
|
||||
private void ResetModelContextMenu()
|
||||
{
|
||||
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
{
|
||||
var index = i;
|
||||
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||
button.Checked = SelectedModelIndex == index && _customModel == null;
|
||||
button.Tag = index;
|
||||
}
|
||||
|
||||
_modelWidgetButtonMenu.AddSeparator();
|
||||
_customModelPicker = new AssetPicker(new ScriptType(typeof(Model)), Float2.Zero);
|
||||
|
||||
// Label button
|
||||
var customModelPickerLabel = _modelWidgetButtonMenu.AddButton("Custom Model:");
|
||||
customModelPickerLabel.CloseMenuOnClick = false;
|
||||
customModelPickerLabel.Checked = _customModel != null;
|
||||
|
||||
// Container button
|
||||
var customModelPickerButton = _modelWidgetButtonMenu.AddButton("");
|
||||
customModelPickerButton.Height = _customModelPicker.Height + 4;
|
||||
customModelPickerButton.CloseMenuOnClick = false;
|
||||
_customModelPicker.Parent = customModelPickerButton;
|
||||
_customModelPicker.Validator.SelectedAsset = _customModel;
|
||||
_customModelPicker.SelectedItemChanged += () =>
|
||||
{
|
||||
_customModel = _customModelPicker.Validator.SelectedAsset as Model;
|
||||
if (_customModelPicker.Validator.SelectedAsset == null)
|
||||
{
|
||||
SelectedModelIndex = 0;
|
||||
ResetModelContextMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
_previewModel.Model = _customModel;
|
||||
_previewModel.Transform = Transforms[0];
|
||||
SelectedModelIndex = -1;
|
||||
ResetModelContextMenu();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MaterialPreview"/> class.
|
||||
/// </summary>
|
||||
@@ -107,17 +162,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
if (!control.Visible)
|
||||
return;
|
||||
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
{
|
||||
var index = i;
|
||||
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||
button.Checked = SelectedModelIndex == index;
|
||||
button.Tag = index;
|
||||
}
|
||||
ResetModelContextMenu();
|
||||
};
|
||||
new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
private bool _checked;
|
||||
private bool _autoCheck;
|
||||
private bool _isMosueDown;
|
||||
private float _forcedTextWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when user toggles checked state.
|
||||
@@ -63,14 +64,16 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="icon">The icon.</param>
|
||||
/// <param name="contextMenu">The context menu.</param>
|
||||
/// <param name="autoCheck">if set to <c>true</c> will be automatic checked on mouse click.</param>
|
||||
public ViewportWidgetButton(string text, SpriteHandle icon, ContextMenu contextMenu = null, bool autoCheck = false)
|
||||
: base(0, 0, CalculateButtonWidth(0, icon.IsValid), ViewportWidgetsContainer.WidgetsHeight)
|
||||
/// <param name="autoCheck">If set to <c>true</c> will be automatic checked on mouse click.</param>
|
||||
/// <param name="textWidth">Forces the text to be drawn with the specified width.</param>
|
||||
public ViewportWidgetButton(string text, SpriteHandle icon, ContextMenu contextMenu = null, bool autoCheck = false, float textWidth = 0.0f)
|
||||
: base(0, 0, CalculateButtonWidth(textWidth, icon.IsValid), ViewportWidgetsContainer.WidgetsHeight)
|
||||
{
|
||||
_text = text;
|
||||
Icon = icon;
|
||||
_cm = contextMenu;
|
||||
_autoCheck = autoCheck;
|
||||
_forcedTextWidth = textWidth;
|
||||
|
||||
if (_cm != null)
|
||||
_cm.VisibleChanged += CmOnVisibleChanged;
|
||||
@@ -160,7 +163,7 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
var style = Style.Current;
|
||||
|
||||
if (style != null && style.FontMedium)
|
||||
Width = CalculateButtonWidth(style.FontMedium.MeasureText(_text).X, Icon.IsValid);
|
||||
Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user