mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-19 05:48:07 -04:00
847 lines
20 KiB
C#
847 lines
20 KiB
C#
using System.IO;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace Editor;
|
|
|
|
/// <summary>
|
|
/// A list of assets with filtering options.
|
|
/// </summary>
|
|
public partial class AssetBrowser : Widget, IBrowser, AssetSystem.IEventListener
|
|
{
|
|
|
|
/// <summary>
|
|
/// Directory, contents of which are currently being displayed.
|
|
/// </summary>
|
|
public Location CurrentLocation { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// Only show these asset types.
|
|
/// </summary>
|
|
public List<AssetType> FilterAssetTypes { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// Internal asset list panel.
|
|
/// </summary>
|
|
protected AssetList AssetList;
|
|
|
|
/// <inheritdoc cref="BaseItemWidget.MultiSelect"/>
|
|
public bool MultiSelect
|
|
{
|
|
get => AssetList.MultiSelect;
|
|
set => AssetList.MultiSelect = value;
|
|
}
|
|
|
|
public int SelectedCount => AssetList.Selection.Count;
|
|
public List<T> GetSelected<T>() => AssetList.Selection.OfType<T>().ToList();
|
|
|
|
public PathWidget Path;
|
|
public SearchWidget Search;
|
|
|
|
ToolButton ViewMode;
|
|
ChipsWidget Chips;
|
|
ListHeader ListHeader;
|
|
string lastSortColumn = "";
|
|
bool lastSortAscending = true;
|
|
|
|
/// <summary>
|
|
/// Internal left side panel that lists all local projects and other asset locations.
|
|
/// </summary>
|
|
protected AssetLocations AssetLocations;
|
|
|
|
/// <summary>
|
|
/// Asset has been clicked
|
|
/// </summary>
|
|
public Action<Asset> OnAssetHighlight;
|
|
|
|
/// <summary>
|
|
/// Asset has been clicked
|
|
/// </summary>
|
|
public Action<Asset[]> OnAssetsHighlight;
|
|
|
|
/// <inheritdoc cref="AssetList.OnHighlight"/>
|
|
public Action<IEnumerable<IAssetListEntry>> OnHighlight;
|
|
|
|
/// <summary>
|
|
/// Asset has been selected (double click, or enter)
|
|
/// </summary>
|
|
public Action<Asset> OnAssetSelected;
|
|
|
|
/// <summary>
|
|
/// Package has been selected (double click, or enter)
|
|
/// </summary>
|
|
public Action<Package> OnPackageSelected;
|
|
|
|
/// <summary>
|
|
/// Asset has been selected (double click, or enter)
|
|
/// </summary>
|
|
public Action<string> OnFileSelected;
|
|
|
|
public Action OnFolderOpened;
|
|
|
|
/// <summary>
|
|
/// Show files generated by asset compilation and other usually unuseful files.
|
|
/// </summary>
|
|
public bool ShowJunkFiles { get; set; }
|
|
public bool HideNonAssets { get; set; }
|
|
|
|
bool _showRecursive = false;
|
|
|
|
private HistoryStack<string> History = new();
|
|
|
|
protected virtual string CookieKey => "AssetBrowser";
|
|
|
|
/// <summary>
|
|
/// If true, show all files in a flat view, if false, show files and folders like they would appear in Windows Explorer.
|
|
/// </summary>
|
|
public bool ShowRecursiveFiles
|
|
{
|
|
get => _showRecursive;
|
|
set
|
|
{
|
|
if ( _showRecursive == value ) return;
|
|
_showRecursive = value;
|
|
|
|
watcher.IncludeSubdirectories = value;
|
|
|
|
UpdateAssetList();
|
|
SaveSettings();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// View mode, i.e. thumbs, list or whatever else.
|
|
/// </summary>
|
|
public AssetListViewMode ViewModeType
|
|
{
|
|
get;
|
|
set
|
|
{
|
|
field = value;
|
|
AssetList.ViewMode = field;
|
|
UpdateViewModeIcon();
|
|
SaveSettings();
|
|
}
|
|
}
|
|
|
|
private FileSystemWatcher watcher;
|
|
|
|
public AssetBrowser( Widget parent ) : this( parent, null )
|
|
{
|
|
|
|
}
|
|
|
|
public AssetBrowser( Widget parent, List<AssetType> assetTypeFilters ) : base( parent )
|
|
{
|
|
MinimumSize = new( 100, 100 );
|
|
|
|
Layout = Layout.Row();
|
|
|
|
FilterAssetTypes = assetTypeFilters;
|
|
|
|
Path = new PathWidget( this );
|
|
Path.MinimumHeight = Theme.RowHeight;
|
|
Path.OnPathEdited += ( path ) => NavigateTo( path );
|
|
|
|
Search = new SearchWidget( this );
|
|
Search.MinimumHeight = Theme.RowHeight;
|
|
Search.ValueChanged += () => { UpdateAssetList(); SaveSettings(); };
|
|
|
|
ListHeader = new ListHeader( this, ["Name", "Date", "Type", "Size", "Path"] );
|
|
ListHeader.SetColumnVisible( "Path", false );
|
|
ListHeader.OnColumnSelect += SortAssetList;
|
|
ListHeader.OnColumnResize += () => AssetList?.Update();
|
|
|
|
AssetList = new AssetList( this );
|
|
AssetList.Browser = this;
|
|
AssetList.OnViewModeChanged += () => { UpdateViewModeIcon(); SaveSettings(); };
|
|
AssetList.OnHighlight = ( entries ) =>
|
|
{
|
|
var assets = entries.OfType<AssetEntry>().ToList();
|
|
if ( assets.Count == entries.Count() )
|
|
{
|
|
if ( assets.Count > 1 )
|
|
{
|
|
OnAssetsHighlight?.Invoke( assets.Select( x => x.Asset ).ToArray() );
|
|
}
|
|
else
|
|
{
|
|
OnAssetHighlight?.Invoke( assets.First().Asset );
|
|
}
|
|
}
|
|
|
|
OnHighlight?.Invoke( entries );
|
|
};
|
|
|
|
Chips = new ChipsWidget();
|
|
Chips.OnActiveChanged = UpdateAssetList;
|
|
|
|
var body = new Widget( this );
|
|
body.Layout = Layout.Column();
|
|
|
|
var toolbar = body.Layout.AddRow();
|
|
body.Layout.AddSpacingCell( 2 );
|
|
body.Layout.Add( Chips );
|
|
body.Layout.AddSpacingCell( 2 );
|
|
body.Layout.Add( ListHeader );
|
|
body.Layout.Add( AssetList, 1 );
|
|
|
|
CreateLocations();
|
|
|
|
var splitter = new Splitter( this );
|
|
splitter.IsHorizontal = true;
|
|
splitter.AddWidget( AssetLocations );
|
|
splitter.SetStretch( 0, 1 );
|
|
splitter.AddWidget( body );
|
|
splitter.SetStretch( 1, 5 );
|
|
|
|
Layout.Add( splitter );
|
|
|
|
BuildToolbar( toolbar );
|
|
UpdateToolbarOptions();
|
|
|
|
if ( FilterAssetTypes is not null )
|
|
{
|
|
Search.AssetTypes.ActiveTags = FilterAssetTypes.SelectMany( x => x.FileExtensions ).ToHashSet();
|
|
Search.AssetTypes.Rebuild();
|
|
Search.AssetTypes.Enabled = false;
|
|
}
|
|
|
|
watcher = new FileSystemWatcher();
|
|
watcher.Changed += OnExternalChanges;
|
|
watcher.Created += OnExternalChanges;
|
|
watcher.Deleted += OnExternalChanges;
|
|
watcher.Renamed += OnExternalChanges;
|
|
|
|
SetInitialLocation();
|
|
RefreshCookies();
|
|
|
|
if ( History.Count == 0 && CurrentLocation != null )
|
|
{
|
|
History.Add( CurrentLocation.Path );
|
|
}
|
|
}
|
|
|
|
protected virtual void SetInitialLocation()
|
|
{
|
|
|
|
}
|
|
|
|
protected virtual void CreateLocations()
|
|
{
|
|
|
|
}
|
|
|
|
public override void OnDestroyed()
|
|
{
|
|
base.OnDestroyed();
|
|
watcher?.Dispose();
|
|
}
|
|
|
|
private void OnExternalChanges( object sender, FileSystemEventArgs e )
|
|
{
|
|
MainThread.Queue( UpdateAssetList );
|
|
}
|
|
|
|
private void GoBack()
|
|
{
|
|
if ( !History.CanGoBack() )
|
|
return;
|
|
|
|
NavigateTo( History.GoBack(), false );
|
|
}
|
|
|
|
private void GoForward()
|
|
{
|
|
if ( !History.CanGoForward() )
|
|
return;
|
|
|
|
NavigateTo( History.GoForward(), false );
|
|
}
|
|
|
|
protected override void OnMousePress( MouseEvent e )
|
|
{
|
|
base.OnMousePress( e );
|
|
|
|
if ( e.Button == MouseButtons.Back && History.CanGoBack() )
|
|
{
|
|
GoBack();
|
|
return;
|
|
}
|
|
|
|
if ( e.Button == MouseButtons.Forward && History.CanGoForward() )
|
|
{
|
|
GoForward();
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void RefreshCookies()
|
|
{
|
|
SettingsCookie = GetCookieKey();
|
|
History.StateCookie = SettingsCookie;
|
|
|
|
GlobalSettingsCookie = "AssetBrowser.Global";
|
|
|
|
if ( History.Count > 0 )
|
|
{
|
|
NavigateTo( History.Current, false );
|
|
}
|
|
|
|
LoadSettings();
|
|
|
|
UpdateAssetList();
|
|
}
|
|
|
|
private string GetCookieKey()
|
|
{
|
|
StringBuilder stringBuilder = new( CookieKey );
|
|
|
|
if ( FilterAssetTypes != null )
|
|
stringBuilder.Append( $".{string.Join( ",", FilterAssetTypes )}" );
|
|
|
|
return stringBuilder.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Go up once in the folder structure.
|
|
/// </summary>
|
|
public void OpenParentFolder()
|
|
{
|
|
if ( CurrentLocation == null ) return;
|
|
if ( !CurrentLocation.CanGoUp() ) return;
|
|
|
|
string path = CurrentLocation.Path.NormalizeFilename( false, false );
|
|
string parentPath = path.TrimEnd( '/' ); // remove trailing slash if any
|
|
int i = path.LastIndexOf( '/' );
|
|
parentPath = (i > 0) ? path[..(i + 1)] : null;
|
|
|
|
NavigateTo( parentPath );
|
|
}
|
|
|
|
public void NavigateTo( string absolutePath, bool addToHistory = true )
|
|
{
|
|
if ( string.IsNullOrEmpty( absolutePath ) )
|
|
return;
|
|
|
|
if ( !Location.TryParse( absolutePath.NormalizeFilename( false, false ), out var location ) )
|
|
return;
|
|
|
|
NavigateTo( location, addToHistory );
|
|
}
|
|
|
|
public void NavigateTo( Location location, bool addToHistory = true )
|
|
{
|
|
if ( !location.IsValid() )
|
|
return;
|
|
|
|
CurrentLocation = location;
|
|
UpdateAssetList();
|
|
|
|
if ( location is DiskLocation )
|
|
{
|
|
watcher.Path = CurrentLocation.Path;
|
|
watcher.EnableRaisingEvents = true;
|
|
}
|
|
else
|
|
{
|
|
watcher.EnableRaisingEvents = false;
|
|
}
|
|
|
|
if ( addToHistory )
|
|
{
|
|
History.Add( CurrentLocation.Path );
|
|
}
|
|
|
|
OnFolderOpened?.Invoke();
|
|
AssetLocations.SelectFolder( CurrentLocation.Path );
|
|
}
|
|
|
|
void AssetSystem.IEventListener.OnAssetSystemChanges()
|
|
{
|
|
UpdateAssetList();
|
|
}
|
|
|
|
|
|
[Event( "content.changed" )]
|
|
void OnContentChanged( string file )
|
|
{
|
|
UpdateAssetList();
|
|
}
|
|
|
|
CancellationTokenSource refreshToken;
|
|
Task<bool> RefreshTask;
|
|
|
|
/// <summary>
|
|
/// Update the list of displayed assets.
|
|
/// </summary>
|
|
public virtual void UpdateAssetList()
|
|
{
|
|
if ( CurrentLocation is null )
|
|
return;
|
|
|
|
refreshToken?.Cancel();
|
|
refreshToken = new CancellationTokenSource();
|
|
|
|
if ( !CurrentLocation.IsValid() )
|
|
{
|
|
// folder must've been deleted, try go somewhere safe
|
|
var location = new DiskLocation( Sandbox.Project.Current.GetAssetsPath() );
|
|
if ( location.IsValid() )
|
|
{
|
|
NavigateTo( location, false );
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool recursive = ShowRecursiveFiles || !Search.IsEmpty;
|
|
AssetList.FullPathMode = recursive;
|
|
|
|
Path.UpdateSegments();
|
|
RefreshTask = UpdateAssetListAsync( recursive, refreshToken.Token );
|
|
}
|
|
|
|
private async Task<bool> UpdateAssetListAsync( bool recursive, CancellationToken token )
|
|
{
|
|
List<object> items = new List<object>();
|
|
var tagCounts = new Dictionary<string, int>();
|
|
|
|
AssetList.Clear();
|
|
|
|
await Task.Run( () =>
|
|
{
|
|
//
|
|
// Collect directories
|
|
//
|
|
if ( !recursive )
|
|
{
|
|
string path = CurrentLocation.Path;
|
|
foreach ( var directory in CurrentLocation.GetDirectories() )
|
|
{
|
|
if ( token.IsCancellationRequested )
|
|
return;
|
|
|
|
var di = new DirectoryEntry( directory.Path );
|
|
|
|
// Hide hidden directories
|
|
if ( di.DirectoryInfo.Exists && di.DirectoryInfo.Attributes.HasFlag( FileAttributes.Hidden ) )
|
|
continue;
|
|
|
|
// Hide obj/ folder in code directories
|
|
if ( EditorUtility.IsCodeFolder( path ) && di.Name.Equals( "obj", StringComparison.OrdinalIgnoreCase ) )
|
|
continue;
|
|
|
|
items.Add( di );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Collect files
|
|
//
|
|
|
|
var files = Search.Filter( CurrentLocation, token, recursive );
|
|
if ( files is null ) return;
|
|
|
|
foreach ( var file in files )
|
|
{
|
|
if ( token.IsCancellationRequested )
|
|
return;
|
|
|
|
if ( Chips.ShouldFilter( file ) )
|
|
continue;
|
|
|
|
if ( !ShowJunkFiles )
|
|
{
|
|
//
|
|
// Filter out unwanted crap
|
|
//
|
|
if ( file.Name.Contains( ".generated", StringComparison.OrdinalIgnoreCase ) ||
|
|
file.Name.EndsWith( ".meta", StringComparison.OrdinalIgnoreCase ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( file.Exists && file.Attributes.HasFlag( FileAttributes.Hidden ) )
|
|
continue;
|
|
|
|
var asset = AssetSystem.FindByPath( file.ToString() );
|
|
|
|
//
|
|
// Filter out compiled assets if we have a source asset
|
|
//
|
|
string sourcePath = asset?.GetSourceFile();
|
|
if ( file.Name.EndsWith( "_c" ) && !string.IsNullOrEmpty( sourcePath ) )
|
|
{
|
|
// ( but only if the extensions are similar and would both be shown in this filter, eg so we don't hide .sounds because we have a .wav etc )
|
|
if ( Search.AssetTypes.ActiveTags.Count == 0 || file.Extension.Contains( System.IO.Path.GetExtension( sourcePath ) ) )
|
|
continue;
|
|
}
|
|
|
|
if ( asset == null && HideNonAssets )
|
|
continue;
|
|
|
|
items.Add( new AssetEntry( file, asset ) );
|
|
|
|
if ( asset != null )
|
|
{
|
|
foreach ( var tag in asset.Tags )
|
|
{
|
|
tagCounts[tag] = tagCounts.GetValueOrDefault( tag ) + 1;
|
|
}
|
|
}
|
|
}
|
|
}, token );
|
|
|
|
if ( token.IsCancellationRequested )
|
|
return false;
|
|
|
|
AssetList.SetItems( items );
|
|
if ( !string.IsNullOrEmpty( lastSortColumn ) )
|
|
{
|
|
SortAssetList( lastSortColumn, lastSortAscending );
|
|
}
|
|
|
|
Chips.ClearButKeepActive();
|
|
|
|
foreach ( var tagPair in tagCounts )
|
|
{
|
|
if ( token.IsCancellationRequested )
|
|
return false;
|
|
|
|
var tag = FindTag( tagPair.Key );
|
|
Chips.AddOption( tag );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void SortAssetList( string sortBy, bool ascending )
|
|
{
|
|
List<object> items;
|
|
|
|
switch ( sortBy )
|
|
{
|
|
case "Name":
|
|
items = AssetList.Items.OrderBy( x =>
|
|
{
|
|
string sort = "";
|
|
if ( x is AssetEntry ae )
|
|
{
|
|
sort = ae.Name.ToLower();
|
|
}
|
|
else if ( x is DirectoryEntry de )
|
|
{
|
|
// Directories always come first
|
|
sort = (ListHeader.SortAscending ? "! " : "z ") + de.Name.ToLower();
|
|
}
|
|
return sort;
|
|
} ).ToList();
|
|
break;
|
|
case "Date":
|
|
items = AssetList.Items.OrderBy( x =>
|
|
{
|
|
var date = DateTimeOffset.Now;
|
|
if ( x is AssetEntry ae )
|
|
{
|
|
date = ae.FileInfo?.LastWriteTime ?? date;
|
|
}
|
|
var sort = date.UtcTicks.ToString();
|
|
if ( x is DirectoryEntry de )
|
|
{
|
|
// Directories always come first
|
|
sort = (ListHeader.SortAscending ? "! " : "z ") + de.Name.ToLower();
|
|
}
|
|
return sort;
|
|
} ).ToList();
|
|
break;
|
|
case "Type":
|
|
items = AssetList.Items.OrderBy( x =>
|
|
{
|
|
var type = (x is AssetEntry ae) ? ae.TypeName : "";
|
|
var sort = type.ToLower();
|
|
if ( x is DirectoryEntry de )
|
|
{
|
|
// Directories always come first
|
|
sort = (ListHeader.SortAscending ? "! " : "z ") + de.Name.ToLower();
|
|
}
|
|
return sort;
|
|
} ).ToList();
|
|
break;
|
|
case "Size":
|
|
items = AssetList.Items.OrderBy( x =>
|
|
{
|
|
long size = (x is AssetEntry ae) ? (ae.FileInfo?.Length ?? 0) : 0;
|
|
if ( x is DirectoryEntry de )
|
|
{
|
|
// Directories always come first/last
|
|
size = -long.MaxValue + de.Name.ToLong();
|
|
}
|
|
return size;
|
|
} ).ToList();
|
|
break;
|
|
default:
|
|
items = AssetList.Items.ToList();
|
|
break;
|
|
}
|
|
|
|
if ( !ascending )
|
|
{
|
|
items.Reverse();
|
|
}
|
|
AssetList.SetItems( items );
|
|
|
|
lastSortColumn = sortBy;
|
|
lastSortAscending = ascending;
|
|
}
|
|
|
|
private static AssetTagSystem.TagDefinition FindTag( string tagName )
|
|
{
|
|
foreach ( var tag in AssetTagSystem.All )
|
|
{
|
|
if ( tag.Tag.Equals( tagName ) )
|
|
{
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
protected override void OnKeyPress( KeyEvent e )
|
|
{
|
|
if ( e.Key == KeyCode.F5 )
|
|
{
|
|
UpdateAssetList();
|
|
return;
|
|
}
|
|
|
|
base.OnKeyPress( e );
|
|
}
|
|
|
|
void OpenSettingsMenu( Rect source )
|
|
{
|
|
var menu = new ContextMenu( this );
|
|
|
|
{
|
|
var o = menu.AddOption( new Option( this, "Show Bullshit Files" ) );
|
|
o.StatusTip = "Show All Files, even those that are useless for us to see and will make a big mess of the UI.";
|
|
o.Checkable = true;
|
|
o.Checked = ShowJunkFiles;
|
|
o.Toggled = ( b ) =>
|
|
{
|
|
ShowJunkFiles = b;
|
|
UpdateAssetList();
|
|
SaveSettings();
|
|
};
|
|
}
|
|
|
|
{
|
|
var o = menu.AddOption( new Option( this, "Show Assets Only" ) );
|
|
o.StatusTip = "Filters out file types that aren't part of our asset system";
|
|
o.Checkable = true;
|
|
o.Checked = HideNonAssets;
|
|
o.Toggled = ( b ) =>
|
|
{
|
|
HideNonAssets = b;
|
|
UpdateAssetList();
|
|
SaveSettings();
|
|
};
|
|
}
|
|
|
|
{
|
|
var o = new Option( "Include Path in Search" );
|
|
o.Checkable = true;
|
|
o.Checked = AssetLocations.IncludePathNames;
|
|
o.Toggled = ( b ) =>
|
|
{
|
|
AssetLocations.SetIncludePathNames( b );
|
|
UpdateAssetList();
|
|
};
|
|
menu.AddOption( o );
|
|
}
|
|
|
|
menu.AddSeparator();
|
|
|
|
{
|
|
var o = menu.AddOption( new Option( this, "Split Left", "first_page" ) );
|
|
o.Triggered = () =>
|
|
{
|
|
var ab = EditorWindow.DockManager.Create<MainAssetBrowser>();
|
|
ab.Local.NavigateTo( CurrentLocation );
|
|
ab.Local.ViewModeType = ViewModeType;
|
|
EditorWindow.DockManager.AddDock( this, ab, DockArea.Left );
|
|
};
|
|
}
|
|
|
|
{
|
|
var o = menu.AddOption( new Option( this, "Split Right", "last_page" ) );
|
|
o.Triggered = () =>
|
|
{
|
|
var ab = EditorWindow.DockManager.Create<MainAssetBrowser>();
|
|
ab.Local.NavigateTo( CurrentLocation );
|
|
ab.Local.ViewModeType = ViewModeType;
|
|
EditorWindow.DockManager.AddDock( this, ab, DockArea.Right );
|
|
};
|
|
}
|
|
|
|
menu.OpenAt( source.BottomLeft, false );
|
|
}
|
|
|
|
string SettingsCookie { get; set; }
|
|
string GlobalSettingsCookie { get; set; }
|
|
bool SuppressSaveSettings { get; set; }
|
|
|
|
/// <summary>
|
|
/// Load settings saved by <see cref="SaveSettings"/>.
|
|
/// </summary>
|
|
public virtual void LoadSettings()
|
|
{
|
|
if ( string.IsNullOrEmpty( SettingsCookie ) )
|
|
return;
|
|
|
|
SuppressSaveSettings = true;
|
|
|
|
ViewModeType = (AssetListViewMode)ProjectCookie.Get( $"{SettingsCookie}.ViewMode", (int)AssetList.ViewMode );
|
|
ShowJunkFiles = ProjectCookie.Get( $"{SettingsCookie}.ShowJunkFiles", ShowJunkFiles );
|
|
HideNonAssets = ProjectCookie.Get( $"{SettingsCookie}.HideNonAssets", HideNonAssets );
|
|
ShowRecursiveFiles = ProjectCookie.Get( $"{SettingsCookie}.ShowRecursiveFiles", ShowRecursiveFiles );
|
|
|
|
if ( FilterAssetTypes is null )
|
|
{
|
|
Search.Value = ProjectCookie.Get( $"{SettingsCookie}.Search", Search.Value );
|
|
|
|
if ( ProjectCookie.TryGet( $"{SettingsCookie}.ActiveTags", out string activeTags ) )
|
|
{
|
|
Search.AssetTypes.ActiveTags = activeTags.Split( ';' ).Where( x => x.Length > 0 ).ToHashSet();
|
|
}
|
|
if ( ProjectCookie.TryGet( $"{SettingsCookie}.ExcludedTags", out string excludedTags ) )
|
|
{
|
|
Search.AssetTypes.ExcludedTags = excludedTags.Split( ';' ).Where( x => x.Length > 0 ).ToHashSet();
|
|
}
|
|
|
|
Search.AssetTypes.Rebuild();
|
|
}
|
|
|
|
SuppressSaveSettings = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save asset browser user settings, such as view mode, <see cref="ShowJunkFiles"/> and <see cref="ShowRecursiveFiles"/>.
|
|
/// The save slot is separate for each asset browser filter combination set in the constructor.
|
|
/// </summary>
|
|
public virtual void SaveSettings()
|
|
{
|
|
if ( string.IsNullOrEmpty( SettingsCookie ) )
|
|
return;
|
|
|
|
if ( SuppressSaveSettings )
|
|
return;
|
|
|
|
ProjectCookie.Set( $"{SettingsCookie}.ViewMode", (int)AssetList.ViewMode );
|
|
ProjectCookie.Set( $"{SettingsCookie}.ShowJunkFiles", ShowJunkFiles );
|
|
ProjectCookie.Set( $"{SettingsCookie}.HideNonAssets", HideNonAssets );
|
|
ProjectCookie.Set( $"{SettingsCookie}.ShowRecursiveFiles", ShowRecursiveFiles );
|
|
|
|
if ( FilterAssetTypes is null )
|
|
{
|
|
ProjectCookie.Set( $"{SettingsCookie}.Search", Search.Value );
|
|
ProjectCookie.Set( $"{SettingsCookie}.ActiveTags", string.Join( ';', Search.AssetTypes.ActiveTags ) );
|
|
ProjectCookie.Set( $"{SettingsCookie}.ExcludedTags", string.Join( ';', Search.AssetTypes.ExcludedTags ) );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Focus on this asset, make it selected
|
|
/// </summary>
|
|
public async void FocusOnAsset( Asset asset, bool skipEvents = false )
|
|
{
|
|
if ( asset is null ) return;
|
|
|
|
var folder = System.IO.Path.GetDirectoryName( asset.AbsolutePath );
|
|
|
|
EditorWindow.DockManager.RaiseDock( this );
|
|
|
|
NavigateTo( folder );
|
|
|
|
// wait for the list to (successfully) populate before selecting the item
|
|
bool success = await RefreshTask;
|
|
if ( !success )
|
|
return;
|
|
|
|
AssetEntry entry = AssetList.Items.OfType<AssetEntry>().Where( x => x.Asset?.RelativePath == asset.RelativePath ).FirstOrDefault();
|
|
if ( entry is null )
|
|
return;
|
|
|
|
AssetList.SelectItem( entry, skipEvents: skipEvents );
|
|
AssetList.ScrollTo( entry );
|
|
}
|
|
|
|
public void OnAssetCreated( Asset asset, string path )
|
|
{
|
|
var entry = AssetList.AddItem( new AssetEntry( new FileInfo( path ), asset ) );
|
|
AssetList.SelectItem( entry, skipEvents: true );
|
|
|
|
AssetList.OpenRenameFlyout( entry );
|
|
}
|
|
|
|
void AssetSystem.IEventListener.OnAssetTagsChanged()
|
|
{
|
|
UpdateToolbarOptions();
|
|
}
|
|
|
|
[Event( "tools.package.loaded" ), Event( "localaddons.changed", Priority = 9999 )]
|
|
void UpdateToolbarOptions()
|
|
{
|
|
if ( Search.AssetTypes == null ) return;
|
|
|
|
Search.AssetTypes.Options.Clear();
|
|
|
|
TagPicker.Option SelectAssetTypes( AssetType x ) => new( x.FileExtension )
|
|
{
|
|
PixmapIcon = x.Icon16,
|
|
Title = x.FriendlyName,
|
|
Group = (string.IsNullOrEmpty( x.Category ) ? "Other" : x.Category),
|
|
Column = 0,
|
|
Count = () => AssetSystem.All.Where( y => y.AssetType == x ).Count(),
|
|
Color = x.Color
|
|
};
|
|
|
|
TagPicker.Option SelectAssetTags( AssetTagSystem.TagDefinition x ) => new( $"tag:{x.Tag}" )
|
|
{
|
|
PixmapIcon = x.IconPixmap,
|
|
Title = x.Title,
|
|
Subtitle = x.Description,
|
|
Group = "Tags",
|
|
Column = 1,
|
|
Count = () => AssetSystem.All.Where( y => y.Tags.Contains( x.Tag ) ).Count()
|
|
};
|
|
|
|
Search.AssetTypes.Options.AddRange( AssetType.All.Where( x => !x.HiddenByDefault && !x.IsGameResource ).Select( SelectAssetTypes ).OrderBy( x => x.Title ) );
|
|
Search.AssetTypes.Options.AddRange( AssetType.All.Where( x => !x.HiddenByDefault && x.IsGameResource ).Select( SelectAssetTypes ).OrderBy( x => x.Title ) );
|
|
Search.AssetTypes.Options.AddRange( AssetTagSystem.All.Where( x => x.AutoTag ).Select( SelectAssetTags ).OrderBy( x => x.Title ) );
|
|
Search.AssetTypes.Options.AddRange( AssetTagSystem.All.Where( x => !x.AutoTag ).Select( SelectAssetTags ).OrderBy( x => x.Title ) );
|
|
}
|
|
|
|
[Event( "assetsystem.highlight" )]
|
|
public void Highlight( string path )
|
|
{
|
|
var folder = System.IO.Path.GetDirectoryName( path );
|
|
var absolutePath = FileSystem.Mounted.GetFullPath( folder );
|
|
NavigateTo( absolutePath );
|
|
|
|
foreach ( var item in AssetList.Items )
|
|
{
|
|
if ( item is AssetEntry entry && entry.FileInfo.FullName == path )
|
|
{
|
|
AssetList.SelectItem( item );
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void AddPin( string filter )
|
|
{
|
|
|
|
}
|
|
}
|