Merge branch 'master' into sdl_platform

# Conflicts:
#	Source/Editor/GUI/Docking/DockHintWindow.cs
#	Source/Editor/Options/InterfaceOptions.cs
This commit is contained in:
2025-09-07 19:02:09 +03:00
74 changed files with 1530 additions and 416 deletions

View File

@@ -641,5 +641,8 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
public bool LockSelection { get; set; }
/// <inheritdoc />
public ISceneEditingContext SceneContext => null;
}
}

View File

@@ -7,6 +7,7 @@ using FlaxEditor.Content.Create;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI;
using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
@@ -171,13 +172,49 @@ namespace FlaxEditor.Windows.Assets
}
private readonly SplitPanel _split;
private readonly ModelBasePreview _preview;
private readonly CollisionDataPreview _preview;
private readonly CustomEditorPresenter _propertiesPresenter;
private readonly PropertiesProxy _properties;
private Model _collisionWiresModel;
private StaticModel _collisionWiresShowActor;
private bool _updateWireMesh;
private class CollisionDataPreview : ModelBasePreview
{
public bool ShowCollisionData = false;
private int _verticesCount = 0;
private int _trianglesCount = 0;
public void SetVerticesAndTriangleCount(int verticesCount, int triangleCount)
{
_verticesCount = verticesCount;
_trianglesCount = triangleCount;
}
/// <inheritdoc />
public CollisionDataPreview(bool useWidgets)
: base(useWidgets)
{
ViewportCamera = new FPSCamera();
Task.ViewFlags &= ~ViewFlags.Sky & ~ViewFlags.Bloom & ~ViewFlags.EyeAdaptation;
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
if (ShowCollisionData)
{
var text = string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}\nMemory Size: {2}", _trianglesCount, _verticesCount, Utilities.Utils.FormatBytesCount(Asset.MemoryUsage));
var font = Style.Current.FontMedium;
var pos = new Float2(10, 50);
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black);
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White);
}
}
}
/// <inheritdoc />
public CollisionDataWindow(Editor editor, AssetItem item)
: base(editor, item)
@@ -185,6 +222,12 @@ namespace FlaxEditor.Windows.Assets
// Toolstrip
_toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole collision");
var infoButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64).LinkTooltip("Show Collision Data");
infoButton.Clicked += () =>
{
_preview.ShowCollisionData = !_preview.ShowCollisionData;
infoButton.Checked = _preview.ShowCollisionData;
};
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
// Split Panel
@@ -197,12 +240,10 @@ namespace FlaxEditor.Windows.Assets
};
// Model preview
_preview = new ModelBasePreview(true)
_preview = new CollisionDataPreview(true)
{
ViewportCamera = new FPSCamera(),
Parent = _split.Panel1
};
_preview.Task.ViewFlags &= ~ViewFlags.Sky & ~ViewFlags.Bloom & ~ViewFlags.EyeAdaptation;
// Asset properties
_propertiesPresenter = new CustomEditorPresenter(null);
@@ -240,7 +281,7 @@ namespace FlaxEditor.Windows.Assets
_collisionWiresModel = FlaxEngine.Content.CreateVirtualAsset<Model>();
_collisionWiresModel.SetupLODs(new[] { 1 });
}
Editor.Internal_GetCollisionWires(FlaxEngine.Object.GetUnmanagedPtr(Asset), out var triangles, out var indices, out var _, out var _);
Editor.Internal_GetCollisionWires(FlaxEngine.Object.GetUnmanagedPtr(Asset), out var triangles, out var indices, out var triangleCount, out var indicesCount);
if (triangles != null && indices != null)
_collisionWiresModel.LODs[0].Meshes[0].UpdateMesh(triangles, indices);
else
@@ -252,6 +293,7 @@ namespace FlaxEditor.Windows.Assets
}
_collisionWiresShowActor.Model = _collisionWiresModel;
_collisionWiresShowActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial));
_preview.SetVerticesAndTriangleCount(triangleCount, indicesCount / 3);
_preview.Asset = FlaxEngine.Content.LoadAsync<ModelBase>(_asset.Options.Model);
}

View File

@@ -430,6 +430,12 @@ namespace FlaxEditor.Windows.Assets
}
}
/// <inheritdoc />
public void DeleteSelection()
{
Delete();
}
/// <inheritdoc />
public void FocusSelection()
{
@@ -488,7 +494,8 @@ namespace FlaxEditor.Windows.Assets
/// <param name="actor">The actor.</param>
/// <param name="parent">The parent.</param>
/// <param name="orderInParent">The order of the actor under the parent.</param>
public void Spawn(Actor actor, Actor parent, int orderInParent = -1)
/// <param name="autoSelect">True if automatically select the spawned actor, otherwise false.</param>
public void Spawn(Actor actor, Actor parent, int orderInParent = -1, bool autoSelect = true)
{
if (actor == null)
throw new ArgumentNullException(nameof(actor));
@@ -514,8 +521,11 @@ namespace FlaxEditor.Windows.Assets
// Create undo action
var action = new CustomDeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
Undo.AddAction(action);
Focus();
Select(actorNode);
if (autoSelect)
{
Focus();
Select(actorNode);
}
}
private void OnTreeRightClick(TreeNode node, Float2 location)

View File

@@ -91,6 +91,9 @@ namespace FlaxEditor.Windows.Assets
}
}
/// <inheritdoc />
public ISceneEditingContext SceneContext => this;
/// <summary>
/// Gets or sets a value indicating whether use live reloading for the prefab changes (applies prefab changes on modification by auto).
/// </summary>

View File

@@ -194,17 +194,18 @@ namespace FlaxEditor.Windows
nodes.Add(node);
node = node.ParentNode;
}
float margin = 1;
float x = NavigationBar.DefaultButtonsMargin;
float h = _toolStrip.ItemsHeight - 2 * ToolStrip.DefaultMarginV;
float h = _toolStrip.ItemsHeight - 2 * margin;
for (int i = nodes.Count - 1; i >= 0; i--)
{
var button = new ContentNavigationButton(nodes[i], x, ToolStrip.DefaultMarginV, h);
var button = new ContentNavigationButton(nodes[i], x, margin, h);
button.PerformLayout();
x += button.Width + NavigationBar.DefaultButtonsMargin;
_navigationBar.AddChild(button);
if (i > 0)
{
var separator = new ContentNavigationSeparator(button, x, ToolStrip.DefaultMarginV, h);
var separator = new ContentNavigationSeparator(button, x, margin, h);
separator.PerformLayout();
x += separator.Width + NavigationBar.DefaultButtonsMargin;
_navigationBar.AddChild(separator);
@@ -215,6 +216,7 @@ namespace FlaxEditor.Windows
// Update
_navigationBar.IsLayoutLocked = wasLayoutLocked;
_navigationBar.PerformLayout();
UpdateNavigationBarBounds();
}
/// <summary>

View File

@@ -1016,6 +1016,21 @@ namespace FlaxEditor.Windows
_navigateUpButton.Enabled = folder != null && _tree.SelectedNode != _root;
}
private void UpdateNavigationBarBounds()
{
if (_navigationBar != null && _toolStrip != null)
{
var bottomPrev = _toolStrip.Bottom;
_navigationBar.UpdateBounds(_toolStrip);
if (bottomPrev != _toolStrip.Bottom)
{
// Navigation bar changed toolstrip height
_split.Offsets = new Margin(0, 0, _toolStrip.Bottom, 0);
PerformLayout();
}
}
}
/// <inheritdoc />
public override void OnInit()
{
@@ -1200,9 +1215,9 @@ namespace FlaxEditor.Windows
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
UpdateNavigationBarBounds();
_navigationBar?.UpdateBounds(_toolStrip);
base.PerformLayoutBeforeChildren();
}
/// <inheritdoc />

View File

@@ -58,6 +58,9 @@ namespace FlaxEditor.Windows
}
}
/// <inheritdoc />
public ISceneEditingContext SceneContext => Editor.Windows.EditWin;
/// <summary>
/// Initializes a new instance of the <see cref="PropertiesWindow"/> class.
/// </summary>

View File

@@ -26,12 +26,24 @@ namespace FlaxEditor.Windows
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
}
/// <inheritdoc />
public void DeleteSelection()
{
Editor.SceneEditing.Delete();
}
/// <inheritdoc />
public void FocusSelection()
{
Editor.Windows.EditWin.Viewport.FocusSelection();
}
/// <inheritdoc />
public void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true)
{
Editor.SceneEditing.Spawn(actor, parent, orderInParent, autoSelect);
}
/// <inheritdoc />
public EditorViewport Viewport => Editor.Windows.EditWin.Viewport;

View File

@@ -9,6 +9,7 @@
#include "Engine/Render2D/Font.h"
#include "Engine/Render2D/TextLayoutOptions.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Content/Content.h"
#include "FlaxEngine.Gen.h"
@@ -189,8 +190,7 @@ void SplashScreen::Show()
// Setup
_dpiScale = dpiScale;
_width = settings.Size.X;
_height = settings.Size.Y;
_size = settings.Size;
_startTime = DateTime::NowUTC();
auto str = Globals::ProjectFolder;
#if PLATFORM_WIN32
@@ -215,6 +215,12 @@ void SplashScreen::Show()
font->OnLoaded.Bind<SplashScreen, &SplashScreen::OnFontLoaded>(this);
}
// Load custom image
_splashTexture.Loaded.Bind<SplashScreen, &SplashScreen::OnSplashLoaded>(this);
String splashImagePath = Globals::ProjectContentFolder / TEXT("SplashImage.flax");
if (FileSystem::FileExists(splashImagePath))
_splashTexture = Content::LoadAsync<Texture>(splashImagePath);
_window->Show();
}
@@ -228,6 +234,10 @@ void SplashScreen::Close()
// Close window
_window->Close(ClosingReason::CloseEvent);
_window = nullptr;
_titleFont = nullptr;
_subtitleFont = nullptr;
_splashTexture = nullptr;
}
void SplashScreen::OnShown()
@@ -240,16 +250,29 @@ void SplashScreen::OnShown()
void SplashScreen::OnDraw()
{
const float s = _dpiScale;
const float width = _width;
const float height = _height;
const float width = _size.X;
const float height = _size.Y;
// Peek time
const float time = static_cast<float>((DateTime::NowUTC() - _startTime).GetTotalSeconds());
// Background
const float lightBarHeight = 112 * s;
Render2D::FillRectangle(Rectangle(0, 0, width, 150 * s), Color::FromRGB(0x1C1C1C));
Render2D::FillRectangle(Rectangle(0, lightBarHeight, width, height), Color::FromRGB(0x0C0C0C));
float lightBarHeight = 112 * s;
if (_splashTexture != nullptr)
{
if (_splashTexture->IsLoaded())
{
lightBarHeight = height - lightBarHeight + 20 * s;
Render2D::DrawTexture(_splashTexture, Rectangle(0, 0, width, height));
Color rectColor = Color::FromRGB(0x0C0C0C);
Render2D::FillRectangle(Rectangle(0, lightBarHeight, width, height - lightBarHeight),rectColor.AlphaMultiplied(0.85f), rectColor.AlphaMultiplied(0.85f), rectColor, rectColor);
}
}
else
{
Render2D::FillRectangle(Rectangle(0, 0, width, 150 * s), Color::FromRGB(0x1C1C1C));
Render2D::FillRectangle(Rectangle(0, lightBarHeight, width, height), Color::FromRGB(0x0C0C0C));
}
// Animated border
const float anim = Math::Sin(time * 4.0f) * 0.5f + 0.5f;
@@ -277,15 +300,27 @@ void SplashScreen::OnDraw()
for (int32 i = 0; i < 4 - static_cast<int32>(time * 2.0f) % 4; i++)
subtitle += TEXT(' ');
}
layout.Bounds = Rectangle(width - 224 * s, lightBarHeight - 39 * s, 220 * s, 35 * s);
if (_splashTexture != nullptr)
{
layout.Bounds = Rectangle(width - 224 * s, lightBarHeight + 4 * s, 220 * s, 35 * s);
layout.VerticalAlignment = TextAlignment::Near;
}
else
{
layout.Bounds = Rectangle(width - 224 * s, lightBarHeight - 39 * s, 220 * s, 35 * s);
layout.VerticalAlignment = TextAlignment::Far;
}
layout.Scale = 1.0f;
layout.HorizontalAlignment = TextAlignment::Far;
layout.VerticalAlignment = TextAlignment::Far;
Render2D::DrawText(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout);
// Additional info
const float infoMargin = 6 * s;
layout.Bounds = Rectangle(infoMargin, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin));
if (_splashTexture != nullptr)
layout.Bounds = Rectangle(infoMargin + 4 * s, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin));
else
layout.Bounds = Rectangle(infoMargin, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin));
layout.HorizontalAlignment = TextAlignment::Near;
layout.VerticalAlignment = TextAlignment::Center;
Render2D::DrawText(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout);
@@ -308,3 +343,14 @@ void SplashScreen::OnFontLoaded(Asset* asset)
_titleFont = font->CreateFont(35 * s);
_subtitleFont = font->CreateFont(9 * s);
}
void SplashScreen::OnSplashLoaded()
{
// Resize window to be larger if texture is being used
auto desktopSize = Platform::GetDesktopSize();
auto xSize = (desktopSize.X / (600.0f * 3.0f)) * 600.0f;
auto ySize = (desktopSize.Y / (200.0f * 3.0f)) * 200.0f;
_window->SetClientSize(Float2(xSize, ySize));
_size = _window->GetSize();
_window->SetPosition((desktopSize - _size) * 0.5f);
}

View File

@@ -2,6 +2,8 @@
#pragma once
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Platform/Window.h"
@@ -18,10 +20,12 @@ private:
Window* _window = nullptr;
Font* _titleFont = nullptr;
Font* _subtitleFont = nullptr;
AssetReference<Texture> _splashTexture;
String _title;
DateTime _startTime;
String _infoText;
float _dpiScale, _width, _height;
float _dpiScale;
Float2 _size;
StringView _quote;
public:
@@ -78,4 +82,5 @@ private:
void OnDraw();
bool HasLoadedFonts() const;
void OnFontLoaded(Asset* asset);
void OnSplashLoaded();
};

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEditor.GUI.Tabs;
using FlaxEditor.GUI.Tree;
@@ -98,10 +99,22 @@ namespace FlaxEditor.Windows
}
}
[Flags]
private enum SearchFilter
{
UI = 1,
Actors = 2,
Primitives = 4,
[HideInEditor]
Default = UI | Actors | Primitives,
}
private TextBox _searchBox;
private ContainerControl _groupSearch;
private Tabs _actorGroups;
private ContainerControl groupPrimitives;
private Button _viewDropdown;
private int _searchFilterMask = (int)SearchFilter.Default;
/// <summary>
/// The editor instance.
@@ -127,16 +140,25 @@ namespace FlaxEditor.Windows
UseScroll = true,
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
TabsSize = new Float2(120, 32),
TabsSize = new Float2(90, 32),
Parent = this,
};
_groupSearch = CreateGroupWithList(_actorGroups, "Search", 26);
_viewDropdown = new Button(2, 2, 45.0f, TextBoxBase.DefaultHeight)
{
TooltipText = "Change search filter options.",
Text = "Filters",
Parent = _groupSearch.Parent.Parent,
};
_viewDropdown.Clicked += OnViewButtonClicked;
_searchBox = new SearchBox
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Parent = _groupSearch.Parent.Parent,
Bounds = new Rectangle(4, 4, _actorGroups.Width - 8, 18),
Bounds = new Rectangle(_viewDropdown.Right + 2, 2, _actorGroups.Width - 4, TextBoxBase.DefaultHeight),
};
_searchBox.TextChanged += OnSearchBoxTextChanged;
@@ -145,10 +167,38 @@ namespace FlaxEditor.Windows
_actorGroups.SelectedTabIndex = 1;
}
private void OnViewButtonClicked()
{
var menu = new ContextMenu();
AddSearchFilterButton(menu, SearchFilter.UI, "UI");
AddSearchFilterButton(menu, SearchFilter.Actors, "Actors");
AddSearchFilterButton(menu, SearchFilter.Primitives, "Primitives");
menu.Show(_viewDropdown.Parent, _viewDropdown.BottomLeft);
}
private void AddSearchFilterButton(ContextMenu menu, SearchFilter value, string name)
{
var button = menu.AddButton(name);
button.AutoCheck = true;
button.Checked = (_searchFilterMask & (int)value) != 0;
button.Clicked += () =>
{
_searchFilterMask ^= (int)value;
OnSearchBoxTextChanged();
};
}
/// <inheritdoc/>
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
_searchBox.Width = _groupSearch.Width - _viewDropdown.Right - 4;
}
private void OnScriptsReload()
{
// Prevent any references to actor types from the game assemblies that will be reloaded
_searchBox.Clear();
_groupSearch.DisposeChildren();
_groupSearch.PerformLayout();
@@ -172,6 +222,7 @@ namespace FlaxEditor.Windows
private void OnScriptsReloadEnd()
{
RefreshActorTabs();
OnSearchBoxTextChanged();
}
private void RefreshActorTabs()
@@ -192,14 +243,21 @@ namespace FlaxEditor.Windows
group.Dispose();
}
// Setup primitives tabs
// Add primitives to primtives and search tab
groupPrimitives = CreateGroupWithList(_actorGroups, "Primitives");
groupPrimitives.AddChild(CreateEditorAssetItem("Cube", "Primitives/Cube.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Cube", "Primitives/Cube.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Sphere", "Primitives/Sphere.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Sphere", "Primitives/Sphere.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Plane", "Primitives/Plane.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Plane", "Primitives/Plane.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Cylinder", "Primitives/Cylinder.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Cylinder", "Primitives/Cylinder.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Cone", "Primitives/Cone.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Cone", "Primitives/Cone.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Capsule", "Primitives/Capsule.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Capsule", "Primitives/Capsule.flax"));
// Created first to order specific tabs
CreateGroupWithList(_actorGroups, "Lights");
@@ -312,57 +370,115 @@ namespace FlaxEditor.Windows
_groupSearch.LockChildrenRecursive();
_groupSearch.DisposeChildren();
foreach (var actorType in Editor.CodeEditing.Actors.Get())
if (((int)SearchFilter.Actors & _searchFilterMask) != 0)
{
ActorToolboxAttribute attribute = null;
foreach (var e in actorType.GetAttributes(false))
foreach (var actorType in Editor.CodeEditing.Actors.Get())
{
if (e is ActorToolboxAttribute actorToolboxAttribute)
ActorToolboxAttribute attribute = null;
foreach (var e in actorType.GetAttributes(false))
{
attribute = actorToolboxAttribute;
break;
if (e is ActorToolboxAttribute actorToolboxAttribute)
{
attribute = actorToolboxAttribute;
break;
}
}
}
var text = (attribute == null) ? actorType.Name : string.IsNullOrEmpty(attribute.Name) ? actorType.Name : attribute.Name;
var text = (attribute == null) ? actorType.Name : string.IsNullOrEmpty(attribute.Name) ? actorType.Name : attribute.Name;
// Display all actors on no search
if (string.IsNullOrEmpty(filterText))
_groupSearch.AddChild(CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType));
if (!QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
continue;
var item = CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType);
SearchFilterHighlights(item, text, ranges);
}
// Hack primitive models into the search results
foreach (var child in groupPrimitives.Children)
{
if (child is Item primitiveAssetItem)
{
var text = primitiveAssetItem.Text;
// Display all actors on no search
if (string.IsNullOrEmpty(filterText))
_groupSearch.AddChild(CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType));
if (!QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
continue;
// Rebuild the path based on item name (it would be better to convert the drag data back to a string somehow)
string path = $"Primitives/{text}.flax";
var item = CreateEditorAssetItem(text, path);
var item = CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType);
SearchFilterHighlights(item, text, ranges);
}
}
if (string.IsNullOrEmpty(filterText))
_groupSearch.SortChildren();
if (((int)SearchFilter.Primitives & _searchFilterMask) != 0)
{
// Hack primitive models into the search results
foreach (var child in groupPrimitives.Children)
{
if (child is Item primitiveAssetItem)
{
var text = primitiveAssetItem.Text;
// Rebuild the path based on item name (it would be better to convert the drag data back to a string somehow)
string path = $"Primitives/{text}.flax";
// Display all primitives on no search
if (string.IsNullOrEmpty(filterText))
_groupSearch.AddChild(CreateEditorAssetItem(text, path));
if (!QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
continue;
var item = CreateEditorAssetItem(text, path);
SearchFilterHighlights(item, text, ranges);
}
}
}
if (((int)SearchFilter.UI & _searchFilterMask) != 0)
{
foreach (var controlType in Editor.Instance.CodeEditing.Controls.Get())
{
if (controlType.IsAbstract)
continue;
ActorToolboxAttribute attribute = null;
foreach (var e in controlType.GetAttributes(false))
{
if (e is ActorToolboxAttribute actorToolboxAttribute)
{
attribute = actorToolboxAttribute;
break;
}
}
var text = (attribute == null) ? controlType.Name : string.IsNullOrEmpty(attribute.Name) ? controlType.Name : attribute.Name;
// Display all controls on no search
if (string.IsNullOrEmpty(filterText))
_groupSearch.AddChild(CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType));
if (!QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
continue;
var item = CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType);
SearchFilterHighlights(item, text, ranges);
}
}
// Sort the search results alphabetically
_groupSearch.SortChildren();
_groupSearch.UnlockChildrenRecursive();
PerformLayout();
PerformLayout();
}
/// <inheritdoc/>
public override void Draw()
{
base.Draw();
// Show a text to hint the user that either no filter is active or the search does not return any results
bool noSearchResults = _groupSearch.Children.Count == 0 && !string.IsNullOrEmpty(_searchBox.Text);
bool showHint = _searchFilterMask == 0 || noSearchResults;
if (showHint)
{
string hint = noSearchResults ? "No results" : "No search filter active, please enable at least one filter";
var textRect = _groupSearch.Parent.Parent.Bounds;
var style = Style.Current;
Render2D.DrawText(style.FontMedium, hint, textRect, style.ForegroundGrey, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords);
}
}
private void SearchFilterHighlights(Item item, string text, QueryFilterHelper.Range[] ranges)
{
_groupSearch.AddChild(item);