diff --git a/Dalamud/Interface/ImGuiFileDialog/DriveListLoader.cs b/Dalamud/Interface/ImGuiFileDialog/DriveListLoader.cs new file mode 100644 index 000000000..35271a953 --- /dev/null +++ b/Dalamud/Interface/ImGuiFileDialog/DriveListLoader.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Threading.Tasks; + +namespace Dalamud.Interface.ImGuiFileDialog; + +/// +/// A drive list loader. Thread-safety guaranteed. +/// +public class DriveListLoader +{ + private bool initialized; + + /// + /// Initializes a new instance of the class. + /// + public DriveListLoader() + { + this.Drives = ImmutableArray.Empty; + } + + /// + /// Gets the drive list. This may be incomplete if the loader is still loading. + /// + public IReadOnlyList Drives { get; private set; } + + /// + /// Gets a value indicating whether or not the loader is loading. + /// + public bool Loading { get; private set; } + + /// + /// Loads the drive list, asynchronously. + /// + /// A representing the asynchronous operation. + public async Task LoadDrivesAsync() + { + this.Loading = true; + try + { + await this.InitDrives(); + } + finally + { + this.Loading = false; + } + } + + private async Task InitDrives() + { + var drives = ImmutableArray.Empty; + foreach (var drive in DriveInfo.GetDrives()) + { + drives = drives.Add(drive); + if (!this.initialized) + { + // Show results as soon as they load initially, but otherwise keep + // the existing drive list + this.Drives = drives; + } + + // Force async to avoid this being invoked synchronously unless it's awaited + await Task.Yield(); + } + + // Replace the whole drive list + this.Drives = drives; + this.initialized = true; + } +} diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs index c34d047d9..ba71c8cfa 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs @@ -12,11 +12,13 @@ public partial class FileDialog { private readonly object filesLock = new(); + private readonly DriveListLoader driveListLoader = new(); + private List files = new(); private List filteredFiles = new(); private SortingField currentSortingField = SortingField.FileName; - private bool[] sortDescending = new[] { false, false, false, false }; + private bool[] sortDescending = { false, false, false, false }; private enum FileStructType { @@ -296,12 +298,14 @@ public partial class FileDialog } } + private IEnumerable GetDrives() + { + return this.driveListLoader.Drives.Select(drive => new SideBarItem(drive.Name, drive.Name, FontAwesomeIcon.Server)); + } + private void SetupSideBar() { - foreach (var drive in DriveInfo.GetDrives()) - { - this.drives.Add(new SideBarItem(drive.Name, drive.Name, FontAwesomeIcon.Server)); - } + _ = this.driveListLoader.LoadDrivesAsync(); var personal = Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.Personal)); diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs index c55722b4a..608ed6d6d 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs @@ -316,7 +316,7 @@ public partial class FileDialog ImGui.SetCursorPosY(ImGui.GetCursorPosY() + Scaled(5)); var idx = 0; - foreach (var qa in this.drives.Concat(this.quickAccess).Where(qa => qa.Exists)) + foreach (var qa in this.GetDrives().Concat(this.quickAccess).Where(qa => qa.Exists)) { ImGui.PushID(idx++); ImGui.SetCursorPosX(Scaled(25)); diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs index fe224be76..b0ede4fb3 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs @@ -52,7 +52,6 @@ public partial class FileDialog private float footerHeight = 0; private string selectedSideBar = string.Empty; - private List drives = new(); private List quickAccess = new(); ///