diff --git a/Source/Editor/Content/GUI/ContentNavigation.cs b/Source/Editor/Content/GUI/ContentNavigation.cs
index 1dec6f3fd..0cae5e23f 100644
--- a/Source/Editor/Content/GUI/ContentNavigation.cs
+++ b/Source/Editor/Content/GUI/ContentNavigation.cs
@@ -161,7 +161,7 @@ namespace FlaxEditor.Content.GUI
{
var style = Style.Current;
var rect = new Rectangle(Float2.Zero, Size);
- var color = IsDragOver ? style.BackgroundSelected * 0.6f : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent));
+ var color = IsDragOver ? Color.Transparent : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent));
Render2D.FillRectangle(rect, color);
Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(rect.Location.X, rect.Y + rect.Size.Y * 0.25f, rect.Size.X, rect.Size.X), EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled);
}
diff --git a/Source/Editor/Content/GUI/ContentView.DragDrop.cs b/Source/Editor/Content/GUI/ContentView.DragDrop.cs
deleted file mode 100644
index 812cb2a25..000000000
--- a/Source/Editor/Content/GUI/ContentView.DragDrop.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
-
-using FlaxEditor.GUI.Drag;
-using FlaxEditor.SceneGraph;
-using FlaxEngine;
-using FlaxEngine.GUI;
-
-namespace FlaxEditor.Content.GUI
-{
- public partial class ContentView
- {
- private bool _validDragOver;
- private DragActors _dragActors;
-
- ///
- public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
- {
- var result = base.OnDragEnter(ref location, data);
- if (result != DragDropEffect.None)
- return result;
-
- // Check if drop file(s)
- if (data is DragDataFiles)
- {
- _validDragOver = true;
- return DragDropEffect.Copy;
- }
-
- // Check if drop actor(s)
- if (_dragActors == null)
- _dragActors = new DragActors(ValidateDragActors);
- if (_dragActors.OnDragEnter(data))
- {
- _validDragOver = true;
- return DragDropEffect.Move;
- }
-
- return DragDropEffect.None;
- }
-
- private bool ValidateDragActors(ActorNode actor)
- {
- return actor.CanCreatePrefab && Editor.Instance.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
- }
-
- private void ImportActors(DragActors actors, ContentFolder location)
- {
- foreach (var actorNode in actors.Objects)
- {
- var actor = actorNode.Actor;
- if (actors.Objects.Contains(actorNode.ParentNode as ActorNode))
- continue;
-
- Editor.Instance.Prefabs.CreatePrefab(actor, false);
- }
- }
-
- ///
- public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
- {
- _validDragOver = false;
- var result = base.OnDragMove(ref location, data);
- if (result != DragDropEffect.None)
- return result;
-
- if (data is DragDataFiles)
- {
- _validDragOver = true;
- result = DragDropEffect.Copy;
- }
- else if (_dragActors != null && _dragActors.HasValidDrag)
- {
- _validDragOver = true;
- result = DragDropEffect.Move;
- }
-
- return result;
- }
-
- ///
- public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
- {
- var result = base.OnDragDrop(ref location, data);
- if (result != DragDropEffect.None)
- return result;
-
- // Check if drop file(s)
- if (data is DragDataFiles files)
- {
- // Import files
- var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
- if (currentFolder != null)
- Editor.Instance.ContentImporting.Import(files.Files, currentFolder);
- result = DragDropEffect.Copy;
- }
- // Check if drop actor(s)
- else if (_dragActors != null && _dragActors.HasValidDrag)
- {
- // Import actors
- var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
- if (currentFolder != null)
- ImportActors(_dragActors, currentFolder);
-
- _dragActors.OnDragDrop();
- result = DragDropEffect.Move;
- }
-
- // Clear cache
- _validDragOver = false;
-
- return result;
- }
-
- ///
- public override void OnDragLeave()
- {
- _validDragOver = false;
- _dragActors?.OnDragLeave();
-
- base.OnDragLeave();
- }
- }
-}
diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs
index 754e14d3d..105df76d4 100644
--- a/Source/Editor/Content/GUI/ContentView.cs
+++ b/Source/Editor/Content/GUI/ContentView.cs
@@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using FlaxEditor.GUI.Drag;
using FlaxEditor.Options;
+using FlaxEditor.SceneGraph;
using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -52,14 +54,17 @@ namespace FlaxEditor.Content.GUI
public partial class ContentView : ContainerControl, IContentItemOwner
{
private readonly List _items = new List(256);
- private readonly List _selection = new List(16);
+ private readonly List _selection = new List();
private float _viewScale = 1.0f;
private ContentViewType _viewType = ContentViewType.Tiles;
- private bool _isRubberBandSpanning = false;
- private Float2 _mousePresslocation;
+ private bool _isRubberBandSpanning;
+ private Float2 _mousePressLocation;
private Rectangle _rubberBandRectangle;
+ private bool _validDragOver;
+ private DragActors _dragActors;
+
#region External Events
///
@@ -193,6 +198,7 @@ namespace FlaxEditor.Content.GUI
OnDelete?.Invoke(_selection);
}),
new InputActionsContainer.Binding(options => options.SelectAll, SelectAll),
+ new InputActionsContainer.Binding(options => options.DeselectAll, DeselectAll),
new InputActionsContainer.Binding(options => options.Rename, () =>
{
if (HasSelection && _selection[0].CanRename)
@@ -397,10 +403,7 @@ namespace FlaxEditor.Content.GUI
PerformLayout();
}
- ///
- /// Selects all the items.
- ///
- public void SelectAll()
+ private void BulkSelectUpdate(bool select = true)
{
// Lock layout
var wasLayoutLocked = IsLayoutLocked;
@@ -408,13 +411,30 @@ namespace FlaxEditor.Content.GUI
// Select items
_selection.Clear();
- _selection.AddRange(_items);
+ if (select)
+ _selection.AddRange(_items);
// Unload and perform UI layout
IsLayoutLocked = wasLayoutLocked;
PerformLayout();
}
+ ///
+ /// Selects all the items.
+ ///
+ public void SelectAll()
+ {
+ BulkSelectUpdate(true);
+ }
+
+ ///
+ /// Deselects all the items.
+ ///
+ public void DeselectAll()
+ {
+ BulkSelectUpdate(false);
+ }
+
///
/// Deselects the specified item.
///
@@ -595,7 +615,9 @@ namespace FlaxEditor.Content.GUI
// Check if drag is over
if (IsDragOver && _validDragOver)
{
- Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
+ var bounds = new Rectangle(Float2.One, Size - Float2.One * 2);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
}
// Check if it's an empty thing
@@ -604,10 +626,11 @@ namespace FlaxEditor.Content.GUI
Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
}
+ // Selection
if (_isRubberBandSpanning)
{
- Render2D.FillRectangle(_rubberBandRectangle, Color.Orange * 0.4f);
- Render2D.DrawRectangle(_rubberBandRectangle, Color.Orange);
+ Render2D.FillRectangle(_rubberBandRectangle, style.Selection);
+ Render2D.DrawRectangle(_rubberBandRectangle, style.SelectionBorder);
}
}
@@ -619,8 +642,8 @@ namespace FlaxEditor.Content.GUI
if (button == MouseButton.Left)
{
- _mousePresslocation = location;
- _rubberBandRectangle = new Rectangle(_mousePresslocation, 0, 0);
+ _mousePressLocation = location;
+ _rubberBandRectangle = new Rectangle(_mousePressLocation, 0, 0);
_isRubberBandSpanning = true;
StartMouseCapture();
}
@@ -632,8 +655,8 @@ namespace FlaxEditor.Content.GUI
{
if (_isRubberBandSpanning)
{
- _rubberBandRectangle.Width = location.X - _mousePresslocation.X;
- _rubberBandRectangle.Height = location.Y - _mousePresslocation.Y;
+ _rubberBandRectangle.Width = location.X - _mousePressLocation.X;
+ _rubberBandRectangle.Height = location.Y - _mousePressLocation.Y;
}
base.OnMouseMove(location);
@@ -768,6 +791,114 @@ namespace FlaxEditor.Content.GUI
return false;
}
+ ///
+ public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
+ {
+ var result = base.OnDragEnter(ref location, data);
+ if (result != DragDropEffect.None)
+ return result;
+
+ // Check if drop file(s)
+ if (data is DragDataFiles)
+ {
+ _validDragOver = true;
+ return DragDropEffect.Copy;
+ }
+
+ // Check if drop actor(s)
+ if (_dragActors == null)
+ _dragActors = new DragActors(ValidateDragActors);
+ if (_dragActors.OnDragEnter(data))
+ {
+ _validDragOver = true;
+ return DragDropEffect.Move;
+ }
+
+ return DragDropEffect.None;
+ }
+
+ private bool ValidateDragActors(ActorNode actor)
+ {
+ return actor.CanCreatePrefab && Editor.Instance.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
+ }
+
+ private void ImportActors(DragActors actors, ContentFolder location)
+ {
+ foreach (var actorNode in actors.Objects)
+ {
+ var actor = actorNode.Actor;
+ if (actors.Objects.Contains(actorNode.ParentNode as ActorNode))
+ continue;
+
+ Editor.Instance.Prefabs.CreatePrefab(actor, false);
+ }
+ }
+
+ ///
+ public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
+ {
+ _validDragOver = false;
+ var result = base.OnDragMove(ref location, data);
+ if (result != DragDropEffect.None)
+ return result;
+
+ if (data is DragDataFiles)
+ {
+ _validDragOver = true;
+ result = DragDropEffect.Copy;
+ }
+ else if (_dragActors != null && _dragActors.HasValidDrag)
+ {
+ _validDragOver = true;
+ result = DragDropEffect.Move;
+ }
+
+ return result;
+ }
+
+ ///
+ public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
+ {
+ var result = base.OnDragDrop(ref location, data);
+ if (result != DragDropEffect.None)
+ return result;
+
+ // Check if drop file(s)
+ if (data is DragDataFiles files)
+ {
+ // Import files
+ var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
+ if (currentFolder != null)
+ Editor.Instance.ContentImporting.Import(files.Files, currentFolder);
+ result = DragDropEffect.Copy;
+ }
+ // Check if drop actor(s)
+ else if (_dragActors != null && _dragActors.HasValidDrag)
+ {
+ // Import actors
+ var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
+ if (currentFolder != null)
+ ImportActors(_dragActors, currentFolder);
+
+ _dragActors.OnDragDrop();
+ result = DragDropEffect.Move;
+ }
+
+ // Clear cache
+ _validDragOver = false;
+
+ return result;
+ }
+
+ ///
+ public override void OnDragLeave()
+ {
+ _validDragOver = false;
+ _dragActors?.OnDragLeave();
+
+ base.OnDragLeave();
+ }
+
///
protected override void PerformLayoutBeforeChildren()
{
@@ -833,6 +964,9 @@ namespace FlaxEditor.Content.GUI
///
public override void OnDestroy()
{
+ if (IsDisposing)
+ return;
+
// Ensure to unlink all items
ClearItems();
diff --git a/Source/Editor/Content/Items/ContentFolder.cs b/Source/Editor/Content/Items/ContentFolder.cs
index 9a8e4af77..94cc8368e 100644
--- a/Source/Editor/Content/Items/ContentFolder.cs
+++ b/Source/Editor/Content/Items/ContentFolder.cs
@@ -241,7 +241,12 @@ namespace FlaxEditor.Content
// Check if drag is over
if (IsDragOver && _validDragOver)
- Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BackgroundSelected * 0.6f);
+ {
+ var style = Style.Current;
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
+ }
}
private bool ValidateDragItem(ContentItem item)
diff --git a/Source/Editor/Content/Proxy/PrefabProxy.cs b/Source/Editor/Content/Proxy/PrefabProxy.cs
index 0c0114fa8..ee3674daa 100644
--- a/Source/Editor/Content/Proxy/PrefabProxy.cs
+++ b/Source/Editor/Content/Proxy/PrefabProxy.cs
@@ -75,6 +75,8 @@ namespace FlaxEditor.Content
///
public override void Create(string outputPath, object arg)
{
+ bool resetTransform = false;
+ var transform = Transform.Identity;
if (!(arg is Actor actor))
{
// Create default prefab root object
@@ -86,8 +88,17 @@ namespace FlaxEditor.Content
// Cleanup it after usage
Object.Destroy(actor, 20.0f);
}
+ else if (actor.Scene != null)
+ {
+ // Create prefab with identity transform so the actor instance on a level will have it customized
+ resetTransform = true;
+ transform = actor.LocalTransform;
+ actor.LocalTransform = Transform.Identity;
+ }
PrefabManager.CreatePrefab(actor, outputPath, true);
+ if (resetTransform)
+ actor.LocalTransform = transform;
}
///
diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs
index ab87389de..ef6302e8e 100644
--- a/Source/Editor/CustomEditors/CustomEditor.cs
+++ b/Source/Editor/CustomEditors/CustomEditor.cs
@@ -747,7 +747,7 @@ namespace FlaxEditor.CustomEditors
///
public void SetValueToDefault()
{
- SetValueCloned(Values.DefaultValue);
+ SetValue(Utilities.Utils.CloneValue(Values.DefaultValue));
}
///
@@ -784,19 +784,7 @@ namespace FlaxEditor.CustomEditors
return;
}
- SetValueCloned(Values.ReferenceValue);
- }
-
- private void SetValueCloned(object value)
- {
- // For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
- if (value != null && !value.GetType().IsValueType)
- {
- var json = JsonSerializer.Serialize(value);
- value = JsonSerializer.Deserialize(json, value.GetType());
- }
-
- SetValue(value);
+ SetValue(Utilities.Utils.CloneValue(Values.ReferenceValue));
}
///
diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
index db77e24e7..3699b8254 100644
--- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
@@ -245,9 +245,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
// Special case for new Script added to actor
if (editor.Values[0] is Script script && !script.HasPrefabLink)
- {
return CreateDiffNode(editor);
- }
// Skip if no change detected
var isRefEdited = editor.Values.IsReferenceValueModified;
@@ -258,9 +256,16 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (editor.ChildrenEditors.Count == 0 || (isRefEdited && editor is CollectionEditor))
result = CreateDiffNode(editor);
bool isScriptEditorWithRefValue = editor is ScriptsEditor && editor.Values.HasReferenceValue;
+ bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.Scene != null;
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
{
- var child = ProcessDiff(editor.ChildrenEditors[i], !isScriptEditorWithRefValue);
+ var childEditor = editor.ChildrenEditors[i];
+
+ // Special case for root actor transformation (can be applied only in Prefab editor, not in Level)
+ if (isActorEditorInLevel && childEditor.Values.Info.Name is "LocalPosition" or "LocalOrientation" or "LocalScale")
+ continue;
+
+ var child = ProcessDiff(childEditor, !isScriptEditorWithRefValue);
if (child != null)
{
if (result == null)
@@ -276,7 +281,6 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
var prefabObjectScript = prefabObjectScripts[j];
bool isRemoved = true;
-
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
{
if (editor.ChildrenEditors[i].Values is ScriptsEditor.ScriptsContainer container && container.PrefabObjectId == prefabObjectScript.PrefabObjectID)
@@ -286,14 +290,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
break;
}
}
-
if (isRemoved)
{
var dummy = new RemovedScriptDummy
{
PrefabObject = prefabObjectScript
};
-
var child = CreateDiffNode(dummy);
if (result == null)
result = CreateDiffNode(editor);
diff --git a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
index bb18f6de0..81ca30361 100644
--- a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
@@ -67,25 +67,6 @@ public class MissingScriptEditor : GenericEditor
base.Initialize(layout);
}
- private void FindActorsWithMatchingMissingScript(List missingScripts)
- {
- foreach (Actor actor in Level.GetActors(typeof(Actor)))
- {
- for (int scriptIndex = 0; scriptIndex < actor.ScriptsCount; scriptIndex++)
- {
- Script actorScript = actor.Scripts[scriptIndex];
- if (actorScript is not MissingScript missingActorScript)
- continue;
-
- MissingScript currentMissing = Values[0] as MissingScript;
- if (missingActorScript.MissingTypeName != currentMissing.MissingTypeName)
- continue;
-
- missingScripts.Add(missingActorScript);
- }
- }
- }
-
private void RunReplacementMultiCast(List actions)
{
if (actions.Count == 0)
@@ -104,16 +85,54 @@ public class MissingScriptEditor : GenericEditor
}
}
- private void ReplaceScript(ScriptType script, bool replaceAllInScene)
+ private void GetMissingScripts(Actor actor, string currentMissingTypeName, List missingScripts)
{
- var actions = new List(4);
+ // Iterate over the scripts of this actor
+ for (int i = 0; i < actor.ScriptsCount; i++)
+ {
+ if (actor.GetScript(i) is MissingScript otherMissing &&
+ otherMissing.MissingTypeName == currentMissingTypeName)
+ {
+ missingScripts.Add(otherMissing);
+ }
+ }
+ // Iterate over this actor children (recursive)
+ for (int i = 0; i < actor.ChildrenCount; i++)
+ {
+ GetMissingScripts(actor.GetChild(i), currentMissingTypeName, missingScripts);
+ }
+ }
+
+ private void ReplaceScript(ScriptType script)
+ {
var missingScripts = new List();
- if (!replaceAllInScene)
- missingScripts.Add((MissingScript)Values[0]);
+ var currentMissing = (MissingScript)Values[0];
+ var currentMissingTypeName = currentMissing.MissingTypeName;
+ if (_shouldReplaceAllCheckbox.Checked)
+ {
+ if (currentMissing.Scene == null && currentMissing.Actor != null)
+ {
+ // Find all missing scripts in prefab instance
+ GetMissingScripts(currentMissing.Actor.GetPrefabRoot(), currentMissingTypeName, missingScripts);
+ }
+ else
+ {
+ // Find all missing scripts in all loaded levels that match this type
+ for (int i = 0; i < Level.ScenesCount; i++)
+ {
+ GetMissingScripts(Level.GetScene(i), currentMissingTypeName, missingScripts);
+ }
+ }
+ }
else
- FindActorsWithMatchingMissingScript(missingScripts);
+ {
+ // Use the current instance only
+ foreach (var value in Values)
+ missingScripts.Add((MissingScript)value);
+ }
+ var actions = new List(4);
foreach (var missingScript in missingScripts)
actions.Add(AddRemoveScript.Add(missingScript.Actor, script));
RunReplacementMultiCast(actions);
@@ -149,7 +168,7 @@ public class MissingScriptEditor : GenericEditor
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.ItemClicked += item => ReplaceScript((ScriptType)item.Tag);
cm.SortItems();
cm.Show(_dropPanel, _replaceScriptButton.BottomLeft - new Float2((cm.Width - _replaceScriptButton.Width) / 2, 0));
}
diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
index 175312b6a..17387a202 100644
--- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
@@ -160,7 +160,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var actions = new List();
foreach (var body in bodies)
{
- var action = new Actions.DeleteActorsAction(new List { SceneGraphFactory.FindNode(body.ID) });
+ var action = new Actions.DeleteActorsAction(body);
action.Do();
actions.Add(action);
}
@@ -185,7 +185,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var body = bodies.FirstOrDefault(x => x.Name == name);
if (body != null)
{
- var action = new Actions.DeleteActorsAction(new List { SceneGraphFactory.FindNode(body.ID) });
+ var action = new Actions.DeleteActorsAction(body);
action.Do();
Presenter.Undo?.AddAction(action);
}
@@ -224,7 +224,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
else
{
// Remove joint that will no longer be valid
- var action = new Actions.DeleteActorsAction(new List { SceneGraphFactory.FindNode(joint.ID) });
+ var action = new Actions.DeleteActorsAction(joint);
action.Do();
Presenter.Undo?.AddAction(action);
}
@@ -233,7 +233,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Remove body
{
- var action = new Actions.DeleteActorsAction(new List { SceneGraphFactory.FindNode(body.ID) });
+ var action = new Actions.DeleteActorsAction(body);
action.Do();
Presenter.Undo?.AddAction(action);
}
diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
index a8bcedf18..fe068e4de 100644
--- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
@@ -151,8 +151,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag)
{
var area = new Rectangle(Float2.Zero, size);
- Render2D.FillRectangle(area, Color.Orange * 0.5f);
- Render2D.DrawRectangle(area, Color.Black);
+ Render2D.FillRectangle(area, style.Selection);
+ Render2D.DrawRectangle(area, style.SelectionBorder);
}
base.Draw();
@@ -520,7 +520,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
base.Draw();
- var color = FlaxEngine.GUI.Style.Current.BackgroundSelected * (IsDragOver ? 0.9f : 0.1f);
+ var color = Style.Current.BackgroundSelected * (IsDragOver ? 0.9f : 0.1f);
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), color);
}
diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
index cd0b534e0..27be8e538 100644
--- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
@@ -589,25 +589,27 @@ namespace FlaxEditor.CustomEditors.Dedicated
LayoutElementsContainer yEl;
LayoutElementsContainer hEl;
LayoutElementsContainer vEl;
+ Color axisColorX = ActorTransformEditor.AxisColorX;
+ Color axisColorY = ActorTransformEditor.AxisColorY;
if (xEq)
{
- xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values));
- vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values));
+ xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX);
+ vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX);
}
else
{
- xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values));
- vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values));
+ xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX);
+ vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX);
}
if (yEq)
{
- yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values));
- hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values));
+ yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY);
+ hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY);
}
else
{
- yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values));
- hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values));
+ yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY);
+ hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY);
}
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
@@ -624,28 +626,34 @@ namespace FlaxEditor.CustomEditors.Dedicated
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
{
- var horUp = cont.VerticalPanel();
- horUp.Panel.Margin = Margin.Zero;
- return horUp;
+ var panel = cont.VerticalPanel();
+ panel.Panel.Margin = Margin.Zero;
+ return panel;
}
private CustomElementsContainer UniformGridTwoByOne(LayoutElementsContainer cont)
{
- var horUp = cont.CustomContainer();
- horUp.CustomControl.SlotsHorizontally = 2;
- horUp.CustomControl.SlotsVertically = 1;
- horUp.CustomControl.SlotPadding = Margin.Zero;
- horUp.CustomControl.ClipChildren = false;
- return horUp;
+ var grid = cont.CustomContainer();
+ grid.CustomControl.SlotsHorizontally = 2;
+ grid.CustomControl.SlotsVertically = 1;
+ grid.CustomControl.SlotPadding = Margin.Zero;
+ grid.CustomControl.ClipChildren = false;
+ return grid;
}
- private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values)
+ private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor)
{
- CustomElementsContainer hor = UniformGridTwoByOne(el);
- hor.CustomControl.SlotPadding = new Margin(5, 5, 0, 0);
- LabelElement lab = hor.Label(text);
- hor.Object(values);
- return hor;
+ var grid = UniformGridTwoByOne(el);
+ grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
+ var label = grid.Label(text);
+ var editor = grid.Object(values);
+ if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
+ {
+ var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
+ floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
+ floatEditorElement.ValueBox.BorderSelectedColor = borderColor;
+ }
+ return grid;
}
private bool _cachedXEq;
diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
index 29a9f45e8..fb33d470c 100644
--- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
@@ -26,6 +26,11 @@ namespace FlaxEditor.CustomEditors.Editors
///
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
+ ///
+ /// The axes colors grey out scale when input field is not focused.
+ ///
+ public static float AxisGreyOutFactor = 0.6f;
+
///
/// Custom editor for actor position property.
///
@@ -39,12 +44,11 @@ namespace FlaxEditor.CustomEditors.Editors
// Override colors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
- var grayOutFactor = 0.6f;
- XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
+ XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
XElement.ValueBox.BorderSelectedColor = AxisColorX;
- YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
+ YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
YElement.ValueBox.BorderSelectedColor = AxisColorY;
- ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
+ ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
}
}
@@ -62,12 +66,11 @@ namespace FlaxEditor.CustomEditors.Editors
// Override colors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
- var grayOutFactor = 0.6f;
- XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
+ XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
XElement.ValueBox.BorderSelectedColor = AxisColorX;
- YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
+ YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
YElement.ValueBox.BorderSelectedColor = AxisColorY;
- ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
+ ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
}
}
diff --git a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs
index 276417f96..25163febd 100644
--- a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections;
-using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.Utilities;
@@ -47,8 +46,9 @@ namespace FlaxEditor.CustomEditors.Editors
if (elementType.IsValueType || NotNullItems)
{
// Fill new entries with the last value
+ var lastValue = array.GetValue(oldSize - 1);
for (int i = oldSize; i < newSize; i++)
- Array.Copy(array, oldSize - 1, newValues, i, 1);
+ newValues.SetValue(Utilities.Utils.CloneValue(lastValue), i);
}
else
{
diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
index 211fb3980..41715d654 100644
--- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
@@ -542,9 +542,10 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (_dragHandlers is { HasValidDrag: true })
{
+ var style = FlaxEngine.GUI.Style.Current;
var area = new Rectangle(Float2.Zero, Size);
- Render2D.FillRectangle(area, Color.Orange * 0.5f);
- Render2D.DrawRectangle(area, Color.Black);
+ Render2D.FillRectangle(area, style.Selection);
+ Render2D.DrawRectangle(area, style.SelectionBorder);
}
base.Draw();
diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
index 2035057de..97b016fba 100644
--- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
@@ -220,7 +220,11 @@ namespace FlaxEditor.CustomEditors.Editors
// Check if drag is over
if (IsDragOver && _hasValidDragOver)
- Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
+ {
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
+ }
}
///
diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
index 3b4a6eed2..a82b1eccd 100644
--- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
@@ -175,7 +175,11 @@ namespace FlaxEditor.CustomEditors.Editors
// Check if drag is over
if (IsDragOver && _hasValidDragOver)
- Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
+ {
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
+ }
}
///
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index b37f0468c..4cee6d971 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -1676,6 +1676,9 @@ namespace FlaxEditor
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
+ [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetPrefabNestedObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
+ internal static partial void Internal_GetPrefabNestedObject(IntPtr prefabId, IntPtr prefabObjectId, IntPtr outPrefabId, IntPtr outPrefabObjectId);
+
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
internal static partial float Internal_GetAnimationTime(IntPtr animatedModel);
diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs
index 2e7116957..5c8f02a06 100644
--- a/Source/Editor/GUI/AssetPicker.cs
+++ b/Source/Editor/GUI/AssetPicker.cs
@@ -182,7 +182,11 @@ namespace FlaxEditor.GUI
// Check if drag is over
if (IsDragOver && _dragOverElement != null && _dragOverElement.HasValidDrag)
- Render2D.FillRectangle(iconRect, style.BackgroundSelected * 0.4f);
+ {
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
+ }
}
///
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenu.cs
index a0a9b1cb8..05d62d3f2 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenu.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenu.cs
@@ -359,6 +359,17 @@ namespace FlaxEditor.GUI.ContextMenu
ButtonClicked?.Invoke(button);
}
+ ///
+ public override void Show(Control parent, Float2 location)
+ {
+ // Remove last separator to make context menu look better
+ int lastIndex = _panel.Children.Count - 1;
+ if (lastIndex >= 0 && _panel.Children[lastIndex] is ContextMenuSeparator separator)
+ separator.Dispose();
+
+ base.Show(parent, location);
+ }
+
///
public override bool ContainsPoint(ref Float2 location, bool precise)
{
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
index f2feed095..511cda2e5 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
@@ -168,30 +168,30 @@ namespace FlaxEditor.GUI.ContextMenu
bool isUp = false, isLeft = false;
if (UseAutomaticDirectionFix)
{
+ var parentMenu = parent as ContextMenu;
if (monitorBounds.Bottom < rightBottomLocationSS.Y)
{
- // Direction: up
isUp = true;
locationSS.Y -= dpiSize.Y;
-
- // Offset to fix sub-menu location
- if (parent is ContextMenu menu && menu._childCM != null)
+ if (parentMenu != null && parentMenu._childCM != null)
locationSS.Y += 30.0f * dpiScale;
}
- if (monitorBounds.Right < rightBottomLocationSS.X || _parentCM?.Direction == ContextMenuDirection.LeftDown || _parentCM?.Direction == ContextMenuDirection.LeftUp)
+ if (parentMenu == null)
{
- // Direction: left
- isLeft = true;
-
- if (IsSubMenu && _parentCM != null)
- {
- locationSS.X -= _parentCM.Width + dpiSize.X;
- }
- else
+ if (monitorBounds.Right < rightBottomLocationSS.X)
{
+ isLeft = true;
locationSS.X -= dpiSize.X;
}
}
+ else if (monitorBounds.Right < rightBottomLocationSS.X || _parentCM?.Direction == ContextMenuDirection.LeftDown || _parentCM?.Direction == ContextMenuDirection.LeftUp)
+ {
+ isLeft = true;
+ if (IsSubMenu && _parentCM != null)
+ locationSS.X -= _parentCM.Width + dpiSize.X;
+ else
+ locationSS.X -= dpiSize.X;
+ }
}
// Update direction flag
diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs
index 7e7fbbd32..bdb1c650b 100644
--- a/Source/Editor/GUI/CurveEditor.Contents.cs
+++ b/Source/Editor/GUI/CurveEditor.Contents.cs
@@ -484,6 +484,7 @@ namespace FlaxEditor.GUI
cm.AddSeparator();
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
cm.AddButton("Select all keyframes", _editor.SelectAll);
+ cm.AddButton("Deselect all keyframes", _editor.DeselectAll);
cm.AddButton("Copy all keyframes", () =>
{
_editor.SelectAll();
diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs
index 69671b1da..141b3aa60 100644
--- a/Source/Editor/GUI/CurveEditor.cs
+++ b/Source/Editor/GUI/CurveEditor.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.Linq;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -713,15 +714,28 @@ namespace FlaxEditor.GUI
}
}
+ private void BulkSelectUpdate(bool select = true)
+ {
+ for (int i = 0; i < _points.Count; i++)
+ {
+ _points[i].IsSelected = select;
+ }
+ }
+
///
/// Selects all keyframes.
///
public void SelectAll()
{
- for (int i = 0; i < _points.Count; i++)
- {
- _points[i].IsSelected = true;
- }
+ BulkSelectUpdate(true);
+ }
+
+ ///
+ /// Deselects all keyframes.
+ ///
+ public void DeselectAll()
+ {
+ BulkSelectUpdate(false);
}
///
@@ -899,8 +913,8 @@ namespace FlaxEditor.GUI
_mainPanel.PointToParent(_contents.PointToParent(_contents._leftMouseDownPos)),
_mainPanel.PointToParent(_contents.PointToParent(_contents._mousePos))
);
- Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
- Render2D.DrawRectangle(selectionRect, Color.Orange);
+ Render2D.FillRectangle(selectionRect, style.Selection);
+ Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
}
base.Draw();
@@ -926,34 +940,35 @@ namespace FlaxEditor.GUI
if (base.OnKeyDown(key))
return true;
- switch (key)
+ InputOptions options = Editor.Instance.Options.Options.Input;
+ if (options.SelectAll.Process(this))
+ {
+ SelectAll();
+ UpdateTangents();
+ return true;
+ }
+ else if (options.DeselectAll.Process(this))
+ {
+ DeselectAll();
+ UpdateTangents();
+ return true;
+ }
+ else if (options.Delete.Process(this))
{
- case KeyboardKeys.Delete:
RemoveKeyframes();
return true;
- case KeyboardKeys.A:
- if (Root.GetKey(KeyboardKeys.Control))
- {
- SelectAll();
- UpdateTangents();
- return true;
- }
- break;
- case KeyboardKeys.C:
- if (Root.GetKey(KeyboardKeys.Control))
- {
- CopyKeyframes();
- return true;
- }
- break;
- case KeyboardKeys.V:
- if (Root.GetKey(KeyboardKeys.Control))
- {
- KeyframesEditorUtils.Paste(this);
- return true;
- }
- break;
}
+ else if (options.Copy.Process(this))
+ {
+ CopyKeyframes();
+ return true;
+ }
+ else if (options.Paste.Process(this))
+ {
+ KeyframesEditorUtils.Paste(this);
+ return true;
+ }
+
return false;
}
diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs
index 4fd496548..d10a1e51c 100644
--- a/Source/Editor/GUI/Input/ValueBox.cs
+++ b/Source/Editor/GUI/Input/ValueBox.cs
@@ -201,8 +201,9 @@ namespace FlaxEditor.GUI.Input
if (_isSliding)
{
// Draw overlay
- // TODO: render nicer overlay with some glow from the borders (inside)
- Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Color.Orange * 0.3f);
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
}
}
}
diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs
index 458db481f..78fbcd779 100644
--- a/Source/Editor/GUI/ItemsListContextMenu.cs
+++ b/Source/Editor/GUI/ItemsListContextMenu.cs
@@ -122,6 +122,10 @@ namespace FlaxEditor.GUI
if (IsMouseOver || IsFocused)
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundHighlighted);
+ // Indent for drop panel items is handled by drop panel margin
+ if (Parent is not DropPanel)
+ textRect.Location += new Float2(Editor.Instance.Icons.ArrowRight12.Size.X + 2, 0);
+
// Draw all highlights
if (_highlights != null)
{
@@ -262,6 +266,10 @@ namespace FlaxEditor.GUI
}
}
category.Visible = anyVisible;
+ if (string.IsNullOrEmpty(_searchBox.Text))
+ category.Close(false);
+ else
+ category.Open(false);
}
}
@@ -338,9 +346,14 @@ namespace FlaxEditor.GUI
var categoryPanel = new DropPanel
{
HeaderText = item.Category,
+ ArrowImageOpened = new SpriteBrush(Editor.Instance.Icons.ArrowDown12),
+ ArrowImageClosed = new SpriteBrush(Editor.Instance.Icons.ArrowRight12),
+ EnableDropDownIcon = true,
+ ItemsMargin = new Margin(28, 0, 2, 2),
+ HeaderColor = Style.Current.Background,
Parent = parent,
};
- categoryPanel.Open(false);
+ categoryPanel.Close(false);
_categoryPanels.Add(categoryPanel);
parent = categoryPanel;
}
@@ -382,6 +395,7 @@ namespace FlaxEditor.GUI
item2.UpdateFilter(null);
}
category.Visible = true;
+ category.Close(false);
}
}
diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs
index 2ed709948..4de6ac37e 100644
--- a/Source/Editor/GUI/NavigationButton.cs
+++ b/Source/Editor/GUI/NavigationButton.cs
@@ -45,7 +45,8 @@ namespace FlaxEditor.GUI
// Draw background
if (IsDragOver && _validDragOver)
{
- Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f);
+ Render2D.FillRectangle(clientRect, style.Selection);
+ Render2D.DrawRectangle(clientRect, style.SelectionBorder);
}
else if (_isPressed)
{
diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs
index c84e4c01b..4ec634a0b 100644
--- a/Source/Editor/GUI/Timeline/GUI/Background.cs
+++ b/Source/Editor/GUI/Timeline/GUI/Background.cs
@@ -227,8 +227,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
if (_isSelecting)
{
var selectionRect = Rectangle.FromPoints(_selectingStartPos, _mousePos);
- Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
- Render2D.DrawRectangle(selectionRect, Color.Orange);
+ Render2D.FillRectangle(selectionRect, style.Selection);
+ Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
}
DrawChildren();
diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs
index 3ede72503..797a0a044 100644
--- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs
+++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Options;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -432,6 +433,7 @@ namespace FlaxEditor.GUI
if (_editor.EnableKeyframesValueEdit)
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
cm.AddButton("Select all keyframes", _editor.SelectAll).Enabled = _editor._points.Count > 0;
+ cm.AddButton("Deselect all keyframes", _editor.DeselectAll).Enabled = _editor._points.Count > 0;
cm.AddButton("Copy all keyframes", () =>
{
_editor.SelectAll();
@@ -902,7 +904,7 @@ namespace FlaxEditor.GUI
var k = new Keyframe
{
Time = keyframesPos.X,
- Value = DefaultValue,
+ Value = Utilities.Utils.CloneValue(DefaultValue),
};
OnEditingStart();
AddKeyframe(k);
@@ -1209,15 +1211,28 @@ namespace FlaxEditor.GUI
}
}
+ private void BulkSelectUpdate(bool select = true)
+ {
+ for (int i = 0; i < _points.Count; i++)
+ {
+ _points[i].IsSelected = select;
+ }
+ }
+
///
/// Selects all keyframes.
///
public void SelectAll()
{
- for (int i = 0; i < _points.Count; i++)
- {
- _points[i].IsSelected = true;
- }
+ BulkSelectUpdate(true);
+ }
+
+ ///
+ /// Deselects all keyframes.
+ ///
+ public void DeselectAll()
+ {
+ BulkSelectUpdate(false);
}
///
@@ -1241,8 +1256,8 @@ namespace FlaxEditor.GUI
_mainPanel.PointToParent(_contents.PointToParent(_contents._leftMouseDownPos)),
_mainPanel.PointToParent(_contents.PointToParent(_contents._mousePos))
);
- Render2D.FillRectangle(selectionRect, Color.Orange * 0.4f);
- Render2D.DrawRectangle(selectionRect, Color.Orange);
+ Render2D.FillRectangle(selectionRect, style.Selection);
+ Render2D.DrawRectangle(selectionRect, style.SelectionBorder);
}
base.Draw();
@@ -1268,33 +1283,33 @@ namespace FlaxEditor.GUI
if (base.OnKeyDown(key))
return true;
- switch (key)
+ InputOptions options = Editor.Instance.Options.Options.Input;
+ if (options.SelectAll.Process(this))
+ {
+ SelectAll();
+ return true;
+ }
+ else if (options.DeselectAll.Process(this))
+ {
+ DeselectAll();
+ return true;
+ }
+ else if (options.Delete.Process(this))
{
- case KeyboardKeys.Delete:
RemoveKeyframes();
return true;
- case KeyboardKeys.A:
- if (Root.GetKey(KeyboardKeys.Control))
- {
- SelectAll();
- return true;
- }
- break;
- case KeyboardKeys.C:
- if (Root.GetKey(KeyboardKeys.Control))
- {
- CopyKeyframes();
- return true;
- }
- break;
- case KeyboardKeys.V:
- if (Root.GetKey(KeyboardKeys.Control))
- {
- KeyframesEditorUtils.Paste(this);
- return true;
- }
- break;
}
+ else if (options.Copy.Process(this))
+ {
+ CopyKeyframes();
+ return true;
+ }
+ else if (options.Paste.Process(this))
+ {
+ KeyframesEditorUtils.Paste(this);
+ return true;
+ }
+
return false;
}
diff --git a/Source/Editor/GUI/Timeline/Timeline.UI.cs b/Source/Editor/GUI/Timeline/Timeline.UI.cs
index d06515688..64deadef4 100644
--- a/Source/Editor/GUI/Timeline/Timeline.UI.cs
+++ b/Source/Editor/GUI/Timeline/Timeline.UI.cs
@@ -304,7 +304,9 @@ namespace FlaxEditor.GUI.Timeline
if (IsDragOver && _currentDragEffect != DragDropEffect.None)
{
var style = Style.Current;
- Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundSelected * 0.4f);
+ var bounds = new Rectangle(Float2.Zero, Size);
+ Render2D.FillRectangle(bounds, style.Selection);
+ Render2D.DrawRectangle(bounds, style.SelectionBorder);
}
base.Draw();
diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs
index 7f8617bcc..f86730b99 100644
--- a/Source/Editor/GUI/Timeline/Timeline.cs
+++ b/Source/Editor/GUI/Timeline/Timeline.cs
@@ -187,6 +187,11 @@ namespace FlaxEditor.GUI.Timeline
private bool _showPreviewValues = true;
private PlaybackStates _state = PlaybackStates.Disabled;
+ ///
+ /// The Track that is being dragged over. This could have a value when not dragging.
+ ///
+ internal Track DraggedOverTrack = null;
+
///
/// Flag used to mark modified timeline data.
///
diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs
index 227646ab2..929b213e4 100644
--- a/Source/Editor/GUI/Timeline/Track.cs
+++ b/Source/Editor/GUI/Timeline/Track.cs
@@ -774,14 +774,33 @@ namespace FlaxEditor.GUI.Timeline
/// Updates the drag over mode based on the given mouse location.
///
/// The location.
- private void UpdateDrawPositioning(ref Float2 location)
+ private void UpdateDragPositioning(ref Float2 location)
{
+ // Check collision with drag areas
if (new Rectangle(0, 0 - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
_dragOverMode = DragItemPositioning.Above;
else if (IsCollapsed && new Rectangle(0, Height - DefaultDragInsertPositionMargin, Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
_dragOverMode = DragItemPositioning.Below;
else
_dragOverMode = DragItemPositioning.At;
+
+ // Update DraggedOverTrack
+ var timeline = Timeline;
+ if (_dragOverMode == DragItemPositioning.None)
+ {
+ if (timeline != null && timeline.DraggedOverTrack == this)
+ timeline.DraggedOverTrack = null;
+ }
+ else if (timeline != null)
+ timeline.DraggedOverTrack = this;
+ }
+
+ private void ClearDragPositioning()
+ {
+ _dragOverMode = DragItemPositioning.None;
+ var timeline = Timeline;
+ if (timeline != null && timeline.DraggedOverTrack == this)
+ timeline.DraggedOverTrack = null;
}
///
@@ -975,26 +994,21 @@ namespace FlaxEditor.GUI.Timeline
}
// Draw drag and drop effect
- if (IsDragOver && _isDragOverHeader)
+ if (IsDragOver && _timeline.DraggedOverTrack == this)
{
- Color dragOverColor = style.BackgroundSelected * 0.6f;
- Rectangle rect;
switch (_dragOverMode)
{
case DragItemPositioning.At:
- rect = textRect;
+ Render2D.FillRectangle(textRect, style.Selection);
+ Render2D.DrawRectangle(textRect, style.SelectionBorder);
break;
case DragItemPositioning.Above:
- rect = new Rectangle(textRect.X, textRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
+ Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin * 0.5f - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
break;
case DragItemPositioning.Below:
- rect = new Rectangle(textRect.X, textRect.Bottom - DefaultDragInsertPositionMargin, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
- break;
- default:
- rect = Rectangle.Empty;
+ Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin * 0.5f, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
break;
}
- Render2D.FillRectangle(rect, dragOverColor);
}
base.Draw();
@@ -1170,18 +1184,18 @@ namespace FlaxEditor.GUI.Timeline
_dragOverMode = DragItemPositioning.None;
if (result == DragDropEffect.None)
{
- UpdateDrawPositioning(ref location);
+ UpdateDragPositioning(ref location);
// Check if mouse is over header
_isDragOverHeader = TestHeaderHit(ref location);
if (_isDragOverHeader)
{
- // Check if mouse is over arrow
+ if (Timeline != null)
+ Timeline.DraggedOverTrack = this;
+
+ // Expand node if mouse goes over arrow
if (_children.Count > 0 && ArrowRect.Contains(location))
- {
- // Expand track
Expand();
- }
result = OnDragEnterHeader(data);
}
@@ -1199,21 +1213,18 @@ namespace FlaxEditor.GUI.Timeline
var result = base.OnDragMove(ref location, data);
// Check if no children handled that event
- _dragOverMode = DragItemPositioning.None;
+ ClearDragPositioning();
if (result == DragDropEffect.None)
{
- UpdateDrawPositioning(ref location);
+ UpdateDragPositioning(ref location);
// Check if mouse is over header
bool isDragOverHeader = TestHeaderHit(ref location);
if (isDragOverHeader)
{
- // Check if mouse is over arrow
+ // Expand node if mouse goes over arrow
if (_children.Count > 0 && ArrowRect.Contains(location))
- {
- // Expand track
Expand();
- }
if (!_isDragOverHeader)
result = OnDragEnterHeader(data);
@@ -1226,10 +1237,8 @@ namespace FlaxEditor.GUI.Timeline
}
_isDragOverHeader = isDragOverHeader;
- if (result == DragDropEffect.None || !isDragOverHeader)
- {
+ if (result == DragDropEffect.None)
_dragOverMode = DragItemPositioning.None;
- }
}
return result;
@@ -1243,7 +1252,7 @@ namespace FlaxEditor.GUI.Timeline
// Check if no children handled that event
if (result == DragDropEffect.None)
{
- UpdateDrawPositioning(ref location);
+ UpdateDragPositioning(ref location);
// Check if mouse is over header
if (TestHeaderHit(ref location))
@@ -1254,7 +1263,7 @@ namespace FlaxEditor.GUI.Timeline
// Clear cache
_isDragOverHeader = false;
- _dragOverMode = DragItemPositioning.None;
+ ClearDragPositioning();
return result;
}
@@ -1262,15 +1271,15 @@ namespace FlaxEditor.GUI.Timeline
///
public override void OnDragLeave()
{
- base.OnDragLeave();
-
// Clear cache
if (_isDragOverHeader)
{
_isDragOverHeader = false;
OnDragLeaveHeader();
}
- _dragOverMode = DragItemPositioning.None;
+ ClearDragPositioning();
+
+ base.OnDragLeave();
}
///
diff --git a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs
index 95b5dc57f..795674289 100644
--- a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs
+++ b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs
@@ -204,7 +204,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
b.TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
}
}
- menu.AddButton("Select...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track";
+ menu.AddButton("Retarget...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track";
}
///
diff --git a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs
index dbff7267a..ed1a53981 100644
--- a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs
+++ b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs
@@ -7,6 +7,8 @@ using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using FlaxEditor.GUI.Timeline.Undo;
+using FlaxEditor.Scripting;
+using FlaxEditor.Utilities;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
@@ -56,7 +58,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
throw new Exception("Invalid track data.");
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
- var dataBuffer = new byte[e.ValueSize];
var propertyType = TypeUtils.GetManagedType(e.MemberTypeName);
if (propertyType == null)
{
@@ -67,20 +68,40 @@ namespace FlaxEditor.GUI.Timeline.Tracks
return;
}
- GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
- for (int i = 0; i < keyframesCount; i++)
+ if (e.ValueSize != 0)
{
- var time = stream.ReadSingle();
- stream.Read(dataBuffer, 0, e.ValueSize);
- var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
-
- keyframes[i] = new KeyframesEditor.Keyframe
+ // POD value type - use raw memory
+ var dataBuffer = new byte[e.ValueSize];
+ GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
+ for (int i = 0; i < keyframesCount; i++)
{
- Time = time,
- Value = value,
- };
+ var time = stream.ReadSingle();
+ stream.Read(dataBuffer, 0, e.ValueSize);
+ var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
+
+ keyframes[i] = new KeyframesEditor.Keyframe
+ {
+ Time = time,
+ Value = value,
+ };
+ }
+ handle.Free();
+ }
+ else
+ {
+ // Generic value - use Json storage (as UTF-8)
+ for (int i = 0; i < keyframesCount; i++)
+ {
+ var time = stream.ReadSingle();
+ var len = stream.ReadInt32();
+ var value = len != 0 ? FlaxEngine.Json.JsonSerializer.Deserialize(Encoding.UTF8.GetString(stream.ReadBytes(len)), propertyType) : null;
+ keyframes[i] = new KeyframesEditor.Keyframe
+ {
+ Time = time,
+ Value = value,
+ };
+ }
}
- handle.Free();
e.Keyframes.DefaultValue = e.GetDefaultValue(propertyType);
e.Keyframes.SetKeyframes(keyframes);
@@ -113,17 +134,35 @@ namespace FlaxEditor.GUI.Timeline.Tracks
stream.Write(propertyTypeNameData);
stream.Write('\0');
- var dataBuffer = new byte[e.ValueSize];
- IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize);
- for (int i = 0; i < keyframes.Count; i++)
+ if (e.ValueSize != 0)
{
- var keyframe = keyframes[i];
- Marshal.StructureToPtr(keyframe.Value, ptr, true);
- Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
- stream.Write(keyframe.Time);
- stream.Write(dataBuffer);
+ // POD value type - use raw memory
+ var dataBuffer = new byte[e.ValueSize];
+ IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize);
+ for (int i = 0; i < keyframes.Count; i++)
+ {
+ var keyframe = keyframes[i];
+ Marshal.StructureToPtr(keyframe.Value, ptr, true);
+ Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
+ stream.Write(keyframe.Time);
+ stream.Write(dataBuffer);
+ }
+ Marshal.FreeHGlobal(ptr);
+ }
+ else
+ {
+ // Generic value - use Json storage (as UTF-8)
+ for (int i = 0; i < keyframes.Count; i++)
+ {
+ var keyframe = keyframes[i];
+ stream.Write(keyframe.Time);
+ var json = keyframe.Value != null ? FlaxEngine.Json.JsonSerializer.Serialize(keyframe.Value) : null;
+ var len = json?.Length ?? 0;
+ stream.Write(len);
+ if (len > 0)
+ stream.Write(Encoding.UTF8.GetBytes(json));
+ }
}
- Marshal.FreeHGlobal(ptr);
}
private byte[] _keyframesEditingStartData;
@@ -281,7 +320,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
private void OnKeyframesEditingEnd()
{
var after = EditTrackAction.CaptureData(this);
- if (!Utils.ArraysEqual(_keyframesEditingStartData, after))
+ if (!FlaxEngine.Utils.ArraysEqual(_keyframesEditingStartData, after))
Timeline.AddBatchedUndoAction(new EditTrackAction(Timeline, this, _keyframesEditingStartData, after));
_keyframesEditingStartData = null;
}
@@ -308,7 +347,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// The default value.
protected virtual object GetDefaultValue(Type propertyType)
{
- return Activator.CreateInstance(propertyType);
+ var value = TypeUtils.GetDefaultValue(new ScriptType(propertyType));
+ if (value == null)
+ value = Activator.CreateInstance(propertyType);
+ return value;
}
///
diff --git a/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs
index eccd9ab06..f6fb4b4b2 100644
--- a/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs
+++ b/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs
@@ -424,6 +424,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
{ typeof(Guid), KeyframesPropertyTrack.GetArchetype() },
{ typeof(DateTime), KeyframesPropertyTrack.GetArchetype() },
{ typeof(TimeSpan), KeyframesPropertyTrack.GetArchetype() },
+ { typeof(LocalizedString), KeyframesPropertyTrack.GetArchetype() },
{ typeof(string), StringPropertyTrack.GetArchetype() },
};
}
diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs
index d01695abc..b702b530c 100644
--- a/Source/Editor/GUI/Tree/Tree.cs
+++ b/Source/Editor/GUI/Tree/Tree.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI;
@@ -43,7 +44,7 @@ namespace FlaxEditor.GUI.Tree
///
/// The TreeNode that is being dragged over. This could have a value when not dragging.
///
- public TreeNode DraggedOverNode = null;
+ internal TreeNode DraggedOverNode = null;
///
/// Action fired when tree nodes selection gets changed.
@@ -315,10 +316,7 @@ namespace FlaxEditor.GUI.Tree
}
}
- ///
- /// Select all expanded nodes
- ///
- public void SelectAllExpanded()
+ private void BulkSelectUpdateExpanded(bool select = true)
{
if (_supportMultiSelect)
{
@@ -327,7 +325,8 @@ namespace FlaxEditor.GUI.Tree
// Update selection
Selection.Clear();
- WalkSelectExpandedTree(Selection, _children[0] as TreeNode);
+ if (select)
+ WalkSelectExpandedTree(Selection, _children[0] as TreeNode);
// Check if changed
if (Selection.Count != prev.Count || !Selection.SequenceEqual(prev))
@@ -338,6 +337,22 @@ namespace FlaxEditor.GUI.Tree
}
}
+ ///
+ /// Select all expanded nodes
+ ///
+ public void SelectAllExpanded()
+ {
+ BulkSelectUpdateExpanded(true);
+ }
+
+ ///
+ /// Deselect all nodes
+ ///
+ public void DeselectAll()
+ {
+ BulkSelectUpdateExpanded(false);
+ }
+
///
public override void Update(float deltaTime)
{
@@ -472,14 +487,19 @@ namespace FlaxEditor.GUI.Tree
// Check if can use multi selection
if (_supportMultiSelect)
{
- bool isCtrlDown = Root.GetKey(KeyboardKeys.Control);
+ InputOptions options = Editor.Instance.Options.Options.Input;
// Select all expanded nodes
- if (key == KeyboardKeys.A && isCtrlDown)
+ if (options.SelectAll.Process(this))
{
SelectAllExpanded();
return true;
}
+ else if (options.DeselectAll.Process(this))
+ {
+ DeselectAll();
+ return true;
+ }
}
return base.OnKeyDown(key);
diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs
index 3e1827438..36331d6d2 100644
--- a/Source/Editor/GUI/Tree/TreeNode.cs
+++ b/Source/Editor/GUI/Tree/TreeNode.cs
@@ -16,7 +16,7 @@ namespace FlaxEditor.GUI.Tree
///
/// The default drag insert position margin.
///
- public const float DefaultDragInsertPositionMargin = 2.0f;
+ public const float DefaultDragInsertPositionMargin = 3.0f;
///
/// The default node offset on Y axis.
@@ -42,6 +42,7 @@ namespace FlaxEditor.GUI.Tree
private DragItemPositioning _dragOverMode;
private bool _isDragOverHeader;
+ private static ulong _dragEndFrame;
///
/// Gets or sets the text.
@@ -546,14 +547,33 @@ namespace FlaxEditor.GUI.Tree
/// Updates the drag over mode based on the given mouse location.
///
/// The location.
- private void UpdateDrawPositioning(ref Float2 location)
+ private void UpdateDragPositioning(ref Float2 location)
{
+ // Check collision with drag areas
if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
_dragOverMode = DragItemPositioning.Above;
else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
_dragOverMode = DragItemPositioning.Below;
else
_dragOverMode = DragItemPositioning.At;
+
+ // Update DraggedOverNode
+ var tree = ParentTree;
+ if (_dragOverMode == DragItemPositioning.None)
+ {
+ if (tree != null && tree.DraggedOverNode == this)
+ tree.DraggedOverNode = null;
+ }
+ else if (tree != null)
+ tree.DraggedOverNode = this;
+ }
+
+ private void ClearDragPositioning()
+ {
+ _dragOverMode = DragItemPositioning.None;
+ var tree = ParentTree;
+ if (tree != null && tree.DraggedOverNode == this)
+ tree.DraggedOverNode = null;
}
///
@@ -661,27 +681,19 @@ namespace FlaxEditor.GUI.Tree
// Draw drag and drop effect
if (IsDragOver && _tree.DraggedOverNode == this)
{
- Color dragOverColor = style.BackgroundSelected;
- Rectangle rect;
switch (_dragOverMode)
{
case DragItemPositioning.At:
- dragOverColor *= 0.6f;
- rect = textRect;
+ Render2D.FillRectangle(textRect, style.Selection);
+ Render2D.DrawRectangle(textRect, style.SelectionBorder);
break;
case DragItemPositioning.Above:
- dragOverColor *= 1.2f;
- rect = new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
+ Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Top - DefaultDragInsertPositionMargin * 0.5f - DefaultNodeOffsetY - _margin.Top, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
break;
case DragItemPositioning.Below:
- dragOverColor *= 1.2f;
- rect = new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin, textRect.Width, DefaultDragInsertPositionMargin * 2.0f);
- break;
- default:
- rect = Rectangle.Empty;
+ Render2D.DrawRectangle(new Rectangle(textRect.X, textRect.Bottom + _margin.Bottom - DefaultDragInsertPositionMargin * 0.5f, textRect.Width, DefaultDragInsertPositionMargin), style.SelectionBorder);
break;
}
- Render2D.FillRectangle(rect, dragOverColor);
}
// Base
@@ -736,9 +748,8 @@ namespace FlaxEditor.GUI.Tree
UpdateMouseOverFlags(location);
// Clear flag for left button
- if (button == MouseButton.Left)
+ if (button == MouseButton.Left && _isMouseDown)
{
- // Clear flag
_isMouseDown = false;
_mouseDownTime = -1;
}
@@ -746,6 +757,10 @@ namespace FlaxEditor.GUI.Tree
// Check if mouse hits bar and node isn't a root
if (_mouseOverHeader)
{
+ // Skip mouse up event right after drag drop ends
+ if (button == MouseButton.Left && Engine.FrameCount - _dragEndFrame < 10)
+ return true;
+
// Prevent from selecting node when user is just clicking at an arrow
if (!_mouseOverArrow)
{
@@ -937,20 +952,18 @@ namespace FlaxEditor.GUI.Tree
_dragOverMode = DragItemPositioning.None;
if (result == DragDropEffect.None)
{
- UpdateDrawPositioning(ref location);
- if (ParentTree != null)
- ParentTree.DraggedOverNode = this;
+ UpdateDragPositioning(ref location);
// Check if mouse is over header
_isDragOverHeader = TestHeaderHit(ref location);
if (_isDragOverHeader)
{
- // Check if mouse is over arrow
+ if (ParentTree != null)
+ ParentTree.DraggedOverNode = this;
+
+ // Expand node if mouse goes over arrow
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
- {
- // Expand node (no animation)
Expand(true);
- }
result = OnDragEnterHeader(data);
}
@@ -968,26 +981,31 @@ namespace FlaxEditor.GUI.Tree
var result = base.OnDragMove(ref location, data);
// Check if no children handled that event
- _dragOverMode = DragItemPositioning.None;
+ ClearDragPositioning();
if (result == DragDropEffect.None)
{
- UpdateDrawPositioning(ref location);
+ UpdateDragPositioning(ref location);
// Check if mouse is over header
bool isDragOverHeader = TestHeaderHit(ref location);
if (isDragOverHeader)
{
- if (ArrowRect.Contains(location) && HasAnyVisibleChild)
- {
- // Expand node (no animation)
- Expand(true);
- }
+ if (ParentTree != null)
+ ParentTree.DraggedOverNode = this;
+ // Expand node if mouse goes over arrow
+ if (ArrowRect.Contains(location) && HasAnyVisibleChild)
+ Expand(true);
+
if (!_isDragOverHeader)
result = OnDragEnterHeader(data);
else
result = OnDragMoveHeader(data);
}
+ else if (_isDragOverHeader)
+ {
+ OnDragLeaveHeader();
+ }
_isDragOverHeader = isDragOverHeader;
if (result == DragDropEffect.None)
@@ -1005,7 +1023,8 @@ namespace FlaxEditor.GUI.Tree
// Check if no children handled that event
if (result == DragDropEffect.None)
{
- UpdateDrawPositioning(ref location);
+ UpdateDragPositioning(ref location);
+ _dragEndFrame = Engine.FrameCount;
// Check if mouse is over header
if (TestHeaderHit(ref location))
@@ -1016,9 +1035,7 @@ namespace FlaxEditor.GUI.Tree
// Clear cache
_isDragOverHeader = false;
- _dragOverMode = DragItemPositioning.None;
- if (ParentTree != null)
- ParentTree.DraggedOverNode = null;
+ ClearDragPositioning();
return result;
}
@@ -1032,7 +1049,7 @@ namespace FlaxEditor.GUI.Tree
_isDragOverHeader = false;
OnDragLeaveHeader();
}
- _dragOverMode = DragItemPositioning.None;
+ ClearDragPositioning();
base.OnDragLeave();
}
diff --git a/Source/Editor/Gizmo/TransformGizmo.cs b/Source/Editor/Gizmo/TransformGizmo.cs
index 4918743ce..64f969990 100644
--- a/Source/Editor/Gizmo/TransformGizmo.cs
+++ b/Source/Editor/Gizmo/TransformGizmo.cs
@@ -263,6 +263,8 @@ namespace FlaxEditor.Gizmo
// Note: because selection may contain objects and their children we have to split them and get only parents.
// Later during transformation we apply translation/scale/rotation only on them (children inherit transformations)
SceneGraphTools.BuildNodesParents(_selection, _selectionParents);
+
+ base.OnSelectionChanged(newSelection);
}
///
diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs
index 99b1c8122..81f76d392 100644
--- a/Source/Editor/Gizmo/TransformGizmoBase.cs
+++ b/Source/Editor/Gizmo/TransformGizmoBase.cs
@@ -437,8 +437,6 @@ namespace FlaxEditor.Gizmo
{
case Mode.Translate:
UpdateTranslateScale();
- if (Owner.SnapToVertex)
- UpdateVertexSnapping();
break;
case Mode.Scale:
UpdateTranslateScale();
@@ -447,6 +445,8 @@ namespace FlaxEditor.Gizmo
UpdateRotate(dt);
break;
}
+ if (Owner.SnapToVertex)
+ UpdateVertexSnapping();
}
else
{
@@ -553,41 +553,21 @@ namespace FlaxEditor.Gizmo
for (int i = 0; i < SelectionCount; i++)
{
var obj = GetSelectedObject(i);
- if (obj.CanVertexSnap && obj.RayCastSelf(ref ray, out var distance, out _) && distance < closestDistance)
+ if (obj.RayCastSelf(ref ray, out var distance, out _) && distance < closestDistance)
{
closestDistance = distance;
closestObject = obj;
}
}
if (closestObject == null)
- {
- // Find the closest object in selection (in case ray didn't hit anything)
- for (int i = 0; i < SelectionCount; i++)
- {
- var obj = GetSelectedObject(i);
- if (obj.CanVertexSnap)
- {
- GetSelectedObjectsBounds(out var bounds, out _);
- CollisionsHelper.ClosestPointBoxPoint(ref bounds, ref ray.Ray.Position, out var point);
- var distance = Vector3.Distance(ref point, ref ray.Ray.Position);
- if (distance < closestDistance)
- {
- closestDistance = distance;
- closestObject = obj;
- }
- }
- }
- }
- _vertexSnapObject = closestObject;
- if (closestObject == null)
- return;
+ return; // ignore it if there is nothing under the mouse closestObject is only null if ray caster missed everything or Selection Count == 0
- // Find the closest vertex to bounding box point (collision detection approximation)
- var closestPoint = ray.Ray.GetPoint(closestDistance);
- if (!closestObject.OnVertexSnap(ref closestPoint, out _vertexSnapPoint))
+ _vertexSnapObject = closestObject;
+ if (!closestObject.OnVertexSnap(ref ray.Ray, closestDistance, out _vertexSnapPoint))
{
- // Failed to get the closest point
- _vertexSnapPoint = closestPoint;
+ // The OnVertexSnap is unimplemented or failed to get point return because there is nothing to do
+ _vertexSnapPoint = Vector3.Zero;
+ return;
}
// Transform back to the local space of the object to work when moving it
@@ -619,12 +599,11 @@ namespace FlaxEditor.Gizmo
for (int i = 0; i < SelectionCount; i++)
rayCast.ExcludeObjects.Add(GetSelectedObject(i));
var hit = Owner.SceneGraphRoot.RayCast(ref rayCast, out var distance, out var _);
- if (hit != null && hit.CanVertexSnap)
+ if (hit != null)
{
- var point = rayCast.Ray.GetPoint(distance);
- if (hit.OnVertexSnap(ref point, out var pointSnapped)
+ if (hit.OnVertexSnap(ref rayCast.Ray, distance, out var pointSnapped)
//&& Vector3.Distance(point, pointSnapped) <= 25.0f
- )
+ )
{
_vertexSnapObjectTo = hit;
_vertexSnapPointTo = hit.Transform.WorldToLocal(pointSnapped);
@@ -712,5 +691,12 @@ namespace FlaxEditor.Gizmo
protected virtual void OnDuplicate()
{
}
+
+ ///
+ public override void OnSelectionChanged(List newSelection)
+ {
+ EndVertexSnapping();
+ UpdateGizmoPosition();
+ }
}
}
diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp
index 9c6c73dfd..43c805bf4 100644
--- a/Source/Editor/Managed/ManagedEditor.Internal.cpp
+++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp
@@ -509,6 +509,21 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CanSetToRoot(Prefab* prefab, Actor* ta
return true;
}
+DEFINE_INTERNAL_CALL(void) EditorInternal_GetPrefabNestedObject(Guid* prefabId, Guid* prefabObjectId, Guid* outPrefabId, Guid* outPrefabObjectId)
+{
+ *outPrefabId = Guid::Empty;
+ *outPrefabObjectId = Guid::Empty;
+ const auto prefab = Content::Load(*prefabId);
+ if (!prefab)
+ return;
+ const ISerializable::DeserializeStream** prefabObjectDataPtr = prefab->ObjectsDataCache.TryGet(*prefabObjectId);
+ if (!prefabObjectDataPtr)
+ return;
+ const ISerializable::DeserializeStream& prefabObjectData = **prefabObjectDataPtr;
+ JsonTools::GetGuidIfValid(*outPrefabId, prefabObjectData, "PrefabID");
+ JsonTools::GetGuidIfValid(*outPrefabObjectId, prefabObjectData, "PrefabObjectID");
+}
+
DEFINE_INTERNAL_CALL(float) EditorInternal_GetAnimationTime(AnimatedModel* animatedModel)
{
return animatedModel && animatedModel->GraphInstance.State.Count() == 1 ? animatedModel->GraphInstance.State[0].Animation.TimePosition : 0.0f;
diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp
index 0abe361ad..7a9e55b88 100644
--- a/Source/Editor/Managed/ManagedEditor.cpp
+++ b/Source/Editor/Managed/ManagedEditor.cpp
@@ -278,13 +278,7 @@ void ManagedEditor::Update()
void ManagedEditor::Exit()
{
if (WasExitCalled)
- {
- // Ups xD
- LOG(Warning, "Managed Editor exit called after exit or before init.");
return;
- }
-
- // Set flag
WasExitCalled = true;
// Skip if managed object is missing
diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs
index 5cd212db0..e0e464c04 100644
--- a/Source/Editor/Modules/PrefabsModule.cs
+++ b/Source/Editor/Modules/PrefabsModule.cs
@@ -225,8 +225,15 @@ namespace FlaxEditor.Modules
throw new ArgumentException("Missing prefab to apply.");
PrefabApplying?.Invoke(prefab, instance);
+ // When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
+ var originalTransform = instance.LocalTransform;
+ if (instance.IsPrefabRoot && instance.Scene != null)
+ instance.LocalTransform = prefab.GetDefaultInstance().Transform;
+
// Call backend
- if (PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance)))
+ var failed = PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance));
+ instance.LocalTransform = originalTransform;
+ if (failed)
throw new Exception("Failed to apply the prefab. See log to learn more.");
PrefabApplied?.Invoke(prefab, instance);
diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs
index 831ebf3e0..df9009cc8 100644
--- a/Source/Editor/Modules/SceneEditingModule.cs
+++ b/Source/Editor/Modules/SceneEditingModule.cs
@@ -60,13 +60,26 @@ namespace FlaxEditor.Modules
{
}
+ private void BulkScenesSelectUpdate(bool select = true)
+ {
+ // Blank list deselects all
+ Select(select ? Editor.Scene.Root.ChildNodes : new List());
+ }
+
///
/// Selects all scenes.
///
public void SelectAllScenes()
{
- // Select all scenes (linked to the root node)
- Select(Editor.Scene.Root.ChildNodes);
+ BulkScenesSelectUpdate(true);
+ }
+
+ ///
+ /// Deselects all scenes.
+ ///
+ public void DeselectAllScenes()
+ {
+ BulkScenesSelectUpdate(false);
}
///
@@ -320,7 +333,7 @@ namespace FlaxEditor.Modules
actorNode.PostSpawn();
// Create undo action
- IUndoAction action = new DeleteActorsAction(new List(1) { actorNode }, true);
+ IUndoAction action = new DeleteActorsAction(actorNode, true);
if (autoSelect)
{
var before = Selection.ToArray();
diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs
index 302d1e02a..9aae0b72f 100644
--- a/Source/Editor/Modules/SceneModule.cs
+++ b/Source/Editor/Modules/SceneModule.cs
@@ -6,6 +6,7 @@ using System.IO;
using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
+using FlaxEngine.GUI;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Modules
@@ -454,6 +455,41 @@ namespace FlaxEditor.Modules
Profiler.EndEvent();
}
+ private Dictionary _uiRootSizes;
+
+ internal void OnSaveStart(ContainerControl uiRoot)
+ {
+ // Force viewport UI to have fixed size during scene/prefabs saving to result in stable data (less mess in version control diffs)
+ if (_uiRootSizes == null)
+ _uiRootSizes = new Dictionary();
+ _uiRootSizes[uiRoot] = uiRoot.Size;
+ uiRoot.Size = new Float2(1920, 1080);
+ }
+
+ internal void OnSaveEnd(ContainerControl uiRoot)
+ {
+ // Restore cached size of the UI root container
+ if (_uiRootSizes != null && _uiRootSizes.Remove(uiRoot, out var size))
+ {
+ uiRoot.Size = size;
+ }
+ }
+
+ private void OnSceneSaving(Scene scene, Guid sceneId)
+ {
+ OnSaveStart(RootControl.GameRoot);
+ }
+
+ private void OnSceneSaved(Scene scene, Guid sceneId)
+ {
+ OnSaveEnd(RootControl.GameRoot);
+ }
+
+ private void OnSceneSaveError(Scene scene, Guid sceneId)
+ {
+ OnSaveEnd(RootControl.GameRoot);
+ }
+
private void OnSceneLoaded(Scene scene, Guid sceneId)
{
var startTime = DateTime.UtcNow;
@@ -659,6 +695,9 @@ namespace FlaxEditor.Modules
Root = new ScenesRootNode();
// Bind events
+ Level.SceneSaving += OnSceneSaving;
+ Level.SceneSaved += OnSceneSaved;
+ Level.SceneSaveError += OnSceneSaveError;
Level.SceneLoaded += OnSceneLoaded;
Level.SceneUnloading += OnSceneUnloading;
Level.ActorSpawned += OnActorSpawned;
@@ -673,6 +712,9 @@ namespace FlaxEditor.Modules
public override void OnExit()
{
// Unbind events
+ Level.SceneSaving -= OnSceneSaving;
+ Level.SceneSaved -= OnSceneSaved;
+ Level.SceneSaveError -= OnSceneSaveError;
Level.SceneLoaded -= OnSceneLoaded;
Level.SceneUnloading -= OnSceneUnloading;
Level.ActorSpawned -= OnActorSpawned;
diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs
index 39c86d5c3..c77bc4492 100644
--- a/Source/Editor/Modules/SimulationModule.cs
+++ b/Source/Editor/Modules/SimulationModule.cs
@@ -264,11 +264,14 @@ namespace FlaxEditor.Modules
_enterPlayFocusedWindow = gameWin;
// Show Game widow if hidden
- if (gameWin != null && gameWin.FocusOnPlay)
+ if (gameWin != null)
{
- gameWin.FocusGameViewport();
+ if (gameWin.FocusOnPlay)
+ gameWin.FocusGameViewport();
+ gameWin.SetWindowMode(Editor.Options.Options.Interface.DefaultGameWindowMode);
}
+
Editor.Log("[PlayMode] Enter");
}
diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs
index e1dc68fc2..22044d095 100644
--- a/Source/Editor/Modules/UIModule.cs
+++ b/Source/Editor/Modules/UIModule.cs
@@ -54,6 +54,7 @@ namespace FlaxEditor.Modules
private ContextMenuButton _menuEditDelete;
private ContextMenuButton _menuEditDuplicate;
private ContextMenuButton _menuEditSelectAll;
+ private ContextMenuButton _menuEditDeselectAll;
private ContextMenuButton _menuEditFind;
private ContextMenuButton _menuSceneMoveActorToViewport;
private ContextMenuButton _menuSceneAlignActorWithViewport;
@@ -554,6 +555,7 @@ namespace FlaxEditor.Modules
_menuEditDuplicate = cm.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
cm.AddSeparator();
_menuEditSelectAll = cm.AddButton("Select all", inputOptions.SelectAll, Editor.SceneEditing.SelectAllScenes);
+ _menuEditDeselectAll = cm.AddButton("Deselect all", inputOptions.DeselectAll, Editor.SceneEditing.DeselectAllScenes);
_menuCreateParentForSelectedActors = cm.AddButton("Create parent for selected actors", Editor.SceneEditing.CreateParentForSelectedActors);
_menuEditFind = cm.AddButton("Find", inputOptions.Search, Editor.Windows.SceneWin.Search);
cm.AddSeparator();
@@ -673,6 +675,7 @@ namespace FlaxEditor.Modules
_menuEditDelete.ShortKeys = inputOptions.Delete.ToString();
_menuEditDuplicate.ShortKeys = inputOptions.Duplicate.ToString();
_menuEditSelectAll.ShortKeys = inputOptions.SelectAll.ToString();
+ _menuEditDeselectAll.ShortKeys = inputOptions.DeselectAll.ToString();
_menuEditFind.ShortKeys = inputOptions.Search.ToString();
_menuGamePlayGame.ShortKeys = inputOptions.Play.ToString();
_menuGamePlayCurrentScenes.ShortKeys = inputOptions.PlayCurrentScenes.ToString();
@@ -739,6 +742,17 @@ namespace FlaxEditor.Modules
playActionGroup.SelectedChanged = SetPlayAction;
Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; };
+ var windowModesGroup = new ContextMenuSingleSelectGroup();
+ var windowTypeMenu = _toolStripPlay.ContextMenu.AddChildMenu("Game window mode");
+ windowModesGroup.AddItem("Docked", InterfaceOptions.GameWindowMode.Docked, null, "Shows the game window docked, inside the editor");
+ windowModesGroup.AddItem("Popup", InterfaceOptions.GameWindowMode.PopupWindow, null, "Shows the game window as a popup");
+ windowModesGroup.AddItem("Maximized", InterfaceOptions.GameWindowMode.MaximizedWindow, null, "Shows the game window maximized (Same as pressing F11)");
+ windowModesGroup.AddItem("Borderless", InterfaceOptions.GameWindowMode.BorderlessWindow, null, "Shows the game window borderless");
+ windowModesGroup.AddItemsToContextMenu(windowTypeMenu.ContextMenu);
+ windowModesGroup.Selected = Editor.Options.Options.Interface.DefaultGameWindowMode;
+ windowModesGroup.SelectedChanged = SetGameWindowMode;
+ Editor.Options.OptionsChanged += options => { windowModesGroup.Selected = options.Interface.DefaultGameWindowMode; };
+
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game ({inputOptions.Pause})");
_toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game");
@@ -871,6 +885,7 @@ namespace FlaxEditor.Modules
_menuEditDelete.Enabled = hasSthSelected;
_menuEditDuplicate.Enabled = hasSthSelected;
_menuEditSelectAll.Enabled = Level.IsAnySceneLoaded;
+ _menuEditDeselectAll.Enabled = hasSthSelected;
control.PerformLayout();
}
@@ -1043,6 +1058,13 @@ namespace FlaxEditor.Modules
Editor.Options.Apply(options);
}
+ private void SetGameWindowMode(InterfaceOptions.GameWindowMode newGameWindowMode)
+ {
+ var options = Editor.Options.Options;
+ options.Interface.DefaultGameWindowMode = newGameWindowMode;
+ Editor.Options.Apply(options);
+ }
+
private void OnMainWindowClosing()
{
// Clear UI references (GUI cannot be used after window closing)
diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs
index cb79373b7..9bd556f20 100644
--- a/Source/Editor/Options/InputOptions.cs
+++ b/Source/Editor/Options/InputOptions.cs
@@ -56,6 +56,10 @@ namespace FlaxEditor.Options
[EditorDisplay("Common"), EditorOrder(190)]
public InputBinding SelectAll = new InputBinding(KeyboardKeys.A, KeyboardKeys.Control);
+ [DefaultValue(typeof(InputBinding), "Ctrl+Shift+A")]
+ [EditorDisplay("Common"), EditorOrder(195)]
+ public InputBinding DeselectAll = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift, KeyboardKeys.Control);
+
[DefaultValue(typeof(InputBinding), "F")]
[EditorDisplay("Common"), EditorOrder(200)]
public InputBinding FocusSelection = new InputBinding(KeyboardKeys.F);
diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs
index c3b934531..05557f3c0 100644
--- a/Source/Editor/Options/InterfaceOptions.cs
+++ b/Source/Editor/Options/InterfaceOptions.cs
@@ -90,6 +90,32 @@ namespace FlaxEditor.Options
PlayScenes,
}
+ ///
+ /// Available window modes for the game window.
+ ///
+ public enum GameWindowMode
+ {
+ ///
+ /// Shows the game window docked, inside the editor.
+ ///
+ Docked,
+
+ ///
+ /// Shows the game window as a popup.
+ ///
+ PopupWindow,
+
+ ///
+ /// Shows the game window maximized. (Same as pressing F11)
+ ///
+ MaximizedWindow,
+
+ ///
+ /// Shows the game window borderless.
+ ///
+ BorderlessWindow,
+ }
+
///
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
///
@@ -229,6 +255,13 @@ namespace FlaxEditor.Options
[EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)]
public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes;
+ ///
+ /// Gets or sets a value indicating how the game window should be displayed when the game is launched.
+ ///
+ [DefaultValue(GameWindowMode.Docked)]
+ [EditorDisplay("Play In-Editor", "Game Window Mode"), EditorOrder(420), Tooltip("Determines how the game window is displayed when the game is launched.")]
+ public GameWindowMode DefaultGameWindowMode { get; set; } = GameWindowMode.Docked;
+
///
/// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game.
///
diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs
index f04368a77..2dceb3400 100644
--- a/Source/Editor/Options/OptionsModule.cs
+++ b/Source/Editor/Options/OptionsModule.cs
@@ -212,6 +212,14 @@ namespace FlaxEditor.Options
string styleName = themeOptions.SelectedStyle;
if (styleName != ThemeOptions.DefaultName && styleName != ThemeOptions.LightDefault && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null)
{
+ // Setup defaults for newly added components that might be missing
+ if (style.Selection == Color.Transparent && style.SelectionBorder == Color.Transparent)
+ {
+ // [Deprecated on 6.03.2024, expires on 6.03.2026]
+ style.Selection = Color.Orange * 0.4f;
+ style.SelectionBorder = Color.Orange;
+ }
+
Style.Current = style;
}
else
@@ -258,6 +266,8 @@ namespace FlaxEditor.Options
TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46),
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
ProgressNormal = Color.FromBgra(0xFF0ad328),
+ Selection = Color.Orange * 0.4f,
+ SelectionBorder = Color.Orange,
Statusbar = new Style.StatusbarStyle
{
@@ -318,6 +328,8 @@ namespace FlaxEditor.Options
TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f),
CollectionBackgroundColor = new Color(0.85f, 0.85f, 0.88f, 1f),
ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f),
+ Selection = Color.Orange * 0.4f,
+ SelectionBorder = Color.Orange,
// Fonts
FontTitle = options.Interface.TitleFont.GetFont(),
diff --git a/Source/Editor/SceneGraph/ActorNode.cs b/Source/Editor/SceneGraph/ActorNode.cs
index 5c6adb055..91bf26103 100644
--- a/Source/Editor/SceneGraph/ActorNode.cs
+++ b/Source/Editor/SceneGraph/ActorNode.cs
@@ -316,41 +316,7 @@ namespace FlaxEditor.SceneGraph
{
base.OnParentChanged();
- // Update UI (special case if actor is spawned and added to existing scene tree)
- var parentTreeNode = (parentNode as ActorNode)?.TreeNode;
- if (parentTreeNode != null && !parentTreeNode.IsLayoutLocked)
- {
- parentTreeNode.IsLayoutLocked = true;
- _treeNode.Parent = parentTreeNode;
- _treeNode.IndexInParent = _actor.OrderInParent;
- parentTreeNode.IsLayoutLocked = false;
-
- // Skip UI update if node won't be in a view
- if (parentTreeNode.IsCollapsed)
- {
- TreeNode.UnlockChildrenRecursive();
- }
- else
- {
- // Try to perform layout at the level where it makes it the most performant (the least computations)
- var tree = parentTreeNode.ParentTree;
- if (tree != null)
- {
- if (tree.Parent is FlaxEngine.GUI.Panel treeParent)
- treeParent.PerformLayout();
- else
- tree.PerformLayout();
- }
- else
- {
- parentTreeNode.PerformLayout();
- }
- }
- }
- else
- {
- _treeNode.Parent = parentTreeNode;
- }
+ _treeNode.OnParentChanged(_actor, parentNode as ActorNode);
}
///
diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
index f1ab36041..60be5bef9 100644
--- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
+++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
@@ -1,5 +1,11 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+#if USE_LARGE_WORLDS
+using Real = System.Double;
+#else
+using Real = System.Single;
+#endif
+
using System;
using System.Collections.Generic;
using FlaxEditor.Content;
@@ -25,18 +31,20 @@ namespace FlaxEditor.SceneGraph.Actors
}
///
- public override bool OnVertexSnap(ref Vector3 point, out Vector3 result)
+ public override bool OnVertexSnap(ref Ray ray, Real hitDistance, out Vector3 result)
{
- result = point;
+ // Find the closest vertex to bounding box point (collision detection approximation)
+ result = ray.GetPoint(hitDistance);
var model = ((StaticModel)Actor).Model;
if (model && !model.WaitForLoaded())
{
// TODO: move to C++ and use cached vertex buffer internally inside the Mesh
if (_vertices == null)
_vertices = new();
- var pointLocal = (Float3)Actor.Transform.WorldToLocal(point);
- var minDistance = float.MaxValue;
- foreach (var lod in model.LODs)
+ var pointLocal = (Float3)Actor.Transform.WorldToLocal(result);
+ var minDistance = Real.MaxValue;
+ var lodIndex = 0; // TODO: use LOD index based on the game view
+ var lod = model.LODs[lodIndex];
{
var hit = false;
foreach (var mesh in lod.Meshes)
diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
index 270647433..6056cf68b 100644
--- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
+++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using FlaxEditor.Actions;
using FlaxEditor.Content;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Drag;
@@ -14,7 +15,6 @@ using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
-using Object = FlaxEngine.Object;
namespace FlaxEditor.SceneGraph.GUI
{
@@ -82,8 +82,51 @@ namespace FlaxEditor.SceneGraph.GUI
UpdateText();
}
+ internal void OnParentChanged(Actor actor, ActorNode parentNode)
+ {
+ // Update cached value
+ _orderInParent = actor.OrderInParent;
+
+ // Update UI (special case if actor is spawned and added to existing scene tree)
+ var parentTreeNode = parentNode?.TreeNode;
+ if (parentTreeNode != null && !parentTreeNode.IsLayoutLocked)
+ {
+ parentTreeNode.IsLayoutLocked = true;
+ Parent = parentTreeNode;
+ IndexInParent = _orderInParent;
+ parentTreeNode.IsLayoutLocked = false;
+
+ // Skip UI update if node won't be in a view
+ if (parentTreeNode.IsCollapsed)
+ {
+ UnlockChildrenRecursive();
+ }
+ else
+ {
+ // Try to perform layout at the level where it makes it the most performant (the least computations)
+ var tree = parentTreeNode.ParentTree;
+ if (tree != null)
+ {
+ if (tree.Parent is Panel treeParent)
+ treeParent.PerformLayout();
+ else
+ tree.PerformLayout();
+ }
+ else
+ {
+ parentTreeNode.PerformLayout();
+ }
+ }
+ }
+ else
+ {
+ Parent = parentTreeNode;
+ }
+ }
+
internal void OnOrderInParentChanged()
{
+ // Use cached value to check if we need to update UI layout (and update siblings order at once)
if (Parent is ActorTreeNode parent)
{
var anyChanged = false;
@@ -419,134 +462,6 @@ namespace FlaxEditor.SceneGraph.GUI
_dragHandlers.OnDragLeave();
}
- [Serializable]
- private class ReparentAction : IUndoAction
- {
- [Serialize]
- private Guid[] _ids;
-
- [Serialize]
- private int _actorsCount;
-
- [Serialize]
- private Guid[] _prefabIds;
-
- [Serialize]
- private Guid[] _prefabObjectIds;
-
- public ReparentAction(Actor actor)
- : this(new List { actor })
- {
- }
-
- public ReparentAction(List actors)
- {
- var allActors = new List(Mathf.NextPowerOfTwo(actors.Count));
-
- for (int i = 0; i < actors.Count; i++)
- {
- GetAllActors(allActors, actors[i]);
- }
-
- var allScripts = new List