diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index 139bf2416..a8ebb4c62 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -9,7 +9,7 @@ jobs: # Editor editor-mac: - name: Editor (Mac, Development x64) + name: Editor (Mac, Development ARM64) runs-on: "macos-latest" steps: - name: Checkout repo @@ -30,11 +30,11 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor + ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor # Game game-mac: - name: Game (Mac, Release x64) + name: Game (Mac, Release ARM64) runs-on: "macos-latest" steps: - name: Checkout repo @@ -55,4 +55,4 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Release -buildtargets=FlaxGame + ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 70f275eac..d83b40363 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -166,7 +166,7 @@ jobs: dotnet workload --info - name: Build run: | - ./PackageEditor.command -arch=x64 -platform=Mac -deployOutput=Output + ./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output - name: Upload uses: actions/upload-artifact@v3 with: @@ -194,7 +194,7 @@ jobs: dotnet workload --info - name: Build run: | - ./PackagePlatforms.command -arch=x64 -platform=Mac -deployOutput=Output + ./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output - name: Upload uses: actions/upload-artifact@v3 with: diff --git a/Source/Editor/Content/Proxy/PrefabProxy.cs b/Source/Editor/Content/Proxy/PrefabProxy.cs index 18860995e..c0c4e5c88 100644 --- a/Source/Editor/Content/Proxy/PrefabProxy.cs +++ b/Source/Editor/Content/Proxy/PrefabProxy.cs @@ -89,7 +89,7 @@ namespace FlaxEditor.Content // Cleanup it after usage Object.Destroy(actor, 20.0f); } - else if (actor.Scene != null) + else if (actor.HasScene) { // Create prefab with identity transform so the actor instance on a level will have it customized resetTransform = true; diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index d55ad01e3..5ad88db24 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -203,16 +203,16 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) switch (defaultOrienation) { case AndroidPlatformSettings::ScreenOrientation::Portrait: - orientation = String("portrait"); + orientation = String("userPortrait"); break; - case AndroidPlatformSettings::ScreenOrientation::PortraitReverse: - orientation = String("reversePortrait"); + case AndroidPlatformSettings::ScreenOrientation::Landscape: + orientation = String("userLandscape"); break; - case AndroidPlatformSettings::ScreenOrientation::LandscapeRight: - orientation = String("landscape"); + case AndroidPlatformSettings::ScreenOrientation::SensorPortrait: + orientation = String("sensorPortrait"); break; - case AndroidPlatformSettings::ScreenOrientation::LandscapeLeft: - orientation = String("reverseLandscape"); + case AndroidPlatformSettings::ScreenOrientation::SensorLandscape: + orientation = String("sensorLandscape"); break; case AndroidPlatformSettings::ScreenOrientation::AutoRotation: orientation = String("fullSensor"); @@ -266,9 +266,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) } } + String versionCode = platformSettings->VersionCode; + if (versionCode.IsEmpty()) + { + LOG(Error, "AndroidSettings: Invalid version code"); + return true; + } + + String minimumSdk = platformSettings->MinimumAPILevel; + if (minimumSdk.IsEmpty()) + { + LOG(Error, "AndroidSettings: Invalid minimum API level"); + return true; + } + + String targetSdk = platformSettings->TargetAPILevel; + if (targetSdk.IsEmpty()) + { + LOG(Error, "AndroidSettings: Invalid target API level"); + return true; + } + // Format project template files const String buildGradlePath = data.OriginalOutputPath / TEXT("app/build.gradle"); EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${PackageName}"), packageName); + EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${VersionCode}"), versionCode); + EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${MinimumSdk}"), minimumSdk); + EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${TargetSdk}"), targetSdk); EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${ProjectVersion}"), projectVersion); EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${PackageAbi}"), abi); const String manifestPath = data.OriginalOutputPath / TEXT("app/src/main/AndroidManifest.xml"); diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 1561e6245..bdbf22ee9 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -291,7 +291,7 @@ 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; + bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.HasScene; for (int i = 0; i < editor.ChildrenEditors.Count; i++) { var childEditor = editor.ChildrenEditors[i]; diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs index 194bc23ea..fbb817768 100644 --- a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs @@ -45,6 +45,12 @@ public class ModelPrefabEditor : GenericEditor break; _prefabId = prefabObject.PrefabID; } + else + { + // The model was removed earlier + _prefabId = Guid.Empty; + break; + } } var button = layout.Button("Reimport", "Reimports the source asset as prefab."); diff --git a/Source/Editor/CustomEditors/Editors/StringEditor.cs b/Source/Editor/CustomEditors/Editors/StringEditor.cs index 9e38fd0ac..9f6b4ce32 100644 --- a/Source/Editor/CustomEditors/Editors/StringEditor.cs +++ b/Source/Editor/CustomEditors/Editors/StringEditor.cs @@ -13,6 +13,9 @@ namespace FlaxEditor.CustomEditors.Editors public sealed class StringEditor : CustomEditor { private TextBoxElement _element; + private string _watermarkText; + private Color _watermarkColor; + private Color _defaultWatermarkColor; /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -21,15 +24,26 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { bool isMultiLine = false; + _watermarkText = string.Empty; var attributes = Values.GetAttributes(); var multiLine = attributes?.FirstOrDefault(x => x is MultilineTextAttribute); + var watermarkAttribute = attributes?.FirstOrDefault(x => x is WatermarkAttribute); if (multiLine != null) { isMultiLine = true; } _element = layout.TextBox(isMultiLine); + _defaultWatermarkColor = _element.TextBox.WatermarkTextColor; + if (watermarkAttribute is WatermarkAttribute watermark) + { + _watermarkText = watermark.WatermarkText; + var watermarkColor = watermark.WatermarkColor > 0 ? Color.FromRGBA(watermark.WatermarkColor) : FlaxEngine.GUI.Style.Current.ForegroundDisabled; + _watermarkColor = watermarkColor; + _element.TextBox.WatermarkText = watermark.WatermarkText; + _element.TextBox.WatermarkTextColor = watermarkColor; + } _element.TextBox.EditEnd += () => SetValue(_element.Text); } @@ -41,12 +55,14 @@ namespace FlaxEditor.CustomEditors.Editors if (HasDifferentValues) { _element.TextBox.Text = string.Empty; + _element.TextBox.WatermarkTextColor = _defaultWatermarkColor; _element.TextBox.WatermarkText = "Different values"; } else { _element.TextBox.Text = (string)Values[0]; - _element.TextBox.WatermarkText = string.Empty; + _element.TextBox.WatermarkTextColor = _watermarkColor; + _element.TextBox.WatermarkText = _watermarkText; } } } diff --git a/Source/Editor/GUI/Drag/DragControlType.cs b/Source/Editor/GUI/Drag/DragControlType.cs new file mode 100644 index 000000000..d1286053c --- /dev/null +++ b/Source/Editor/GUI/Drag/DragControlType.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using FlaxEditor.SceneGraph; +using FlaxEditor.Scripting; +using FlaxEngine; +using FlaxEngine.GUI; +using FlaxEngine.Utilities; + +namespace FlaxEditor.GUI.Drag; + +/// +/// Control type drag handler. +/// +public sealed class DragControlType : DragActorType +{ + /// + /// Initializes a new instance of the class. + /// + /// The validation function + public DragControlType(Func validateFunction) + : base(validateFunction) + { + } +} + +/// +/// Helper class for handling control type drag and drop (for spawning). +/// +/// +/// +public class DragControlType : DragHelper where U : DragEventArgs +{ + /// + /// The default prefix for drag data used for actor type drag and drop. + /// + public const string DragPrefix = "CTYPE!?"; + + /// + /// Creates a new DragHelper + /// + /// The validation function + public DragControlType(Func validateFunction) + : base(validateFunction) + { + } + + /// + public override DragData ToDragData(ScriptType item) => GetDragData(item); + + /// + public override DragData ToDragData(IEnumerable items) + { + throw new NotImplementedException(); + } + + /// + /// Gets the drag data. + /// + /// The control type. + /// The data + public static DragData GetDragData(Type item) + { + if (item == null) + throw new ArgumentNullException(); + return new DragDataText(DragPrefix + item.FullName); + } + + /// + /// Gets the drag data. + /// + /// The control type. + /// The data + public static DragData GetDragData(ScriptType item) + { + if (item == ScriptType.Null) + throw new ArgumentNullException(); + return new DragDataText(DragPrefix + item.TypeName); + } + + /// + /// Tries to parse the drag data to extract collection. + /// + /// The data. + /// Gathered objects or empty array if cannot get any valid. + public override IEnumerable FromDragData(DragData data) + { + if (data is DragDataText dataText) + { + if (dataText.Text.StartsWith(DragPrefix)) + { + // Remove prefix and parse spitted names + var types = dataText.Text.Remove(0, DragPrefix.Length).Split('\n'); + var results = new List(types.Length); + for (int i = 0; i < types.Length; i++) + { + // Find type + var obj = TypeUtils.GetType(types[i]); + if (obj) + results.Add(obj); + } + return results; + } + } + return Utils.GetEmptyArray(); + } +} + diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 36331d6d2..d5782c10f 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -38,7 +38,6 @@ namespace FlaxEditor.GUI.Tree private bool _isMouseDown; private float _mouseDownTime; private Float2 _mouseDownPos; - private Color _cachedTextColor; private DragItemPositioning _dragOverMode; private bool _isDragOverHeader; @@ -91,6 +90,11 @@ namespace FlaxEditor.GUI.Tree } } + /// + /// Gets a value indicating whether the node is collapsed in the hierarchy (is collapsed or any of its parents is collapsed). + /// + public bool IsCollapsedInHierarchy => IsCollapsed || (Parent is TreeNode parentNode && parentNode.IsCollapsedInHierarchy); + /// /// Gets or sets the text margin. /// @@ -604,9 +608,6 @@ namespace FlaxEditor.GUI.Tree /// public override void Update(float deltaTime) { - // Cache text color - _cachedTextColor = CacheTextColor(); - // Drop/down animation if (_animationProgress < 1.0f) { @@ -676,7 +677,8 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Color textColor = CacheTextColor(); + Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) @@ -712,6 +714,72 @@ namespace FlaxEditor.GUI.Tree } } + /// + protected override void DrawChildren() + { + // Draw all visible child controls + var children = _children; + if (children.Count == 0) + return; + + if (CullChildren) + { + Render2D.PeekClip(out var globalClipping); + Render2D.PeekTransform(out var globalTransform); + + // Try to estimate the rough location of the first node, assuming the node height is constant + var firstChildGlobalRect = GetChildGlobalRectangle(children[0], ref globalTransform); + var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / firstChildGlobalRect.Height) + 1, 0, children.Count - 1); + if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top || !children[firstVisibleChild].Visible) + { + // Estimate overshoot, either it's partially visible or hidden in the tree + for (; firstVisibleChild > 0; firstVisibleChild--) + { + var child = children[firstVisibleChild]; + if (!child.Visible) + continue; + + if (GetChildGlobalRectangle(child, ref globalTransform).Top < globalClipping.Top) + break; + } + } + + for (int i = firstVisibleChild; i < children.Count; i++) + { + var child = children[i]; + if (!child.Visible) + continue; + + var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform); + if (!globalClipping.Intersects(ref childGlobalRect)) + break; + + Render2D.PushTransform(ref child._cachedTransform); + child.Draw(); + Render2D.PopTransform(); + } + + static Rectangle GetChildGlobalRectangle(Control control, ref Matrix3x3 globalTransform) + { + Matrix3x3.Multiply(ref control._cachedTransform, ref globalTransform, out var globalChildTransform); + return new Rectangle(globalChildTransform.M31, globalChildTransform.M32, control.Width * globalChildTransform.M11, control.Height * globalChildTransform.M22); + } + } + else + { + for (int i = 0; i < children.Count; i++) + { + var child = children[i]; + if (child.Visible) + { + Render2D.PushTransform(ref child._cachedTransform); + child.Draw(); + Render2D.PopTransform(); + } + } + } + } + /// public override bool OnMouseDown(Float2 location, MouseButton button) { @@ -996,7 +1064,7 @@ namespace FlaxEditor.GUI.Tree // Expand node if mouse goes over arrow if (ArrowRect.Contains(location) && HasAnyVisibleChild) Expand(true); - + if (!_isDragOverHeader) result = OnDragEnterHeader(data); else @@ -1104,7 +1172,6 @@ namespace FlaxEditor.GUI.Tree { // TODO: perform layout for any non-TreeNode controls _cachedHeight = _headerHeight; - _cachedTextColor = CacheTextColor(); Size = new Float2(width, _headerHeight); } @@ -1154,7 +1221,6 @@ namespace FlaxEditor.GUI.Tree } _cachedHeight = height; - _cachedTextColor = CacheTextColor(); Height = Mathf.Max(_headerHeight, y); } diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs index e0e464c04..e0ae29299 100644 --- a/Source/Editor/Modules/PrefabsModule.cs +++ b/Source/Editor/Modules/PrefabsModule.cs @@ -227,7 +227,7 @@ namespace FlaxEditor.Modules // 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) + if (instance.IsPrefabRoot && instance.HasScene) instance.LocalTransform = prefab.GetDefaultInstance().Transform; // Call backend diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index bf505394c..9bd5385fd 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -640,7 +640,7 @@ namespace FlaxEditor.Modules cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow); cm.AddSeparator(); cm.AddButton("Save window layout", Editor.Windows.SaveLayout); - _menuWindowApplyWindowLayout = cm.AddChildMenu("Apply window layout"); + _menuWindowApplyWindowLayout = cm.AddChildMenu("Window layouts"); cm.AddButton("Restore default layout", Editor.Windows.LoadDefaultLayout); // Help diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 6056cf68b..3bd317749 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -29,6 +29,7 @@ namespace FlaxEditor.SceneGraph.GUI private DragScripts _dragScripts; private DragAssets _dragAssets; private DragActorType _dragActorType; + private DragControlType _dragControlType; private DragScriptItems _dragScriptItems; private DragHandlers _dragHandlers; private List _highlights; @@ -68,7 +69,7 @@ namespace FlaxEditor.SceneGraph.GUI Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0; // Pick the correct id when inside a prefab window. - var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID; + var id = actor.HasPrefabLink && !actor.HasScene ? actor.PrefabObjectID : actor.ID; if (Editor.Instance.ProjectCache.IsExpandedActor(ref id)) { Expand(true); @@ -97,7 +98,7 @@ namespace FlaxEditor.SceneGraph.GUI parentTreeNode.IsLayoutLocked = false; // Skip UI update if node won't be in a view - if (parentTreeNode.IsCollapsed) + if (parentTreeNode.IsCollapsedInHierarchy) { UnlockChildrenRecursive(); } @@ -291,7 +292,7 @@ namespace FlaxEditor.SceneGraph.GUI return Style.Current.ForegroundGrey; } - if (actor.Scene != null && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic) + if (actor.HasScene && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic) { // Static return color * 0.85f; @@ -366,7 +367,7 @@ namespace FlaxEditor.SceneGraph.GUI if (!IsLayoutLocked && actor) { // Pick the correct id when inside a prefab window. - var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID; + var id = actor.HasPrefabLink && !actor.HasScene ? actor.PrefabObjectID : actor.ID; Editor.Instance.ProjectCache.SetExpandedActor(ref id, IsExpanded); } } @@ -439,6 +440,17 @@ namespace FlaxEditor.SceneGraph.GUI } if (_dragActorType.OnDragEnter(data)) return _dragActorType.Effect; + + // Check if drag control type + if (_dragControlType == null) + { + _dragControlType = new DragControlType(ValidateDragControlType); + _dragHandlers.Add(_dragControlType); + } + if (_dragControlType.OnDragEnter(data)) + return _dragControlType.Effect; + + // Check if drag script item if (_dragScriptItems == null) { _dragScriptItems = new DragScriptItems(ValidateDragScriptItem); @@ -572,10 +584,33 @@ namespace FlaxEditor.SceneGraph.GUI Editor.LogWarning("Failed to spawn actor of type " + item.TypeName); continue; } - actor.StaticFlags = Actor.StaticFlags; + actor.StaticFlags = newParent.StaticFlags; actor.Name = item.Name; - actor.Transform = Actor.Transform; - ActorNode.Root.Spawn(actor, Actor); + ActorNode.Root.Spawn(actor, newParent); + actor.OrderInParent = newOrder; + } + result = DragDropEffect.Move; + } + // Drag control type + else if (_dragControlType != null && _dragControlType.HasValidDrag) + { + for (int i = 0; i < _dragControlType.Objects.Count; i++) + { + var item = _dragControlType.Objects[i]; + var control = item.CreateInstance() as Control; + if (control == null) + { + Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName); + continue; + } + var uiControl = new UIControl + { + Control = control, + StaticFlags = newParent.StaticFlags, + Name = item.Name, + }; + ActorNode.Root.Spawn(uiControl, newParent); + uiControl.OrderInParent = newOrder; } result = DragDropEffect.Move; } @@ -640,8 +675,8 @@ namespace FlaxEditor.SceneGraph.GUI private bool ValidateDragScript(Script script) { // Reject dragging scripts not linked to scene (eg. from prefab) or in the opposite way - var thisHasScene = Actor.Scene != null; - var otherHasScene = script.Scene != null; + var thisHasScene = Actor.HasScene; + var otherHasScene = script.HasScene; if (thisHasScene != otherHasScene) return false; @@ -656,7 +691,12 @@ namespace FlaxEditor.SceneGraph.GUI private static bool ValidateDragActorType(ScriptType actorType) { - return true; + return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType); + } + + private static bool ValidateDragControlType(ScriptType controlType) + { + return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType); } private static bool ValidateDragScriptItem(ScriptItem script) @@ -704,6 +744,7 @@ namespace FlaxEditor.SceneGraph.GUI _dragScripts = null; _dragAssets = null; _dragActorType = null; + _dragControlType = null; _dragScriptItems = null; _dragHandlers?.Clear(); _dragHandlers = null; diff --git a/Source/Editor/SceneGraph/LocalSceneGraph.cs b/Source/Editor/SceneGraph/LocalSceneGraph.cs index f0f99a6ed..3c92c9bc8 100644 --- a/Source/Editor/SceneGraph/LocalSceneGraph.cs +++ b/Source/Editor/SceneGraph/LocalSceneGraph.cs @@ -91,7 +91,7 @@ namespace FlaxEditor.SceneGraph private void OnActorSpawned(Actor actor) { // Skip actors from game - if (actor.Scene != null) + if (actor.HasScene) return; // Check if it has parent diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 80419ba90..c3bf7d0a5 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -186,6 +186,18 @@ namespace FlaxEditor.Surface.Archetypes base.OnEndMouseCapture(); } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + switch (key) + { + case KeyboardKeys.Delete: + _editor.SetAsset(_index, Guid.Empty); + return true; + } + return base.OnKeyDown(key); + } } /// diff --git a/Source/Editor/Surface/Undo/AddRemoveNodeAction.cs b/Source/Editor/Surface/Undo/AddRemoveNodeAction.cs index 300ca700f..2c72f041f 100644 --- a/Source/Editor/Surface/Undo/AddRemoveNodeAction.cs +++ b/Source/Editor/Surface/Undo/AddRemoveNodeAction.cs @@ -66,6 +66,8 @@ namespace FlaxEditor.Surface.Undo // Initialize if (node.Values != null && node.Values.Length == _nodeValues.Length) Array.Copy(_nodeValues, node.Values, _nodeValues.Length); + else if (_nodeValues != null && (node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0) + node.Values = (object[])_nodeValues.Clone(); else if (_nodeValues != null && _nodeValues.Length != 0) throw new InvalidOperationException("Invalid node values."); node.Location = _nodeLocation; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 21e037fbc..d849c9d79 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -65,6 +65,7 @@ namespace FlaxEditor.Windows.Assets private PrefabWindow _window; private DragAssets _dragAssets; private DragActorType _dragActorType; + private DragControlType _dragControlType; private DragScriptItems _dragScriptItems; private DragHandlers _dragHandlers; @@ -83,7 +84,12 @@ namespace FlaxEditor.Windows.Assets private static bool ValidateDragActorType(ScriptType actorType) { - return true; + return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType); + } + + private static bool ValidateDragControlType(ScriptType controlType) + { + return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType); } private static bool ValidateDragScriptItem(ScriptItem script) @@ -113,6 +119,13 @@ namespace FlaxEditor.Windows.Assets } if (_dragActorType.OnDragEnter(data)) return _dragActorType.Effect; + if (_dragControlType == null) + { + _dragControlType = new DragControlType(ValidateDragControlType); + _dragHandlers.Add(_dragControlType); + } + if (_dragControlType.OnDragEnter(data)) + return _dragControlType.Effect; if (_dragScriptItems == null) { _dragScriptItems = new DragScriptItems(ValidateDragScriptItem); @@ -176,6 +189,27 @@ namespace FlaxEditor.Windows.Assets } result = DragDropEffect.Move; } + // Drag control type + else if (_dragControlType != null && _dragControlType.HasValidDrag) + { + for (int i = 0; i < _dragControlType.Objects.Count; i++) + { + var item = _dragControlType.Objects[i]; + var control = item.CreateInstance() as Control; + if (control == null) + { + Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName); + continue; + } + var uiControl = new UIControl + { + Control = control, + Name = item.Name, + }; + _window.Spawn(uiControl); + } + result = DragDropEffect.Move; + } // Drag script item else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag) { @@ -207,6 +241,7 @@ namespace FlaxEditor.Windows.Assets _window = null; _dragAssets = null; _dragActorType = null; + _dragControlType = null; _dragScriptItems = null; _dragHandlers?.Clear(); _dragHandlers = null; @@ -450,6 +485,7 @@ namespace FlaxEditor.Windows.Assets // Create undo action var action = new CustomDeleteActorsAction(new List(1) { actorNode }, true); Undo.AddAction(action); + Focus(); Select(actorNode); } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index a7e4e0e44..fe68896a8 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -30,6 +30,7 @@ namespace FlaxEditor.Windows private DragAssets _dragAssets; private DragActorType _dragActorType; + private DragControlType _dragControlType; private DragScriptItems _dragScriptItems; private DragHandlers _dragHandlers; @@ -275,7 +276,12 @@ namespace FlaxEditor.Windows private static bool ValidateDragActorType(ScriptType actorType) { - return true; + return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType); + } + + private static bool ValidateDragControlType(ScriptType controlType) + { + return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType); } private static bool ValidateDragScriptItem(ScriptItem script) @@ -390,6 +396,13 @@ namespace FlaxEditor.Windows } if (_dragActorType.OnDragEnter(data) && result == DragDropEffect.None) return _dragActorType.Effect; + if (_dragControlType == null) + { + _dragControlType = new DragControlType(ValidateDragControlType); + _dragHandlers.Add(_dragControlType); + } + if (_dragControlType.OnDragEnter(data) && result == DragDropEffect.None) + return _dragControlType.Effect; if (_dragScriptItems == null) { _dragScriptItems = new DragScriptItems(ValidateDragScriptItem); @@ -462,6 +475,28 @@ namespace FlaxEditor.Windows } result = DragDropEffect.Move; } + // Drag control type + else if (_dragControlType != null && _dragControlType.HasValidDrag) + { + for (int i = 0; i < _dragControlType.Objects.Count; i++) + { + var item = _dragControlType.Objects[i]; + var control = item.CreateInstance() as Control; + if (control == null) + { + Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName); + continue; + } + var uiControl = new UIControl + { + Control = control, + Name = item.Name, + }; + Level.SpawnActor(uiControl); + Editor.Scene.MarkSceneEdited(uiControl.Scene); + } + result = DragDropEffect.Move; + } // Drag script item else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag) { @@ -495,6 +530,7 @@ namespace FlaxEditor.Windows { _dragAssets = null; _dragActorType = null; + _dragControlType = null; _dragScriptItems = null; _dragHandlers?.Clear(); _dragHandlers = null; diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index ab9c218c5..7c15d29c9 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -191,6 +191,52 @@ namespace FlaxEditor.Windows CreateGroupWithList(_actorGroups, "GUI"); CreateGroupWithList(_actorGroups, "Other"); + // Add control types to tabs + foreach (var controlType in Editor.Instance.CodeEditing.Controls.Get()) + { + if (controlType.IsAbstract) + continue; + _groupSearch.AddChild(CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType)); + ActorToolboxAttribute attribute = null; + foreach (var e in controlType.GetAttributes(false)) + { + if (e is ActorToolboxAttribute actorToolboxAttribute) + { + attribute = actorToolboxAttribute; + break; + } + } + if (attribute == null) + continue; + var groupName = attribute.Group.Trim(); + + // Check if tab already exists and add it to the tab + var actorTabExists = false; + foreach (var child in _actorGroups.Children) + { + if (child is Tab tab) + { + if (string.Equals(tab.Text, groupName, StringComparison.OrdinalIgnoreCase)) + { + var tree = tab.GetChild().GetChild(); + if (tree != null) + { + tree.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType) : CreateControlItem(attribute.Name, controlType)); + tree.SortChildren(); + } + actorTabExists = true; + break; + } + } + } + if (actorTabExists) + continue; + + var group = CreateGroupWithList(_actorGroups, groupName); + group.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType) : CreateControlItem(attribute.Name, controlType)); + group.SortChildren(); + } + // Add other actor types to respective tab based on attribute foreach (var actorType in Editor.CodeEditing.Actors.Get()) { @@ -304,6 +350,11 @@ namespace FlaxEditor.Windows return new ScriptTypeItem(name, type, GUI.Drag.DragActorType.GetDragData(type)); } + private Item CreateControlItem(string name, ScriptType type) + { + return new ScriptTypeItem(name, type, GUI.Drag.DragControlType.GetDragData(type)); + } + private ContainerControl CreateGroupWithList(Tabs parentTabs, string title, float topOffset = 0) { var tab = parentTabs.AddTab(new Tab(title)); diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 0a500eaf4..20063ca0f 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -286,14 +286,14 @@ void Engine::OnLateFixedUpdate() { PROFILE_CPU_NAMED("Late Fixed Update"); + // Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step) + Physics::CollectResults(); + // Call event LateFixedUpdate(); // Update services EngineService::OnLateFixedUpdate(); - - // Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step) - Physics::CollectResults(); } void Engine::OnUpdate() diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index d31cb9a06..16a19030e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -33,6 +33,14 @@ #define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS #define VULKAN_USE_DEBUG_DATA (GPU_ENABLE_DIAGNOSTICS && COMPILE_WITH_DEV_ENV) +#ifndef VULKAN_USE_VALIDATION_CACHE +#ifdef VK_EXT_validation_cache +#define VULKAN_USE_VALIDATION_CACHE VK_EXT_validation_cache +#else +#define VULKAN_USE_VALIDATION_CACHE 0 +#endif +#endif + #ifndef VULKAN_USE_QUERIES #define VULKAN_USE_QUERIES 1 #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp index 9b590cb16..c8ee2ccd9 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp @@ -39,7 +39,7 @@ static const char* GInstanceExtensions[] = VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, #endif -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, #endif #if defined(VK_KHR_display) && 0 @@ -57,7 +57,7 @@ static const char* GDeviceExtensions[] = #if VK_KHR_maintenance1 VK_KHR_MAINTENANCE1_EXTENSION_NAME, #endif -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, #endif #if VK_KHR_sampler_mirror_clamp_to_edge @@ -582,7 +582,7 @@ void GPUDeviceVulkan::ParseOptionalDeviceExtensions(const Array& de OptionalDeviceExtensions.HasKHRMaintenance2 = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_MAINTENANCE2_EXTENSION_NAME); #endif OptionalDeviceExtensions.HasMirrorClampToEdge = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME); -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE OptionalDeviceExtensions.HasEXTValidationCache = RenderToolsVulkan::HasExtension(deviceExtensions, VK_EXT_VALIDATION_CACHE_EXTENSION_NAME); #endif } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 565037e47..febeb22a1 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1439,7 +1439,7 @@ bool GPUDeviceVulkan::SavePipelineCache() return File::WriteAllBytes(path, data); } -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE void GetValidationCachePath(String& path) { @@ -1900,7 +1900,7 @@ bool GPUDeviceVulkan::Init() const VkResult result = vkCreatePipelineCache(Device, &pipelineCacheCreateInfo, nullptr, &PipelineCache); LOG_VULKAN_RESULT(result); } -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE if (OptionalDeviceExtensions.HasEXTValidationCache && vkCreateValidationCacheEXT && vkDestroyValidationCacheEXT) { Array data; @@ -1915,16 +1915,16 @@ bool GPUDeviceVulkan::Init() int32* dataPtr = (int32*)data.Get(); if (*dataPtr > 0) { - dataPtr++; - const int32 version = *dataPtr++; - const int32 versionExpected = VK_PIPELINE_CACHE_HEADER_VERSION_ONE; - if (version == versionExpected) + const int32 cacheSize = *dataPtr++; + const int32 cacheVersion = *dataPtr++; + const int32 cacheVersionExpected = VK_PIPELINE_CACHE_HEADER_VERSION_ONE; + if (cacheVersion == cacheVersionExpected) { dataPtr += VK_UUID_SIZE / sizeof(int32); } else { - LOG(Warning, "Bad validation cache file, version: {0}, expected: {1}", version, versionExpected); + LOG(Warning, "Bad validation cache file, version: {0}, expected: {1}", cacheVersion, cacheVersionExpected); data.Clear(); } } @@ -2003,7 +2003,7 @@ void GPUDeviceVulkan::Dispose() vkDestroyPipelineCache(Device, PipelineCache, nullptr); PipelineCache = VK_NULL_HANDLE; } -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE if (ValidationCache != VK_NULL_HANDLE) { if (SaveValidationCache()) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index a77744766..ee08b3eee 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -400,7 +400,9 @@ public: uint32 HasKHRMaintenance1 : 1; uint32 HasKHRMaintenance2 : 1; uint32 HasMirrorClampToEdge : 1; +#if VULKAN_USE_VALIDATION_CACHE uint32 HasEXTValidationCache : 1; +#endif }; static void GetInstanceLayersAndExtensions(Array& outInstanceExtensions, Array& outInstanceLayers, bool& outDebugUtils); @@ -496,13 +498,11 @@ public: /// VkPipelineCache PipelineCache = VK_NULL_HANDLE; -#if VK_EXT_validation_cache - +#if VULKAN_USE_VALIDATION_CACHE /// /// The optional validation cache. /// VkValidationCacheEXT ValidationCache = VK_NULL_HANDLE; - #endif /// @@ -584,12 +584,10 @@ public: bool SavePipelineCache(); #if VK_EXT_validation_cache - /// /// Saves the validation cache. /// bool SaveValidationCache(); - #endif private: diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp index b62324529..854500527 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp @@ -116,7 +116,7 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO); createInfo.codeSize = (size_t)spirv.Length(); createInfo.pCode = (const uint32_t*)spirv.Get(); -#if VK_EXT_validation_cache +#if VULKAN_USE_VALIDATION_CACHE VkShaderModuleValidationCacheCreateInfoEXT validationInfo; if (_device->ValidationCache != VK_NULL_HANDLE) { diff --git a/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h b/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h index e01e980d7..887e5f89d 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h +++ b/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h @@ -14,6 +14,7 @@ #include #undef VK_EXT_debug_utils #undef VK_EXT_validation_cache +#define VULKAN_USE_VALIDATION_CACHE 0 #pragma clang diagnostic ignored "-Wpointer-bool-conversion" #pragma clang diagnostic ignored "-Wtautological-pointer-compare" diff --git a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h index 3c8ee101e..a0e95cb2f 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h @@ -9,6 +9,9 @@ // Support more backbuffers in case driver decides to use more (https://gitlab.freedesktop.org/apinheiro/mesa/-/issues/9) #define VULKAN_BACK_BUFFERS_COUNT_MAX 8 +// Prevent wierd error 'Invalid VkValidationCacheEXT Object' +#define VULKAN_USE_VALIDATION_CACHE 0 + /// /// The implementation for the Vulkan API support for Linux platform. /// diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index fdbcd1bd0..98045fc85 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -382,6 +382,15 @@ public: return _isActiveInHierarchy != 0; } + /// + /// Gets value indicating if actor is in a scene. + /// + API_PROPERTY(Attributes="HideInEditor, NoSerialize") + FORCE_INLINE bool HasScene() const + { + return _scene != nullptr; + } + /// /// Returns true if object is fully static on the scene, otherwise false. /// diff --git a/Source/Engine/Level/Prefabs/Prefab.cpp b/Source/Engine/Level/Prefabs/Prefab.cpp index 310de590f..066ae04be 100644 --- a/Source/Engine/Level/Prefabs/Prefab.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.cpp @@ -8,9 +8,7 @@ #include "Engine/Level/Prefabs/PrefabManager.h" #include "Engine/Level/Actor.h" #include "Engine/Threading/Threading.h" -#if USE_EDITOR #include "Engine/Scripting/Scripting.h" -#endif REGISTER_JSON_ASSET(Prefab, "FlaxEngine.Prefab", true); @@ -163,10 +161,10 @@ Asset::LoadResult Prefab::loadAsset() } } -#if USE_EDITOR // Register for scripts reload and unload (need to cleanup all user objects including scripts that may be attached to the default instance - it can be always restored) - Scripting::ScriptsReloading.Bind(this); Scripting::ScriptsUnload.Bind(this); +#if USE_EDITOR + Scripting::ScriptsReloading.Bind(this); #endif return LoadResult::Ok; @@ -174,10 +172,10 @@ Asset::LoadResult Prefab::loadAsset() void Prefab::unload(bool isReloading) { -#if USE_EDITOR // Unlink - Scripting::ScriptsReloading.Unbind(this); Scripting::ScriptsUnload.Unbind(this); +#if USE_EDITOR + Scripting::ScriptsReloading.Unbind(this); #endif // Base diff --git a/Source/Engine/Level/Tags.cpp b/Source/Engine/Level/Tags.cpp index c12fed765..f1edeb91c 100644 --- a/Source/Engine/Level/Tags.cpp +++ b/Source/Engine/Level/Tags.cpp @@ -55,6 +55,11 @@ Tag Tags::Get(const StringView& tagName) return tag; } +Tag Tags::Find(const StringView& tagName) +{ + return Tag(List.Find(tagName) + 1); +} + Array Tags::GetSubTags(Tag parentTag) { Array subTags; diff --git a/Source/Engine/Level/Tags.h b/Source/Engine/Level/Tags.h index 7c261f313..350c88796 100644 --- a/Source/Engine/Level/Tags.h +++ b/Source/Engine/Level/Tags.h @@ -92,6 +92,13 @@ API_CLASS(Static) class FLAXENGINE_API Tags /// The tag. API_FUNCTION() static Tag Get(const StringView& tagName); + /// + /// Gets the tag. Returns empty one if it doesn't exist. + /// + /// The tag name. + /// The tag (might be empty). + API_FUNCTION() static Tag Find(const StringView& tagName); + /// /// Get all subtags of the specific Tag /// diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 390a3936e..c42270a05 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -23,24 +23,24 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_ENUM() enum class FLAXENGINE_API ScreenOrientation { /// - /// "portrait" mode + /// "userPortrait" mode /// Portrait, /// - /// "reversePortrait" mode + /// "userLandscape" mode /// - PortraitReverse, + Landscape, /// - /// "landscape" mode + /// "sensorPortrait" mode /// - LandscapeRight, + SensorPortrait, /// - /// "reverseLandscape" mode + /// "sensorLandscape" mode /// - LandscapeLeft, + SensorLandscape, /// /// "fullSensor" mode @@ -72,6 +72,24 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")") String PackageName = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}"); + /// + /// The application version code (eg. 1, 12, 123). + /// + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"General\")") + String VersionCode = TEXT("1"); + + /// + /// The minimum Android API level (eg. 20, 28, 34). + /// + API_FIELD(Attributes = "EditorOrder(20), EditorDisplay(\"General\")") + String MinimumAPILevel = TEXT("23"); + + /// + /// The target Android API level (eg. 20, 28, 34). + /// + API_FIELD(Attributes = "EditorOrder(30), EditorDisplay(\"General\")") + String TargetAPILevel = TEXT("33"); + /// /// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file. /// diff --git a/Source/Engine/Scripting/Attributes/Editor/WatermarkAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/WatermarkAttribute.cs new file mode 100644 index 000000000..73814d11c --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/WatermarkAttribute.cs @@ -0,0 +1,42 @@ +using System; + +namespace FlaxEngine; + +/// +/// Used to add a watermark to a string textbox in the editor field +/// +[Serializable] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +public class WatermarkAttribute : Attribute +{ + /// + /// The watermark text. + /// + public string WatermarkText; + + /// + /// The watermark color. + /// + public uint WatermarkColor; + + /// + /// Initializes a new instance of the class. + /// + /// The watermark text. + public WatermarkAttribute(string text) + { + WatermarkText = text; + WatermarkColor = 0; // default color of watermark in textbox + } + + /// + /// Initializes a new instance of the class. + /// + /// The watermark text. + /// The watermark color. 0 to use default. + public WatermarkAttribute(string text, uint color) + { + WatermarkText = text; + WatermarkColor = color; + } +} diff --git a/Source/Engine/Scripting/Script.cs b/Source/Engine/Scripting/Script.cs index f7d6c0242..b980baf3f 100644 --- a/Source/Engine/Scripting/Script.cs +++ b/Source/Engine/Scripting/Script.cs @@ -17,6 +17,12 @@ namespace FlaxEngine } } + /// + /// Gets value indicating if the actor owning the script is in a scene. + /// + [HideInEditor, NoSerialize] + public bool HasScene => Actor?.HasScene ?? false; + /// /// Gets or sets the world space transformation of the actors owning this script. /// diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index 612de3f59..abcd97a38 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -7,6 +7,7 @@ namespace FlaxEngine.GUI /// /// UI canvas scaling component for user interface that targets multiple different game resolutions (eg. mobile screens). /// + [ActorToolbox("GUI")] public class CanvasScaler : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/Border.cs b/Source/Engine/UI/GUI/Common/Border.cs index 58c281a3d..8c9433a98 100644 --- a/Source/Engine/UI/GUI/Common/Border.cs +++ b/Source/Engine/UI/GUI/Common/Border.cs @@ -5,6 +5,7 @@ namespace FlaxEngine.GUI /// /// Border control that draws the border around the control edges (inner and outer sides). /// + [ActorToolbox("GUI")] public class Border : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index ff4a5fe79..ff0b776f9 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -7,6 +7,7 @@ namespace FlaxEngine.GUI /// /// Button control /// + [ActorToolbox("GUI")] public class Button : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index d5ab3483c..de880c039 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -29,6 +29,7 @@ namespace FlaxEngine.GUI /// Check box control. /// /// + [ActorToolbox("GUI")] public class CheckBox : Control { /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 60e8e801b..1dbac95ab 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -9,6 +9,7 @@ namespace FlaxEngine.GUI /// Dropdown menu control allows to choose one item from the provided collection of options. /// /// + [ActorToolbox("GUI")] public class Dropdown : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/Image.cs b/Source/Engine/UI/GUI/Common/Image.cs index 0a0129db0..897c82b3f 100644 --- a/Source/Engine/UI/GUI/Common/Image.cs +++ b/Source/Engine/UI/GUI/Common/Image.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// The basic GUI image control. Shows texture, sprite or render target. /// /// + [ActorToolbox("GUI")] public class Image : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 46cb7415e..61282e7a5 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// The basic GUI label control. /// /// + [ActorToolbox("GUI")] public class Label : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/ProgressBar.cs b/Source/Engine/UI/GUI/Common/ProgressBar.cs index 6a17535b0..ff048600e 100644 --- a/Source/Engine/UI/GUI/Common/ProgressBar.cs +++ b/Source/Engine/UI/GUI/Common/ProgressBar.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// Progress bar control shows visual progress of the action or set of actions. /// /// + [ActorToolbox("GUI")] public class ProgressBar : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs b/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs index 499668d93..664a20df9 100644 --- a/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs +++ b/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs @@ -5,6 +5,7 @@ namespace FlaxEngine.GUI /// /// UI container control that can render children to texture and display pre-cached texture instead of drawing children every frame. It can be also used to render part of UI to texture and use it in material or shader. /// + [ActorToolbox("GUI")] public class RenderToTextureControl : ContainerControl { private bool _invalid, _redrawRegistered, _isDuringTextureDraw; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 77382a5d7..c4a69b08f 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -7,6 +7,7 @@ namespace FlaxEngine.GUI /// /// Rich text box control which can gather text input from the user and present text in highly formatted and stylized way. /// + [ActorToolbox("GUI")] public partial class RichTextBox : RichTextBoxBase { private TextBlockStyle _textStyle; diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index a62cbe8ee..39023ee1f 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -7,6 +7,7 @@ namespace FlaxEngine.GUI; /// /// The slider control. /// +[ActorToolbox("GUI")] public class Slider : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/Spacer.cs b/Source/Engine/UI/GUI/Common/Spacer.cs index e87cfbabc..005620d7d 100644 --- a/Source/Engine/UI/GUI/Common/Spacer.cs +++ b/Source/Engine/UI/GUI/Common/Spacer.cs @@ -6,6 +6,7 @@ namespace FlaxEngine.GUI /// Helper control used to insert blank space into the layout. /// /// + [ActorToolbox("GUI")] public sealed class Spacer : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 2553dc439..3d0581c4e 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -5,6 +5,7 @@ namespace FlaxEngine.GUI /// /// Text box control which can gather text input from the user. /// + [ActorToolbox("GUI")] public class TextBox : TextBoxBase { private TextLayoutOptions _layout; @@ -213,7 +214,7 @@ namespace FlaxEngine.GUI color *= 0.6f; Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); } - else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) + else if (!string.IsNullOrEmpty(_watermarkText)) { Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 460d6f4b1..2db8c33e2 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -1203,7 +1203,7 @@ namespace FlaxEngine.GUI if (base.OnMouseDown(location, button)) return true; - if (button == MouseButton.Left && _text.Length > 0 && _isSelectable) + if (button == MouseButton.Left && _isSelectable) { Focus(); OnSelectingBegin(); @@ -1219,6 +1219,10 @@ namespace FlaxEngine.GUI else SetSelection(_selectionStart, hitPos); } + else if (string.IsNullOrEmpty(_text)) + { + SetSelection(0); + } else { SetSelection(hitPos); diff --git a/Source/Engine/UI/GUI/Panels/AlphaPanel.cs b/Source/Engine/UI/GUI/Panels/AlphaPanel.cs index a67d47fac..dd421571a 100644 --- a/Source/Engine/UI/GUI/Panels/AlphaPanel.cs +++ b/Source/Engine/UI/GUI/Panels/AlphaPanel.cs @@ -5,6 +5,7 @@ namespace FlaxEngine.GUI /// /// Changes alpha of all its children /// + [ActorToolbox("GUI")] public class AlphaPanel : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Panels/BlurPanel.cs b/Source/Engine/UI/GUI/Panels/BlurPanel.cs index 44a9e3b4d..963dadd8e 100644 --- a/Source/Engine/UI/GUI/Panels/BlurPanel.cs +++ b/Source/Engine/UI/GUI/Panels/BlurPanel.cs @@ -6,6 +6,7 @@ namespace FlaxEngine.GUI /// The blur panel that applied the Gaussian-blur to all content beneath the control. /// /// + [ActorToolbox("GUI")] public class BlurPanel : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index e0785a99d..e053edd9c 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// Drop Panel arranges control vertically and provides feature to collapse contents. /// /// + [ActorToolbox("GUI")] public class DropPanel : ContainerControl { /// diff --git a/Source/Engine/UI/GUI/Panels/GridPanel.cs b/Source/Engine/UI/GUI/Panels/GridPanel.cs index 629343f44..2b1a0a123 100644 --- a/Source/Engine/UI/GUI/Panels/GridPanel.cs +++ b/Source/Engine/UI/GUI/Panels/GridPanel.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// A panel that divides up available space between all of its children. /// /// + [ActorToolbox("GUI")] public class GridPanel : ContainerControl { private Margin _slotPadding; diff --git a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs index 29bb498dd..6d3256a00 100644 --- a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs @@ -6,6 +6,7 @@ namespace FlaxEngine.GUI /// This panel arranges child controls horizontally. /// /// + [ActorToolbox("GUI")] public class HorizontalPanel : PanelWithMargins { /// diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 8435854f5..61a2d2b8c 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// Panel UI control. /// /// + [ActorToolbox("GUI")] public class Panel : ScrollableControl { private bool _layoutChanged; diff --git a/Source/Engine/UI/GUI/Panels/TilesPanel.cs b/Source/Engine/UI/GUI/Panels/TilesPanel.cs index 13e40d8bf..8b2148bce 100644 --- a/Source/Engine/UI/GUI/Panels/TilesPanel.cs +++ b/Source/Engine/UI/GUI/Panels/TilesPanel.cs @@ -8,6 +8,7 @@ namespace FlaxEngine.GUI /// Panel that arranges child controls like tiles. /// /// + [ActorToolbox("GUI")] public class TilesPanel : ContainerControl { private Margin _tileMargin; diff --git a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs index ed6ed1d8a..6c112d9d5 100644 --- a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs +++ b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs @@ -6,6 +6,7 @@ namespace FlaxEngine.GUI /// A panel that evenly divides up available space between all of its children. /// /// + [ActorToolbox("GUI")] public class UniformGridPanel : ContainerControl { private Margin _slotPadding; diff --git a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs index 39bd6a294..b2fcdeae9 100644 --- a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs @@ -6,6 +6,7 @@ namespace FlaxEngine.GUI /// This panel arranges child controls vertically. /// /// + [ActorToolbox("GUI")] public class VerticalPanel : PanelWithMargins { /// diff --git a/Source/Engine/UI/SpriteRender.h b/Source/Engine/UI/SpriteRender.h index dbec117e4..d9eb769c2 100644 --- a/Source/Engine/UI/SpriteRender.h +++ b/Source/Engine/UI/SpriteRender.h @@ -10,7 +10,7 @@ /// /// Sprite rendering object. /// -API_CLASS(Attributes="ActorContextMenu(\"New/UI/Sprite Render\"), ActorToolbox(\"GUI\")") +API_CLASS(Attributes="ActorContextMenu(\"New/UI/Sprite Render\"), ActorToolbox(\"Visuals\")") class FLAXENGINE_API SpriteRender : public Actor { DECLARE_SCENE_OBJECT(SpriteRender); diff --git a/Source/Engine/UI/TextRender.h b/Source/Engine/UI/TextRender.h index a99a53318..cc89fbee1 100644 --- a/Source/Engine/UI/TextRender.h +++ b/Source/Engine/UI/TextRender.h @@ -18,7 +18,7 @@ /// /// Text rendering object. /// -API_CLASS(Attributes="ActorContextMenu(\"New/UI/Text Render\"), ActorToolbox(\"GUI\")") +API_CLASS(Attributes="ActorContextMenu(\"New/UI/Text Render\"), ActorToolbox(\"Visuals\")") class FLAXENGINE_API TextRender : public Actor { DECLARE_SCENE_OBJECT(TextRender); diff --git a/Source/Engine/UI/UIControl.h b/Source/Engine/UI/UIControl.h index e76aca65a..7cdbbe7ac 100644 --- a/Source/Engine/UI/UIControl.h +++ b/Source/Engine/UI/UIControl.h @@ -8,7 +8,7 @@ /// /// Contains a single GUI control (on C# side). /// -API_CLASS(Sealed, Attributes="ActorContextMenu(\"New/UI/UI Control\"), ActorToolbox(\"GUI\")") +API_CLASS(Sealed, Attributes="ActorContextMenu(\"New/UI/UI Control\"), ActorToolbox(\"GUI\", \"Empty UIControl\")") class FLAXENGINE_API UIControl : public Actor { DECLARE_SCENE_OBJECT(UIControl); diff --git a/Source/Platforms/Android/Binaries/Project/app/build.gradle b/Source/Platforms/Android/Binaries/Project/app/build.gradle index 2f5da0a56..fd7764060 100644 --- a/Source/Platforms/Android/Binaries/Project/app/build.gradle +++ b/Source/Platforms/Android/Binaries/Project/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 24 + compileSdk ${TargetSdk} namespace "${PackageName}" defaultConfig { applicationId "${PackageName}" - minSdkVersion 24 - targetSdkVersion 24 - versionCode 1 + minSdk ${MinimumSdk} + targetSdk ${TargetSdk} + versionCode ${VersionCode} versionName "${ProjectVersion}" ndk { abiFilter "${PackageAbi}" diff --git a/Source/Platforms/Android/Binaries/Project/app/src/main/AndroidManifest.xml b/Source/Platforms/Android/Binaries/Project/app/src/main/AndroidManifest.xml index 5169e3846..6e84915ca 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/AndroidManifest.xml +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ android:extractNativeLibs="true" android:hasCode="true">