Merge branch 'ui-actions-new' into 'master'

UI: Actions Menu

See merge request [ryubing/ryujinx!232](https://git.ryujinx.app/ryubing/ryujinx/-/merge_requests/232)
This commit is contained in:
Neo 2025-12-09 08:56:11 -06:00
commit b259baf2d7
11 changed files with 1268 additions and 1378 deletions

File diff suppressed because it is too large Load diff

View file

@ -105,8 +105,8 @@
CommandParameter="{Binding}" CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuTrimXCI}" Header="{ext:Locale GameListContextMenuTrimXCI}"
IsEnabled="{Binding TrimXCIEnabled}" IsEnabled="{Binding TrimXCIEnabled}"
Icon="{ext:Icon fa-solid fa-scissors}" IsVisible="{Binding IsXCIFile}"
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" /> Icon="{ext:Icon fa-solid fa-scissors}" />
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon fa-solid fa-memory}"> <MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon fa-solid fa-memory}">
<MenuItem <MenuItem
Command="{Binding PurgePtcCache}" Command="{Binding PurgePtcCache}"

View file

@ -317,40 +317,43 @@ namespace Ryujinx.Ava.UI.ViewModels
_amiiboSeries.Clear(); _amiiboSeries.Clear();
_amiibos.Clear(); _amiibos.Clear();
for (int i = 0; i < _amiiboList.Count; i++) foreach (var amiibo in _amiiboList)
{ {
if (!_amiiboSeries.Contains(_amiiboList[i].AmiiboSeries)) if (!_amiiboSeries.Contains(amiibo.AmiiboSeries))
{ {
if (!ShowAllAmiibo) if (!ShowAllAmiibo)
{ {
foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) bool compatible = amiibo.GamesSwitch.Any(game => game != null && game.GameId.Contains(TitleId));
if (compatible)
{ {
if (game != null) _amiiboSeries.Add(amiibo.AmiiboSeries);
{
if (game.GameId.Contains(TitleId))
{
AmiiboSeries.Add(_amiiboList[i].AmiiboSeries);
break;
}
}
} }
} }
else else
{ {
AmiiboSeries.Add(_amiiboList[i].AmiiboSeries); _amiiboSeries.Add(amiibo.AmiiboSeries);
} }
} }
} }
if (LastScannedAmiiboId != string.Empty) if (ShowAllAmiibo && _amiiboSeries.Count > 0)
{
SelectLastScannedAmiibo();
}
else
{ {
SeriesSelectedIndex = 0; SeriesSelectedIndex = 0;
} }
else if (LastScannedAmiiboId != string.Empty)
{
SelectLastScannedAmiibo();
}
else if (_amiiboSeries.Count > 0)
{
SeriesSelectedIndex = 0;
}
else
{
SeriesSelectedIndex = -1;
}
FilterAmiibo();
} }
private void SelectLastScannedAmiibo() private void SelectLastScannedAmiibo()
@ -367,50 +370,51 @@ namespace Ryujinx.Ava.UI.ViewModels
List<AmiiboApi> amiiboSortedList; List<AmiiboApi> amiiboSortedList;
// If no series selected, just display all available amiibos
if (_seriesSelectedIndex < 0) if (_seriesSelectedIndex < 0)
{ {
amiiboSortedList = _amiiboList amiiboSortedList = _amiiboList
.OrderBy(amiibo => amiibo.AmiiboSeries) .OrderBy(amiibo => amiibo.AmiiboSeries)
.ThenBy(x => x.Name) .ThenBy(amiibo => amiibo.Name)
.ToList(); .ToList();
} }
else else
{ {
string selectedSeries = _amiiboSeries[SeriesSelectedIndex];
amiiboSortedList = _amiiboList amiiboSortedList = _amiiboList
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex]) .Where(amiibo => amiibo.AmiiboSeries == selectedSeries)
.OrderBy(amiibo => amiibo.Name).ToList(); .OrderBy(amiibo => amiibo.Name)
.ToList();
} }
foreach (var amiibo in amiiboSortedList)
for (int i = 0; i < amiiboSortedList.Count; i++)
{
if (!_amiibos.Contains(amiiboSortedList[i]))
{ {
if (!_showAllAmiibo) if (!_showAllAmiibo)
{ {
foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) bool compatible = amiibo.GamesSwitch.Any(game => game != null && game.GameId.Contains(TitleId));
{
if (game != null)
{
if (game.GameId.Contains(TitleId))
{
_amiibos.Add(amiiboSortedList[i]);
break; if (compatible)
} {
} _amiibos.Add(amiibo);
} }
} }
else else
{ {
_amiibos.Add(amiiboSortedList[i]); _amiibos.Add(amiibo);
}
} }
} }
AmiiboSelectedIndex = 0; int restoredIndex = -1;
for (int i = 0; i < _amiibos.Count; i++)
{
if (_amiibos[i].GetId() == LastScannedAmiiboId)
{
restoredIndex = i;
break;
}
}
AmiiboSelectedIndex = restoredIndex != -1 ? restoredIndex : (_amiibos.Count > 0 ? 0 : -1);
} }
private void SetAmiiboDetails() private void SetAmiiboDetails()
@ -420,31 +424,21 @@ namespace Ryujinx.Ava.UI.ViewModels
Usage = string.Empty; Usage = string.Empty;
if (_amiiboSelectedIndex < 0 || _amiibos.Count < 1) if (_amiiboSelectedIndex < 0 || _amiibos.Count < 1)
{
return; return;
}
AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image; string imageUrl = selected.Image;
StringBuilder usageStringBuilder = new(); StringBuilder usageStringBuilder = new();
for (int i = 0; i < _amiiboList.Count; i++) foreach (var game in selected.GamesSwitch)
{ {
if (_amiiboList[i].Equals(selected)) if (game != null && game.GameId.Contains(TitleId))
{ {
bool writable = false; foreach (var usageItem in game.AmiiboUsage)
foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
{
if (item.GameId.Contains(TitleId))
{
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
{ {
usageStringBuilder.Append($"{Environment.NewLine}- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"); usageStringBuilder.Append($"{Environment.NewLine}- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}");
writable = usageItem.Write;
} }
} }
} }
@ -454,9 +448,7 @@ namespace Ryujinx.Ava.UI.ViewModels
usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}."); usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
} }
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : string.Empty)} : {usageStringBuilder}"; Usage = $"{LocaleManager.Instance[LocaleKeys.AmiiboUsage]}{usageStringBuilder}";
}
}
_ = UpdateAmiiboPreview(imageUrl); _ = UpdateAmiiboPreview(imageUrl);
} }

View file

@ -457,6 +457,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo; public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo;
public bool IsXCIFile => Path.GetExtension(SelectedApplication.Path)?.ToLower() == ".xci";
public bool HasDlc => ApplicationLibrary.HasDlcs(SelectedApplication.Id); public bool HasDlc => ApplicationLibrary.HasDlcs(SelectedApplication.Id);
public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder &&
@ -925,7 +927,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
dialogMessage += dialogMessage +=
LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareAndKeysInstallConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
dialogTitle, dialogTitle,
@ -1025,7 +1027,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
string dialogTitle = string dialogTitle =
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallTitle); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.MenuBarActionsInstallKeys);
string dialogMessage = string dialogMessage =
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage);
@ -1036,7 +1038,7 @@ namespace Ryujinx.Ava.UI.ViewModels
.DialogKeysInstallerKeysInstallSubMessage); .DialogKeysInstallerKeysInstallSubMessage);
} }
dialogMessage += LocaleManager.Instance[LocaleKeys.DialogKeysInstallerKeysInstallConfirmMessage]; dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareAndKeysInstallConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
dialogTitle, dialogTitle,
@ -1357,9 +1359,10 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.InstallFirmwareFromFileDialogTitle],
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
{ {
new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes]) new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
{ {
Patterns = ["*.xci", "*.zip"], Patterns = ["*.xci", "*.zip"],
AppleUniformTypeIdentifiers = ["com.ryujinx.xci", "public.zip-archive"], AppleUniformTypeIdentifiers = ["com.ryujinx.xci", "public.zip-archive"],
@ -1388,7 +1391,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task InstallFirmwareFromFolder() public async Task InstallFirmwareFromFolder()
{ {
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(); Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.InstallFirmwareFromFolderDialogTitle]
});
if (result.HasValue) if (result.HasValue)
{ {
@ -1400,6 +1406,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.InstallKeysFromFileDialogTitle],
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
{ {
new("KEYS") new("KEYS")
@ -1419,7 +1426,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task InstallKeysFromFolder() public async Task InstallKeysFromFolder()
{ {
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(); Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.InstallKeysFromFolderDialogTitle]
});
if (result.HasValue) if (result.HasValue)
{ {
@ -1841,17 +1851,35 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public async Task OpenBinFile() public async Task OpenCheatManagerForCurrentApp()
{
if (IsGameRunning)
{
string name = AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await StyleableAppWindow.ShowAsync(
new CheatWindow(
Window.VirtualFileSystem,
AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
SelectedApplication.Path)
);
AppHost.Device.EnableCheats();
}
}
public async Task OpenAmiiboBinFile()
{ {
if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning) if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning)
{ {
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync( Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(
new FilePickerOpenOptions new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle], Title = LocaleManager.Instance[LocaleKeys.OpenAmiiboBinFileDialogTitle],
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
{ {
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats]) new("BIN")
{ {
Patterns = ["*.bin"], Patterns = ["*.bin"],
} }
@ -2015,11 +2043,10 @@ namespace Ryujinx.Ava.UI.ViewModels
if (trimmer.CanBeTrimmed) if (trimmer.CanBeTrimmed)
{ {
double savings = (double)trimmer.DiskSpaceSavingsB / 1024.0 / 1024.0; int savings = (int)Math.Round((double)trimmer.DiskSpaceSavingsB / 1024.0 / 1024.0);
double currentFileSize = (double)trimmer.FileSizeB / 1024.0 / 1024.0; int currentFileSize = (int)Math.Round((double)trimmer.FileSizeB / 1024.0 / 1024.0);
double cartDataSize = (double)trimmer.DataSizeB / 1024.0 / 1024.0; int cartDataSize = (int)Math.Round((double)trimmer.DataSizeB / 1024.0 / 1024.0);
string secondaryText = LocaleManager.Instance.UpdateAndGetDynamicValue( string secondaryText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TrimXCIFileDialogSecondaryText, currentFileSize.ToString("0"), cartDataSize.ToString("0"), savings.ToString("0"));
LocaleKeys.TrimXCIFileDialogSecondaryText, currentFileSize, cartDataSize, savings);
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogPrimaryText], LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogPrimaryText],
@ -2037,7 +2064,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
StatusBarProgressStatusText = StatusBarProgressStatusText =
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarXCIFileTrimming, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarXCIFileTrimming,
Path.GetFileName(filename)); Path.GetFileNameWithoutExtension(filename));
StatusBarProgressStatusVisible = true; StatusBarProgressStatusVisible = true;
StatusBarProgressMaximum = 1; StatusBarProgressMaximum = 1;
StatusBarProgressValue = 0; StatusBarProgressValue = 0;

View file

@ -115,6 +115,7 @@ namespace Ryujinx.Ava.UI.ViewModels
nameof(PotentialSavings), nameof(PotentialSavings),
nameof(ActualSavings), nameof(ActualSavings),
nameof(CanTrim), nameof(CanTrim),
nameof(SavingsDifference),
nameof(CanUntrim)); nameof(CanUntrim));
DisplayedChanged(); DisplayedChanged();
@ -312,13 +313,14 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public void SelectDisplayed() public void SelectAll()
{ {
SelectedXCIFiles.Clear();
SelectedXCIFiles.AddRange(DisplayedXCIFiles); SelectedXCIFiles.AddRange(DisplayedXCIFiles);
SelectionChanged(); SelectionChanged();
} }
public void DeselectDisplayed() public void DeselectAll()
{ {
SelectedXCIFiles.RemoveMany(DisplayedXCIFiles); SelectedXCIFiles.RemoveMany(DisplayedXCIFiles);
SelectionChanged(); SelectionChanged();
@ -426,8 +428,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
return _processingMode switch return _processingMode switch
{ {
ProcessingMode.Trimming => string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusTrimming], DisplayedXCIFiles.Count), ProcessingMode.Trimming => string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusTrimming], SelectedXCIFiles.Count),
ProcessingMode.Untrimming => string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusUntrimming], DisplayedXCIFiles.Count), ProcessingMode.Untrimming => string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusUntrimming], SelectedXCIFiles.Count),
_ => string.Empty _ => string.Empty
}; };
} }
@ -529,6 +531,18 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string SavingsDifference
{
get
{
long potentialSavings = AllXCIFiles.Sum(xci => xci.PotentialSavingsB);
long actualSavings = AllXCIFiles.Sum(xci => xci.CurrentSavingsB);
long differenceMb = (potentialSavings - actualSavings) / BytesPerMb;
return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerCanStillSaveMB], differenceMb);
}
}
public IEnumerable<XCITrimmerFileModel> SelectedDisplayedXCIFiles public IEnumerable<XCITrimmerFileModel> SelectedDisplayedXCIFiles
{ {
get get

View file

@ -13,23 +13,40 @@
x:DataType="viewModels:XciTrimmerViewModel" x:DataType="viewModels:XciTrimmerViewModel"
Focusable="True" Focusable="True"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="20 0 20 0" RowDefinitions="Auto,Auto,*,Auto,Auto"> <Grid Margin="30,10,30,0" RowDefinitions="Auto,Auto,*,Auto,Auto">
<Panel <Panel
Margin="10 10 10 10" Margin="0,0,0,10"
Grid.Row="0"> Grid.Row="0">
<TextBlock Text="{Binding Status}" /> <TextBlock Text="{Binding Status}" />
</Panel> </Panel>
<Panel <Grid Margin="0,0,0,10" Grid.Row="1" IsVisible="{Binding !Processing}" ColumnDefinitions="Auto,*">
Margin="0 0 10 10"
IsVisible="{Binding !Processing}"
Grid.Row="1">
<Grid ColumnDefinitions="Auto,*,Auto">
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Orientation="Horizontal"> Orientation="Horizontal"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Button
Name="SelectAllButton"
MinWidth="90"
Margin="0,0,10,0"
Command="{Binding SelectAll}">
<TextBlock Text="{ext:Locale XCITrimmerSelectAll}" />
</Button>
<Button
Name="DeselectAllButton"
MinWidth="90"
Margin="0,0,0,0"
Command="{Binding DeselectAll}">
<TextBlock Text="{ext:Locale XCITrimmerDeselectAll}" />
</Button>
</StackPanel>
<StackPanel
Orientation="Horizontal"
Grid.Column="1"
HorizontalAlignment="Right">
<DropDownButton <DropDownButton
Width="150" Width="150"
HorizontalAlignment="Left" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
Content="{Binding SortingFieldName}"> Content="{Binding SortingFieldName}">
<DropDownButton.Flyout> <DropDownButton.Flyout>
@ -77,38 +94,20 @@
</Flyout> </Flyout>
</DropDownButton.Flyout> </DropDownButton.Flyout>
</DropDownButton> </DropDownButton>
</StackPanel>
<TextBox <TextBox
Grid.Column="1" Grid.Column="1"
MinHeight="29" Width="200"
MaxHeight="29" MaxWidth="200"
Margin="5 0 5 0" Margin="5,0,0,0"
HorizontalAlignment="Stretch" VerticalAlignment="Center"
HorizontalAlignment="Right"
Watermark="{ext:Locale Search}" Watermark="{ext:Locale Search}"
Text="{Binding Search}" /> Text="{Binding Search}" />
<StackPanel
Grid.Column="2"
Orientation="Horizontal">
<Button
Name="SelectDisplayedButton"
MinWidth="90"
Margin="5"
Command="{Binding SelectDisplayed}">
<TextBlock Text="{ext:Locale XCITrimmerSelectDisplayed}" />
</Button>
<Button
Name="DeselectDisplayedButton"
MinWidth="90"
Margin="5"
Command="{Binding DeselectDisplayed}">
<TextBlock Text="{ext:Locale XCITrimmerDeselectDisplayed}" />
</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Panel>
<Border <Border
Grid.Row="2" Grid.Row="2"
Margin="0 0 0 10" Margin="0,0,0,20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
@ -124,14 +123,19 @@
SelectedItems="{Binding SelectedDisplayedXCIFiles, Mode=OneWay}" SelectedItems="{Binding SelectedDisplayedXCIFiles, Mode=OneWay}"
ItemsSource="{Binding DisplayedXCIFiles}" ItemsSource="{Binding DisplayedXCIFiles}"
IsEnabled="{Binding !Processing}"> IsEnabled="{Binding !Processing}">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="Transparent" />
</Style>
</ListBox.Styles>
<ListBox.DataTemplates> <ListBox.DataTemplates>
<DataTemplate <DataTemplate
DataType="models:XCITrimmerFileModel"> DataType="models:XCITrimmerFileModel">
<Panel Margin="10"> <Grid ColumnDefinitions="65*,50*">
<Grid ColumnDefinitions="65*,35*">
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"
Margin="10 0 10 0" Margin="10,0,10,0"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="2" MaxLines="2"
@ -139,10 +143,10 @@
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
Text="{Binding Name}"> Text="{Binding Name}">
</TextBlock> </TextBlock>
<Grid Grid.Column="1" ColumnDefinitions="45*,55*"> <Grid Grid.Column="1" ColumnDefinitions="50*,90*">
<ProgressBar <ProgressBar
Height="10" Height="10"
Margin="10 0 10 0" Margin="10,0,10,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center"
CornerRadius="5" CornerRadius="5"
@ -152,8 +156,8 @@
Value="{Binding PercentageProgress}" /> Value="{Binding PercentageProgress}" />
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"
Margin="10 0 10 0" Margin="10,0,10,0"
HorizontalAlignment="Left" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="1" MaxLines="1"
Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileStatusConverter.Instance}}"> Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileStatusConverter.Instance}}">
@ -175,71 +179,43 @@
</TextBlock> </TextBlock>
<TextBlock <TextBlock
Grid.Column="1" Grid.Column="1"
Margin="10 0 10 0" Margin="10,0,10,0"
HorizontalAlignment="Left" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="1" MaxLines="1"
Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileSpaceSavingsConverter.Instance}}">> Text="{Binding ., Converter={x:Static helpers:XCITrimmerFileSpaceSavingsConverter.Instance}}">
</TextBlock> </TextBlock>
</Grid> </Grid>
</Grid> </Grid>
</Panel>
</DataTemplate> </DataTemplate>
</ListBox.DataTemplates> </ListBox.DataTemplates>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Background" Value="Transparent" />
</Style>
</ListBox.Styles>
</ListBox> </ListBox>
</Border> </Border>
<Border <StackPanel Grid.Row="3" Margin="0,0,0,20" Spacing="5" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
Grid.Row="3"
Margin="0 0 0 10"
HorizontalAlignment="Stretch"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
BorderThickness="1"
CornerRadius="5"
Padding="2.5">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
<TextBlock <TextBlock
Grid.Column="0"
Grid.Row="0"
Classes="h1" Classes="h1"
Margin="5" HorizontalAlignment="Center"
HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="1" MaxLines="1"
Text="{ext:Locale XCITrimmerPotentialSavings}" /> Text="{ext:Locale XCITrimmerTotalSavings}" />
<TextBlock <TextBlock
Grid.Column="0"
Grid.Row="1"
Classes="h1" Classes="h1"
Margin="5" HorizontalAlignment="Center"
HorizontalAlignment="Right"
VerticalAlignment="Center"
MaxLines="1"
Text="{ext:Locale XCITrimmerActualSavings}" />
<TextBlock
Grid.Column="1"
Grid.Row="0"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
MaxLines="1"
Text="{Binding PotentialSavings}" />
<TextBlock
Grid.Column="1"
Grid.Row="1"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
FontWeight="Regular"
MaxLines="1" MaxLines="1"
Text="{Binding ActualSavings}" /> Text="{Binding ActualSavings}" />
</Grid> <TextBlock
</Border> Classes="h1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Regular"
MaxLines="1"
Text="{Binding SavingsDifference}" />
</StackPanel>
<Panel <Panel
Grid.Row="4" Grid.Row="4"
Margin="0,10,0,0"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Grid ColumnDefinitions="*,Auto"> <Grid ColumnDefinitions="*,Auto">
<StackPanel <StackPanel
@ -250,7 +226,6 @@
<Button <Button
Name="TrimButton" Name="TrimButton"
MinWidth="90" MinWidth="90"
Margin="5"
Click="Trim" Click="Trim"
IsEnabled="{Binding CanTrim}"> IsEnabled="{Binding CanTrim}">
<TextBlock Text="{ext:Locale XCITrimmerTrim}" /> <TextBlock Text="{ext:Locale XCITrimmerTrim}" />
@ -258,7 +233,6 @@
<Button <Button
Name="UntrimButton" Name="UntrimButton"
MinWidth="90" MinWidth="90"
Margin="5"
Click="Untrim" Click="Untrim"
IsEnabled="{Binding CanUntrim}"> IsEnabled="{Binding CanUntrim}">
<TextBlock Text="{ext:Locale XCITrimmerUntrim}" /> <TextBlock Text="{ext:Locale XCITrimmerUntrim}" />
@ -272,7 +246,6 @@
<Button <Button
Name="CancellingButton" Name="CancellingButton"
MinWidth="90" MinWidth="90"
Margin="5"
Click="Cancel" Click="Cancel"
IsEnabled="False"> IsEnabled="False">
<Button.IsVisible> <Button.IsVisible>
@ -286,7 +259,6 @@
<Button <Button
Name="CancelButton" Name="CancelButton"
MinWidth="90" MinWidth="90"
Margin="5"
Click="Cancel"> Click="Cancel">
<Button.IsVisible> <Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}"> <MultiBinding Converter="{x:Static BoolConverters.And}">
@ -299,10 +271,9 @@
<Button <Button
Name="CloseButton" Name="CloseButton"
MinWidth="90" MinWidth="90"
Margin="5"
Click="Close" Click="Close"
IsVisible="{Binding !Processing}"> IsVisible="{Binding !Processing}">
<TextBlock Text="{ext:Locale InputDialogClose}" /> <TextBlock Text="{ext:Locale SettingsButtonClose}" />
</Button> </Button>
</StackPanel> </StackPanel>
</Grid> </Grid>

View file

@ -29,7 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Dialog
{ {
ViewModel = new XciTrimmerViewModel(RyujinxApp.MainWindow.ViewModel) ViewModel = new XciTrimmerViewModel(RyujinxApp.MainWindow.ViewModel)
}, },
Title = LocaleManager.Instance[LocaleKeys.XCITrimmerWindowTitle] Title = LocaleManager.Instance[LocaleKeys.MenuBarActionsXCITrimmer]
}; };
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View file

@ -166,7 +166,7 @@
Icon="{ext:Icon fa-solid fa-stop}" Icon="{ext:Icon fa-solid fa-stop}"
InputGesture="Escape" InputGesture="Escape"
IsEnabled="{Binding IsGameRunning}" /> IsEnabled="{Binding IsGameRunning}" />
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" Icon="{ext:Icon fa-solid fa-sun}" /> <MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" Icon="{ext:Icon fa-solid fa-sun}" InputGesture="Ctrl+M" />
<Separator /> <Separator />
<MenuItem <MenuItem
Command="{Binding OpenAmiiboWindow}" Command="{Binding OpenAmiiboWindow}"
@ -176,7 +176,7 @@
InputGesture="Ctrl + A" InputGesture="Ctrl + A"
IsEnabled="{Binding IsAmiiboRequested}" /> IsEnabled="{Binding IsAmiiboRequested}" />
<MenuItem <MenuItem
Command="{Binding OpenBinFile}" Command="{Binding OpenAmiiboBinFile}"
AttachedToVisualTree="ScanBinAmiiboMenuItem_AttachedToVisualTree" AttachedToVisualTree="ScanBinAmiiboMenuItem_AttachedToVisualTree"
Header="{ext:Locale MenuBarActionsScanAmiiboBin}" Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
Icon="{ext:Icon fa-solid fa-cube}" Icon="{ext:Icon fa-solid fa-cube}"
@ -196,9 +196,10 @@
InputGesture="{Binding ShowUiKey}" InputGesture="{Binding ShowUiKey}"
IsEnabled="{Binding IsGameRunning}" /> IsEnabled="{Binding IsGameRunning}" />
<MenuItem <MenuItem
Name="CheatManagerMenuItem" Command="{Binding OpenCheatManagerForCurrentApp}"
Header="{ext:Locale GameListContextMenuManageCheat}" Header="{ext:Locale GameListContextMenuManageCheat}"
Icon="{ext:Icon fa-solid fa-code}" Icon="{ext:Icon fa-solid fa-code}"
InputGesture="Ctrl + C"
IsEnabled="{Binding IsGameRunning}" /> IsEnabled="{Binding IsGameRunning}" />
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarActions}" IsVisible="{Binding EnableNonGameRunningControls}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarActions}" IsVisible="{Binding EnableNonGameRunningControls}">
@ -217,8 +218,8 @@
<Separator /> <Separator />
<MenuItem Header="{ext:Locale MenuBarActionsTools}" Icon="{ext:Icon fa-solid fa-toolbox}"> <MenuItem Header="{ext:Locale MenuBarActionsTools}" Icon="{ext:Icon fa-solid fa-toolbox}">
<MenuItem <MenuItem
Name="MiiAppletMenuItem" Header="{ext:Locale MenuBarActionsOpenMiiEditor}" Icon="{ext:Icon fa-solid fa-face-grin-wide}" ToolTip.Tip="{ext:Locale MenuBarActionsOpenMiiEditorToolTip}" /> Name="MiiAppletMenuItem" Header="{ext:Locale MenuBarActionsOpenMiiEditor}" Icon="{ext:Icon fa-solid fa-face-grin-wide}" />
<MenuItem Name="XciTrimmerMenuItem" Header="{ext:Locale MenuBarActionsXCITrimmer}" Icon="{ext:Icon fa-solid fa-scissors}" /> <MenuItem Name="XciTrimmerMenuItem" Header="{ext:Locale MenuBarActionsXCITrimmer}" Icon="{ext:Icon fa-solid fa-scissors}" ToolTip.Tip="{ext:Locale MenuBarActionsXCITrimmerTooltip}" />
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}">

View file

@ -43,7 +43,6 @@ namespace Ryujinx.Ava.UI.Views.Main
PauseEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Pause()); PauseEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Pause());
ResumeEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Resume()); ResumeEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Resume());
StopEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted()); StopEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
CheatManagerMenuItem.Command = Commands.CreateSilentFail(OpenCheatManagerForCurrentApp);
InstallFileTypesMenuItem.Command = Commands.Create(InstallFileTypes); InstallFileTypesMenuItem.Command = Commands.Create(InstallFileTypes);
UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes); UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
XciTrimmerMenuItem.Command = Commands.Create(XciTrimmerView.Show); XciTrimmerMenuItem.Command = Commands.Create(XciTrimmerView.Show);
@ -166,7 +165,7 @@ namespace Ryujinx.Ava.UI.Views.Main
ViewModel.LoadConfigurableHotKeys(); ViewModel.LoadConfigurableHotKeys();
} }
public AppletMetadata MiiApplet => new(ViewModel.ContentManager, "miiEdit", 0x0100000000001009); public AppletMetadata MiiApplet => new(ViewModel.ContentManager, LocaleManager.Instance[LocaleKeys.MenuBarActionsOpenMiiEditor], 0x0100000000001009);
public async Task OpenMiiApplet() public async Task OpenMiiApplet()
{ {
@ -176,24 +175,6 @@ namespace Ryujinx.Ava.UI.Views.Main
await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
} }
public async Task OpenCheatManagerForCurrentApp()
{
if (!ViewModel.IsGameRunning)
return;
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await StyleableAppWindow.ShowAsync(
new CheatWindow(
Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
ViewModel.SelectedApplication.Path)
);
ViewModel.AppHost.Device.EnableCheats();
}
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{ {
if (sender is MenuItem) if (sender is MenuItem)

View file

@ -16,75 +16,77 @@
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Width="800" Width="800"
MinHeight="650" MinHeight="650"
Height="650" Height="700"
SizeToContent="Manual" SizeToContent="Manual"
MinWidth="600" MinWidth="600"
Focusable="True"> Focusable="True">
<Design.DataContext> <Design.DataContext>
<viewModels:AmiiboWindowViewModel /> <viewModels:AmiiboWindowViewModel />
</Design.DataContext> </Design.DataContext>
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <Grid Grid.Row="0" RowDefinitions="Auto,Auto,*,Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid Name="FlushControls" ColumnDefinitions="Auto,Auto" Grid.Row="1" Margin="-15">
<!-- UI FlushControls -->
<Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,Auto,Auto" Name="FlushControls">
<controls:RyujinxLogo <controls:RyujinxLogo
Grid.Column="0" Grid.Column="0"
HorizontalAlignment="Left" Margin="7,0,7,0"
VerticalAlignment="Top"
Margin="8, 8, 7, 0"
ToolTip.Tip="{ext:WindowTitle Amiibo}" /> ToolTip.Tip="{ext:WindowTitle Amiibo}" />
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="0, 0, 0, 20"> <StackPanel Grid.Column="1" Orientation="Horizontal">
<StackPanel Spacing="10" Orientation="Horizontal" HorizontalAlignment="Left"> <TextBlock Margin="8,0,0,0" VerticalAlignment="Center" Text="{ext:Locale AmiiboSeriesLabel}" />
<TextBlock VerticalAlignment="Center" Text="{ext:Locale AmiiboSeriesLabel}" /> <ComboBox Margin="5,5,0,5" SelectedIndex="{Binding SeriesSelectedIndex}" ItemsSource="{Binding AmiiboSeries}" MinWidth="100" />
<ComboBox Margin="0, 8, 0, 0" SelectedIndex="{Binding SeriesSelectedIndex}" ItemsSource="{Binding AmiiboSeries}" MinWidth="100" />
</StackPanel>
</StackPanel> </StackPanel>
<CheckBox Grid.Column="2" Margin="15,0,0,0" IsChecked="{Binding ShowAllAmiibo}">
<TextBlock Text="{ext:Locale AmiiboOptionsShowAllLabel}" />
</CheckBox>
<CheckBox Grid.Column="3" Margin="15,0,0,0" HotKey="H" IsChecked="{Binding UseRandomUuid}" >
<TextBlock Text="{ext:Locale AmiiboOptionsUsRandomTagLabel}" />
</CheckBox>
</Grid> </Grid>
<Grid Name="NormalControls" ColumnDefinitions="*,*" Grid.Row="1" HorizontalAlignment="Stretch" >
<StackPanel Spacing="10" Orientation="Horizontal" HorizontalAlignment="Left"> <!-- UI NormalControls -->
<TextBlock VerticalAlignment="Center" Text="{ext:Locale AmiiboSeriesLabel}" /> <Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,Auto" Name="NormalControls">
<ComboBox SelectedIndex="{Binding SeriesSelectedIndex}" ItemsSource="{Binding AmiiboSeries}" MinWidth="100" /> <StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Margin="15,0,0,0" VerticalAlignment="Center" Text="{ext:Locale AmiiboSeriesLabel}" />
<ComboBox Margin="5,5,0,5" SelectedIndex="{Binding SeriesSelectedIndex}" ItemsSource="{Binding AmiiboSeries}" MinWidth="100" />
</StackPanel> </StackPanel>
<CheckBox Grid.Column="1" Margin="15,0,0,0" IsChecked="{Binding ShowAllAmiibo}">
<TextBlock Text="{ext:Locale AmiiboOptionsShowAllLabel}" />
</CheckBox>
<CheckBox Grid.Column="2" Margin="15,0,0,0" IsChecked="{Binding UseRandomUuid}" HotKey="H">
<TextBlock Text="{ext:Locale AmiiboOptionsUsRandomTagLabel}" />
</CheckBox>
</Grid> </Grid>
<DockPanel Grid.Row="2">
<!-- Amiibo ListBox, Images, Buttons -->
<StackPanel Grid.Row="2" Margin="0,20,0,20" Orientation="Horizontal" HorizontalAlignment="Center" Spacing="50">
<ListBox <ListBox
DockPanel.Dock="Left" Width="250"
Width="300" Padding="5"
Margin="20"
SelectionMode="Single" SelectionMode="Single"
ItemsSource="{Binding AmiiboList}" ItemsSource="{Binding AmiiboList}"
SelectedIndex="{Binding AmiiboSelectedIndex}"> SelectedIndex="{Binding AmiiboSelectedIndex}">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Margin="10,0,0,0" Text="{Binding Name}" /> <TextBlock Margin="10,0,0,0" Text="{Binding Name}" />
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
<StackPanel Margin="20"> <StackPanel Width="250">
<Image Source="{Binding AmiiboImage}" Height="350" Width="150" HorizontalAlignment="Center" /> <Image Source="{Binding AmiiboImage}" Height="400" Width="200" />
<ScrollViewer MaxHeight="120" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" <ScrollViewer MaxHeight="120" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Margin="20" VerticalAlignment="Top" HorizontalAlignment="Stretch">
Margin="20" VerticalAlignment="Top" HorizontalAlignment="Stretch"> <TextBlock TextWrapping="Wrap" Text="{Binding Usage}" HorizontalAlignment="Center" TextAlignment="Center" />
<TextBlock TextWrapping="Wrap" Text="{Binding Usage}" HorizontalAlignment="Center"
TextAlignment="Center" />
</ScrollViewer> </ScrollViewer>
</StackPanel> </StackPanel>
</DockPanel> </StackPanel>
<Grid Grid.Row="3" ColumnDefinitions="Auto,Auto,*,Auto,Auto"> <Grid Grid.Row="3" Margin="0,0,10,10" ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<CheckBox Margin="10" Grid.Column="0" VerticalContentAlignment="Center"
IsChecked="{Binding ShowAllAmiibo}"
Content="{ext:Locale AmiiboOptionsShowAllLabel}" />
<CheckBox HotKey="H"
Margin="10" VerticalContentAlignment="Center" Grid.Column="1"
IsChecked="{Binding UseRandomUuid}"
Content="{ext:Locale AmiiboOptionsUsRandomTagLabel}" />
<Button Grid.Column="3" <Button Grid.Column="3"
IsEnabled="{Binding EnableScanning}" IsEnabled="{Binding EnableScanning}"
Width="80"
Name="ScanButton" Name="ScanButton"
HotKey="Return" HotKey="Return"
Content="{ext:Locale AmiiboScanButtonLabel}" Content="{ext:Locale AmiiboScanButtonLabel}"
Click="ScanButton_Click" /> Click="ScanButton_Click" />
<Button Grid.Column="4" <Button Grid.Column="4"
Margin="10,0" Margin="10,0,0,0"
Width="80" Width="80"
Name="CancelButton" Name="CancelButton"
HotKey="Escape" HotKey="Escape"

View file

@ -40,7 +40,9 @@
<KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" /> <KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" />
<KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" /> <KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" />
<KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" /> <KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" />
<KeyBinding Gesture="Ctrl+B" Command="{Binding OpenBinFile}" /> <KeyBinding Gesture="Ctrl+B" Command="{Binding OpenAmiiboBinFile}" />
<KeyBinding Gesture="Ctrl+C" Command="{Binding OpenCheatManagerForCurrentApp}" />
<KeyBinding Gesture="Ctrl+M" Command="{Binding SimulateWakeUpMessage}" />
</Window.KeyBindings> </Window.KeyBindings>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*">
<helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" /> <helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" />