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 bd6e7a25c..8db294611 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"); @@ -339,7 +363,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) Platform::CreateProcess(procSettings); } #endif - const bool distributionPackage = buildSettings->ForDistribution; + const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release; { CreateProcessSettings procSettings; procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug")); diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 316ae25aa..d6d2455d0 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -52,13 +52,18 @@ namespace FlaxEditor.CustomEditors // Check if use provided editor if (overrideEditor != null) return overrideEditor; + ScriptType targetType = values.Type; // Special case if property is a pure object type and all values are the same type - if (values.Type.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes) + if (targetType.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes) return CreateEditor(TypeUtils.GetObjectType(values[0]), canUseRefPicker); + // Special case if property is interface but the value is implemented as Scripting Object that should use reference picker + if (targetType.IsInterface && canUseRefPicker && values.Count > 0 && values[0] is FlaxEngine.Object) + return new DummyEditor(); + // Use editor for the property type - return CreateEditor(values.Type, canUseRefPicker); + return CreateEditor(targetType, canUseRefPicker); } internal static CustomEditor CreateEditor(ScriptType targetType, bool canUseRefPicker = true) 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/LayersMaskEditor.cs b/Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs index ff157c74b..193e6225b 100644 --- a/Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEditor.Content.Settings; using FlaxEngine; using FlaxEngine.GUI; @@ -12,7 +13,7 @@ namespace FlaxEditor.CustomEditors.Dedicated [CustomEditor(typeof(LayersMask)), DefaultEditor] internal class LayersMaskEditor : CustomEditor { - private CheckBox[] _checkBoxes; + private List _checkBoxes; /// public override void Initialize(LayoutElementsContainer layout) @@ -24,16 +25,18 @@ namespace FlaxEditor.CustomEditors.Dedicated return; } - _checkBoxes = new CheckBox[layers.Length]; + _checkBoxes = new List(); for (int i = 0; i < layers.Length; i++) { var layer = layers[i]; - var property = layout.AddPropertyItem(layer); + if (string.IsNullOrEmpty(layer)) + continue; + var property = layout.AddPropertyItem($"{i}: {layer}"); var checkbox = property.Checkbox().CheckBox; UpdateCheckbox(checkbox, i); checkbox.Tag = i; checkbox.StateChanged += OnCheckboxStateChanged; - _checkBoxes[i] = checkbox; + _checkBoxes.Add(checkbox); } } @@ -50,9 +53,9 @@ namespace FlaxEditor.CustomEditors.Dedicated { if (_checkBoxes != null) { - for (int i = 0; i < _checkBoxes.Length; i++) + for (int i = 0; i < _checkBoxes.Count; i++) { - UpdateCheckbox(_checkBoxes[i], i); + UpdateCheckbox(_checkBoxes[i], (int)_checkBoxes[i].Tag); } } 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/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 27be8e538..d517d12fd 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -456,14 +456,47 @@ namespace FlaxEditor.CustomEditors.Dedicated for (int i = 0; i < layout.Children.Count; i++) { - if (layout.Children[i] is GroupElement group && group.Panel.HeaderText == "Transform") + if (layout.Children[i] is GroupElement group && group.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal)) { - VerticalPanelElement mainHor = VerticalPanelWithoutMargin(group); - CreateTransformElements(mainHor, ValuesTypes); - group.ContainerControl.ChangeChildIndex(mainHor.Control, 0); + layout.Children.Remove(group); + layout.ContainerControl.Children.Remove(group.Panel); break; } } + + // Setup transform + if (Presenter is LayoutElementsContainer l) + { + var transformGroup = l.Group("Transform"); + VerticalPanelElement mainHor = VerticalPanelWithoutMargin(transformGroup); + CreateTransformElements(mainHor, ValuesTypes); + + ScriptMemberInfo scaleInfo = ValuesTypes[0].GetProperty("Scale"); + ItemInfo scaleItem = new ItemInfo(scaleInfo); + transformGroup.Property("Scale", scaleItem.GetValues(Values)); + + ScriptMemberInfo pivotInfo = ValuesTypes[0].GetProperty("Pivot"); + ItemInfo pivotItem = new ItemInfo(pivotInfo); + transformGroup.Property("Pivot", pivotItem.GetValues(Values)); + + ScriptMemberInfo shearInfo = ValuesTypes[0].GetProperty("Shear"); + ItemInfo shearItem = new ItemInfo(shearInfo); + transformGroup.Property("Shear", shearItem.GetValues(Values)); + + ScriptMemberInfo rotationInfo = ValuesTypes[0].GetProperty("Rotation"); + ItemInfo rotationItem = new ItemInfo(rotationInfo); + transformGroup.Property("Rotation", rotationItem.GetValues(Values)); + + // Get position of general tab + for (int i = 0; i < l.Children.Count; i++) + { + if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("General", StringComparison.Ordinal) && i + 1 <= l.Children.Count) + { + Presenter.ContainerControl.ChangeChildIndex(transformGroup.Control, i + 1); + break; + } + } + } } private void CreateTransformElements(LayoutElementsContainer main, ScriptType[] valueTypes) @@ -645,7 +678,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { var grid = UniformGridTwoByOne(el); grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1); - var label = grid.Label(text); + var label = grid.Label(text, TextAlignment.Far); var editor = grid.Object(values); if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement) { diff --git a/Source/Editor/CustomEditors/Editors/DummyEditor.cs b/Source/Editor/CustomEditors/Editors/DummyEditor.cs new file mode 100644 index 000000000..8b952d2f1 --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/DummyEditor.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +namespace FlaxEditor.CustomEditors.Editors +{ + internal sealed class DummyEditor : CustomEditor + { + public override void Initialize(LayoutElementsContainer layout) + { + string valueName; + if (Values.Count != 0 && Values[0] != null) + valueName = Values[0].ToString(); + else + valueName = "null"; + layout.Label($"{valueName} ({Values.Type})"); + } + } +} 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/CustomEditors/GUI/PropertiesList.cs b/Source/Editor/CustomEditors/GUI/PropertiesList.cs index 28e1c9a4f..208dc64e6 100644 --- a/Source/Editor/CustomEditors/GUI/PropertiesList.cs +++ b/Source/Editor/CustomEditors/GUI/PropertiesList.cs @@ -242,7 +242,7 @@ namespace FlaxEditor.CustomEditors.GUI float namesWidth = _splitterValue * Width; int count = _element.Labels.Count; float[] yStarts = new float[count + 1]; - for (int i = 1; i < count; i++) + for (int i = 0; i < count; i++) { var label = _element.Labels[i]; @@ -251,9 +251,13 @@ namespace FlaxEditor.CustomEditors.GUI else if (_children.Count <= label.FirstChildControlIndex) yStarts[i] = y; else + { yStarts[i] = _children[label.FirstChildControlIndex].Top; + if (i == count - 1) + yStarts[i + 1] = _children[label.FirstChildControlIndex].Bottom; + } + } - yStarts[count] = y; for (int i = 0; i < count; i++) { var label = _element.Labels[i]; diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 2ef9fdecd..714f5a0b3 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -264,6 +264,7 @@ namespace FlaxEditor.GUI.Dialogs { Text = "+", Parent = this, + TooltipText = "Save Color.", Tag = null, }; savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b); @@ -498,6 +499,7 @@ namespace FlaxEditor.GUI.Dialogs { Text = "+", Parent = this, + TooltipText = "Save Color.", Tag = null, }; savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b); diff --git a/Source/Editor/GUI/Dialogs/ColorSelector.cs b/Source/Editor/GUI/Dialogs/ColorSelector.cs index 646bfe41a..d729ec571 100644 --- a/Source/Editor/GUI/Dialogs/ColorSelector.cs +++ b/Source/Editor/GUI/Dialogs/ColorSelector.cs @@ -311,7 +311,9 @@ namespace FlaxEditor.GUI.Dialogs // Alpha float alphaY = _slider2Rect.Height * (1 - _color.A); var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness); - Render2D.FillRectangle(_slider2Rect, _color, _color, Color.Transparent, Color.Transparent); + var color = _color; + color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting. + Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent); Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black); Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray); } 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/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index debc0a663..5a7aa886f 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -255,6 +255,17 @@ namespace FlaxEditor.Options } } + // Ensure custom fonts are valid, reset if not + var defaultInterfaceOptions = new InterfaceOptions(); + if (Style.Current.FontTitle == null) + Style.Current.FontTitle = defaultInterfaceOptions.TitleFont.GetFont(); + if (Style.Current.FontSmall == null) + Style.Current.FontSmall = defaultInterfaceOptions.SmallFont.GetFont(); + if (Style.Current.FontMedium == null) + Style.Current.FontMedium = defaultInterfaceOptions.MediumFont.GetFont(); + if (Style.Current.FontLarge == null) + Style.Current.FontLarge = defaultInterfaceOptions.LargeFont.GetFont(); + // Set fallback fonts var fallbackFonts = Options.Interface.FallbackFonts; if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null)) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 6056cf68b..a56ab7b42 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; @@ -354,7 +355,7 @@ namespace FlaxEditor.SceneGraph.GUI private void OnRenamed(RenamePopup renamePopup) { using (new UndoBlock(ActorNode.Root.Undo, Actor, "Rename")) - Actor.Name = renamePopup.Text; + Actor.Name = renamePopup.Text.Trim(); } /// @@ -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/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs index 9c1d00196..d0284d6fa 100644 --- a/Source/Editor/Surface/Archetypes/Math.cs +++ b/Source/Editor/Surface/Archetypes/Math.cs @@ -430,7 +430,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Smoothstep", Description = "Returns a smooth Hermite interpolation between 0 and 1, if value is in the range [min, max].", Flags = NodeFlags.MaterialGraph, - Size = new Float2(120, 60), + Size = new Float2(200, 60), ConnectionsHints = ConnectionsHint.Numeric, IndependentBoxes = new[] { 0, 1, 2 }, DependentBoxes = new[] { 3 }, 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/GameplayGlobalsWindow.cs b/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs index 6899bfac6..477be532c 100644 --- a/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs +++ b/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs @@ -302,8 +302,17 @@ namespace FlaxEditor.Windows.Assets // TODO: improve the UI layout.Space(40); var addParamType = layout.ComboBox().ComboBox; - addParamType.Items = AllowedTypes.Select(CustomEditorsUtil.GetTypeNameUI).ToList(); - addParamType.SelectedIndex = 0; + object lastValue = null; + foreach (var e in _proxy.DefaultValues) + lastValue = e.Value; + + var allowedTypes = AllowedTypes.Select(CustomEditorsUtil.GetTypeNameUI).ToList(); + int index = 0; + if (lastValue != null) + index = allowedTypes.FindIndex(x => x.Equals(CustomEditorsUtil.GetTypeNameUI(lastValue.GetType()), StringComparison.Ordinal)); + + addParamType.Items = allowedTypes; + addParamType.SelectedIndex = index; _addParamType = addParamType; var addParamButton = layout.Button("Add").Button; addParamButton.Clicked += OnAddParamButtonClicked; 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/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 3e29b979f..c147579f9 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -542,6 +542,8 @@ namespace FlaxEditor.Windows return; } + newShortName = newShortName.Trim(); + // Cache data string extension = item.IsFolder ? "" : Path.GetExtension(item.Path); var newPath = StringUtils.CombinePaths(item.ParentFolder.Path, newShortName + extension); diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index fe0c79084..349301ec9 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -470,6 +470,10 @@ namespace FlaxEditor.Windows IsMaximized = false; IsBorderless = false; Cursor = CursorType.Default; + Screen.CursorLock = CursorLockMode.None; + if (Screen.MainWindow.IsMouseTracking) + Screen.MainWindow.EndTrackingMouse(); + RootControl.GameRoot.EndMouseCapture(); } /// @@ -478,7 +482,7 @@ namespace FlaxEditor.Windows base.OnMouseLeave(); // Remove focus from game window when mouse moves out and the cursor is hidden during game - if ((IsFocused || ContainsFocus) && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible) + if (ContainsFocus && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible && Screen.CursorLock == CursorLockMode.None) { Parent.Focus(); } diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index cd76412c8..5d37ce865 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -281,7 +281,7 @@ namespace FlaxEditor.Windows if (IsLayoutLocked) return; - _hScroll.Maximum = _output.TextSize.X; + _hScroll.Maximum = Mathf.Max(_output.TextSize.X, _hScroll.Minimum); _vScroll.Maximum = Mathf.Max(_output.TextSize.Y - _output.Height, _vScroll.Minimum); } 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..4b347ea83 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)); @@ -316,6 +367,7 @@ namespace FlaxEditor.Windows var tree = new Tree(false) { AnchorPreset = AnchorPresets.HorizontalStretchTop, + Margin = new Margin(0, 0, 0, panel.ScrollBarsSize), IsScrollable = true, Parent = panel }; diff --git a/Source/Engine/Core/Config/BuildSettings.h b/Source/Engine/Core/Config/BuildSettings.h index 86bc9571f..ff90d1b9d 100644 --- a/Source/Engine/Core/Config/BuildSettings.h +++ b/Source/Engine/Core/Config/BuildSettings.h @@ -42,7 +42,7 @@ public: int32 ContentKey = 0; /// - /// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett). + /// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett). Enabled by default for `Release` builds. /// API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"General\")") bool ForDistribution = false; 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/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index bb20ced62..638b58223 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -586,8 +586,17 @@ namespace FlaxEngine.Interop internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) { Type elementType = Unsafe.As(elementTypeHandle.Target); - Type classType = ArrayFactory.GetArrayType(elementType); - return GetTypeManagedHandle(classType); + Type arrayType = ArrayFactory.GetArrayType(elementType); + return GetTypeManagedHandle(arrayType); + } + + [UnmanagedCallersOnly] + internal static ManagedHandle GetArrayTypeFromWrappedArray(ManagedHandle arrayHandle) + { + ManagedArray managedArray = Unsafe.As(arrayHandle.Target); + Type elementType = managedArray.ArrayType.GetElementType(); + Type arrayType = ArrayFactory.GetArrayType(elementType); + return GetTypeManagedHandle(arrayType); } [UnmanagedCallersOnly] 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/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index fcfdbeb23..f259ce337 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -770,14 +770,24 @@ void WindowsWindow::CheckForWindowResize() } } -void WindowsWindow::UpdateCursor() const +void WindowsWindow::UpdateCursor() { // Don't hide cursor when window is not focused if (_cursor == CursorType::Hidden && _focused) { + if (!_lastCursorHidden) + { + _lastCursorHidden = true; + ::ShowCursor(FALSE); + } ::SetCursor(nullptr); return; } + else if (_lastCursorHidden) + { + _lastCursorHidden = false; + ::ShowCursor(TRUE); + } int32 index = 0; switch (_cursor) diff --git a/Source/Engine/Platform/Windows/WindowsWindow.h b/Source/Engine/Platform/Windows/WindowsWindow.h index 5a963c251..c2dad4a13 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.h +++ b/Source/Engine/Platform/Windows/WindowsWindow.h @@ -28,6 +28,7 @@ private: bool _isSwitchingFullScreen = false; bool _trackingMouse = false; bool _clipCursorSet = false; + bool _lastCursorHidden = false; bool _isDuringMaximize = false; Windows::HANDLE _monitor = nullptr; Windows::LONG _clipCursorRect[4]; @@ -90,7 +91,7 @@ public: private: void CheckForWindowResize(); - void UpdateCursor() const; + void UpdateCursor(); void UpdateRegion(); public: diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 31136b455..c65a9f401 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1175,7 +1175,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, drawCall.AsChar.Mat = nullptr; } Float2 pointer = location; - for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { // Cache current character const Char currentChar = text[currentIndex]; 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/Internal/StdTypesContainer.cpp b/Source/Engine/Scripting/Internal/StdTypesContainer.cpp index 817c86a57..e1ff23ee2 100644 --- a/Source/Engine/Scripting/Internal/StdTypesContainer.cpp +++ b/Source/Engine/Scripting/Internal/StdTypesContainer.cpp @@ -40,6 +40,8 @@ void StdTypesContainer::Clear() Json_SerializeDiff = nullptr; Json_Deserialize = nullptr; + ManagedArrayClass = nullptr; + #if USE_EDITOR ExecuteInEditModeAttribute = nullptr; #endif @@ -88,6 +90,8 @@ bool StdTypesContainer::Gather() GET_METHOD(Json_SerializeDiff, JSON, "SerializeDiff", 3); GET_METHOD(Json_Deserialize, JSON, "Deserialize", 3); + GET_CLASS(FlaxEngine, ManagedArrayClass, "FlaxEngine.Interop.ManagedArray"); + #if USE_EDITOR GET_CLASS(FlaxEngine, ExecuteInEditModeAttribute, "FlaxEngine.ExecuteInEditModeAttribute"); #endif diff --git a/Source/Engine/Scripting/Internal/StdTypesContainer.h b/Source/Engine/Scripting/Internal/StdTypesContainer.h index 62231a689..94ea73675 100644 --- a/Source/Engine/Scripting/Internal/StdTypesContainer.h +++ b/Source/Engine/Scripting/Internal/StdTypesContainer.h @@ -45,6 +45,8 @@ public: MMethod* Json_SerializeDiff; MMethod* Json_Deserialize; + MClass* ManagedArrayClass; + #if USE_EDITOR MClass* ExecuteInEditModeAttribute; #endif diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index 5352a4b29..35227df56 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -83,6 +83,7 @@ public: { static MArray* New(const MClass* elementKlass, int32 length); static MClass* GetClass(MClass* elementKlass); + static MClass* GetArrayClass(const MArray* obj); static int32 GetLength(const MArray* obj); static void* GetAddress(const MArray* obj); static MArray* Unbox(MObject* obj); diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index c0c3cdc45..810abbe48 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -394,14 +394,15 @@ Variant MUtils::UnboxVariant(MObject* value) case MTypes::Array: { void* ptr = MCore::Array::GetAddress((MArray*)value); - MClass* elementClass = klass->GetElementClass(); + const MClass* arrayClass = klass == stdTypes.ManagedArrayClass ? MCore::Array::GetArrayClass((MArray*)value) : klass; + const MClass* elementClass = arrayClass->GetElementClass(); if (elementClass == MCore::TypeCache::Byte) { Variant v; v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value)); return v; } - const StringAnsiView fullname = klass->GetFullName(); + const StringAnsiView fullname = arrayClass->GetFullName(); Variant v; v.SetType(MoveTemp(VariantType(VariantType::Array, fullname))); auto& array = v.AsArray(); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 01b43c049..a3ec1321f 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -402,8 +402,15 @@ MArray* MCore::Array::New(const MClass* elementKlass, int32 length) MClass* MCore::Array::GetClass(MClass* elementKlass) { - static void* GetArrayLengthPtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType")); - MType* typeHandle = (MType*)CallStaticMethod(GetArrayLengthPtr, elementKlass->_handle); + static void* GetArrayTypeFromElementTypePtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType")); + MType* typeHandle = (MType*)CallStaticMethod(GetArrayTypeFromElementTypePtr, elementKlass->_handle); + return GetOrCreateClass(typeHandle); +} + +MClass* MCore::Array::GetArrayClass(const MArray* obj) +{ + static void* GetArrayTypeFromWrappedArrayPtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromWrappedArray")); + MType* typeHandle = (MType*)CallStaticMethod(GetArrayTypeFromWrappedArrayPtr, (void*)obj); return GetOrCreateClass(typeHandle); } diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 2e507a44a..40fa8f4bf 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -795,6 +795,12 @@ MClass* MCore::Array::GetClass(MClass* elementKlass) return FindClass(monoClass); } +MClass* MCore::Array::GetArrayClass(const MArray* obj) +{ + CRASH; // Not applicable + return nullptr; +} + int32 MCore::Array::GetLength(const MArray* obj) { return (int32)mono_array_length((MonoArray*)obj); diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index 1fb0e6d1f..bb83fd27d 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -130,6 +130,11 @@ MClass* MCore::Array::GetClass(MClass* elementKlass) return nullptr; } +MClass* MCore::Array::GetArrayClass(const MArray* obj) +{ + return nullptr; +} + int32 MCore::Array::GetLength(const MArray* obj) { return 0; 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/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index 3eb527e4d..aa677cda9 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -12,6 +12,7 @@ #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/Textures/GPUTexture.h" +#include "Engine/Physics/PhysicsScene.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Renderer/GlobalSignDistanceFieldPass.h" #include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h" @@ -807,6 +808,15 @@ void Terrain::OnEnable() #if TERRAIN_USE_PHYSICS_DEBUG GetSceneRendering()->AddPhysicsDebug(this); #endif + void* scene = GetPhysicsScene()->GetPhysicsScene(); + for (int32 i = 0; i < _patches.Count(); i++) + { + auto patch = _patches[i]; + if (patch->_physicsActor) + { + PhysicsBackend::AddSceneActor(scene, patch->_physicsActor); + } + } // Base Actor::OnEnable(); diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index b20b5e9a1..34fc57e84 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -2126,7 +2126,8 @@ void TerrainPatch::CreateCollision() void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene(); _physicsActor = PhysicsBackend::CreateRigidStaticActor(nullptr, terrainTransform.LocalToWorld(_offset), terrainTransform.Orientation, scene); PhysicsBackend::AttachShape(_physicsShape, _physicsActor); - PhysicsBackend::AddSceneActor(scene, _physicsActor); + if (_terrain->IsDuringPlay()) + PhysicsBackend::AddSceneActor(scene, _physicsActor); } bool TerrainPatch::CreateHeightField() 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..95b49850b 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); @@ -1265,7 +1269,11 @@ namespace FlaxEngine.GUI // Multiline scroll if (IsMultiline && _text.Length != 0 && IsMultilineScrollable) { - TargetViewOffset = Float2.Clamp(_targetViewOffset - new Float2(0, delta * 10.0f), Float2.Zero, new Float2(_targetViewOffset.X, _textSize.Y - Height)); + if (Input.GetKey(KeyboardKeys.Shift)) + TargetViewOffset = Float2.Clamp(_targetViewOffset - new Float2(delta * 20.0f, 0), Float2.Zero, new Float2(_textSize.X, _targetViewOffset.Y)); + else + TargetViewOffset = Float2.Clamp(_targetViewOffset - new Float2(0, delta * 10.0f), Float2.Zero, new Float2(_targetViewOffset.X, _textSize.Y - Height)); + return true; } 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..8c90b6544 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; @@ -291,11 +292,15 @@ namespace FlaxEngine.GUI if (base.OnMouseWheel(location, delta)) return true; + if (Input.GetKey(KeyboardKeys.Shift)) + { + if (HScrollBar != null && HScrollBar.Enabled && HScrollBar.OnMouseWheel(HScrollBar.PointFromParent(ref location), delta)) + return true; + } + // Roll back to scroll bars if (VScrollBar != null && VScrollBar.Enabled && VScrollBar.OnMouseWheel(VScrollBar.PointFromParent(ref location), delta)) return true; - if (HScrollBar != null && HScrollBar.Enabled && HScrollBar.OnMouseWheel(HScrollBar.PointFromParent(ref location), delta)) - return true; // No event handled return false; 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..cadb4c8ee 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/AndroidManifest.xml +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/AndroidManifest.xml @@ -5,9 +5,13 @@