Editor/IndexerWidget.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Editor;
using Sandbox;
public class SearchResult
{
public string Title;
public string Subtitle;
public string Type;
public string Icon;
public float Score;
public Action OnSelect;
}
public sealed class IndexerPopup : Window
{
private LineEdit Search;
private ListView Results;
private List<ISearchProvider> Providers = new();
public static Action PendingAction;
public IndexerPopup()
{
IsDialog = true;
WindowTitle = "Indexer";
Size = new Vector2( 1200, 400 );
Canvas = new Widget( null );
Canvas.Layout = Layout.Column();
Canvas.Layout.Spacing = 4;
Canvas.Layout.Margin = 8;
Search = Canvas.Layout.Add( new LineEdit( "", this ) );
Search.PlaceholderText = "Search assets, objects, actions...";
Search.TextChanged += OnSearchChanged;
Results = Canvas.Layout.Add( new ListView( this ) );
Results.ItemSize = new Vector2( 0, 32 );
Results.ItemPaint = PaintResultItem;
Results.ItemClicked += OnResultClicked;
Providers.Add( new AssetSearchProvider() );
Providers.Add( new SceneObjectsProvider() );
Providers.Add( new ComponentSearchProvider() );
Providers.Add( new EditorActionProvider() );
Providers.Add( new MathProvider() );
Providers.Add( new ColorProvider() );
Show();
Search.Focus();
}
protected override void OnKeyPress( KeyEvent e )
{
if ( e.Key == KeyCode.Down )
{
Results.SelectMoveRow( 1 );
e.Accepted = true;
return;
}
if ( e.Key == KeyCode.Up )
{
Results.SelectMoveRow( -1 );
e.Accepted = true;
return;
}
if ( e.Key == KeyCode.Enter || e.Key == KeyCode.Return )
{
var selected = Results.SelectedItems.FirstOrDefault() as SearchResult;
selected?.OnSelect?.Invoke();
Close();
e.Accepted = true;
return;
}
if ( e.Key == KeyCode.Escape )
{
Close();
e.Accepted = true;
return;
}
base.OnKeyPress( e );
}
private void OnResultClicked( object obj )
{
if ( obj is SearchResult result )
{
Close();
}
}
private void OnSearchChanged( string query )
{
if ( string.IsNullOrWhiteSpace( query ) )
{
Results.SetItems( Array.Empty<SearchResult>() );
return;
}
var results = Providers.SelectMany( p => p.Search( query ) ).OrderByDescending( r => r.Score ).Take( 50 )
.ToList();
Results.SetItems( results );
}
void PaintResultItem( VirtualWidget vw )
{
var item = vw.Object as SearchResult;
if ( item == null ) return;
var rect = vw.Rect;
if ( Paint.HasSelected )
{
Paint.ClearPen();
Paint.SetBrush( Theme.Primary );
var bg = rect.Shrink( 1f );
Paint.DrawRect( in bg, Theme.ControlRadius );
}
else if ( Paint.HasMouseOver )
{
Paint.ClearPen();
Paint.SetBrush( Theme.ControlBackground );
var bg = rect.Shrink( 1f );
Paint.DrawRect( in bg, Theme.ControlRadius );
}
var inner = rect.Shrink( 8f, 0f, 8f, 0f );
var iconRect = inner;
iconRect.Width = inner.Height;
var iconColor = Paint.HasSelected ? Theme.TextSelected : Theme.TextLight;
Paint.SetPen( in iconColor );
Paint.DrawIcon( iconRect, item.Icon, inner.Height * 0.55f );
if ( item.Type == "Color" )
{
var parsed = Color.Parse( item.Title );
if ( parsed.HasValue )
{
var swatchRect = new Rect( iconRect.Left, iconRect.Top + 4f, iconRect.Width, iconRect.Height - 8f );
Paint.ClearPen();
Paint.SetBrush( parsed.Value );
Paint.DrawRect( in swatchRect, 4f );
}
}
var textRect = inner;
textRect.Left = iconRect.Right + 6f;
textRect.Right = inner.Right - inner.Width * 0.5f;
var titleColor = Paint.HasSelected ? Theme.TextSelected : Theme.Text;
Paint.SetPen( in titleColor );
Paint.SetFont( Theme.DefaultFont, 9f, 500 );
Paint.DrawText( in textRect, item.Title, TextFlag.LeftCenter );
var typeRect = inner;
typeRect.Left = textRect.Right + 4f;
typeRect.Right = inner.Right - inner.Width * 0.25f;
var typeColor = Paint.HasSelected
? Theme.TextSelected.WithAlpha( 0.5f )
: Theme.TextDisabled;
Paint.SetPen( in typeColor );
Paint.SetFont( Theme.DefaultFont, 8f, 400 );
Paint.DrawText( in typeRect, item.Type, TextFlag.Center );
var subRect = inner;
subRect.Left = typeRect.Right + 4f;
var subColor = Paint.HasSelected
? Theme.TextSelected.WithAlpha( 0.6f )
: Theme.TextDisabled;
Paint.SetPen( in subColor );
Paint.SetFont( Theme.DefaultFont, 8f, 400 );
Paint.DrawText( in subRect, item.Subtitle, TextFlag.RightCenter );
}
[EditorEvent.Frame]
public static void ProcessPending()
{
if ( PendingAction == null ) return;
var action = PendingAction;
PendingAction = null;
action();
}
[Shortcut( "editor.indexer", "CTRL+P" )]
public static void OpenIndexer()
{
new IndexerPopup();
}
}