Skip to content

Commit

Permalink
Fixed performance issues that occur when selecting lots of files
Browse files Browse the repository at this point in the history
  • Loading branch information
yasirkula committed Nov 2, 2020
1 parent 4476eda commit 52ecce6
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 64 deletions.
162 changes: 107 additions & 55 deletions Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public override string ToString()
private const string FOLDERS_FILTER_TEXT = "Folders";
private string DEFAULT_PATH;

private const int FILENAME_INPUT_FIELD_MAX_FILE_COUNT = 7;

#if !UNITY_EDITOR && UNITY_ANDROID
private const string SAF_PICK_FOLDER_QUICK_LINK_TEXT = "Pick Folder";
private const string SAF_PICK_FOLDER_QUICK_LINK_PATH = "SAF_PICK_FOLDER";
Expand Down Expand Up @@ -365,13 +367,19 @@ private string CurrentPath
if( value != null )
value = GetPathWithoutTrailingDirectorySeparator( value.Trim() );

if( value == null )
if( string.IsNullOrEmpty( value ) )
{
pathInputField.text = m_currentPath;
return;
}

if( m_currentPath != value )
{
if( !FileBrowserHelpers.DirectoryExists( value ) )
{
pathInputField.text = m_currentPath;
return;
}

m_currentPath = value;
pathInputField.text = m_currentPath;
Expand Down Expand Up @@ -404,7 +412,10 @@ private string CurrentPath

filenameImage.color = Color.white;
if( m_folderSelectMode )
{
filenameInputField.text = string.Empty;
filenameInputField.interactable = true;
}
}

m_multiSelectionToggleSelectionMode = false;
Expand Down Expand Up @@ -465,8 +476,8 @@ private bool FolderSelectMode
}

Text placeholder = filenameInputField.placeholder as Text;
if( placeholder != null )
placeholder.text = m_folderSelectMode ? string.Empty : "Filename";
if( placeholder )
placeholder.gameObject.SetActive( !m_folderSelectMode );
}
}
}
Expand Down Expand Up @@ -873,64 +884,85 @@ public void OnSubmitButtonClicked()
return;
}

// In the first iteration, verify that all filenames entered to the input field are valid
// ExtractFilenameFromInput doesn't use Substring, so this iteration is GC-free
int startIndex = 0, nextStartIndex;
int fileCount = 0;
int indexOfDirectoryEntryToOpen = -1;
while( startIndex < filenameInput.Length )
if( m_allowMultiSelection && selectedFileEntries.Count > 1 )
{
int filenameLength = ExtractFilenameFromInput( filenameInput, ref startIndex, out nextStartIndex );
if( filenameLength == 0 )
continue;
// When multiple files are selected via file browser UI, filenameInputField is not interactable and will show
// only the first FILENAME_INPUT_FIELD_MAX_FILE_COUNT entries for performance reasons. We should iterate over
// selectedFileEntries instead of filenameInputField

if( m_acceptNonExistingFilename )
fileCount++;
else
// Beforehand, check if a folder is selected in file selection mode. If so, open that directory
if( !m_folderSelectMode )
{
int fileEntryIndex = FilenameInputToFileEntryIndex( filenameInput, startIndex, filenameLength );
if( fileEntryIndex < 0 )
{
// File doesn't exist
filenameImage.color = wrongFilenameColor;
return;
}

if( validFileEntries[fileEntryIndex].IsDirectory )
for( int i = 0; i < selectedFileEntries.Count; i++ )
{
if( m_folderSelectMode )
fileCount++;
else
if( validFileEntries[selectedFileEntries[i]].IsDirectory )
{
// Selected a directory in file selection mode, we'll open that directory if no files are selected
indexOfDirectoryEntryToOpen = fileEntryIndex;
CurrentPath = validFileEntries[selectedFileEntries[i]].Path;
return;
}
}
}

string[] result = new string[selectedFileEntries.Count];
for( int i = 0; i < selectedFileEntries.Count; i++ )
result[i] = validFileEntries[selectedFileEntries[i]].Path;

OnOperationSuccessful( result );
}
else
{
// When multiple files aren't selected via file browser UI, we must consider the rare case where user manually enters
// multiple filenames to filenameInputField in format "file1" "file2" and so on. So, we must parse filenameInputField

// In the first iteration, verify that all filenames entered to the input field are valid
// ExtractFilenameFromInput doesn't use Substring, so this iteration is GC-free
int fileCount = 0;
int startIndex = 0, nextStartIndex;
while( startIndex < filenameInput.Length )
{
int filenameLength = ExtractFilenameFromInput( filenameInput, ref startIndex, out nextStartIndex );
if( filenameLength == 0 )
continue;

if( m_acceptNonExistingFilename )
fileCount++;
else
{
if( !m_folderSelectMode )
fileCount++;
else
int fileEntryIndex = FilenameInputToFileEntryIndex( filenameInput, startIndex, filenameLength );
if( fileEntryIndex < 0 )
{
// Can't select a file in folder selection mode
// File doesn't exist
filenameImage.color = wrongFilenameColor;
return;
}

if( !validFileEntries[fileEntryIndex].IsDirectory )
fileCount++;
else
{
if( m_folderSelectMode )
fileCount++;
else
{
// Selected a directory in file selection mode, open that directory
CurrentPath = validFileEntries[fileEntryIndex].Path;
return;
}
}
}

startIndex = nextStartIndex;
}

startIndex = nextStartIndex;
}
if( fileCount == 0 )
{
filenameImage.color = wrongFilenameColor;
return;
}

if( indexOfDirectoryEntryToOpen >= 0 )
CurrentPath = validFileEntries[indexOfDirectoryEntryToOpen].Path;
else if( fileCount == 0 )
filenameImage.color = wrongFilenameColor;
else
{
// In the second iteration, extract filenames from the input field
string[] result = new string[fileCount];

// In the second iteration, extract filenames from the input field
startIndex = 0;
fileCount = 0;
while( startIndex < filenameInput.Length )
Expand Down Expand Up @@ -1271,6 +1303,7 @@ public void Show( string initialPath )
filesScrollRect.verticalNormalizedPosition = 1;

filenameInputField.text = string.Empty;
filenameInputField.interactable = true;
filenameImage.color = Color.white;

IsOpen = true;
Expand Down Expand Up @@ -1376,6 +1409,14 @@ public void RefreshFiles( bool pathChanged )
pendingFileEntrySelection.Clear();
}

if( !filenameInputField.interactable && selectedFileEntries.Count <= 1 )
{
filenameInputField.interactable = true;

if( selectedFileEntries.Count == 0 )
filenameInputField.text = string.Empty;
}

listView.UpdateList();

// Prevent the case where all the content stays offscreen after changing the search string
Expand Down Expand Up @@ -1412,6 +1453,8 @@ public void DeselectAllFiles()
MultiSelectionToggleSelectionMode = false;

filenameInputField.text = string.Empty;
filenameInputField.interactable = true;

listView.UpdateList();
}

Expand All @@ -1431,6 +1474,9 @@ private IEnumerator CreateNewFolderCoroutine()
selectedFileEntries.Clear();
MultiSelectionToggleSelectionMode = false;

filenameInputField.text = string.Empty;
filenameInputField.interactable = true;

listView.UpdateList();
}

Expand Down Expand Up @@ -1546,16 +1592,7 @@ public void DeleteSelectedFiles()

selectedFileEntries.Sort();

Sprite[] icons = new Sprite[selectedFileEntries.Count];
string[] filenames = new string[selectedFileEntries.Count];
for( int i = 0; i < selectedFileEntries.Count; i++ )
{
FileSystemEntry fileInfo = validFileEntries[selectedFileEntries[i]];
icons[i] = GetIconForFileEntry( fileInfo );
filenames[i] = fileInfo.Name;
}

deleteConfirmationPanel.Show( icons, filenames, () =>
deleteConfirmationPanel.Show( this, validFileEntries, selectedFileEntries, () =>
{
for( int i = selectedFileEntries.Count - 1; i >= 0; i-- )
{
Expand Down Expand Up @@ -1702,7 +1739,7 @@ internal void OnWindowDimensionsChanged( Vector2 size )
}
}

private Sprite GetIconForFileEntry( FileSystemEntry fileInfo )
internal Sprite GetIconForFileEntry( FileSystemEntry fileInfo )
{
Sprite icon;
if( fileInfo.IsDirectory )
Expand Down Expand Up @@ -1741,7 +1778,7 @@ private void UpdateFilenameInputFieldWithSelection()
// Refresh filenameInputField as follows:
// 0 files selected: *blank*
// 1 file selected: file.Name
// 2+ files selected: "file1.Name" "file2.Name" ...
// 2+ files selected: "file1.Name" "file2.Name" ... (up to FILENAME_INPUT_FIELD_MAX_FILE_COUNT filenames are displayed for performance reasons)
int filenameContributingFileCount = 0;
if( FolderSelectMode )
filenameContributingFileCount = selectedFileEntries.Count;
Expand All @@ -1750,10 +1787,17 @@ private void UpdateFilenameInputFieldWithSelection()
for( int i = 0; i < selectedFileEntries.Count; i++ )
{
if( !validFileEntries[selectedFileEntries[i]].IsDirectory )
{
filenameContributingFileCount++;

if( filenameContributingFileCount >= FILENAME_INPUT_FIELD_MAX_FILE_COUNT )
break;
}
}
}

filenameInputField.interactable = selectedFileEntries.Count <= 1;

if( filenameContributingFileCount == 0 )
filenameInputField.text = string.Empty;
else
Expand All @@ -1766,7 +1810,7 @@ private void UpdateFilenameInputFieldWithSelection()
multiSelectionFilenameBuilder.Length = 0;
}

for( int i = 0; i < selectedFileEntries.Count; i++ )
for( int i = 0, fileCount = 0; i < selectedFileEntries.Count; i++ )
{
FileSystemEntry selectedFile = validFileEntries[selectedFileEntries[i]];
if( FolderSelectMode || !selectedFile.IsDirectory )
Expand All @@ -1777,7 +1821,15 @@ private void UpdateFilenameInputFieldWithSelection()
break;
}
else
{
multiSelectionFilenameBuilder.Append( "\"" ).Append( selectedFile.Name ).Append( "\" " );

if( ++fileCount >= FILENAME_INPUT_FIELD_MAX_FILE_COUNT )
{
multiSelectionFilenameBuilder.Append( "..." );
break;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace SimpleFileBrowser
Expand Down Expand Up @@ -35,22 +36,22 @@ public class FileBrowserDeleteConfirmationPanel : MonoBehaviour

private OnDeletionConfirmed onDeletionConfirmed;

internal void Show( Sprite[] icons, string[] filenames, OnDeletionConfirmed onDeletionConfirmed )
internal void Show( FileBrowser fileBrowser, List<FileSystemEntry> items, List<int> selectedItemIndices, OnDeletionConfirmed onDeletionConfirmed )
{
this.onDeletionConfirmed = onDeletionConfirmed;

for( int i = 0; i < deletedItems.Length; i++ )
deletedItems[i].SetActive( i < icons.Length );
deletedItems[i].SetActive( i < selectedItemIndices.Count );

for( int i = 0; i < deletedItems.Length && i < icons.Length; i++ )
for( int i = 0; i < deletedItems.Length && i < selectedItemIndices.Count; i++ )
{
deletedItemIcons[i].sprite = icons[i];
deletedItemNames[i].text = filenames[i];
deletedItemIcons[i].sprite = fileBrowser.GetIconForFileEntry( items[selectedItemIndices[i]] );
deletedItemNames[i].text = items[selectedItemIndices[i]].Name;
}

if( icons.Length > deletedItems.Length )
if( selectedItemIndices.Count > deletedItems.Length )
{
deletedItemsRestLabel.text = string.Concat( "...and ", ( icons.Length - deletedItems.Length ).ToString(), " other" );
deletedItemsRestLabel.text = string.Concat( "...and ", ( selectedItemIndices.Count - deletedItems.Length ).ToString(), " other" );
deletedItemsRest.SetActive( true );
}
else
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "com.yasirkula.simplefilebrowser",
"displayName": "Simple File Browser",
"version": "1.3.5",
"version": "1.3.6",
"description": "This plugin helps you show save/load dialogs during gameplay with its uGUI based file browser."
}

0 comments on commit 52ecce6

Please sign in to comment.