diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs index 749cb69786..03b176e0f0 100644 --- a/src/Ryujinx.Gtk3/Program.cs +++ b/src/Ryujinx.Gtk3/Program.cs @@ -320,6 +320,11 @@ namespace Ryujinx } } + if (ConfigurationState.Instance.UI.ShowProfilesAtStartup) + { + mainWindow.ShowProfilesSelector(); + } + if (CommandLineState.LaunchPathArg != null) { mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg); diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.cs b/src/Ryujinx.Gtk3/UI/MainWindow.cs index d1ca6ce6ab..ff4a674cdd 100644 --- a/src/Ryujinx.Gtk3/UI/MainWindow.cs +++ b/src/Ryujinx.Gtk3/UI/MainWindow.cs @@ -887,6 +887,12 @@ namespace Ryujinx.UI return false; } + public void ShowProfilesSelector() + { + var window = new UserProfilesSelectorWindow(_accountManager, _contentManager, _virtualFileSystem); + window.Show(); + } + public void RunApplication(string path, bool startFullscreen = false) { if (_gameLoaded) diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs index dc467c0f21..192e38890e 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs @@ -52,6 +52,7 @@ namespace Ryujinx.UI.Windows [GUI] CheckButton _dockedModeToggle; [GUI] CheckButton _discordToggle; [GUI] CheckButton _checkUpdatesToggle; + [GUI] CheckButton _showProfileToggle; [GUI] CheckButton _showConfirmExitToggle; [GUI] RadioButton _hideCursorNever; [GUI] RadioButton _hideCursorOnIdle; @@ -225,6 +226,11 @@ namespace Ryujinx.UI.Windows _checkUpdatesToggle.Click(); } + if (ConfigurationState.Instance.UI.ShowProfilesAtStartup) + { + _showProfileToggle.Click(); + } + if (ConfigurationState.Instance.ShowConfirmExit) { _showConfirmExitToggle.Click(); @@ -626,6 +632,7 @@ namespace Ryujinx.UI.Windows ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active; ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; + ConfigurationState.Instance.UI.ShowProfilesAtStartup.Value = _showProfileToggle.Active; ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; ConfigurationState.Instance.HideCursor.Value = hideCursor; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade index f0dbd6b637..bbf9c7c515 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade +++ b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade @@ -143,6 +143,22 @@ 1 + + + Show Profiles on Launch + True + True + False + start + True + + + False + True + 5 + 2 + + Show "Confirm Exit" Dialog @@ -156,7 +172,7 @@ False True 5 - 2 + 3 diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesSelectorWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesSelectorWindow.Designer.cs new file mode 100644 index 0000000000..e58072edcd --- /dev/null +++ b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesSelectorWindow.Designer.cs @@ -0,0 +1,160 @@ +using Gtk; +using Pango; +using System; + +namespace Ryujinx.UI.Windows +{ + public partial class UserProfilesSelectorWindow : Window + { + private Box _mainBox; + private Label _selectedLabel; + private Box _selectedUserBox; + private Image _selectedUserImage; + private Box _selectedUserInfoBox; + private Entry _selectedUserNameEntry; + private Label _selectedUserIdLabel; + private Box _selectedUserButtonsBox; + private Button _saveProfileNameButton; + private Button _changeProfileImageButton; + private Box _usersTreeViewBox; + private Label _availableUsersLabel; + private ScrolledWindow _usersTreeViewWindow; + private ListStore _tableStore; + private TreeView _usersTreeView; + private Box _bottomBox; + private Button _addButton; + private Button _deleteButton; + private Button _closeButton; + + private void InitializeComponent() + { + // + // UserProfilesManagerWindow + // + CanFocus = false; + Resizable = false; + Modal = true; + WindowPosition = WindowPosition.Center; + DefaultWidth = 620; + DefaultHeight = 548; + TypeHint = Gdk.WindowTypeHint.Dialog; + + // + // _mainBox + // + _mainBox = new Box(Orientation.Vertical, 0); + + + // + // _viewBox + // + _usersTreeViewBox = new Box(Orientation.Vertical, 0); + + // + // _saveProfileNameButton + // + _saveProfileNameButton = new Button() + { + Label = "Save Profile Name", + CanFocus = true, + ReceivesDefault = true, + Sensitive = false, + }; + _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed; + + // + // _changeProfileImageButton + // + _changeProfileImageButton = new Button() + { + Label = "Change Profile Image", + CanFocus = true, + ReceivesDefault = true, + MarginTop = 10, + }; + _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed; + + // + // _availableUsersLabel + // + _availableUsersLabel = new Label("Available User Profiles:") + { + Margin = 15, + Attributes = new AttrList(), + Halign = Align.Start, + }; + _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); + + // + // _usersTreeViewWindow + // + _usersTreeViewWindow = new ScrolledWindow() + { + ShadowType = ShadowType.In, + CanFocus = true, + Expand = true, + MarginStart = 30, + MarginEnd = 30, + MarginBottom = 15, + }; + + // + // _tableStore + // + _tableStore = new ListStore(typeof(bool), typeof(Gdk.Pixbuf), typeof(string), typeof(Gdk.RGBA)); + + // + // _usersTreeView + // + _usersTreeView = new TreeView(_tableStore) + { + HoverSelection = true, + HeadersVisible = false, + }; + _usersTreeView.RowActivated += UsersTreeView_Activated; + + // + // _bottomBox + // + _bottomBox = new Box(Orientation.Horizontal, 0) + { + MarginStart = 30, + MarginEnd = 30, + MarginBottom = 15, + }; + + + // + // _closeButton + // + _closeButton = new Button() + { + Label = "Close", + CanFocus = true, + ReceivesDefault = true, + HeightRequest = 35, + WidthRequest = 80, + }; + _closeButton.Clicked += CloseButton_Pressed; + + ShowComponent(); + } + + private void ShowComponent() + { + _usersTreeViewWindow.Add(_usersTreeView); + + _usersTreeViewBox.Add(_usersTreeViewWindow); + _bottomBox.PackStart(_addButton, false, false, 0); + _bottomBox.PackEnd(_closeButton, false, false, 0); + + _mainBox.PackStart(_availableUsersLabel, false, false, 0); + _mainBox.Add(_usersTreeViewBox); + _mainBox.Add(_bottomBox); + + Add(_mainBox); + + ShowAll(); + } + } +} diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesSelectorWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesSelectorWindow.cs new file mode 100644 index 0000000000..943d9d7596 --- /dev/null +++ b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesSelectorWindow.cs @@ -0,0 +1,271 @@ +using Gtk; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.Widgets; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Image = SixLabors.ImageSharp.Image; + +namespace Ryujinx.UI.Windows +{ + public partial class UserProfilesSelectorWindow : Window + { + private readonly AccountManager _accountManager; + private readonly ContentManager _contentManager; + + private byte[] _bufferImageProfile; + private string _tempNewProfileName; + + private Gdk.RGBA _selectedColor; + + private readonly ManualResetEvent _avatarsPreloadingEvent = new(false); + + public UserProfilesSelectorWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles") + { + Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); + + InitializeComponent(); + + _selectedColor.Red = 0.212; + _selectedColor.Green = 0.843; + _selectedColor.Blue = 0.718; + _selectedColor.Alpha = 1; + + _accountManager = accountManager; + _contentManager = contentManager; + + CellRendererToggle userSelectedToggle = new(); + userSelectedToggle.Toggled += UserSelectedToggle_Toggled; + + // NOTE: Uncomment following line when multiple selection of user profiles is supported. + //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0); + _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1); + _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); + + _tableStore.SetSortColumnId(0, SortType.Descending); + + RefreshList(); + + if (_contentManager.GetCurrentFirmwareVersion() != null) + { + Task.Run(() => + { + AvatarWindow.PreloadAvatars(contentManager, virtualFileSystem); + _avatarsPreloadingEvent.Set(); + }); + } + } + + public void RefreshList() + { + _tableStore.Clear(); + + foreach (UserProfile userProfile in _accountManager.GetAllUsers()) + { + _tableStore.AppendValues(userProfile.AccountState == AccountState.Open, new Gdk.Pixbuf(userProfile.Image, 96, 96), $"{userProfile.Name}\n{userProfile.UserId}", Gdk.RGBA.Zero); + + if (userProfile.AccountState == AccountState.Open) + { + + _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); + _tableStore.SetValue(firstIter, 3, _selectedColor); + } + } + } + + // + // Events + // + + private void UsersTreeView_Activated(object o, RowActivatedArgs args) + { + SelectUserTreeView(); + } + + private void UserSelectedToggle_Toggled(object o, ToggledArgs args) + { + SelectUserTreeView(); + } + + private void SelectUserTreeView() + { + // Get selected item informations. + _usersTreeView.Selection.GetSelected(out TreeIter selectedIter); + + string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; + + // Open the selected one. + _accountManager.OpenUser(new UserId(userId)); + + Close(); + } + + + private void EditProfileNameButton_Pressed(object sender, EventArgs e) + { + _saveProfileNameButton.Sensitive = false; + + _accountManager.SetUserName(GetSelectedUserId(), _selectedUserNameEntry.Text); + + RefreshList(); + } + + private void ProcessProfileImage(byte[] buffer) + { + using Image image = Image.Load(buffer); + + image.Mutate(x => x.Resize(256, 256)); + + using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); + + image.SaveAsJpeg(streamJpg); + + _bufferImageProfile = streamJpg.ToArray(); + } + + private void ProfileImageFileChooser() + { + FileChooserNative fileChooser = new("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel") + { + SelectMultiple = false, + }; + + FileFilter filter = new() + { + Name = "Custom Profile Images", + }; + filter.AddPattern("*.jpg"); + filter.AddPattern("*.jpeg"); + filter.AddPattern("*.png"); + filter.AddPattern("*.bmp"); + + fileChooser.AddFilter(filter); + + if (fileChooser.Run() == (int)ResponseType.Accept) + { + ProcessProfileImage(File.ReadAllBytes(fileChooser.Filename)); + } + + fileChooser.Dispose(); + } + + private void SelectProfileImage(bool newUser = false) + { + if (_contentManager.GetCurrentFirmwareVersion() == null) + { + ProfileImageFileChooser(); + } + else + { + Dictionary buttons = new() + { + { 0, "Import Image File" }, + { 1, "Select Firmware Avatar" }, + }; + + ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection", + "Choose a Profile Image", + "You may import a custom profile image, or select an avatar from the system firmware.", + buttons, MessageType.Question); + + if (responseDialog == 0) + { + ProfileImageFileChooser(); + } + else if (responseDialog == (ResponseType)1) + { + AvatarWindow avatarWindow = new() + { + NewUser = newUser, + }; + + avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent; + + avatarWindow.SetSizeRequest((int)(avatarWindow.DefaultWidth * Program.WindowScaleFactor), (int)(avatarWindow.DefaultHeight * Program.WindowScaleFactor)); + avatarWindow.Show(); + } + } + } + + private void ChangeProfileImageButton_Pressed(object sender, EventArgs e) + { + if (_contentManager.GetCurrentFirmwareVersion() != null) + { + _avatarsPreloadingEvent.WaitOne(); + } + + SelectProfileImage(); + + if (_bufferImageProfile != null) + { + SetUserImage(); + } + } + + private void AvatarWindow_DeleteEvent(object sender, DeleteEventArgs args) + { + _bufferImageProfile = ((AvatarWindow)sender).SelectedProfileImage; + + if (_bufferImageProfile != null) + { + if (((AvatarWindow)sender).NewUser) + { + AddUser(); + } + else + { + SetUserImage(); + } + } + } + + private void AddUser() + { + _accountManager.AddUser(_tempNewProfileName, _bufferImageProfile); + + _bufferImageProfile = null; + _tempNewProfileName = ""; + + RefreshList(); + } + + private void SetUserImage() + { + _accountManager.SetUserImage(GetSelectedUserId(), _bufferImageProfile); + + _bufferImageProfile = null; + + RefreshList(); + } + + private UserId GetSelectedUserId() + { + if (_usersTreeView.Model.GetIterFirst(out TreeIter iter)) + { + do + { + if ((bool)_tableStore.GetValue(iter, 0)) + { + break; + } + } + while (_usersTreeView.Model.IterNext(ref iter)); + } + + return new UserId(_tableStore.GetValue(iter, 2).ToString().Split("\n")[1]); + } + + private void CloseButton_Pressed(object sender, EventArgs e) + { + Close(); + } + } +} diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index af3ad0a1da..0371d352a3 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -15,7 +15,7 @@ namespace Ryujinx.UI.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 51; + public const int CurrentVersion = 52; /// /// Version of the configuration file format @@ -162,6 +162,11 @@ namespace Ryujinx.UI.Common.Configuration /// public bool ShowConfirmExit { get; set; } + /// + /// Show Profiles Dialog at Startup + /// + public bool ShowProfilesAtStartup { get; set; } + /// /// Enables or disables save window size, position and state on close. /// diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index 8420dc5d98..6683005b59 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -187,6 +187,12 @@ namespace Ryujinx.UI.Common.Configuration /// public ReactiveObject IsAscendingOrder { get; private set; } + /// + /// Sets if Grid is ordered in Ascending Order + /// + public ReactiveObject ShowProfilesAtStartup { get; private set; } + + public UISection() { GuiColumns = new Columns(); @@ -205,6 +211,7 @@ namespace Ryujinx.UI.Common.Configuration IsAscendingOrder = new ReactiveObject(); LanguageCode = new ReactiveObject(); ShowConsole = new ReactiveObject(); + ShowProfilesAtStartup = new ReactiveObject(); ShowConsole.Event += static (s, e) => { ConsoleHelper.SetConsoleWindowState(e.NewValue); }; } } @@ -766,6 +773,7 @@ namespace Ryujinx.UI.Common.Configuration PreferredGpu = Graphics.PreferredGpu, MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId, MultiplayerMode = Multiplayer.Mode, + ShowProfilesAtStartup = UI.ShowProfilesAtStartup, }; return configurationFile; @@ -853,6 +861,7 @@ namespace Ryujinx.UI.Common.Configuration UI.IsAscendingOrder.Value = true; UI.StartFullscreen.Value = false; UI.ShowConsole.Value = true; + UI.ShowProfilesAtStartup.Value = false; UI.WindowStartup.WindowSizeWidth.Value = 1280; UI.WindowStartup.WindowSizeHeight.Value = 760; UI.WindowStartup.WindowPositionX.Value = 0; @@ -1477,6 +1486,15 @@ namespace Ryujinx.UI.Common.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 52) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 52."); + + configurationFileFormat.ShowProfilesAtStartup = false; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1555,6 +1573,7 @@ namespace Ryujinx.UI.Common.Configuration UI.ApplicationSort.Value = configurationFileFormat.ApplicationSort; UI.StartFullscreen.Value = configurationFileFormat.StartFullscreen; UI.ShowConsole.Value = configurationFileFormat.ShowConsole; + UI.ShowProfilesAtStartup.Value = configurationFileFormat.ShowProfilesAtStartup; UI.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth; UI.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight; UI.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX; diff --git a/src/Ryujinx/App.axaml b/src/Ryujinx/App.axaml index eab318b7ba..8f8bd50bc0 100644 --- a/src/Ryujinx/App.axaml +++ b/src/Ryujinx/App.axaml @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx/Assets/Locales/ar_SA.json b/src/Ryujinx/Assets/Locales/ar_SA.json index 73e55633bb..d00f7aa088 100644 --- a/src/Ryujinx/Assets/Locales/ar_SA.json +++ b/src/Ryujinx/Assets/Locales/ar_SA.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "تمكين وجود ديسكورد الغني", "SettingsTabGeneralCheckUpdatesOnLaunch": "التحقق من وجود تحديثات عند التشغيل", "SettingsTabGeneralShowConfirmExitDialog": "إظهار مربع حوار \"تأكيد الخروج\"", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "تذكر حجم/موضع النافذة", "SettingsTabGeneralHideCursor": "إخفاء المؤشر:", "SettingsTabGeneralHideCursorNever": "مطلقا", diff --git a/src/Ryujinx/Assets/Locales/de_DE.json b/src/Ryujinx/Assets/Locales/de_DE.json index 4012931985..25a762a4d2 100644 --- a/src/Ryujinx/Assets/Locales/de_DE.json +++ b/src/Ryujinx/Assets/Locales/de_DE.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Aktiviere die Statusanzeige für Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Beim Start nach Updates suchen", "SettingsTabGeneralShowConfirmExitDialog": "Zeige den \"Beenden bestätigen\"-Dialog", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Fenstergröße/-position merken", "SettingsTabGeneralHideCursor": "Mauszeiger ausblenden", "SettingsTabGeneralHideCursorNever": "Niemals", diff --git a/src/Ryujinx/Assets/Locales/el_GR.json b/src/Ryujinx/Assets/Locales/el_GR.json index ccdf6e0e41..9e72db1cba 100644 --- a/src/Ryujinx/Assets/Locales/el_GR.json +++ b/src/Ryujinx/Assets/Locales/el_GR.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Ενεργοποίηση Εμπλουτισμένης Παρουσίας Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Έλεγχος για Ενημερώσεις στην Εκκίνηση", "SettingsTabGeneralShowConfirmExitDialog": "Εμφάνιση διαλόγου \"Επιβεβαίωση Εξόδου\".", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Απόκρυψη Κέρσορα:", "SettingsTabGeneralHideCursorNever": "Ποτέ", diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index 8df0f96a14..a7d086187a 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch", "SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Hide Cursor:", "SettingsTabGeneralHideCursorNever": "Never", diff --git a/src/Ryujinx/Assets/Locales/es_ES.json b/src/Ryujinx/Assets/Locales/es_ES.json index e58fa5dcf8..b5e8b85e94 100644 --- a/src/Ryujinx/Assets/Locales/es_ES.json +++ b/src/Ryujinx/Assets/Locales/es_ES.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Habilitar estado en Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Buscar actualizaciones al iniciar", "SettingsTabGeneralShowConfirmExitDialog": "Mostrar diálogo de confirmación al cerrar", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Esconder el cursor:", "SettingsTabGeneralHideCursorNever": "Nunca", diff --git a/src/Ryujinx/Assets/Locales/fr_FR.json b/src/Ryujinx/Assets/Locales/fr_FR.json index 99a060650c..256a8084a3 100644 --- a/src/Ryujinx/Assets/Locales/fr_FR.json +++ b/src/Ryujinx/Assets/Locales/fr_FR.json @@ -95,6 +95,7 @@ "SettingsTabGeneralGeneral": "Général", "SettingsTabGeneralEnableDiscordRichPresence": "Activer Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Vérifier les mises à jour au démarrage", + "SettingsTabGeneralShoProfilesAtStartup": "Afficher les profiles au démarrage", "SettingsTabGeneralShowConfirmExitDialog": "Afficher le message de \"Confirmation de sortie\"", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Masquer le Curseur :", diff --git a/src/Ryujinx/Assets/Locales/he_IL.json b/src/Ryujinx/Assets/Locales/he_IL.json index 848f780809..a4e94c834c 100644 --- a/src/Ryujinx/Assets/Locales/he_IL.json +++ b/src/Ryujinx/Assets/Locales/he_IL.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "הפעלת תצוגה עשירה בדיסקורד", "SettingsTabGeneralCheckUpdatesOnLaunch": "בדוק אם קיימים עדכונים בהפעלה", "SettingsTabGeneralShowConfirmExitDialog": "הראה דיאלוג \"אשר יציאה\"", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "הסתר את הסמן", "SettingsTabGeneralHideCursorNever": "אף פעם", diff --git a/src/Ryujinx/Assets/Locales/it_IT.json b/src/Ryujinx/Assets/Locales/it_IT.json index 280ebd880b..f535d046ae 100644 --- a/src/Ryujinx/Assets/Locales/it_IT.json +++ b/src/Ryujinx/Assets/Locales/it_IT.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Attiva Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "Controlla aggiornamenti all'avvio", "SettingsTabGeneralShowConfirmExitDialog": "Mostra dialogo \"Conferma Uscita\"", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Nascondi il cursore:", "SettingsTabGeneralHideCursorNever": "Mai", diff --git a/src/Ryujinx/Assets/Locales/ja_JP.json b/src/Ryujinx/Assets/Locales/ja_JP.json index 61e9632581..33b8621882 100644 --- a/src/Ryujinx/Assets/Locales/ja_JP.json +++ b/src/Ryujinx/Assets/Locales/ja_JP.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Discord リッチプレゼンスを有効にする", "SettingsTabGeneralCheckUpdatesOnLaunch": "起動時にアップデートを確認する", "SettingsTabGeneralShowConfirmExitDialog": "\"終了を確認\" ダイアログを表示する", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "マウスカーソルを非表示", "SettingsTabGeneralHideCursorNever": "決して", diff --git a/src/Ryujinx/Assets/Locales/ko_KR.json b/src/Ryujinx/Assets/Locales/ko_KR.json index a92d084e01..a6fbca4a2f 100644 --- a/src/Ryujinx/Assets/Locales/ko_KR.json +++ b/src/Ryujinx/Assets/Locales/ko_KR.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "디스코드 활동 상태 활성화", "SettingsTabGeneralCheckUpdatesOnLaunch": "시작 시, 업데이트 확인", "SettingsTabGeneralShowConfirmExitDialog": "\"종료 확인\" 대화 상자 표시", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "창 크기/위치 기억", "SettingsTabGeneralHideCursor": "마우스 커서 숨기기", "SettingsTabGeneralHideCursorNever": "절대 안 함", diff --git a/src/Ryujinx/Assets/Locales/pl_PL.json b/src/Ryujinx/Assets/Locales/pl_PL.json index 9d1bd7b447..bf85a828e4 100644 --- a/src/Ryujinx/Assets/Locales/pl_PL.json +++ b/src/Ryujinx/Assets/Locales/pl_PL.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Włącz Bogatą Obecność Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Sprawdzaj aktualizacje przy uruchomieniu", "SettingsTabGeneralShowConfirmExitDialog": "Pokazuj okno dialogowe \"Potwierdź wyjście\"", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Ukryj kursor:", "SettingsTabGeneralHideCursorNever": "Nigdy", diff --git a/src/Ryujinx/Assets/Locales/pt_BR.json b/src/Ryujinx/Assets/Locales/pt_BR.json index a8c244b650..9e4f784fd4 100644 --- a/src/Ryujinx/Assets/Locales/pt_BR.json +++ b/src/Ryujinx/Assets/Locales/pt_BR.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Habilitar Rich Presence do Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Verificar se há atualizações ao iniciar", "SettingsTabGeneralShowConfirmExitDialog": "Exibir diálogo de confirmação ao sair", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Lembrar tamanho/posição da Janela", "SettingsTabGeneralHideCursor": "Esconder o cursor do mouse:", "SettingsTabGeneralHideCursorNever": "Nunca", diff --git a/src/Ryujinx/Assets/Locales/ru_RU.json b/src/Ryujinx/Assets/Locales/ru_RU.json index 75fd4fe129..245b89b527 100644 --- a/src/Ryujinx/Assets/Locales/ru_RU.json +++ b/src/Ryujinx/Assets/Locales/ru_RU.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Статус активности в Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Проверять наличие обновлений при запуске", "SettingsTabGeneralShowConfirmExitDialog": "Подтверждать выход из приложения", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Запомнить размер/положение окна", "SettingsTabGeneralHideCursor": "Скрывать курсор", "SettingsTabGeneralHideCursorNever": "Никогда", diff --git a/src/Ryujinx/Assets/Locales/th_TH.json b/src/Ryujinx/Assets/Locales/th_TH.json index 6294422695..c8694a8ec4 100644 --- a/src/Ryujinx/Assets/Locales/th_TH.json +++ b/src/Ryujinx/Assets/Locales/th_TH.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "เปิดใช้งาน Discord Rich Presence", "SettingsTabGeneralCheckUpdatesOnLaunch": "ตรวจหาการอัปเดตเมื่อเปิดโปรแกรม", "SettingsTabGeneralShowConfirmExitDialog": "แสดง \"ยืนยันการออก\" กล่องข้อความโต้ตอบ", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "ซ่อน เคอร์เซอร์:", "SettingsTabGeneralHideCursorNever": "ไม่มี", diff --git a/src/Ryujinx/Assets/Locales/tr_TR.json b/src/Ryujinx/Assets/Locales/tr_TR.json index f74baaa182..3e1032c300 100644 --- a/src/Ryujinx/Assets/Locales/tr_TR.json +++ b/src/Ryujinx/Assets/Locales/tr_TR.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Discord Zengin İçerik'i Etkinleştir", "SettingsTabGeneralCheckUpdatesOnLaunch": "Her Açılışta Güncellemeleri Denetle", "SettingsTabGeneralShowConfirmExitDialog": "\"Çıkışı Onayla\" Diyaloğunu Göster", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "İşaretçiyi Gizle:", "SettingsTabGeneralHideCursorNever": "Hiçbir Zaman", diff --git a/src/Ryujinx/Assets/Locales/uk_UA.json b/src/Ryujinx/Assets/Locales/uk_UA.json index 976edfb1b3..779f4ec311 100644 --- a/src/Ryujinx/Assets/Locales/uk_UA.json +++ b/src/Ryujinx/Assets/Locales/uk_UA.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "Увімкнути розширену присутність Discord", "SettingsTabGeneralCheckUpdatesOnLaunch": "Перевіряти наявність оновлень під час запуску", "SettingsTabGeneralShowConfirmExitDialog": "Показати діалогове вікно «Підтвердити вихід».", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralHideCursor": "Сховати вказівник:", "SettingsTabGeneralHideCursorNever": "Ніколи", diff --git a/src/Ryujinx/Assets/Locales/zh_CN.json b/src/Ryujinx/Assets/Locales/zh_CN.json index 66f59ecd04..8d588b0c0a 100644 --- a/src/Ryujinx/Assets/Locales/zh_CN.json +++ b/src/Ryujinx/Assets/Locales/zh_CN.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "启用 Discord 在线状态展示", "SettingsTabGeneralCheckUpdatesOnLaunch": "启动时检查更新", "SettingsTabGeneralShowConfirmExitDialog": "退出游戏时需要确认", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "记住窗口大小和位置", "SettingsTabGeneralHideCursor": "隐藏鼠标指针:", "SettingsTabGeneralHideCursorNever": "从不隐藏", diff --git a/src/Ryujinx/Assets/Locales/zh_TW.json b/src/Ryujinx/Assets/Locales/zh_TW.json index fc838d2510..9a4482f684 100644 --- a/src/Ryujinx/Assets/Locales/zh_TW.json +++ b/src/Ryujinx/Assets/Locales/zh_TW.json @@ -96,6 +96,7 @@ "SettingsTabGeneralEnableDiscordRichPresence": "啟用 Discord 動態狀態展示", "SettingsTabGeneralCheckUpdatesOnLaunch": "啟動時檢查更新", "SettingsTabGeneralShowConfirmExitDialog": "顯示「確認結束」對話方塊", + "SettingsTabGeneralShoProfilesAtStartup": "Show Profiles at Startup", "SettingsTabGeneralRememberWindowState": "記住視窗大小/位置", "SettingsTabGeneralHideCursor": "隱藏滑鼠游標:", "SettingsTabGeneralHideCursorNever": "從不", diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 6718b7fcc4..b9d562ecf4 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -164,4 +164,10 @@ + + + UserSimpleSelectorView.axaml + Code + + diff --git a/src/Ryujinx/UI/ViewModels/ProfilesViewModel.cs b/src/Ryujinx/UI/ViewModels/ProfilesViewModel.cs new file mode 100644 index 0000000000..078898a5d0 --- /dev/null +++ b/src/Ryujinx/UI/ViewModels/ProfilesViewModel.cs @@ -0,0 +1,50 @@ +using Avalonia.Collections; +using System; +using System.Collections.Generic; + +using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public class ProfilesViewModel : BaseModel + { + + private UserProfile _selectedProfile; + + public event Action CloseWindow; + public event Action ApplyProfile; + + public ProfilesViewModel() + { + Profiles = new AvaloniaList(); + Profiles.Clear(); + } + + public ProfilesViewModel(IEnumerable profiles) + { + Profiles = new AvaloniaList(); + Profiles.Clear(); + Profiles.AddRange(profiles); + } + + public AvaloniaList Profiles + { + get; set; + } + + public UserProfile SelectedProfile + { + get => _selectedProfile; + set + { + _selectedProfile = value; + OnPropertyChanged(); + } + } + + public void Close() + { + CloseWindow?.Invoke(); + } + } +} diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 70e5fa5c74..9ae7d8cbd0 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -130,6 +130,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableDiscordIntegration { get; set; } public bool CheckUpdatesOnStart { get; set; } + + public bool ShowProfilesAtStartup { get; set; } public bool ShowConfirmExit { get; set; } public bool RememberWindowState { get; set; } public int HideCursor { get; set; } @@ -391,6 +393,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableDiscordIntegration = config.EnableDiscordIntegration; CheckUpdatesOnStart = config.CheckUpdatesOnStart; ShowConfirmExit = config.ShowConfirmExit; + ShowProfilesAtStartup = config.UI.ShowProfilesAtStartup; RememberWindowState = config.RememberWindowState; HideCursor = (int)config.HideCursor.Value; @@ -483,6 +486,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.EnableDiscordIntegration.Value = EnableDiscordIntegration; config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart; config.ShowConfirmExit.Value = ShowConfirmExit; + config.UI.ShowProfilesAtStartup.Value = ShowProfilesAtStartup; config.RememberWindowState.Value = RememberWindowState; config.HideCursor.Value = (HideCursorMode)HideCursor; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml index f9b9be44b2..9ceaa0c54f 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml @@ -1,4 +1,4 @@ - + + + diff --git a/src/Ryujinx/UI/Views/User/UserSimpleSelectorView.axaml b/src/Ryujinx/UI/Views/User/UserSimpleSelectorView.axaml new file mode 100644 index 0000000000..d544b750cb --- /dev/null +++ b/src/Ryujinx/UI/Views/User/UserSimpleSelectorView.axaml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +