Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public partial class AssetBrowser : EditorWindow, IDragHandler

[SerializeField]
private List<string> _selectedLibraries;
private readonly Dictionary<AssetLibrary, string> _libraryAssetPaths = new();
private readonly HashSet<AssetLibrary> _pendingLibraryReindexes = new();
private bool _fullLibraryRefreshPending;

[NonSerialized]
private List<AssetResult> _assetResults;
Expand All @@ -57,8 +60,9 @@ private AssetResult LastSelectedResult {
private AssetResult _firstSelectedResult;
private readonly HashSet<AssetResult> _selectedResults = new();

private readonly Dictionary<Asset, VisualElement> _elementByAsset = new();
private readonly Dictionary<VisualElement, AssetResult> _resultByElement = new();
private readonly Dictionary<AssetResult, VisualElement> _visibleElements = new();
private readonly List<int> _gridRowStarts = new();
private int _gridColumnCount = 1;

private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

Expand Down Expand Up @@ -97,6 +101,11 @@ private void RefreshLibraries()
.Select(AssetDatabase.GUIDToAssetPath)
.Select(AssetDatabase.LoadAssetAtPath<AssetLibrary>)
.Where(lib => lib != null).ToList();
_libraryAssetPaths.Clear();
foreach (var lib in Libraries) {
lib.IsActive = selectedLibraries?.Contains(lib.Id) ?? true;
_libraryAssetPaths[lib] = NormalizeAssetPath(AssetDatabase.GetAssetPath(lib));
}

// toggle label
if (Libraries.Count == 0) {
Expand All @@ -106,24 +115,93 @@ private void RefreshLibraries()
}

// setup query
if (Query != null) {
Query.OnQueryUpdated -= OnQueryUpdated;
}
Query = new AssetQuery(Libraries.Where(lib => lib.IsActive).ToList());
Query.OnQueryUpdated += OnQueryUpdated;

// update left column and subscribe
_libraryList.Clear();
foreach (var lib in Libraries) {
lib.IsActive = selectedLibraries?.Contains(lib.Id) ?? true;
_libraryList.Add(NewAssetLibrary(lib));
lib.OnChange += OnLibraryChanged;
}
}

private void OnLibraryChanged(object sender, EventArgs e)
{
RefreshLibraries();
if (sender is AssetLibrary library) {
ScheduleLibraryReindex(library);
} else {
ScheduleFullLibraryRefresh();
}
}

private void OnAssetFilesChanged(string[] paths)
{
var normalizedPaths = paths.Select(NormalizeAssetPath).ToArray();
var libraries = (Libraries ?? Enumerable.Empty<AssetLibrary>()).ToArray();
var databaseRoots = libraries.ToDictionary(library => library, library => NormalizeAssetPath(library.DatabaseRoot));
if (normalizedPaths.Any(path => _libraryAssetPaths.Values.Contains(path))) {
ScheduleFullLibraryRefresh();
return;
}
var pathsOutsideDatabases = normalizedPaths
.Where(path => databaseRoots.Values.All(root => !IsPathWithin(path, root)));
if (pathsOutsideDatabases.Any(path => AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(AssetLibrary))) {
ScheduleFullLibraryRefresh();
return;
}
foreach (var library in libraries) {
if (normalizedPaths.Any(path => IsPathWithin(path, databaseRoots[library]))) {
ScheduleLibraryReindex(library);
}
}
}

private void ScheduleLibraryReindex(AssetLibrary library)
{
if (library != null) {
_pendingLibraryReindexes.Add(library);
}
ScheduleLibraryRefresh();
}

private void ScheduleFullLibraryRefresh()
{
_fullLibraryRefreshPending = true;
ScheduleLibraryRefresh();
}

private void ScheduleLibraryRefresh()
{
_libraryRefreshScheduledItem?.Pause();
_libraryRefreshScheduledItem = rootVisualElement.schedule.Execute(ApplyPendingLibraryChanges).StartingIn(50);
}

private void ApplyPendingLibraryChanges()
{
if (_fullLibraryRefreshPending) {
_fullLibraryRefreshPending = false;
_pendingLibraryReindexes.Clear();
Refresh();
return;
}
var libraries = _pendingLibraryReindexes.ToArray();
_pendingLibraryReindexes.Clear();
foreach (var library in libraries.Where(library => Libraries.Contains(library))) {
Query.Reindex(library);
}
RefreshCategories();
Comment thread
greptile-apps[bot] marked this conversation as resolved.
RefreshAssets();
}

private static string NormalizeAssetPath(string path) => (path ?? string.Empty).Replace('\\', '/').TrimEnd('/');

private static bool IsPathWithin(string path, string root) => root.Length > 0
&& (string.Equals(path, root, StringComparison.Ordinal) || path.StartsWith(root + "/", StringComparison.Ordinal));

public void FilterByAttribute(string attributeKey, string value, bool remove = false)
{
var queryString = attributeKey.Contains(" ") ? $"\"{attributeKey}\":" : $"{attributeKey}:";
Expand Down Expand Up @@ -172,20 +250,10 @@ private void OnQueryUpdated(object sender, AssetQueryResult e)
private void UpdateQueryResults(List<AssetResult> results, long duration)
{
_assetResults = results;
_gridContent.Clear();
_elementByAsset.Clear();
_resultByElement.Clear();
_selectedResults.Clear();

LastSelectedResult = null;
foreach (var row in results) {
var element = NewItem(row);
_elementByAsset[row.Asset] = element;
_resultByElement[element] = row;
_gridContent.Add(element);
}

_gridContent.MarkDirtyRepaint(); // todo doesn't work, scrolling is still screwed.
RebuildGridRows(true);

if (!results.Contains(_firstSelectedResult)) {
_firstSelectedResult = null;
Expand All @@ -196,19 +264,11 @@ private void UpdateQueryResults(List<AssetResult> results, long duration)
_statusLabel.text = $"Found {results.Count} asset" + (results.Count == 1 ? "" : "s") + $" in {duration}ms.";
}

private void AddAssetContextMenu(ContextualMenuPopulateEvent evt)
private void AddAssetContextMenu(ContextualMenuPopulateEvent evt, AssetResult clickedAsset)
{
if (evt.target is not VisualElement target) {
Debug.Log("Early out in AddAssetContextMenu, target is no VisualElement.");
if (clickedAsset == null) {
return;
}

if (!_resultByElement.ContainsKey(target.parent.parent)) {
Debug.Log($"Early out in AddAssetContextMenu, {target.parent.parent} not in resultByElement.");
return;
}

var clickedAsset = _resultByElement[target.parent.parent];
var libs = clickedAsset.Asset.Libraries;
if (libs.All(l => l.IsLocked)) {
Debug.Log("Early out in AddAssetContextMenu, all libraries are locked.");
Expand All @@ -219,15 +279,14 @@ private void AddAssetContextMenu(ContextualMenuPopulateEvent evt)
foreach (var lib in libs.Where(l => !l.IsLocked)) {
evt.menu.AppendAction($"Remove from {lib.Name}", _ => {
if (_selectedResults.Add(clickedAsset)) {
ToggleSelectionClass(_elementByAsset[clickedAsset.Asset]);
UpdateSelectionClass(clickedAsset);
}
var numRemovedAssets = 0;
foreach (var assetResult in _selectedResults.Where(a => lib.HasAsset(a.Asset.GUID)).ToList()) {
_selectedResults.Remove(assetResult);
lib.DeleteAsset(assetResult.Asset);
if (libs.Length == 1 && _thumbCache.ContainsKey(assetResult.Asset.GUID)) {
DestroyImmediate(_thumbCache[assetResult.Asset.GUID]);
_thumbCache.Remove(assetResult.Asset.GUID);
if (libs.Length == 1) {
RemoveCachedThumbnails(assetResult.Asset.GUID);
}
numRemovedAssets++;
}
Expand Down Expand Up @@ -300,7 +359,7 @@ private void AddAssetContextMenu(ContextualMenuPopulateEvent evt)
foreach (var attr in srcAsset.Attributes) {
destAsset.AddAttribute(attr.Key, attr.Value);
}
foreach (var link in srcAsset.Links.Where(link => destAsset.Links.FirstOrDefault(l => l.Name == link.Name) != null)) {
foreach (var link in srcAsset.Links.Where(link => destAsset.Links.All(l => l.Name != link.Name))) {
destAsset.Links.Add(new AssetLink(link.Name, link.Url));
}

Expand Down Expand Up @@ -330,7 +389,9 @@ private void OnAssetClicked(ClickEvent evt, VisualElement element)
if (evt.button != 0) {
return;
}
var clickedAsset = _resultByElement[element];
if (element.userData is not AssetResult clickedAsset) {
return;
}

// no modifier pressed
if (!evt.shiftKey && !evt.ctrlKey) {
Expand Down Expand Up @@ -370,7 +431,12 @@ private void OnAssetClicked(ClickEvent evt, VisualElement element)
}

public void OnCategoriesUpdated(Dictionary<AssetLibrary, List<AssetCategory>> categories) => Query.Filter(categories);
private void OnSearchQueryChanged(ChangeEvent<string> evt) => Query.Search(evt.newValue);
private void OnSearchQueryChanged(ChangeEvent<string> evt)
{
_pendingSearch = evt.newValue;
_searchScheduledItem?.Pause();
_searchScheduledItem = _queryInput.schedule.Execute(() => Query?.Search(_pendingSearch)).StartingIn(200);
}
private void OnLibraryToggled(AssetLibrary lib, bool enabled)
{
lib.IsActive = enabled;
Expand All @@ -381,11 +447,14 @@ private void OnLibraryToggled(AssetLibrary lib, bool enabled)

public AssetLibrary GetLibraryByPath(string pathToCheck)
{
pathToCheck = pathToCheck.Replace('\\', '/');
return Libraries.FirstOrDefault(assetLibrary => {
var libraryPath = assetLibrary.LibraryRoot.Replace('\\', '/');
return pathToCheck.StartsWith(libraryPath);
});
pathToCheck = pathToCheck.Replace('\\', '/').TrimEnd('/');
return Libraries
.OrderByDescending(assetLibrary => assetLibrary.LibraryRoot.Length)
.FirstOrDefault(assetLibrary => {
var libraryPath = assetLibrary.LibraryRoot.Replace('\\', '/').TrimEnd('/');
return string.Equals(pathToCheck, libraryPath, StringComparison.Ordinal)
|| pathToCheck.StartsWith(libraryPath + "/", StringComparison.Ordinal);
});
}

public void AddAssets(IEnumerable<string> paths, Func<AssetLibrary, AssetCategory> getCategory)
Expand Down Expand Up @@ -438,19 +507,19 @@ private void SelectRange(int start, int end)
if (i >= start && i <= end) {
if (!_selectedResults.Contains(asset)) {
_selectedResults.Add(asset);
ToggleSelectionClass(_elementByAsset[asset.Asset]);
UpdateSelectionClass(asset);
}
} else if (_selectedResults.Contains(asset)) {
_selectedResults.Remove(asset);
ToggleSelectionClass(_elementByAsset[asset.Asset]);
UpdateSelectionClass(asset);
}
}
}

private void SelectNone()
{
foreach (var selectedAsset in _selectedResults) {
ToggleSelectionClass(_elementByAsset[selectedAsset.Asset]);
UpdateSelectionClass(selectedAsset, false);
}
_selectedResults.Clear();
_firstSelectedResult = null;
Expand All @@ -462,15 +531,15 @@ private void SelectOnly(AssetResult result)
var wasAlreadySelected = false;
foreach (var selectedAsset in _selectedResults) {
if (selectedAsset != result) {
ToggleSelectionClass(_elementByAsset[selectedAsset.Asset]);
UpdateSelectionClass(selectedAsset, false);
} else {
wasAlreadySelected = true;
}
}
_selectedResults.Clear();
_selectedResults.Add(result);
if (!wasAlreadySelected) {
ToggleSelectionClass(_elementByAsset[result.Asset]);
UpdateSelectionClass(result, true);
}
_firstSelectedResult = result;
LastSelectedResult = result;
Expand All @@ -479,7 +548,7 @@ private void SelectOnly(AssetResult result)
private void UnSelect(AssetResult result)
{
_selectedResults.Remove(result);
ToggleSelectionClass(_elementByAsset[result.Asset]);
UpdateSelectionClass(result, false);
_firstSelectedResult = _selectedResults.Count > 0 ? _selectedResults.FirstOrDefault() : null;
LastSelectedResult = _selectedResults.Count > 0 ? _selectedResults.LastOrDefault() : null;
}
Expand All @@ -488,11 +557,16 @@ private void UnSelect(AssetResult result)
private void Select(AssetResult result)
{
_selectedResults.Add(result);
ToggleSelectionClass(_elementByAsset[result.Asset]);
UpdateSelectionClass(result, true);
LastSelectedResult = result;
}

private static void ToggleSelectionClass(VisualElement element) => element.ToggleInClassList("selected");
private void UpdateSelectionClass(AssetResult result, bool? selected = null)
{
if (_visibleElements.TryGetValue(result, out var element)) {
element.EnableInClassList("selected", selected ?? _selectedResults.Contains(result));
}
}

#endregion Selection

Expand Down Expand Up @@ -567,26 +641,23 @@ private void OnDragPerformEvent(DragPerformEvent evt)

public void RefreshThumb(Asset asset)
{
if (_thumbCache.ContainsKey(asset.GUID)) {
DestroyImmediate(_thumbCache[asset.GUID]);
_thumbCache.Remove(asset.GUID);
}
if (_elementByAsset.ContainsKey(asset)) {
LoadThumb(_elementByAsset[asset], asset);
RemoveCachedThumbnails(asset.GUID);
var visibleResult = _visibleElements.Keys.FirstOrDefault(result => result.Asset == asset);
if (visibleResult != null) {
LoadThumb(_visibleElements[visibleResult], asset);
}
}

private void OnThumbSizeChanged(ChangeEvent<float> evt)
{
_thumbnailSize = (int)evt.newValue;
foreach (var e in _elementByAsset.Values) {
e.Q<LibraryAssetElement>().SetSize(_thumbnailSize);
}
RebuildGridRows();
}

public void AttachData(AssetResult clickedResult)
{
_selectedResults.Add(clickedResult);
UpdateSelectionClass(clickedResult, true);
DragAndDrop.objectReferences = _selectedResults.Select(result => result.Asset.Object).ToArray();
StartDraggingAssets(_selectedResults);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ LibraryCategoryView {
background-color: #333333; /* sampled from project window */
}

#gridContent .unity-scroll-view__content-container {
#gridContent .unity-collection-view__item {
padding: 0;
}

.asset-grid-row {
flex-direction: row;
flex-wrap: wrap;
padding: 10px;
padding-left: 10px;
padding-right: 10px;
}

#gridContent .unity-image {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<ui:VisualElement>

<!-- CONTENT -->
<ui:ScrollView name="gridContent" focusable="true"/>
<ui:ListView name="gridContent" focusable="true"/>

<!-- ERROR PANEL -->
<ui:VisualElement name="dragErrorContainer" class="hidden">
Expand Down
Loading
Loading