ryujinx/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
Emmanuel Hansen deb99d2cae
Avalonia UI - Part 1 (#3270)
* avalonia part 1

* remove vulkan ui backend

* move ui common files to ui common project

* get name for oading screen from device

* rebase.

* review 1

* review 1.1

* review

* cleanup

* addressed review

* use cancellation token

* review

* review

* rebased

* cancel library loading when closing window

* remove star  image, use fonticon instead

* delete render control frame buffer when game ends. change position of fav star

* addressed @Thog review

* ensure the right ui is downloaded in updates

* fix crash when showing not supported dialog during controller request

* add prefix to artifact names

* Auto-format Avalonia project

* Fix input

* Fix build, simplify app disposal

* remove nv stutter thread

* addressed review

* add missing change

* maintain window size if new size is zero length

* add game, handheld, docked to local

* reverse scale main window

* Update de_DE.json

* Update de_DE.json

* Update de_DE.json

* Update italian json

* Update it_IT.json

* let render timer poll with no wait

* remove unused code

* more unused code

* enabled tiered compilation and trimming

* check if window event is not closed before signaling

* fix atmospher case

* locale fix

* locale fix

* remove explicit tiered compilation declarations

* Remove ) it_IT.json

* Remove ) de_DE.json

* Update it_IT.json

* Update pt_BR locale with latest strings

* Remove ')'

* add more strings to locale

* update locale

* remove extra slash

* remove extra slash

* set firmware version to 0 if key's not found

* fix

* revert timer changes

* lock  on object instead

* Update it_IT.json

* remove unused method

* add load screen text to locale

* drop swap event

* Update de_DE.json

* Update de_DE.json

* do null check when stopping emulator

* Update de_DE.json

* Create tr_TR.json

* Add tr_TR

* Add tr_TR + Turkish

* Update it_IT.json

* Update Ryujinx.Ava/Input/AvaloniaMappingHelper.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* addressed review

* Update Ryujinx.Ava/Ui/Backend/OpenGl/OpenGlRenderTarget.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* use avalonia's inbuilt renderer on linux

* removed whitespace

* workaround for queue render crash with vsync off

* drop custom backend

* format files

* fix not closing issue

* remove warnings

* rebase

* update avalonia library

* Reposition the Text and Button on About Page

* Assign build version

* Remove appveyor text

Co-authored-by: gdk <gab.dark.100@gmail.com>
Co-authored-by: Niwu34 <67392333+Niwu34@users.noreply.github.com>
Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com>
Co-authored-by: aegiff <99728970+aegiff@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: MostlyWhat <78652091+MostlyWhat@users.noreply.github.com>
2022-05-15 13:30:15 +02:00

330 lines
11 KiB
C#

using Gtk;
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;
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
namespace Ryujinx.Ui.Windows
{
public partial class UserProfilesManagerWindow : Window
{
private const uint MaxProfileNameLength = 0x20;
private readonly AccountManager _accountManager;
private readonly ContentManager _contentManager;
private byte[] _bufferImageProfile;
private string _tempNewProfileName;
private Gdk.RGBA _selectedColor;
private ManualResetEvent _avatarsPreloadingEvent = new ManualResetEvent(false);
public UserProfilesManagerWindow(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 CellRendererToggle();
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)
{
_selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96);
_selectedUserIdLabel.Text = userProfile.UserId.ToString();
_selectedUserNameEntry.Text = userProfile.Name;
_deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId;
_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);
Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1);
string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0];
string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1];
// Unselect the first user.
_usersTreeView.Model.GetIterFirst(out TreeIter firstIter);
_tableStore.SetValue(firstIter, 0, false);
_tableStore.SetValue(firstIter, 3, Gdk.RGBA.Zero);
// Set new informations.
_tableStore.SetValue(selectedIter, 0, true);
_selectedUserImage.Pixbuf = userPicture;
_selectedUserNameEntry.Text = userName;
_selectedUserIdLabel.Text = userId;
_saveProfileNameButton.Sensitive = false;
// Open the selected one.
_accountManager.OpenUser(new UserId(userId));
_deleteButton.Sensitive = userId != AccountManager.DefaultUserId.ToString();
_tableStore.SetValue(selectedIter, 3, _selectedColor);
}
private void SelectedUserNameEntry_KeyReleaseEvent(object o, KeyReleaseEventArgs args)
{
if (_saveProfileNameButton.Sensitive == false)
{
_saveProfileNameButton.Sensitive = true;
}
}
private void AddButton_Pressed(object sender, EventArgs e)
{
_tempNewProfileName = GtkDialog.CreateInputDialog(this, "Choose the Profile Name", "Please Enter a Profile Name", MaxProfileNameLength);
if (_tempNewProfileName != "")
{
SelectProfileImage(true);
if (_bufferImageProfile != null)
{
AddUser();
}
}
}
private void DeleteButton_Pressed(object sender, EventArgs e)
{
if (GtkDialog.CreateChoiceDialog("Delete User Profile", "Are you sure you want to delete the profile ?", "Deleting this profile will also delete all associated save data."))
{
_accountManager.DeleteUser(GetSelectedUserId());
RefreshList();
}
}
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 = new MemoryStream())
{
image.SaveAsJpeg(streamJpg);
_bufferImageProfile = streamJpg.ToArray();
}
}
}
private void ProfileImageFileChooser()
{
FileChooserNative fileChooser = new FileChooserNative("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel")
{
SelectMultiple = false
};
FileFilter filter = new FileFilter()
{
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<int, string> buttons = new Dictionary<int, string>()
{
{ 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 AvatarWindow()
{
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();
}
}
}