using System.IO;
namespace Editor.AssetPickers;
///
/// An asset browser allowing the user to pick a single asset.
/// Supports limiting display to only certain asset types.
///
public class GenericPicker : AssetPicker
{
///
/// Internal asset browser.
///
public LocalAssetBrowser AssetBrowser { get; protected set; }
///
/// Internal cloud browser.
///
public CloudAssetBrowser CloudBrowser { get; protected set; }
///
/// The picked assets.
///
public List Assets { get; protected set; }
Button ConfirmButton;
Button CancelButton;
public GenericPicker( Widget parent, List assetTypes, PickerOptions options ) : base( parent, null, options )
{
Window.Size = new Vector2( 1280, 720 );
Window.SetModal( true );
Window.MinimumSize = 200;
Window.MaximumSize = 10000;
Window.Title = $"Select {string.Join( ", ", assetTypes?.Select( x => x.FriendlyName ) ?? new List() )} - Asset Picker";
Window.StatusBar = new StatusBar( this );
Window.StateCookie = "AssetPicker";
Window.RestoreFromStateCookie();
Layout = Layout.Column();
CreateUI( assetTypes );
}
private DockManager DockManager;
void CreateUI( List assetTypes )
{
DockManager = new DockManager( this );
DockManager.DockProperty properties = DockManager.DockProperty.HideCloseButton
| DockManager.DockProperty.DisallowUserDocking | DockManager.DockProperty.DisableDraggableTab;
AssetBrowser = new LocalAssetBrowser( DockManager, assetTypes );
AssetBrowser.WindowTitle = "Asset Browser";
AssetBrowser.SetWindowIcon( "folder" );
AssetBrowser.OnAssetHighlight += Highlight;
AssetBrowser.OnAssetsHighlight += Highlight;
AssetBrowser.OnAssetSelected += ( a ) => Select();
AssetBrowser.OnHighlight += Highlight;
AssetBrowser.MultiSelect = Options.EnableMultiselect;
DockManager.AddDock( null, AssetBrowser, DockArea.Inside, properties );
if ( Options.EnableCloud )
{
CloudBrowser = new CloudAssetBrowser( DockManager, assetTypes );
CloudBrowser.WindowTitle = "Cloud Browser";
CloudBrowser.SetWindowIcon( "cloud_download" );
CloudBrowser.OnPackageHighlight = HighlightPackage;
CloudBrowser.OnPackageSelected = ( a ) => Select();
CloudBrowser.MultiSelect = Options.EnableMultiselect;
DockManager.AddDock( null, CloudBrowser, DockArea.Inside, properties );
}
Layout.Add( DockManager, 1 );
Layout.AddSeparator();
var bottom = Layout.AddRow();
bottom.Spacing = 10;
bottom.Margin = 10;
bottom.AddStretchCell();
ConfirmButton = bottom.Add( new Button.Primary( "Select" ) );
ConfirmButton.Enabled = false;
ConfirmButton.Clicked = Select;
CancelButton = bottom.Add( new Button( "Cancel" ) );
CancelButton.Enabled = true;
CancelButton.Clicked = Close;
}
public override void SetSelection( Asset asset )
{
if ( asset is null )
{
DockManager.RaiseDock( AssetBrowser );
AssetBrowser.NavigateTo( Project.Current.GetAssetsPath() );
return;
}
Package package = asset?.Package;
if ( package is not null )
{
SetSelection( package );
}
else
{
DockManager.RaiseDock( AssetBrowser );
AssetBrowser.FocusOnAsset( asset );
}
}
public override void SetSelection( Package package )
{
if ( package is null )
return;
DockManager.RaiseDock( CloudBrowser );
CloudBrowser.Search.Value += $" {package.FullIdent}";
CloudBrowser.FocusOnPackage( package );
}
void Select()
{
if ( AssetBrowser.Visible )
{
var assets = AssetBrowser.GetSelected().Select( x => x.Asset ).ToList();
if ( !IsSelectionValid( assets ) )
return;
Submit( assets.ToArray() );
}
else
{
Package package = CloudBrowser.GetSelected().FirstOrDefault().Package;
if ( package is not null )
{
Submit( package );
}
}
}
bool IsSelectionValid( List assets ) => assets.TrueForAll( IsSelectionValid );
bool IsSelectionValid( Asset asset )
{
// If no asset types are selected, allow anything to be selected
if ( AssetBrowser.FilterAssetTypes?.Count == 0 )
{
return true;
}
if ( asset is null )
{
return false;
}
if ( !AssetBrowser.FilterAssetTypes?.Contains( asset.AssetType ) ?? false )
{
return false;
}
return true;
}
bool IsSelectionValid( Package package )
{
if ( package is null )
return false;
if ( package.TypeName == "map" )
{
// special case because these can be vpks, not assets
return AssetBrowser.FilterAssetTypes.Contains( AssetType.MapFile );
}
var primaryAssetName = package.GetCachedMeta( "PrimaryAsset" );
string ext = Path.GetExtension( primaryAssetName );
if ( !AssetBrowser.FilterAssetTypes?.Any( x => ext == $".{x.FileExtension}" ) ?? false )
{
return false;
}
return true;
}
void Highlight( Asset a )
{
bool isValid = IsSelectionValid( a );
if ( isValid )
{
Assets = new List() { a };
OnAssetHighlighted?.Invoke( Assets.ToArray() );
ConfirmButton.Enabled = isValid;
BindSystem.Flush();
}
EditorUtility.PlayAssetSound( a );
ConfirmButton.Enabled = isValid;
}
void Highlight( Asset[] a )
{
var assets = new List( a );
bool isValid = IsSelectionValid( assets );
if ( isValid )
{
Assets = assets;
OnAssetHighlighted?.Invoke( Assets.ToArray() );
BindSystem.Flush();
}
ConfirmButton.Enabled = isValid;
}
void Highlight( IEnumerable entries )
{
if ( entries.Any( x => x is not AssetEntry or PackageEntry ) )
{
// not something we can pick
ConfirmButton.Enabled = false;
}
}
async void HighlightPackage( Package package )
{
ConfirmButton.Enabled = false;
if ( AssetSystem.CanCloudInstall( package ) )
{
var a = await AssetSystem.InstallAsync( package.FullIdent );
if ( !IsValid ) return;
if ( a is not null )
{
EditorUtility.PlayAssetSound( a );
Assets = new List() { a };
OnAssetHighlighted?.Invoke( Assets.ToArray() );
}
}
package = await Package.FetchAsync( package.FullIdent, false );
if ( !IsValid ) return;
ConfirmButton.Enabled = IsSelectionValid( package );
}
protected override void OnKeyPress( KeyEvent e )
{
base.OnKeyPress( e );
if ( e.Key == KeyCode.Escape )
{
Close();
}
}
///
/// Called from native to open an asset picker
///
[Event( "assetsystem.openpicker" )]
static void OpenAssetPicker( AssetSystem.AssetPickerParameters parameters )
{
// TODO: Maybe should pass a parent widget which would be the callback listener?
AssetPicker picker = null;
var options = new PickerOptions()
{
EnableCloud = parameters.ShowCloudAssets,
AdditionalTypes = parameters.FilterAssetTypes
};
if ( parameters.FilterAssetTypes.Select( x => x.ResourceType ).Distinct().Where( x => x != null ).Count() == 1 )
{
picker = Create( parameters.ParentWidget, parameters.FilterAssetTypes[0], options );
}
else
{
picker = new GenericPicker( parameters.ParentWidget, parameters.FilterAssetTypes, options );
}
if ( picker is GenericPicker )
{
var gp = picker as GenericPicker;
// parameters.ViewMode is 0 for list, 1 for grid, try to preserve icon size from the asset browser cookie
if ( (parameters.ViewMode == 0 && gp.AssetBrowser.ViewModeType != AssetListViewMode.List) ||
(parameters.ViewMode == 1 && gp.AssetBrowser.ViewModeType == AssetListViewMode.List) )
{
gp.AssetBrowser.ViewModeType = (AssetListViewMode)parameters.ViewMode;
}
}
picker.OnAssetPicked = parameters.AssetSelectedCallback;
//
// Native can pass down a title which it also wants to use as a settings key
//
if ( !string.IsNullOrEmpty( parameters.Title ) )
{
picker.Title = parameters.Title;
// TODO: Use the title as the AssetBrowser cookie (does it make sense for the window StateCookie too?)
}
picker.Show();
//
// Focus on the initial asset if we have one ( e.g whatever the current asset already is )
// Do this after the window is shown so it can scroll properly
//
picker.SetSelection( parameters.InitialSelectedAsset );
picker.SetSearchText( parameters.InitialSearchText );
}
}