diff --git a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs index 65cf7a9e6..ccd707058 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs @@ -41,6 +41,7 @@ namespace Ryujinx.UI.App.Common private readonly byte[] _ncaIcon; private readonly byte[] _nroIcon; private readonly byte[] _nsoIcon; + private readonly byte[] _folderIcon; private readonly VirtualFileSystem _virtualFileSystem; private Language _desiredTitleLanguage; @@ -58,6 +59,7 @@ namespace Ryujinx.UI.App.Common _ncaIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NCA.png"); _nroIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NRO.png"); _nsoIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSO.png"); + _folderIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_Folder.png"); } private static byte[] GetResourceBytes(string resourceName) @@ -113,7 +115,31 @@ namespace Ryujinx.UI.App.Common try { - IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file => + IEnumerable<string> folders = Directory.EnumerateDirectories(appDir, "*", SearchOption.TopDirectoryOnly); + foreach (string folder in folders) + { + if (_cancellationToken.Token.IsCancellationRequested) + { + return; + } + + var fileInfo = new FileInfo(folder); + + var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; + ApplicationData folderData = new() + { + TitleName = fileInfo.Name, + FileExtension = "Folder", + Path = fullPath, + Icon = _folderIcon, + }; + OnApplicationAdded(new ApplicationAddedEventArgs + { + AppData = folderData, + }); + } + + IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.TopDirectoryOnly).Where(file => { return (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || @@ -163,7 +189,26 @@ namespace Ryujinx.UI.App.Common return; } - long fileSize = new FileInfo(applicationPath).Length; + var fileInfo = new FileInfo(applicationPath); + + if ((fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + Console.WriteLine($"Found directory 2: {fileInfo.Name}"); + ApplicationData folder = new() + { + TitleName = fileInfo.Name, + FileExtension = "Folder", + Developer = "null", + Path = applicationPath, + Icon = _nsoIcon, + }; + OnApplicationAdded(new ApplicationAddedEventArgs + { + AppData = folder, + }); + } + + long fileSize = fileInfo.Length; string titleName = "Unknown"; string titleId = "0000000000000000"; string developer = "Unknown"; diff --git a/src/Ryujinx.UI.Common/Resources/Icon_Folder.png b/src/Ryujinx.UI.Common/Resources/Icon_Folder.png new file mode 100644 index 000000000..dba7ff44d Binary files /dev/null and b/src/Ryujinx.UI.Common/Resources/Icon_Folder.png differ diff --git a/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj b/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj index 387e998b0..eb84aad61 100644 --- a/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj +++ b/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj @@ -15,6 +15,7 @@ <None Remove="Resources\Icon_NSO.png" /> <None Remove="Resources\Icon_NSP.png" /> <None Remove="Resources\Icon_XCI.png" /> + <None Remove="Resources\Icon_Folder.png" /> <None Remove="Resources\Logo_Amiibo.png" /> <None Remove="Resources\Logo_Discord.png" /> <None Remove="Resources\Logo_GitHub.png" /> @@ -33,6 +34,7 @@ <EmbeddedResource Include="Resources\Icon_NSO.png" /> <EmbeddedResource Include="Resources\Icon_NSP.png" /> <EmbeddedResource Include="Resources\Icon_XCI.png" /> + <EmbeddedResource Include="Resources\Icon_Folder.png" /> <EmbeddedResource Include="Resources\Logo_Amiibo.png" /> <EmbeddedResource Include="Resources\Logo_Ryujinx.png" /> <EmbeddedResource Include="Resources\Logo_Discord_Dark.png" /> diff --git a/src/Ryujinx/UI/Helpers/Glyph.cs b/src/Ryujinx/UI/Helpers/Glyph.cs index f257dc02c..d44f0875a 100644 --- a/src/Ryujinx/UI/Helpers/Glyph.cs +++ b/src/Ryujinx/UI/Helpers/Glyph.cs @@ -4,6 +4,7 @@ namespace Ryujinx.Ava.UI.Helpers { List, Grid, + Back, Chip, } } diff --git a/src/Ryujinx/UI/Helpers/GlyphValueConverter.cs b/src/Ryujinx/UI/Helpers/GlyphValueConverter.cs index 7da23648e..9731123e9 100644 --- a/src/Ryujinx/UI/Helpers/GlyphValueConverter.cs +++ b/src/Ryujinx/UI/Helpers/GlyphValueConverter.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Ava.UI.Helpers { { Glyph.List, char.ConvertFromUtf32((int)Symbol.List) }, { Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll) }, + { Glyph.Back, char.ConvertFromUtf32((int)Symbol.Back) }, { Glyph.Chip, char.ConvertFromUtf32(59748) }, }; diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 17bd69b14..c5de76f5a 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -51,6 +51,7 @@ namespace Ryujinx.Ava.UI.ViewModels private const int HotKeyPressDelayMs = 500; private ObservableCollection<ApplicationData> _applications; + private Queue<string> _pathHistory; private string _aspectStatusText; private string _loadHeading; @@ -120,6 +121,7 @@ namespace Ryujinx.Ava.UI.ViewModels .Bind(out _appsObservableList).AsObservableList(); _rendererWaitEvent = new AutoResetEvent(false); + _pathHistory = new Queue<string>(); if (Program.PreviewerDetached) { @@ -1281,6 +1283,34 @@ namespace Ryujinx.Ava.UI.ViewModels ShowConsole = !ShowConsole; } + public void OpenFolder(string path) + { + _pathHistory.Enqueue(path); + Applications.Clear(); + List<string> SearchPaths = new List<string>(); + SearchPaths.Add(path); + ApplicationLibrary.LoadApplications(SearchPaths, ConfigurationState.Instance.System.Language); + } + + public void NavigateBack() + { + if (_pathHistory.Count != 0) + { + string path = _pathHistory.Dequeue(); + Applications.Clear(); + if (_pathHistory.Count == 0) + { + ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language); + } + else + { + List<string> SearchPaths = new List<string>(); + SearchPaths.Add(path); + ApplicationLibrary.LoadApplications(SearchPaths, ConfigurationState.Instance.System.Language); + } + } + } + public void SetListMode() { Glyph = Glyph.List; diff --git a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml index cc21b5c60..b49aaacc2 100644 --- a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml +++ b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml @@ -18,6 +18,19 @@ Margin="0,0,0,5" Height="35" HorizontalAlignment="Stretch"> + <Button + Width="80" + MinWidth="80" + Margin="5,2,0,2" + VerticalAlignment="Stretch" + Command="{Binding NavigateBack}"> + <ui:FontIcon + Margin="0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + FontFamily="avares://FluentAvalonia/Fonts#Symbols" + Glyph="{helpers:GlyphValueConverter Back}" /> + </Button> <Button Width="40" MinWidth="40" diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 33a9af5b6..def449a1d 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -137,6 +137,11 @@ namespace Ryujinx.Ava.UI.Windows { if (args.Application != null) { + if (args.Application.FileExtension == "Folder") + { + ViewModel.OpenFolder(args.Application.Path); + return; + } ViewModel.SelectedIcon = args.Application.Icon; string path = new FileInfo(args.Application.Path).FullName;