diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Ava/UI/Views | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Ava/UI/Views')
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 |
