Merge remote-tracking branch 'origin/master' into 1.9

# Conflicts:
#	Flax.flaxproj
This commit is contained in:
Wojtek Figat
2024-05-08 19:27:56 +02:00
88 changed files with 798 additions and 118 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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;

View File

@@ -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"));

View File

@@ -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)

View File

@@ -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];

View File

@@ -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<CheckBox> _checkBoxes;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -24,16 +25,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
return;
}
_checkBoxes = new CheckBox[layers.Length];
_checkBoxes = new List<CheckBox>();
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);
}
}

View File

@@ -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.");

View File

@@ -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)
{

View File

@@ -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})");
}
}
}

View File

@@ -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;
/// <inheritdoc />
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;
}
}
}

View File

@@ -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];

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
/// <summary>
/// Control type drag handler.
/// </summary>
public sealed class DragControlType : DragActorType<DragEventArgs>
{
/// <summary>
/// Initializes a new instance of the <see cref="DragControlType"/> class.
/// </summary>
/// <param name="validateFunction">The validation function</param>
public DragControlType(Func<ScriptType, bool> validateFunction)
: base(validateFunction)
{
}
}
/// <summary>
/// Helper class for handling control type drag and drop (for spawning).
/// </summary>
/// <seealso cref="Control" />
/// <seealso cref="ActorNode" />
public class DragControlType<U> : DragHelper<ScriptType, U> where U : DragEventArgs
{
/// <summary>
/// The default prefix for drag data used for actor type drag and drop.
/// </summary>
public const string DragPrefix = "CTYPE!?";
/// <summary>
/// Creates a new DragHelper
/// </summary>
/// <param name="validateFunction">The validation function</param>
public DragControlType(Func<ScriptType, bool> validateFunction)
: base(validateFunction)
{
}
/// <inheritdoc/>
public override DragData ToDragData(ScriptType item) => GetDragData(item);
/// <inheritdoc/>
public override DragData ToDragData(IEnumerable<ScriptType> items)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the drag data.
/// </summary>
/// <param name="item">The control type.</param>
/// <returns>The data</returns>
public static DragData GetDragData(Type item)
{
if (item == null)
throw new ArgumentNullException();
return new DragDataText(DragPrefix + item.FullName);
}
/// <summary>
/// Gets the drag data.
/// </summary>
/// <param name="item">The control type.</param>
/// <returns>The data</returns>
public static DragData GetDragData(ScriptType item)
{
if (item == ScriptType.Null)
throw new ArgumentNullException();
return new DragDataText(DragPrefix + item.TypeName);
}
/// <summary>
/// Tries to parse the drag data to extract <see cref="Type"/> collection.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>Gathered objects or empty array if cannot get any valid.</returns>
public override IEnumerable<ScriptType> 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<ScriptType>(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<ScriptType>();
}
}

View File

@@ -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
}
}
/// <summary>
/// Gets a value indicating whether the node is collapsed in the hierarchy (is collapsed or any of its parents is collapsed).
/// </summary>
public bool IsCollapsedInHierarchy => IsCollapsed || (Parent is TreeNode parentNode && parentNode.IsCollapsedInHierarchy);
/// <summary>
/// Gets or sets the text margin.
/// </summary>
@@ -604,9 +608,6 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc />
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
}
}
/// <inheritdoc />
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();
}
}
}
}
/// <inheritdoc />
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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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<Rectangle> _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();
}
/// <inheritdoc />
@@ -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;

View File

@@ -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

View File

@@ -186,6 +186,18 @@ namespace FlaxEditor.Surface.Archetypes
base.OnEndMouseCapture();
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
switch (key)
{
case KeyboardKeys.Delete:
_editor.SetAsset(_index, Guid.Empty);
return true;
}
return base.OnKeyDown(key);
}
}
/// <summary>

View File

@@ -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 },

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<SceneGraphNode>(1) { actorNode }, true);
Undo.AddAction(action);
Focus();
Select(actorNode);
}

View File

@@ -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);

View File

@@ -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();
}
/// <inheritdoc />
@@ -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();
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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<Panel>().GetChild<Tree>();
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
};

View File

@@ -42,7 +42,7 @@ public:
int32 ContentKey = 0;
/// <summary>
/// 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.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"General\")")
bool ForDistribution = false;

View File

@@ -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()

View File

@@ -586,8 +586,17 @@ namespace FlaxEngine.Interop
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
{
Type elementType = Unsafe.As<TypeHolder>(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<ManagedArray>(arrayHandle.Target);
Type elementType = managedArray.ArrayType.GetElementType();
Type arrayType = ArrayFactory.GetArrayType(elementType);
return GetTypeManagedHandle(arrayType);
}
[UnmanagedCallersOnly]

View File

@@ -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

View File

@@ -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<const char*>& 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
}

View File

@@ -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<uint8> 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())

View File

@@ -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<const char*>& outInstanceExtensions, Array<const char*>& outInstanceLayers, bool& outDebugUtils);
@@ -496,13 +498,11 @@ public:
/// </summary>
VkPipelineCache PipelineCache = VK_NULL_HANDLE;
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
/// <summary>
/// The optional validation cache.
/// </summary>
VkValidationCacheEXT ValidationCache = VK_NULL_HANDLE;
#endif
/// <summary>
@@ -584,12 +584,10 @@ public:
bool SavePipelineCache();
#if VK_EXT_validation_cache
/// <summary>
/// Saves the validation cache.
/// </summary>
bool SaveValidationCache();
#endif
private:

View File

@@ -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)
{

View File

@@ -14,6 +14,7 @@
#include <vulkan/vulkan.h>
#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"

View File

@@ -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
/// <summary>
/// The implementation for the Vulkan API support for Linux platform.
/// </summary>

View File

@@ -382,6 +382,15 @@ public:
return _isActiveInHierarchy != 0;
}
/// <summary>
/// Gets value indicating if actor is in a scene.
/// </summary>
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
FORCE_INLINE bool HasScene() const
{
return _scene != nullptr;
}
/// <summary>
/// Returns true if object is fully static on the scene, otherwise false.
/// </summary>

View File

@@ -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<Prefab, &Prefab::DeleteDefaultInstance>(this);
Scripting::ScriptsUnload.Bind<Prefab, &Prefab::DeleteDefaultInstance>(this);
#if USE_EDITOR
Scripting::ScriptsReloading.Bind<Prefab, &Prefab::DeleteDefaultInstance>(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<Prefab, &Prefab::DeleteDefaultInstance>(this);
Scripting::ScriptsUnload.Unbind<Prefab, &Prefab::DeleteDefaultInstance>(this);
#if USE_EDITOR
Scripting::ScriptsReloading.Unbind<Prefab, &Prefab::DeleteDefaultInstance>(this);
#endif
// Base

View File

@@ -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<Tag> Tags::GetSubTags(Tag parentTag)
{
Array<Tag> subTags;

View File

@@ -92,6 +92,13 @@ API_CLASS(Static) class FLAXENGINE_API Tags
/// <returns>The tag.</returns>
API_FUNCTION() static Tag Get(const StringView& tagName);
/// <summary>
/// Gets the tag. Returns empty one if it doesn't exist.
/// </summary>
/// <param name="tagName">The tag name.</param>
/// <returns>The tag (might be empty).</returns>
API_FUNCTION() static Tag Find(const StringView& tagName);
/// <summary>
/// Get all subtags of the specific Tag
/// </summary>

View File

@@ -23,24 +23,24 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
API_ENUM() enum class FLAXENGINE_API ScreenOrientation
{
/// <summary>
/// "portrait" mode
/// "userPortrait" mode
/// </summary>
Portrait,
/// <summary>
/// "reversePortrait" mode
/// "userLandscape" mode
/// </summary>
PortraitReverse,
Landscape,
/// <summary>
/// "landscape" mode
/// "sensorPortrait" mode
/// </summary>
LandscapeRight,
SensorPortrait,
/// <summary>
/// "reverseLandscape" mode
/// "sensorLandscape" mode
/// </summary>
LandscapeLeft,
SensorLandscape,
/// <summary>
/// "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}");
/// <summary>
/// The application version code (eg. 1, 12, 123).
/// </summary>
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"General\")")
String VersionCode = TEXT("1");
/// <summary>
/// The minimum Android API level (eg. 20, 28, 34).
/// </summary>
API_FIELD(Attributes = "EditorOrder(20), EditorDisplay(\"General\")")
String MinimumAPILevel = TEXT("23");
/// <summary>
/// The target Android API level (eg. 20, 28, 34).
/// </summary>
API_FIELD(Attributes = "EditorOrder(30), EditorDisplay(\"General\")")
String TargetAPILevel = TEXT("33");
/// <summary>
/// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.
/// </summary>

View File

@@ -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)

View File

@@ -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:

View File

@@ -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];

View File

@@ -0,0 +1,42 @@
using System;
namespace FlaxEngine;
/// <summary>
/// Used to add a watermark to a string textbox in the editor field
/// </summary>
[Serializable]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class WatermarkAttribute : Attribute
{
/// <summary>
/// The watermark text.
/// </summary>
public string WatermarkText;
/// <summary>
/// The watermark color.
/// </summary>
public uint WatermarkColor;
/// <summary>
/// Initializes a new instance of the <see cref="WatermarkAttribute"/> class.
/// </summary>
/// <param name="text">The watermark text.</param>
public WatermarkAttribute(string text)
{
WatermarkText = text;
WatermarkColor = 0; // default color of watermark in textbox
}
/// <summary>
/// Initializes a new instance of the <see cref="WatermarkAttribute"/> class.
/// </summary>
/// <param name="text">The watermark text.</param>
/// <param name="color">The watermark color. 0 to use default.</param>
public WatermarkAttribute(string text, uint color)
{
WatermarkText = text;
WatermarkColor = color;
}
}

View File

@@ -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

View File

@@ -45,6 +45,8 @@ public:
MMethod* Json_SerializeDiff;
MMethod* Json_Deserialize;
MClass* ManagedArrayClass;
#if USE_EDITOR
MClass* ExecuteInEditModeAttribute;
#endif

View File

@@ -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);

View File

@@ -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();

View File

@@ -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<void*, void*>(GetArrayLengthPtr, elementKlass->_handle);
static void* GetArrayTypeFromElementTypePtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType"));
MType* typeHandle = (MType*)CallStaticMethod<void*, void*>(GetArrayTypeFromElementTypePtr, elementKlass->_handle);
return GetOrCreateClass(typeHandle);
}
MClass* MCore::Array::GetArrayClass(const MArray* obj)
{
static void* GetArrayTypeFromWrappedArrayPtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromWrappedArray"));
MType* typeHandle = (MType*)CallStaticMethod<void*, void*>(GetArrayTypeFromWrappedArrayPtr, (void*)obj);
return GetOrCreateClass(typeHandle);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -17,6 +17,12 @@ namespace FlaxEngine
}
}
/// <summary>
/// Gets value indicating if the actor owning the script is in a scene.
/// </summary>
[HideInEditor, NoSerialize]
public bool HasScene => Actor?.HasScene ?? false;
/// <summary>
/// Gets or sets the world space transformation of the actors owning this script.
/// </summary>

View File

@@ -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<Terrain, &Terrain::DrawPhysicsDebug>(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();

View File

@@ -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()

View File

@@ -7,6 +7,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// UI canvas scaling component for user interface that targets multiple different game resolutions (eg. mobile screens).
/// </summary>
[ActorToolbox("GUI")]
public class CanvasScaler : ContainerControl
{
/// <summary>

View File

@@ -5,6 +5,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Border control that draws the border around the control edges (inner and outer sides).
/// </summary>
[ActorToolbox("GUI")]
public class Border : ContainerControl
{
/// <summary>

View File

@@ -7,6 +7,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Button control
/// </summary>
[ActorToolbox("GUI")]
public class Button : ContainerControl
{
/// <summary>

View File

@@ -29,6 +29,7 @@ namespace FlaxEngine.GUI
/// Check box control.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.Control" />
[ActorToolbox("GUI")]
public class CheckBox : Control
{
/// <summary>

View File

@@ -9,6 +9,7 @@ namespace FlaxEngine.GUI
/// Dropdown menu control allows to choose one item from the provided collection of options.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class Dropdown : ContainerControl
{
/// <summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// The basic GUI image control. Shows texture, sprite or render target.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class Image : ContainerControl
{
/// <summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// The basic GUI label control.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class Label : ContainerControl
{
/// <summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// Progress bar control shows visual progress of the action or set of actions.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.Control" />
[ActorToolbox("GUI")]
public class ProgressBar : ContainerControl
{
/// <summary>

View File

@@ -5,6 +5,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// 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.
/// </summary>
[ActorToolbox("GUI")]
public class RenderToTextureControl : ContainerControl
{
private bool _invalid, _redrawRegistered, _isDuringTextureDraw;

View File

@@ -7,6 +7,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Rich text box control which can gather text input from the user and present text in highly formatted and stylized way.
/// </summary>
[ActorToolbox("GUI")]
public partial class RichTextBox : RichTextBoxBase
{
private TextBlockStyle _textStyle;

View File

@@ -7,6 +7,7 @@ namespace FlaxEngine.GUI;
/// <summary>
/// The slider control.
/// </summary>
[ActorToolbox("GUI")]
public class Slider : ContainerControl
{
/// <summary>

View File

@@ -6,6 +6,7 @@ namespace FlaxEngine.GUI
/// Helper control used to insert blank space into the layout.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public sealed class Spacer : ContainerControl
{
/// <summary>

View File

@@ -5,6 +5,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Text box control which can gather text input from the user.
/// </summary>
[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);
}

View File

@@ -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;
}

View File

@@ -5,6 +5,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Changes alpha of all its children
/// </summary>
[ActorToolbox("GUI")]
public class AlphaPanel : ContainerControl
{
/// <summary>

View File

@@ -6,6 +6,7 @@ namespace FlaxEngine.GUI
/// The blur panel that applied the Gaussian-blur to all content beneath the control.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class BlurPanel : ContainerControl
{
/// <summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// Drop Panel arranges control vertically and provides feature to collapse contents.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class DropPanel : ContainerControl
{
/// <summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// A panel that divides up available space between all of its children.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class GridPanel : ContainerControl
{
private Margin _slotPadding;

View File

@@ -6,6 +6,7 @@ namespace FlaxEngine.GUI
/// This panel arranges child controls horizontally.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.PanelWithMargins" />
[ActorToolbox("GUI")]
public class HorizontalPanel : PanelWithMargins
{
/// <summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// Panel UI control.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ScrollableControl" />
[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;

View File

@@ -8,6 +8,7 @@ namespace FlaxEngine.GUI
/// Panel that arranges child controls like tiles.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class TilesPanel : ContainerControl
{
private Margin _tileMargin;

View File

@@ -6,6 +6,7 @@ namespace FlaxEngine.GUI
/// A panel that evenly divides up available space between all of its children.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[ActorToolbox("GUI")]
public class UniformGridPanel : ContainerControl
{
private Margin _slotPadding;

View File

@@ -6,6 +6,7 @@ namespace FlaxEngine.GUI
/// This panel arranges child controls vertically.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.PanelWithMargins" />
[ActorToolbox("GUI")]
public class VerticalPanel : PanelWithMargins
{
/// <summary>

View File

@@ -10,7 +10,7 @@
/// <summary>
/// Sprite rendering object.
/// </summary>
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);

View File

@@ -18,7 +18,7 @@
/// <summary>
/// Text rendering object.
/// </summary>
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);

View File

@@ -8,7 +8,7 @@
/// <summary>
/// Contains a single GUI control (on C# side).
/// </summary>
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);

View File

@@ -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}"

View File

@@ -5,9 +5,13 @@
<application android:label="@string/app_name"${AndroidAttributes}
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:icon="@mipmap/icon"
android:appCategory="game"
android:isGame="true"
android:hasFragileUserData="false"
android:extractNativeLibs="true"
android:hasCode="true">
<activity android:name="com.flaxengine.GameActivity"
android:exported="true"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="${DefaultOrientation}">