Compare commits

...

11 Commits

Author SHA1 Message Date
Otozinclus
37c165e9fc Only delay shader translation on Metal (#480)
This way the Arbitrary Shader Translation Delay hack will no longer
affect shader loading when using Vulkan.
2025-01-01 00:18:17 -06:00
Daenorth
003a6d322b Update to no_NO Norwegian Translation (#475)
Updated for time resync & auto graphics backend
2025-01-01 00:15:21 -06:00
jozz024
978d2c132b add a keyboard shortcut for opening amiibo .bin files (#461) 2024-12-31 22:45:52 -06:00
Evan Husted
5d63706cea misc: Bake in ValueEqual logic into ReactiveEventArgs
[ci skip]
2024-12-31 22:34:14 -06:00
Evan Husted
732aafd3bb misc: Prevent value change logging when the value is changed to the same thing it was before the value change. 2024-12-31 22:23:08 -06:00
Evan Husted
3fa714bb72 misc: DateTimeOffset Extract extension from Gommon 2024-12-31 21:21:54 -06:00
Evan Husted
7c01633f13 UI: Show the path of the mod on the folder button 2024-12-31 21:15:50 -06:00
Evan Husted
27c5cba10b misc: More Mvvm usage instead of writing out the observable properties 2024-12-31 21:11:57 -06:00
Evan Husted
3525d5ecd4 UI: clean up slider UI for shader translation delay 2024-12-31 20:11:49 -06:00
Evan Husted
6286501550 misc: do not log dirty hack changes if ShowDirtyHacks is disabled 2024-12-31 20:11:44 -06:00
Evan Husted
61ae427a4d misc: Add CommunityToolkit.Mvvm for observable property generation; apply it to MainWindowViewModel for now. 2024-12-31 03:29:08 -06:00
23 changed files with 223 additions and 937 deletions

View File

@@ -17,6 +17,7 @@
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/> <PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/>
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/> <PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
<PackageVersion Include="Concentus" Version="2.2.0" /> <PackageVersion Include="Concentus" Version="2.2.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="9.0.4" /> <PackageVersion Include="DynamicData" Version="9.0.4" />
@@ -41,7 +42,7 @@
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" /> <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.0" /> <PackageVersion Include="Gommon" Version="2.7.0.1" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" /> <PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />

View File

@@ -53,6 +53,9 @@ namespace Ryujinx.Common
{ {
public static void LogValueChange<T>(LogClass logClass, ReactiveEventArgs<T> eventArgs, string valueName) public static void LogValueChange<T>(LogClass logClass, ReactiveEventArgs<T> eventArgs, string valueName)
{ {
if (eventArgs.AreValuesEqual)
return;
string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}"); string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}");
Logger.Info?.Print(logClass, message); Logger.Info?.Print(logClass, message);
@@ -65,5 +68,22 @@ namespace Ryujinx.Common
{ {
public T OldValue { get; } = oldValue; public T OldValue { get; } = oldValue;
public T NewValue { get; } = newValue; public T NewValue { get; } = newValue;
public bool AreValuesEqual
{
get
{
if (OldValue == null && NewValue == null)
return true;
if (OldValue == null && NewValue != null)
return false;
if (OldValue != null && NewValue == null)
return false;
return OldValue!.Equals(NewValue);
}
}
} }
} }

View File

@@ -367,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{ {
try try
{ {
if (_context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay)) if (_context.Capabilities.Api == TargetApi.Metal && _context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay))
Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]); Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]);
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);

View File

@@ -314,7 +314,7 @@ namespace Ryujinx.Ava
_renderer.Window?.ChangeVSyncMode(e.NewValue); _renderer.Window?.ChangeVSyncMode(e.NewValue);
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom); _viewModel.UpdateVSyncIntervalPicker();
} }
public void VSyncModeToggle() public void VSyncModeToggle()

View File

@@ -1235,7 +1235,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "자주 묻는 질문(FAQ) 및 안내", "ko_KR": "자주 묻는 질문(FAQ) 및 안내",
"no_NO": "", "no_NO": "Vanlige spørsmål og veiledninger",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "FAQ и Руководства", "ru_RU": "FAQ и Руководства",
@@ -1460,7 +1460,7 @@
"it_IT": "Preferito", "it_IT": "Preferito",
"ja_JP": "お気に入り", "ja_JP": "お気に入り",
"ko_KR": "즐겨찾기", "ko_KR": "즐겨찾기",
"no_NO": "", "no_NO": "Favoritter",
"pl_PL": "Ulubione", "pl_PL": "Ulubione",
"pt_BR": "Favorito", "pt_BR": "Favorito",
"ru_RU": "Избранное", "ru_RU": "Избранное",
@@ -2610,7 +2610,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "펌웨어 버전 : {0}", "ko_KR": "펌웨어 버전 : {0}",
"no_NO": "", "no_NO": "Fastvareversjon: {0}",
"pl_PL": "", "pl_PL": "",
"pt_BR": "Versão do firmware: {0}", "pt_BR": "Versão do firmware: {0}",
"ru_RU": "Версия прошивки: {0}", "ru_RU": "Версия прошивки: {0}",
@@ -3460,7 +3460,7 @@
"it_IT": "Corea", "it_IT": "Corea",
"ja_JP": "韓国", "ja_JP": "韓国",
"ko_KR": "한국", "ko_KR": "한국",
"no_NO": "", "no_NO": "Koreansk",
"pl_PL": "", "pl_PL": "",
"pt_BR": "Coreia", "pt_BR": "Coreia",
"ru_RU": "Корея", "ru_RU": "Корея",
@@ -4010,7 +4010,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "PC 날짜와 시간에 동기화", "ko_KR": "PC 날짜와 시간에 동기화",
"no_NO": "", "no_NO": "Resynkroniser til PC-dato og -klokkeslett",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Повторная синхронизация с датой и временем на компьютере", "ru_RU": "Повторная синхронизация с датой и временем на компьютере",
@@ -15260,7 +15260,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "시스템 시간을 PC의 현재 날짜 및 시간과 일치하도록 다시 동기화합니다.\n\n이 설정은 활성 설정이 아니므로 여전히 동기화되지 않을 수 있으며, 이 경우 이 버튼을 다시 클릭하면 됩니다.", "ko_KR": "시스템 시간을 PC의 현재 날짜 및 시간과 일치하도록 다시 동기화합니다.\n\n이 설정은 활성 설정이 아니므로 여전히 동기화되지 않을 수 있으며, 이 경우 이 버튼을 다시 클릭하면 됩니다.",
"no_NO": "", "no_NO": "Resynkroniser systemtiden slik at den samsvarer med PC-ens gjeldende dato og klokkeslett. \\Dette er ikke en aktiv innstilling, men den kan likevel komme ut av synkronisering; i så fall er det bare å klikke på denne knappen igjen.",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Повторно синхронизирует системное время, чтобы оно соответствовало текущей дате и времени вашего компьютера.\n\nЭто не активная настройка, она все еще может рассинхронизироваться; в этом случае просто нажмите эту кнопку еще раз.", "ru_RU": "Повторно синхронизирует системное время, чтобы оно соответствовало текущей дате и времени вашего компьютера.\n\nЭто не активная настройка, она все еще может рассинхронизироваться; в этом случае просто нажмите эту кнопку еще раз.",
@@ -20535,7 +20535,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "Vulkan을 사용합니다.\nARM 맥에서 해당 플랫폼에서 잘 실행되는 게임을 플레이하는 경우 Metal 후단부를 사용합니다.", "ko_KR": "Vulkan을 사용합니다.\nARM 맥에서 해당 플랫폼에서 잘 실행되는 게임을 플레이하는 경우 Metal 후단부를 사용합니다.",
"no_NO": "", "no_NO": "Bruker Vulkan \nPå en ARM Mac, og når du spiller et spill som kjører bra under den, bruker du Metal-backend.",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Использует Vulkan.\nНа Mac с ARM процессорами используется Metal, если игра с ним совместима и хорошо работает.", "ru_RU": "Использует Vulkan.\nНа Mac с ARM процессорами используется Metal, если игра с ним совместима и хорошо работает.",
@@ -22598,4 +22598,4 @@
} }
} }
] ]
} }

View File

@@ -49,6 +49,7 @@
<PackageReference Include="Avalonia.Svg" /> <PackageReference Include="Avalonia.Svg" />
<PackageReference Include="Avalonia.Svg.Skia" /> <PackageReference Include="Avalonia.Svg.Skia" />
<PackageReference Include="CommandLineParser" /> <PackageReference Include="CommandLineParser" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="DiscordRichPresence" /> <PackageReference Include="DiscordRichPresence" />
<PackageReference Include="DynamicData" /> <PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" /> <PackageReference Include="FluentAvaloniaUI" />
@@ -162,4 +163,4 @@
<ItemGroup> <ItemGroup>
<AdditionalFiles Include="Assets\locales.json" /> <AdditionalFiles Include="Assets\locales.json" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,7 @@
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
@@ -8,42 +9,11 @@ using System;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class AboutWindowViewModel : BaseModel, IDisposable public partial class AboutWindowViewModel : BaseModel, IDisposable
{ {
private Bitmap _githubLogo; [ObservableProperty] private Bitmap _githubLogo;
private Bitmap _discordLogo; [ObservableProperty] private Bitmap _discordLogo;
[ObservableProperty] private string _version;
private string _version;
public Bitmap GithubLogo
{
get => _githubLogo;
set
{
_githubLogo = value;
OnPropertyChanged();
}
}
public Bitmap DiscordLogo
{
get => _discordLogo;
set
{
_discordLogo = value;
OnPropertyChanged();
}
}
public string Version
{
get => _version;
set
{
_version = value;
OnPropertyChanged();
}
}
public string Developers => "GreemDev"; public string Developers => "GreemDev";

View File

@@ -1,18 +1,10 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System; using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class BaseModel : INotifyPropertyChanged public class BaseModel : ObservableObject
{ {
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan<string> propertyNames) protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan<string> propertyNames)
{ {
OnPropertyChanged(firstPropertyName); OnPropertyChanged(firstPropertyName);

View File

@@ -2,6 +2,7 @@ using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -17,13 +18,13 @@ using Application = Avalonia.Application;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class DownloadableContentManagerViewModel : BaseModel public partial class DownloadableContentManagerViewModel : BaseModel
{ {
private readonly ApplicationLibrary _applicationLibrary; private readonly ApplicationLibrary _applicationLibrary;
private AvaloniaList<DownloadableContentModel> _downloadableContents = new(); private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new(); [ObservableProperty] private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
private AvaloniaList<DownloadableContentModel> _views = new(); [ObservableProperty] private AvaloniaList<DownloadableContentModel> _views = new();
private bool _showBundledContentNotice = false; [ObservableProperty] private bool _showBundledContentNotice = false;
private string _search; private string _search;
private readonly ApplicationData _applicationData; private readonly ApplicationData _applicationData;
@@ -41,26 +42,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public AvaloniaList<DownloadableContentModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public AvaloniaList<DownloadableContentModel> SelectedDownloadableContents
{
get => _selectedDownloadableContents;
set
{
_selectedDownloadableContents = value;
OnPropertyChanged();
}
}
public string Search public string Search
{ {
get => _search; get => _search;
@@ -77,16 +58,6 @@ namespace Ryujinx.Ava.UI.ViewModels
get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
} }
public bool ShowBundledContentNotice
{
get => _showBundledContentNotice;
set
{
_showBundledContentNotice = value;
OnPropertyChanged();
}
}
public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData)
{ {
_applicationLibrary = applicationLibrary; _applicationLibrary = applicationLibrary;

View File

@@ -6,6 +6,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
@@ -54,80 +55,76 @@ using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class MainWindowViewModel : BaseModel public partial class MainWindowViewModel : BaseModel
{ {
private const int HotKeyPressDelayMs = 500; private const int HotKeyPressDelayMs = 500;
private delegate int LoadContentFromFolderDelegate(List<string> dirs, out int numRemoved); private delegate int LoadContentFromFolderDelegate(List<string> dirs, out int numRemoved);
private ObservableCollectionExtended<ApplicationData> _applications; [ObservableProperty] private ObservableCollectionExtended<ApplicationData> _applications;
private string _aspectStatusText; [ObservableProperty] private string _aspectRatioStatusText;
[ObservableProperty] private string _loadHeading;
private string _loadHeading; [ObservableProperty] private string _cacheLoadStatus;
private string _cacheLoadStatus; [ObservableProperty] private string _dockedStatusText;
private string _searchText; [ObservableProperty] private string _fifoStatusText;
private Timer _searchTimer; [ObservableProperty] private string _gameStatusText;
private string _dockedStatusText; [ObservableProperty] private string _volumeStatusText;
private string _vSyncModeText; [ObservableProperty] private string _gpuNameText;
private string _fifoStatusText; [ObservableProperty] private string _backendText;
private string _gameStatusText; [ObservableProperty] private string _shaderCountText;
private string _volumeStatusText; [ObservableProperty] private bool _showShaderCompilationHint;
private string _gpuStatusText; [ObservableProperty] private bool _isFullScreen;
private string _shaderCountText; [ObservableProperty] private int _progressMaximum;
[ObservableProperty] private int _progressValue;
[ObservableProperty] private bool _showMenuAndStatusBar = true;
[ObservableProperty] private bool _showStatusSeparator;
[ObservableProperty] private Brush _progressBarForegroundColor;
[ObservableProperty] private Brush _progressBarBackgroundColor;
[ObservableProperty] private Brush _vSyncModeColor;
[ObservableProperty] private byte[] _selectedIcon;
[ObservableProperty] private int _statusBarProgressMaximum;
[ObservableProperty] private int _statusBarProgressValue;
[ObservableProperty] private string _statusBarProgressStatusText;
[ObservableProperty] private bool _statusBarProgressStatusVisible;
[ObservableProperty] private bool _isPaused;
[ObservableProperty] private bool _isLoadingIndeterminate = true;
[ObservableProperty] private bool _showAll;
[ObservableProperty] private string _lastScannedAmiiboId;
[ObservableProperty] private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
[ObservableProperty] private long _lastFullscreenToggle = Environment.TickCount64;
[ObservableProperty] private bool _showContent = true;
[ObservableProperty] private float _volumeBeforeMute;
[ObservableProperty] private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered;
[ObservableProperty] private Cursor _cursor;
[ObservableProperty] private string _title;
[ObservableProperty] private WindowState _windowState;
[ObservableProperty] private double _windowWidth;
[ObservableProperty] private double _windowHeight;
[ObservableProperty] private bool _isActive;
[ObservableProperty] private bool _isSubMenuOpen;
[ObservableProperty] private ApplicationContextMenu _listAppContextMenu;
[ObservableProperty] private ApplicationContextMenu _gridAppContextMenu;
private bool _showLoadProgress;
private bool _isGameRunning;
private bool _isAmiiboRequested; private bool _isAmiiboRequested;
private bool _isAmiiboBinRequested; private bool _isAmiiboBinRequested;
private bool _showShaderCompilationHint; private string _searchText;
private bool _isGameRunning; private Timer _searchTimer;
private bool _isFullScreen; private string _vSyncModeText;
private int _progressMaximum;
private int _progressValue;
private long _lastFullscreenToggle = Environment.TickCount64;
private bool _showLoadProgress;
private bool _showMenuAndStatusBar = true;
private bool _showStatusSeparator;
private Brush _progressBarForegroundColor;
private Brush _progressBarBackgroundColor;
private Brush _vSyncModeColor;
private byte[] _selectedIcon;
private bool _isAppletMenuActive;
private int _statusBarProgressMaximum;
private int _statusBarProgressValue;
private string _statusBarProgressStatusText;
private bool _statusBarProgressStatusVisible;
private bool _isPaused;
private bool _showContent = true;
private bool _isLoadingIndeterminate = true;
private bool _showAll;
private string _lastScannedAmiiboId;
private bool _statusBarVisible;
private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
private string _showUiKey = "F4"; private string _showUiKey = "F4";
private string _pauseKey = "F5"; private string _pauseKey = "F5";
private string _screenshotKey = "F8"; private string _screenshotKey = "F8";
private float _volume; private float _volume;
private float _volumeBeforeMute; private bool _isAppletMenuActive;
private string _backendText; private bool _statusBarVisible;
private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered;
private bool _canUpdate = true; private bool _canUpdate = true;
private Cursor _cursor;
private string _title;
private ApplicationData _currentApplicationData; private ApplicationData _currentApplicationData;
private readonly AutoResetEvent _rendererWaitEvent; private readonly AutoResetEvent _rendererWaitEvent;
private WindowState _windowState;
private double _windowWidth;
private double _windowHeight;
private int _customVSyncInterval; private int _customVSyncInterval;
private int _customVSyncIntervalPercentageProxy; private int _customVSyncIntervalPercentageProxy;
private bool _isActive;
private bool _isSubMenuOpen;
private ApplicationData _listSelectedApplication; private ApplicationData _listSelectedApplication;
private ApplicationData _gridSelectedApplication; private ApplicationData _gridSelectedApplication;
private ApplicationContextMenu _listAppContextMenu;
private ApplicationContextMenu _gridAppContextMenu;
public ApplicationData ListSelectedApplication public ApplicationData ListSelectedApplication
{ {
get => _listSelectedApplication; get => _listSelectedApplication;
@@ -135,10 +132,13 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_listSelectedApplication = value; _listSelectedApplication = value;
#pragma warning disable MVVMTK0034
if (_listSelectedApplication != null && _listAppContextMenu == null) if (_listSelectedApplication != null && _listAppContextMenu == null)
ListAppContextMenu = new ApplicationContextMenu(); ListAppContextMenu = new ApplicationContextMenu();
else if (_listSelectedApplication == null && _listAppContextMenu != null) else if (_listSelectedApplication == null && _listAppContextMenu != null)
ListAppContextMenu = null; ListAppContextMenu = null!;
#pragma warning restore MVVMTK0034
OnPropertyChanged(); OnPropertyChanged();
} }
@@ -151,10 +151,12 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_gridSelectedApplication = value; _gridSelectedApplication = value;
#pragma warning disable MVVMTK0034
if (_gridSelectedApplication != null && _gridAppContextMenu == null) if (_gridSelectedApplication != null && _gridAppContextMenu == null)
GridAppContextMenu = new ApplicationContextMenu(); GridAppContextMenu = new ApplicationContextMenu();
else if (_gridSelectedApplication == null && _gridAppContextMenu != null) else if (_gridSelectedApplication == null && _gridAppContextMenu != null)
GridAppContextMenu = null; GridAppContextMenu = null!;
#pragma warning restore MVVMTK0034
OnPropertyChanged(); OnPropertyChanged();
} }
@@ -260,71 +262,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public Cursor Cursor
{
get => _cursor;
set
{
_cursor = value;
OnPropertyChanged();
}
}
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
{
get => _appsObservableList;
set
{
_appsObservableList = value;
OnPropertyChanged();
}
}
public ApplicationContextMenu ListAppContextMenu
{
get => _listAppContextMenu;
set
{
_listAppContextMenu = value;
OnPropertyChanged();
}
}
public ApplicationContextMenu GridAppContextMenu
{
get => _gridAppContextMenu;
set
{
_gridAppContextMenu = value;
OnPropertyChanged();
}
}
public bool IsPaused
{
get => _isPaused;
set
{
_isPaused = value;
OnPropertyChanged();
}
}
public long LastFullscreenToggle
{
get => _lastFullscreenToggle;
set
{
_lastFullscreenToggle = value;
OnPropertyChanged();
}
}
public bool StatusBarVisible public bool StatusBarVisible
{ {
get => _statusBarVisible && EnableNonGameRunningControls; get => _statusBarVisible && EnableNonGameRunningControls;
@@ -340,17 +277,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool ShowFirmwareStatus => !ShowLoadProgress; public bool ShowFirmwareStatus => !ShowLoadProgress;
public bool ShowShaderCompilationHint
{
get => _showShaderCompilationHint;
set
{
_showShaderCompilationHint = value;
OnPropertyChanged();
}
}
public bool IsGameRunning public bool IsGameRunning
{ {
get => _isGameRunning; get => _isGameRunning;
@@ -393,7 +319,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile; public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile;
public bool ShowLoadProgress public bool ShowLoadProgress
{ {
get => _showLoadProgress; get => _showLoadProgress;
@@ -406,61 +332,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string GameStatusText
{
get => _gameStatusText;
set
{
_gameStatusText = value;
OnPropertyChanged();
}
}
public bool IsFullScreen
{
get => _isFullScreen;
set
{
_isFullScreen = value;
OnPropertyChanged();
}
}
public bool IsSubMenuOpen
{
get => _isSubMenuOpen;
set
{
_isSubMenuOpen = value;
OnPropertyChanged();
}
}
public bool ShowAll
{
get => _showAll;
set
{
_showAll = value;
OnPropertyChanged();
}
}
public string LastScannedAmiiboId
{
get => _lastScannedAmiiboId;
set
{
_lastScannedAmiiboId = value;
OnPropertyChanged();
}
}
public ApplicationData SelectedApplication public ApplicationData SelectedApplication
{ {
get get
@@ -482,79 +353,12 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool OpenBcatSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public bool OpenBcatSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public string LoadHeading public bool ShowCustomVSyncIntervalPicker
=> _isGameRunning && AppHost.Device.VSyncMode == VSyncMode.Custom;
public void UpdateVSyncIntervalPicker()
{ {
get => _loadHeading; OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker));
set
{
_loadHeading = value;
OnPropertyChanged();
}
}
public string CacheLoadStatus
{
get => _cacheLoadStatus;
set
{
_cacheLoadStatus = value;
OnPropertyChanged();
}
}
public Brush ProgressBarBackgroundColor
{
get => _progressBarBackgroundColor;
set
{
_progressBarBackgroundColor = value;
OnPropertyChanged();
}
}
public Brush ProgressBarForegroundColor
{
get => _progressBarForegroundColor;
set
{
_progressBarForegroundColor = value;
OnPropertyChanged();
}
}
public Brush VSyncModeColor
{
get => _vSyncModeColor;
set
{
_vSyncModeColor = value;
OnPropertyChanged();
}
}
public bool ShowCustomVSyncIntervalPicker
{
get
{
if (_isGameRunning)
{
return AppHost.Device.VSyncMode ==
VSyncMode.Custom;
}
else
{
return false;
}
}
set
{
OnPropertyChanged();
}
} }
public int CustomVSyncIntervalPercentageProxy public int CustomVSyncIntervalPercentageProxy
@@ -607,126 +411,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public byte[] SelectedIcon
{
get => _selectedIcon;
set
{
_selectedIcon = value;
OnPropertyChanged();
}
}
public int ProgressMaximum
{
get => _progressMaximum;
set
{
_progressMaximum = value;
OnPropertyChanged();
}
}
public int ProgressValue
{
get => _progressValue;
set
{
_progressValue = value;
OnPropertyChanged();
}
}
public int StatusBarProgressMaximum
{
get => _statusBarProgressMaximum;
set
{
_statusBarProgressMaximum = value;
OnPropertyChanged();
}
}
public int StatusBarProgressValue
{
get => _statusBarProgressValue;
set
{
_statusBarProgressValue = value;
OnPropertyChanged();
}
}
public bool StatusBarProgressStatusVisible
{
get => _statusBarProgressStatusVisible;
set
{
_statusBarProgressStatusVisible = value;
OnPropertyChanged();
}
}
public string StatusBarProgressStatusText
{
get => _statusBarProgressStatusText;
set
{
_statusBarProgressStatusText = value;
OnPropertyChanged();
}
}
public string FifoStatusText
{
get => _fifoStatusText;
set
{
_fifoStatusText = value;
OnPropertyChanged();
}
}
public string GpuNameText
{
get => _gpuStatusText;
set
{
_gpuStatusText = value;
OnPropertyChanged();
}
}
public string ShaderCountText
{
get => _shaderCountText;
set
{
_shaderCountText = value;
OnPropertyChanged();
}
}
public string BackendText
{
get => _backendText;
set
{
_backendText = value;
OnPropertyChanged();
}
}
public string VSyncModeText public string VSyncModeText
{ {
get => _vSyncModeText; get => _vSyncModeText;
@@ -735,39 +419,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_vSyncModeText = value; _vSyncModeText = value;
OnPropertyChanged(); OnPropertyChanged();
} OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker));
}
public string DockedStatusText
{
get => _dockedStatusText;
set
{
_dockedStatusText = value;
OnPropertyChanged();
}
}
public string AspectRatioStatusText
{
get => _aspectStatusText;
set
{
_aspectStatusText = value;
OnPropertyChanged();
}
}
public string VolumeStatusText
{
get => _volumeStatusText;
set
{
_volumeStatusText = value;
OnPropertyChanged();
} }
} }
@@ -791,73 +443,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public float VolumeBeforeMute
{
get => _volumeBeforeMute;
set
{
_volumeBeforeMute = value;
OnPropertyChanged();
}
}
public bool ShowStatusSeparator
{
get => _showStatusSeparator;
set
{
_showStatusSeparator = value;
OnPropertyChanged();
}
}
public bool ShowMenuAndStatusBar
{
get => _showMenuAndStatusBar;
set
{
_showMenuAndStatusBar = value;
OnPropertyChanged();
}
}
public bool IsLoadingIndeterminate
{
get => _isLoadingIndeterminate;
set
{
_isLoadingIndeterminate = value;
OnPropertyChanged();
}
}
public bool IsActive
{
get => _isActive;
set
{
_isActive = value;
OnPropertyChanged();
}
}
public bool ShowContent
{
get => _showContent;
set
{
_showContent = value;
OnPropertyChanged();
}
}
public bool IsAppletMenuActive public bool IsAppletMenuActive
{ {
get => _isAppletMenuActive && EnableNonGameRunningControls; get => _isAppletMenuActive && EnableNonGameRunningControls;
@@ -869,39 +454,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public WindowState WindowState
{
get => _windowState;
internal set
{
_windowState = value;
OnPropertyChanged();
}
}
public double WindowWidth
{
get => _windowWidth;
set
{
_windowWidth = value;
OnPropertyChanged();
}
}
public double WindowHeight
{
get => _windowHeight;
set
{
_windowHeight = value;
OnPropertyChanged();
}
}
public bool IsGrid => Glyph == Glyph.Grid; public bool IsGrid => Glyph == Glyph.Grid;
public bool IsList => Glyph == Glyph.List; public bool IsList => Glyph == Glyph.List;
@@ -945,17 +497,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string Title
{
get => _title;
set
{
_title = value;
OnPropertyChanged();
}
}
public bool ShowConsoleVisible public bool ShowConsoleVisible
{ {
get => ConsoleHelper.SetConsoleWindowStateSupported; get => ConsoleHelper.SetConsoleWindowStateSupported;
@@ -966,27 +507,6 @@ namespace Ryujinx.Ava.UI.ViewModels
get => FileAssociationHelper.IsTypeAssociationSupported; get => FileAssociationHelper.IsTypeAssociationSupported;
} }
public bool AreMimeTypesRegistered
{
get => _areMimeTypesRegistered;
set {
_areMimeTypesRegistered = value;
OnPropertyChanged();
}
}
public ObservableCollectionExtended<ApplicationData> Applications
{
get => _applications;
set
{
_applications = value;
OnPropertyChanged();
}
}
public Glyph Glyph public Glyph Glyph
{ {
get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value; get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value;
@@ -1004,7 +524,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool ShowNames public bool ShowNames
{ {
get => ConfigurationState.Instance.UI.ShowNames && ConfigurationState.Instance.UI.GridSize > 1; set get => ConfigurationState.Instance.UI.ShowNames && ConfigurationState.Instance.UI.GridSize > 1;
set
{ {
ConfigurationState.Instance.UI.ShowNames.Value = value; ConfigurationState.Instance.UI.ShowNames.Value = value;
@@ -1564,8 +1085,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
VSyncModeText = args.VSyncMode == "Custom" ? "Custom" : "VSync"; VSyncModeText = args.VSyncMode == "Custom" ? "Custom" : "VSync";
ShowCustomVSyncIntervalPicker =
args.VSyncMode == VSyncMode.Custom.ToString();
DockedStatusText = args.DockedMode; DockedStatusText = args.DockedMode;
AspectRatioStatusText = args.AspectRatio; AspectRatioStatusText = args.AspectRatio;
GameStatusText = args.GameStatus; GameStatusText = args.GameStatus;

View File

@@ -1,8 +1,7 @@
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using Gommon; using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -18,13 +17,13 @@ using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class ModManagerViewModel : BaseModel public partial class ModManagerViewModel : BaseModel
{ {
private readonly string _modJsonPath; private readonly string _modJsonPath;
private AvaloniaList<ModModel> _mods = new(); private AvaloniaList<ModModel> _mods = new();
private AvaloniaList<ModModel> _views = new(); [ObservableProperty] private AvaloniaList<ModModel> _views = new();
private AvaloniaList<ModModel> _selectedMods = new(); [ObservableProperty] private AvaloniaList<ModModel> _selectedMods = new();
private string _search; private string _search;
private readonly ulong _applicationId; private readonly ulong _applicationId;
@@ -44,26 +43,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public AvaloniaList<ModModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public AvaloniaList<ModModel> SelectedMods
{
get => _selectedMods;
set
{
_selectedMods = value;
OnPropertyChanged();
}
}
public string Search public string Search
{ {
get => _search; get => _search;
@@ -143,8 +122,10 @@ namespace Ryujinx.Ava.UI.ViewModels
.Filter(Filter) .Filter(Filter)
.Bind(out var view).AsObservableList(); .Bind(out var view).AsObservableList();
#pragma warning disable MVVMTK0034 // Event to update is fired below
_views.Clear(); _views.Clear();
_views.AddRange(view); _views.AddRange(view);
#pragma warning restore MVVMTK0034
SelectedMods = new(Views.Where(x => x.Enabled)); SelectedMods = new(Views.Where(x => x.Enabled));

View File

@@ -1,9 +1,10 @@
using Gommon; using CommunityToolkit.Mvvm.ComponentModel;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class SettingsHacksViewModel : BaseModel public partial class SettingsHacksViewModel : BaseModel
{ {
private readonly SettingsViewModel _baseViewModel; private readonly SettingsViewModel _baseViewModel;
@@ -14,33 +15,11 @@ namespace Ryujinx.Ava.UI.ViewModels
_baseViewModel = settingsVm; _baseViewModel = settingsVm;
} }
private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; [ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix;
private bool _shaderTranslationThreadSleep = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; [ObservableProperty] private bool _shaderTranslationDelayEnabled = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay;
private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay; private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay;
public bool Xc2MenuSoftlockFixEnabled
{
get => _xc2MenuSoftlockFix;
set
{
_xc2MenuSoftlockFix = value;
OnPropertyChanged();
}
}
public bool ShaderTranslationDelayEnabled
{
get => _shaderTranslationThreadSleep;
set
{
_shaderTranslationThreadSleep = value;
OnPropertyChanged();
}
}
public string ShaderTranslationDelayTooltipText => $"Current value: {ShaderTranslationDelay}"; public string ShaderTranslationDelayValueText => $"{ShaderTranslationDelay}ms";
public int ShaderTranslationDelay public int ShaderTranslationDelay
{ {
@@ -49,7 +28,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_shaderTranslationSleepDelay = value; _shaderTranslationSleepDelay = value;
OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayTooltipText)); OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayValueText));
} }
} }

View File

@@ -1,6 +1,7 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Gommon; using Gommon;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.OpenAL;
@@ -46,9 +47,9 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _resolutionScale; private int _resolutionScale;
private int _graphicsBackendMultithreadingIndex; private int _graphicsBackendMultithreadingIndex;
private float _volume; private float _volume;
private bool _isVulkanAvailable = true; [ObservableProperty] private bool _isVulkanAvailable = true;
private bool _gameDirectoryChanged; [ObservableProperty] private bool _gameDirectoryChanged;
private bool _autoloadDirectoryChanged; [ObservableProperty] private bool _autoloadDirectoryChanged;
private readonly List<string> _gpuIds = new(); private readonly List<string> _gpuIds = new();
private int _graphicsBackendIndex; private int _graphicsBackendIndex;
private int _scalingFilter; private int _scalingFilter;
@@ -63,7 +64,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _networkInterfaceIndex; private int _networkInterfaceIndex;
private int _multiplayerModeIndex; private int _multiplayerModeIndex;
private string _ldnPassphrase; private string _ldnPassphrase;
private string _ldnServer; [ObservableProperty] private string _ldnServer;
public SettingsHacksViewModel DirtyHacks { get; } public SettingsHacksViewModel DirtyHacks { get; }
@@ -111,43 +112,10 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool IsVulkanAvailable
{
get => _isVulkanAvailable;
set
{
_isVulkanAvailable = value;
OnPropertyChanged();
}
}
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
public bool GameDirectoryChanged
{
get => _gameDirectoryChanged;
set
{
_gameDirectoryChanged = value;
OnPropertyChanged();
}
}
public bool AutoloadDirectoryChanged
{
get => _autoloadDirectoryChanged;
set
{
_autoloadDirectoryChanged = value;
OnPropertyChanged();
}
}
public bool IsMacOS => OperatingSystem.IsMacOS(); public bool IsMacOS => OperatingSystem.IsMacOS();
public bool EnableDiscordIntegration { get; set; } public bool EnableDiscordIntegration { get; set; }
@@ -182,19 +150,12 @@ namespace Ryujinx.Ava.UI.ViewModels
_customVSyncInterval = newInterval; _customVSyncInterval = newInterval;
_customVSyncIntervalPercentageProxy = value; _customVSyncIntervalPercentageProxy = value;
OnPropertiesChanged( OnPropertiesChanged(
nameof(CustomVSyncInterval), nameof(CustomVSyncInterval),
nameof(CustomVSyncIntervalPercentageText)); nameof(CustomVSyncIntervalPercentageText));
} }
} }
public string CustomVSyncIntervalPercentageText public string CustomVSyncIntervalPercentageText => CustomVSyncIntervalPercentageProxy + "%";
{
get
{
string text = CustomVSyncIntervalPercentageProxy + "%";
return text;
}
}
public bool EnableCustomVSyncInterval public bool EnableCustomVSyncInterval
{ {
@@ -356,7 +317,6 @@ namespace Ryujinx.Ava.UI.ViewModels
set set
{ {
_networkInterfaceIndex = value != -1 ? value : 0; _networkInterfaceIndex = value != -1 ? value : 0;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
} }
} }
@@ -366,7 +326,6 @@ namespace Ryujinx.Ava.UI.ViewModels
set set
{ {
_multiplayerModeIndex = value; _multiplayerModeIndex = value;
ConfigurationState.Instance.Multiplayer.Mode.Value = (MultiplayerMode)_multiplayerModeIndex;
} }
} }
@@ -375,16 +334,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsInvalidLdnPassphraseVisible { get; set; } public bool IsInvalidLdnPassphraseVisible { get; set; }
public string LdnServer
{
get => _ldnServer;
set
{
_ldnServer = value;
OnPropertyChanged();
}
}
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
{ {
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
@@ -466,11 +415,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public void MatchSystemTime() public void MatchSystemTime()
{ {
var dto = DateTimeOffset.Now; (DateTimeOffset dto, TimeSpan timeOfDay) = DateTimeOffset.Now.Extract();
CurrentDate = new DateTimeOffset(dto.Year, dto.Month, dto.Day, 0, 0, 0, dto.Offset);
CurrentTime = dto.TimeOfDay; CurrentDate = dto;
CurrentTime = timeOfDay;
OnPropertyChanged(nameof(CurrentDate)); OnPropertyChanged(nameof(CurrentDate));
OnPropertyChanged(nameof(CurrentTime)); OnPropertyChanged(nameof(CurrentTime));
@@ -648,16 +596,14 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ShowTitleBar.Value = ShowTitleBar; config.ShowTitleBar.Value = ShowTitleBar;
config.HideCursor.Value = (HideCursorMode)HideCursor; config.HideCursor.Value = (HideCursorMode)HideCursor;
if (_gameDirectoryChanged) if (GameDirectoryChanged)
{ {
List<string> gameDirs = new(GameDirectories); config.UI.GameDirs.Value = [..GameDirectories];
config.UI.GameDirs.Value = gameDirs;
} }
if (_autoloadDirectoryChanged) if (AutoloadDirectoryChanged)
{ {
List<string> autoloadDirs = new(AutoloadDirectories); config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
config.UI.AutoloadDirs.Value = autoloadDirs;
} }
config.UI.BaseStyle.Value = BaseStyleIndex switch config.UI.BaseStyle.Value = BaseStyleIndex switch
@@ -756,7 +702,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Multiplayer.LdnServer.Value = LdnServer; config.Multiplayer.LdnServer.Value = LdnServer;
// Dirty Hacks // Dirty Hacks
config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFixEnabled; config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix;
config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled;
config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay;
@@ -767,8 +713,8 @@ namespace Ryujinx.Ava.UI.ViewModels
SaveSettingsEvent?.Invoke(); SaveSettingsEvent?.Invoke();
_gameDirectoryChanged = false; GameDirectoryChanged = false;
_autoloadDirectoryChanged = false; AutoloadDirectoryChanged = false;
} }
private static void RevertIfNotSaved() private static void RevertIfNotSaved()

View File

@@ -1,74 +1,32 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.HLE.FileSystem;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Application = Avalonia.Application;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public record TitleUpdateViewNoUpdateSentinal(); public record TitleUpdateViewModelNoUpdate;
public class TitleUpdateViewModel : BaseModel public partial class TitleUpdateViewModel : BaseModel
{ {
private ApplicationLibrary ApplicationLibrary { get; } private ApplicationLibrary ApplicationLibrary { get; }
private ApplicationData ApplicationData { get; } private ApplicationData ApplicationData { get; }
private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); [ObservableProperty] private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
private AvaloniaList<object> _views = new(); [ObservableProperty] private AvaloniaList<object> _views = new();
private object _selectedUpdate = new TitleUpdateViewNoUpdateSentinal(); [ObservableProperty] private object _selectedUpdate = new TitleUpdateViewModelNoUpdate();
private bool _showBundledContentNotice = false; [ObservableProperty] private bool _showBundledContentNotice;
public AvaloniaList<TitleUpdateModel> TitleUpdates private readonly IStorageProvider _storageProvider;
{
get => _titleUpdates;
set
{
_titleUpdates = value;
OnPropertyChanged();
}
}
public AvaloniaList<object> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public object SelectedUpdate
{
get => _selectedUpdate;
set
{
_selectedUpdate = value;
OnPropertyChanged();
}
}
public bool ShowBundledContentNotice
{
get => _showBundledContentNotice;
set
{
_showBundledContentNotice = value;
OnPropertyChanged();
}
}
public IStorageProvider StorageProvider;
public TitleUpdateViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) public TitleUpdateViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData)
{ {
@@ -76,7 +34,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ApplicationData = applicationData; ApplicationData = applicationData;
StorageProvider = RyujinxApp.MainWindow.StorageProvider; _storageProvider = RyujinxApp.MainWindow.StorageProvider;
LoadUpdates(); LoadUpdates();
} }
@@ -87,7 +45,7 @@ namespace Ryujinx.Ava.UI.ViewModels
.Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase); .Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase);
bool hasBundledContent = false; bool hasBundledContent = false;
SelectedUpdate = new TitleUpdateViewNoUpdateSentinal(); SelectedUpdate = new TitleUpdateViewModelNoUpdate();
foreach ((TitleUpdateModel update, bool isSelected) in updates) foreach ((TitleUpdateModel update, bool isSelected) in updates)
{ {
TitleUpdates.Add(update); TitleUpdates.Add(update);
@@ -113,12 +71,12 @@ namespace Ryujinx.Ava.UI.ViewModels
var selected = SelectedUpdate; var selected = SelectedUpdate;
Views.Clear(); Views.Clear();
Views.Add(new TitleUpdateViewNoUpdateSentinal()); Views.Add(new TitleUpdateViewModelNoUpdate());
Views.AddRange(sortedUpdates); Views.AddRange(sortedUpdates);
SelectedUpdate = selected; SelectedUpdate = selected;
if (SelectedUpdate is TitleUpdateViewNoUpdateSentinal) if (SelectedUpdate is TitleUpdateViewModelNoUpdate)
{ {
SelectedUpdate = Views[0]; SelectedUpdate = Views[0];
} }
@@ -176,7 +134,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
else if (update == SelectedUpdate as TitleUpdateModel) else if (update == SelectedUpdate as TitleUpdateModel)
{ {
SelectedUpdate = new TitleUpdateViewNoUpdateSentinal(); SelectedUpdate = new TitleUpdateViewModelNoUpdate();
} }
SortUpdates(); SortUpdates();
@@ -184,7 +142,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task Add() public async Task Add()
{ {
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = true, AllowMultiple = true,
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>

View File

@@ -1,4 +1,5 @@
using Avalonia.Media; using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
@@ -20,12 +21,12 @@ using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
internal class UserFirmwareAvatarSelectorViewModel : BaseModel internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel
{ {
private static readonly Dictionary<string, byte[]> _avatarStore = new(); private static readonly Dictionary<string, byte[]> _avatarStore = new();
private ObservableCollection<ProfileImageModel> _images; [ObservableProperty] private ObservableCollection<ProfileImageModel> _images;
private Color _backgroundColor = Colors.White; [ObservableProperty] private Color _backgroundColor = Colors.White;
private int _selectedIndex; private int _selectedIndex;
@@ -34,27 +35,11 @@ namespace Ryujinx.Ava.UI.ViewModels
_images = new ObservableCollection<ProfileImageModel>(); _images = new ObservableCollection<ProfileImageModel>();
LoadImagesFromStore(); LoadImagesFromStore();
} PropertyChanged += (_, args) =>
public Color BackgroundColor
{
get => _backgroundColor;
set
{ {
_backgroundColor = value; if (args.PropertyName == nameof(BackgroundColor))
OnPropertyChanged(); ChangeImageBackground();
ChangeImageBackground(); };
}
}
public ObservableCollection<ProfileImageModel> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
} }
public int SelectedIndex public int SelectedIndex
@@ -70,7 +55,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
else else
{ {
SelectedImage = _images[_selectedIndex].Data; SelectedImage = Images[_selectedIndex].Data;
} }
OnPropertyChanged(); OnPropertyChanged();

View File

@@ -1,18 +1,9 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
internal class UserProfileImageSelectorViewModel : BaseModel internal partial class UserProfileImageSelectorViewModel : BaseModel
{ {
private bool _firmwareFound; [ObservableProperty] private bool _firmwareFound;
public bool FirmwareFound
{
get => _firmwareFound;
set
{
_firmwareFound = value;
OnPropertyChanged();
}
}
} }
} }

View File

@@ -1,3 +1,4 @@
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -8,74 +9,31 @@ using System.Collections.ObjectModel;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class UserSaveManagerViewModel : BaseModel public partial class UserSaveManagerViewModel : BaseModel
{ {
private int _sortIndex; [ObservableProperty] private int _sortIndex;
private int _orderIndex; [ObservableProperty] private int _orderIndex;
private string _search; [ObservableProperty] private string _search;
private ObservableCollection<SaveModel> _saves = new(); [ObservableProperty] private ObservableCollection<SaveModel> _saves = new();
private ObservableCollection<SaveModel> _views = new(); [ObservableProperty] private ObservableCollection<SaveModel> _views = new();
private readonly AccountManager _accountManager; private readonly AccountManager _accountManager;
public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
public int SortIndex
{
get => _sortIndex;
set
{
_sortIndex = value;
OnPropertyChanged();
Sort();
}
}
public int OrderIndex
{
get => _orderIndex;
set
{
_orderIndex = value;
OnPropertyChanged();
Sort();
}
}
public string Search
{
get => _search;
set
{
_search = value;
OnPropertyChanged();
Sort();
}
}
public ObservableCollection<SaveModel> Saves
{
get => _saves;
set
{
_saves = value;
OnPropertyChanged();
Sort();
}
}
public ObservableCollection<SaveModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public UserSaveManagerViewModel(AccountManager accountManager) public UserSaveManagerViewModel(AccountManager accountManager)
{ {
_accountManager = accountManager; _accountManager = accountManager;
PropertyChanged += (_, evt) =>
{
if (evt.PropertyName is
nameof(SortIndex) or
nameof(OrderIndex) or
nameof(Search) or
nameof(Saves))
{
Sort();
}
};
} }
public void Sort() public void Sort()
@@ -85,8 +43,10 @@ namespace Ryujinx.Ava.UI.ViewModels
.Sort(GetComparer()) .Sort(GetComparer())
.Bind(out var view).AsObservableList(); .Bind(out var view).AsObservableList();
#pragma warning disable MVVMTK0034
_views.Clear(); _views.Clear();
_views.AddRange(view); _views.AddRange(view);
#pragma warning restore MVVMTK0034
OnPropertyChanged(nameof(Views)); OnPropertyChanged(nameof(Views));
} }
@@ -94,7 +54,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
if (arg is SaveModel save) if (arg is SaveModel save)
{ {
return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower()); return string.IsNullOrWhiteSpace(Search) || save.Title.ToLower().Contains(Search.ToLower());
} }
return false; return false;

View File

@@ -247,6 +247,7 @@
Header="{ext:Locale MenuBarActionsScanAmiiboBin}" Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
Icon="{ext:Icon mdi-cube-scan}" Icon="{ext:Icon mdi-cube-scan}"
IsVisible="{Binding CanScanAmiiboBinaries}" IsVisible="{Binding CanScanAmiiboBinaries}"
InputGesture="Ctrl + B"
IsEnabled="{Binding IsAmiiboBinRequested}" /> IsEnabled="{Binding IsAmiiboBinRequested}" />
<MenuItem <MenuItem
Command="{Binding TakeScreenshot}" Command="{Binding TakeScreenshot}"

View File

@@ -37,7 +37,7 @@
ToolTip.Tip="{Binding DirtyHacks.Xc2MenuFixTooltip}"> ToolTip.Tip="{Binding DirtyHacks.Xc2MenuFixTooltip}">
<CheckBox <CheckBox
Margin="0" Margin="0"
IsChecked="{Binding DirtyHacks.Xc2MenuSoftlockFixEnabled}"/> IsChecked="{Binding DirtyHacks.Xc2MenuSoftlockFix}"/>
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Text="Xenoblade Chronicles 2 Menu Softlock Fix" /> Text="Xenoblade Chronicles 2 Menu Softlock Fix" />
@@ -54,21 +54,27 @@
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="Arbitrary Delay on Shader Translation"/> Text="Arbitrary Delay on Shader Translation"/>
</StackPanel> </StackPanel>
<Slider IsVisible="{Binding DirtyHacks.ShaderTranslationDelayEnabled}" <StackPanel
HorizontalAlignment="Center" IsVisible="{Binding DirtyHacks.ShaderTranslationDelayEnabled}"
Value="{Binding DirtyHacks.ShaderTranslationDelay}" Margin="0,10,0,0"
ToolTip.Tip="{Binding DirtyHacks.ShaderTranslationDelayTooltipText}" Orientation="Horizontal"
Width="175" HorizontalAlignment="Center">
Margin="0,-3,0,0" <Slider HorizontalAlignment="Center"
Height="32" Value="{Binding DirtyHacks.ShaderTranslationDelay}"
Padding="0,-5" Width="175"
TickFrequency="1" Margin="0,-3,0,0"
IsSnapToTickEnabled="True" Height="32"
LargeChange="10" Padding="0,-5"
SmallChange="1" TickFrequency="1"
VerticalAlignment="Center" IsSnapToTickEnabled="True"
Minimum="1" LargeChange="10"
Maximum="1000" /> SmallChange="1"
VerticalAlignment="Center"
Minimum="1"
Maximum="1000" />
<TextBlock Margin="5,0"
Text="{Binding DirtyHacks.ShaderTranslationDelayValueText}"/>
</StackPanel>
<Separator/> <Separator/>
</StackPanel> </StackPanel>
</Border> </Border>

View File

@@ -40,6 +40,7 @@
<KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" /> <KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" />
<KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" /> <KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" />
<KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" /> <KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" />
<KeyBinding Gesture="Ctrl+B" Command="{Binding OpenBinFile}" />
</Window.KeyBindings> </Window.KeyBindings>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*">
<helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" /> <helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" />

View File

@@ -93,6 +93,7 @@
Padding="10" Padding="10"
MinWidth="0" MinWidth="0"
MinHeight="0" MinHeight="0"
ToolTip.Tip="{Binding Path}"
Click="OpenLocation"> Click="OpenLocation">
<ui:SymbolIcon <ui:SymbolIcon
Symbol="OpenFolder" Symbol="OpenFolder"

View File

@@ -95,7 +95,7 @@
</Panel> </Panel>
</DataTemplate> </DataTemplate>
<DataTemplate <DataTemplate
DataType="viewModels:TitleUpdateViewNoUpdateSentinal"> DataType="viewModels:TitleUpdateViewModelNoUpdate">
<Panel <Panel
Height="33" Height="33"
Margin="10"> Margin="10">

View File

@@ -645,6 +645,9 @@ namespace Ryujinx.Ava.Utilities.Configuration
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe) private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
{ {
if (!ShowDirtyHacks)
return;
var newHacks = EnabledHacks.Select(x => x.Hack) var newHacks = EnabledHacks.Select(x => x.Hack)
.JoinToString(", "); .JoinToString(", ");