Skip to content

Commit 52ecce6

Browse files
committed
Fixed performance issues that occur when selecting lots of files
1 parent 4476eda commit 52ecce6

File tree

3 files changed

+117
-64
lines changed

3 files changed

+117
-64
lines changed

Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs

Lines changed: 107 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ public override string ToString()
103103
private const string FOLDERS_FILTER_TEXT = "Folders";
104104
private string DEFAULT_PATH;
105105

106+
private const int FILENAME_INPUT_FIELD_MAX_FILE_COUNT = 7;
107+
106108
#if !UNITY_EDITOR && UNITY_ANDROID
107109
private const string SAF_PICK_FOLDER_QUICK_LINK_TEXT = "Pick Folder";
108110
private const string SAF_PICK_FOLDER_QUICK_LINK_PATH = "SAF_PICK_FOLDER";
@@ -365,13 +367,19 @@ private string CurrentPath
365367
if( value != null )
366368
value = GetPathWithoutTrailingDirectorySeparator( value.Trim() );
367369

368-
if( value == null )
370+
if( string.IsNullOrEmpty( value ) )
371+
{
372+
pathInputField.text = m_currentPath;
369373
return;
374+
}
370375

371376
if( m_currentPath != value )
372377
{
373378
if( !FileBrowserHelpers.DirectoryExists( value ) )
379+
{
380+
pathInputField.text = m_currentPath;
374381
return;
382+
}
375383

376384
m_currentPath = value;
377385
pathInputField.text = m_currentPath;
@@ -404,7 +412,10 @@ private string CurrentPath
404412

405413
filenameImage.color = Color.white;
406414
if( m_folderSelectMode )
415+
{
407416
filenameInputField.text = string.Empty;
417+
filenameInputField.interactable = true;
418+
}
408419
}
409420

410421
m_multiSelectionToggleSelectionMode = false;
@@ -465,8 +476,8 @@ private bool FolderSelectMode
465476
}
466477

467478
Text placeholder = filenameInputField.placeholder as Text;
468-
if( placeholder != null )
469-
placeholder.text = m_folderSelectMode ? string.Empty : "Filename";
479+
if( placeholder )
480+
placeholder.gameObject.SetActive( !m_folderSelectMode );
470481
}
471482
}
472483
}
@@ -873,64 +884,85 @@ public void OnSubmitButtonClicked()
873884
return;
874885
}
875886

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

887-
if( m_acceptNonExistingFilename )
888-
fileCount++;
889-
else
893+
// Beforehand, check if a folder is selected in file selection mode. If so, open that directory
894+
if( !m_folderSelectMode )
890895
{
891-
int fileEntryIndex = FilenameInputToFileEntryIndex( filenameInput, startIndex, filenameLength );
892-
if( fileEntryIndex < 0 )
893-
{
894-
// File doesn't exist
895-
filenameImage.color = wrongFilenameColor;
896-
return;
897-
}
898-
899-
if( validFileEntries[fileEntryIndex].IsDirectory )
896+
for( int i = 0; i < selectedFileEntries.Count; i++ )
900897
{
901-
if( m_folderSelectMode )
902-
fileCount++;
903-
else
898+
if( validFileEntries[selectedFileEntries[i]].IsDirectory )
904899
{
905-
// Selected a directory in file selection mode, we'll open that directory if no files are selected
906-
indexOfDirectoryEntryToOpen = fileEntryIndex;
900+
CurrentPath = validFileEntries[selectedFileEntries[i]].Path;
901+
return;
907902
}
908903
}
904+
}
905+
906+
string[] result = new string[selectedFileEntries.Count];
907+
for( int i = 0; i < selectedFileEntries.Count; i++ )
908+
result[i] = validFileEntries[selectedFileEntries[i]].Path;
909+
910+
OnOperationSuccessful( result );
911+
}
912+
else
913+
{
914+
// When multiple files aren't selected via file browser UI, we must consider the rare case where user manually enters
915+
// multiple filenames to filenameInputField in format "file1" "file2" and so on. So, we must parse filenameInputField
916+
917+
// In the first iteration, verify that all filenames entered to the input field are valid
918+
// ExtractFilenameFromInput doesn't use Substring, so this iteration is GC-free
919+
int fileCount = 0;
920+
int startIndex = 0, nextStartIndex;
921+
while( startIndex < filenameInput.Length )
922+
{
923+
int filenameLength = ExtractFilenameFromInput( filenameInput, ref startIndex, out nextStartIndex );
924+
if( filenameLength == 0 )
925+
continue;
926+
927+
if( m_acceptNonExistingFilename )
928+
fileCount++;
909929
else
910930
{
911-
if( !m_folderSelectMode )
912-
fileCount++;
913-
else
931+
int fileEntryIndex = FilenameInputToFileEntryIndex( filenameInput, startIndex, filenameLength );
932+
if( fileEntryIndex < 0 )
914933
{
915-
// Can't select a file in folder selection mode
934+
// File doesn't exist
916935
filenameImage.color = wrongFilenameColor;
917936
return;
918937
}
938+
939+
if( !validFileEntries[fileEntryIndex].IsDirectory )
940+
fileCount++;
941+
else
942+
{
943+
if( m_folderSelectMode )
944+
fileCount++;
945+
else
946+
{
947+
// Selected a directory in file selection mode, open that directory
948+
CurrentPath = validFileEntries[fileEntryIndex].Path;
949+
return;
950+
}
951+
}
919952
}
953+
954+
startIndex = nextStartIndex;
920955
}
921956

922-
startIndex = nextStartIndex;
923-
}
957+
if( fileCount == 0 )
958+
{
959+
filenameImage.color = wrongFilenameColor;
960+
return;
961+
}
924962

925-
if( indexOfDirectoryEntryToOpen >= 0 )
926-
CurrentPath = validFileEntries[indexOfDirectoryEntryToOpen].Path;
927-
else if( fileCount == 0 )
928-
filenameImage.color = wrongFilenameColor;
929-
else
930-
{
963+
// In the second iteration, extract filenames from the input field
931964
string[] result = new string[fileCount];
932965

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

12731305
filenameInputField.text = string.Empty;
1306+
filenameInputField.interactable = true;
12741307
filenameImage.color = Color.white;
12751308

12761309
IsOpen = true;
@@ -1376,6 +1409,14 @@ public void RefreshFiles( bool pathChanged )
13761409
pendingFileEntrySelection.Clear();
13771410
}
13781411

1412+
if( !filenameInputField.interactable && selectedFileEntries.Count <= 1 )
1413+
{
1414+
filenameInputField.interactable = true;
1415+
1416+
if( selectedFileEntries.Count == 0 )
1417+
filenameInputField.text = string.Empty;
1418+
}
1419+
13791420
listView.UpdateList();
13801421

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

14141455
filenameInputField.text = string.Empty;
1456+
filenameInputField.interactable = true;
1457+
14151458
listView.UpdateList();
14161459
}
14171460

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

1477+
filenameInputField.text = string.Empty;
1478+
filenameInputField.interactable = true;
1479+
14341480
listView.UpdateList();
14351481
}
14361482

@@ -1546,16 +1592,7 @@ public void DeleteSelectedFiles()
15461592

15471593
selectedFileEntries.Sort();
15481594

1549-
Sprite[] icons = new Sprite[selectedFileEntries.Count];
1550-
string[] filenames = new string[selectedFileEntries.Count];
1551-
for( int i = 0; i < selectedFileEntries.Count; i++ )
1552-
{
1553-
FileSystemEntry fileInfo = validFileEntries[selectedFileEntries[i]];
1554-
icons[i] = GetIconForFileEntry( fileInfo );
1555-
filenames[i] = fileInfo.Name;
1556-
}
1557-
1558-
deleteConfirmationPanel.Show( icons, filenames, () =>
1595+
deleteConfirmationPanel.Show( this, validFileEntries, selectedFileEntries, () =>
15591596
{
15601597
for( int i = selectedFileEntries.Count - 1; i >= 0; i-- )
15611598
{
@@ -1702,7 +1739,7 @@ internal void OnWindowDimensionsChanged( Vector2 size )
17021739
}
17031740
}
17041741

1705-
private Sprite GetIconForFileEntry( FileSystemEntry fileInfo )
1742+
internal Sprite GetIconForFileEntry( FileSystemEntry fileInfo )
17061743
{
17071744
Sprite icon;
17081745
if( fileInfo.IsDirectory )
@@ -1741,7 +1778,7 @@ private void UpdateFilenameInputFieldWithSelection()
17411778
// Refresh filenameInputField as follows:
17421779
// 0 files selected: *blank*
17431780
// 1 file selected: file.Name
1744-
// 2+ files selected: "file1.Name" "file2.Name" ...
1781+
// 2+ files selected: "file1.Name" "file2.Name" ... (up to FILENAME_INPUT_FIELD_MAX_FILE_COUNT filenames are displayed for performance reasons)
17451782
int filenameContributingFileCount = 0;
17461783
if( FolderSelectMode )
17471784
filenameContributingFileCount = selectedFileEntries.Count;
@@ -1750,10 +1787,17 @@ private void UpdateFilenameInputFieldWithSelection()
17501787
for( int i = 0; i < selectedFileEntries.Count; i++ )
17511788
{
17521789
if( !validFileEntries[selectedFileEntries[i]].IsDirectory )
1790+
{
17531791
filenameContributingFileCount++;
1792+
1793+
if( filenameContributingFileCount >= FILENAME_INPUT_FIELD_MAX_FILE_COUNT )
1794+
break;
1795+
}
17541796
}
17551797
}
17561798

1799+
filenameInputField.interactable = selectedFileEntries.Count <= 1;
1800+
17571801
if( filenameContributingFileCount == 0 )
17581802
filenameInputField.text = string.Empty;
17591803
else
@@ -1766,7 +1810,7 @@ private void UpdateFilenameInputFieldWithSelection()
17661810
multiSelectionFilenameBuilder.Length = 0;
17671811
}
17681812

1769-
for( int i = 0; i < selectedFileEntries.Count; i++ )
1813+
for( int i = 0, fileCount = 0; i < selectedFileEntries.Count; i++ )
17701814
{
17711815
FileSystemEntry selectedFile = validFileEntries[selectedFileEntries[i]];
17721816
if( FolderSelectMode || !selectedFile.IsDirectory )
@@ -1777,7 +1821,15 @@ private void UpdateFilenameInputFieldWithSelection()
17771821
break;
17781822
}
17791823
else
1824+
{
17801825
multiSelectionFilenameBuilder.Append( "\"" ).Append( selectedFile.Name ).Append( "\" " );
1826+
1827+
if( ++fileCount >= FILENAME_INPUT_FIELD_MAX_FILE_COUNT )
1828+
{
1829+
multiSelectionFilenameBuilder.Append( "..." );
1830+
break;
1831+
}
1832+
}
17811833
}
17821834
}
17831835

Plugins/SimpleFileBrowser/Scripts/FileBrowserDeleteConfirmationPanel.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using UnityEngine;
1+
using System.Collections.Generic;
2+
using UnityEngine;
23
using UnityEngine.UI;
34

45
namespace SimpleFileBrowser
@@ -35,22 +36,22 @@ public class FileBrowserDeleteConfirmationPanel : MonoBehaviour
3536

3637
private OnDeletionConfirmed onDeletionConfirmed;
3738

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

4243
for( int i = 0; i < deletedItems.Length; i++ )
43-
deletedItems[i].SetActive( i < icons.Length );
44+
deletedItems[i].SetActive( i < selectedItemIndices.Count );
4445

45-
for( int i = 0; i < deletedItems.Length && i < icons.Length; i++ )
46+
for( int i = 0; i < deletedItems.Length && i < selectedItemIndices.Count; i++ )
4647
{
47-
deletedItemIcons[i].sprite = icons[i];
48-
deletedItemNames[i].text = filenames[i];
48+
deletedItemIcons[i].sprite = fileBrowser.GetIconForFileEntry( items[selectedItemIndices[i]] );
49+
deletedItemNames[i].text = items[selectedItemIndices[i]].Name;
4950
}
5051

51-
if( icons.Length > deletedItems.Length )
52+
if( selectedItemIndices.Count > deletedItems.Length )
5253
{
53-
deletedItemsRestLabel.text = string.Concat( "...and ", ( icons.Length - deletedItems.Length ).ToString(), " other" );
54+
deletedItemsRestLabel.text = string.Concat( "...and ", ( selectedItemIndices.Count - deletedItems.Length ).ToString(), " other" );
5455
deletedItemsRest.SetActive( true );
5556
}
5657
else

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "com.yasirkula.simplefilebrowser",
33
"displayName": "Simple File Browser",
4-
"version": "1.3.5",
4+
"version": "1.3.6",
55
"description": "This plugin helps you show save/load dialogs during gameplay with its uGUI based file browser."
66
}

0 commit comments

Comments
 (0)