diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index 3c9c605a4..7ad66794d 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -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; /// /// 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(); + }; + } + + /// + 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(); } + /// + 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);