Selaa lähdekoodia

UI: Added the ability to view Compat information on right click, and on clicking the status itself like the title ID button.

Evan Husted 1 vuosi sitten
vanhempi
sitoutus
df9e6e4812

+ 50 - 0
src/Ryujinx/Assets/locales.json

@@ -2522,6 +2522,56 @@
         "zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式"
         "zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式"
       }
       }
     },
     },
+    {
+      "ID": "GameListContextMenuShowCompatEntry",
+      "Translations": {
+        "ar_SA": "",
+        "de_DE": "",
+        "el_GR": "",
+        "en_US": "Show Compatibility Entry",
+        "es_ES": "",
+        "fr_FR": "",
+        "he_IL": "",
+        "it_IT": "",
+        "ja_JP": "",
+        "ko_KR": "",
+        "no_NO": "",
+        "pl_PL": "",
+        "pt_BR": "",
+        "ru_RU": "",
+        "sv_SE": "",
+        "th_TH": "",
+        "tr_TR": "",
+        "uk_UA": "",
+        "zh_CN": "",
+        "zh_TW": ""
+      }
+    },
+    {
+      "ID": "GameListContextMenuShowCompatEntryToolTip",
+      "Translations": {
+        "ar_SA": "",
+        "de_DE": "",
+        "el_GR": "",
+        "en_US": "Show the selected game in the Compatibility List you can normally access via the Help menu.",
+        "es_ES": "",
+        "fr_FR": "",
+        "he_IL": "",
+        "it_IT": "",
+        "ja_JP": "",
+        "ko_KR": "",
+        "no_NO": "",
+        "pl_PL": "",
+        "pt_BR": "",
+        "ru_RU": "",
+        "sv_SE": "",
+        "th_TH": "",
+        "tr_TR": "",
+        "uk_UA": "",
+        "zh_CN": "",
+        "zh_TW": ""
+      }
+    },
     {
     {
       "ID": "GameListContextMenuOpenModsDirectory",
       "ID": "GameListContextMenuOpenModsDirectory",
       "Translations": {
       "Translations": {

+ 6 - 1
src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml

@@ -19,6 +19,12 @@
         Header="{ext:Locale GameListContextMenuCreateShortcut}"
         Header="{ext:Locale GameListContextMenuCreateShortcut}"
         Icon="{ext:Icon fa-solid fa-bookmark}"
         Icon="{ext:Icon fa-solid fa-bookmark}"
         ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
         ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
+    <MenuItem
+        IsVisible="{Binding HasCompatibilityEntry}"
+        Click="OpenApplicationCompatibility_Click"
+        Header="{ext:Locale GameListContextMenuShowCompatEntry}"
+        Icon="{ext:Icon mdi-gamepad}"
+        ToolTip.Tip="{ext:Locale GameListContextMenuShowCompatEntryToolTip}"/>
     <Separator />
     <Separator />
     <MenuItem
     <MenuItem
         Click="OpenUserSaveDirectory_Click"
         Click="OpenUserSaveDirectory_Click"
@@ -74,7 +80,6 @@
 		Header="{ext:Locale GameListContextMenuTrimXCI}"
 		Header="{ext:Locale GameListContextMenuTrimXCI}"
         IsEnabled="{Binding TrimXCIEnabled}"
         IsEnabled="{Binding TrimXCIEnabled}"
 		ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
 		ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
-    <Separator />
     <MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
     <MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
         <MenuItem
         <MenuItem
             Click="PurgePtcCache_Click"
             Click="PurgePtcCache_Click"

+ 7 - 0
src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs

@@ -12,6 +12,7 @@ using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Ava.Utilities;
 using Ryujinx.Ava.Utilities;
 using Ryujinx.Ava.Utilities.AppLibrary;
 using Ryujinx.Ava.Utilities.AppLibrary;
+using Ryujinx.Ava.Utilities.Compat;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Helper;
 using Ryujinx.Common.Helper;
 using Ryujinx.HLE.HOS;
 using Ryujinx.HLE.HOS;
@@ -385,6 +386,12 @@ namespace Ryujinx.Ava.UI.Controls
                     viewModel.SelectedApplication.Icon
                     viewModel.SelectedApplication.Icon
                 );
                 );
         }
         }
+        
+        public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
+        {
+            if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
+                await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
+        }
 
 
         public async void RunApplication_Click(object sender, RoutedEventArgs args)
         public async void RunApplication_Click(object sender, RoutedEventArgs args)
         {
         {

+ 22 - 6
src/Ryujinx/UI/Controls/ApplicationListView.axaml

@@ -86,13 +86,29 @@
                                             Text="{Binding Version}"
                                             Text="{Binding Version}"
                                             TextAlignment="Start"
                                             TextAlignment="Start"
                                             TextWrapping="Wrap" />
                                             TextWrapping="Wrap" />
-                                        <TextBlock
+                                        <Button 
+                                            Click="PlayabilityStatus_OnClick" 
+                                            HorizontalContentAlignment="Left"
+                                            VerticalAlignment="Center"
                                             IsVisible="{Binding HasPlayabilityInfo}"
                                             IsVisible="{Binding HasPlayabilityInfo}"
-                                            HorizontalAlignment="Stretch"
-                                            Text="{Binding LocalizedStatus}"
-                                            Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
-                                            TextAlignment="Start"
-                                            TextWrapping="Wrap" />
+                                            Background="{DynamicResource AppListBackgroundColor}"
+                                            Margin="-1, 0, 0, 0"
+                                            Padding="0" >
+                                            <TextBlock
+                                                Margin="1.5"
+                                                Tag="{Binding IdString}"
+                                                Text="{Binding LocalizedStatus}"
+                                                Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
+                                                TextAlignment="Start"
+                                                TextWrapping="Wrap" />
+                                            <Button.Styles>
+                                                <Style Selector="Button">
+                                                    <Setter Property="MinWidth"
+                                                            Value="0" /> 
+                                <!-- avoids very wide buttons from the overall project avalonia style -->
+                                                </Style>
+                                            </Button.Styles>
+                                        </Button>
                                     </StackPanel>
                                     </StackPanel>
                                 </Border>
                                 </Border>
                                 <StackPanel
                                 <StackPanel

+ 19 - 0
src/Ryujinx/UI/Controls/ApplicationListView.axaml.cs

@@ -5,7 +5,9 @@ using Avalonia.Interactivity;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ava.Utilities.AppLibrary;
 using Ryujinx.Ava.Utilities.AppLibrary;
+using Ryujinx.Ava.Utilities.Compat;
 using System;
 using System;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 
 
 namespace Ryujinx.Ava.UI.Controls
 namespace Ryujinx.Ava.UI.Controls
@@ -28,6 +30,23 @@ namespace Ryujinx.Ava.UI.Controls
             if (sender is ListBox { SelectedItem: ApplicationData selected })
             if (sender is ListBox { SelectedItem: ApplicationData selected })
                 RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
                 RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
         }
         }
+        
+        private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
+        {
+            if (DataContext is not MainWindowViewModel mwvm)
+                return;
+            
+            if (sender is not Button { Content: TextBlock playabilityLabel })
+                return;
+
+            if (!ulong.TryParse((string)playabilityLabel.Tag, NumberStyles.HexNumber, null, out ulong titleId))
+                return;
+
+            if (!mwvm.ApplicationLibrary.FindApplication(titleId, out ApplicationData appData))
+                return;
+
+            await CompatibilityList.Show(appData.IdString);
+        }
 
 
         private async void IdString_OnClick(object sender, RoutedEventArgs e)
         private async void IdString_OnClick(object sender, RoutedEventArgs e)
         {
         {

+ 11 - 0
src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs

@@ -349,6 +349,17 @@ namespace Ryujinx.Ava.UI.ViewModels
             }
             }
         }
         }
 
 
+        public bool HasCompatibilityEntry
+        {
+            get
+            {
+                DynamicData.Kernel.Optional<ApplicationData> appData =
+                    ApplicationLibrary.Applications.Lookup(SelectedApplication.Id);
+
+                return appData.HasValue && appData.Value.HasPlayabilityInfo;
+            }
+        }
+
         public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
         public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
 
 
         public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
         public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;

+ 1 - 1
src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs

@@ -50,7 +50,7 @@ namespace Ryujinx.Ava.UI.Views.Main
             UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
             UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
             XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
             XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
             AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
             AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
-            CompatibilityListMenuItem.Command = Commands.Create(CompatibilityList.Show);
+            CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show());
             
             
             UpdateMenuItem.Command = Commands.Create(async () =>
             UpdateMenuItem.Command = Commands.Create(async () =>
             {
             {

+ 8 - 0
src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs

@@ -135,6 +135,14 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
             return id.ToString("X16");
             return id.ToString("X16");
         }
         }
 
 
+        public bool FindApplication(ulong id, out ApplicationData foundData)
+        {
+            DynamicData.Kernel.Optional<ApplicationData> appData = Applications.Lookup(id);
+            foundData = appData.HasValue ? appData.Value : null;
+
+            return appData.HasValue;
+        }
+
         /// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
         /// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
         /// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
         /// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
         /// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
         /// <exception cref="NotSupportedException">The NCA version is not supported.</exception>

+ 11 - 14
src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs

@@ -113,20 +113,17 @@ namespace Ryujinx.Ava.Utilities.Compat
             .Select(FormatLabelName)
             .Select(FormatLabelName)
             .JoinToString(", ");
             .JoinToString(", ");
 
 
-        public override string ToString()
-        {
-            StringBuilder sb = new("CompatibilityEntry: {");
-            sb.Append($"{nameof(GameName)}=\"{GameName}\", ");
-            sb.Append($"{nameof(TitleId)}={TitleId}, ");
-            sb.Append($"{nameof(Labels)}={
-                Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]")
-            }, ");
-            sb.Append($"{nameof(Status)}=\"{Status}\", ");
-            sb.Append($"{nameof(LastUpdated)}=\"{LastUpdated}\"");
-            sb.Append('}');
-
-            return sb.ToString();
-        }
+        public override string ToString() =>
+            new StringBuilder("CompatibilityEntry: {")
+                .Append($"{nameof(GameName)}=\"{GameName}\", ")
+                .Append($"{nameof(TitleId)}={TitleId}, ")
+                .Append($"{nameof(Labels)}={
+                    Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]")
+                }, ")
+                .Append($"{nameof(Status)}=\"{Status}\", ")
+                .Append($"{nameof(LastUpdated)}=\"{LastUpdated}\"")
+                .Append('}')
+                .ToString();
 
 
         public static string FormatLabelName(string labelName) => labelName.ToLower() switch
         public static string FormatLabelName(string labelName) => labelName.ToLower() switch
         {
         {

+ 1 - 1
src/Ryujinx/Utilities/Compat/CompatibilityList.axaml

@@ -34,7 +34,7 @@
                 Text="{ext:Locale CompatibilityListWarning}" />
                 Text="{ext:Locale CompatibilityListWarning}" />
         </Grid>
         </Grid>
         <Grid Grid.Row="1" ColumnDefinitions="*,Auto,Auto">
         <Grid Grid.Row="1" ColumnDefinitions="*,Auto,Auto">
-            <TextBox Grid.Column="0" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" />
+            <TextBox Name="SearchBox" Grid.Column="0" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" />
             <CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
             <CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
             <TextBlock Grid.Column="2" Margin="-10, 0, 0, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
             <TextBlock Grid.Column="2" Margin="-10, 0, 0, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
         </Grid>
         </Grid>

+ 5 - 2
src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs

@@ -9,7 +9,7 @@ namespace Ryujinx.Ava.Utilities.Compat
 {
 {
     public partial class CompatibilityList : UserControl
     public partial class CompatibilityList : UserControl
     {
     {
-        public static async Task Show()
+        public static async Task Show(string titleId = null)
         {
         {
             ContentDialog contentDialog = new()
             ContentDialog contentDialog = new()
             {
             {
@@ -18,7 +18,10 @@ namespace Ryujinx.Ava.Utilities.Compat
                 CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
                 CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
                 Content = new CompatibilityList
                 Content = new CompatibilityList
                 {
                 {
-                    DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary)
+                    DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary), 
+                    SearchBox = {
+                        Text = titleId ?? ""
+                    }
                 }
                 }
             };
             };