Merge branch 'ui-userprofiles-and-misc' into 'master'

UI: User Profiles + General Fixes

See merge request [ryubing/ryujinx!173](https://git.ryujinx.app/ryubing/ryujinx/-/merge_requests/173)
This commit is contained in:
Neo 2025-12-07 03:59:12 -06:00
commit 31a0b016bf
16 changed files with 619 additions and 793 deletions

File diff suppressed because it is too large Load diff

View file

@ -16,14 +16,8 @@
<Design.DataContext>
<viewModels:ProfileSelectorDialogViewModel />
</Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
<Border
CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
BorderThickness="1">
<Grid Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
<StackPanel>
<ListBox
MaxHeight="300"
HorizontalAlignment="Stretch"
@ -31,7 +25,6 @@
Background="Transparent"
ItemsSource="{Binding Profiles}"
SelectionChanged="ProfilesList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
@ -40,17 +33,15 @@
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Margin" Value="5 5 0 5" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="CornerRadius" Value="5" />
</Style>
<Style Selector="Rectangle#SelectionIndicator">
<Setter Property="Opacity" Value="0" />
</Style>
</ListBox.Styles>
<ListBox.DataTemplates>
<DataTemplate
DataType="models:UserProfile">
@ -58,6 +49,7 @@
PointerEntered="Grid_PointerEntered"
PointerExited="Grid_OnPointerExited">
<Border
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ClipToBounds="True"
@ -69,37 +61,26 @@
<Image
Width="96"
Height="96"
Margin="0,0,0,10"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
<TextBlock
HorizontalAlignment="Stretch"
MaxWidth="90"
Text="{Binding Name}"
Height="30"
MaxWidth="90"
TextAlignment="Center"
TextWrapping="Wrap"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"
MaxLines="2"
Margin="5" />
TextWrapping="Wrap"
MaxLines="2" />
</StackPanel>
</Border>
</Grid>
</DataTemplate>
<DataTemplate
DataType="viewModels:BaseModel">
<Panel
Height="118"
Width="96">
<Panel.Styles>
<Style Selector="Panel">
<Setter Property="Background" Value="{DynamicResource ListBoxBackground}" />
</Style>
</Panel.Styles>
</Panel>
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
</Border>
</StackPanel>
</Grid>
</UserControl>

View file

@ -13,7 +13,7 @@
<MenuItem
Command="{Binding ToggleFavorite}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuToggleFavorite}"
Header="{Binding FavoriteStatusText}"
Icon="{ext:Icon fa-solid fa-star}" />
<MenuItem
Command="{Binding CreateApplicationShortcut}"

View file

@ -166,7 +166,7 @@ namespace Ryujinx.Ava.UI.Controls
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage],
string.Empty,
LocaleManager.Instance[LocaleKeys.IrreversibleActionNote],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
string.Empty);

View file

@ -2091,6 +2091,8 @@ namespace Ryujinx.Ava.UI.ViewModels
}
);
public string FavoriteStatusText => SelectedApplication?.Favorite == false ? LocaleManager.Instance[LocaleKeys.GameListContextMenuAddToFavorites] : LocaleManager.Instance[LocaleKeys.GameListContextMenuRemoveFromFavorites];
public static RelayCommand<MainWindowViewModel> CreateApplicationShortcut { get; } =
Commands.CreateConditional<MainWindowViewModel>(vm => vm?.SelectedApplication != null,
viewModel => ShortcutHelper.CreateAppShortcut(

View file

@ -1,7 +1,7 @@
using Ryujinx.Ava.UI.Models;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Collections.Specialized;
namespace Ryujinx.Ava.UI.ViewModels
{
@ -9,20 +9,35 @@ namespace Ryujinx.Ava.UI.ViewModels
{
public UserProfileViewModel()
{
Profiles = [];
LostProfiles = [];
IsEmpty = !LostProfiles.Any();
Profiles = new ObservableCollection<BaseModel>();
LostProfiles = new ObservableCollection<UserProfile>();
LostProfiles.CollectionChanged += LostProfilesChanged;
}
public ObservableCollection<BaseModel> Profiles { get; set; }
public ObservableCollection<BaseModel> Profiles { get; }
public ObservableCollection<UserProfile> LostProfiles { get; set; }
public ObservableCollection<UserProfile> LostProfiles { get; }
public bool IsEmpty { get; set; }
public bool IsEmpty => LostProfiles.Count == 0;
public void Dispose()
{
GC.SuppressFinalize(this);
LostProfiles.CollectionChanged -= LostProfilesChanged;
}
private void LostProfilesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(IsEmpty));
}
public void UpdateLostProfiles(ObservableCollection<UserProfile> newProfiles)
{
LostProfiles.Clear();
foreach (var profile in newProfiles)
LostProfiles.Add(profile);
OnPropertyChanged(nameof(IsEmpty));
}
}
}

View file

@ -27,7 +27,7 @@ namespace Ryujinx.Ava.UI.Views.Dialog
{
PrimaryButtonText = string.Empty,
SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
Content = new AboutView { ViewModel = viewModel }
};

View file

@ -14,7 +14,7 @@
mc:Ignorable="d"
Focusable="True"
x:DataType="models:TempProfile">
<Grid Margin="0" ColumnDefinitions="Auto,*" RowDefinitions="*,Auto">
<Grid Margin="10" ColumnDefinitions="Auto,*" RowDefinitions="*,Auto">
<StackPanel
Grid.Row="0"
Grid.Column="0"
@ -48,9 +48,9 @@
BorderThickness="1">
<Panel>
<ui:SymbolIcon
FontSize="60"
Width="96"
Height="96"
FontSize="70"
Width="115"
Height="115"
Margin="0"
Foreground="{DynamicResource AppListHoverBackgroundColor}"
HorizontalAlignment="Stretch"
@ -58,8 +58,8 @@
Symbol="Camera" />
<Image
Name="ProfileImage"
Width="96"
Height="96"
Width="115"
Height="115"
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
@ -73,7 +73,7 @@
Grid.ColumnSpan="2"
HorizontalAlignment="Left"
Orientation="Horizontal"
Margin="0 24 0 0"
Margin="0,30,0,0"
Spacing="10">
<Button
Width="50"
@ -88,7 +88,7 @@
Grid.ColumnSpan="2"
HorizontalAlignment="Right"
Orientation="Horizontal"
Margin="0 24 0 0"
Margin="0,30,0,0"
Spacing="10">
<Button
Name="DeleteButton"
@ -108,7 +108,7 @@
<Button
Name="SaveButton"
Click="SaveButton_Click">
<TextBlock Text="{ext:Locale UserProfilesSetProfileImage}" />
<TextBlock Text="{ext:Locale Save}" />
</Button>
</StackPanel>
</Grid>

View file

@ -17,10 +17,9 @@ namespace Ryujinx.Ava.UI.Views.User
private NavigationDialogHost _parent;
private UserProfile _profile;
private bool _isNewUser;
public static uint MaxProfileNameLength => 0x20;
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
public string UserEditorTitle => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UserEditorTitle, _profile.Name);
public UserEditorView()
{
InitializeComponent();
@ -47,7 +46,7 @@ namespace Ryujinx.Ava.UI.Views.User
}
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleNewUser] : UserEditorTitle)}";
AddPictureButton.IsVisible = _isNewUser;
ChangePictureButton.IsVisible = !_isNewUser;

View file

@ -18,7 +18,7 @@
<viewModels:UserFirmwareAvatarSelectorViewModel />
</Design.DataContext>
<Grid
Margin="0"
Margin="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto,Auto">
<ListBox
@ -62,12 +62,12 @@
Grid.Row="3"
Orientation="Horizontal"
Spacing="10"
Margin="0 24 0 0"
Margin="0,30,0,0"
HorizontalAlignment="Left">
<Button
Width="50"
MinWidth="50"
Height="35"
Height="37"
Click="GoBack">
<ui:SymbolIcon Symbol="Back" />
</Button>
@ -76,7 +76,7 @@
Grid.Row="3"
Orientation="Horizontal"
Spacing="10"
Margin="0 24 0 0"
Margin="0,30,0,0"
HorizontalAlignment="Right">
<ui:ColorPickerButton
FlyoutPlacement="Top"
@ -96,9 +96,10 @@
</ui:ColorPickerButton>
<Button
Content="{ext:Locale AvatarChoose}"
Height="35"
Name="ChooseButton"
Click="ChooseButton_OnClick" />
Height="37"
Click="ChooseButton_OnClick">
<TextBlock Text="{ext:Locale AvatarChoose}" />
</Button>
</StackPanel>
</Grid>
</UserControl>

View file

@ -15,7 +15,7 @@
<Design.DataContext>
<viewModles:UserProfileImageSelectorViewModel />
</Design.DataContext>
<Grid
<Grid Margin="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" RowDefinitions="Auto,70,Auto">
<TextBlock

View file

@ -64,6 +64,7 @@ namespace Ryujinx.Ava.UI.Views.User
{
IReadOnlyList<IStorageFile> result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.LoadSupportedImageFormatDialogTitle],
AllowMultiple = false,
FileTypeFilter = new List<FilePickerFileType>
{
@ -73,6 +74,30 @@ namespace Ryujinx.Ava.UI.Views.User
AppleUniformTypeIdentifiers = ["public.jpeg", "public.png", "com.microsoft.bmp"],
MimeTypes = ["image/jpeg", "image/png", "image/bmp"],
},
new("JPG")
{
Patterns = ["*.jpg"],
AppleUniformTypeIdentifiers = ["public.jpeg"],
MimeTypes = ["image/jpeg"],
},
new("JPEG")
{
Patterns = ["*.jpeg"],
AppleUniformTypeIdentifiers = ["public.jpeg"],
MimeTypes = ["image/jpeg"],
},
new("PNG")
{
Patterns = ["*.png"],
AppleUniformTypeIdentifiers = ["public.png"],
MimeTypes = ["image/png"],
},
new("BMP")
{
Patterns = ["*.bmp"],
AppleUniformTypeIdentifiers = ["com.microsoft.bmp"],
MimeTypes = ["image/bmp"],
},
},
});

View file

@ -17,37 +17,49 @@
<Design.DataContext>
<viewModels:UserProfileViewModel />
</Design.DataContext>
<Grid HorizontalAlignment="Stretch"
<Grid Margin="10" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" RowDefinitions="*,Auto">
<Border
CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
BorderThickness="1"
Grid.Row="0">
Grid.Row="0"
Padding="2.5">
<Panel>
<ListBox
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding LostProfiles}">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="10" />
<Setter Property="Margin" Value="0" />
</Style>
<Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
<Setter Property="IsVisible" Value="False" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<Border
Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ClipToBounds="True"
CornerRadius="5">
CornerRadius="4">
<Grid Margin="0" ColumnDefinitions="*,Auto">
<TextBlock
HorizontalAlignment="Stretch"
Margin="5"
Text="{Binding UserId}"
TextAlignment="Start"
TextWrapping="Wrap" />
<Button Grid.Column="1"
HorizontalAlignment="Right"
Click="Recover"
CommandParameter="{Binding}"
Content="{ext:Locale Recover}"/>
Margin="5"
Command="{Binding Recover}"
CommandParameter="{Binding}">
<TextBlock Text="{ext:Locale Recover}" />
</Button>
</Grid>
</Border>
</DataTemplate>
@ -61,7 +73,7 @@
</Border>
<StackPanel
Grid.Row="1"
Margin="0 24 0 0"
Margin="0,30,0,0"
Orientation="Horizontal">
<Button
Width="50"

View file

@ -27,6 +27,7 @@ namespace Ryujinx.Ava.UI.Views.User
switch (arg.NavigationMode)
{
case NavigationMode.New:
case NavigationMode.Back:
NavigationDialogHost parent = (NavigationDialogHost)arg.Parameter;
_parent = parent;
@ -42,10 +43,5 @@ namespace Ryujinx.Ava.UI.Views.User
{
_parent?.GoBack();
}
private void Recover(object sender, RoutedEventArgs e)
{
_parent?.RecoverLostAccounts();
}
}
}

View file

@ -19,12 +19,12 @@
<Design.DataContext>
<viewModels:UserSaveManagerViewModel />
</Design.DataContext>
<Grid RowDefinitions="Auto,*,Auto">
<Grid Margin="10" RowDefinitions="Auto,*,Auto">
<Grid
Grid.Row="0"
Margin="0,0,0,5"
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*">
<StackPanel
Margin="0,0,0,10"
Spacing="10"
Orientation="Horizontal"
HorizontalAlignment="Left"
@ -59,18 +59,18 @@
<Grid
Grid.Column="1"
HorizontalAlignment="Stretch"
Margin="10,0, 0, 0" ColumnDefinitions="Auto,*">
<TextBlock Text="{ext:Locale Search}" VerticalAlignment="Center" />
Margin="20,0,0,10" ColumnDefinitions="Auto,*">
<TextBox
Margin="10,0,0,0"
Margin="5,0,0,0"
Grid.Column="1"
HorizontalAlignment="Stretch"
Text="{Binding Search}" />
Text="{Binding Search}"
Watermark="{ext:Locale Search}" />
</Grid>
</Grid>
<Border
Grid.Row="1"
Margin="0,5"
Padding="2.5"
BorderThickness="1"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
CornerRadius="5"
@ -84,7 +84,7 @@
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="10" />
<Setter Property="Margin" Value="5" />
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
@ -168,7 +168,7 @@
</Border>
<StackPanel
Grid.Row="2"
Margin="0 24 0 0"
Margin="0,30,0,0"
Orientation="Horizontal">
<Button
Width="50"

View file

@ -18,14 +18,11 @@
<Design.DataContext>
<viewModels:UserProfileViewModel />
</Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
<Border
CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
BorderThickness="1">
<Grid Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
<StackPanel>
<ListBox
MaxHeight="300"
HorizontalAlignment="Stretch"
HorizontalAlignment="Center"
VerticalAlignment="Center"
SelectionChanged="ProfilesList_SelectionChanged"
Background="Transparent"
@ -40,7 +37,7 @@
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Margin" Value="5 5 0 5" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="CornerRadius" Value="5" />
</Style>
<Style Selector="Rectangle#SelectionIndicator">
@ -54,6 +51,7 @@
PointerEntered="Grid_PointerEntered"
PointerExited="Grid_OnPointerExited">
<Border
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ClipToBounds="True"
@ -65,18 +63,20 @@
<Image
Width="96"
Height="96"
Margin="0,0,0,10"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
<TextBlock
HorizontalAlignment="Stretch"
MaxWidth="90"
Text="{Binding Name}"
Height="30"
MaxWidth="90"
TextAlignment="Center"
TextWrapping="Wrap"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"
MaxLines="2"
Margin="5" />
TextWrapping="Wrap"
MaxLines="2" />
</StackPanel>
</Border>
<Border
@ -104,8 +104,8 @@
<DataTemplate
DataType="viewModels:BaseModel">
<Panel
Height="118"
Width="96">
Height="120"
Width="100">
<Button
MinWidth="50"
MinHeight="50"
@ -119,19 +119,14 @@
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>
<StackPanel
Grid.Row="1"
Margin="0 24 0 0"
Margin="10,30,0,0"
HorizontalAlignment="Left"
Orientation="Horizontal"
Spacing="10">
@ -146,12 +141,12 @@
</StackPanel>
<StackPanel
Grid.Row="1"
Margin="0 24 0 0"
Margin="0,30,0,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Click="Close">
<TextBlock Text="{ext:Locale UserProfilesClose}" />
<TextBlock Text="{ext:Locale SettingsButtonClose}" />
</Button>
</StackPanel>
</Grid>