Merge branch 'ToolboxSearchFilterAndControls' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ToolboxSearchFilterAndControls

This commit is contained in:
Wojtek Figat
2025-08-25 14:49:00 +02:00

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Input;
using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tabs;
using FlaxEditor.GUI.Tree; using FlaxEditor.GUI.Tree;
@@ -98,10 +99,20 @@ namespace FlaxEditor.Windows
} }
} }
private enum SearchFilter
{
Ui = 1,
Actors = 2,
Primitives = 4,
}
private TextBox _searchBox; private TextBox _searchBox;
private ContainerControl _groupSearch; private ContainerControl _groupSearch;
private Tabs _actorGroups; private Tabs _actorGroups;
private ContainerControl groupPrimitives; private ContainerControl groupPrimitives;
private Button _viewDropdown;
private int _searchFilterMask = (int)SearchFilter.Ui | (int)SearchFilter.Actors | (int)SearchFilter.Primitives;
/// <summary> /// <summary>
/// The editor instance. /// The editor instance.
@@ -127,16 +138,25 @@ namespace FlaxEditor.Windows
UseScroll = true, UseScroll = true,
AnchorPreset = AnchorPresets.StretchAll, AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero, Offsets = Margin.Zero,
TabsSize = new Float2(120, 32), TabsSize = new Float2(90, 32),
Parent = this, Parent = this,
}; };
_groupSearch = CreateGroupWithList(_actorGroups, "Search", 26); _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 _searchBox = new SearchBox
{ {
AnchorPreset = AnchorPresets.HorizontalStretchTop, AnchorPreset = AnchorPresets.HorizontalStretchTop,
Parent = _groupSearch.Parent.Parent, 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; _searchBox.TextChanged += OnSearchBoxTextChanged;
@@ -145,10 +165,45 @@ namespace FlaxEditor.Windows
_actorGroups.SelectedTabIndex = 1; _actorGroups.SelectedTabIndex = 1;
} }
private void OnViewButtonClicked()
{
var menu = new ContextMenu();
var uiFilterButton = menu.AddButton("Ui");
uiFilterButton.AutoCheck = true;
uiFilterButton.Checked = (_searchFilterMask & (int)SearchFilter.Ui) != 0;
uiFilterButton.Clicked += () => ToggleSearchFilter(SearchFilter.Ui);
var actorFilterButton = menu.AddButton("Actors");
actorFilterButton.AutoCheck = true;
actorFilterButton.Checked = (_searchFilterMask & (int)SearchFilter.Actors) != 0;
actorFilterButton.Clicked += () => ToggleSearchFilter(SearchFilter.Actors);
var primitiveFilterButton = menu.AddButton("Primitives");
primitiveFilterButton.AutoCheck = true;
primitiveFilterButton.Checked = (_searchFilterMask & (int)SearchFilter.Primitives) != 0;
primitiveFilterButton.Clicked += () => ToggleSearchFilter(SearchFilter.Primitives);
menu.Show(_viewDropdown.Parent, _viewDropdown.BottomLeft);
}
private void ToggleSearchFilter(SearchFilter type)
{
_searchFilterMask ^= (int)type;
OnSearchBoxTextChanged();
}
/// <inheritdoc/>
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
_searchBox.Width = _groupSearch.Width - _viewDropdown.Right - 4;
}
private void OnScriptsReload() private void OnScriptsReload()
{ {
// Prevent any references to actor types from the game assemblies that will be reloaded // Prevent any references to actor types from the game assemblies that will be reloaded
_searchBox.Clear();
_groupSearch.DisposeChildren(); _groupSearch.DisposeChildren();
_groupSearch.PerformLayout(); _groupSearch.PerformLayout();
@@ -172,6 +227,7 @@ namespace FlaxEditor.Windows
private void OnScriptsReloadEnd() private void OnScriptsReloadEnd()
{ {
RefreshActorTabs(); RefreshActorTabs();
OnSearchBoxTextChanged();
} }
private void RefreshActorTabs() private void RefreshActorTabs()
@@ -192,14 +248,21 @@ namespace FlaxEditor.Windows
group.Dispose(); group.Dispose();
} }
// Setup primitives tabs // Add primitives to primtives and search tab
groupPrimitives = CreateGroupWithList(_actorGroups, "Primitives"); groupPrimitives = CreateGroupWithList(_actorGroups, "Primitives");
groupPrimitives.AddChild(CreateEditorAssetItem("Cube", "Primitives/Cube.flax")); groupPrimitives.AddChild(CreateEditorAssetItem("Cube", "Primitives/Cube.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Cube", "Primitives/Cube.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Sphere", "Primitives/Sphere.flax")); groupPrimitives.AddChild(CreateEditorAssetItem("Sphere", "Primitives/Sphere.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Sphere", "Primitives/Sphere.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Plane", "Primitives/Plane.flax")); groupPrimitives.AddChild(CreateEditorAssetItem("Plane", "Primitives/Plane.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Plane", "Primitives/Plane.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Cylinder", "Primitives/Cylinder.flax")); groupPrimitives.AddChild(CreateEditorAssetItem("Cylinder", "Primitives/Cylinder.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Cylinder", "Primitives/Cylinder.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Cone", "Primitives/Cone.flax")); groupPrimitives.AddChild(CreateEditorAssetItem("Cone", "Primitives/Cone.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Cone", "Primitives/Cone.flax"));
groupPrimitives.AddChild(CreateEditorAssetItem("Capsule", "Primitives/Capsule.flax")); groupPrimitives.AddChild(CreateEditorAssetItem("Capsule", "Primitives/Capsule.flax"));
_groupSearch.AddChild(CreateEditorAssetItem("Capsule", "Primitives/Capsule.flax"));
// Created first to order specific tabs // Created first to order specific tabs
CreateGroupWithList(_actorGroups, "Lights"); CreateGroupWithList(_actorGroups, "Lights");
@@ -312,6 +375,8 @@ namespace FlaxEditor.Windows
_groupSearch.LockChildrenRecursive(); _groupSearch.LockChildrenRecursive();
_groupSearch.DisposeChildren(); _groupSearch.DisposeChildren();
if (((int)SearchFilter.Actors & _searchFilterMask) != 0)
{
foreach (var actorType in Editor.CodeEditing.Actors.Get()) foreach (var actorType in Editor.CodeEditing.Actors.Get())
{ {
ActorToolboxAttribute attribute = null; ActorToolboxAttribute attribute = null;
@@ -336,7 +401,10 @@ namespace FlaxEditor.Windows
var item = CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType); var item = CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType);
SearchFilterHighlights(item, text, ranges); SearchFilterHighlights(item, text, ranges);
} }
}
if (((int)SearchFilter.Primitives & _searchFilterMask) != 0)
{
// Hack primitive models into the search results // Hack primitive models into the search results
foreach (var child in groupPrimitives.Children) foreach (var child in groupPrimitives.Children)
{ {
@@ -344,18 +412,54 @@ namespace FlaxEditor.Windows
{ {
var text = primitiveAssetItem.Text; var text = primitiveAssetItem.Text;
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) // 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"; 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); var item = CreateEditorAssetItem(text, path);
SearchFilterHighlights(item, text, ranges); 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)) 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.SortChildren();
_groupSearch.UnlockChildrenRecursive(); _groupSearch.UnlockChildrenRecursive();
@@ -363,6 +467,28 @@ namespace FlaxEditor.Windows
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 = (((int)SearchFilter.Actors & _searchFilterMask) == 0 &&
((int)SearchFilter.Primitives & _searchFilterMask) == 0 &&
((int)SearchFilter.Ui & _searchFilterMask) == 0) ||
noSearchResults;
String hint = noSearchResults ? "No results." : "No search filter active, please enable at least one filter.";
if (showHint)
{
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) private void SearchFilterHighlights(Item item, string text, QueryFilterHelper.Range[] ranges)
{ {
_groupSearch.AddChild(item); _groupSearch.AddChild(item);