aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Ava/UI/Views
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Ava/UI/Views
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Ava/UI/Views')
-rw-r--r--src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml164
-rw-r--r--src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs236
-rw-r--r--src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml232
-rw-r--r--src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs52
-rw-r--r--src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml175
-rw-r--r--src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs54
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml81
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs12
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml78
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs12
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml296
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs12
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml104
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs81
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml46
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs17
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml121
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs12
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml46
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs12
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml195
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs52
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml156
-rw-r--r--src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs82
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml123
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs165
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml114
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs88
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml63
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs124
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml83
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs51
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml215
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs147
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml165
-rw-r--r--src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs128
36 files changed, 3794 insertions, 0 deletions
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml
new file mode 100644
index 00000000..d5b5efcd
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml
@@ -0,0 +1,164 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ mc:Ignorable="d"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ x:DataType="viewModels:MainWindowViewModel"
+ x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView"
+ x:CompileBindings="True">
+ <Design.DataContext>
+ <viewModels:MainWindowViewModel />
+ </Design.DataContext>
+ <DockPanel HorizontalAlignment="Stretch">
+ <Menu
+ Name="Menu"
+ Height="35"
+ Margin="0"
+ HorizontalAlignment="Left">
+ <Menu.ItemsPanel>
+ <ItemsPanelTemplate>
+ <DockPanel Margin="0" HorizontalAlignment="Stretch" />
+ </ItemsPanelTemplate>
+ </Menu.ItemsPanel>
+ <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
+ <MenuItem
+ Command="{ReflectionBinding OpenFile}"
+ Header="{locale:Locale MenuBarFileOpenFromFile}"
+ IsEnabled="{Binding EnableNonGameRunningControls}"
+ ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
+ <MenuItem
+ Command="{ReflectionBinding OpenFolder}"
+ Header="{locale:Locale MenuBarFileOpenUnpacked}"
+ IsEnabled="{Binding EnableNonGameRunningControls}"
+ ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
+ <MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
+ <MenuItem
+ Click="OpenMiiApplet"
+ Header="Mii Edit Applet"
+ ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
+ </MenuItem>
+ <Separator />
+ <MenuItem
+ Command="{ReflectionBinding OpenRyujinxFolder}"
+ Header="{locale:Locale MenuBarFileOpenEmuFolder}"
+ ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
+ <MenuItem
+ Command="{ReflectionBinding OpenLogsFolder}"
+ Header="{locale:Locale MenuBarFileOpenLogsFolder}"
+ ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
+ <Separator />
+ <MenuItem
+ Click="CloseWindow"
+ Header="{locale:Locale MenuBarFileExit}"
+ ToolTip.Tip="{locale:Locale ExitTooltip}" />
+ </MenuItem>
+ <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
+ <MenuItem
+ Command="{ReflectionBinding ToggleFullscreen}"
+ Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
+ InputGesture="F11" />
+ <MenuItem>
+ <MenuItem.Icon>
+ <CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
+ MinWidth="250">
+ <TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
+ </CheckBox>
+ </MenuItem.Icon>
+ </MenuItem>
+ <MenuItem IsVisible="{Binding ShowConsoleVisible}">
+ <MenuItem.Icon>
+ <CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
+ MinWidth="250">
+ <TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
+ </CheckBox>
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator />
+ <MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
+ <MenuItem Name="ToggleFileTypesMenuItem" Header="{locale:Locale MenuBarShowFileTypes}" />
+ <Separator />
+ <MenuItem
+ Click="OpenSettings"
+ Header="{locale:Locale MenuBarOptionsSettings}"
+ ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
+ <MenuItem
+ Command="{ReflectionBinding ManageProfiles}"
+ Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
+ IsEnabled="{Binding EnableNonGameRunningControls}"
+ ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
+ </MenuItem>
+ <MenuItem
+ Name="ActionsMenuItem"
+ VerticalAlignment="Center"
+ Header="{locale:Locale MenuBarActions}"
+ IsEnabled="{Binding IsGameRunning}">
+ <MenuItem
+ Click="PauseEmulation_Click"
+ Header="{locale:Locale MenuBarOptionsPauseEmulation}"
+ InputGesture="{Binding PauseKey}"
+ IsEnabled="{Binding !IsPaused}"
+ IsVisible="{Binding !IsPaused}" />
+ <MenuItem
+ Click="ResumeEmulation_Click"
+ Header="{locale:Locale MenuBarOptionsResumeEmulation}"
+ InputGesture="{Binding PauseKey}"
+ IsEnabled="{Binding IsPaused}"
+ IsVisible="{Binding IsPaused}" />
+ <MenuItem
+ Click="StopEmulation_Click"
+ Header="{locale:Locale MenuBarOptionsStopEmulation}"
+ InputGesture="Escape"
+ IsEnabled="{Binding IsGameRunning}"
+ ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
+ <MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
+ <Separator />
+ <MenuItem
+ Name="ScanAmiiboMenuItem"
+ AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
+ Click="OpenAmiiboWindow"
+ Header="{locale:Locale MenuBarActionsScanAmiibo}"
+ IsEnabled="{Binding IsAmiiboRequested}" />
+ <MenuItem
+ Command="{ReflectionBinding TakeScreenshot}"
+ Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
+ InputGesture="{Binding ScreenshotKey}"
+ IsEnabled="{Binding IsGameRunning}" />
+ <MenuItem
+ Command="{ReflectionBinding HideUi}"
+ Header="{locale:Locale MenuBarFileToolsHideUi}"
+ InputGesture="{Binding ShowUiKey}"
+ IsEnabled="{Binding IsGameRunning}" />
+ <MenuItem
+ Click="OpenCheatManagerForCurrentApp"
+ Header="{locale:Locale GameListContextMenuManageCheat}"
+ IsEnabled="{Binding IsGameRunning}" />
+ </MenuItem>
+ <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
+ <MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
+ <MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
+ <MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
+ </MenuItem>
+ <MenuItem Header="{locale:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
+ <MenuItem Header="{locale:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/>
+ <MenuItem Header="{locale:Locale MenuBarToolsUninstallFileTypes}" Click="UninstallFileTypes_Click"/>
+ </MenuItem>
+ </MenuItem>
+ <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
+ <MenuItem
+ Name="UpdateMenuItem"
+ IsEnabled="{Binding CanUpdate}"
+ Click="CheckForUpdates"
+ Header="{locale:Locale MenuBarHelpCheckForUpdates}"
+ ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
+ <Separator />
+ <MenuItem
+ Click="OpenAboutWindow"
+ Header="{locale:Locale MenuBarHelpAbout}"
+ ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
+ </MenuItem>
+ </Menu>
+ </DockPanel>
+</UserControl>
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
new file mode 100644
index 00000000..bdf2cf9f
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
@@ -0,0 +1,236 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using LibHac.Ncm;
+using LibHac.Tools.FsSystem.NcaUtils;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Helpers;
+using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.Ava.UI.Windows;
+using Ryujinx.Common;
+using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.HOS;
+using Ryujinx.Modules;
+using Ryujinx.Ui.Common;
+using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Ryujinx.Ava.UI.Views.Main
+{
+ public partial class MainMenuBarView : UserControl
+ {
+ public MainWindow Window { get; private set; }
+ public MainWindowViewModel ViewModel { get; private set; }
+
+ public MainMenuBarView()
+ {
+ InitializeComponent();
+
+ ToggleFileTypesMenuItem.Items = GenerateToggleFileTypeItems();
+ ChangeLanguageMenuItem.Items = GenerateLanguageMenuItems();
+ }
+
+ private CheckBox[] GenerateToggleFileTypeItems()
+ {
+ List<CheckBox> checkBoxes = new();
+
+ foreach (var item in Enum.GetValues(typeof (FileTypes)))
+ {
+ string fileName = Enum.GetName(typeof (FileTypes), item);
+ checkBoxes.Add(new CheckBox()
+ {
+ Content = $".{fileName}",
+ IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
+ Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName))
+ });
+ }
+
+ return checkBoxes.ToArray();
+ }
+
+ private MenuItem[] GenerateLanguageMenuItems()
+ {
+ List<MenuItem> menuItems = new();
+
+ string localePath = "Ryujinx.Ava/Assets/Locales";
+ string localeExt = ".json";
+
+ string[] localesPath = EmbeddedResources.GetAllAvailableResources(localePath, localeExt);
+
+ Array.Sort(localesPath);
+
+ foreach (string locale in localesPath)
+ {
+ string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
+ string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
+ var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
+
+ if (!strings.TryGetValue("Language", out string languageName))
+ {
+ languageName = languageCode;
+ }
+
+ MenuItem menuItem = new()
+ {
+ Header = languageName,
+ Command = MiniCommand.Create(() =>
+ {
+ ViewModel.ChangeLanguage(languageCode);
+ })
+ };
+
+ menuItems.Add(menuItem);
+ }
+
+ return menuItems.ToArray();
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ if (VisualRoot is MainWindow window)
+ {
+ Window = window;
+ }
+
+ ViewModel = Window.ViewModel;
+ DataContext = ViewModel;
+ }
+
+ private async void StopEmulation_Click(object sender, RoutedEventArgs e)
+ {
+ await Window.ViewModel.AppHost?.ShowExitPrompt();
+ }
+
+ private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
+ {
+ await Task.Run(() =>
+ {
+ Window.ViewModel.AppHost?.Pause();
+ });
+ }
+
+ private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
+ {
+ await Task.Run(() =>
+ {
+ Window.ViewModel.AppHost?.Resume();
+ });
+ }
+
+ public async void OpenSettings(object sender, RoutedEventArgs e)
+ {
+ Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
+
+ await Window.SettingsWindow.ShowDialog(Window);
+
+ ViewModel.LoadConfigurableHotKeys();
+ }
+
+ public void OpenMiiApplet(object sender, RoutedEventArgs e)
+ {
+ string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
+
+ if (!string.IsNullOrEmpty(contentPath))
+ {
+ ViewModel.LoadApplication(contentPath, false, "Mii Applet");
+ }
+ }
+
+ public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
+ {
+ if (!ViewModel.IsAmiiboRequested)
+ {
+ return;
+ }
+
+ if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
+ {
+ string titleId = ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper();
+ AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId);
+
+ await window.ShowDialog(Window);
+
+ if (window.IsScanned)
+ {
+ ViewModel.ShowAll = window.ViewModel.ShowAllAmiibo;
+ ViewModel.LastScannedAmiiboId = window.ScannedAmiibo.GetId();
+
+ ViewModel.AppHost.Device.System.ScanAmiibo(deviceId, ViewModel.LastScannedAmiiboId, window.ViewModel.UseRandomUuid);
+ }
+ }
+ }
+
+ public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
+ {
+ if (!ViewModel.IsGameRunning)
+ {
+ return;
+ }
+
+ string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
+
+ await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window);
+
+ ViewModel.AppHost.Device.EnableCheats();
+ }
+
+ private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
+ {
+ if (sender is MenuItem)
+ {
+ ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
+ }
+ }
+
+ private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
+ {
+ if (FileAssociationHelper.Install())
+ {
+ await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
+ string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
+ }
+ else
+ {
+ await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
+ }
+ }
+
+ private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e)
+ {
+ if (FileAssociationHelper.Uninstall())
+ {
+ await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
+ string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
+ }
+ else
+ {
+ await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
+ }
+ }
+
+ public async void CheckForUpdates(object sender, RoutedEventArgs e)
+ {
+ if (Updater.CanUpdate(true))
+ {
+ await Updater.BeginParse(Window, true);
+ }
+ }
+
+ public async void OpenAboutWindow(object sender, RoutedEventArgs e)
+ {
+ await AboutWindow.Show();
+ }
+
+ public void CloseWindow(object sender, RoutedEventArgs e)
+ {
+ Window.Close();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml
new file mode 100644
index 00000000..16705695
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml
@@ -0,0 +1,232 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
+ x:CompileBindings="True"
+ x:DataType="viewModels:MainWindowViewModel">
+ <Design.DataContext>
+ <viewModels:MainWindowViewModel />
+ </Design.DataContext>
+ <Grid
+ Name="StatusBar"
+ Margin="0"
+ MinHeight="22"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Bottom"
+ Background="{DynamicResource ThemeContentBackgroundColor}"
+ DockPanel.Dock="Bottom"
+ IsVisible="{Binding ShowMenuAndStatusBar}">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <StackPanel
+ Grid.Column="0"
+ Margin="5"
+ VerticalAlignment="Center"
+ IsVisible="{Binding EnableNonGameRunningControls}">
+ <Grid Margin="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition />
+ </Grid.ColumnDefinitions>
+ <Button
+ Width="25"
+ Height="25"
+ MinWidth="0"
+ Margin="0,0,5,0"
+ VerticalAlignment="Center"
+ Background="Transparent"
+ Command="{ReflectionBinding LoadApplications}">
+ <ui:SymbolIcon
+ Width="50"
+ Height="100"
+ Symbol="Refresh" />
+ </Button>
+ <TextBlock
+ Name="LoadStatus"
+ Grid.Column="1"
+ Margin="0,0,5,0"
+ VerticalAlignment="Center"
+ IsVisible="{Binding EnableNonGameRunningControls}"
+ Text="{locale:Locale StatusBarGamesLoaded}" />
+ <ProgressBar
+ Name="LoadProgressBar"
+ Grid.Column="2"
+ Height="6"
+ VerticalAlignment="Center"
+ Foreground="{DynamicResource HighlightColor}"
+ IsVisible="{Binding StatusBarVisible}"
+ Maximum="{Binding StatusBarProgressMaximum}"
+ Value="{Binding StatusBarProgressValue}" />
+ </Grid>
+ </StackPanel>
+ <StackPanel
+ Grid.Column="1"
+ Margin="0,2"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding IsGameRunning}"
+ MaxHeight="18"
+ Orientation="Horizontal">
+ <TextBlock
+ Name="VsyncStatus"
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ Foreground="{Binding VsyncColor}"
+ IsVisible="{Binding !ShowLoadProgress}"
+ PointerReleased="VsyncStatus_PointerReleased"
+ Text="VSync"
+ TextAlignment="Left" />
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <TextBlock
+ Name="DockedStatus"
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding !ShowLoadProgress}"
+ PointerReleased="DockedStatus_PointerReleased"
+ Text="{Binding DockedStatusText}"
+ TextAlignment="Left" />
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <TextBlock
+ Name="AspectRatioStatus"
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding !ShowLoadProgress}"
+ PointerReleased="AspectRatioStatus_PointerReleased"
+ Text="{Binding AspectRatioStatusText}"
+ TextAlignment="Left" />
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <ui:ToggleSplitButton
+ Name="VolumeStatus"
+ Padding="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ VerticalContentAlignment="Center"
+ Background="{DynamicResource ThemeContentBackgroundColor}"
+ BorderThickness="0"
+ Content="{Binding VolumeStatusText}"
+ IsChecked="{Binding VolumeMuted}"
+ IsVisible="{Binding !ShowLoadProgress}">
+ <ui:ToggleSplitButton.Flyout>
+ <Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
+ <Grid Margin="0">
+ <Slider
+ MaxHeight="40"
+ Width="150"
+ Margin="0"
+ Padding="0"
+ IsSnapToTickEnabled="True"
+ LargeChange="0.05"
+ Maximum="1"
+ Minimum="0"
+ SmallChange="0.01"
+ TickFrequency="0.05"
+ ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
+ Value="{Binding Volume}" />
+ </Grid>
+ </Flyout>
+ </ui:ToggleSplitButton.Flyout>
+ </ui:ToggleSplitButton>
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <TextBlock
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding !ShowLoadProgress}"
+ Text="{Binding GameStatusText}"
+ TextAlignment="Left" />
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <TextBlock
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding !ShowLoadProgress}"
+ Text="{Binding FifoStatusText}"
+ TextAlignment="Left" />
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <TextBlock
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding !ShowLoadProgress}"
+ Text="{Binding BackendText}"
+ TextAlignment="Left" />
+ <Border
+ Width="2"
+ Height="12"
+ Margin="0"
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding !ShowLoadProgress}" />
+ <TextBlock
+ Margin="5,0,5,0"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center"
+ IsVisible="{Binding !ShowLoadProgress}"
+ Text="{Binding GpuNameText}"
+ TextAlignment="Left" />
+ </StackPanel>
+ <StackPanel
+ Grid.Column="3"
+ Margin="0,0,5,0"
+ VerticalAlignment="Center"
+ IsVisible="{Binding ShowFirmwareStatus}"
+ Orientation="Horizontal">
+ <TextBlock
+ Name="FirmwareStatus"
+ Margin="0"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Center"
+ Text="{locale:Locale StatusBarSystemVersion}" />
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs
new file mode 100644
index 00000000..473de0ee
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs
@@ -0,0 +1,52 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Ryujinx.Ava.UI.Windows;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Logging;
+using Ryujinx.Ui.Common.Configuration;
+using System;
+
+namespace Ryujinx.Ava.UI.Views.Main
+{
+ public partial class MainStatusBarView : UserControl
+ {
+ public MainWindow Window;
+
+ public MainStatusBarView()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ if (VisualRoot is MainWindow window)
+ {
+ Window = window;
+ }
+
+ DataContext = Window.ViewModel;
+ }
+
+ private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
+ {
+ Window.ViewModel.AppHost.Device.EnableDeviceVsync = !Window.ViewModel.AppHost.Device.EnableDeviceVsync;
+
+ Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {Window.ViewModel.AppHost.Device.EnableDeviceVsync}");
+ }
+
+ private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
+ {
+ ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
+ }
+
+ private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
+ {
+ AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
+
+ ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
new file mode 100644
index 00000000..ac8ede7a
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
@@ -0,0 +1,175 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="Ryujinx.Ava.UI.Views.Main.MainViewControls"
+ x:CompileBindings="True"
+ x:DataType="viewModels:MainWindowViewModel">
+ <Design.DataContext>
+ <viewModels:MainWindowViewModel />
+ </Design.DataContext>
+ <DockPanel
+ Margin="0,0,0,5"
+ HorizontalAlignment="Stretch">
+ <Button
+ Width="40"
+ MinWidth="40"
+ Margin="5,2,0,2"
+ VerticalAlignment="Stretch"
+ Command="{ReflectionBinding SetListMode}"
+ IsEnabled="{Binding IsGrid}">
+ <ui:FontIcon
+ Margin="0"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Center"
+ FontFamily="avares://FluentAvalonia/Fonts#Symbols"
+ Glyph="{helpers:GlyphValueConverter List}" />
+ </Button>
+ <Button
+ Width="40"
+ MinWidth="40"
+ Margin="5,2,5,2"
+ VerticalAlignment="Stretch"
+ Command="{ReflectionBinding SetGridMode}"
+ IsEnabled="{Binding IsList}">
+ <ui:FontIcon
+ Margin="0"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Center"
+ FontFamily="avares://FluentAvalonia/Fonts#Symbols"
+ Glyph="{helpers:GlyphValueConverter Grid}" />
+ </Button>
+ <TextBlock
+ Margin="10,0"
+ VerticalAlignment="Center"
+ Text="{locale:Locale IconSize}"
+ ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
+ <Slider
+ Width="150"
+ Height="35"
+ Margin="5,-10,5,0"
+ VerticalAlignment="Center"
+ IsSnapToTickEnabled="True"
+ Maximum="4"
+ Minimum="1"
+ TickFrequency="1"
+ ToolTip.Tip="{locale:Locale IconSizeTooltip}"
+ Value="{Binding GridSizeScale}" />
+ <CheckBox
+ Margin="0"
+ VerticalAlignment="Center"
+ IsChecked="{Binding ShowNames, Mode=TwoWay}"
+ IsVisible="{Binding IsGrid}">
+ <TextBlock Margin="5,3,0,0" Text="{locale:Locale CommonShowNames}" />
+ </CheckBox>
+ <TextBox
+ Name="SearchBox"
+ MinWidth="200"
+ Margin="5,0,5,0"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Center"
+ DockPanel.Dock="Right"
+ KeyUp="SearchBox_OnKeyUp"
+ Text="{Binding SearchText}"
+ Watermark="{locale:Locale MenuSearch}" />
+ <ui:DropDownButton
+ Width="150"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Center"
+ Content="{Binding SortName}"
+ DockPanel.Dock="Right">
+ <ui:DropDownButton.Flyout>
+ <Flyout Placement="Bottom">
+ <StackPanel
+ Margin="0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical">
+ <StackPanel>
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale CommonFavorite}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
+ Tag="Favorite" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderApplication}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
+ Tag="Title" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderDeveloper}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
+ Tag="Developer" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderTimePlayed}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
+ Tag="TotalTimePlayed" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderLastPlayed}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
+ Tag="LastPlayed" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderFileExtension}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByType, Mode=OneTime}"
+ Tag="FileType" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderFileSize}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
+ Tag="FileSize" />
+ <RadioButton
+ Checked="Sort_Checked"
+ Content="{locale:Locale GameListHeaderPath}"
+ GroupName="Sort"
+ IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
+ Tag="Path" />
+ </StackPanel>
+ <Border
+ Width="60"
+ Height="2"
+ Margin="5"
+ HorizontalAlignment="Stretch"
+ BorderBrush="White"
+ BorderThickness="0,1,0,0">
+ <Separator Height="0" HorizontalAlignment="Stretch" />
+ </Border>
+ <RadioButton
+ Checked="Order_Checked"
+ Content="{locale:Locale OrderAscending}"
+ GroupName="Order"
+ IsChecked="{Binding IsAscending, Mode=OneTime}"
+ Tag="Ascending" />
+ <RadioButton
+ Checked="Order_Checked"
+ Content="{locale:Locale OrderDescending}"
+ GroupName="Order"
+ IsChecked="{Binding !IsAscending, Mode=OneTime}"
+ Tag="Descending" />
+ </StackPanel>
+ </Flyout>
+ </ui:DropDownButton.Flyout>
+ </ui:DropDownButton>
+ <TextBlock
+ Margin="10,0"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Center"
+ DockPanel.Dock="Right"
+ Text="{locale:Locale CommonSort}" />
+ </DockPanel>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs
new file mode 100644
index 00000000..fd757893
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs
@@ -0,0 +1,54 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Ryujinx.Ava.Common;
+using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.Ava.UI.Windows;
+using System;
+
+namespace Ryujinx.Ava.UI.Views.Main
+{
+ public partial class MainViewControls : UserControl
+ {
+ public MainWindowViewModel ViewModel;
+
+ public MainViewControls()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ if (VisualRoot is MainWindow window)
+ {
+ ViewModel = window.ViewModel;
+ }
+
+ DataContext = ViewModel;
+ }
+
+ public void Sort_Checked(object sender, RoutedEventArgs args)
+ {
+ if (sender is RadioButton button)
+ {
+ ViewModel.Sort(Enum.Parse<ApplicationSort>(button.Tag.ToString()));
+ }
+ }
+
+ public void Order_Checked(object sender, RoutedEventArgs args)
+ {
+ if (sender is RadioButton button)
+ {
+ ViewModel.Sort(button.Tag.ToString() != "Descending");
+ }
+ }
+
+ private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
+ {
+ ViewModel.SearchText = SearchBox.Text;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml
new file mode 100644
index 00000000..35283353
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml
@@ -0,0 +1,81 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsAudioView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="AudioPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemAudioBackend}"
+ ToolTip.Tip="{locale:Locale AudioBackendTooltip}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding AudioBackend}"
+ Width="350"
+ HorizontalContentAlignment="Left">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendDummy}" />
+ </ComboBoxItem>
+ <ComboBoxItem IsEnabled="{Binding IsOpenAlEnabled}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendOpenAL}" />
+ </ComboBoxItem>
+ <ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSoundIO}" />
+ </ComboBoxItem>
+ <ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSDL2}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemAudioVolume}"
+ ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
+ Width="250" />
+ <ui:NumberBox Value="{Binding Volume}"
+ ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
+ Width="350"
+ SmallChange="1"
+ LargeChange="10"
+ SimpleNumberFormat="F0"
+ SpinButtonPlacementMode="Inline"
+ Minimum="0"
+ Maximum="100" />
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <Slider Value="{Binding Volume}"
+ Margin="250,0,0,0"
+ ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
+ Minimum="0"
+ Maximum="100"
+ SmallChange="5"
+ TickFrequency="5"
+ IsSnapToTickEnabled="True"
+ LargeChange="10"
+ Width="350" />
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs
new file mode 100644
index 00000000..026c7fdf
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsAudioView : UserControl
+ {
+ public SettingsAudioView()
+ {
+ InitializeComponent();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml
new file mode 100644
index 00000000..e98b963c
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml
@@ -0,0 +1,78 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsCPUView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="CpuPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical">
+ <CheckBox IsChecked="{Binding EnablePptc}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemEnablePptc}"
+ ToolTip.Tip="{locale:Locale PptcToggleTooltip}" />
+ </CheckBox>
+ </StackPanel>
+ <Separator Height="1" />
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemMemoryManagerMode}"
+ ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding MemoryMode}"
+ ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
+ HorizontalContentAlignment="Left"
+ Width="350">
+ <ComboBoxItem
+ ToolTip.Tip="{locale:Locale MemoryManagerSoftwareTooltip}">
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
+ </ComboBoxItem>
+ <ComboBoxItem
+ ToolTip.Tip="{locale:Locale MemoryManagerHostTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemMemoryManagerModeHost}" />
+ </ComboBoxItem>
+ <ComboBoxItem
+ ToolTip.Tip="{locale:Locale MemoryManagerUnsafeTooltip}">
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ <CheckBox IsChecked="{Binding UseHypervisor}"
+ IsVisible="{Binding IsHypervisorAvailable}"
+ ToolTip.Tip="{locale:Locale UseHypervisorTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemUseHypervisor}"
+ ToolTip.Tip="{locale:Locale UseHypervisorTooltip}" />
+ </CheckBox>
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl>
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs
new file mode 100644
index 00000000..5c5b6079
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsCPUView : UserControl
+ {
+ public SettingsCPUView()
+ {
+ InitializeComponent();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml
new file mode 100644
index 00000000..8e4122f3
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml
@@ -0,0 +1,296 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ Design.Width="1000"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="GraphicsPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsBackend}"
+ Width="250" />
+ <ComboBox Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
+ SelectedIndex="{Binding GraphicsBackendIndex}">
+ <ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
+ <TextBlock Text="Vulkan" />
+ </ComboBoxItem>
+ <ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
+ <TextBlock Text="OpenGL" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ <StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsPreferredGpu}"
+ Width="250" />
+ <ComboBox Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
+ SelectedIndex="{Binding PreferredGpuIndex}"
+ Items="{Binding AvailableGpus}"/>
+ </StackPanel>
+ </StackPanel>
+ <Separator Height="1" />
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
+ <StackPanel Orientation="Vertical">
+ <CheckBox IsChecked="{Binding EnableShaderCache}"
+ ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsEnableShaderCache}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableTextureRecompression}"
+ ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
+ <TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableMacroHLE}"
+ ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
+ <TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
+ </CheckBox>
+ </StackPanel>
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsResolutionScale}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding ResolutionScale}"
+ Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale2x}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale3x}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
+ </ComboBoxItem>
+ </ComboBox>
+ <ui:NumberBox
+ Margin="10,0,0,0"
+ ToolTip.Tip="{locale:Locale ResolutionScaleEntryTooltip}"
+ MinWidth="150"
+ SmallChange="0.1"
+ LargeChange="1"
+ SimpleNumberFormat="F2"
+ SpinButtonPlacementMode="Inline"
+ IsVisible="{Binding IsCustomResolutionScaleActive}"
+ Maximum="100"
+ Minimum="0.1"
+ Value="{Binding CustomResolutionScale}" />
+ </StackPanel>
+ <StackPanel
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale GraphicsAATooltip}"
+ Text="{locale:Locale GraphicsAALabel}"
+ Width="250" />
+ <ComboBox Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale GraphicsAATooltip}"
+ SelectedIndex="{Binding AntiAliasingEffect}">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="FXAA" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SmaaLow}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SmaaMedium}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SmaaHigh}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SmaaUltra}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ </StackPanel>
+ <StackPanel
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}"
+ Text="{locale:Locale GraphicsScalingFilterLabel}"
+ Width="250" />
+ <ComboBox Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}"
+ SelectedIndex="{Binding ScalingFilter}">
+ <ComboBoxItem>
+ <TextBlock Text="Bilinear" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="Nearest" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="FSR" />
+ </ComboBoxItem>
+ </ComboBox>
+ <Slider Value="{Binding ScalingFilterLevel}"
+ ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
+ MinWidth="150"
+ Margin="10,-3,0,0"
+ Height="32"
+ Padding="0,-5"
+ IsVisible="{Binding IsScalingFilterActive}"
+ TickFrequency="1"
+ IsSnapToTickEnabled="True"
+ LargeChange="10"
+ SmallChange="1"
+ VerticalAlignment="Center"
+ Minimum="0"
+ Maximum="100" />
+ <TextBlock Margin="5,0"
+ Width="40"
+ IsVisible="{Binding IsScalingFilterActive}"
+ Text="{Binding ScalingFilterLevelText}"/>
+ </StackPanel>
+ </StackPanel>
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding MaxAnisotropy}"
+ Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale AnisotropyTooltip}">
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabGraphicsAnisotropicFilteringAuto}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering2x}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering4x}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering8x}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering16x}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale AspectRatioTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsAspectRatio}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding AspectRatio}"
+ Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio4x3}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x9}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x10}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio21x9}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio32x9}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatioStretch}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ </StackPanel>
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale GraphicsBackendThreadingTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsBackendMultithreading}"
+ Width="250" />
+ <ComboBox Width="350"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale GalThreadingTooltip}"
+ SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale CommonAuto}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale CommonOff}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale CommonOn}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ </StackPanel>
+ <Separator Height="1" />
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <StackPanel Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}"
+ Text="{locale:Locale SettingsTabGraphicsShaderDumpPath}"
+ Width="250" />
+ <TextBox Text="{Binding ShaderDumpPath}"
+ Width="350"
+ ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}" />
+ </StackPanel>
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs
new file mode 100644
index 00000000..8fe08552
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsGraphicsView : UserControl
+ {
+ public SettingsGraphicsView()
+ {
+ InitializeComponent();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml
new file mode 100644
index 00000000..361125bf
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml
@@ -0,0 +1,104 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel"
+ Focusable="True">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <UserControl.Resources>
+ <helpers:KeyValueConverter x:Key="Key" />
+ </UserControl.Resources>
+ <ScrollViewer
+ Name="HotkeysPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel Margin="10" Orientation="Vertical" Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.VolumeUp, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="230" />
+ <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
+ <TextBlock
+ Text="{Binding KeyboardHotkeys.VolumeDown, Mode=TwoWay, Converter={StaticResource Key}}"
+ TextAlignment="Center" />
+ </ToggleButton>
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs
new file mode 100644
index 00000000..702f73e8
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs
@@ -0,0 +1,81 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Ryujinx.Ava.Input;
+using Ryujinx.Ava.UI.Helpers;
+using Ryujinx.Input;
+using Ryujinx.Input.Assigner;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsHotkeysView : UserControl
+ {
+ private ButtonKeyAssigner _currentAssigner;
+ private IGamepadDriver AvaloniaKeyboardDriver;
+
+ public SettingsHotkeysView()
+ {
+ InitializeComponent();
+ AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
+ }
+
+ private void MouseClick(object sender, PointerPressedEventArgs e)
+ {
+ bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
+
+ _currentAssigner?.Cancel(shouldUnbind);
+
+ PointerPressed -= MouseClick;
+ }
+
+ private void Button_Checked(object sender, RoutedEventArgs e)
+ {
+ if (sender is ToggleButton button)
+ {
+ if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
+ {
+ return;
+ }
+
+ if (_currentAssigner == null && button.IsChecked != null && (bool)button.IsChecked)
+ {
+ _currentAssigner = new ButtonKeyAssigner(button);
+
+ FocusManager.Instance?.Focus(this, NavigationMethod.Pointer);
+
+ PointerPressed += MouseClick;
+
+ var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]);
+ IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
+
+ _currentAssigner.GetInputAndAssign(assigner);
+ }
+ else
+ {
+ if (_currentAssigner != null)
+ {
+ ToggleButton oldButton = _currentAssigner.ToggledButton;
+
+ _currentAssigner.Cancel();
+ _currentAssigner = null;
+
+ button.IsChecked = false;
+ }
+ }
+ }
+ }
+
+ private void Button_Unchecked(object sender, RoutedEventArgs e)
+ {
+ _currentAssigner?.Cancel();
+ _currentAssigner = null;
+ }
+
+ public void Dispose()
+ {
+ _currentAssigner?.Cancel();
+ _currentAssigner = null;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml
new file mode 100644
index 00000000..1c774bda
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml
@@ -0,0 +1,46 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="InputPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel Margin="4" Orientation="Vertical">
+ <StackPanel Orientation="Horizontal">
+ <CheckBox Margin="5,0"
+ ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
+ IsChecked="{Binding EnableDockedMode}">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
+ </CheckBox>
+ <CheckBox Margin="5,0"
+ ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
+ IsChecked="{Binding EnableKeyboard}">
+ <TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
+ </CheckBox>
+ <CheckBox Margin="5,0"
+ ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
+ IsChecked="{Binding EnableMouse}">
+ <TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
+ </CheckBox>
+ </StackPanel>
+ <window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs
new file mode 100644
index 00000000..0c2105ec
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Controls;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsInputView : UserControl
+ {
+ public SettingsInputView()
+ {
+ InitializeComponent();
+ }
+
+ public void Dispose()
+ {
+ ControllerSettings.Dispose();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml
new file mode 100644
index 00000000..948e7181
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml
@@ -0,0 +1,121 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsLoggingView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="LoggingPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Vertical">
+ <CheckBox IsChecked="{Binding EnableFileLog}"
+ ToolTip.Tip="{locale:Locale FileLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableLoggingToFile}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableStub}"
+ ToolTip.Tip="{locale:Locale StubLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableStubLogs}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableInfo}"
+ ToolTip.Tip="{locale:Locale InfoLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableInfoLogs}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableWarn}"
+ ToolTip.Tip="{locale:Locale WarnLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableWarningLogs}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableError}"
+ ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableGuest}"
+ ToolTip.Tip="{locale:Locale GuestLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
+ </CheckBox>
+ </StackPanel>
+ <Separator Height="1" />
+ <StackPanel Orientation="Vertical" Spacing="2">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
+ <TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabLoggingDeveloperOptionsNote}" />
+ </StackPanel>
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <StackPanel Orientation="Vertical">
+ <CheckBox IsChecked="{Binding EnableTrace}"
+ ToolTip.Tip="{locale:Locale TraceLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableFsAccessLog}"
+ ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableDebug}"
+ ToolTip.Tip="{locale:Locale DebugLogTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
+ </CheckBox>
+ <StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"
+ Text="{locale:Locale SettingsTabLoggingFsGlobalAccessLogMode}"
+ Width="285" />
+ <ui:NumberBox
+ Maximum="3"
+ Minimum="0"
+ Width="150"
+ SpinButtonPlacementMode="Inline"
+ SmallChange="1"
+ LargeChange="1"
+ Value="{Binding FsGlobalAccessLogMode}" />
+ </StackPanel>
+ <StackPanel Margin="0,10,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevel}"
+ ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
+ Width="285" />
+ <ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
+ Width="150"
+ HorizontalContentAlignment="Left"
+ ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelError}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelPerformance}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelAll}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ </StackPanel>
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs
new file mode 100644
index 00000000..2ec476ac
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsLoggingView : UserControl
+ {
+ public SettingsLoggingView()
+ {
+ InitializeComponent();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
new file mode 100644
index 00000000..ab8a7f6d
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
@@ -0,0 +1,46 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="NetworkPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
+ <CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
+ ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
+ </CheckBox>
+ <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabNetworkInterface}"
+ ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
+ Width="200" />
+ <ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}"
+ ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
+ HorizontalContentAlignment="Left"
+ Items="{Binding NetworkInterfaceList}"
+ Width="250" />
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl>
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs
new file mode 100644
index 00000000..d7407b9d
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsNetworkView : UserControl
+ {
+ public SettingsNetworkView()
+ {
+ InitializeComponent();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml
new file mode 100644
index 00000000..1d4f040f
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml
@@ -0,0 +1,195 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsSystemView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="SystemPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Vertical">
+ <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemSystemRegion}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding Region}"
+ ToolTip.Tip="{locale:Locale RegionTooltip}"
+ HorizontalContentAlignment="Left"
+ Width="350">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionUSA}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionEurope}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionAustralia}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionChina}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionKorea}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionTaiwan}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemSystemLanguage}"
+ ToolTip.Tip="{locale:Locale LanguageTooltip}"
+ Width="250" />
+ <ComboBox SelectedIndex="{Binding Language}"
+ ToolTip.Tip="{locale:Locale LanguageTooltip}"
+ HorizontalContentAlignment="Left"
+ Width="350">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageGerman}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageItalian}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSpanish}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageChinese}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageKorean}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageDutch}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguagePortuguese}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageRussian}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock
+ Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </StackPanel>
+ <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
+ ToolTip.Tip="{locale:Locale TimezoneTooltip}"
+ Width="250" />
+ <AutoCompleteBox
+ Name="TimeZoneBox"
+ Width="350"
+ MaxDropDownHeight="500"
+ FilterMode="Contains"
+ Items="{Binding TimeZones}"
+ SelectionChanged="TimeZoneBox_OnSelectionChanged"
+ Text="{Binding Path=TimeZone, Mode=OneWay}"
+ TextChanged="TimeZoneBox_OnTextChanged"
+ ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
+ </StackPanel>
+ <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+ <TextBlock VerticalAlignment="Center"
+ Text="{locale:Locale SettingsTabSystemSystemTime}"
+ ToolTip.Tip="{locale:Locale TimeTooltip}"
+ Width="250"/>
+ <DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
+ ToolTip.Tip="{locale:Locale TimeTooltip}"
+ Width="350" />
+ </StackPanel>
+ <StackPanel Margin="250,0,0,10" Orientation="Horizontal">
+ <TimePicker
+ VerticalAlignment="Center"
+ ClockIdentifier="24HourClock"
+ SelectedTime="{Binding TimeOffset}"
+ Width="350"
+ ToolTip.Tip="{locale:Locale TimeTooltip}" />
+ </StackPanel>
+ <CheckBox IsChecked="{Binding EnableVsync}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
+ ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
+ ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
+ </CheckBox>
+ </StackPanel>
+ <Separator Height="1" />
+ <StackPanel Orientation="Vertical" Spacing="2">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
+ <TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabSystemHacksNote}" />
+ </StackPanel>
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical">
+ <CheckBox IsChecked="{Binding ExpandDramSize}"
+ ToolTip.Tip="{locale:Locale DRamTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding IgnoreMissingServices}"
+ ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
+ </CheckBox>
+ </StackPanel>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs
new file mode 100644
index 00000000..d2ea59de
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs
@@ -0,0 +1,52 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+using Ryujinx.Ava.UI.ViewModels;
+using System;
+using System.Linq;
+using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsSystemView : UserControl
+ {
+ public SettingsViewModel ViewModel;
+
+ public SettingsSystemView()
+ {
+ InitializeComponent();
+
+ FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
+ MultiBinding tzMultiBinding = new() { Converter = converter };
+
+ tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
+ tzMultiBinding.Bindings.Add(new Binding("Location"));
+ tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
+
+ TimeZoneBox.ValueMemberBinding = tzMultiBinding;
+ }
+
+ private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (e.AddedItems != null && e.AddedItems.Count > 0)
+ {
+ if (e.AddedItems[0] is TimeZone timeZone)
+ {
+ e.Handled = true;
+
+ ViewModel.ValidateAndSetTimeZone(timeZone.Location);
+ }
+ }
+ }
+
+ private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
+ {
+ if (sender is AutoCompleteBox box && box.SelectedItem is TimeZone timeZone)
+ {
+ {
+ ViewModel.ValidateAndSetTimeZone(timeZone.Location);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml
new file mode 100644
index 00000000..61b6c433
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml
@@ -0,0 +1,156 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ x:CompileBindings="True"
+ x:DataType="viewModels:SettingsViewModel">
+ <Design.DataContext>
+ <viewModels:SettingsViewModel />
+ </Design.DataContext>
+ <ScrollViewer
+ Name="UiPage"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ HorizontalScrollBarVisibility="Disabled"
+ VerticalScrollBarVisibility="Auto">
+ <Border Classes="settings">
+ <StackPanel
+ Margin="10"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
+ <StackPanel Margin="10,0,0,0" Orientation="Vertical">
+ <CheckBox IsChecked="{Binding EnableDiscordIntegration}">
+ <TextBlock VerticalAlignment="Center"
+ ToolTip.Tip="{locale:Locale ToggleDiscordTooltip}"
+ Text="{locale:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
+ <TextBlock Text="{locale:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding ShowConfirmExit}">
+ <TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
+ </CheckBox>
+ <CheckBox IsChecked="{Binding HideCursorOnIdle}">
+ <TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
+ </CheckBox>
+ </StackPanel>
+ <Separator Height="1" />
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
+ <StackPanel
+ Margin="10,0,0,0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <ListBox
+ Name="GameList"
+ MinHeight="230"
+ Items="{Binding GameDirectories}">
+ <ListBox.Styles>
+ <Style Selector="ListBoxItem">
+ <Setter Property="Padding" Value="10" />
+ <Setter Property="Background" Value="{DynamicResource ListBoxBackground}" />
+ </Style>
+ </ListBox.Styles>
+ </ListBox>
+ <Grid HorizontalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <TextBox
+ Name="PathBox"
+ Margin="0"
+ ToolTip.Tip="{locale:Locale AddGameDirBoxTooltip}"
+ VerticalAlignment="Stretch" />
+ <Button
+ Name="AddButton"
+ Grid.Column="1"
+ MinWidth="90"
+ Margin="10,0,0,0"
+ ToolTip.Tip="{locale:Locale AddGameDirTooltip}"
+ Click="AddButton_OnClick">
+ <TextBlock HorizontalAlignment="Center"
+ Text="{locale:Locale SettingsTabGeneralAdd}" />
+ </Button>
+ <Button
+ Name="RemoveButton"
+ Grid.Column="2"
+ MinWidth="90"
+ Margin="10,0,0,0"
+ ToolTip.Tip="{locale:Locale RemoveGameDirTooltip}"
+ Click="RemoveButton_OnClick">
+ <TextBlock HorizontalAlignment="Center"
+ Text="{locale:Locale SettingsTabGeneralRemove}" />
+ </Button>
+ </Grid>
+ </StackPanel>
+ <Separator Height="1" />
+ <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
+ <Grid Margin="10,0,0,0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition />
+ <RowDefinition />
+ <RowDefinition />
+ </Grid.RowDefinitions>
+ <CheckBox
+ IsChecked="{Binding EnableCustomTheme}"
+ ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}">
+ <TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" />
+ </CheckBox>
+ <TextBlock
+ Grid.Column="0"
+ Grid.Row="1"
+ VerticalAlignment="Center"
+ Margin="0,10,0,0"
+ Text="{locale:Locale SettingsTabGeneralThemeCustomTheme}"
+ ToolTip.Tip="{locale:Locale CustomThemePathTooltip}" />
+ <TextBox
+ Grid.Row="1"
+ Grid.Column="1"
+ Margin="0,10,0,0"
+ Text="{Binding CustomThemePath}" />
+ <Button
+ Grid.Row="1"
+ Grid.Column="2"
+ Margin="10,10,0,0"
+ Click="BrowseTheme"
+ ToolTip.Tip="{locale:Locale CustomThemeBrowseTooltip}"
+ Content="{locale:Locale ButtonBrowse}" />
+ <TextBlock
+ Grid.Column="0"
+ Grid.Row="2"
+ VerticalAlignment="Center"
+ Margin="0,10,0,0"
+ Text="{locale:Locale SettingsTabGeneralThemeBaseStyle}" />
+ <ComboBox
+ Grid.Column="1"
+ Grid.Row="2"
+ VerticalAlignment="Center"
+ Margin="0,10,0,0"
+ MinWidth="100"
+ SelectedIndex="{Binding BaseStyleIndex}">
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleLight}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleDark}" />
+ </ComboBoxItem>
+ </ComboBox>
+ </Grid>
+ </StackPanel>
+ </Border>
+ </ScrollViewer>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs
new file mode 100644
index 00000000..8e6da7c5
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs
@@ -0,0 +1,82 @@
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Interactivity;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.ViewModels;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.Ava.UI.Views.Settings
+{
+ public partial class SettingsUIView : UserControl
+ {
+ public SettingsViewModel ViewModel;
+
+ public SettingsUIView()
+ {
+ InitializeComponent();
+ }
+
+ private async void AddButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ string path = PathBox.Text;
+
+ if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
+ {
+ ViewModel.GameDirectories.Add(path);
+ ViewModel.DirectoryChanged = true;
+ }
+ else
+ {
+ if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow);
+
+ if (!string.IsNullOrWhiteSpace(path))
+ {
+ ViewModel.GameDirectories.Add(path);
+ ViewModel.DirectoryChanged = true;
+ }
+ }
+ }
+ }
+
+ private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ int oldIndex = GameList.SelectedIndex;
+
+ foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>()))
+ {
+ ViewModel.GameDirectories.Remove(path);
+ ViewModel.DirectoryChanged = true;
+ }
+
+ if (GameList.ItemCount > 0)
+ {
+ GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
+ }
+ }
+
+ public async void BrowseTheme(object sender, RoutedEventArgs e)
+ {
+ var dialog = new OpenFileDialog()
+ {
+ Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
+ AllowMultiple = false
+ };
+
+ dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
+
+ if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ var file = await dialog.ShowAsync(desktop.MainWindow);
+
+ if (file != null && file.Length > 0)
+ {
+ ViewModel.CustomThemePath = file[0];
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml
new file mode 100644
index 00000000..7e55f25e
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml
@@ -0,0 +1,123 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.User.UserEditorView"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+ xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
+ Margin="0"
+ MinWidth="500"
+ Padding="0"
+ mc:Ignorable="d"
+ Focusable="True"
+ x:CompileBindings="True"
+ x:DataType="models:TempProfile">
+ <UserControl.Resources>
+ <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
+ </UserControl.Resources>
+ <Grid Margin="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <StackPanel
+ Grid.Row="0"
+ Grid.Column="0"
+ HorizontalAlignment="Stretch"
+ Orientation="Vertical"
+ Spacing="10">
+ <TextBlock Text="{locale:Locale UserProfilesName}" />
+ <TextBox
+ Name="NameBox"
+ Width="300"
+ HorizontalAlignment="Stretch"
+ MaxLength="{Binding MaxProfileNameLength}"
+ Watermark="{locale:Locale ProfileNameSelectionWatermark}"
+ Text="{Binding Name}" />
+ <TextBlock Name="IdText" Text="{locale:Locale UserProfilesUserId}" />
+ <TextBox
+ Name="IdLabel"
+ Width="300"
+ HorizontalAlignment="Stretch"
+ IsReadOnly="True"
+ Text="{Binding UserIdString}" />
+ </StackPanel>
+ <StackPanel
+ Grid.Row="0"
+ Grid.Column="1"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Stretch"
+ Orientation="Vertical">
+ <Border
+ BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
+ BorderThickness="1">
+ <Panel>
+ <ui:SymbolIcon
+ FontSize="60"
+ Width="96"
+ Height="96"
+ Margin="0"
+ Foreground="{DynamicResource AppListHoverBackgroundColor}"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Top"
+ Symbol="Camera" />
+ <Image
+ Name="ProfileImage"
+ Width="96"
+ Height="96"
+ Margin="0"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Top"
+ Source="{Binding Image, Converter={StaticResource ByteImage}}" />
+ </Panel>
+ </Border>
+ </StackPanel>
+ <StackPanel
+ Grid.Row="1"
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ HorizontalAlignment="Left"
+ Orientation="Horizontal"
+ Margin="0 24 0 0"
+ Spacing="10">
+ <Button
+ Width="50"
+ MinWidth="50"
+ Click="BackButton_Click">
+ <ui:SymbolIcon Symbol="Back" />
+ </Button>
+ </StackPanel>
+ <StackPanel
+ Grid.Row="1"
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ HorizontalAlignment="Right"
+ Orientation="Horizontal"
+ Margin="0 24 0 0"
+ Spacing="10">
+ <Button
+ Name="DeleteButton"
+ Click="DeleteButton_Click"
+ Content="{locale:Locale UserProfilesDelete}" />
+ <Button
+ Name="ChangePictureButton"
+ Click="ChangePictureButton_Click"
+ Content="{locale:Locale UserProfilesChangeProfileImage}" />
+ <Button
+ Name="AddPictureButton"
+ Click="ChangePictureButton_Click"
+ Content="{locale:Locale UserProfilesSetProfileImage}" />
+ <Button
+ Name="SaveButton"
+ Click="SaveButton_Click"
+ Content="{locale:Locale Save}" />
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs
new file mode 100644
index 00000000..81938d23
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml.cs
@@ -0,0 +1,165 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Interactivity;
+using FluentAvalonia.UI.Controls;
+using FluentAvalonia.UI.Navigation;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Controls;
+using Ryujinx.Ava.UI.Helpers;
+using Ryujinx.Ava.UI.Models;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using System;
+using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
+
+namespace Ryujinx.Ava.UI.Views.User
+{
+ public partial class UserEditorView : UserControl
+ {
+ private NavigationDialogHost _parent;
+ private UserProfile _profile;
+ private bool _isNewUser;
+
+ public TempProfile TempProfile { get; set; }
+ public uint MaxProfileNameLength => 0x20;
+ public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
+
+ public UserEditorView()
+ {
+ InitializeComponent();
+ AddHandler(Frame.NavigatedToEvent, (s, e) =>
+ {
+ NavigatedTo(e);
+ }, RoutingStrategies.Direct);
+ }
+
+ private void NavigatedTo(NavigationEventArgs arg)
+ {
+ if (Program.PreviewerDetached)
+ {
+ switch (arg.NavigationMode)
+ {
+ case NavigationMode.New:
+ var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
+ _isNewUser = args.isNewUser;
+ _profile = args.profile;
+ TempProfile = new TempProfile(_profile);
+
+ _parent = args.parent;
+ break;
+ }
+
+ ((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;
+ IdText.IsVisible = _profile != null;
+ if (!_isNewUser && IsDeletable)
+ {
+ DeleteButton.IsVisible = true;
+ }
+ else
+ {
+ DeleteButton.IsVisible = false;
+ }
+ }
+ }
+
+ private async void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (_isNewUser)
+ {
+ if (TempProfile.Name != String.Empty || TempProfile.Image != null)
+ {
+ if (await ContentDialogHelper.CreateChoiceDialog(
+ LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
+ LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesMessage],
+ LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesSubMessage]))
+ {
+ _parent?.GoBack();
+ }
+ }
+ else
+ {
+ _parent?.GoBack();
+ }
+ }
+ else
+ {
+ if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image)
+ {
+ if (await ContentDialogHelper.CreateChoiceDialog(
+ LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
+ LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesMessage],
+ LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesSubMessage]))
+ {
+ _parent?.GoBack();
+ }
+ }
+ else
+ {
+ _parent?.GoBack();
+ }
+ }
+ }
+
+ private void DeleteButton_Click(object sender, RoutedEventArgs e)
+ {
+ _parent.DeleteUser(_profile);
+ }
+
+ private void SaveButton_Click(object sender, RoutedEventArgs e)
+ {
+ DataValidationErrors.ClearErrors(NameBox);
+
+ if (string.IsNullOrWhiteSpace(TempProfile.Name))
+ {
+ DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError]));
+
+ return;
+ }
+
+ if (TempProfile.Image == null)
+ {
+ _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
+
+ return;
+ }
+
+ if (_profile != null && !_isNewUser)
+ {
+ _profile.Name = TempProfile.Name;
+ _profile.Image = TempProfile.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);
+ }
+ else
+ {
+ return;
+ }
+
+ _parent?.GoBack();
+ }
+
+ public void SelectProfileImage()
+ {
+ _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
+ }
+
+ private void ChangePictureButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (_profile != null || _isNewUser)
+ {
+ SelectProfileImage();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml
new file mode 100644
index 00000000..d46fcefc
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml
@@ -0,0 +1,114 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ Width="528"
+ d:DesignWidth="578"
+ d:DesignHeight="350"
+ x:Class="Ryujinx.Ava.UI.Views.User.UserFirmwareAvatarSelectorView"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+ x:CompileBindings="True"
+ x:DataType="viewModels:UserFirmwareAvatarSelectorViewModel"
+ Focusable="True">
+ <Design.DataContext>
+ <viewModels:UserFirmwareAvatarSelectorViewModel />
+ </Design.DataContext>
+ <UserControl.Resources>
+ <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
+ </UserControl.Resources>
+ <Grid
+ Margin="0"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <ListBox
+ Grid.Row="1"
+ BorderThickness="0"
+ SelectedIndex="{Binding SelectedIndex}"
+ Height="400"
+ Items="{Binding Images}"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Center">
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <WrapPanel
+ Orientation="Horizontal"
+ Margin="0"
+ HorizontalAlignment="Center" />
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.Styles>
+ <Style Selector="ListBoxItem">
+ <Setter Property="CornerRadius" Value="4" />
+ <Setter Property="Width" Value="85" />
+ <Setter Property="MaxWidth" Value="85" />
+ <Setter Property="MinWidth" Value="85" />
+ </Style>
+ <Style Selector="ListBoxItem /template/ Border#SelectionIndicator">
+ <Setter Property="MinHeight" Value="70" />
+ </Style>
+ </ListBox.Styles>
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <Panel
+ Background="{Binding BackgroundColor}"
+ Margin="5">
+ <Image Source="{Binding Data, Converter={StaticResource ByteImage}}" />
+ </Panel>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+ <StackPanel
+ Grid.Row="3"
+ Orientation="Horizontal"
+ Spacing="10"
+ Margin="0 24 0 0"
+ HorizontalAlignment="Left">
+ <Button
+ Width="50"
+ MinWidth="50"
+ Height="35"
+ Click="GoBack">
+ <ui:SymbolIcon Symbol="Back" />
+ </Button>
+ </StackPanel>
+ <StackPanel
+ Grid.Row="3"
+ Orientation="Horizontal"
+ Spacing="10"
+ Margin="0 24 0 0"
+ HorizontalAlignment="Right">
+ <ui:ColorPickerButton
+ FlyoutPlacement="Top"
+ IsMoreButtonVisible="False"
+ UseColorPalette="False"
+ UseColorTriangle="False"
+ UseColorWheel="False"
+ ShowAcceptDismissButtons="False"
+ IsAlphaEnabled="False"
+ Color="{Binding BackgroundColor, Mode=TwoWay}"
+ Name="ColorButton">
+ <ui:ColorPickerButton.Styles>
+ <Style Selector="Grid#Root > DockPanel > Grid">
+ <Setter Property="IsVisible" Value="False" />
+ </Style>
+ </ui:ColorPickerButton.Styles>
+ </ui:ColorPickerButton>
+ <Button
+ Content="{locale:Locale AvatarChoose}"
+ Height="35"
+ Name="ChooseButton"
+ Click="ChooseButton_OnClick" />
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs
new file mode 100644
index 00000000..7c9191ab
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs
@@ -0,0 +1,88 @@
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using FluentAvalonia.UI.Controls;
+using FluentAvalonia.UI.Navigation;
+using Ryujinx.Ava.UI.Controls;
+using Ryujinx.Ava.UI.Models;
+using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.HLE.FileSystem;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using System.IO;
+
+namespace Ryujinx.Ava.UI.Views.User
+{
+ public partial class UserFirmwareAvatarSelectorView : UserControl
+ {
+ private NavigationDialogHost _parent;
+ private TempProfile _profile;
+
+ public UserFirmwareAvatarSelectorView(ContentManager contentManager)
+ {
+ ContentManager = contentManager;
+
+ DataContext = ViewModel;
+
+ InitializeComponent();
+ }
+
+ public UserFirmwareAvatarSelectorView()
+ {
+ InitializeComponent();
+
+ AddHandler(Frame.NavigatedToEvent, (s, e) =>
+ {
+ NavigatedTo(e);
+ }, RoutingStrategies.Direct);
+ }
+
+ private void NavigatedTo(NavigationEventArgs arg)
+ {
+ if (Program.PreviewerDetached)
+ {
+ if (arg.NavigationMode == NavigationMode.New)
+ {
+ (_parent, _profile) = ((NavigationDialogHost, TempProfile))arg.Parameter;
+ ContentManager = _parent.ContentManager;
+ if (Program.PreviewerDetached)
+ {
+ ViewModel = new UserFirmwareAvatarSelectorViewModel();
+ }
+
+ DataContext = ViewModel;
+ }
+ }
+ }
+
+ public ContentManager ContentManager { get; private set; }
+
+ internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; }
+
+ private void GoBack(object sender, RoutedEventArgs e)
+ {
+ _parent.GoBack();
+ }
+
+ private void ChooseButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (ViewModel.SelectedImage != null)
+ {
+ MemoryStream streamJpg = new();
+ SixLabors.ImageSharp.Image avatarImage = SixLabors.ImageSharp.Image.Load(ViewModel.SelectedImage, new PngDecoder());
+
+ avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
+ ViewModel.BackgroundColor.R,
+ ViewModel.BackgroundColor.G,
+ ViewModel.BackgroundColor.B,
+ ViewModel.BackgroundColor.A)));
+ avatarImage.SaveAsJpeg(streamJpg);
+
+ _profile.Image = streamJpg.ToArray();
+
+ _parent.GoBack();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml
new file mode 100644
index 00000000..b9f51fdc
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml
@@ -0,0 +1,63 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:viewModles="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ Focusable="True"
+ mc:Ignorable="d"
+ x:Class="Ryujinx.Ava.UI.Views.User.UserProfileImageSelectorView"
+ x:CompileBindings="True"
+ x:DataType="viewModles:UserProfileImageSelectorViewModel"
+ Width="500"
+ d:DesignWidth="500">
+ <Design.DataContext>
+ <viewModles:UserProfileImageSelectorViewModel />
+ </Design.DataContext>
+ <Grid
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Center">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="70" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <TextBlock
+ Grid.Row="0"
+ TextWrapping="Wrap"
+ HorizontalAlignment="Left"
+ TextAlignment="Left"
+ Text="{locale:Locale ProfileImageSelectionNote}" />
+ <StackPanel
+ Grid.Row="2"
+ Spacing="10"
+ HorizontalAlignment="Left"
+ Orientation="Horizontal">
+ <Button
+ Width="50"
+ MinWidth="50"
+ Click="GoBack">
+ <ui:SymbolIcon Symbol="Back" />
+ </Button>
+ </StackPanel>
+ <StackPanel
+ Grid.Row="2"
+ Spacing="10"
+ HorizontalAlignment="Right"
+ Orientation="Horizontal">
+ <Button
+ Name="Import"
+ Click="Import_OnClick">
+ <TextBlock Text="{locale:Locale ProfileImageSelectionImportImage}" />
+ </Button>
+ <Button
+ Name="SelectFirmwareImage"
+ IsEnabled="{Binding FirmwareFound}"
+ Click="SelectFirmwareImage_OnClick">
+ <TextBlock Text="{locale:Locale ProfileImageSelectionSelectAvatar}" />
+ </Button>
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs
new file mode 100644
index 00000000..18f76f80
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs
@@ -0,0 +1,124 @@
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.VisualTree;
+using FluentAvalonia.UI.Controls;
+using FluentAvalonia.UI.Navigation;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Controls;
+using Ryujinx.Ava.UI.Models;
+using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.HLE.FileSystem;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Processing;
+using System.IO;
+using Image = SixLabors.ImageSharp.Image;
+
+namespace Ryujinx.Ava.UI.Views.User
+{
+ public partial class UserProfileImageSelectorView : UserControl
+ {
+ private ContentManager _contentManager;
+ private NavigationDialogHost _parent;
+ private TempProfile _profile;
+
+ internal UserProfileImageSelectorViewModel ViewModel { get; private set; }
+
+ public UserProfileImageSelectorView()
+ {
+ InitializeComponent();
+ AddHandler(Frame.NavigatedToEvent, (s, e) =>
+ {
+ NavigatedTo(e);
+ }, RoutingStrategies.Direct);
+ }
+
+ private void NavigatedTo(NavigationEventArgs arg)
+ {
+ if (Program.PreviewerDetached)
+ {
+ switch (arg.NavigationMode)
+ {
+ case NavigationMode.New:
+ (_parent, _profile) = ((NavigationDialogHost, TempProfile))arg.Parameter;
+ _contentManager = _parent.ContentManager;
+
+ ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - {LocaleManager.Instance[LocaleKeys.ProfileImageSelectionHeader]}";
+
+ if (Program.PreviewerDetached)
+ {
+ DataContext = ViewModel = new UserProfileImageSelectorViewModel();
+ ViewModel.FirmwareFound = _contentManager.GetCurrentFirmwareVersion() != null;
+ }
+
+ break;
+ case NavigationMode.Back:
+ if (_profile.Image != null)
+ {
+ _parent.GoBack();
+ }
+ break;
+ }
+ }
+ }
+
+ private async void Import_OnClick(object sender, RoutedEventArgs e)
+ {
+ OpenFileDialog dialog = new();
+ dialog.Filters.Add(new FileDialogFilter
+ {
+ Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
+ Extensions = { "jpg", "jpeg", "png", "bmp" }
+ });
+ dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } });
+ dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } });
+ dialog.Filters.Add(new FileDialogFilter { Name = "BMP", Extensions = { "bmp" } });
+
+ dialog.AllowMultiple = false;
+
+ string[] image = await dialog.ShowAsync(((TopLevel)_parent.GetVisualRoot()) as Window);
+
+ if (image != null)
+ {
+ if (image.Length > 0)
+ {
+ string imageFile = image[0];
+
+ _profile.Image = ProcessProfileImage(File.ReadAllBytes(imageFile));
+
+ if (_profile.Image != null)
+ {
+ _parent.GoBack();
+ }
+ }
+ }
+ }
+
+ private void GoBack(object sender, RoutedEventArgs e)
+ {
+ _parent.GoBack();
+ }
+
+ private void SelectFirmwareImage_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (ViewModel.FirmwareFound)
+ {
+ _parent.Navigate(typeof(UserFirmwareAvatarSelectorView), (_parent, _profile));
+ }
+ }
+
+ private static byte[] ProcessProfileImage(byte[] buffer)
+ {
+ using (Image image = Image.Load(buffer))
+ {
+ image.Mutate(x => x.Resize(256, 256));
+
+ using (MemoryStream streamJpg = new())
+ {
+ image.SaveAsJpeg(streamJpg);
+
+ return streamJpg.ToArray();
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml
new file mode 100644
index 00000000..62b5e184
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml
@@ -0,0 +1,83 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ d:DesignWidth="550"
+ d:DesignHeight="450"
+ Width="500"
+ Height="400"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ x:Class="Ryujinx.Ava.UI.Views.User.UserRecovererView"
+ x:CompileBindings="True"
+ x:DataType="viewModels:UserProfileViewModel"
+ Focusable="True">
+ <Design.DataContext>
+ <viewModels:UserProfileViewModel />
+ </Design.DataContext>
+ <Grid HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch">
+ <Grid.RowDefinitions>
+ <RowDefinition/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Border
+ CornerRadius="5"
+ BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
+ BorderThickness="1"
+ Grid.Row="0">
+ <Panel>
+ <ListBox
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ Items="{Binding LostProfiles}">
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <Border
+ Margin="2"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ ClipToBounds="True"
+ CornerRadius="5">
+ <Grid Margin="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <TextBlock
+ HorizontalAlignment="Stretch"
+ Text="{Binding UserId}"
+ TextAlignment="Left"
+ TextWrapping="Wrap" />
+ <Button Grid.Column="1"
+ HorizontalAlignment="Right"
+ Click="Recover"
+ CommandParameter="{Binding}"
+ Content="{locale:Locale Recover}"/>
+ </Grid>
+ </Border>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+ <TextBlock
+ IsVisible="{Binding IsEmpty}"
+ TextAlignment="Center"
+ Text="{locale:Locale UserProfilesRecoverEmptyList}"/>
+ </Panel>
+ </Border>
+ <StackPanel
+ Grid.Row="1"
+ Margin="0 24 0 0"
+ Orientation="Horizontal">
+ <Button
+ Width="50"
+ MinWidth="50"
+ Click="GoBack">
+ <ui:SymbolIcon Symbol="Back"/>
+ </Button>
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs
new file mode 100644
index 00000000..0c53e53d
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs
@@ -0,0 +1,51 @@
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using FluentAvalonia.UI.Controls;
+using FluentAvalonia.UI.Navigation;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Controls;
+
+namespace Ryujinx.Ava.UI.Views.User
+{
+ public partial class UserRecovererView : UserControl
+ {
+ private NavigationDialogHost _parent;
+
+ public UserRecovererView()
+ {
+ InitializeComponent();
+ AddHandler(Frame.NavigatedToEvent, (s, e) =>
+ {
+ NavigatedTo(e);
+ }, RoutingStrategies.Direct);
+ }
+
+ private void NavigatedTo(NavigationEventArgs arg)
+ {
+ if (Program.PreviewerDetached)
+ {
+ switch (arg.NavigationMode)
+ {
+ case NavigationMode.New:
+ var parent = (NavigationDialogHost)arg.Parameter;
+
+ _parent = parent;
+
+ ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - {LocaleManager.Instance[LocaleKeys.UserProfilesRecoverHeading]}";
+
+ break;
+ }
+ }
+ }
+
+ private void GoBack(object sender, RoutedEventArgs e)
+ {
+ _parent?.GoBack();
+ }
+
+ private void Recover(object sender, RoutedEventArgs e)
+ {
+ _parent?.RecoverLostAccounts();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml
new file mode 100644
index 00000000..ec931dd9
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml
@@ -0,0 +1,215 @@
+<UserControl
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+ xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ mc:Ignorable="d"
+ d:DesignWidth="600"
+ d:DesignHeight="500"
+ Height="450"
+ Width="550"
+ x:Class="Ryujinx.Ava.UI.Views.User.UserSaveManagerView"
+ x:CompileBindings="True"
+ x:DataType="viewModels:UserSaveManagerViewModel"
+ Focusable="True">
+ <Design.DataContext>
+ <viewModels:UserSaveManagerViewModel />
+ </Design.DataContext>
+ <UserControl.Resources>
+ <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
+ </UserControl.Resources>
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <Grid
+ Grid.Row="0"
+ HorizontalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition />
+ </Grid.ColumnDefinitions>
+ <StackPanel
+ Spacing="10"
+ Orientation="Horizontal"
+ HorizontalAlignment="Left"
+ VerticalAlignment="Center">
+ <Label Content="{locale:Locale CommonSort}" VerticalAlignment="Center" />
+ <ComboBox SelectedIndex="{Binding SortIndex}" Width="100">
+ <ComboBoxItem>
+ <Label
+ VerticalAlignment="Center"
+ HorizontalContentAlignment="Left"
+ Content="{locale:Locale Name}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <Label
+ VerticalAlignment="Center"
+ HorizontalContentAlignment="Left"
+ Content="{locale:Locale Size}" />
+ </ComboBoxItem>
+ <ComboBox.Styles>
+ <Style Selector="ContentControl#ContentPresenter">
+ <Setter Property="HorizontalAlignment" Value="Left" />
+ </Style>
+ </ComboBox.Styles>
+ </ComboBox>
+ <ComboBox SelectedIndex="{Binding OrderIndex}" Width="150">
+ <ComboBoxItem>
+ <Label
+ VerticalAlignment="Center"
+ HorizontalContentAlignment="Left"
+ Content="{locale:Locale OrderAscending}" />
+ </ComboBoxItem>
+ <ComboBoxItem>
+ <Label
+ VerticalAlignment="Center"
+ HorizontalContentAlignment="Left"
+ Content="{locale:Locale OrderDescending}" />
+ </ComboBoxItem>
+ <ComboBox.Styles>
+ <Style Selector="ContentControl#ContentPresenter">
+ <Setter Property="HorizontalAlignment" Value="Left" />
+ </Style>
+ </ComboBox.Styles>
+ </ComboBox>
+ </StackPanel>
+ <Grid
+ Grid.Column="1"
+ HorizontalAlignment="Stretch"
+ Margin="10,0, 0, 0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition/>
+ </Grid.ColumnDefinitions>
+ <Label Content="{locale:Locale Search}" VerticalAlignment="Center" />
+ <TextBox
+ Margin="5,0,0,0"
+ Grid.Column="1"
+ HorizontalAlignment="Stretch"
+ Text="{Binding Search}" />
+ </Grid>
+ </Grid>
+ <Border
+ Grid.Row="1"
+ Margin="0,5"
+ BorderThickness="1"
+ BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
+ CornerRadius="5"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch">
+ <ListBox
+ Name="SaveList"
+ VirtualizationMode="None"
+ Items="{Binding Views}"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch">
+ <ListBox.Styles>
+ <Style Selector="ListBoxItem">
+ <Setter Property="Padding" Value="10" />
+ <Setter Property="Margin" Value="5" />
+ <Setter Property="CornerRadius" Value="4" />
+ </Style>
+ <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
+ <Setter Property="IsVisible" Value="False" />
+ </Style>
+ </ListBox.Styles>
+ <ListBox.ItemTemplate>
+ <DataTemplate x:DataType="models:SaveModel">
+ <Grid HorizontalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <StackPanel
+ Grid.Column="0"
+ Orientation="Horizontal"
+ Spacing="5">
+ <Border
+ Height="42"
+ Width="42"
+ Padding="10"
+ BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
+ BorderThickness="1"
+ IsVisible="{Binding !InGameList}">
+ <ui:SymbolIcon
+ Symbol="Help"
+ FontSize="30"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center" />
+ </Border>
+ <Image
+ IsVisible="{Binding InGameList}"
+ Width="42"
+ Height="42"
+ Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
+ <TextBlock
+ MaxLines="3"
+ Width="320"
+ Margin="5"
+ TextWrapping="Wrap"
+ Text="{Binding Title}"
+ VerticalAlignment="Center" />
+ </StackPanel>
+ <StackPanel
+ Grid.Column="1"
+ Spacing="10"
+ HorizontalAlignment="Right"
+ Orientation="Horizontal">
+ <Label
+ Content="{Binding SizeString}"
+ IsVisible="{Binding SizeAvailable}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Right" />
+ <Button
+ VerticalAlignment="Center"
+ HorizontalAlignment="Right"
+ Padding="10"
+ MinWidth="0"
+ MinHeight="0"
+ Name="OpenLocation"
+ Click="OpenLocation">
+ <ui:SymbolIcon
+ Symbol="OpenFolder"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center" />
+ </Button>
+ <Button
+ VerticalAlignment="Center"
+ HorizontalAlignment="Right"
+ Padding="10"
+ MinWidth="0"
+ MinHeight="0"
+ Name="Delete"
+ Click="Delete">
+ <ui:SymbolIcon
+ Symbol="Delete"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center" />
+ </Button>
+ </StackPanel>
+ </Grid>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+ </Border>
+ <StackPanel
+ Grid.Row="2"
+ Margin="0 24 0 0"
+ Orientation="Horizontal">
+ <Button
+ Width="50"
+ MinWidth="50"
+ Click="GoBack">
+ <ui:SymbolIcon Symbol="Back" />
+ </Button>
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs
new file mode 100644
index 00000000..08fef27d
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs
@@ -0,0 +1,147 @@
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Threading;
+using FluentAvalonia.UI.Controls;
+using FluentAvalonia.UI.Navigation;
+using LibHac;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Shim;
+using Ryujinx.Ava.Common;
+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;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using System;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using UserId = LibHac.Fs.UserId;
+
+namespace Ryujinx.Ava.UI.Views.User
+{
+ public partial class UserSaveManagerView : UserControl
+ {
+ internal UserSaveManagerViewModel ViewModel { get; private set; }
+
+ private AccountManager _accountManager;
+ private HorizonClient _horizonClient;
+ private VirtualFileSystem _virtualFileSystem;
+ private NavigationDialogHost _parent;
+
+ public UserSaveManagerView()
+ {
+ InitializeComponent();
+ AddHandler(Frame.NavigatedToEvent, (s, e) =>
+ {
+ NavigatedTo(e);
+ }, RoutingStrategies.Direct);
+ }
+
+ private void NavigatedTo(NavigationEventArgs arg)
+ {
+ if (Program.PreviewerDetached)
+ {
+ switch (arg.NavigationMode)
+ {
+ case NavigationMode.New:
+ var args = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter;
+ _accountManager = args.accountManager;
+ _horizonClient = args.client;
+ _virtualFileSystem = args.virtualFileSystem;
+
+ _parent = args.parent;
+ break;
+ }
+
+ DataContext = ViewModel = new UserSaveManagerViewModel(_accountManager);
+ ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - {ViewModel.SaveManagerHeading}";
+
+ Task.Run(LoadSaves);
+ }
+ }
+
+ public void LoadSaves()
+ {
+ ViewModel.Saves.Clear();
+ var saves = new ObservableCollection<SaveModel>();
+ var saveDataFilter = SaveDataFilter.Make(
+ programId: default,
+ saveType: SaveDataType.Account,
+ new UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low),
+ saveDataId: default,
+ index: default);
+
+ using var saveDataIterator = new UniqueRef<SaveDataIterator>();
+
+ _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
+
+ Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
+
+ while (true)
+ {
+ saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure();
+
+ if (readCount == 0)
+ {
+ break;
+ }
+
+ for (int i = 0; i < readCount; i++)
+ {
+ var save = saveDataInfo[i];
+ if (save.ProgramId.Value != 0)
+ {
+ var saveModel = new SaveModel(save, _virtualFileSystem);
+ saves.Add(saveModel);
+ }
+ }
+ }
+
+ Dispatcher.UIThread.Post(() =>
+ {
+ ViewModel.Saves = saves;
+ ViewModel.Sort();
+ });
+ }
+
+ private void GoBack(object sender, RoutedEventArgs e)
+ {
+ _parent?.GoBack();
+ }
+
+ private void OpenLocation(object sender, RoutedEventArgs e)
+ {
+ if (sender is Avalonia.Controls.Button button)
+ {
+ if (button.DataContext is SaveModel saveModel)
+ {
+ ApplicationHelper.OpenSaveDir(saveModel.SaveId);
+ }
+ }
+ }
+
+ private async void Delete(object sender, RoutedEventArgs e)
+ {
+ if (sender is Avalonia.Controls.Button button)
+ {
+ if (button.DataContext is SaveModel saveModel)
+ {
+ var result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DeleteUserSave],
+ LocaleManager.Instance[LocaleKeys.IrreversibleActionNote],
+ LocaleManager.Instance[LocaleKeys.InputDialogYes],
+ LocaleManager.Instance[LocaleKeys.InputDialogNo], "");
+
+ if (result == UserResult.Yes)
+ {
+ _horizonClient.Fs.DeleteSaveData(SaveDataSpaceId.User, saveModel.SaveId);
+ ViewModel.Saves.Remove(saveModel);
+ ViewModel.Sort();
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml
new file mode 100644
index 00000000..9a6ba054
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml
@@ -0,0 +1,165 @@
+<UserControl
+ x:Class="Ryujinx.Ava.UI.Views.User.UserSelectorViews"
+ xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+ xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
+ xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ d:DesignHeight="450"
+ MinWidth="500"
+ d:DesignWidth="800"
+ mc:Ignorable="d"
+ Focusable="True"
+ x:CompileBindings="True"
+ x:DataType="viewModels:UserProfileViewModel">
+ <UserControl.Resources>
+ <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
+ </UserControl.Resources>
+ <Design.DataContext>
+ <viewModels:UserProfileViewModel />
+ </Design.DataContext>
+ <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
+ <Grid.RowDefinitions>
+ <RowDefinition />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <Border
+ CornerRadius="5"
+ BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
+ BorderThickness="1">
+ <ListBox
+ MaxHeight="300"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Center"
+ SelectionChanged="ProfilesList_SelectionChanged"
+ Background="Transparent"
+ Items="{Binding Profiles}">
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <flex:FlexPanel
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ AlignContent="FlexStart"
+ JustifyContent="FlexStart" />
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.Styles>
+ <Style Selector="ListBoxItem">
+ <Setter Property="Margin" Value="5 5 0 5" />
+ <Setter Property="CornerRadius" Value="5" />
+ </Style>
+ <Style Selector="Border#SelectionIndicator">
+ <Setter Property="Opacity" Value="0" />
+ </Style>
+ </ListBox.Styles>
+ <ListBox.DataTemplates>
+ <DataTemplate
+ DataType="models:UserProfile">
+ <Grid
+ PointerEnter="Grid_PointerEntered"
+ PointerLeave="Grid_OnPointerExited">
+ <Border
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ ClipToBounds="True"
+ CornerRadius="5"
+ Background="{Binding BackgroundColor}">
+ <StackPanel
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch">
+ <Image
+ Width="96"
+ Height="96"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Top"
+ Source="{Binding Image, Converter={StaticResource ByteImage}}" />
+ <TextBlock
+ HorizontalAlignment="Stretch"
+ MaxWidth="90"
+ Text="{Binding Name}"
+ TextAlignment="Center"
+ TextWrapping="Wrap"
+ TextTrimming="CharacterEllipsis"
+ MaxLines="2"
+ Margin="5" />
+ </StackPanel>
+ </Border>
+ <Border
+ Margin="2"
+ Height="24"
+ Width="24"
+ CornerRadius="12"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Top"
+ Background="{DynamicResource ThemeContentBackgroundColor}"
+ IsVisible="{Binding IsPointerOver}">
+ <Button
+ MaxHeight="24"
+ MaxWidth="24"
+ MinHeight="24"
+ MinWidth="24"
+ CornerRadius="12"
+ Padding="0"
+ Click="EditUser">
+ <ui:SymbolIcon Symbol="Edit" />
+ </Button>
+ </Border>
+ </Grid>
+ </DataTemplate>
+ <DataTemplate
+ DataType="viewModels:BaseModel">
+ <Panel
+ Height="118"
+ Width="96">
+ <Button
+ MinWidth="50"
+ MinHeight="50"
+ MaxWidth="50"
+ MaxHeight="50"
+ CornerRadius="25"
+ Margin="10"
+ Padding="0"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center"
+ Click="AddUser">
+ <ui:SymbolIcon Symbol="Add" />
+ </Button>
+ <Panel.Styles>
+ <Style Selector="Panel">
+ <Setter Property="Background" Value="{DynamicResource ListBoxBackground}"/>
+ </Style>
+ </Panel.Styles>
+ </Panel>
+ </DataTemplate>
+ </ListBox.DataTemplates>
+ </ListBox>
+ </Border>
+ <StackPanel
+ Grid.Row="1"
+ Margin="0 24 0 0"
+ HorizontalAlignment="Left"
+ Orientation="Horizontal"
+ Spacing="10">
+ <Button
+ Click="ManageSaves"
+ Content="{locale:Locale UserProfilesManageSaves}" />
+ <Button
+ Click="RecoverLostAccounts"
+ Content="{locale:Locale UserProfilesRecoverLostAccounts}" />
+ </StackPanel>
+ <StackPanel
+ Grid.Row="1"
+ Margin="0 24 0 0"
+ HorizontalAlignment="Right"
+ Orientation="Horizontal">
+ <Button
+ Click="Close"
+ Content="{locale:Locale UserProfilesClose}" />
+ </StackPanel>
+ </Grid>
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs
new file mode 100644
index 00000000..aa89fea9
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs
@@ -0,0 +1,128 @@
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using FluentAvalonia.UI.Controls;
+using FluentAvalonia.UI.Navigation;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Controls;
+using Ryujinx.Ava.UI.ViewModels;
+using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
+
+namespace Ryujinx.Ava.UI.Views.User
+{
+ public partial class UserSelectorViews : UserControl
+ {
+ private NavigationDialogHost _parent;
+
+ public UserProfileViewModel ViewModel { get; set; }
+
+ public UserSelectorViews()
+ {
+ InitializeComponent();
+
+ if (Program.PreviewerDetached)
+ {
+ AddHandler(Frame.NavigatedToEvent, (s, e) =>
+ {
+ NavigatedTo(e);
+ }, RoutingStrategies.Direct);
+ }
+ }
+
+ private void NavigatedTo(NavigationEventArgs arg)
+ {
+ if (Program.PreviewerDetached)
+ {
+ if (arg.NavigationMode == NavigationMode.New)
+ {
+ _parent = (NavigationDialogHost)arg.Parameter;
+ ViewModel = _parent.ViewModel;
+ }
+
+ if (arg.NavigationMode == NavigationMode.Back)
+ {
+ ((ContentDialog)_parent.Parent).Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle];
+ }
+
+ DataContext = ViewModel;
+ }
+ }
+
+ private void Grid_PointerEntered(object sender, PointerEventArgs e)
+ {
+ if (sender is Grid grid)
+ {
+ if (grid.DataContext is UserProfile profile)
+ {
+ profile.IsPointerOver = true;
+ }
+ }
+ }
+
+ private void Grid_OnPointerExited(object sender, PointerEventArgs e)
+ {
+ if (sender is Grid grid)
+ {
+ if (grid.DataContext is UserProfile profile)
+ {
+ profile.IsPointerOver = false;
+ }
+ }
+ }
+
+ private void ProfilesList_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (sender is ListBox listBox)
+ {
+ int selectedIndex = listBox.SelectedIndex;
+
+ if (selectedIndex >= 0 && selectedIndex < ViewModel.Profiles.Count)
+ {
+ if (ViewModel.Profiles[selectedIndex] is UserProfile userProfile)
+ {
+ _parent?.AccountManager?.OpenUser(userProfile.UserId);
+
+ foreach (BaseModel profile in ViewModel.Profiles)
+ {
+ if (profile is UserProfile uProfile)
+ {
+ uProfile.UpdateState();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void AddUser(object sender, RoutedEventArgs e)
+ {
+ _parent.AddUser();
+ }
+
+ private void EditUser(object sender, RoutedEventArgs e)
+ {
+ if (sender is Avalonia.Controls.Button button)
+ {
+ if (button.DataContext is UserProfile userProfile)
+ {
+ _parent.EditUser(userProfile);
+ }
+ }
+ }
+
+ private void ManageSaves(object sender, RoutedEventArgs e)
+ {
+ _parent.ManageSaves();
+ }
+
+ private void RecoverLostAccounts(object sender, RoutedEventArgs e)
+ {
+ _parent.RecoverLostAccounts();
+ }
+
+ private void Close(object sender, RoutedEventArgs e)
+ {
+ ((ContentDialog)_parent.Parent).Hide();
+ }
+ }
+} \ No newline at end of file