Compare commits

...

7 Commits

Author SHA1 Message Date
Keaton
d61ff7a2c6 Merge a8e453e1dc into da3f4e1d3a 2025-03-02 21:30:01 -06:00
Evan Husted
da3f4e1d3a misc: Created generic type RyujinxControl to allow for more unified control view model definitions 2025-03-02 21:24:39 -06:00
Evan Husted
69d79322bb misc: chore: remove old title ID constructor for RendererHost 2025-03-02 21:23:36 -06:00
Evan Husted
a8e453e1dc Merge branch 'master' into ffmpeg-win-arm64 2025-03-02 18:41:17 -06:00
KeatonTheBot
03a73d763e Disable trimming on win-arm64
* nuget: Bump System group to 9.0.2
* Clean up RuntimeIdentifiers in project files
2025-02-26 15:38:40 -06:00
KeatonTheBot
6c6640c7f3 Add win-arm64 to workflows 2025-02-26 15:38:40 -06:00
KeatonTheBot
05cb518d60 Update FFmpeg runtimes to 6.1.2
Add runtimes for win-arm64 arch.
2025-02-26 15:38:40 -06:00
34 changed files with 161 additions and 201 deletions

View File

@@ -19,6 +19,7 @@ jobs:
configuration: [Debug, Release]
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }

View File

@@ -62,6 +62,7 @@ jobs:
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
@@ -79,6 +80,7 @@ jobs:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:
@@ -122,7 +124,6 @@ jobs:
if: matrix.platform.os == 'windows-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
shell: bash
@@ -131,7 +132,6 @@ jobs:
if: matrix.platform.os == 'ubuntu-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
chmod +x Ryujinx.sh Ryujinx
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd

View File

@@ -65,6 +65,7 @@ jobs:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:
@@ -107,7 +108,6 @@ jobs:
if: matrix.platform.os == 'windows-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
popd
shell: bash

View File

@@ -39,7 +39,7 @@
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
@@ -53,8 +53,8 @@
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
<PackageVersion Include="System.Management" Version="9.0.0" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
<PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>

View File

@@ -11,7 +11,7 @@
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Native\libs\libarmeilleure-jitsupport.dylib" Condition="'$(RuntimeIdentifier)' == '' OR '$(RuntimeIdentifier)' == 'osx-arm64'">
<ContentWithTargetPath Include="Native\libs\libarmeilleure-jitsupport.dylib" Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libarmeilleure-jitsupport.dylib</TargetPath>
</ContentWithTargetPath>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64</RuntimeIdentifiers>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup>
@@ -11,15 +11,15 @@
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dylib</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.so</TargetPath>
</ContentWithTargetPath>

View File

@@ -12,8 +12,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new()
{
{ AvCodecLibraryName, (58, 59) },
{ AvUtilLibraryName, (56, 57) },
{ AvCodecLibraryName, (58, 61) },
{ AvUtilLibraryName, (56, 59) },
};
private static string FormatLibraryNameForCurrentOs(string libraryName, int version)

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64;</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>
@@ -29,12 +29,18 @@
<TrimMode>partial</TrimMode>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<!--
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
See:
https://github.com/amwx/FluentAvalonia/issues/481
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
-->
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
@@ -57,8 +63,8 @@
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
<PackageReference Include="securifybv.ShellLink" />
<PackageReference Include="Sep" />
<PackageReference Include="Silk.NET.Vulkan" />
@@ -67,7 +73,7 @@
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
@@ -84,7 +90,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>alsoft.ini</TargetPath>
</Content>

View File

@@ -9,17 +9,14 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
namespace Ryujinx.Ava.UI.Applet
{
public partial class ProfileSelectorDialog : UserControl
public partial class ProfileSelectorDialog : RyujinxControl<ProfileSelectorDialogViewModel>
{
public ProfileSelectorDialogViewModel ViewModel { get; set; }
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
{
DataContext = ViewModel = viewModel;

View File

@@ -26,6 +26,7 @@ namespace Ryujinx.Ava.UI.Controls
{
public class ApplicationContextMenu : MenuFlyout
{
public ApplicationContextMenu()
{
InitializeComponent();

View File

@@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Controls
{
public partial class ApplicationDataView : UserControl
public partial class ApplicationDataView : RyujinxControl<ApplicationDataViewModel>
{
public static async Task Show(ApplicationData appData)
{
@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Controls
SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
MinWidth = 256,
Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) }
Content = new ApplicationDataView { ViewModel = new ApplicationDataViewModel(appData) }
};
Style closeButton = new(x => x.Name("CloseButton"));

View File

@@ -2,12 +2,13 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary;
using System;
namespace Ryujinx.Ava.UI.Controls
{
public partial class ApplicationGridView : UserControl
public partial class ApplicationGridView : RyujinxControl<MainWindowViewModel>
{
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);

View File

@@ -11,7 +11,7 @@ using System.Linq;
namespace Ryujinx.Ava.UI.Controls
{
public partial class ApplicationListView : UserControl
public partial class ApplicationListView : RyujinxControl<MainWindowViewModel>
{
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
@@ -32,9 +32,6 @@ namespace Ryujinx.Ava.UI.Controls
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;
@@ -43,16 +40,13 @@ namespace Ryujinx.Ava.UI.Controls
private async void IdString_OnClick(object sender, RoutedEventArgs e)
{
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock idText })
return;
if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard))
return;
ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
ApplicationData appData = ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text);
if (appData is null)
return;

View File

@@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Controls
{
public partial class DlcSelectView : UserControl
public partial class DlcSelectView : RyujinxControl<DlcSelectViewModel>
{
public DlcSelectView()
{
@@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Controls
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
SecondaryButtonText = string.Empty,
CloseButtonText = string.Empty,
Content = new DlcSelectView { DataContext = viewModel }
Content = new DlcSelectView { ViewModel = viewModel }
};
Style closeButton = new(x => x.Name("CloseButton"));

View File

@@ -23,13 +23,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls
{
public partial class NavigationDialogHost : UserControl
public partial class NavigationDialogHost : RyujinxControl<UserProfileViewModel>
{
public AccountManager AccountManager { get; }
public ContentManager ContentManager { get; }
public VirtualFileSystem VirtualFileSystem { get; }
public HorizonClient HorizonClient { get; }
public UserProfileViewModel ViewModel { get; set; }
public NavigationDialogHost()
{

View File

@@ -0,0 +1,18 @@
using Avalonia.Controls;
using Gommon;
using Ryujinx.Ava.UI.ViewModels;
using System;
namespace Ryujinx.Ava.UI.Controls
{
public class RyujinxControl<TViewModel> : UserControl where TViewModel : BaseModel
{
public TViewModel ViewModel
{
get => (TViewModel)DataContext ?? throw new InvalidOperationException(
$"Underlying DataContext is not of type {typeof(TViewModel).AsPrettyString()}; " +
$"Actual type is {DataContext?.GetType().AsPrettyString()}");
set => DataContext = value;
}
}
}

View File

@@ -2,9 +2,7 @@
using Avalonia.Controls;
using Avalonia.Media;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.Ava.UI.Renderer
@@ -38,32 +36,6 @@ namespace Ryujinx.Ava.UI.Renderer
EmbeddedWindowOpenGL => GraphicsBackend.OpenGl,
_ => throw new NotImplementedException()
};
public RendererHost(string titleId)
{
Focusable = true;
FlowDirection = FlowDirection.LeftToRight;
EmbeddedWindow =
#pragma warning disable CS8524
ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch
#pragma warning restore CS8524
{
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(),
};
string backendText = EmbeddedWindow switch
{
EmbeddedWindowVulkan => "Vulkan",
EmbeddedWindowOpenGL => "OpenGL",
_ => throw new NotImplementedException()
};
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend ({ConfigurationState.Instance.Graphics.GraphicsBackend.Value}): {backendText}");
Initialize();
}
private void Initialize()

View File

@@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels
{
internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel
public partial class UserFirmwareAvatarSelectorViewModel : BaseModel
{
private static readonly Dictionary<string, byte[]> _avatarStore = new();

View File

@@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace Ryujinx.Ava.UI.ViewModels
{
internal partial class UserProfileImageSelectorViewModel : BaseModel
public partial class UserProfileImageSelectorViewModel : BaseModel
{
[ObservableProperty] private bool _firmwareFound;
}

View File

@@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Common.Configuration.Hid.Controller;
@@ -14,7 +15,7 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
namespace Ryujinx.Ava.UI.Views.Input
{
public partial class ControllerInputView : UserControl
public partial class ControllerInputView : RyujinxControl<ControllerInputViewModel>
{
private ButtonKeyAssigner _currentAssigner;
@@ -217,20 +218,12 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed -= MouseClick;
}
private IButtonAssigner CreateButtonAssigner(bool forStick)
{
IButtonAssigner assigner;
ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel;
assigner = new GamepadButtonAssigner(
controllerInputViewModel.ParentModel.SelectedGamepad,
(controllerInputViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
private IButtonAssigner CreateButtonAssigner(bool forStick) =>
new GamepadButtonAssigner(
ViewModel.ParentModel.SelectedGamepad,
(ViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
forStick);
return assigner;
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);

View File

@@ -1,19 +1,19 @@
using Avalonia.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels.Input;
namespace Ryujinx.Ava.UI.Views.Input
{
public partial class InputView : UserControl
public partial class InputView : RyujinxControl<InputViewModel>
{
private bool _dialogOpen;
private InputViewModel ViewModel { get; set; }
public InputView()
{
DataContext = ViewModel = new InputViewModel(this);
ViewModel = new InputViewModel(this);
InitializeComponent();
}

View File

@@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input;
@@ -13,7 +14,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.UI.Views.Input
{
public partial class KeyboardInputView : UserControl
public partial class KeyboardInputView : RyujinxControl<KeyboardInputViewModel>
{
private ButtonKeyAssigner _currentAssigner;
@@ -60,106 +61,103 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed += MouseClick;
if (DataContext is not KeyboardInputViewModel viewModel)
return;
IKeyboard keyboard =
(IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
(IKeyboard)ViewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
IButtonAssigner assigner =
new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad);
new KeyboardKeyAssigner((IKeyboard)ViewModel.ParentModel.SelectedGamepad);
_currentAssigner.ButtonAssigned += (_, e) =>
_currentAssigner.ButtonAssigned += (_, be) =>
{
if (e.ButtonValue.HasValue)
if (be.ButtonValue.HasValue)
{
Button buttonValue = e.ButtonValue.Value;
viewModel.ParentModel.IsModified = true;
Button buttonValue = be.ButtonValue.Value;
ViewModel.ParentModel.IsModified = true;
switch (button.Name)
{
case "ButtonZl":
viewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
break;
case "ButtonL":
viewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
break;
case "ButtonMinus":
viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
break;
case "LeftStickButton":
viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
break;
case "LeftStickUp":
viewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
break;
case "LeftStickDown":
viewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
break;
case "LeftStickRight":
viewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
break;
case "LeftStickLeft":
viewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
break;
case "DpadUp":
viewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
ViewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
break;
case "DpadDown":
viewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
ViewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
break;
case "DpadLeft":
viewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
ViewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
break;
case "DpadRight":
viewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
ViewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
break;
case "LeftButtonSr":
viewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
break;
case "LeftButtonSl":
viewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
ViewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
break;
case "RightButtonSr":
viewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
ViewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
break;
case "RightButtonSl":
viewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
ViewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
break;
case "ButtonZr":
viewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
break;
case "ButtonR":
viewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
break;
case "ButtonPlus":
viewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
break;
case "ButtonA":
viewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
break;
case "ButtonB":
viewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
break;
case "ButtonX":
viewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
break;
case "ButtonY":
viewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
ViewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
break;
case "RightStickButton":
viewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
ViewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
break;
case "RightStickUp":
viewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
ViewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
break;
case "RightStickDown":
viewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
ViewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
break;
case "RightStickRight":
viewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
ViewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
break;
case "RightStickLeft":
viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
ViewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
break;
}
}

View File

@@ -2,19 +2,18 @@
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks;
namespace Ryujinx.UI.Views.Input
{
public partial class LedInputView : UserControl
public partial class LedInputView : RyujinxControl<LedInputViewModel>
{
private readonly LedInputViewModel _viewModel;
public LedInputView(ControllerInputViewModel viewModel)
{
DataContext = _viewModel = new LedInputViewModel
ViewModel = new LedInputViewModel
{
ParentModel = viewModel.ParentModel,
TurnOffLed = viewModel.Config.TurnOffLed,
@@ -29,20 +28,18 @@ namespace Ryujinx.UI.Views.Input
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
{
if (!args.NewColor.HasValue) return;
if (DataContext is not LedInputViewModel lvm) return;
if (!lvm.EnableLedChanging) return;
if (lvm.TurnOffLed) return;
if (!ViewModel.EnableLedChanging) return;
if (ViewModel.TurnOffLed) return;
lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
}
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
if (DataContext is not LedInputViewModel lvm) return;
if (!lvm.EnableLedChanging) return;
if (lvm.TurnOffLed) return;
if (!ViewModel.EnableLedChanging) return;
if (ViewModel.TurnOffLed) return;
lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32());
ViewModel.ParentModel.SelectedGamepad.SetLed(ViewModel.LedColor.ToUInt32());
}
public static async Task Show(ControllerInputViewModel viewModel)
@@ -57,13 +54,13 @@ namespace Ryujinx.UI.Views.Input
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content,
};
contentDialog.PrimaryButtonClick += (sender, args) =>
contentDialog.PrimaryButtonClick += (_, _) =>
{
GamepadInputConfig config = viewModel.Config;
config.EnableLedChanging = content._viewModel.EnableLedChanging;
config.LedColor = content._viewModel.LedColor;
config.UseRainbowLed = content._viewModel.UseRainbowLed;
config.TurnOffLed = content._viewModel.TurnOffLed;
config.EnableLedChanging = content.ViewModel.EnableLedChanging;
config.LedColor = content.ViewModel.LedColor;
config.UseRainbowLed = content.ViewModel.UseRainbowLed;
config.TurnOffLed = content.ViewModel.TurnOffLed;
};
await contentDialog.ShowAsync();

View File

@@ -1,16 +1,15 @@
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Input
{
public partial class MotionInputView : UserControl
public partial class MotionInputView : RyujinxControl<MotionInputViewModel>
{
private readonly MotionInputViewModel _viewModel;
public MotionInputView()
{
InitializeComponent();
@@ -20,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{
GamepadInputConfig config = viewModel.Config;
_viewModel = new MotionInputViewModel
ViewModel = new MotionInputViewModel
{
Slot = config.Slot,
AltSlot = config.AltSlot,
@@ -33,7 +32,6 @@ namespace Ryujinx.Ava.UI.Views.Input
};
InitializeComponent();
DataContext = _viewModel;
}
public static async Task Show(ControllerInputViewModel viewModel)
@@ -48,17 +46,17 @@ namespace Ryujinx.Ava.UI.Views.Input
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content,
};
contentDialog.PrimaryButtonClick += (sender, args) =>
contentDialog.PrimaryButtonClick += (_, _) =>
{
GamepadInputConfig config = viewModel.Config;
config.Slot = content._viewModel.Slot;
config.Sensitivity = content._viewModel.Sensitivity;
config.GyroDeadzone = content._viewModel.GyroDeadzone;
config.AltSlot = content._viewModel.AltSlot;
config.DsuServerHost = content._viewModel.DsuServerHost;
config.DsuServerPort = content._viewModel.DsuServerPort;
config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
config.MirrorInput = content._viewModel.MirrorInput;
config.Slot = content.ViewModel.Slot;
config.Sensitivity = content.ViewModel.Sensitivity;
config.GyroDeadzone = content.ViewModel.GyroDeadzone;
config.AltSlot = content.ViewModel.AltSlot;
config.DsuServerHost = content.ViewModel.DsuServerHost;
config.DsuServerPort = content.ViewModel.DsuServerPort;
config.EnableCemuHookMotion = content.ViewModel.EnableCemuHookMotion;
config.MirrorInput = content.ViewModel.MirrorInput;
};
await contentDialog.ShowAsync();

View File

@@ -1,16 +1,15 @@
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Input
{
public partial class RumbleInputView : UserControl
public partial class RumbleInputView : RyujinxControl<RumbleInputViewModel>
{
private readonly RumbleInputViewModel _viewModel;
public RumbleInputView()
{
InitializeComponent();
@@ -20,15 +19,13 @@ namespace Ryujinx.Ava.UI.Views.Input
{
GamepadInputConfig config = viewModel.Config;
_viewModel = new RumbleInputViewModel
ViewModel = new RumbleInputViewModel
{
StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble,
};
InitializeComponent();
DataContext = _viewModel;
}
public static async Task Show(ControllerInputViewModel viewModel)
@@ -44,11 +41,11 @@ namespace Ryujinx.Ava.UI.Views.Input
Content = content,
};
contentDialog.PrimaryButtonClick += (sender, args) =>
contentDialog.PrimaryButtonClick += (_, _) =>
{
GamepadInputConfig config = viewModel.Config;
config.StrongRumble = content._viewModel.StrongRumble;
config.WeakRumble = content._viewModel.WeakRumble;
config.StrongRumble = content.ViewModel.StrongRumble;
config.WeakRumble = content.ViewModel.WeakRumble;
};
await contentDialog.ShowAsync();

View File

@@ -6,6 +6,7 @@ using Gommon;
using LibHac.Common;
using LibHac.Ns;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
@@ -25,10 +26,9 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Main
{
public partial class MainMenuBarView : UserControl
public partial class MainMenuBarView : RyujinxControl<MainWindowViewModel>
{
public MainWindow Window { get; private set; }
public MainWindowViewModel ViewModel { get; private set; }
public MainMenuBarView()
{

View File

@@ -4,6 +4,8 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
@@ -13,7 +15,7 @@ using System;
namespace Ryujinx.Ava.UI.Views.Main
{
public partial class MainStatusBarView : UserControl
public partial class MainStatusBarView : RyujinxControl<MainWindowViewModel>
{
public MainWindow Window;
@@ -29,7 +31,7 @@ namespace Ryujinx.Ava.UI.Views.Main
if (VisualRoot is MainWindow window)
{
Window = window;
DataContext = window.ViewModel;
ViewModel = window.ViewModel;
LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() =>
{
if (Window.ViewModel.EnableNonGameRunningControls)

View File

@@ -3,16 +3,15 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using System;
namespace Ryujinx.Ava.UI.Views.Main
{
public partial class MainViewControls : UserControl
public partial class MainViewControls : RyujinxControl<MainWindowViewModel>
{
public MainWindowViewModel ViewModel;
public MainViewControls()
{
InitializeComponent();
@@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Views.Main
if (VisualRoot is MainWindow window)
{
DataContext = ViewModel = window.ViewModel;
ViewModel = window.ViewModel;
}
}

View File

@@ -13,13 +13,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Views.User
{
public partial class UserEditorView : UserControl
public partial class UserEditorView : RyujinxControl<TempProfile>
{
private NavigationDialogHost _parent;
private UserProfile _profile;
private bool _isNewUser;
public TempProfile TempProfile { get; set; }
public static uint MaxProfileNameLength => 0x20;
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
@@ -42,7 +41,7 @@ namespace Ryujinx.Ava.UI.Views.User
(NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
_isNewUser = isNewUser;
_profile = profile;
TempProfile = new TempProfile(_profile);
ViewModel = new TempProfile(_profile);
_parent = parent;
break;
@@ -51,8 +50,6 @@ namespace Ryujinx.Ava.UI.Views.User
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
DataContext = TempProfile;
AddPictureButton.IsVisible = _isNewUser;
ChangePictureButton.IsVisible = !_isNewUser;
IdLabel.IsVisible = _profile != null;
@@ -72,7 +69,7 @@ namespace Ryujinx.Ava.UI.Views.User
{
if (_isNewUser)
{
if (TempProfile.Name != String.Empty || TempProfile.Image != null)
if (ViewModel.Name != string.Empty || ViewModel.Image != null)
{
if (await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
@@ -89,7 +86,7 @@ namespace Ryujinx.Ava.UI.Views.User
}
else
{
if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image)
if (_profile.Name != ViewModel.Name || _profile.Image != ViewModel.Image)
{
if (await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
@@ -115,31 +112,31 @@ namespace Ryujinx.Ava.UI.Views.User
{
DataValidationErrors.ClearErrors(NameBox);
if (string.IsNullOrWhiteSpace(TempProfile.Name))
if (string.IsNullOrWhiteSpace(ViewModel.Name))
{
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError]));
return;
}
if (TempProfile.Image == null)
if (ViewModel.Image == null)
{
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel));
return;
}
if (_profile != null && !_isNewUser)
{
_profile.Name = TempProfile.Name;
_profile.Image = TempProfile.Image;
_profile.Name = ViewModel.Name;
_profile.Image = ViewModel.Image;
_profile.UpdateState();
_parent.AccountManager.SetUserName(_profile.UserId, _profile.Name);
_parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image);
}
else if (_isNewUser)
{
_parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId);
_parent.AccountManager.AddUser(ViewModel.Name, ViewModel.Image, ViewModel.UserId);
}
else
{
@@ -151,7 +148,7 @@ namespace Ryujinx.Ava.UI.Views.User
public void SelectProfileImage()
{
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel));
}
private void ChangePictureButton_Click(object sender, RoutedEventArgs e)

View File

@@ -1,4 +1,3 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
@@ -11,7 +10,7 @@ using System.IO;
namespace Ryujinx.Ava.UI.Views.User
{
public partial class UserFirmwareAvatarSelectorView : UserControl
public partial class UserFirmwareAvatarSelectorView : RyujinxControl<UserFirmwareAvatarSelectorViewModel>
{
private NavigationDialogHost _parent;
private TempProfile _profile;
@@ -20,8 +19,6 @@ namespace Ryujinx.Ava.UI.Views.User
{
ContentManager = contentManager;
DataContext = ViewModel;
InitializeComponent();
}
@@ -55,8 +52,6 @@ namespace Ryujinx.Ava.UI.Views.User
public ContentManager ContentManager { get; private set; }
internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; }
private void GoBack(object sender, RoutedEventArgs e)
{
_parent.GoBack();

View File

@@ -15,14 +15,12 @@ using System.IO;
namespace Ryujinx.Ava.UI.Views.User
{
public partial class UserProfileImageSelectorView : UserControl
public partial class UserProfileImageSelectorView : RyujinxControl<UserProfileImageSelectorViewModel>
{
private ContentManager _contentManager;
private NavigationDialogHost _parent;
private TempProfile _profile;
internal UserProfileImageSelectorViewModel ViewModel { get; private set; }
public UserProfileImageSelectorView()
{
InitializeComponent();

View File

@@ -4,10 +4,11 @@ using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
namespace Ryujinx.Ava.UI.Views.User
{
public partial class UserRecovererView : UserControl
public partial class UserRecovererView : RyujinxControl<UserProfileViewModel>
{
private NavigationDialogHost _parent;

View File

@@ -23,10 +23,8 @@ using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Views.User
{
public partial class UserSaveManagerView : UserControl
public partial class UserSaveManagerView : RyujinxControl<UserSaveManagerViewModel>
{
internal UserSaveManagerViewModel ViewModel { get; private set; }
private AccountManager _accountManager;
private HorizonClient _horizonClient;
private VirtualFileSystem _virtualFileSystem;
@@ -66,7 +64,7 @@ namespace Ryujinx.Ava.UI.Views.User
public void LoadSaves()
{
ViewModel.Saves.Clear();
Dispatcher.UIThread.Post(() => ViewModel.Saves.Clear());
ObservableCollection<SaveModel> saves = [];
SaveDataFilter saveDataFilter = SaveDataFilter.Make(
programId: default,

View File

@@ -11,12 +11,10 @@ using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Views.User
{
public partial class UserSelectorViews : UserControl
public partial class UserSelectorViews : RyujinxControl<UserProfileViewModel>
{
private NavigationDialogHost _parent;
public UserProfileViewModel ViewModel { get; set; }
public UserSelectorViews()
{
InitializeComponent();