diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 405b78037..436850e80 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,12 +1,13 @@ name: Continuous Deployment on: schedule: - - cron: '15 4 * * *' + - cron: '15 6 * * *' workflow_dispatch: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -20,7 +21,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -53,7 +54,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -83,7 +84,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev @@ -114,7 +115,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev @@ -147,7 +148,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -175,7 +176,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET diff --git a/Content/Shaders/Editor/Grid.flax b/Content/Shaders/Editor/Grid.flax index 1c87a1332..440d37512 100644 --- a/Content/Shaders/Editor/Grid.flax +++ b/Content/Shaders/Editor/Grid.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce516159bb427e1c2cbd48f86be17b2b3c065d2ecd6943aace05451853d6b3e1 -size 4626 +oid sha256:9aa0cb389296afe4ee474395f188e61f8a1e428aac1f09fc7d54825b1d6e58df +size 4744 diff --git a/README.md b/README.md index 5e1374e02..c4d6e7298 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Follow the instructions below to compile and run the engine from source. * Install Visual Studio 2022 or newer * Install Windows 8.1 SDK or newer (via Visual Studio Installer) * Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer) -* Install .NET 8 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) +* Install .NET 8 or 9 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Install Git with LFS * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** @@ -44,8 +44,9 @@ Follow the instructions below to compile and run the engine from source. ## Linux * Install Visual Studio Code -* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) +* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Ubuntu: `sudo apt install dotnet-sdk-8.0` + * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Ubuntu: `sudo apt install vulkan-sdk` * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers` @@ -67,12 +68,12 @@ Follow the instructions below to compile and run the engine from source. ## Mac * Install XCode -* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) +* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Clone repo (with LFS) * Run `GenerateProjectFiles.command` * Open workspace with XCode or Visual Studio Code -* Build and run (configuration `Editor.Mac.Development`) +* Build and run (configuration `Editor.Mac.Development`) #### Troubleshooting diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 18c5b6d7b..77e34dc70 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -750,7 +750,8 @@ namespace FlaxEditor.Content // Draw short name Render2D.PushClip(ref textRect); - Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); + var scale = 0.95f * view.ViewScale; + Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, scale); Render2D.PopClip(); if (IsBeingCut) diff --git a/Source/Editor/Content/Proxy/JsonAssetProxy.cs b/Source/Editor/Content/Proxy/JsonAssetProxy.cs index 27d0e2347..dfdec8330 100644 --- a/Source/Editor/Content/Proxy/JsonAssetProxy.cs +++ b/Source/Editor/Content/Proxy/JsonAssetProxy.cs @@ -1,9 +1,11 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.Content.Create; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.Scripting; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; using FlaxEngine; @@ -84,18 +86,67 @@ namespace FlaxEditor.Content if (_element != null) { - // Define the rule for the types that can be used to create a json data asset - _element.CustomControl.CheckValid += type => - type.Type != null && - type.IsClass && - type.Type.IsVisible && - !type.IsAbstract && - !type.IsGenericType && - type.Type.GetConstructor(Type.EmptyTypes) != null && - !typeof(FlaxEngine.GUI.Control).IsAssignableFrom(type.Type) && - !typeof(FlaxEngine.Object).IsAssignableFrom(type.Type); + _element.CustomControl.CheckValid += OnCheckValidJsonAssetType; } } + + private static Type[] BlacklistedClasses = + [ + typeof(System.Attribute), + typeof(FlaxEngine.Object), + typeof(FlaxEngine.GUI.Control), + ]; + + private static Type[] BlacklistedStructs = + [ + typeof(Float2), + typeof(Float3), + typeof(Float4), + typeof(Double2), + typeof(Double3), + typeof(Double4), + typeof(Vector2), + typeof(Vector3), + typeof(Vector4), + typeof(Half2), + typeof(Half3), + typeof(Half4), + typeof(Int2), + typeof(Int3), + typeof(Int4), + typeof(Transform), + typeof(Quaternion), + typeof(BoundingBox), + typeof(BoundingSphere), + typeof(BoundingFrustum), + typeof(Ray), + typeof(Plane), + typeof(Matrix), + typeof(Color), + typeof(Color32), + typeof(FloatR11G11B10), + typeof(FloatR10G10B10A2), + typeof(FlaxEngine.Half), + ]; + + private static bool OnCheckValidJsonAssetType(ScriptType type) + { + // Define the rule for the types that can be used to create a json data asset + var mType = type.Type; + if (mType == null || + type.IsAbstract || + type.IsStatic || + type.IsGenericType || + !mType.IsVisible) + return false; + if (type.IsClass) + return mType.GetConstructor(Type.EmptyTypes) != null && BlacklistedClasses.FirstOrDefault(x => x.IsAssignableFrom(mType)) == null; + if (type.IsStructure) + return !type.IsPrimitive && + !type.IsVoid && + !BlacklistedStructs.Contains(mType); + return false; + } } } @@ -175,7 +226,7 @@ namespace FlaxEditor.Content { _thumbnail = SpriteHandle.Invalid; } - + /// /// Constructor with overriden thumbnail. /// @@ -196,7 +247,7 @@ namespace FlaxEditor.Content { Editor.SaveJsonAsset(outputPath, new T()); } - + /// public override AssetItem ConstructItem(string path, string typeName, ref Guid id) { diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs index b3d7ffa49..524bebfc3 100644 --- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs +++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs @@ -496,7 +496,7 @@ namespace FlaxEditor.Content.Thumbnails // Prepare requests bool isAnyReady = false; int checks = Mathf.Min(10, _requests.Count); - for (int i = 0; i < checks; i++) + for (int i = 0; i < checks && i < _requests.Count; i++) { var request = _requests[i]; try diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 8c3810c3c..79c00b38a 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -883,7 +883,7 @@ namespace FlaxEditor.CustomEditors /// protected virtual void ClearToken() { - ParentEditor.ClearToken(); + ParentEditor?.ClearToken(); } } } diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 3e6785491..d1cdd8780 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -68,12 +68,16 @@ namespace FlaxEditor.CustomEditors.Dedicated // Use default prefab instance as a reference for the editor Values.SetReferenceValue(prefabInstance); - if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter) + // Display prefab UI (when displaying object inside Prefab Window then display only nested prefabs) + var prefabId = prefab.ID; + Editor.GetPrefabNestedObject(ref prefabId, ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId); + var nestedPrefab = FlaxEngine.Content.Load(nestedPrefabId); + var panel = layout.CustomContainer(); + panel.CustomControl.Height = 20.0f; + panel.CustomControl.SlotsVertically = 1; + if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter || nestedPrefab) { - // Add some UI - var panel = layout.CustomContainer(); - panel.CustomControl.Height = 20.0f; - panel.CustomControl.SlotsVertically = 1; + var targetPrefab = nestedPrefab ?? prefab; panel.CustomControl.SlotsHorizontally = 3; // Selecting actor prefab asset @@ -81,17 +85,21 @@ namespace FlaxEditor.CustomEditors.Dedicated selectPrefab.Button.Clicked += () => { Editor.Instance.Windows.ContentWin.ClearItemsSearch(); - Editor.Instance.Windows.ContentWin.Select(prefab); + Editor.Instance.Windows.ContentWin.Select(targetPrefab); }; // Edit selected prefab asset var editPrefab = panel.Button("Edit Prefab"); - editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(prefab.ID)); - - // Viewing changes applied to this actor - var viewChanges = panel.Button("View Changes"); - viewChanges.Button.Clicked += () => ViewChanges(viewChanges.Button, new Float2(0.0f, 20.0f)); + editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(targetPrefab.ID)); } + else + { + panel.CustomControl.SlotsHorizontally = 1; + } + + // Viewing changes applied to this actor + var viewChanges = panel.Button("View Changes"); + viewChanges.Button.Clicked += () => ViewChanges(viewChanges.Button, new Float2(0.0f, 20.0f)); // Link event to update editor on prefab apply _linkedPrefabId = prefab.ID; diff --git a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs index 81d826a04..a39297572 100644 --- a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs @@ -15,13 +15,23 @@ namespace FlaxEditor.CustomEditors.Dedicated private int _firstTimeShow; private BezierCurveEditor _curve; private Splitter _splitter; + private string _heightCachedPath; /// public override void Initialize(LayoutElementsContainer layout) { var item = layout.CustomContainer>(); _curve = item.CustomControl; - _curve.Height = 120.0f; + var height = 120.0f; + var presenter = Presenter; + if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) + { + // Try to restore curve height + _heightCachedPath = layout.GetLayoutCachePath("Height"); + if (Editor.Instance.ProjectCache.TryGetCustomData(_heightCachedPath, out float cachedHeight) && cachedHeight > 10.0f) + height = cachedHeight; + } + _curve.Height = height; _curve.Edited += OnCurveEdited; _firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing) _splitter = new Splitter @@ -45,7 +55,11 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplitterMoved(Float2 location) { - _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f); + _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f); + + // Cache curve height + if (_heightCachedPath != null) + Editor.Instance.ProjectCache.SetCustomData(_heightCachedPath, _curve.Height); } /// @@ -133,13 +147,23 @@ namespace FlaxEditor.CustomEditors.Dedicated private int _firstTimeShow; private LinearCurveEditor _curve; private Splitter _splitter; + private string _heightCachedPath; /// public override void Initialize(LayoutElementsContainer layout) { var item = layout.CustomContainer>(); _curve = item.CustomControl; - _curve.Height = 120.0f; + var height = 120.0f; + var presenter = Presenter; + if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) + { + // Try to restore curve height + _heightCachedPath = layout.GetLayoutCachePath("Height"); + if (Editor.Instance.ProjectCache.TryGetCustomData(_heightCachedPath, out float cachedHeight) && cachedHeight > 10.0f) + height = cachedHeight; + } + _curve.Height = height; _curve.Edited += OnCurveEdited; _firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing) _splitter = new Splitter @@ -164,6 +188,10 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplitterMoved(Float2 location) { _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f); + + // Cache curve height + if (_heightCachedPath != null) + Editor.Instance.ProjectCache.SetCustomData(_heightCachedPath, _curve.Height); } /// diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 83fa1f129..e9c405207 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.Content; using FlaxEditor.GUI; +using FlaxEditor.GUI.Drag; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -122,7 +123,9 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); - if (!HasDifferentValues) + var differentValues = HasDifferentValues; + Picker.DifferentValues = differentValues; + if (!differentValues) { _isRefreshing = true; var value = Values[0]; @@ -156,6 +159,17 @@ namespace FlaxEditor.CustomEditors.Editors private Rectangle DropdownRect => new Rectangle(Width - DropdownIconSize - DropdownIconMargin, DropdownIconMargin, DropdownIconSize, DropdownIconSize); public Action ShowPicker; + public Action OnAssetDropped; + + private DragItems _dragItems; + private DragHandlers _dragHandlers; + private bool _hasValidDragOver; + private Func _validate; + + public void SetValidationMethod(Func validate) + { + _validate = validate; + } public override void Draw() { @@ -164,6 +178,14 @@ namespace FlaxEditor.CustomEditors.Editors var style = FlaxEngine.GUI.Style.Current; var dropdownRect = DropdownRect; Render2D.DrawSprite(style.ArrowDown, dropdownRect, Enabled ? (DropdownRect.Contains(PointFromWindow(RootWindow.MousePosition)) ? style.BorderSelected : style.Foreground) : style.ForegroundDisabled); + + // Check if drag is over + if (IsDragOver && _hasValidDragOver) + { + var bounds = new Rectangle(Float2.Zero, Size); + Render2D.FillRectangle(bounds, style.Selection); + Render2D.DrawRectangle(bounds, style.SelectionBorder); + } } public override bool OnMouseDown(Float2 location, MouseButton button) @@ -207,6 +229,68 @@ namespace FlaxEditor.CustomEditors.Editors return result; } } + + private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None; + + /// + public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) + { + base.OnDragEnter(ref location, data); + + // Ensure to have valid drag helpers (uses lazy init) + if (_dragItems == null) + _dragItems = new DragItems(ValidateDragAsset); + if (_dragHandlers == null) + { + _dragHandlers = new DragHandlers + { + _dragItems, + }; + } + + _hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None; + + + return DragEffect; + } + + private bool ValidateDragAsset(ContentItem contentItem) + { + // Load or get asset + return _validate?.Invoke(contentItem) ?? false; + } + + /// + public override DragDropEffect OnDragMove(ref Float2 location, DragData data) + { + base.OnDragMove(ref location, data); + + return DragEffect; + } + + /// + public override void OnDragLeave() + { + _hasValidDragOver = false; + _dragHandlers.OnDragLeave(); + + base.OnDragLeave(); + } + + /// + public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) + { + var result = DragEffect; + + base.OnDragDrop(ref location, data); + + if (_dragItems.HasValidDrag) + { + OnAssetDropped(_dragItems.Objects[0]); + } + + return result; + } } private TextBoxWithPicker _textBox; @@ -221,13 +305,21 @@ namespace FlaxEditor.CustomEditors.Editors { if (HasDifferentTypes) return; + + _validator = new AssetPickerValidator(ScriptType.Null); _textBox = layout.Custom().CustomControl; _textBox.ShowPicker = OnShowPicker; + _textBox.OnAssetDropped = OnItemDropped; _textBox.EditEnd += OnEditEnd; - _validator = new AssetPickerValidator(ScriptType.Null); + _textBox.SetValidationMethod(_validator.IsValid); AssetRefEditor.ApplyAssetReferenceAttribute(Values, out _, _validator); } + private void OnItemDropped(ContentItem item) + { + SetPickerPath(item); + } + private void OnShowPicker() { if (_validator.AssetType != ScriptType.Null) @@ -285,12 +377,9 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); - if (!HasDifferentValues) - { - _isRefreshing = true; - _textBox.Text = GetPath(); - _isRefreshing = false; - } + _isRefreshing = true; + _textBox.Text = HasDifferentValues ? "Multiple Values" : GetPath(); + _isRefreshing = false; } /// diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 72c2b3980..9a8d8b47e 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -9,6 +9,8 @@ using FlaxEditor.GUI.Drag; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.GUI; using FlaxEditor.Scripting; +using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; @@ -40,6 +42,11 @@ namespace FlaxEditor.CustomEditors.Editors private DragScripts _dragScripts; private DragHandlers _dragHandlers; + /// + /// The presenter using this control. + /// + public IPresenterOwner PresenterContext; + /// /// Gets or sets the allowed objects type (given type and all sub classes). Must be type of any subclass. /// @@ -129,6 +136,11 @@ namespace FlaxEditor.CustomEditors.Editors /// public Func CheckValid; + /// + /// Utility flag used to indicate that there are different values assigned to this reference editor and user should be informed about it. + /// + public bool DifferentValues; + /// /// Initializes a new instance of the class. /// @@ -154,7 +166,7 @@ namespace FlaxEditor.CustomEditors.Editors Value = actor; RootWindow.Focus(); Focus(); - }); + }, PresenterContext); } else { @@ -163,7 +175,7 @@ namespace FlaxEditor.CustomEditors.Editors Value = script; RootWindow.Focus(); Focus(); - }); + }, PresenterContext); } } @@ -197,7 +209,14 @@ namespace FlaxEditor.CustomEditors.Editors Render2D.DrawRectangle(frameRect, isEnabled && (IsMouseOver || IsNavFocused) ? style.BorderHighlighted : style.BorderNormal); // Check if has item selected - if (isSelected) + if (DifferentValues) + { + // Draw info + Render2D.PushClip(nameRect); + Render2D.DrawText(style.FontMedium, Type != null ? $"Multiple Values ({Utilities.Utils.GetPropertyNameUI(Type.ToString())})" : "-", nameRect, isEnabled ? style.ForegroundGrey : style.ForegroundGrey.AlphaMultiplied(0.75f), TextAlignment.Near, TextAlignment.Center); + Render2D.PopClip(); + } + else if (isSelected) { // Draw name Render2D.PushClip(nameRect); @@ -437,13 +456,13 @@ namespace FlaxEditor.CustomEditors.Editors // Ensure to have valid drag helpers (uses lazy init) if (_dragActors == null) - _dragActors = new DragActors(x => IsValid(x.Actor)); + _dragActors = new DragActors(ValidateDragActor); if (_dragActorsWithScript == null) _dragActorsWithScript = new DragActors(ValidateDragActorWithScript); if (_dragAssets == null) _dragAssets = new DragAssets(ValidateDragAsset); if (_dragScripts == null) - _dragScripts = new DragScripts(IsValid); + _dragScripts = new DragScripts(ValidateDragScript); if (_dragHandlers == null) { _dragHandlers = new DragHandlers @@ -468,6 +487,43 @@ namespace FlaxEditor.CustomEditors.Editors return DragEffect; } + private bool ValidateDragActor(ActorNode a) + { + if (!IsValid(a.Actor)) + return false; + + if (PresenterContext is PrefabWindow prefabWindow) + { + if (prefabWindow.Tree == a.TreeNode.ParentTree) + return true; + } + else if (PresenterContext is PropertiesWindow || PresenterContext == null) + { + if (a.ParentScene != null) + return true; + } + return false; + } + + private bool ValidateDragScript(Script script) + { + if (!IsValid(script)) + return false; + + if (PresenterContext is PrefabWindow prefabWindow) + { + var actorNode = prefabWindow.Graph.Root.Find(script.Actor); + if (actorNode != null) + return true; + } + else if (PresenterContext is PropertiesWindow || PresenterContext == null) + { + if (script.Actor.HasScene) + return true; + } + return false; + } + private bool ValidateDragAsset(AssetItem assetItem) { // Check if can accept assets @@ -486,7 +542,18 @@ namespace FlaxEditor.CustomEditors.Editors private bool ValidateDragActorWithScript(ActorNode node) { - return node.Actor.Scripts.Any(IsValid); + bool isCorrectContext = false; + if (PresenterContext is PrefabWindow prefabWindow) + { + if (prefabWindow.Tree == node.TreeNode.ParentTree) + isCorrectContext = true; + } + else if (PresenterContext is PropertiesWindow || PresenterContext == null) + { + if (node.ParentScene != null) + isCorrectContext = true; + } + return node.Actor.Scripts.Any(IsValid) && isCorrectContext; } /// @@ -558,6 +625,7 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentTypes) { _element = layout.Custom(); + _element.CustomControl.PresenterContext = Presenter.Owner; _element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); _element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value); } @@ -568,7 +636,9 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); - if (!HasDifferentValues) + var differentValues = HasDifferentValues; + _element.CustomControl.DifferentValues = differentValues; + if (!differentValues) { _element.CustomControl.Value = Values[0] as Object; } diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 5b0e2f742..9fa090902 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -96,6 +96,20 @@ namespace FlaxEditor.CustomEditors menu.Show(groupPanel, location); } + internal string GetLayoutCachePath(string name) + { + // Build group identifier (made of path from group titles) + var expandPath = name; + var container = this; + while (container != null && !(container is CustomEditorPresenter)) + { + if (container.ContainerControl is DropPanel dropPanel) + expandPath = dropPanel.HeaderText + "/" + expandPath; + container = container._parent; + } + return expandPath; + } + /// /// Adds new group element. /// @@ -112,14 +126,7 @@ namespace FlaxEditor.CustomEditors if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { // Build group identifier (made of path from group titles) - var expandPath = title; - var container = this; - while (container != null && !(container is CustomEditorPresenter)) - { - if (container.ContainerControl is DropPanel dropPanel) - expandPath = dropPanel.HeaderText + "/" + expandPath; - container = container._parent; - } + var expandPath = GetLayoutCachePath(title); // Caching/restoring expanded groups (non-root groups cache expanded state so invert boolean expression) if (Editor.Instance.ProjectCache.IsGroupToggled(expandPath) ^ isSubGroup) diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 904d5bc09..3c360b4b3 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -250,7 +250,7 @@ namespace FlaxEditor.CustomEditors if (objA == null && objB is string objBStr && objBStr.Length == 0) return true; - return Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals(objA, objB); + return FlaxEngine.Json.JsonSerializer.ValueEquals(objA, objB); } /// diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 795f89d55..3a11ed449 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1686,9 +1686,6 @@ 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 7aca96c4e..92941be5d 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -48,6 +48,11 @@ namespace FlaxEditor.GUI /// public bool CanEdit = true; + /// + /// Utility flag used to indicate that there are different values assigned to this reference editor and user should be informed about it. + /// + public bool DifferentValues; + /// /// Initializes a new instance of the class. /// @@ -121,7 +126,13 @@ namespace FlaxEditor.GUI if (CanEdit) Render2D.DrawSprite(style.ArrowDown, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey); - if (Validator.SelectedItem != null) + if (DifferentValues) + { + // No element selected + Render2D.FillRectangle(iconRect, style.BackgroundNormal); + Render2D.DrawText(style.FontMedium, "Multiple\nValues", iconRect, style.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + } + else if (Validator.SelectedItem != null) { // Draw item preview Validator.SelectedItem.DrawThumbnail(ref iconRect); diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index 25954abfa..510e429ae 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -30,8 +30,10 @@ namespace FlaxEditor.GUI internal bool _isMovingTangent; internal bool _movedView; internal bool _movedKeyframes; + internal bool _toggledSelection; private TangentPoint _movingTangent; private Float2 _movingSelectionStart; + private Float2 _movingSelectionStartPosLock; private Float2[] _movingSelectionOffsets; private Float2 _cmShowPos; @@ -56,12 +58,11 @@ namespace FlaxEditor.GUI internal void UpdateSelection(ref Rectangle selectionRect) { // Find controls to select - for (int i = 0; i < Children.Count; i++) + var children = _children; + for (int i = 0; i < children.Count; i++) { - if (Children[i] is KeyframePoint p) - { + if (children[i] is KeyframePoint p) p.IsSelected = p.Bounds.Intersects(ref selectionRect); - } } _editor.UpdateTangents(); } @@ -72,6 +73,7 @@ namespace FlaxEditor.GUI _isMovingSelection = true; _movedKeyframes = false; var viewRect = _editor._mainPanel.GetClientArea(); + _movingSelectionStartPosLock = location; _movingSelectionStart = PointToKeyframes(location, ref viewRect); if (_movingSelectionOffsets == null || _movingSelectionOffsets.Length != _editor._points.Count) _movingSelectionOffsets = new Float2[_editor._points.Count]; @@ -82,10 +84,17 @@ namespace FlaxEditor.GUI internal void OnMove(Float2 location) { + // Skip updating keyframes until move actual starts to be meaningful + if (Float2.Distance(ref _movingSelectionStartPosLock, ref location) < 1.5f) + return; + _movingSelectionStartPosLock = Float2.Minimum; + var viewRect = _editor._mainPanel.GetClientArea(); var locationKeyframes = PointToKeyframes(location, ref viewRect); var accessor = _editor.Accessor; var components = accessor.GetCurveComponents(); + var snapEnabled = Root.GetKey(KeyboardKeys.Control); + var snapGrid = snapEnabled ? _editor.GetGridSnap() : Float2.One; for (var i = 0; i < _editor._points.Count; i++) { var p = _editor._points[i]; @@ -122,7 +131,20 @@ namespace FlaxEditor.GUI if (isFirstSelected) { time = locationKeyframes.X + offset.X; + } + if (snapEnabled) + { + // Snap to the grid + var key = new Float2(time, value); + key = Float2.SnapToGrid(key, snapGrid); + time = key.X; + value = key.Y; + } + + // Clamp and snap time to the valid range + if (isFirstSelected) + { if (_editor.FPS.HasValue) { float fps = _editor.FPS.Value; @@ -131,8 +153,6 @@ namespace FlaxEditor.GUI time = Mathf.Clamp(time, minTime, maxTime); } - // TODO: snapping keyframes to grid when moving - _editor.SetKeyframeInternal(p.Index, time, value, p.Component); } _editor.UpdateKeyframes(); @@ -234,7 +254,11 @@ namespace FlaxEditor.GUI var k = _editor.GetKeyframe(_movingTangent.Index); var kv = _editor.GetKeyframeValue(k); var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component); - _movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value; + var tangent = PointToKeyframes(location, ref viewRect).Y - value; + if (Root.GetKey(KeyboardKeys.Control)) + tangent = Float2.SnapToGrid(new Float2(0, tangent), _editor.GetGridSnap()).Y; // Snap tangent over Y axis + tangent = tangent * _editor.ViewScale.X * 2; + _movingTangent.TangentValue = tangent; _editor.UpdateTangents(); Cursor = CursorType.SizeNS; _movedKeyframes = true; @@ -283,6 +307,7 @@ namespace FlaxEditor.GUI } // Cache data + _toggledSelection = false; _isMovingSelection = false; _isMovingTangent = false; _mousePos = location; @@ -305,13 +330,7 @@ namespace FlaxEditor.GUI { if (_leftMouseDown) { - if (Root.GetKey(KeyboardKeys.Control)) - { - // Toggle selection - keyframe.IsSelected = !keyframe.IsSelected; - _editor.UpdateTangents(); - } - else if (Root.GetKey(KeyboardKeys.Shift)) + if (Root.GetKey(KeyboardKeys.Shift)) { // Select range keyframe.IsSelected = true; @@ -335,10 +354,14 @@ namespace FlaxEditor.GUI else if (!keyframe.IsSelected) { // Select node - if (_editor.KeyframesEditorContext != null) - _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); - else - _editor.ClearSelection(); + if (!Root.GetKey(KeyboardKeys.Control)) + { + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); + else + _editor.ClearSelection(); + } + _toggledSelection = true; keyframe.IsSelected = true; _editor.UpdateTangents(); } @@ -429,6 +452,12 @@ namespace FlaxEditor.GUI else OnMoveEnd(location); } + // Toggle selection + else if (!_toggledSelection && Root.GetKey(KeyboardKeys.Control) && GetChildAt(location) is KeyframePoint keyframe) + { + keyframe.IsSelected = !keyframe.IsSelected; + _editor.UpdateTangents(); + } _isMovingSelection = false; _isMovingTangent = false; @@ -514,11 +543,11 @@ namespace FlaxEditor.GUI { if (base.OnMouseDoubleClick(location, button)) return true; - + // Add keyframe on double click var child = GetChildAt(location); - if (child is not KeyframePoint && - child is not TangentPoint && + if (child is not KeyframePoint && + child is not TangentPoint && _editor.KeyframesCount < _editor.MaxKeyframes) { var viewRect = _editor._mainPanel.GetClientArea(); @@ -545,7 +574,7 @@ namespace FlaxEditor.GUI var viewRect = _editor._mainPanel.GetClientArea(); var locationInKeyframes = PointToKeyframes(location, ref viewRect); var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect); - + // Scale relative to the curve size var scale = new Float2(delta * 0.1f); _editor._mainPanel.GetDesireClientArea(out var mainPanelArea); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index ed0885afd..9a774e519 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -163,10 +163,11 @@ namespace FlaxEditor.GUI /// public override void Draw() { + var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); - var color = Editor.ShowCollapsed ? Color.Gray : Editor.Colors[Component]; + var color = Editor.ShowCollapsed ? style.ForegroundDisabled : Editor.Colors[Component]; if (IsSelected) - color = Editor.ContainsFocus ? Color.YellowGreen : Color.Lerp(Color.Gray, Color.YellowGreen, 0.4f); + color = Editor.ContainsFocus ? style.SelectionBorder : Color.Lerp(style.ForegroundDisabled, style.SelectionBorder, 0.4f); if (IsMouseOver) color *= 1.1f; Render2D.FillRectangle(rect, color); @@ -244,14 +245,19 @@ namespace FlaxEditor.GUI set => Editor.SetKeyframeTangentInternal(Index, IsIn, Component, value); } + internal float TangentOffset => 50.0f / Editor.ViewScale.X; + /// public override void Draw() { + var style = Style.Current; + var thickness = 6.0f / Mathf.Max(Editor.ViewScale.X, 1.0f); + var size = Size; var pointPos = PointFromParent(Point.Center); - Render2D.DrawLine(Size * 0.5f, pointPos, Color.Gray); + Render2D.DrawLine(size * 0.5f, pointPos, style.ForegroundDisabled, thickness); - var rect = new Rectangle(Float2.Zero, Size); - var color = Color.MediumVioletRed; + var rect = new Rectangle(Float2.Zero, size); + var color = style.BorderSelected; if (IsMouseOver) color *= 1.1f; Render2D.FillRectangle(rect, color); @@ -289,7 +295,7 @@ namespace FlaxEditor.GUI /// /// The curve time/value axes tick steps. /// - protected float[] TickSteps = Utilities.Utils.CurveTickSteps; + protected double[] TickSteps = Utilities.Utils.CurveTickSteps; /// /// The curve contents area. @@ -442,7 +448,7 @@ namespace FlaxEditor.GUI _mainPanel = new Panel(ScrollBars.Both) { ScrollMargin = new Margin(150.0f), - AlwaysShowScrollbars = true, + AlwaysShowScrollbars = false, AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, Parent = this @@ -668,26 +674,82 @@ namespace FlaxEditor.GUI OnEditingEnd(); } + private void ShowCurve(bool selectedOnly) + { + if (_points.Count == 0) + return; + int pass = 1; + REDO: + + // Get curve bounds in Keyframes (time and value) + Float2 posMin = Float2.Maximum, posMax = Float2.Minimum; + // TODO: include bezier curve bounds calculation to handle curve outside the bounds made out of points + foreach (var point in _points) + { + if (selectedOnly && !point.IsSelected) + continue; + var pos = point.Point; + Float2.Min(ref posMin, ref pos, out posMin); + Float2.Max(ref posMax, ref pos, out posMax); + } + + // Apply margin around the area + var posMargin = (posMax - posMin) * 0.05f; + posMin -= posMargin; + posMax += posMargin; + + // Convert from Keyframes to Contents + _mainPanel.GetDesireClientArea(out var viewRect); + PointFromKeyframesToContents(ref posMin, ref viewRect); + PointFromKeyframesToContents(ref posMax, ref viewRect); + var tmp = posMin; + Float2.Min(ref posMin, ref posMax, out posMin); + Float2.Max(ref posMax, ref tmp, out posMax); + var contentsSize = posMax - posMin; + + // Convert from Contents to Main Panel + posMin = _contents.PointToParent(posMin); + posMax = _contents.PointToParent(posMax); + tmp = posMin; + Float2.Min(ref posMin, ref posMax, out posMin); + Float2.Max(ref posMax, ref tmp, out posMax); + + // Update zoom (leave unchanged when focusing a single point) + var zoomMask = EnableZoom; + if (Mathf.IsZero(posMargin.X)) + zoomMask &= ~UseMode.Horizontal; + if (Mathf.IsZero(posMargin.Y)) + zoomMask &= ~UseMode.Vertical; + ViewScale = ApplyUseModeMask(zoomMask, viewRect.Size / contentsSize, ViewScale); + + // Update scroll (attempt to center the area when it's smaller than the view) + Float2 viewOffset = -posMin; + Float2 viewSize = _mainPanel.Size; + Float2 viewSizeLeft = viewSize - Float2.Clamp(posMax - posMin, Float2.Zero, viewSize); + viewOffset += viewSizeLeft * 0.5f; + viewOffset = ApplyUseModeMask(EnablePanning, viewOffset, _mainPanel.ViewOffset); + _mainPanel.ViewOffset = viewOffset; + + // Do it multiple times so the view offset can be properly calculate once the view scale gets changes + if (pass++ <= 2) + goto REDO; + + UpdateKeyframes(); + } + + /// + /// Focuses the view on the selected keyframes. + /// + public void FocusSelection() + { + // Fallback to showing whole curve if nothing is selected + ShowCurve(SelectionCount != 0); + } + /// public override void ShowWholeCurve() { - _mainPanel.GetDesireClientArea(out var mainPanelArea); - ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale); - Float2 minPos = Float2.Maximum; - foreach (var point in _points) - { - var pos = point.PointToParent(point.Location); - Float2.Min(ref minPos, ref pos, out minPos); - } - var minPosPoint = _contents.PointToParent(ref minPos); - var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0); - scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll); - if (_mainPanel.HScrollBar != null) - _mainPanel.HScrollBar.TargetValue = scroll.X; - if (_mainPanel.VScrollBar != null) - _mainPanel.VScrollBar.TargetValue = scroll.Y; - - UpdateKeyframes(); + ShowCurve(false); } /// @@ -766,10 +828,7 @@ namespace FlaxEditor.GUI point = _contents.PointFromParent(point); // Contents -> Keyframes - return new Float2( - (point.X + _contents.Location.X) / UnitsPerSecond, - (point.Y + _contents.Location.Y - curveContentAreaBounds.Height) / -UnitsPerSecond - ); + return PointFromContentsToKeyframes(ref point, ref curveContentAreaBounds); } /// @@ -781,10 +840,7 @@ namespace FlaxEditor.GUI protected Float2 PointFromKeyframes(Float2 point, ref Rectangle curveContentAreaBounds) { // Keyframes -> Contents - point = new Float2( - point.X * UnitsPerSecond - _contents.Location.X, - point.Y * -UnitsPerSecond + curveContentAreaBounds.Height - _contents.Location.Y - ); + PointFromKeyframesToContents(ref point, ref curveContentAreaBounds); // Contents -> Main Panel point = _contents.PointToParent(point); @@ -793,11 +849,27 @@ namespace FlaxEditor.GUI return _mainPanel.PointToParent(point); } + internal Float2 PointFromContentsToKeyframes(ref Float2 point, ref Rectangle curveContentAreaBounds) + { + return new Float2( + (point.X + _contents.Location.X) / UnitsPerSecond, + (point.Y + _contents.Location.Y - curveContentAreaBounds.Height) / -UnitsPerSecond + ); + } + + internal void PointFromKeyframesToContents(ref Float2 point, ref Rectangle curveContentAreaBounds) + { + point = new Float2( + point.X * UnitsPerSecond - _contents.Location.X, + point.Y * -UnitsPerSecond + curveContentAreaBounds.Height - _contents.Location.Y + ); + } + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) { - Utilities.Utils.DrawCurveTicks((float tick, float strength) => + Utilities.Utils.DrawCurveTicks((decimal tick, double step, float strength) => { - var p = PointFromKeyframes(axis * tick, ref viewRect); + var p = PointFromKeyframes(axis * (float)tick, ref viewRect); // Draw line var lineRect = new Rectangle @@ -820,6 +892,24 @@ namespace FlaxEditor.GUI }, TickSteps, ref _tickStrengths, min, max, pixelRange); } + private void SetupGrid(out Float2 min, out Float2 max, out Float2 pixelRange) + { + var viewRect = _mainPanel.GetClientArea(); + var upperLeft = PointToKeyframes(viewRect.Location, ref viewRect); + var bottomRight = PointToKeyframes(viewRect.Size, ref viewRect); + + min = Float2.Min(upperLeft, bottomRight); + max = Float2.Max(upperLeft, bottomRight); + pixelRange = (max - min) * ViewScale * UnitsPerSecond; + } + + private Float2 GetGridSnap() + { + SetupGrid(out var min, out var max, out var pixelRange); + return new Float2(Utilities.Utils.GetCurveGridSnap(TickSteps, ref _tickStrengths, min.X, max.X, pixelRange.X), + Utilities.Utils.GetCurveGridSnap(TickSteps, ref _tickStrengths, min.Y, max.Y, pixelRange.Y)); + } + /// /// Draws the curve. /// @@ -849,12 +939,7 @@ namespace FlaxEditor.GUI // Draw time and values axes if (ShowAxes != UseMode.Off) { - var upperLeft = PointToKeyframes(viewRect.Location, ref viewRect); - var bottomRight = PointToKeyframes(viewRect.Size, ref viewRect); - - var min = Float2.Min(upperLeft, bottomRight); - var max = Float2.Max(upperLeft, bottomRight); - var pixelRange = (max - min) * ViewScale * UnitsPerSecond; + SetupGrid(out var min, out var max, out var pixelRange); Render2D.PushClip(ref viewRect); @@ -939,7 +1024,7 @@ namespace FlaxEditor.GUI } else if (options.FocusSelection.Process(this)) { - ShowWholeCurve(); + FocusSelection(); return true; } @@ -2200,7 +2285,7 @@ namespace FlaxEditor.GUI var tangent = t.TangentValue; var direction = t.IsIn ? -1.0f : 1.0f; - var offset = 30.0f; + var offset = t.TangentOffset; var location = GetKeyframePoint(ref k, selectedComponent); t.Size = KeyframesSize / ViewScale; t.Location = new Float2 @@ -2227,6 +2312,18 @@ namespace FlaxEditor.GUI } } + /// + protected override void SetScaleInternal(ref Float2 scale) + { + base.SetScaleInternal(ref scale); + + if (!_showCollapsed) + { + // Refresh keyframes when zooming (their size depends on the scale) + UpdateKeyframes(); + } + } + /// protected override void OnShowContextMenu(ContextMenu.ContextMenu cm, int selectionCount) { diff --git a/Source/Editor/GUI/Popups/ActorSearchPopup.cs b/Source/Editor/GUI/Popups/ActorSearchPopup.cs index 2c327b685..7be17db98 100644 --- a/Source/Editor/GUI/Popups/ActorSearchPopup.cs +++ b/Source/Editor/GUI/Popups/ActorSearchPopup.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; @@ -55,18 +57,26 @@ namespace FlaxEditor.GUI private IsValidDelegate _isValid; private Action _selected; - private ActorSearchPopup(IsValidDelegate isValid, Action selected) + private ActorSearchPopup(IsValidDelegate isValid, Action selected, CustomEditors.IPresenterOwner context) { _isValid = isValid; _selected = selected; ItemClicked += OnItemClicked; - // TODO: use async thread to search scenes - for (int i = 0; i < Level.ScenesCount; i++) + if (context is PropertiesWindow propertiesWindow || context == null) { - Find(Level.GetScene(i)); + // TODO: use async thread to search scenes + for (int i = 0; i < Level.ScenesCount; i++) + { + Find(Level.GetScene(i)); + } } + else if (context is PrefabWindow prefabWindow) + { + Find(prefabWindow.Graph.MainActor); + } + SortItems(); } @@ -98,10 +108,11 @@ namespace FlaxEditor.GUI /// The show target location. /// Event called to check if a given actor item is valid to be used. /// Event called on actor item pick. + /// The presenter owner context (i.e. PrefabWindow, PropertiesWindow). /// The dialog. - public static ActorSearchPopup Show(Control showTarget, Float2 showTargetLocation, IsValidDelegate isValid, Action selected) + public static ActorSearchPopup Show(Control showTarget, Float2 showTargetLocation, IsValidDelegate isValid, Action selected, CustomEditors.IPresenterOwner context) { - var popup = new ActorSearchPopup(isValid, selected); + var popup = new ActorSearchPopup(isValid, selected, context); popup.Show(showTarget, showTargetLocation); return popup; } diff --git a/Source/Editor/GUI/Popups/ScriptSearchPopup.cs b/Source/Editor/GUI/Popups/ScriptSearchPopup.cs index 93c860275..a6414bb86 100644 --- a/Source/Editor/GUI/Popups/ScriptSearchPopup.cs +++ b/Source/Editor/GUI/Popups/ScriptSearchPopup.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; @@ -66,18 +68,26 @@ namespace FlaxEditor.GUI private IsValidDelegate _isValid; private Action