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();
	}
}