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

# Conflicts:
#	Content/Shaders/Editor/Grid.flax
#	Content/Shaders/GBuffer.flax
#	Content/Shaders/GlobalSignDistanceField.flax
#	Content/Shaders/ProbesFilter.flax
#	Content/Shaders/SSR.flax
#	Content/Shaders/VolumetricFog.flax
#	Development/Documentation/mono.md
#	Source/Editor/Modules/ContentDatabaseModule.cs
This commit is contained in:
2026-08-30 22:23:31 +02:00
34 changed files with 604 additions and 269 deletions
+16 -2
View File
@@ -1,8 +1,10 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor
@@ -89,8 +91,20 @@ namespace FlaxEditor
{
Editor.Instance.Windows.AddToRestore(this);
}
Window.Close();
Window.Dispose();
Window.Close(ClosingReason.ScriptsReload);
}
/// <summary>
/// Reattaches the window control to existing floating window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="state">Initial window state.</param>
/// <param name="toDock">The panel to dock to, if any.</param>
/// <param name="autoSelect">Only used if <paramref name="toDock"/> is set. If true the window will be selected after docking it.</param>
/// <param name="splitterValue">The splitter value to use if toDock is not null. If not specified, a default value will be used.</param>
public void Restore(IntPtr windowHandle, DockState state = DockState.Float, DockPanel toDock = null, bool autoSelect = true, float? splitterValue = null)
{
_win.Restore(windowHandle, state, toDock, autoSelect, splitterValue);
}
/// <summary>
@@ -322,7 +322,11 @@ namespace FlaxEditor.CustomEditors.Editors
// Deselect
if (_value != null && button1Rect.Contains(ref location))
{
Focus();
Value = null;
return true;
}
// Picker dropdown menu
if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location))
@@ -372,6 +376,18 @@ namespace FlaxEditor.CustomEditors.Editors
if (_hasValidDragOver)
_hasValidDragOver = false;
}
if (button == MouseButton.Middle)
{
// Clear value
Focus();
Value = null;
return true;
}
if (button == MouseButton.Right && _supportsPickDropDown)
{
// Show picker
ShowDropDownMenu();
}
return base.OnMouseUp(location, button);
}
+2
View File
@@ -642,9 +642,11 @@ int32 Editor::LoadProduct()
}
}
#if !FLAX_TESTS
// Update the last opened project path
if (lastProjectPath.Compare(Project->ProjectFolderPath) != 0)
File::WriteAllText(lastProjectSettingPath, Project->ProjectFolderPath, Encoding::UTF8);
#endif
return 0;
}
+13
View File
@@ -299,6 +299,7 @@ namespace FlaxEditor.GUI
}
else if (Button1Rect.Contains(location))
{
// Show picker
Focus();
OnSubmit();
}
@@ -318,6 +319,18 @@ namespace FlaxEditor.GUI
}
}
}
if (button == MouseButton.Middle && IconRect.Contains(ref location))
{
// Clear value
Focus();
Validator.SelectedItem = null;
}
if (button == MouseButton.Right && IconRect.Contains(ref location))
{
// Show picker
Focus();
OnSubmit();
}
// Handled
return true;
+50 -2
View File
@@ -1,7 +1,9 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Xml;
using System.Globalization;
using System.Linq;
using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI;
@@ -50,7 +52,7 @@ namespace FlaxEditor.GUI.Docking
/// <summary>
/// Gets a value indicating whether this window is docked.
/// </summary>
public bool IsDocked => _dockedTo != null;
public bool IsDocked => _dockedTo != null && _dockedTo.TabsCount > 1;
/// <summary>
/// Gets a value indicating whether this window is selected.
@@ -232,6 +234,46 @@ namespace FlaxEditor.GUI.Docking
}
}
/// <summary>
/// Reattaches the window control to existing floating window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="state">Initial window state.</param>
/// <param name="toDock">Panel to dock to it.</param>
/// <param name="autoSelect">Only used if <paramref name="toDock"/> is set. If true the window will be selected after docking it.</param>
/// <param name="splitterValue">Only used if <paramref name="toDock"/> is set. The splitter value to use. If not specified, a default value will be used.</param>
public void Restore(IntPtr windowHandle, DockState state = DockState.Float, DockPanel toDock = null, bool autoSelect = true, float? splitterValue = null)
{
if (state != DockState.Float)
{
Show(state, toDock, autoSelect, splitterValue);
return;
}
Undock();
// Find the existing window and remove all controls from it
var window = Editor.GetWindows().First(x => x.NativePtr == windowHandle);
var windowGUI = window.GUI;
while (windowGUI.Children.Count > 0)
windowGUI.Children[^1].Dispose();
windowGUI.EndTrackingMouse();
// Create dock panel for the window
var dockPanel = new FloatWindowDockPanel(_masterPanel, windowGUI);
dockPanel.DockWindowInternal(DockState.DockFill, this);
// Perform layout
Visible = true;
windowGUI.UnlockChildrenRecursive();
windowGUI.PerformLayout();
OnShow();
// Perform layout again
windowGUI.PerformLayout();
}
/// <summary>
/// Shows the window.
/// </summary>
@@ -332,6 +374,12 @@ namespace FlaxEditor.GUI.Docking
}
else
{
if (reason == ClosingReason.ScriptsReload && _dockedTo is FloatWindowDockPanel floatPanel)
{
// Unlink the window to keep it alive during scripts reload
floatPanel.UnlinkWindow();
}
// Undock
Undock();
@@ -488,7 +536,7 @@ namespace FlaxEditor.GUI.Docking
base.Focus();
SelectTab(false);
BringToFront();
_dockedTo?.RootWindow?.Focus();
}
/// <inheritdoc />
@@ -49,6 +49,7 @@ namespace FlaxEditor.GUI.Docking
private MasterDockPanel _masterPanel;
private WindowRootControl _window;
private bool _showDecorations;
/// <summary>
/// Gets the master panel.
@@ -65,6 +66,31 @@ namespace FlaxEditor.GUI.Docking
/// </summary>
public bool IsDragging { get; internal set; }
/// <summary>
/// Shows client-side window decorations around this panel.
/// </summary>
public bool ShowDecorations
{
get => _showDecorations;
set
{
if (value == _showDecorations)
return;
_showDecorations = value;
if (value)
{
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
}
else
{
var decorations = Parent.GetChild<FloatWindowDecorations>();
if (decorations != null)
decorations.Dispose();
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FloatWindowDockPanel"/> class.
/// </summary>
@@ -82,10 +108,16 @@ namespace FlaxEditor.GUI.Docking
_window.Window.Closing += OnClosing;
_window.Window.LeftButtonHit += OnLeftButtonHit;
if (Utilities.Utils.UseCustomWindowDecorations())
ShowDecorations = Utilities.Utils.UseCustomWindowDecorations();
}
internal void UnlinkWindow()
{
if (_window != null)
{
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
_window.Window.Closing -= OnClosing;
_window.Window.LeftButtonHit -= OnLeftButtonHit;
_window = null;
}
}
@@ -94,13 +126,10 @@ namespace FlaxEditor.GUI.Docking
{
base.PerformLayoutBeforeChildren();
var decorations = Parent.GetChild<FloatWindowDecorations>();
if (decorations != null)
{
// Apply offset for the title bar
foreach (var child in Children)
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
}
// Apply offset for the title bar
var decorationsHeight = Parent.GetChild<FloatWindowDecorations>()?.Height ?? 0;
foreach (var child in Children)
child.Bounds = child.Bounds with { Y = decorationsHeight, Height = Parent.Height - decorationsHeight };
}
/// <summary>
@@ -191,9 +220,7 @@ namespace FlaxEditor.GUI.Docking
}
// Unlink
_window.Window.Closing -= OnClosing;
_window.Window.LeftButtonHit = null;
_window = null;
UnlinkWindow();
// Remove object
FlaxEngine.Assertions.Assert.IsTrue(TabsCount == 0 && ChildPanelsCount == 0);
@@ -244,6 +271,7 @@ namespace FlaxEditor.GUI.Docking
{
_masterPanel?.FloatingPanels.Remove(this);
UnlinkWindow();
base.OnDestroy();
}
}
+80 -59
View File
@@ -748,21 +748,27 @@ namespace FlaxEditor.Modules
if (item.Path.Contains(".Build.cs", StringComparison.Ordinal) && item.ItemType == ContentItemType.Script)
Editor.Instance.CodeEditing.RemoveModule(item.Path);
// Check if it's an asset
if (item.IsAsset)
{
// Delete asset by using content pool
FlaxEngine.Content.DeleteAsset(path);
}
else if (item is ScriptItem)
{
FlaxEngine.Content.DeleteScript(path);
}
else if (deletedByUser)
{
// Delete file
if (File.Exists(path))
File.Delete(path);
// Delete asset file only if it was explicitly deleted by the user.
// Some applications might modify the file by deleting the original and replacing
// it with a new file which sometimes is caught in the middle of these operations.
if (deletedByUser)
{
// Check if it's an asset
if (item.IsAsset)
{
// Delete asset by using content pool
FlaxEngine.Content.DeleteAsset(path);
}
else if (item is ScriptItem)
{
FlaxEngine.Content.DeleteScript(path);
}
else
{
// Delete file
if (File.Exists(path))
File.Delete(path);
}
}
// Unlink from the parent
@@ -826,23 +832,29 @@ namespace FlaxEditor.Modules
var startTime = Platform.TimeSeconds;
_rebuildFlag = false;
_rebuildInitFlag = false;
_enableEvents = false;
// Load all folders
// TODO: we should create async task for gathering content and whole workspace contents if it takes too long
// TODO: create progress bar in content window and after end we should enable events and update it
_enableEvents = false;
_isDuringFastSetup = true;
var startItems = _itemsCreated;
foreach (var project in Projects)
try
{
if (project.Content != null)
LoadFolder(project.Content, true);
if (project.Source != null)
LoadFolder(project.Source, true);
// Load all folders
// TODO: we should create async task for gathering content and whole workspace contents if it takes too long
// TODO: create progress bar in content window and after end we should enable events and update it
foreach (var project in Projects)
{
if (project.Content != null)
LoadFolder(project.Content, true);
if (project.Source != null)
LoadFolder(project.Source, true);
}
}
_isDuringFastSetup = false;
_enableEvents = enableEvents;
finally
{
_isDuringFastSetup = false;
_enableEvents = enableEvents;
}
var endTime = Platform.TimeSeconds;
Editor.Log(string.Format("Project database created in {0} ms. Items count: {1}", (int)((endTime - startTime) * 1000.0), _itemsCreated - startItems));
Profiler.EndEvent();
@@ -1340,39 +1352,44 @@ namespace FlaxEditor.Modules
_enableEvents = false;
_isDuringFastSetup = true;
var startItems = _itemsCreated;
foreach (var project in Projects)
try
{
if (project.Content != null)
foreach (var project in Projects)
{
//Dispose(project.Content.Folder);
for (int i = 0; i < project.Content.Folder.Children.Count; i++)
if (project.Content != null)
{
Dispose(project.Content.Folder.Children[i]);
i--;
//Dispose(project.Content.Folder);
for (int i = 0; i < project.Content.Folder.Children.Count; i++)
{
Dispose(project.Content.Folder.Children[i]);
i--;
}
}
if (project.Source != null)
{
//Dispose(project.Source.Folder);
for (int i = 0; i < project.Source.Folder.Children.Count; i++)
{
Dispose(project.Source.Folder.Children[i]);
i--;
}
}
}
if (project.Source != null)
List<ContentProxy> removeProxies = new List<ContentProxy>();
foreach (var proxy in Editor.Instance.ContentDatabase.Proxy)
{
//Dispose(project.Source.Folder);
for (int i = 0; i < project.Source.Folder.Children.Count; i++)
{
Dispose(project.Source.Folder.Children[i]);
i--;
}
if (proxy.GetType().IsCollectible)
removeProxies.Add(proxy);
}
foreach (var proxy in removeProxies)
RemoveProxy(proxy, false);
}
List<ContentProxy> removeProxies = new List<ContentProxy>();
foreach (var proxy in Editor.Instance.ContentDatabase.Proxy)
finally
{
if (proxy.GetType().IsCollectible)
removeProxies.Add(proxy);
_isDuringFastSetup = false;
_enableEvents = enabledEvents;
}
foreach (var proxy in removeProxies)
RemoveProxy(proxy, false);
_isDuringFastSetup = false;
_enableEvents = enabledEvents;
}
private void OnScriptsReloadEnd()
@@ -1383,18 +1400,22 @@ namespace FlaxEditor.Modules
/// <inheritdoc />
public override void OnUpdate()
{
// Update all dirty content tree nodes
lock (_dirtyNodes)
// Check for updates only when the editor is focused to only check once for all changes done in background.
if (FlaxEngine.Engine.HasFocus)
{
Profiler.BeginEvent("ContentDatabase.Refresh");
foreach (var node in _dirtyNodes)
// Update all dirty content tree nodes
lock (_dirtyNodes)
{
LoadFolder(node, true);
Profiler.BeginEvent("ContentDatabase.Refresh");
foreach (var node in _dirtyNodes)
{
LoadFolder(node, true);
}
if (_enableEvents && _dirtyNodes.Count != 0)
WorkspaceModified?.Invoke();
_dirtyNodes.Clear();
Profiler.EndEvent();
}
if (_enableEvents && _dirtyNodes.Count != 0)
WorkspaceModified?.Invoke();
_dirtyNodes.Clear();
Profiler.EndEvent();
}
// Lazy-rebuilds
@@ -505,7 +505,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
base.OnUpdate();
// Automatic project files generation after workspace modifications
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty && !ScriptsBuilder.IsCompiling)
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty && !ScriptsBuilder.IsCompiling && Engine.HasFocus)
{
// Try to delay generation when a lot of files are added at once
if (ScriptsBuilder.IsSourceDirtyFor(TimeSpan.FromMilliseconds(150)))
+101 -113
View File
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
@@ -33,11 +34,12 @@ namespace FlaxEditor.Modules
private float _projectIconScreenshotTimeout = -1;
private string _windowsLayoutPath;
private struct WindowRestoreData
private class WindowRestoreData
{
public string AssemblyName;
public string TypeName;
public IntPtr WindowHandle;
public DockState DockState;
public DockPanel DockedTo;
public int DockedTabIndex;
@@ -45,20 +47,10 @@ namespace FlaxEditor.Modules
public bool SelectOnShow = false;
public bool Maximize;
public bool Minimize;
public Float2 FloatSize;
public Float2 FloatPosition;
public Guid AssetItemID;
// Constructor, to allow for default values
public WindowRestoreData()
{
}
}
private readonly List<WindowRestoreData> _restoreWindows = new List<WindowRestoreData>();
private readonly Dictionary<Guid, WindowRestoreData> _restoreWindows = new();
/// <summary>
/// The main editor window.
@@ -818,10 +810,14 @@ namespace FlaxEditor.Modules
internal void AddToRestore(AssetEditorWindow win)
{
AddToRestore(win, win.GetType(), new WindowRestoreData
var assetItemId = win.Item.ID;
if (!_restoreWindows.TryGetValue(assetItemId, out var winData))
{
AssetItemID = win.Item.ID,
});
winData = new WindowRestoreData();
_restoreWindows.Add(assetItemId, winData);
}
winData.AssetItemID = assetItemId;
AddToRestore(win, win.GetType(), winData);
}
internal void AddToRestore(CustomEditorWindow win)
@@ -832,62 +828,65 @@ namespace FlaxEditor.Modules
if (constructor == null || type.IsGenericType)
return;
AddToRestore(win.Window, type, new WindowRestoreData());
// TODO: Restore data for custom editors
var assetItemId = Guid.NewGuid();
if (!_restoreWindows.TryGetValue(assetItemId, out var winData))
{
winData = new WindowRestoreData();
_restoreWindows.Add(assetItemId, winData);
}
winData.AssetItemID = assetItemId;
AddToRestore(win.Window, type, winData);
}
private void AddToRestore(EditorWindow win, Type type, WindowRestoreData winData)
{
// Ensure that this window is only selected following recompilation
// if it was the active tab in its dock panel. Otherwise, there is a
// risk of interrupting the user's workflow by potentially selecting
// background tabs.
var window = win.RootWindow?.Window;
var panel = win.ParentDockPanel;
winData.SelectOnShow = panel.SelectedTab == win;
winData.DockedTabIndex = 0;
if (panel is FloatWindowDockPanel && window != null && panel.TabsCount == 1)
winData.AssemblyName = type.Assembly.GetName().Name;
winData.TypeName = type.FullName;
if (panel is FloatWindowDockPanel)
{
winData.DockState = DockState.Float;
winData.FloatPosition = window.Position;
winData.FloatSize = window.ClientSize;
winData.Maximize = window.IsMaximized;
winData.Minimize = window.IsMinimized;
winData.DockedTo = panel;
// Populate data for other tabs now, the tab index may change after tab is destroyed
if (winData.DockedTo == null)
{
for (int i = 0; i < panel.Tabs.Count; i++)
{
if (panel.Tabs[i] is AssetEditorWindow assetEditorWindow)
{
window ??= assetEditorWindow.RootWindow?.Window; // The window handle is sometimes missing in some tabs
var assetItemId = assetEditorWindow.Item.ID;
if (!_restoreWindows.TryGetValue(assetItemId, out var tabWinData))
{
tabWinData = new WindowRestoreData();
_restoreWindows.Add(assetItemId, tabWinData);
}
tabWinData.DockedTabIndex = i;
tabWinData.SelectOnShow = panel.SelectedTab == assetEditorWindow;
tabWinData.DockState = DockState.DockFill;
tabWinData.DockedTo = panel;
}
}
winData.DockState = DockState.Float;
winData.WindowHandle = window?.NativePtr ?? IntPtr.Zero;
}
}
else
{
for (int i = 0; i < panel.Tabs.Count; i++)
{
if (panel.Tabs[i] == win)
{
winData.DockedTabIndex = i;
break;
}
}
if (panel.TabsCount > 1)
{
winData.DockState = DockState.DockFill;
winData.DockedTo = panel;
}
else
{
winData.DockState = panel.TryGetDockState(out var splitterValue);
winData.DockedTo = panel.ParentDockPanel;
winData.SplitterValue = splitterValue;
}
winData.DockState = panel.TryGetDockState(out var splitterValue);
winData.DockedTo = panel.ParentDockPanel;
winData.SplitterValue = splitterValue;
}
winData.AssemblyName = type.Assembly.GetName().Name;
winData.TypeName = type.FullName;
_restoreWindows.Add(winData);
}
private void OnWorkspaceRebuilt()
{
// Go in reverse order to create floating Prefab windows first before docked windows
for (int i = _restoreWindows.Count - 1; i >= 0; i--)
var assetEditorWindows = new Dictionary<Guid, AssetEditorWindow>();
// Create float windows first, then add docked windows in order of the tabs
foreach (var (_, winData) in _restoreWindows)
{
var winData = _restoreWindows[i];
try
{
var assembly = Utils.GetAssemblyByName(winData.AssemblyName);
@@ -904,75 +903,24 @@ namespace FlaxEditor.Modules
var assetType = assetItem.GetType();
var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType });
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
var previouslyDockedTo = winData.DockedTo;
win.Restore(winData.WindowHandle, winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
if (winData.DockState == DockState.Float)
{
var window = win.RootWindow.Window;
window.Position = winData.FloatPosition;
if (winData.Maximize)
{
window.Maximize();
}
else if (winData.Minimize)
{
window.Minimize();
}
else
{
window.ClientSize = winData.FloatSize;
}
// Update panel reference in other windows docked to this panel
foreach (ref var otherData in CollectionsMarshal.AsSpan(_restoreWindows))
foreach (var key in _restoreWindows.Keys)
{
if (otherData.DockedTo == winData.DockedTo)
ref var otherData = ref CollectionsMarshal.GetValueRefOrNullRef(_restoreWindows, key);
if (otherData.DockedTo == previouslyDockedTo)
otherData.DockedTo = win.ParentDockPanel;
}
}
var panel = win.ParentDockPanel;
int currentTabIndex = 0;
for (int pi = 0; pi < panel.TabsCount; pi++)
{
if (panel.Tabs[pi] == win)
{
currentTabIndex = pi;
break;
}
}
while (currentTabIndex > winData.DockedTabIndex)
{
win.ParentDockPanel.MoveTabLeft(currentTabIndex);
currentTabIndex--;
}
while (currentTabIndex < winData.DockedTabIndex)
{
win.ParentDockPanel.MoveTabRight(currentTabIndex);
currentTabIndex++;
}
panel.PerformLayout(true);
assetEditorWindows.Add(winData.AssetItemID, win);
}
else
{
var win = (CustomEditorWindow)Activator.CreateInstance(type);
win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
if (winData.DockState == DockState.Float)
{
var window = win.Window.RootWindow.Window;
window.Position = winData.FloatPosition;
if (winData.Maximize)
{
window.Maximize();
}
else if (winData.Minimize)
{
window.Minimize();
}
else
{
window.ClientSize = winData.FloatSize;
}
}
win.Restore(winData.WindowHandle, winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
}
}
catch (Exception ex)
@@ -981,6 +929,46 @@ namespace FlaxEditor.Modules
Editor.LogWarning(string.Format("Failed to restore window {0} (assembly: {1})", winData.TypeName, winData.AssemblyName));
}
}
// Reorder tabs to previous order
foreach (var (_, winData) in _restoreWindows)
{
var win = assetEditorWindows.GetValueOrDefault(winData.AssetItemID);
if (win == null)
continue;
var panel = win.ParentDockPanel;
int currentTabIndex = 0;
for (int pi = 0; pi < panel.TabsCount; pi++)
{
if (panel.Tabs[pi] == win)
{
currentTabIndex = pi;
break;
}
}
while (currentTabIndex > winData.DockedTabIndex)
{
win.ParentDockPanel.MoveTabLeft(currentTabIndex);
currentTabIndex--;
}
while (currentTabIndex < winData.DockedTabIndex)
{
win.ParentDockPanel.MoveTabRight(currentTabIndex);
currentTabIndex++;
}
}
// Restore last selected tab
foreach (var (_, winData) in _restoreWindows)
{
var win = assetEditorWindows.GetValueOrDefault(winData.AssetItemID);
if (win != null && winData.SelectOnShow)
{
win.ParentDockPanel.SelectTab(win, false);
win.ParentDockPanel.PerformLayout(true);
}
}
// Restored windows stole the focus from Editor
if (_restoreWindows.Count > 0)
+2 -2
View File
@@ -159,11 +159,11 @@ namespace FlaxEditor.States
SceneDuplicated?.Invoke();
RestoreSelection();
Time.Synchronize(true);
Editor.OnPlayBegin();
IsPlayModeStarting = false;
Profiler.EndEvent();
Time.Synchronize(true);
}
private void SetupEditorEnvOptions()
+16 -10
View File
@@ -76,6 +76,11 @@ namespace FlaxEditor.Surface
/// The footer rectangle (local space).
/// </summary>
protected Rectangle _footerRect;
/// <summary>
/// The last mouse down position.
/// </summary>
protected Float2 _mouseDownMousePosition;
/// <summary>
/// The node archetype.
@@ -163,8 +168,6 @@ namespace FlaxEditor.Surface
/// </summary>
protected virtual Color ArchetypeColor => GroupArchetype.Color;
private Float2 mouseDownMousePosition;
/// <summary>
/// Calculates the size of the node including header, footer, and margins.
/// </summary>
@@ -1166,10 +1169,9 @@ namespace FlaxEditor.Surface
if (base.OnMouseDown(location, button))
return true;
_mouseDownMousePosition = location;
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
return true;
if (button == MouseButton.Right)
mouseDownMousePosition = Input.Mouse.Position;
return false;
}
@@ -1180,18 +1182,22 @@ namespace FlaxEditor.Surface
if (base.OnMouseUp(location, button))
return true;
// Close/ delete
bool canDelete = !Surface.IsConnecting && !Surface.WasSelecting && !Surface.WasMovingSelection;
if (button == MouseButton.Left && canDelete && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
if (button == MouseButton.Left)
{
Surface.Delete(this);
return true;
// Close/delete
bool canDelete = !Surface.IsConnecting && !Surface.WasSelecting && !Surface.WasMovingSelection;
if (canDelete && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 &&
_closeButtonRect.Contains(ref location) && _closeButtonRect.Contains(ref _mouseDownMousePosition))
{
Surface.Delete(this);
return true;
}
}
// Secondary Context Menu
if (button == MouseButton.Right)
{
float distance = Float2.Distance(mouseDownMousePosition, Input.Mouse.Position);
float distance = Float2.Distance(_mouseDownMousePosition, location);
if (distance > 2.5f)
return true;
@@ -287,6 +287,7 @@ namespace FlaxEditor.Surface
// Reset instigator list
_connectionInstigators.Clear();
EndMouseCapture();
}
}
}
@@ -177,7 +177,7 @@ namespace FlaxEditor.Windows.Assets
Save();
}
Editor.Instance.Windows.AddToRestore(this);
Close();
Close(ClosingReason.ScriptsReload);
}
}
@@ -274,7 +274,7 @@ namespace FlaxEditor.Windows.Assets
base.OnScriptsReloadBegin();
// TODO: impl hot-reload for BT to nicely refresh state (save asset, clear undo/properties, reload surface)
Close();
Close(ClosingReason.ScriptsReload);
}
private void UpdateKnowledge()
@@ -148,7 +148,7 @@ namespace FlaxEditor.Windows.Assets
protected override void OnScriptsReloadBegin()
{
base.OnScriptsReloadBegin();
Close();
Close(ClosingReason.ScriptsReload);
}
/// <inheritdoc />
+1 -1
View File
@@ -366,7 +366,7 @@ namespace FlaxEditor.Windows.Assets
_viewport.Prefab = null;
_undo?.Clear(); // TODO: maybe don't clear undo?
Close();
Close(ClosingReason.ScriptsReload);
}
private void OnUndoEvent(IUndoAction action)
+24 -9
View File
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Xml;
using FlaxEditor.Gizmo;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Docking;
using FlaxEditor.GUI.Input;
using FlaxEditor.Modules;
using FlaxEditor.Options;
@@ -132,6 +133,8 @@ namespace FlaxEditor.Windows
private float _gameStartTime;
private GUI.Docking.DockState _maximizeRestoreDockState;
private GUI.Docking.DockPanel _maximizeRestoreDockTo;
private GUI.Docking.DockPanel _maximizeRestoreDockToParent;
private float _maximizeRestoreSplitterValue;
private CursorLockMode _cursorLockMode = CursorLockMode.None;
// Viewport scaling variables
@@ -295,7 +298,10 @@ namespace FlaxEditor.Windows
if (value)
{
_maximizeRestoreDockTo = _dockedTo;
_maximizeRestoreDockState = _dockedTo.TryGetDockState(out _);
_maximizeRestoreDockToParent = _dockedTo.ParentDockPanel;
_maximizeRestoreDockState = _dockedTo.TryGetDockState(out _maximizeRestoreSplitterValue);
if (_dockedTo.Tabs.Count > 1)
_maximizeRestoreDockState = DockState.DockFill;
if (_maximizeRestoreDockState != GUI.Docking.DockState.Float)
{
var monitorBounds = Platform.GetMonitorBounds(PointToScreen(Size * 0.5f));
@@ -309,9 +315,11 @@ namespace FlaxEditor.Windows
// Restore
if (rootWindow != null)
rootWindow.Restore();
if (_maximizeRestoreDockTo != null && _maximizeRestoreDockTo.IsDisposing)
_maximizeRestoreDockTo = null;
Show(_maximizeRestoreDockState, _maximizeRestoreDockTo);
var dockTo = _maximizeRestoreDockTo;
if (dockTo != null && dockTo.IsDisposing)
dockTo = _maximizeRestoreDockToParent;
if (_dockedTo != dockTo)
Show(_maximizeRestoreDockState, dockTo, splitterValue: _maximizeRestoreSplitterValue);
}
}
}
@@ -331,6 +339,9 @@ namespace FlaxEditor.Windows
{
IsFloating = true;
var rootWindow = RootWindow;
var floatWin = rootWindow.GetChild<FloatWindowDockPanel>();
if (floatWin != null && floatWin.ShowDecorations)
floatWin.ShowDecorations = false;
var monitorBounds = Platform.GetMonitorBounds(rootWindow.RootWindow.Window.ClientPosition);
rootWindow.Window.Position = monitorBounds.Location;
rootWindow.Window.SetBorderless(true);
@@ -338,6 +349,9 @@ namespace FlaxEditor.Windows
}
else
{
var floatWin = RootWindow.GetChild<FloatWindowDockPanel>();
if (floatWin != null && !floatWin.ShowDecorations && Utilities.Utils.UseCustomWindowDecorations())
floatWin.ShowDecorations = true;
IsFloating = false;
}
}
@@ -658,9 +672,10 @@ namespace FlaxEditor.Windows
IsBorderless = false;
Cursor = CursorType.Default;
Screen.CursorLock = CursorLockMode.None;
if (Screen.MainWindow.IsMouseTracking)
Screen.MainWindow.EndTrackingMouse();
RootControl.GameRoot.EndMouseCapture();
var mainWindow = Screen.MainWindow;
if (mainWindow != null && mainWindow.IsMouseTracking)
mainWindow.EndTrackingMouse();
RootControl.GameRoot?.EndMouseCapture();
}
/// <inheritdoc />
@@ -899,7 +914,7 @@ namespace FlaxEditor.Windows
/// </summary>
public void FocusGameViewport()
{
if (!IsDocked)
if (ParentDockPanel == null)
{
ShowFloating();
}
@@ -1004,7 +1019,7 @@ namespace FlaxEditor.Windows
Screen.CursorVisible = true;
Screen.CursorLock = CursorLockMode.None;
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
if (Editor.IsPlayMode && ParentDockPanel != null && IsSelected && RootWindow.FocusedControl == null)
{
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
@@ -122,11 +122,8 @@ FlaxStorageReference ContentStorageManager::EnsureAccess(const StringView& path)
// Note: because we want to create new storage package it may exists.
// So let's check if any storage container is referencing that location and try to close it.
auto storage = TryGetStorage(path);
if (storage && storage->IsLoaded())
{
LOG(Info, "File \'{0}\' is in use. Trying to release handle to it.", path);
storage->CloseFileHandles();
}
if (storage && storage->IsLoaded() && storage->CloseFileHandles())
LOG(Warning, "Cannot release content storage handle for '{0}'.", path);
return storage;
}
@@ -1438,10 +1438,6 @@ FileReadStream* FlaxStorage::OpenFile()
bool FlaxStorage::CloseFileHandles()
{
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
Platform::InterlockedIncrement(&_isUnloadingData);
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
return false; // Early out when no files are opened
PROFILE_CPU();
@@ -1469,7 +1465,13 @@ bool FlaxStorage::CloseFileHandles()
}
}
}
waitTime = 100;
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
Platform::InterlockedIncrement(&_isUnloadingData);
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
// Wait for chunks lock again (with larger timeout for longer tasks)
waitTime = 1000;
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
Platform::Sleep(1);
if (Platform::AtomicRead(&_chunksLock) != 0)
@@ -901,20 +901,21 @@ namespace FlaxEngine.Interop
Assembly assembly;
#if FLAX_EDITOR
// Load assembly from loaded bytes to prevent file locking in Editor
var assemblyBytes = File.ReadAllBytes(assemblyPath);
using MemoryStream stream = new MemoryStream(assemblyBytes);
// Load assembly with stream to prevent runtime from locking the assembly file
using FileStream stream = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read);
var pdbPath = Path.ChangeExtension(assemblyPath, "pdb");
if (File.Exists(pdbPath))
{
// Load including debug symbols
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open);
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open, FileAccess.Read);
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
}
else
{
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream);
}
// TODO: Use new .NET 11 AssemblyLoadContext.SetAssemblyLocationOverride to specify correct Assembly.Location
#else
// Load assembly from file
assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
+1 -1
View File
@@ -197,7 +197,7 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* ve
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
{
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R16_UInt, vertices, triangles, normals, tangents, uvs, colors);
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R32_UInt, vertices, triangles, normals, tangents, uvs, colors);
}
bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer)
@@ -305,7 +305,7 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Flo
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Int4* blendIndices, const Float4* blendWeights, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
{
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R16_UInt, vertices, triangles, blendIndices, blendWeights, normals, tangents, uvs, colors);
return ::UpdateMesh(this, vertexCount, triangleCount, PixelFormat::R32_UInt, vertices, triangles, blendIndices, blendWeights, normals, tangents, uvs, colors);
}
void SkinnedMesh::Draw(const RenderContext& renderContext, const SkinnedMeshBones& pose, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const
+5
View File
@@ -28,6 +28,11 @@ API_ENUM() enum class ClosingReason
/// The close event.
/// </summary>
CloseEvent,
/// <summary>
/// The scripts reload event.
/// </summary>
ScriptsReload,
};
/// <summary>
@@ -26,6 +26,8 @@
#if USE_EDITOR
#define COMPILE_WITH_ASSETS_IMPORTER 1 // Hack to use shaders importing in this module
#include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/Content/Storage/ContentStorageManager.h"
#include "Engine/Utilities/Encryption.h"
#include "Engine/Platform/FileSystemWatcher.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/File.h"
@@ -499,6 +501,37 @@ String ShadersCompilation::CompactShaderPath(StringView path)
#if USE_EDITOR
bool ShadersCompilation::IsShaderSourceAssetUpToDate(const StringView& sourcePath, const StringView& assetPath)
{
PROFILE_CPU();
StringAnsi source;
if (File::ReadAllText(sourcePath, source))
return false;
if (!source.HasChars() || source[source.Length() - 1] != '\n')
source.Append('\n');
const auto storage = ContentStorageManager::GetStorage(assetPath);
AssetInitData data;
if (!storage
|| storage->GetEntriesCount() != 1
|| storage->GetEntry(0).TypeName != Shader::TypeName
|| storage->LoadAssetHeader(0, data)
|| data.SerializedVersion != Shader::SerializedVersion)
return false;
FlaxChunk* sourceChunk = data.Header.Chunks[SHADER_FILE_CHUNK_SOURCE];
if (!sourceChunk || storage->LoadAssetChunk(sourceChunk) || !sourceChunk->Data.IsValid())
return false;
BytesContainer embeddedSource;
embeddedSource.Copy(sourceChunk->Data);
if (embeddedSource.Length() != source.Length() + 1)
return false;
Encryption::DecryptBytes(embeddedSource.Get(), embeddedSource.Length());
embeddedSource.Get()[embeddedSource.Length() - 1] = 0;
return Platform::MemoryCompare(embeddedSource.Get(), source.Get(), source.Length()) == 0;
}
namespace
{
Array<FileSystemWatcher*> ShadersSourcesWatchers;
@@ -515,6 +548,13 @@ namespace
return result;
}
bool ImportShaderIfChanged(const StringView& sourcePath, const StringView& assetPath, Guid& assetId)
{
if (ShadersCompilation::IsShaderSourceAssetUpToDate(sourcePath, assetPath))
return false;
return AssetsImportingManager::Import(sourcePath, assetPath, assetId);
}
void OnWatcherShadersEvent(const String& path, FileSystemAction action)
{
if (action == FileSystemAction::Delete || !path.EndsWith(TEXT(".shader")))
@@ -538,7 +578,7 @@ namespace
const String name = StringUtils::GetPathWithoutExtension(localPath);
const String outputPath = shadersAssetsPath / name + ASSET_FILES_EXTENSION_WITH_DOT;
Guid id = GetShaderAssetId(name);
AssetsImportingManager::ImportIfEdited(path, outputPath, id);
ImportShaderIfChanged(path, outputPath, id);
}
void RegisterShaderWatchers(const ProjectInfo* project, HashSet<const ProjectInfo*>& projects)
@@ -567,7 +607,7 @@ namespace
const String name = StringUtils::GetPathWithoutExtension(localPath);
const String outputPath = shadersAssetsPath / name + ASSET_FILES_EXTENSION_WITH_DOT;
Guid id = GetShaderAssetId(name);
AssetsImportingManager::ImportIfEdited(path, outputPath, id);
ImportShaderIfChanged(path, outputPath, id);
}
}
@@ -47,6 +47,16 @@ public:
// Compacts the full shader file path into portable format with project name prefix such as './<ProjectName>/ShaderFile.hlsl'.
static String CompactShaderPath(StringView path);
#if USE_EDITOR
/// <summary>
/// Checks whether a shader asset embeds the current source file contents.
/// </summary>
/// <param name="sourcePath">The shader source file path.</param>
/// <param name="assetPath">The shader asset file path.</param>
/// <returns>True when the embedded source matches, otherwise false.</returns>
static bool IsShaderSourceAssetUpToDate(const StringView& sourcePath, const StringView& assetPath);
#endif
private:
static ShaderCompiler* RequestCompiler(ShaderProfile profile, PlatformType platform);
static void FreeCompiler(ShaderCompiler* compiler);
@@ -26,6 +26,29 @@ namespace FlaxEngine.Tests
}
}
private sealed class RemovingControl : MyControl
{
private readonly Control[] _controlsToRemove;
public RemovingControl(float x, float y, float width, float height, params Control[] controlsToRemove)
: base(x, y, width, height)
{
_controlsToRemove = controlsToRemove;
}
public override void OnMouseEnter(Float2 location)
{
for (int i = 0; i < _controlsToRemove.Length; i++)
{
var control = _controlsToRemove[i];
if (control.Parent == Parent)
control.Parent = null;
}
base.OnMouseEnter(location);
}
}
[Test]
public void TestChildren()
{
@@ -53,6 +76,25 @@ namespace FlaxEngine.Tests
Assert.AreEqual(cc1.GetChildAt(new Vector2(15, 5)), cc2);
Assert.AreEqual(cc1.GetChildAtRecursive(new Vector2(35, 25)), c3);
}
[Test]
public void TestMouseMoveAllowsChildrenRemoval()
{
var container = new MyContainerControl(0, 0, 100, 100);
var first = new MyControl(0, 0, 100, 100);
var second = new MyControl(0, 0, 100, 100);
var removing = new RemovingControl(0, 0, 100, 100, first, second);
container.AddChild(first);
container.AddChild(second);
container.AddChild(removing);
// The top-most child removes multiple siblings during input dispatch.
// Traversal must not use the now-stale next index.
container.OnMouseMove(new Float2(50, 50));
Assert.AreEqual(1, container.ChildrenCount);
Assert.AreEqual(removing, container.GetChild(0));
}
}
}
#endif
@@ -0,0 +1,42 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "Engine/Core/ScopeExit.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/ShadersCompilation/ShadersCompilation.h"
#include <ThirdParty/catch2/catch.hpp>
#if COMPILE_WITH_SHADER_COMPILER && USE_EDITOR
TEST_CASE("Shader source asset synchronization ignores timestamps")
{
const String sourcePath = Globals::StartupFolder / TEXT("Source/Shaders/VolumetricFog.shader");
const String assetPath = Globals::EngineContentFolder / TEXT("Shaders/VolumetricFog.flax");
REQUIRE(FileSystem::FileExists(sourcePath));
REQUIRE(FileSystem::FileExists(assetPath));
CHECK(ShadersCompilation::IsShaderSourceAssetUpToDate(sourcePath, assetPath));
const String tempRoot = Globals::TemporaryFolder / (TEXT("ShaderSourceSync-") + Guid::New().ToString(Guid::FormatType::N));
REQUIRE(!FileSystem::CreateDirectory(tempRoot));
SCOPE_EXIT
{
FileSystem::DeleteDirectory(tempRoot, true);
};
DataContainer<byte> assetData;
StringAnsi modifiedSource;
REQUIRE(!File::ReadAllBytes(assetPath, assetData));
REQUIRE(!File::ReadAllText(sourcePath, modifiedSource));
modifiedSource.Append("// Deliberately different source\n");
const String tempSourcePath = tempRoot / TEXT("VolumetricFog.shader");
const String tempAssetPath = tempRoot / TEXT("VolumetricFog.flax");
REQUIRE(!File::WriteAllBytes(tempSourcePath, modifiedSource.Get(), modifiedSource.Length()));
REQUIRE(!File::WriteAllBytes(tempAssetPath, assetData.Get(), assetData.Length()));
CHECK_FALSE(FileSystem::GetFileLastEditTime(tempSourcePath) > FileSystem::GetFileLastEditTime(tempAssetPath));
CHECK_FALSE(ShadersCompilation::IsShaderSourceAssetUpToDate(tempSourcePath, tempAssetPath));
}
#endif
+1
View File
@@ -21,6 +21,7 @@ public class Tests : EngineModule
base.Setup(options);
options.PrivateDependencies.Add("ModelTool");
options.PrivateDependencies.Add("ShadersCompilation");
}
/// <inheritdoc />
@@ -20,26 +20,29 @@
#include <ThirdParty/assimp/scene.h>
#include <ThirdParty/assimp/version.h>
#include <ThirdParty/assimp/postprocess.h>
#include <ThirdParty/assimp/LogStream.hpp>
#include <ThirdParty/assimp/DefaultLogger.hpp>
#include <ThirdParty/assimp/Logger.hpp>
class AssimpLogStream : public Assimp::LogStream
class AssimpLogger final : public Assimp::Logger
{
public:
AssimpLogStream()
AssimpLogger()
: Logger(NORMAL)
{
Assimp::DefaultLogger::create("");
Assimp::DefaultLogger::get()->attachStream(this);
}
~AssimpLogStream()
bool attachStream(Assimp::LogStream*, unsigned int) override
{
Assimp::DefaultLogger::get()->detachStream(this);
Assimp::DefaultLogger::kill();
return false;
}
void write(const char* message) override
bool detachStream(Assimp::LogStream*, unsigned int) override
{
return false;
}
private:
static void Write(const Char* type, const char* message)
{
String s(message);
if (s.Length() <= 0)
@@ -52,10 +55,49 @@ public:
else if (c >= 255)
c = '?';
}
LOG(Info, "[Assimp]: {0}", s);
LOG(Info, "[Assimp]: {0}: {1}", type, s);
}
void OnDebug(const char* message) override
{
if (m_Severity >= DEBUGGING)
Write(TEXT("Debug"), message);
}
void OnVerboseDebug(const char* message) override
{
if (m_Severity >= VERBOSE)
Write(TEXT("Debug"), message);
}
void OnInfo(const char* message) override
{
Write(TEXT("Info"), message);
}
void OnWarn(const char* message) override
{
Write(TEXT("Warn"), message);
}
void OnError(const char* message) override
{
Write(TEXT("Error"), message);
}
};
Assimp::Logger* GetAssimpLogger()
{
static Assimp::Logger* logger = []()
{
auto result = new AssimpLogger();
Assimp::DefaultLogger::set(result);
LOG(Info, "Assimp {0}.{1}.{2}", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
return result;
}();
return logger;
}
Float2 ToFloat2(const aiVector2D& v)
{
return Float2(v.x, v.y);
@@ -148,7 +190,6 @@ struct AssimpBone
struct AssimpImporterData
{
Assimp::Importer AssimpImporter;
AssimpLogStream AssimpLogStream;
const String Path;
const aiScene* Scene = nullptr;
const ModelTool::Options& Options;
@@ -705,12 +746,7 @@ void ImportAnimation(int32 index, ModelData& data, AssimpImporterData& importerD
bool ModelTool::ImportDataAssimp(const String& path, ModelData& data, Options& options, String& errorMsg)
{
static bool AssimpInited = false;
if (!AssimpInited)
{
AssimpInited = true;
LOG(Info, "Assimp {0}.{1}.{2}", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
}
GetAssimpLogger();
bool importMeshes = EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry);
bool importAnimations = EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Animations);
AssimpImporterData context(path, options);
+14 -14
View File
@@ -910,7 +910,7 @@ namespace FlaxEngine.GUI
return false;
}
}
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible)
@@ -928,7 +928,7 @@ namespace FlaxEngine.GUI
public override void OnMouseEnter(Float2 location)
{
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -948,7 +948,7 @@ namespace FlaxEngine.GUI
public override void OnMouseMove(Float2 location)
{
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -998,7 +998,7 @@ namespace FlaxEngine.GUI
public override bool OnMouseWheel(Float2 location, float delta)
{
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1019,7 +1019,7 @@ namespace FlaxEngine.GUI
public override bool OnMouseDown(Float2 location, MouseButton button)
{
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1040,7 +1040,7 @@ namespace FlaxEngine.GUI
public override bool OnMouseUp(Float2 location, MouseButton button)
{
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1061,7 +1061,7 @@ namespace FlaxEngine.GUI
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1096,7 +1096,7 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override void OnTouchEnter(Float2 location, int pointerId)
{
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled && !child.IsTouchPointerOver(pointerId))
@@ -1114,7 +1114,7 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override bool OnTouchDown(Float2 location, int pointerId)
{
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1139,7 +1139,7 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override void OnTouchMove(Float2 location, int pointerId)
{
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1168,7 +1168,7 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override bool OnTouchUp(Float2 location, int pointerId)
{
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled && child.IsTouchPointerOver(pointerId))
@@ -1250,7 +1250,7 @@ namespace FlaxEngine.GUI
var result = base.OnDragEnter(ref location, data);
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1275,7 +1275,7 @@ namespace FlaxEngine.GUI
var result = base.OnDragMove(ref location, data);
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
@@ -1333,7 +1333,7 @@ namespace FlaxEngine.GUI
var result = base.OnDragDrop(ref location, data);
// Check all children collisions with mouse and fire events for them
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
for (int i = _children.Count - 1; i >= 0 && i < _children.Count; i--)
{
var child = _children[i];
if (child.Visible && child.Enabled)
+1
View File
@@ -42,6 +42,7 @@ META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
VertexOutput VS_Grid(ModelInput input)
{
VertexOutput output;
input.Position.xyz += float3(ViewPos.x, 0, ViewPos.z);
output.WorldPosition = input.Position.xyz + ViewOrigin;
float3 geoPosition = input.Position.xyz - float3(0, ViewOrigin.y, 0);
output.Position = mul(float4(geoPosition, 1), ViewProjectionMatrix);
@@ -136,7 +136,7 @@ namespace Flax.Build
/// <summary>
/// The maximum SDK version.
/// </summary>
public static Version MaximumVersion => new Version(10, 0);
public static Version MaximumVersion => new Version(8, 0);
/// <inheritdoc />
public override TargetPlatform[] Platforms
@@ -110,6 +110,11 @@ namespace Flax.Build.NativeCpp
return libFolder;
// Try to find nearest framework folder
if (Framework.StartsWith("netcoreapp"))
{
var version = System.Version.Parse(Framework.Substring(10));
return string.Empty;
}
if (Framework.StartsWith("net"))
{
var baseVersion = int.Parse(Framework.Substring(3, Framework.IndexOf('.') - 3));
@@ -144,7 +149,10 @@ namespace Flax.Build.NativeCpp
var dlls = Directory.Exists(libFolder) ? Directory.GetFiles(libFolder, "*.dll", SearchOption.TopDirectoryOnly) : [];
if (dlls.Length == 0)
{
Log.Error($"Missing NuGet package \"{Name}, {Version}, {Framework}\" binaries (folder: {libFolder})");
if (!File.Exists(Path.Combine(libFolder, "_._"))) // Skip error for packages without a binary (eg. Microsoft.NET.Test.Sdk)
{
Log.Error($"Missing NuGet package \"{Name}, {Version}, {Framework}\" binaries (folder: {libFolder})");
}
return string.Empty;
}
return dlls[0];
@@ -330,9 +330,7 @@ namespace Flax.Build.Projects.VisualStudio
foreach (var analyzer in configuration.TargetBuildOptions.ScriptingAPI.Analyzers)
{
csProjectFileContent.AppendLine(string.Format(" <ItemGroup Condition=\" '$(Configuration)|$(Platform)' == '{0}' \">", configuration.Name));
csProjectFileContent.AppendLine(string.Format(" <Analyzer Include=\"{0}\">", Path.GetFileNameWithoutExtension(analyzer)));
csProjectFileContent.AppendLine(string.Format(" <HintPath>{0}</HintPath>", Utilities.MakePathRelativeTo(analyzer, projectDirectory).Replace('/', '\\')));
csProjectFileContent.AppendLine(" </Analyzer>");
csProjectFileContent.AppendLine(string.Format(" <Analyzer Include=\"{0}\" />", Utilities.MakePathRelativeTo(analyzer, projectDirectory).Replace('/', '\\')));
csProjectFileContent.AppendLine(" </ItemGroup>");
}