Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
31b8d413d5 | |||
6e02cac952 | |||
3a3380fa25 | |||
2d252db0a7 | |||
7f8a3541eb | |||
b34de74f81 | |||
5811d121df | |||
6eb85e846f | |||
c5bddfeab8 | |||
70ec5def9c |
@ -55,7 +55,6 @@ namespace Ryujinx.Ava
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
(desktop.MainWindow as MainWindow).SettingsWindow,
|
||||
LocaleManager.Instance["DialogThemeRestartMessage"],
|
||||
LocaleManager.Instance["DialogThemeRestartSubMessage"],
|
||||
LocaleManager.Instance["InputDialogYes"],
|
||||
|
@ -417,10 +417,12 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
if (userError == UserError.NoFirmware)
|
||||
{
|
||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString);
|
||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"],
|
||||
firmwareVersion.VersionString);
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(_parent,
|
||||
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], message, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], "");
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], message,
|
||||
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], "");
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
@ -450,12 +452,12 @@ namespace Ryujinx.Ava
|
||||
|
||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString);
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(_parent,
|
||||
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
|
||||
message,
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
"",
|
||||
LocaleManager.Instance["RyujinxInfo"]);
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
|
||||
message,
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
"",
|
||||
LocaleManager.Instance["RyujinxInfo"]);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -879,7 +881,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
|
||||
_dialogShown = true;
|
||||
shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(_parent);
|
||||
shouldExit = await ContentDialogHelper.CreateStopEmulationDialog();
|
||||
|
||||
_dialogShown = false;
|
||||
}
|
||||
@ -896,7 +898,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
_parent.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default;
|
||||
_parent.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default;
|
||||
});
|
||||
}
|
||||
else
|
||||
|
@ -118,7 +118,7 @@
|
||||
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||
"SettingsTabSystemHacks": "Hacks",
|
||||
"SettingsTabSystemHacksNote": " - These may cause instabilities",
|
||||
"SettingsTabSystemHacksNote": " (may cause instability)",
|
||||
"SettingsTabSystemExpandDramSize": "Expand DRAM Size to 6GB",
|
||||
"SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services",
|
||||
"SettingsTabGraphics": "Graphics",
|
||||
@ -256,8 +256,8 @@
|
||||
"UserProfilesSaveProfileName": "Save Profile Name",
|
||||
"UserProfilesChangeProfileImage": "Change Profile Image",
|
||||
"UserProfilesAvailableUserProfiles": "Available User Profiles:",
|
||||
"UserProfilesAddNewProfile": "Add New Profile",
|
||||
"UserProfilesDeleteSelectedProfile": "Delete Selected Profile",
|
||||
"UserProfilesAddNewProfile": "Create Profile",
|
||||
"UserProfilesDeleteSelectedProfile": "Delete Selected",
|
||||
"UserProfilesClose": "Close",
|
||||
"ProfileImageSelectionTitle": "Profile Image Selection",
|
||||
"ProfileImageSelectionHeader": "Choose a profile Image",
|
||||
@ -568,5 +568,12 @@
|
||||
"UpdateWindowTitle": "Manage Game Updates",
|
||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||
"DlcWindowHeading": "DLC Available for {0} [{1}]",
|
||||
"UserProfilesEditProfile": "Edit Selected",
|
||||
"Cancel": "Cancel",
|
||||
"Save": "Save",
|
||||
"Discard": "Discard",
|
||||
"UserProfilesSetProfileImage": "Set Profile Image",
|
||||
"UserProfileEmptyNameError": "Name is required",
|
||||
"UserProfileNoImageError": "Profile image must be set",
|
||||
"GameUpdateWindowHeading": "Updates Available for {0} [{1}]"
|
||||
}
|
||||
|
@ -81,7 +81,6 @@ namespace Ryujinx.Ava.Common
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
_owner,
|
||||
string.Format(LocaleManager.Instance["DialogMessageCreateSaveErrorMessage"], result.ToStringWithName()));
|
||||
});
|
||||
|
||||
@ -101,8 +100,7 @@ namespace Ryujinx.Ava.Common
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner,
|
||||
string.Format(LocaleManager.Instance["DialogMessageFindSaveErrorMessage"], result.ToStringWithName()));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageFindSaveErrorMessage"], result.ToStringWithName()));
|
||||
});
|
||||
|
||||
return false;
|
||||
@ -161,7 +159,6 @@ namespace Ryujinx.Ava.Common
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
_owner,
|
||||
string.Format(LocaleManager.Instance["DialogNcaExtractionMessage"], ncaSectionType, Path.GetFileName(titleFilePath)),
|
||||
"",
|
||||
"",
|
||||
@ -232,7 +229,7 @@ namespace Ryujinx.Ava.Common
|
||||
"Extraction failure. The main NCA was not present in the selected file");
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, LocaleManager.Instance["DialogNcaExtractionMainNcaNotFoundErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionMainNcaNotFoundErrorMessage"]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -273,7 +270,7 @@ namespace Ryujinx.Ava.Common
|
||||
$"LibHac returned error code: {resultCode.Value.ErrorCode}");
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, LocaleManager.Instance["DialogNcaExtractionCheckLogErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionCheckLogErrorMessage"]);
|
||||
});
|
||||
}
|
||||
else if (resultCode.Value.IsSuccess())
|
||||
@ -281,7 +278,6 @@ namespace Ryujinx.Ava.Common
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
_owner,
|
||||
LocaleManager.Instance["DialogNcaExtractionSuccessMessage"],
|
||||
"",
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
@ -298,7 +294,7 @@ namespace Ryujinx.Ava.Common
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, ex.Message);
|
||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ namespace Ryujinx.Modules
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(mainWindow, LocaleManager.Instance["DialogUpdaterConvertFailedMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]);
|
||||
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -111,7 +111,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(mainWindow, LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
|
||||
});
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(mainWindow, LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
|
||||
});
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ namespace Ryujinx.Modules
|
||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(mainWindow, LocaleManager.Instance["DialogUpdaterFailedToGetVersionMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdaterFailedToGetVersionMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -157,7 +157,7 @@ namespace Ryujinx.Modules
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(mainWindow, LocaleManager.Instance["DialogUpdaterConvertFailedGithubMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]);
|
||||
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedGithubMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -169,7 +169,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(mainWindow, LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], "");
|
||||
});
|
||||
}
|
||||
|
||||
@ -550,7 +550,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(parent, LocaleManager.Instance["DialogUpdaterArchNotSupportedMessage"],
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterArchNotSupportedMessage"],
|
||||
LocaleManager.Instance["DialogUpdaterArchNotSupportedSubMessage"]);
|
||||
}
|
||||
|
||||
@ -561,7 +561,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(parent, LocaleManager.Instance["DialogUpdaterNoInternetMessage"],
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterNoInternetMessage"],
|
||||
LocaleManager.Instance["DialogUpdaterNoInternetSubMessage"]);
|
||||
}
|
||||
|
||||
@ -572,7 +572,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(parent, LocaleManager.Instance["DialogUpdaterDirtyBuildMessage"],
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterDirtyBuildMessage"],
|
||||
LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]);
|
||||
}
|
||||
|
||||
@ -585,13 +585,11 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (ReleaseInformations.IsFlatHubBuild())
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(parent,
|
||||
LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterFlatpakNotSupportedMessage"]);
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterFlatpakNotSupportedMessage"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(parent,
|
||||
LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]);
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,9 +26,10 @@
|
||||
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||
<PackageReference Include="DynamicData" Version="7.9.4" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
|
||||
|
||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build9" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
|
||||
<PackageReference Include="SPB" Version="0.0.4-build17" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.Ava.Ui.Applet
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_parent, string.Format(LocaleManager.Instance["DialogMessageDialogErrorExceptionMessage"], ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageDialogErrorExceptionMessage"], ex));
|
||||
|
||||
dialogCloseEvent.Set();
|
||||
}
|
||||
@ -126,7 +126,7 @@ namespace Ryujinx.Ava.Ui.Applet
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = true;
|
||||
await ContentDialogHelper.CreateErrorDialog(_parent, string.Format(LocaleManager.Instance["DialogSoftwareKeyboardErrorExceptionMessage"], ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogSoftwareKeyboardErrorExceptionMessage"], ex));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -181,7 +181,7 @@ namespace Ryujinx.Ava.Ui.Applet
|
||||
catch (Exception ex)
|
||||
{
|
||||
dialogCloseEvent.Set();
|
||||
await ContentDialogHelper.CreateErrorDialog(_parent, string.Format(LocaleManager.Instance["DialogErrorAppletErrorExceptionMessage"], ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogErrorAppletErrorExceptionMessage"], ex));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Applet
|
||||
{
|
||||
internal class ErrorAppletWindow : StyleableWindow
|
||||
internal partial class ErrorAppletWindow : StyleableWindow
|
||||
{
|
||||
private readonly Window _owner;
|
||||
private object _buttonResponse;
|
||||
@ -50,8 +50,6 @@ namespace Ryujinx.Ava.Ui.Applet
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public StackPanel ButtonStack { get; set; }
|
||||
|
||||
private void AddButton(string label, object tag)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
@ -79,11 +77,5 @@ namespace Ryujinx.Ava.Ui.Applet
|
||||
|
||||
return _buttonResponse;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
ButtonStack = this.FindControl<StackPanel>("ButtonStack");
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
internal class SwkbdAppletDialog : UserControl
|
||||
internal partial class SwkbdAppletDialog : UserControl
|
||||
{
|
||||
private Predicate<int> _checkLength;
|
||||
private int _inputMax;
|
||||
@ -30,6 +30,10 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
_placeholder = placeholder;
|
||||
InitializeComponent();
|
||||
|
||||
Input.Watermark = _placeholder;
|
||||
|
||||
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
||||
|
||||
SetInputLengthValidation(0, int.MaxValue); // Disable by default.
|
||||
}
|
||||
|
||||
@ -43,23 +47,9 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
public string MainText { get; set; } = "";
|
||||
public string SecondaryText { get; set; } = "";
|
||||
|
||||
public TextBlock Error { get; private set; }
|
||||
public TextBox Input { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
Error = this.FindControl<TextBlock>("Error");
|
||||
Input = this.FindControl<TextBox>("Input");
|
||||
|
||||
Input.Watermark = _placeholder;
|
||||
|
||||
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
||||
}
|
||||
|
||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(StyleableWindow window, string title, SoftwareKeyboardUiArgs args)
|
||||
{
|
||||
ContentDialog contentDialog = window.ContentDialog;
|
||||
ContentDialog contentDialog = new ContentDialog();
|
||||
|
||||
UserResult result = UserResult.Cancel;
|
||||
|
||||
|
@ -16,7 +16,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
private static bool _isChoiceDialogOpen;
|
||||
|
||||
private async static Task<UserResult> ShowContentDialog(
|
||||
StyleableWindow window,
|
||||
string title,
|
||||
string primaryText,
|
||||
string secondaryText,
|
||||
@ -28,35 +27,32 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
UserResult result = UserResult.None;
|
||||
|
||||
ContentDialog contentDialog = window.ContentDialog;
|
||||
ContentDialog contentDialog = new ContentDialog();
|
||||
|
||||
await ShowDialog();
|
||||
|
||||
async Task ShowDialog()
|
||||
{
|
||||
if (contentDialog != null)
|
||||
contentDialog.Title = title;
|
||||
contentDialog.PrimaryButtonText = primaryButton;
|
||||
contentDialog.SecondaryButtonText = secondaryButton;
|
||||
contentDialog.CloseButtonText = closeButton;
|
||||
contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
|
||||
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
contentDialog.Title = title;
|
||||
contentDialog.PrimaryButtonText = primaryButton;
|
||||
contentDialog.SecondaryButtonText = secondaryButton;
|
||||
contentDialog.CloseButtonText = closeButton;
|
||||
contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
|
||||
result = primaryButtonResult;
|
||||
});
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.No;
|
||||
});
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.Cancel;
|
||||
});
|
||||
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = primaryButtonResult;
|
||||
});
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.No;
|
||||
});
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.Cancel;
|
||||
});
|
||||
|
||||
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
||||
};
|
||||
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -78,35 +74,30 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
|
||||
UserResult result = UserResult.None;
|
||||
|
||||
ContentDialog contentDialog = window.ContentDialog;
|
||||
|
||||
Window overlay = window;
|
||||
|
||||
if (contentDialog != null)
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
contentDialog.PrimaryButtonClick += DeferClose;
|
||||
contentDialog.Title = title;
|
||||
contentDialog.PrimaryButtonText = primaryButton;
|
||||
contentDialog.SecondaryButtonText = secondaryButton;
|
||||
contentDialog.CloseButtonText = closeButton;
|
||||
contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
|
||||
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
Title = title,
|
||||
PrimaryButtonText = primaryButton,
|
||||
SecondaryButtonText = secondaryButton,
|
||||
CloseButtonText = closeButton,
|
||||
Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol),
|
||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
|
||||
});
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
contentDialog.PrimaryButtonClick -= DeferClose;
|
||||
result = UserResult.No;
|
||||
});
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
contentDialog.PrimaryButtonClick -= DeferClose;
|
||||
result = UserResult.Cancel;
|
||||
});
|
||||
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
||||
}),
|
||||
};
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
contentDialog.PrimaryButtonClick -= DeferClose;
|
||||
result = UserResult.No;
|
||||
});
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
contentDialog.PrimaryButtonClick -= DeferClose;
|
||||
result = UserResult.Cancel;
|
||||
});
|
||||
contentDialog.PrimaryButtonClick += DeferClose;
|
||||
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
||||
|
||||
return result;
|
||||
|
||||
@ -141,7 +132,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
|
||||
if (doWhileDeferred != null)
|
||||
{
|
||||
await doWhileDeferred(overlay);
|
||||
await doWhileDeferred(window);
|
||||
|
||||
deferResetEvent.Set();
|
||||
}
|
||||
@ -191,7 +182,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
}
|
||||
|
||||
public static async Task<UserResult> CreateInfoDialog(
|
||||
StyleableWindow window,
|
||||
string primary,
|
||||
string secondaryText,
|
||||
string acceptButton,
|
||||
@ -199,7 +189,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
string title)
|
||||
{
|
||||
return await ShowContentDialog(
|
||||
window,
|
||||
title,
|
||||
primary,
|
||||
secondaryText,
|
||||
@ -210,7 +199,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
}
|
||||
|
||||
internal static async Task<UserResult> CreateConfirmationDialog(
|
||||
StyleableWindow window,
|
||||
string primaryText,
|
||||
string secondaryText,
|
||||
string acceptButtonText,
|
||||
@ -219,7 +207,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
UserResult primaryButtonResult = UserResult.Yes)
|
||||
{
|
||||
return await ShowContentDialog(
|
||||
window,
|
||||
string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance["DialogConfirmationTitle"] : title,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
@ -235,10 +222,9 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
return new(mainText, secondaryText);
|
||||
}
|
||||
|
||||
internal static async Task CreateUpdaterInfoDialog(StyleableWindow window, string primary, string secondaryText)
|
||||
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
||||
{
|
||||
await ShowContentDialog(
|
||||
window,
|
||||
LocaleManager.Instance["DialogUpdaterTitle"],
|
||||
primary,
|
||||
secondaryText,
|
||||
@ -248,24 +234,9 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
(int)Symbol.Important);
|
||||
}
|
||||
|
||||
internal static async Task ShowNotAvailableMessage(StyleableWindow window)
|
||||
{
|
||||
// Temporary placeholder for features to be added
|
||||
await ShowContentDialog(
|
||||
window,
|
||||
"Feature Not Available",
|
||||
"The selected feature is not available in this version.",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
(int)Symbol.Important);
|
||||
}
|
||||
|
||||
internal static async Task CreateWarningDialog(StyleableWindow window, string primary, string secondaryText)
|
||||
internal static async Task CreateWarningDialog(string primary, string secondaryText)
|
||||
{
|
||||
await ShowContentDialog(
|
||||
window,
|
||||
LocaleManager.Instance["DialogWarningTitle"],
|
||||
primary,
|
||||
secondaryText,
|
||||
@ -275,12 +246,11 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
(int)Symbol.Important);
|
||||
}
|
||||
|
||||
internal static async Task CreateErrorDialog(StyleableWindow owner, string errorMessage, string secondaryErrorMessage = "")
|
||||
internal static async Task CreateErrorDialog(string errorMessage, string secondaryErrorMessage = "")
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
await ShowContentDialog(
|
||||
owner,
|
||||
LocaleManager.Instance["DialogErrorTitle"],
|
||||
LocaleManager.Instance["DialogErrorMessage"],
|
||||
errorMessage,
|
||||
@ -290,7 +260,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
(int)Symbol.Dismiss);
|
||||
}
|
||||
|
||||
internal static async Task<bool> CreateChoiceDialog(StyleableWindow window, string title, string primary, string secondaryText)
|
||||
internal static async Task<bool> CreateChoiceDialog(string title, string primary, string secondaryText)
|
||||
{
|
||||
if (_isChoiceDialogOpen)
|
||||
{
|
||||
@ -301,7 +271,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
|
||||
UserResult response =
|
||||
await ShowContentDialog(
|
||||
window,
|
||||
title,
|
||||
primary,
|
||||
secondaryText,
|
||||
@ -316,19 +285,17 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
return response == UserResult.Yes;
|
||||
}
|
||||
|
||||
internal static async Task<bool> CreateExitDialog(StyleableWindow owner)
|
||||
internal static async Task<bool> CreateExitDialog()
|
||||
{
|
||||
return await CreateChoiceDialog(
|
||||
owner,
|
||||
LocaleManager.Instance["DialogExitTitle"],
|
||||
LocaleManager.Instance["DialogExitMessage"],
|
||||
LocaleManager.Instance["DialogExitSubMessage"]);
|
||||
}
|
||||
|
||||
internal static async Task<bool> CreateStopEmulationDialog(StyleableWindow owner)
|
||||
internal static async Task<bool> CreateStopEmulationDialog()
|
||||
{
|
||||
return await CreateChoiceDialog(
|
||||
owner,
|
||||
LocaleManager.Instance["DialogStopEmulationTitle"],
|
||||
LocaleManager.Instance["DialogStopEmulationMessage"],
|
||||
LocaleManager.Instance["DialogExitSubMessage"]);
|
||||
@ -338,12 +305,10 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
string title,
|
||||
string mainText,
|
||||
string subText,
|
||||
StyleableWindow owner,
|
||||
uint maxLength = int.MaxValue,
|
||||
string input = "")
|
||||
{
|
||||
var result = await InputDialog.ShowInputDialog(
|
||||
owner,
|
||||
title,
|
||||
mainText,
|
||||
input,
|
||||
|
@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
public class InputDialog : UserControl
|
||||
public partial class InputDialog : UserControl
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string Input { get; set; }
|
||||
@ -24,8 +24,6 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
MaxLength = maxLength;
|
||||
|
||||
DataContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public InputDialog()
|
||||
@ -33,33 +31,26 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message,
|
||||
string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(StyleableWindow window, string title, string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
||||
{
|
||||
ContentDialog contentDialog = window.ContentDialog;
|
||||
|
||||
UserResult result = UserResult.Cancel;
|
||||
|
||||
InputDialog content = new InputDialog(message, input = "", subMessage = "", maxLength);
|
||||
|
||||
if (contentDialog != null)
|
||||
InputDialog content = new InputDialog(message, input, subMessage, maxLength);
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
contentDialog.Title = title;
|
||||
contentDialog.PrimaryButtonText = LocaleManager.Instance["InputDialogOk"];
|
||||
contentDialog.SecondaryButtonText = "";
|
||||
contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"];
|
||||
contentDialog.Content = content;
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
Title = title,
|
||||
PrimaryButtonText = LocaleManager.Instance["InputDialogOk"],
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance["InputDialogCancel"],
|
||||
Content = content,
|
||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.Ok;
|
||||
input = content.Input;
|
||||
});
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
})
|
||||
};
|
||||
await contentDialog.ShowAsync();
|
||||
|
||||
return (result, input);
|
||||
}
|
||||
|
10
Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml
Normal file
10
Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml
Normal file
@ -0,0 +1,10 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.Ui.Controls.NavigationDialogHost">
|
||||
<ui:Frame HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
x:Name="ContentFrame" />
|
||||
</UserControl>
|
85
Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml.cs
Normal file
85
Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Ui.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
public partial class NavigationDialogHost : UserControl
|
||||
{
|
||||
public AccountManager AccountManager { get; }
|
||||
public ContentManager ContentManager { get; }
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
public NavigationDialogHost()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public NavigationDialogHost(AccountManager accountManager, ContentManager contentManager,
|
||||
VirtualFileSystem virtualFileSystem)
|
||||
{
|
||||
AccountManager = accountManager;
|
||||
ContentManager = contentManager;
|
||||
ViewModel = new UserProfileViewModel(this);
|
||||
|
||||
|
||||
if (contentManager.GetCurrentFirmwareVersion() != null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
AvatarProfileViewModel.PreloadAvatars(contentManager, virtualFileSystem);
|
||||
});
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void GoBack(object parameter = null)
|
||||
{
|
||||
if (ContentFrame.BackStack.Count > 0)
|
||||
{
|
||||
ContentFrame.GoBack();
|
||||
}
|
||||
|
||||
ViewModel.LoadProfiles();
|
||||
}
|
||||
|
||||
public void Navigate(Type sourcePageType, object parameter)
|
||||
{
|
||||
ContentFrame.Navigate(sourcePageType, parameter);
|
||||
}
|
||||
|
||||
public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager, VirtualFileSystem ownerVirtualFileSystem)
|
||||
{
|
||||
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem);
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
Title = LocaleManager.Instance["UserProfileWindowTitle"],
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
|
||||
Content = content,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
|
||||
contentDialog.Closed += (sender, args) =>
|
||||
{
|
||||
content.ViewModel.Dispose();
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
Navigate(typeof(UserSelector), this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,10 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
x:Class="Ryujinx.Ava.Ui.Controls.ProfileImageSelectionDialog"
|
||||
SizeToContent="WidthAndHeight"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Title="{Locale:Locale ProfileImageSelectionTitle}"
|
||||
CanResize="false">
|
||||
x:Class="Ryujinx.Ava.Ui.Controls.ProfileImageSelectionDialog">
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,10,5, 5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@ -32,4 +28,4 @@
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
</UserControl>
|
@ -1,8 +1,10 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.VisualTree;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Ui.Models;
|
||||
using Ryujinx.Ava.Ui.Windows;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using SixLabors.ImageSharp;
|
||||
@ -12,36 +14,40 @@ using Image = SixLabors.ImageSharp.Image;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
public class ProfileImageSelectionDialog : StyleableWindow
|
||||
public partial class ProfileImageSelectionDialog : UserControl
|
||||
{
|
||||
private readonly ContentManager _contentManager;
|
||||
private ContentManager _contentManager;
|
||||
private NavigationDialogHost _parent;
|
||||
private TempProfile _profile;
|
||||
|
||||
public bool FirmwareFound => _contentManager.GetCurrentFirmwareVersion() != null;
|
||||
|
||||
public byte[] BufferImageProfile { get; set; }
|
||||
|
||||
public ProfileImageSelectionDialog(ContentManager contentManager)
|
||||
{
|
||||
_contentManager = contentManager;
|
||||
DataContext = this;
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
public ProfileImageSelectionDialog()
|
||||
{
|
||||
DataContext = this;
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||
{
|
||||
NavigatedTo(e);
|
||||
}, RoutingStrategies.Direct);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
private void NavigatedTo(NavigationEventArgs arg)
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
switch (arg.NavigationMode)
|
||||
{
|
||||
case NavigationMode.New:
|
||||
(_parent, _profile) = ((NavigationDialogHost, TempProfile))arg.Parameter;
|
||||
_contentManager = _parent.ContentManager;
|
||||
break;
|
||||
case NavigationMode.Back:
|
||||
_parent.GoBack();
|
||||
break;
|
||||
}
|
||||
|
||||
DataContext = this;
|
||||
}
|
||||
}
|
||||
|
||||
private async void Import_OnClick(object sender, RoutedEventArgs e)
|
||||
@ -58,7 +64,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
|
||||
dialog.AllowMultiple = false;
|
||||
|
||||
string[] image = await dialog.ShowAsync(this);
|
||||
string[] image = await dialog.ShowAsync(((TopLevel)_parent.GetVisualRoot()) as Window);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
@ -66,28 +72,22 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
string imageFile = image[0];
|
||||
|
||||
ProcessProfileImage(File.ReadAllBytes(imageFile));
|
||||
_profile.Image = ProcessProfileImage(File.ReadAllBytes(imageFile));
|
||||
}
|
||||
|
||||
Close();
|
||||
_parent.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
private async void SelectFirmwareImage_OnClick(object sender, RoutedEventArgs e)
|
||||
private void SelectFirmwareImage_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (FirmwareFound)
|
||||
{
|
||||
AvatarWindow window = new(_contentManager);
|
||||
|
||||
await window.ShowDialog(this);
|
||||
|
||||
BufferImageProfile = window.SelectedImage;
|
||||
|
||||
Close();
|
||||
_parent.Navigate(typeof(AvatarWindow), (_parent, _profile));
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessProfileImage(byte[] buffer)
|
||||
private static byte[] ProcessProfileImage(byte[] buffer)
|
||||
{
|
||||
using (Image image = Image.Load(buffer))
|
||||
{
|
||||
@ -97,7 +97,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
image.SaveAsJpeg(streamJpg);
|
||||
|
||||
BufferImageProfile = streamJpg.ToArray();
|
||||
return streamJpg.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using Ryujinx.Ava.Ui.Windows;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
public class UpdateWaitWindow : StyleableWindow
|
||||
public partial class UpdateWaitWindow : StyleableWindow
|
||||
{
|
||||
public UpdateWaitWindow(string primaryText, string secondaryText) : this()
|
||||
{
|
||||
@ -21,15 +21,5 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
public TextBlock PrimaryText { get; private set; }
|
||||
public TextBlock SecondaryText { get; private set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
PrimaryText = this.FindControl<TextBlock>("PrimaryText");
|
||||
SecondaryText = this.FindControl<TextBlock>("SecondaryText");
|
||||
}
|
||||
}
|
||||
}
|
55
Ryujinx.Ava/Ui/Controls/UserEditor.axaml
Normal file
55
Ryujinx.Ava/Ui/Controls/UserEditor.axaml
Normal file
@ -0,0 +1,55 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
x:Class="Ryujinx.Ava.Ui.Controls.UserEditor">
|
||||
<UserControl.Resources>
|
||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Left">
|
||||
<Image
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Height="96" Width="96"
|
||||
Name="ProfileImage"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
<Button Margin="5" Content="{Locale:Locale UserProfilesChangeProfileImage}"
|
||||
Name="ChangePictureButton"
|
||||
Click="ChangePictureButton_Click"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
<Button Margin="5" Content="{Locale:Locale UserProfilesSetProfileImage}"
|
||||
Name="AddPictureButton"
|
||||
Click="ChangePictureButton_Click"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Orientation="Vertical" HorizontalAlignment="Stretch" Grid.Column="1" Spacing="10"
|
||||
Margin="5, 10">
|
||||
<TextBox Name="NameBox" Width="300" Text="{Binding Name}" MaxLength="{Binding MaxProfileNameLength}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
<TextBlock Text="{Binding UserId}" Name="IdLabel" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
|
||||
<Button Content="{Locale:Locale Save}" Name="SaveButton" Click="SaveButton_Click"/>
|
||||
<Button HorizontalAlignment="Right" Content="{Locale:Locale Discard}"
|
||||
Name="CloseButton" Click="CloseButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
123
Ryujinx.Ava/Ui/Controls/UserEditor.axaml.cs
Normal file
123
Ryujinx.Ava/Ui/Controls/UserEditor.axaml.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Ui.Models;
|
||||
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
public partial class UserEditor : UserControl
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
private UserProfile _profile;
|
||||
private bool _isNewUser;
|
||||
|
||||
public TempProfile TempProfile { get; set; }
|
||||
public uint MaxProfileNameLength => 0x20;
|
||||
|
||||
public UserEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||
{
|
||||
NavigatedTo(e);
|
||||
}, RoutingStrategies.Direct);
|
||||
}
|
||||
|
||||
private void NavigatedTo(NavigationEventArgs arg)
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
switch (arg.NavigationMode)
|
||||
{
|
||||
case NavigationMode.New:
|
||||
var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
|
||||
_isNewUser = args.isNewUser;
|
||||
if (!_isNewUser)
|
||||
{
|
||||
_profile = args.profile;
|
||||
TempProfile = new TempProfile(_profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
TempProfile = new TempProfile();
|
||||
}
|
||||
|
||||
_parent = args.parent;
|
||||
break;
|
||||
}
|
||||
|
||||
DataContext = TempProfile;
|
||||
|
||||
AddPictureButton.IsVisible = _isNewUser;
|
||||
IdLabel.IsVisible = !_isNewUser;
|
||||
ChangePictureButton.IsVisible = !_isNewUser;
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_parent?.GoBack();
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DataValidationErrors.ClearErrors(NameBox);
|
||||
bool isInvalid = false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(TempProfile.Name))
|
||||
{
|
||||
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance["UserProfileEmptyNameError"]));
|
||||
|
||||
isInvalid = true;
|
||||
}
|
||||
|
||||
if (TempProfile.Image == null)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UserProfileNoImageError"], "");
|
||||
|
||||
isInvalid = true;
|
||||
}
|
||||
|
||||
if(isInvalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_profile != null)
|
||||
{
|
||||
_profile.Name = TempProfile.Name;
|
||||
_profile.Image = TempProfile.Image;
|
||||
_profile.UpdateState();
|
||||
_parent.AccountManager.SetUserName(_profile.UserId, _profile.Name);
|
||||
_parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image);
|
||||
}
|
||||
else if (_isNewUser)
|
||||
{
|
||||
_parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_parent?.GoBack();
|
||||
}
|
||||
|
||||
public void SelectProfileImage()
|
||||
{
|
||||
_parent.Navigate(typeof(ProfileImageSelectionDialog), (_parent, TempProfile));
|
||||
}
|
||||
|
||||
private void ChangePictureButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_profile != null || _isNewUser)
|
||||
{
|
||||
SelectProfileImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -75,7 +75,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
|
||||
string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance["OpenSetupGuideMessage"] : "";
|
||||
|
||||
var result = await ContentDialogHelper.CreateInfoDialog(owner,
|
||||
var result = await ContentDialogHelper.CreateInfoDialog(
|
||||
string.Format(LocaleManager.Instance["DialogUserErrorDialogMessage"], errorCode, GetErrorTitle(error)),
|
||||
GetErrorDescription(error) + (isInSetupGuide
|
||||
? LocaleManager.Instance["DialogUserErrorDialogInfoMessage"]
|
||||
|
90
Ryujinx.Ava/Ui/Controls/UserSelector.axaml
Normal file
90
Ryujinx.Ava/Ui/Controls/UserSelector.axaml
Normal file
@ -0,0 +1,90 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.Ui.Controls.UserSelector">
|
||||
<UserControl.Resources>
|
||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Design.DataContext>
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5" Items="{Binding Profiles}"
|
||||
DoubleTapped="ProfilesList_DoubleTapped"
|
||||
SelectionChanged="SelectingItemsControl_SelectionChanged">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<flex:FlexPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
AlignContent="FlexStart"
|
||||
JustifyContent="Center" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Border
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="5">
|
||||
<Grid Margin="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Height="96" Width="96"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
IsVisible="{Binding IsOpened}"
|
||||
Background="LimeGreen"
|
||||
Width="10"
|
||||
Height="10"
|
||||
Margin="5"
|
||||
CornerRadius="5" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0" Spacing="10" HorizontalAlignment="Center">
|
||||
<Button Content="{Locale:Locale UserProfilesAddNewProfile}" Command="{Binding AddUser}" />
|
||||
<Button IsEnabled="{Binding IsSelectedProfiledEditable}"
|
||||
Content="{Locale:Locale UserProfilesEditProfile}" Command="{Binding EditUser}" />
|
||||
<Button IsEnabled="{Binding IsSelectedProfileDeletable}"
|
||||
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}" Command="{Binding DeleteUser}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
79
Ryujinx.Ava/Ui/Controls/UserSelector.axaml.cs
Normal file
79
Ryujinx.Ava/Ui/Controls/UserSelector.axaml.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.Ui.ViewModels;
|
||||
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
public partial class UserSelector : UserControl
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
public UserSelector()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||
{
|
||||
NavigatedTo(e);
|
||||
}, Avalonia.Interactivity.RoutingStrategies.Direct);
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigatedTo(NavigationEventArgs arg)
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
switch (arg.NavigationMode)
|
||||
{
|
||||
case NavigationMode.New:
|
||||
_parent = (NavigationDialogHost)arg.Parameter;
|
||||
ViewModel = _parent.ViewModel;
|
||||
break;
|
||||
}
|
||||
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProfilesList_DoubleTapped(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
int selectedIndex = listBox.SelectedIndex;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex < ViewModel.Profiles.Count)
|
||||
{
|
||||
ViewModel.SelectedProfile = ViewModel.Profiles[selectedIndex];
|
||||
|
||||
_parent?.AccountManager?.OpenUser(ViewModel.SelectedProfile.UserId);
|
||||
|
||||
ViewModel.LoadProfiles();
|
||||
|
||||
foreach (UserProfile profile in ViewModel.Profiles)
|
||||
{
|
||||
profile.UpdateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectingItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
int selectedIndex = listBox.SelectedIndex;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex < ViewModel.Profiles.Count)
|
||||
{
|
||||
ViewModel.HighlightedProfile = ViewModel.Profiles[selectedIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
Ryujinx.Ava/Ui/Models/TempProfile.cs
Normal file
55
Ryujinx.Ava/Ui/Models/TempProfile.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Ryujinx.Ava.Ui.ViewModels;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Models
|
||||
{
|
||||
public class TempProfile : BaseModel
|
||||
{
|
||||
private readonly UserProfile _profile;
|
||||
private byte[] _image = null;
|
||||
private string _name = String.Empty;
|
||||
private UserId _userId;
|
||||
|
||||
public byte[] Image
|
||||
{
|
||||
get => _image;
|
||||
set
|
||||
{
|
||||
_image = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public UserId UserId
|
||||
{
|
||||
get => _userId;
|
||||
set
|
||||
{
|
||||
_userId = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public TempProfile(UserProfile profile)
|
||||
{
|
||||
_profile = profile;
|
||||
|
||||
Image = profile.Image;
|
||||
Name = profile.Name;
|
||||
UserId = profile.UserId;
|
||||
}
|
||||
|
||||
public TempProfile(){}
|
||||
}
|
||||
}
|
@ -390,7 +390,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
return amiiboJsonString;
|
||||
}
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(_owner, LocaleManager.Instance["DialogAmiiboApiTitle"],
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogAmiiboApiTitle"],
|
||||
LocaleManager.Instance["DialogAmiiboApiFailFetchMessage"],
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
"",
|
||||
@ -440,7 +440,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
private async void ShowInfoDialog()
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(_owner, LocaleManager.Instance["DialogAmiiboApiTitle"],
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogAmiiboApiTitle"],
|
||||
LocaleManager.Instance["DialogAmiiboApiConnectErrorMessage"],
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
"",
|
||||
|
@ -327,12 +327,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
public async void ShowMotionConfig()
|
||||
{
|
||||
await MotionSettingsWindow.Show(this, _owner.GetVisualRoot() as StyleableWindow);
|
||||
await MotionSettingsWindow.Show(this);
|
||||
}
|
||||
|
||||
public async void ShowRumbleConfig()
|
||||
{
|
||||
await RumbleSettingsWindow.Show(this, _owner.GetVisualRoot() as StyleableWindow);
|
||||
await RumbleSettingsWindow.Show(this);
|
||||
}
|
||||
|
||||
private void LoadInputDriver()
|
||||
@ -701,8 +701,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow,
|
||||
String.Format(LocaleManager.Instance["DialogProfileInvalidProfileErrorMessage"], ProfileName));
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogProfileInvalidProfileErrorMessage"], ProfileName));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -736,7 +736,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow, LocaleManager.Instance["DialogProfileDefaultProfileOverwriteErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogProfileDefaultProfileOverwriteErrorMessage"]);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -769,7 +769,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow, LocaleManager.Instance["DialogProfileInvalidProfileNameErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogProfileInvalidProfileNameErrorMessage"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -782,7 +782,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
_owner.GetVisualRoot() as StyleableWindow,
|
||||
LocaleManager.Instance["DialogProfileDeleteProfileTitle"],
|
||||
LocaleManager.Instance["DialogProfileDeleteProfileMessage"],
|
||||
LocaleManager.Instance["InputDialogYes"],
|
||||
|
@ -708,7 +708,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_owner.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
|
||||
{
|
||||
string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper();
|
||||
@ -975,9 +975,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
public async void ManageProfiles()
|
||||
{
|
||||
UserProfileWindow window = new(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem);
|
||||
|
||||
await window.ShowDialog(_owner);
|
||||
await NavigationDialogHost.Show(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem);
|
||||
}
|
||||
|
||||
public async void OpenAboutWindow()
|
||||
@ -1054,8 +1052,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner,
|
||||
LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -1138,7 +1135,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1"));
|
||||
|
||||
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(_owner, LocaleManager.Instance["DialogWarning"],
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
|
||||
string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
||||
|
||||
List<FileInfo> cacheFiles = new();
|
||||
@ -1163,7 +1160,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, string.Format(LocaleManager.Instance["DialogPPTCDeletionErrorMessage"], file.Name, e));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogPPTCDeletionErrorMessage"], file.Name, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1201,7 +1198,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"));
|
||||
|
||||
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(_owner, LocaleManager.Instance["DialogWarning"],
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
|
||||
string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
||||
|
||||
List<DirectoryInfo> oldCacheDirectories = new List<DirectoryInfo>();
|
||||
@ -1224,7 +1221,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, string.Format(LocaleManager.Instance["DialogPPTCDeletionErrorMessage"], directory.Name, e));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogPPTCDeletionErrorMessage"], directory.Name, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1237,7 +1234,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, string.Format(LocaleManager.Instance["ShaderCachePurgeError"], file.Name, e));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["ShaderCachePurgeError"], file.Name, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1316,12 +1313,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
|
||||
out ulong titleIdNumber))
|
||||
out ulong titleIdNumber))
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner,
|
||||
LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -1342,12 +1338,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
|
||||
out ulong titleIdNumber))
|
||||
out ulong titleIdNumber))
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner,
|
||||
LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogRyujinxErrorMessage"], LocaleManager.Instance["DialogInvalidTitleIdErrorMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -1406,7 +1401,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
if (firmwareVersion == null)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareNotFoundErrorMessage"], filename));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareNotFoundErrorMessage"], filename));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1426,7 +1421,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
dialogMessage += LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallConfirmMessage"];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
_owner,
|
||||
dialogTitle,
|
||||
dialogMessage,
|
||||
LocaleManager.Instance["InputDialogYes"],
|
||||
@ -1456,7 +1450,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
|
||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString);
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(_owner, dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
|
||||
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
|
||||
Logger.Info?.Print(LogClass.Application, message);
|
||||
|
||||
// Purge Applet Cache.
|
||||
@ -1475,7 +1469,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
waitingDialog.Close();
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, ex.Message);
|
||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||
});
|
||||
}
|
||||
finally
|
||||
@ -1496,7 +1490,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner, ex.Message);
|
||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,8 +63,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(_owner,
|
||||
LocaleManager.Instance["DialogSettingsBackendThreadingWarningMessage"],
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance["DialogSettingsBackendThreadingWarningMessage"],
|
||||
"",
|
||||
"",
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
|
@ -1,31 +1,27 @@
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Ui.Controls;
|
||||
using Ryujinx.Ava.Ui.Windows;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
public class UserProfileViewModel : BaseModel, IDisposable
|
||||
{
|
||||
private const uint MaxProfileNameLength = 0x20;
|
||||
|
||||
private readonly UserProfileWindow _owner;
|
||||
private readonly NavigationDialogHost _owner;
|
||||
|
||||
private UserProfile _selectedProfile;
|
||||
private string _tempUserName;
|
||||
private UserProfile _highlightedProfile;
|
||||
|
||||
public UserProfileViewModel()
|
||||
{
|
||||
Profiles = new ObservableCollection<UserProfile>();
|
||||
}
|
||||
|
||||
public UserProfileViewModel(UserProfileWindow owner) : this()
|
||||
public UserProfileViewModel(NavigationDialogHost owner) : this()
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
@ -42,12 +38,29 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
_selectedProfile = value;
|
||||
|
||||
OnPropertyChanged(nameof(SelectedProfile));
|
||||
OnPropertyChanged(nameof(IsSelectedProfileDeletable));
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileDeletable));
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileEditable));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSelectedProfileDeletable =>
|
||||
_selectedProfile != null && _selectedProfile.UserId != AccountManager.DefaultUserId;
|
||||
public bool IsHighlightedProfileEditable =>
|
||||
_highlightedProfile != null;
|
||||
|
||||
public bool IsHighlightedProfileDeletable =>
|
||||
_highlightedProfile != null && _highlightedProfile.UserId != AccountManager.DefaultUserId;
|
||||
|
||||
public UserProfile HighlightedProfile
|
||||
{
|
||||
get => _highlightedProfile;
|
||||
set
|
||||
{
|
||||
_highlightedProfile = value;
|
||||
|
||||
OnPropertyChanged(nameof(HighlightedProfile));
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileDeletable));
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileEditable));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@ -78,64 +91,24 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async void ChooseProfileImage()
|
||||
public void AddUser()
|
||||
{
|
||||
await SelectProfileImage();
|
||||
UserProfile userProfile = null;
|
||||
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
|
||||
}
|
||||
|
||||
public async Task SelectProfileImage(bool isNewUser = false)
|
||||
public void EditUser()
|
||||
{
|
||||
ProfileImageSelectionDialog selectionDialog = new(_owner.ContentManager);
|
||||
|
||||
await selectionDialog.ShowDialog(_owner);
|
||||
|
||||
if (selectionDialog.BufferImageProfile != null)
|
||||
{
|
||||
if (isNewUser)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(_tempUserName))
|
||||
{
|
||||
_owner.AccountManager.AddUser(_tempUserName, selectionDialog.BufferImageProfile);
|
||||
}
|
||||
}
|
||||
else if (SelectedProfile != null)
|
||||
{
|
||||
_owner.AccountManager.SetUserImage(SelectedProfile.UserId, selectionDialog.BufferImageProfile);
|
||||
SelectedProfile.Image = selectionDialog.BufferImageProfile;
|
||||
|
||||
SelectedProfile = null;
|
||||
}
|
||||
|
||||
LoadProfiles();
|
||||
}
|
||||
}
|
||||
|
||||
public async void AddUser()
|
||||
{
|
||||
var dlgTitle = LocaleManager.Instance["InputDialogAddNewProfileTitle"];
|
||||
var dlgMainText = LocaleManager.Instance["InputDialogAddNewProfileHeader"];
|
||||
var dlgSubText = string.Format(LocaleManager.Instance["InputDialogAddNewProfileSubtext"],
|
||||
MaxProfileNameLength);
|
||||
|
||||
_tempUserName =
|
||||
await ContentDialogHelper.CreateInputDialog(dlgTitle, dlgMainText, dlgSubText, _owner,
|
||||
MaxProfileNameLength);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_tempUserName))
|
||||
{
|
||||
await SelectProfileImage(true);
|
||||
}
|
||||
|
||||
_tempUserName = String.Empty;
|
||||
_owner.Navigate(typeof(UserEditor), (this._owner, _highlightedProfile ?? SelectedProfile, false));
|
||||
}
|
||||
|
||||
public async void DeleteUser()
|
||||
{
|
||||
if (_selectedProfile != null)
|
||||
if (_highlightedProfile != null)
|
||||
{
|
||||
var lastUserId = _owner.AccountManager.LastOpenedUser.UserId;
|
||||
|
||||
if (_selectedProfile.UserId == lastUserId)
|
||||
if (_highlightedProfile.UserId == lastUserId)
|
||||
{
|
||||
// If we are deleting the currently open profile, then we must open something else before deleting.
|
||||
var profile = Profiles.FirstOrDefault(x => x.UserId != lastUserId);
|
||||
@ -144,8 +117,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(_owner,
|
||||
LocaleManager.Instance["DialogUserProfileDeletionWarningMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUserProfileDeletionWarningMessage"]);
|
||||
});
|
||||
|
||||
return;
|
||||
@ -155,13 +127,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
}
|
||||
|
||||
var result =
|
||||
await ContentDialogHelper.CreateConfirmationDialog(_owner,
|
||||
LocaleManager.Instance["DialogUserProfileDeletionConfirmMessage"], "",
|
||||
await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogUserProfileDeletionConfirmMessage"], "",
|
||||
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], "");
|
||||
|
||||
if (result == UserResult.Yes)
|
||||
{
|
||||
_owner.AccountManager.DeleteUser(_selectedProfile.UserId);
|
||||
_owner.AccountManager.DeleteUser(_highlightedProfile.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,43 +17,43 @@
|
||||
SizeToContent="Width"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
mc:Ignorable="d">
|
||||
<Grid
|
||||
<Grid
|
||||
Margin="15"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
@ -61,21 +61,21 @@
|
||||
MinWidth="50"
|
||||
Margin="5,10,20,10"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="0,20,0,0"
|
||||
FontSize="35"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,0"
|
||||
FontSize="16"
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Center" />
|
||||
<Button
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="0"
|
||||
@ -83,27 +83,27 @@
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.ryujinx.org/">
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Text="www.ryujinx.org"
|
||||
TextAlignment="Center"
|
||||
TextDecorations="Underline"
|
||||
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Center"
|
||||
MaxLines="2"
|
||||
Text="{locale:Locale AboutDisclaimerMessage}"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Name="AmiiboLabel"
|
||||
Grid.Row="3"
|
||||
Margin="20"
|
||||
@ -112,94 +112,94 @@
|
||||
PointerPressed="AmiiboLabel_OnPointerPressed"
|
||||
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
|
||||
TextAlignment="Center" />
|
||||
<StackPanel
|
||||
<StackPanel
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Button
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.patreon.com/ryujinx">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Patreon.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Patreon.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Patreon" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Button
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_GitHub.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_GitHub.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="GitHub" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Button
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://discordapp.com/invite/N2FmfVc">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Discord.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Discord.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Discord" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Button
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Button
|
||||
Height="65"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://twitter.com/RyujinxEmu">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Twitter.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Twitter.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="Twitter" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="2"
|
||||
@ -207,62 +207,62 @@
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="1,0,0,0">
|
||||
<Separator Width="0" />
|
||||
</Border>
|
||||
<Grid
|
||||
<Separator Width="0" />
|
||||
</Border>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxAboutTitle}"
|
||||
TextDecorations="Underline" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="20,5,5,5"
|
||||
LineHeight="20"
|
||||
Text="{locale:Locale AboutRyujinxAboutContent}" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="0,10,0,0"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxMaintainersTitle}"
|
||||
TextDecorations="Underline" />
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Margin="20,5,5,5"
|
||||
LineHeight="20"
|
||||
Text="{Binding Developers}" />
|
||||
<Button
|
||||
<Button
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Right"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
|
||||
TextAlignment="Right"
|
||||
TextDecorations="Underline"
|
||||
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
</Button>
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Margin="0,0,0,0"
|
||||
FontWeight="Bold"
|
||||
Text="{locale:Locale AboutRyujinxSupprtersTitle}"
|
||||
TextDecorations="Underline" />
|
||||
<Border
|
||||
<Border
|
||||
Grid.Row="6"
|
||||
Width="460"
|
||||
Height="200"
|
||||
@ -271,12 +271,12 @@
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="1">
|
||||
<TextBlock
|
||||
<TextBlock
|
||||
Name="SupportersTextBlock"
|
||||
VerticalAlignment="Top"
|
||||
Text="{Binding Supporters}"
|
||||
TextWrapping="Wrap" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
||||
|
@ -13,7 +13,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class AboutWindow : StyleableWindow
|
||||
public partial class AboutWindow : StyleableWindow
|
||||
{
|
||||
public AboutWindow()
|
||||
{
|
||||
@ -39,15 +39,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
public string Developers => string.Format(LocaleManager.Instance["AboutPageDeveloperListMore"], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«");
|
||||
|
||||
public TextBlock SupportersTextBlock { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
SupportersTextBlock = this.FindControl<TextBlock>("SupportersTextBlock");
|
||||
}
|
||||
|
||||
private void Button_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button)
|
||||
|
@ -7,7 +7,7 @@ using Ryujinx.Ava.Ui.ViewModels;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class AmiiboWindow : StyleableWindow
|
||||
public partial class AmiiboWindow : StyleableWindow
|
||||
{
|
||||
public AmiiboWindow(bool showAll, string lastScannedAmiiboId, string titleId)
|
||||
{
|
||||
@ -44,11 +44,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
public Amiibo.AmiiboApi ScannedAmiibo { get; set; }
|
||||
public AmiiboWindowViewModel ViewModel { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.AmiiboSelectedIndex > -1)
|
||||
|
@ -1,36 +1,35 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
|
||||
x:Class="Ryujinx.Ava.Ui.Windows.AvatarWindow"
|
||||
CanResize="False"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:AvatarProfileViewModel"
|
||||
SizeToContent="WidthAndHeight">
|
||||
x:DataType="viewModels:AvatarProfileViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:AvatarProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Window.Resources>
|
||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</Window.Resources>
|
||||
<Grid Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<UserControl.Resources>
|
||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox Grid.Row="1" BorderThickness="0" SelectedIndex="{Binding SelectedIndex}" Width="600" Height="500"
|
||||
<ListBox Grid.Row="1" BorderThickness="0" SelectedIndex="{Binding SelectedIndex}" Height="400"
|
||||
Items="{Binding Images}" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" MaxWidth="600" Margin="0" HorizontalAlignment="Center" />
|
||||
<WrapPanel Orientation="Horizontal" MaxWidth="700" Margin="0" HorizontalAlignment="Center" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
@ -45,9 +44,9 @@
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Spacing="10" Margin="10" HorizontalAlignment="Center">
|
||||
<Button Content="{Locale:Locale AvatarChoose}" Width="200" Name="ChooseButton" Click="ChooseButton_OnClick" />
|
||||
<ui:ColorPickerButton Color="{Binding BackgroundColor, Mode=TwoWay}" Name="ColorButton" />
|
||||
<Button HorizontalAlignment="Right" Content="{Locale:Locale AvatarClose}" Click="CloseButton_OnClick"
|
||||
<Button HorizontalAlignment="Right" Content="{Locale:Locale Discard}" Click="CloseButton_OnClick"
|
||||
Name="CloseButton"
|
||||
Width="200" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
</UserControl>
|
@ -1,70 +1,76 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.Ui.Controls;
|
||||
using Ryujinx.Ava.Ui.Models;
|
||||
using Ryujinx.Ava.Ui.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class AvatarWindow : StyleableWindow
|
||||
public partial class AvatarWindow : UserControl
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
private TempProfile _profile;
|
||||
|
||||
public AvatarWindow(ContentManager contentManager)
|
||||
{
|
||||
ContentManager = contentManager;
|
||||
ViewModel = new AvatarProfileViewModel(() => ViewModel.ReloadImages());
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["AvatarWindowTitle"];
|
||||
}
|
||||
|
||||
public AvatarWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
|
||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||
{
|
||||
NavigatedTo(e);
|
||||
}, RoutingStrategies.Direct);
|
||||
}
|
||||
|
||||
private void NavigatedTo(NavigationEventArgs arg)
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["AvatarWindowTitle"];
|
||||
if (arg.NavigationMode == NavigationMode.New)
|
||||
{
|
||||
(_parent, _profile) = ((NavigationDialogHost, TempProfile))arg.Parameter;
|
||||
ContentManager = _parent.ContentManager;
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
ViewModel = new AvatarProfileViewModel(() => ViewModel.ReloadImages());
|
||||
}
|
||||
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ContentManager ContentManager { get; }
|
||||
|
||||
public byte[] SelectedImage { get; set; }
|
||||
public ContentManager ContentManager { get; private set; }
|
||||
|
||||
internal AvatarProfileViewModel ViewModel { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
ViewModel.Dispose();
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
private void CloseButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
ViewModel.Dispose();
|
||||
|
||||
_parent.GoBack();
|
||||
}
|
||||
|
||||
private void ChooseButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.SelectedIndex > -1)
|
||||
{
|
||||
SelectedImage = ViewModel.SelectedImage;
|
||||
_profile.Image = ViewModel.SelectedImage;
|
||||
|
||||
Close();
|
||||
ViewModel.Dispose();
|
||||
|
||||
_parent.GoBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class CheatWindow : StyleableWindow
|
||||
public partial class CheatWindow : StyleableWindow
|
||||
{
|
||||
private readonly string _enabledCheatsPath;
|
||||
public bool NoCheatsFound { get; }
|
||||
@ -102,11 +102,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
this.AttachDevTools();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (NoCheatsFound)
|
||||
|
@ -24,11 +24,10 @@ using Key = Ryujinx.Input.Key;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class ControllerSettingsWindow : UserControl
|
||||
public partial class ControllerSettingsWindow : UserControl
|
||||
{
|
||||
private bool _dialogOpen;
|
||||
|
||||
public Grid SettingButtons { get; set; }
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
internal ControllerSettingsViewModel ViewModel { get; set; }
|
||||
|
||||
@ -48,13 +47,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
SettingButtons = this.FindControl<Grid>("SettingButtons");
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
{
|
||||
base.OnPointerReleased(e);
|
||||
@ -165,7 +157,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
_dialogOpen = true;
|
||||
|
||||
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
this.GetVisualRoot() as StyleableWindow,
|
||||
LocaleManager.Instance["DialogControllerSettingsModifiedConfirmMessage"],
|
||||
LocaleManager.Instance["DialogControllerSettingsModifiedConfirmSubMessage"],
|
||||
LocaleManager.Instance["InputDialogYes"],
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
@ -27,7 +26,7 @@ using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class DlcManagerWindow : StyleableWindow
|
||||
public partial class DlcManagerWindow : StyleableWindow
|
||||
{
|
||||
private readonly List<DlcContainer> _dlcContainerList;
|
||||
private readonly string _dlcJsonPath;
|
||||
@ -35,7 +34,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
public VirtualFileSystem VirtualFileSystem { get; }
|
||||
|
||||
public AvaloniaList<DlcModel> Dlcs { get; set; }
|
||||
public Grid DlcGrid { get; private set; }
|
||||
public ulong TitleId { get; }
|
||||
public string TitleName { get; }
|
||||
|
||||
@ -47,7 +45,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
InitializeComponent();
|
||||
AttachDebugDevTools();
|
||||
|
||||
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
|
||||
}
|
||||
|
||||
@ -72,7 +70,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
InitializeComponent();
|
||||
AttachDebugDevTools();
|
||||
|
||||
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
|
||||
|
||||
LoadDlcs();
|
||||
@ -84,15 +82,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
this.AttachDevTools();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
Dlcs = new AvaloniaList<DlcModel>();
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
DlcGrid = this.FindControl<Grid>("DlcGrid");
|
||||
}
|
||||
|
||||
private void LoadDlcs()
|
||||
{
|
||||
foreach (DlcContainer dlcContainer in _dlcContainerList)
|
||||
@ -129,8 +118,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(this,
|
||||
string.Format(LocaleManager.Instance[
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[
|
||||
"DialogDlcLoadNcaErrorMessage"], ex.Message, containerPath));
|
||||
});
|
||||
}
|
||||
@ -180,7 +168,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
if (!containsDlc)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(this, LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,7 +177,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
if (removeSelectedOnly)
|
||||
{
|
||||
Dlcs.RemoveAll(Dlcs.Where(x => x.IsEnabled).ToList());
|
||||
Dlcs.RemoveAll(Dlcs.Where(x => x.IsEnabled).ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -38,18 +38,6 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<controls:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" />
|
||||
<ContentControl
|
||||
Grid.Row="1"
|
||||
Focusable="False"
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False">
|
||||
<ui:ContentDialog
|
||||
Name="ContentDialog"
|
||||
IsPrimaryButtonEnabled="True"
|
||||
IsSecondaryButtonEnabled="True"
|
||||
IsVisible="True"
|
||||
KeyboardNavigation.IsTabStop="False" />
|
||||
</ContentControl>
|
||||
<StackPanel Grid.Row="0" IsVisible="False">
|
||||
<controls:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" />
|
||||
<controls:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" />
|
||||
@ -123,14 +111,20 @@
|
||||
Command="{ReflectionBinding ToggleFullscreen}"
|
||||
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
|
||||
InputGesture="F11" />
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsStartGamesInFullscreen}">
|
||||
<MenuItem>
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}" />
|
||||
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
|
||||
MinWidth="250">
|
||||
<TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
|
||||
</CheckBox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsShowConsole}" IsVisible="{Binding ShowConsoleVisible}">
|
||||
<MenuItem IsVisible="{Binding ShowConsoleVisible}">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}" />
|
||||
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
|
||||
MinWidth="250">
|
||||
<TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
|
||||
</CheckBox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
|
@ -36,7 +36,7 @@ using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
using ProgressBar = Avalonia.Controls.ProgressBar;
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class MainWindow : StyleableWindow
|
||||
public partial class MainWindow : StyleableWindow
|
||||
{
|
||||
private bool _canUpdate;
|
||||
private bool _isClosing;
|
||||
@ -62,22 +62,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
public InputManager InputManager { get; private set; }
|
||||
|
||||
internal RendererControl GlRenderer { get; private set; }
|
||||
public ContentControl ContentFrame { get; private set; }
|
||||
public TextBlock LoadStatus { get; private set; }
|
||||
public TextBlock FirmwareStatus { get; private set; }
|
||||
public TextBox SearchBox { get; private set; }
|
||||
public ProgressBar LoadProgressBar { get; private set; }
|
||||
public Menu Menu { get; private set; }
|
||||
public MenuItem UpdateMenuItem { get; private set; }
|
||||
public MenuItem ActionsMenuItem { get; private set; }
|
||||
public GameGridView GameGrid { get; private set; }
|
||||
public GameListView GameList { get; private set; }
|
||||
public OffscreenTextBox HiddenTextBox { get; private set; }
|
||||
public HotKeyControl FullscreenHotKey { get; private set; }
|
||||
public HotKeyControl FullscreenHotKey2 { get; private set; }
|
||||
public HotKeyControl DockToggleHotKey { get; private set; }
|
||||
public HotKeyControl ExitHotKey { get; private set; }
|
||||
public ToggleSplitButton VolumeStatus { get; set; }
|
||||
internal MainWindowViewModel ViewModel { get; private set; }
|
||||
public SettingsWindow SettingsWindow { get; set; }
|
||||
|
||||
@ -102,6 +86,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
AttachDebugDevTools();
|
||||
|
||||
UiHandler = new AvaHostUiHandler(this);
|
||||
@ -192,7 +177,9 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
string mainMessage = LocaleManager.Instance["DialogPerformanceCheckLoggingEnabledMessage"];
|
||||
string secondaryMessage = LocaleManager.Instance["DialogPerformanceCheckLoggingEnabledConfirmMessage"];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(this, mainMessage, secondaryMessage, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
|
||||
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"],
|
||||
LocaleManager.Instance["RyujinxConfirm"]);
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
@ -205,9 +192,12 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
|
||||
{
|
||||
string mainMessage = LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledMessage"];
|
||||
string secondaryMessage = LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledConfirmMessage"];
|
||||
string secondaryMessage =
|
||||
LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledConfirmMessage"];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(this, mainMessage, secondaryMessage, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
|
||||
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"],
|
||||
LocaleManager.Instance["RyujinxConfirm"]);
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
@ -231,7 +221,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(this,
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
LocaleManager.Instance["DialogLoadAppGameAlreadyLoadedMessage"],
|
||||
LocaleManager.Instance["DialogLoadAppGameAlreadyLoadedSubMessage"],
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
@ -254,7 +244,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
_mainViewContent = ContentFrame.Content as Control;
|
||||
_mainViewContent = Content.Content as Control;
|
||||
|
||||
GlRenderer = new RendererControl(3, 3, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
|
||||
AppHost = new AppHost(GlRenderer, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
|
||||
@ -321,7 +311,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ContentFrame.Content = GlRenderer;
|
||||
Content.Content = GlRenderer;
|
||||
|
||||
if (startFullscreen && WindowState != WindowState.FullScreen)
|
||||
{
|
||||
@ -365,9 +355,9 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (ContentFrame.Content != _mainViewContent)
|
||||
if (Content.Content != _mainViewContent)
|
||||
{
|
||||
ContentFrame.Content = _mainViewContent;
|
||||
Content.Content = _mainViewContent;
|
||||
}
|
||||
|
||||
ViewModel.ShowMenuAndStatusBar = true;
|
||||
@ -501,27 +491,8 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
ViewModel.IsAppletMenuActive = hasApplet;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
private void Load()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
ContentFrame = this.FindControl<ContentControl>("Content");
|
||||
GameList = this.FindControl<GameListView>("GameList");
|
||||
LoadStatus = this.FindControl<TextBlock>("LoadStatus");
|
||||
FirmwareStatus = this.FindControl<TextBlock>("FirmwareStatus");
|
||||
LoadProgressBar = this.FindControl<ProgressBar>("LoadProgressBar");
|
||||
SearchBox = this.FindControl<TextBox>("SearchBox");
|
||||
Menu = this.FindControl<Menu>("Menu");
|
||||
UpdateMenuItem = this.FindControl<MenuItem>("UpdateMenuItem");
|
||||
GameGrid = this.FindControl<GameGridView>("GameGrid");
|
||||
HiddenTextBox = this.FindControl<OffscreenTextBox>("HiddenTextBox");
|
||||
FullscreenHotKey = this.FindControl<HotKeyControl>("FullscreenHotKey");
|
||||
FullscreenHotKey2 = this.FindControl<HotKeyControl>("FullscreenHotKey2");
|
||||
DockToggleHotKey = this.FindControl<HotKeyControl>("DockToggleHotKey");
|
||||
ExitHotKey = this.FindControl<HotKeyControl>("ExitHotKey");
|
||||
VolumeStatus = this.FindControl<ToggleSplitButton>("VolumeStatus");
|
||||
ActionsMenuItem = this.FindControl<MenuItem>("ActionsMenuItem");
|
||||
|
||||
VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
||||
|
||||
GameGrid.ApplicationOpened += Application_Opened;
|
||||
@ -710,7 +681,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
_isClosing = await ContentDialogHelper.CreateExitDialog(this);
|
||||
_isClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
|
||||
if (_isClosing)
|
||||
{
|
||||
|
@ -9,13 +9,14 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class MotionSettingsWindow : UserControl
|
||||
public partial class MotionSettingsWindow : UserControl
|
||||
{
|
||||
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
|
||||
|
||||
public MotionSettingsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = _viewmodel;
|
||||
}
|
||||
|
||||
public MotionSettingsWindow(ControllerSettingsViewModel viewmodel)
|
||||
@ -36,46 +37,36 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
DataContext = _viewmodel;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerSettingsViewModel viewmodel, StyleableWindow window)
|
||||
public static async Task Show(ControllerSettingsViewModel viewmodel)
|
||||
{
|
||||
ContentDialog contentDialog = window.ContentDialog;
|
||||
|
||||
string name = string.Empty;
|
||||
|
||||
MotionSettingsWindow content = new MotionSettingsWindow(viewmodel);
|
||||
|
||||
if (contentDialog != null)
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
contentDialog.Title = LocaleManager.Instance["ControllerMotionTitle"];
|
||||
contentDialog.PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"];
|
||||
contentDialog.SecondaryButtonText = "";
|
||||
contentDialog.CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"];
|
||||
contentDialog.Content = content;
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
{
|
||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||
config.Slot = content._viewmodel.Slot;
|
||||
config.EnableMotion = content._viewmodel.EnableMotion;
|
||||
config.Sensitivity = content._viewmodel.Sensitivity;
|
||||
config.GyroDeadzone = content._viewmodel.GyroDeadzone;
|
||||
config.AltSlot = content._viewmodel.AltSlot;
|
||||
config.DsuServerHost = content._viewmodel.DsuServerHost;
|
||||
config.DsuServerPort = content._viewmodel.DsuServerPort;
|
||||
config.EnableCemuHookMotion = content._viewmodel.EnableCemuHookMotion;
|
||||
config.MirrorInput = content._viewmodel.MirrorInput;
|
||||
};
|
||||
Title = LocaleManager.Instance["ControllerMotionTitle"],
|
||||
PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"],
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"],
|
||||
Content = content
|
||||
};
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
{
|
||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||
config.Slot = content._viewmodel.Slot;
|
||||
config.EnableMotion = content._viewmodel.EnableMotion;
|
||||
config.Sensitivity = content._viewmodel.Sensitivity;
|
||||
config.GyroDeadzone = content._viewmodel.GyroDeadzone;
|
||||
config.AltSlot = content._viewmodel.AltSlot;
|
||||
config.DsuServerHost = content._viewmodel.DsuServerHost;
|
||||
config.DsuServerPort = content._viewmodel.DsuServerPort;
|
||||
config.EnableCemuHookMotion = content._viewmodel.EnableCemuHookMotion;
|
||||
config.MirrorInput = content._viewmodel.MirrorInput;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -9,13 +9,14 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class RumbleSettingsWindow : UserControl
|
||||
public partial class RumbleSettingsWindow : UserControl
|
||||
{
|
||||
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
|
||||
|
||||
public RumbleSettingsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = _viewmodel;
|
||||
}
|
||||
|
||||
public RumbleSettingsWindow(ControllerSettingsViewModel viewmodel)
|
||||
@ -24,44 +25,34 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
|
||||
{
|
||||
StrongRumble = config.StrongRumble,
|
||||
WeakRumble = config.WeakRumble
|
||||
StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
DataContext = _viewmodel;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerSettingsViewModel viewmodel, StyleableWindow window)
|
||||
public static async Task Show(ControllerSettingsViewModel viewmodel)
|
||||
{
|
||||
ContentDialog contentDialog = window.ContentDialog;
|
||||
|
||||
string name = string.Empty;
|
||||
|
||||
RumbleSettingsWindow content = new RumbleSettingsWindow(viewmodel);
|
||||
|
||||
if (contentDialog != null)
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
contentDialog.Title = LocaleManager.Instance["ControllerRumbleTitle"];
|
||||
contentDialog.PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"];
|
||||
contentDialog.SecondaryButtonText = "";
|
||||
contentDialog.CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"];
|
||||
contentDialog.Content = content;
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
{
|
||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||
config.StrongRumble = content._viewmodel.StrongRumble;
|
||||
config.WeakRumble = content._viewmodel.WeakRumble;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
Title = LocaleManager.Instance["ControllerRumbleTitle"],
|
||||
PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"],
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"],
|
||||
Content = content,
|
||||
};
|
||||
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
{
|
||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||
config.StrongRumble = content._viewmodel.StrongRumble;
|
||||
config.WeakRumble = content._viewmodel.WeakRumble;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -31,16 +31,11 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Grid.Row="1"
|
||||
Focusable="False"
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False">
|
||||
<ui:ContentDialog Name="ContentDialog"
|
||||
IsPrimaryButtonEnabled="True"
|
||||
IsSecondaryButtonEnabled="True"
|
||||
IsVisible="False" />
|
||||
</ContentControl>
|
||||
KeyboardNavigation.IsTabStop="False"/>
|
||||
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
|
||||
<ScrollViewer Name="UiPage"
|
||||
Margin="0,0,10,0"
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
@ -28,24 +29,8 @@ using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class SettingsWindow : StyleableWindow
|
||||
public partial class SettingsWindow : StyleableWindow
|
||||
{
|
||||
private ListBox _gameList;
|
||||
private TextBox _pathBox;
|
||||
private AutoCompleteBox _timeZoneBox;
|
||||
private ControllerSettingsWindow _controllerSettings;
|
||||
|
||||
// Pages
|
||||
private Control _uiPage;
|
||||
private Control _inputPage;
|
||||
private Control _hotkeysPage;
|
||||
private Control _systemPage;
|
||||
private Control _cpuPage;
|
||||
private Control _graphicsPage;
|
||||
private Control _audioPage;
|
||||
private Control _networkPage;
|
||||
private Control _loggingPage;
|
||||
private NavigationView _navPanel;
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
|
||||
internal SettingsViewModel ViewModel { get; set; }
|
||||
@ -58,6 +43,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
AttachDebugDevTools();
|
||||
|
||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
|
||||
@ -66,7 +52,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
|
||||
|
||||
_timeZoneBox.ValueMemberBinding = tzMultiBinding;
|
||||
TimeZoneBox.ValueMemberBinding = tzMultiBinding;
|
||||
}
|
||||
|
||||
public SettingsWindow()
|
||||
@ -75,6 +61,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
AttachDebugDevTools();
|
||||
}
|
||||
|
||||
@ -84,31 +71,11 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
this.AttachDevTools();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
private void Load()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
_pathBox = this.FindControl<TextBox>("PathBox");
|
||||
_gameList = this.FindControl<ListBox>("GameList");
|
||||
_timeZoneBox = this.FindControl<AutoCompleteBox>("TimeZoneBox");
|
||||
_controllerSettings = this.FindControl<ControllerSettingsWindow>("ControllerSettings");
|
||||
|
||||
_uiPage = this.FindControl<Control>("UiPage");
|
||||
_inputPage = this.FindControl<Control>("InputPage");
|
||||
_hotkeysPage = this.FindControl<Control>("HotkeysPage");
|
||||
_systemPage = this.FindControl<Control>("SystemPage");
|
||||
_cpuPage = this.FindControl<Control>("CpuPage");
|
||||
_graphicsPage = this.FindControl<Control>("GraphicsPage");
|
||||
_audioPage = this.FindControl<Control>("AudioPage");
|
||||
_networkPage = this.FindControl<Control>("NetworkPage");
|
||||
_loggingPage = this.FindControl<Control>("LoggingPage");
|
||||
|
||||
var pageGrid = this.FindControl<Grid>("Pages");
|
||||
pageGrid.Children.Clear();
|
||||
|
||||
_navPanel = this.FindControl<NavigationView>("NavPanel");
|
||||
_navPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
||||
_navPanel.SelectedItem = _navPanel.MenuItems.ElementAt(0);
|
||||
Pages.Children.Clear();
|
||||
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
||||
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
||||
}
|
||||
|
||||
private void Button_Checked(object sender, RoutedEventArgs e)
|
||||
@ -174,31 +141,31 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
switch (navitem.Tag.ToString())
|
||||
{
|
||||
case "UiPage":
|
||||
_navPanel.Content = _uiPage;
|
||||
NavPanel.Content = UiPage;
|
||||
break;
|
||||
case "InputPage":
|
||||
_navPanel.Content = _inputPage;
|
||||
NavPanel.Content = InputPage;
|
||||
break;
|
||||
case "HotkeysPage":
|
||||
_navPanel.Content = _hotkeysPage;
|
||||
NavPanel.Content = HotkeysPage;
|
||||
break;
|
||||
case "SystemPage":
|
||||
_navPanel.Content = _systemPage;
|
||||
NavPanel.Content = SystemPage;
|
||||
break;
|
||||
case "CpuPage":
|
||||
_navPanel.Content = _cpuPage;
|
||||
NavPanel.Content = CpuPage;
|
||||
break;
|
||||
case "GraphicsPage":
|
||||
_navPanel.Content = _graphicsPage;
|
||||
NavPanel.Content = GraphicsPage;
|
||||
break;
|
||||
case "AudioPage":
|
||||
_navPanel.Content = _audioPage;
|
||||
NavPanel.Content = AudioPage;
|
||||
break;
|
||||
case "NetworkPage":
|
||||
_navPanel.Content = _networkPage;
|
||||
NavPanel.Content = NetworkPage;
|
||||
break;
|
||||
case "LoggingPage":
|
||||
_navPanel.Content = _loggingPage;
|
||||
NavPanel.Content = LoggingPage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -206,7 +173,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
private async void AddButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string path = _pathBox.Text;
|
||||
string path = PathBox.Text;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
|
||||
{
|
||||
@ -225,7 +192,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
List<string> selected = new(_gameList.SelectedItems.Cast<string>());
|
||||
List<string> selected = new(GameList.SelectedItems.Cast<string>());
|
||||
|
||||
foreach (string path in selected)
|
||||
{
|
||||
@ -279,7 +246,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
ViewModel.SaveSettings();
|
||||
|
||||
_controllerSettings?.SaveCurrentProfile();
|
||||
ControllerSettings?.SaveCurrentProfile();
|
||||
|
||||
if (Owner is MainWindow window)
|
||||
{
|
||||
@ -289,7 +256,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_controllerSettings.Dispose();
|
||||
ControllerSettings.Dispose();
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
base.OnClosed(e);
|
||||
|
@ -11,7 +11,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class StyleableWindow : Window
|
||||
{
|
||||
public ContentDialog ContentDialog { get; private set; }
|
||||
public IBitmap IconImage { get; set; }
|
||||
|
||||
public StyleableWindow()
|
||||
@ -26,15 +25,9 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
IconImage = new Bitmap(stream);
|
||||
}
|
||||
|
||||
public void LoadDialog()
|
||||
{
|
||||
ContentDialog = this.FindControl<ContentDialog>("ContentDialog");
|
||||
}
|
||||
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
ContentDialog = this.FindControl<ContentDialog>("ContentDialog");
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
|
@ -28,14 +28,14 @@ using Avalonia.Threading;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class TitleUpdateWindow : StyleableWindow
|
||||
public partial class TitleUpdateWindow : StyleableWindow
|
||||
{
|
||||
private readonly string _updateJsonPath;
|
||||
private TitleUpdateMetadata _titleUpdateWindowData;
|
||||
|
||||
public VirtualFileSystem VirtualFileSystem { get; }
|
||||
|
||||
internal AvaloniaList<TitleUpdateModel> TitleUpdates { get; set; }
|
||||
internal AvaloniaList<TitleUpdateModel> TitleUpdates { get; set; } = new AvaloniaList<TitleUpdateModel>();
|
||||
public string TitleId { get; }
|
||||
public string TitleName { get; }
|
||||
|
||||
@ -84,13 +84,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
this.AttachDevTools();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
TitleUpdates = new AvaloniaList<TitleUpdateModel>();
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void LoadUpdates()
|
||||
{
|
||||
TitleUpdates.Add(new TitleUpdateModel(default, string.Empty, true));
|
||||
@ -154,8 +147,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(this,
|
||||
LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]);
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -163,8 +155,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(this,
|
||||
string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path));
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class UpdaterWindow : StyleableWindow
|
||||
public partial class UpdaterWindow : StyleableWindow
|
||||
{
|
||||
private readonly string _buildUrl;
|
||||
private readonly MainWindow _mainWindow;
|
||||
@ -36,21 +36,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
_buildUrl = buildUrl;
|
||||
}
|
||||
|
||||
public TextBlock MainText { get; set; }
|
||||
public TextBlock SecondaryText { get; set; }
|
||||
public ProgressBar ProgressBar { get; set; }
|
||||
public StackPanel ButtonBox { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
MainText = this.FindControl<TextBlock>("MainText");
|
||||
SecondaryText = this.FindControl<TextBlock>("SecondaryText");
|
||||
ProgressBar = this.FindControl<ProgressBar>("ProgressBar");
|
||||
ButtonBox = this.FindControl<StackPanel>("ButtonBox");
|
||||
}
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int chmod(string path, uint mode);
|
||||
|
||||
|
@ -1,107 +0,0 @@
|
||||
<window:StyleableWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
|
||||
x:Class="Ryujinx.Ava.Ui.Windows.UserProfileWindow"
|
||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
||||
CanResize="False"
|
||||
Width="850" MinHeight="550" Height="550"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
SizeToContent="Manual"
|
||||
MinWidth="600">
|
||||
<Design.DataContext>
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Window.Resources>
|
||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</Window.Resources>
|
||||
<Grid Margin="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl
|
||||
Focusable="False"
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False">
|
||||
<ui:ContentDialog Name="ContentDialog"
|
||||
IsPrimaryButtonEnabled="True"
|
||||
IsSecondaryButtonEnabled="True"
|
||||
IsVisible="False" />
|
||||
</ContentControl>
|
||||
<TextBlock Text="{Locale:Locale UserProfilesSelectedUserProfile}" />
|
||||
<Grid Grid.Row="1" Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Height="96" Width="96"
|
||||
Source="{Binding SelectedProfile.Image, Converter={StaticResource ByteImage}}" />
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Grid.Column="1" Spacing="10"
|
||||
Margin="5, 10">
|
||||
<TextBox Name="NameBox" Text="{Binding SelectedProfile.Name, Mode=OneWay}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
<TextBlock Text="{Binding SelectedProfile.UserId}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Grid.Column="2" Spacing="10"
|
||||
Margin="5">
|
||||
<Button Content="{Locale:Locale UserProfilesSaveProfileName}" Name="SetNameButton"
|
||||
Click="SetNameButton_OnClick" />
|
||||
<Button Name="SelectProfileImage" Command="{Binding ChooseProfileImage}"
|
||||
Content="{Locale:Locale UserProfilesChangeProfileImage}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="{Locale:Locale UserProfilesAvailableUserProfiles}" />
|
||||
<ListBox Grid.Row="1" Margin="10" Name="ProfilesList" DoubleTapped="ProfilesList_DoubleTapped"
|
||||
Items="{Binding Profiles}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0" Background="{DynamicResource ThemeAccentColorBrush}"
|
||||
Grid.ColumnSpan="2"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="5" MinWidth="5"
|
||||
IsVisible="{Binding IsOpened}" />
|
||||
<Image Grid.Column="0" Height="96" Width="96"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
<StackPanel Margin="10" Orientation="Vertical" HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center" Grid.Column="1">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<TextBlock Text="{Binding UserId}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="10,0" Spacing="10" HorizontalAlignment="Stretch">
|
||||
<Button Content="{Locale:Locale UserProfilesAddNewProfile}" Command="{Binding AddUser}" />
|
||||
<Button IsEnabled="{Binding IsSelectedProfileDeletable}"
|
||||
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}" Command="{Binding DeleteUser}" />
|
||||
<Button HorizontalAlignment="Right" Content="{Locale:Locale UserProfilesClose}" Click="CloseButton_OnClick"
|
||||
Name="CloseButton" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
@ -1,102 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Ui.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System.Threading.Tasks;
|
||||
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class UserProfileWindow : StyleableWindow
|
||||
{
|
||||
private TextBox _nameBox;
|
||||
|
||||
public UserProfileWindow(AccountManager accountManager, ContentManager contentManager,
|
||||
VirtualFileSystem virtualFileSystem)
|
||||
{
|
||||
AccountManager = accountManager;
|
||||
ContentManager = contentManager;
|
||||
ViewModel = new UserProfileViewModel(this);
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
if (contentManager.GetCurrentFirmwareVersion() != null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
AvatarProfileViewModel.PreloadAvatars(contentManager, virtualFileSystem);
|
||||
});
|
||||
}
|
||||
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UserProfileWindowTitle"];
|
||||
}
|
||||
|
||||
public UserProfileWindow()
|
||||
{
|
||||
ViewModel = new UserProfileViewModel();
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UserProfileWindowTitle"];
|
||||
}
|
||||
|
||||
public AccountManager AccountManager { get; }
|
||||
public ContentManager ContentManager { get; }
|
||||
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
_nameBox = this.FindControl<TextBox>("NameBox");
|
||||
}
|
||||
|
||||
private void ProfilesList_DoubleTapped(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
int selectedIndex = listBox.SelectedIndex;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex < ViewModel.Profiles.Count)
|
||||
{
|
||||
ViewModel.SelectedProfile = ViewModel.Profiles[selectedIndex];
|
||||
|
||||
AccountManager.OpenUser(ViewModel.SelectedProfile.UserId);
|
||||
|
||||
ViewModel.LoadProfiles();
|
||||
|
||||
foreach (UserProfile profile in ViewModel.Profiles)
|
||||
{
|
||||
profile.UpdateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void SetNameButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(_nameBox.Text))
|
||||
{
|
||||
ViewModel.SelectedProfile.Name = _nameBox.Text;
|
||||
AccountManager.SetUserName(ViewModel.SelectedProfile.UserId, _nameBox.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -208,7 +208,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
|
||||
Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray();
|
||||
|
||||
bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
|
||||
bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
|
||||
@ -262,43 +261,63 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||
target.SynchronizeMemory();
|
||||
target.SetData(data);
|
||||
target.SignalModified();
|
||||
|
||||
return;
|
||||
}
|
||||
else if (srcCalculator.LayoutMatches(dstCalculator))
|
||||
{
|
||||
srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
|
||||
|
||||
memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, dstSpan);
|
||||
|
||||
// No layout conversion has to be performed, just copy the data entirely.
|
||||
memoryManager.Write(dstGpuVa + (ulong)dstBaseOffset, srcSpan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe bool Convert<T>(Span<byte> dstSpan, ReadOnlySpan<byte> srcSpan) where T : unmanaged
|
||||
{
|
||||
fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan)
|
||||
if (srcLinear && dstLinear && srcBpp == dstBpp)
|
||||
{
|
||||
byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
|
||||
byte* srcBase = srcPtr - srcBaseOffset;
|
||||
|
||||
// Optimized path for purely linear copies - we don't need to calculate every single byte offset,
|
||||
// and we can make use of Span.CopyTo which is very very fast (even compared to pointers)
|
||||
for (int y = 0; y < yCount; y++)
|
||||
{
|
||||
srcCalculator.SetY(srcRegionY + y);
|
||||
dstCalculator.SetY(dstRegionY + y);
|
||||
int srcOffset = srcCalculator.GetOffset(srcRegionX);
|
||||
int dstOffset = dstCalculator.GetOffset(dstRegionX);
|
||||
srcSpan.Slice(srcOffset - srcBaseOffset, xCount * srcBpp)
|
||||
.CopyTo(dstSpan.Slice(dstOffset - dstBaseOffset, xCount * dstBpp));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (byte* dstPtr = dstSpan, srcPtr = srcSpan)
|
||||
{
|
||||
byte* dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
|
||||
byte* srcBase = srcPtr - srcBaseOffset;
|
||||
|
||||
for (int x = 0; x < xCount; x++)
|
||||
for (int y = 0; y < yCount; y++)
|
||||
{
|
||||
int srcOffset = srcCalculator.GetOffset(srcRegionX + x);
|
||||
int dstOffset = dstCalculator.GetOffset(dstRegionX + x);
|
||||
srcCalculator.SetY(srcRegionY + y);
|
||||
dstCalculator.SetY(dstRegionY + y);
|
||||
|
||||
*(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset);
|
||||
for (int x = 0; x < xCount; x++)
|
||||
{
|
||||
int srcOffset = srcCalculator.GetOffset(srcRegionX + x);
|
||||
int dstOffset = dstCalculator.GetOffset(dstRegionX + x);
|
||||
|
||||
*(T*)(dstBase + dstOffset) = *(T*)(srcBase + srcOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// OPT: This allocates a (potentially) huge temporary array and then copies an existing
|
||||
// region of memory into it, data that might get overwritten entirely anyways. Ideally this should
|
||||
// all be rewritten to use pooled arrays, but that gets complicated with packed data and strides
|
||||
Span<byte> dstSpan = memoryManager.GetSpan(dstGpuVa + (ulong)dstBaseOffset, dstSize).ToArray();
|
||||
|
||||
bool _ = srcBpp switch
|
||||
{
|
||||
1 => Convert<byte>(dstSpan, srcSpan),
|
||||
|
@ -174,6 +174,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
if (info.Width == info.Height * info.Height)
|
||||
{
|
||||
// Possibly used for a "3D texture" drawn onto a 2D surface.
|
||||
// Some games do this to generate a tone mapping LUT without rendering into 3D texture slices.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 1;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 3457;
|
||||
private const uint CodeGenVersion = 3472;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@ -1,23 +1,21 @@
|
||||
using FFmpeg.AutoGen;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
{
|
||||
unsafe class FFmpegContext : IDisposable
|
||||
{
|
||||
private readonly AVCodec_decode _decodeFrame;
|
||||
private static readonly av_log_set_callback_callback _logFunc;
|
||||
private readonly FFCodec.AVCodec_decode _decodeFrame;
|
||||
private static readonly FFmpegApi.av_log_set_callback_callback _logFunc;
|
||||
private readonly AVCodec* _codec;
|
||||
private AVPacket* _packet;
|
||||
private AVCodecContext* _context;
|
||||
|
||||
public FFmpegContext(AVCodecID codecId)
|
||||
{
|
||||
_codec = ffmpeg.avcodec_find_decoder(codecId);
|
||||
_codec = FFmpegApi.avcodec_find_decoder(codecId);
|
||||
if (_codec == null)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.FFmpeg, $"Codec wasn't found. Make sure you have the {codecId} codec present in your FFmpeg installation.");
|
||||
@ -25,7 +23,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
return;
|
||||
}
|
||||
|
||||
_context = ffmpeg.avcodec_alloc_context3(_codec);
|
||||
_context = FFmpegApi.avcodec_alloc_context3(_codec);
|
||||
if (_context == null)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.FFmpeg, "Codec context couldn't be allocated.");
|
||||
@ -33,14 +31,14 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
return;
|
||||
}
|
||||
|
||||
if (ffmpeg.avcodec_open2(_context, _codec, null) != 0)
|
||||
if (FFmpegApi.avcodec_open2(_context, _codec, null) != 0)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.FFmpeg, "Codec couldn't be opened.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_packet = ffmpeg.av_packet_alloc();
|
||||
_packet = FFmpegApi.av_packet_alloc();
|
||||
if (_packet == null)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.FFmpeg, "Packet couldn't be allocated.");
|
||||
@ -48,52 +46,39 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
return;
|
||||
}
|
||||
|
||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(_codec->decode.Pointer);
|
||||
int avCodecRawVersion = FFmpegApi.avcodec_version();
|
||||
int avCodecMajorVersion = avCodecRawVersion >> 16;
|
||||
int avCodecMinorVersion = (avCodecRawVersion >> 8) & 0xFF;
|
||||
|
||||
// libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union.
|
||||
if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24))
|
||||
{
|
||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodec*)_codec)->CodecCallback);
|
||||
}
|
||||
// libavcodec 59.x changed AvCodec private API layout.
|
||||
else if (avCodecMajorVersion == 59)
|
||||
{
|
||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
|
||||
}
|
||||
// libavcodec 58.x and lower
|
||||
else
|
||||
{
|
||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodecLegacy>*)_codec)->Decode);
|
||||
}
|
||||
}
|
||||
|
||||
static FFmpegContext()
|
||||
{
|
||||
SetRootPath();
|
||||
|
||||
_logFunc = Log;
|
||||
|
||||
// Redirect log output.
|
||||
ffmpeg.av_log_set_level(ffmpeg.AV_LOG_MAX_OFFSET);
|
||||
ffmpeg.av_log_set_callback(_logFunc);
|
||||
FFmpegApi.av_log_set_level(AVLog.MaxOffset);
|
||||
FFmpegApi.av_log_set_callback(_logFunc);
|
||||
}
|
||||
|
||||
private static void SetRootPath()
|
||||
private static void Log(void* ptr, AVLog level, string format, byte* vl)
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
// Configure FFmpeg search path
|
||||
Process lddProcess = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/sh",
|
||||
Arguments = "-c \"ldd $(which ffmpeg 2>/dev/null) | grep libavfilter\" 2>/dev/null",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true
|
||||
});
|
||||
|
||||
string lddOutput = lddProcess.StandardOutput.ReadToEnd();
|
||||
|
||||
lddProcess.WaitForExit();
|
||||
lddProcess.Close();
|
||||
|
||||
if (lddOutput.Contains(" => "))
|
||||
{
|
||||
ffmpeg.RootPath = Path.GetDirectoryName(lddOutput.Split(" => ")[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.FFmpeg, "FFmpeg wasn't found. Make sure that you have it installed and up to date.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Log(void* p0, int level, string format, byte* vl)
|
||||
{
|
||||
if (level > ffmpeg.av_log_get_level())
|
||||
if (level > FFmpegApi.av_log_get_level())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -102,65 +87,67 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
byte* lineBuffer = stackalloc byte[lineSize];
|
||||
int printPrefix = 1;
|
||||
|
||||
ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
|
||||
FFmpegApi.av_log_format_line(ptr, level, format, vl, lineBuffer, lineSize, &printPrefix);
|
||||
|
||||
string line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer).Trim();
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case ffmpeg.AV_LOG_PANIC:
|
||||
case ffmpeg.AV_LOG_FATAL:
|
||||
case ffmpeg.AV_LOG_ERROR:
|
||||
case AVLog.Panic:
|
||||
case AVLog.Fatal:
|
||||
case AVLog.Error:
|
||||
Logger.Error?.Print(LogClass.FFmpeg, line);
|
||||
break;
|
||||
case ffmpeg.AV_LOG_WARNING:
|
||||
case AVLog.Warning:
|
||||
Logger.Warning?.Print(LogClass.FFmpeg, line);
|
||||
break;
|
||||
case ffmpeg.AV_LOG_INFO:
|
||||
case AVLog.Info:
|
||||
Logger.Info?.Print(LogClass.FFmpeg, line);
|
||||
break;
|
||||
case ffmpeg.AV_LOG_VERBOSE:
|
||||
case ffmpeg.AV_LOG_DEBUG:
|
||||
case ffmpeg.AV_LOG_TRACE:
|
||||
case AVLog.Verbose:
|
||||
case AVLog.Debug:
|
||||
Logger.Debug?.Print(LogClass.FFmpeg, line);
|
||||
break;
|
||||
case AVLog.Trace:
|
||||
Logger.Trace?.Print(LogClass.FFmpeg, line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int DecodeFrame(Surface output, ReadOnlySpan<byte> bitstream)
|
||||
{
|
||||
ffmpeg.av_frame_unref(output.Frame);
|
||||
FFmpegApi.av_frame_unref(output.Frame);
|
||||
|
||||
int result;
|
||||
int gotFrame;
|
||||
|
||||
fixed (byte* ptr = bitstream)
|
||||
{
|
||||
_packet->data = ptr;
|
||||
_packet->size = bitstream.Length;
|
||||
_packet->Data = ptr;
|
||||
_packet->Size = bitstream.Length;
|
||||
result = _decodeFrame(_context, output.Frame, &gotFrame, _packet);
|
||||
}
|
||||
|
||||
if (gotFrame == 0)
|
||||
{
|
||||
ffmpeg.av_frame_unref(output.Frame);
|
||||
FFmpegApi.av_frame_unref(output.Frame);
|
||||
|
||||
// If the frame was not delivered, it was probably delayed.
|
||||
// Get the next delayed frame by passing a 0 length packet.
|
||||
_packet->data = null;
|
||||
_packet->size = 0;
|
||||
_packet->Data = null;
|
||||
_packet->Size = 0;
|
||||
result = _decodeFrame(_context, output.Frame, &gotFrame, _packet);
|
||||
|
||||
// We need to set B frames to 0 as we already consumed all delayed frames.
|
||||
// This prevents the decoder from trying to return a delayed frame next time.
|
||||
_context->has_b_frames = 0;
|
||||
_context->HasBFrames = 0;
|
||||
}
|
||||
|
||||
ffmpeg.av_packet_unref(_packet);
|
||||
FFmpegApi.av_packet_unref(_packet);
|
||||
|
||||
if (gotFrame == 0)
|
||||
{
|
||||
ffmpeg.av_frame_unref(output.Frame);
|
||||
FFmpegApi.av_frame_unref(output.Frame);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -172,14 +159,14 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
{
|
||||
fixed (AVPacket** ppPacket = &_packet)
|
||||
{
|
||||
ffmpeg.av_packet_free(ppPacket);
|
||||
FFmpegApi.av_packet_free(ppPacket);
|
||||
}
|
||||
|
||||
ffmpeg.avcodec_close(_context);
|
||||
FFmpegApi.avcodec_close(_context);
|
||||
|
||||
fixed (AVCodecContext** ppContext = &_context)
|
||||
{
|
||||
ffmpeg.avcodec_free_context(ppContext);
|
||||
FFmpegApi.avcodec_free_context(ppContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using FFmpeg.AutoGen;
|
||||
using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
|
||||
using Ryujinx.Graphics.Video;
|
||||
using System;
|
||||
|
||||
|
25
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs
Normal file
25
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct AVCodec
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public unsafe byte* Name;
|
||||
public unsafe byte* LongName;
|
||||
public int Type;
|
||||
public AVCodecID Id;
|
||||
public int Capabilities;
|
||||
public byte MaxLowRes;
|
||||
public unsafe AVRational* SupportedFramerates;
|
||||
public IntPtr PixFmts;
|
||||
public IntPtr SupportedSamplerates;
|
||||
public IntPtr SampleFmts;
|
||||
// Deprecated
|
||||
public unsafe ulong* ChannelLayouts;
|
||||
public unsafe IntPtr PrivClass;
|
||||
public IntPtr Profiles;
|
||||
public unsafe byte* WrapperName;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
171
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs
Normal file
171
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs
Normal file
@ -0,0 +1,171 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct AVCodecContext
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public unsafe IntPtr AvClass;
|
||||
public int LogLevelOffset;
|
||||
public int CodecType;
|
||||
public unsafe AVCodecLegacy* Codec;
|
||||
public AVCodecID CodecId;
|
||||
public uint CodecTag;
|
||||
public IntPtr PrivData;
|
||||
public IntPtr Internal;
|
||||
public IntPtr Opaque;
|
||||
public long BitRate;
|
||||
public int BitRateTolerance;
|
||||
public int GlobalQuality;
|
||||
public int CompressionLevel;
|
||||
public int Flags;
|
||||
public int Flags2;
|
||||
public IntPtr ExtraData;
|
||||
public int ExtraDataSize;
|
||||
public AVRational TimeBase;
|
||||
public int TicksPerFrame;
|
||||
public int Delay;
|
||||
public int Width;
|
||||
public int Height;
|
||||
public int CodedWidth;
|
||||
public int CodedHeight;
|
||||
public int GopSize;
|
||||
public int PixFmt;
|
||||
public IntPtr DrawHorizBand;
|
||||
public IntPtr GetFormat;
|
||||
public int MaxBFrames;
|
||||
public float BQuantFactor;
|
||||
public float BQuantOffset;
|
||||
public int HasBFrames;
|
||||
public float IQuantFactor;
|
||||
public float IQuantOffset;
|
||||
public float LumiMasking;
|
||||
public float TemporalCplxMasking;
|
||||
public float SpatialCplxMasking;
|
||||
public float PMasking;
|
||||
public float DarkMasking;
|
||||
public int SliceCount;
|
||||
public IntPtr SliceOffset;
|
||||
public AVRational SampleAspectRatio;
|
||||
public int MeCmp;
|
||||
public int MeSubCmp;
|
||||
public int MbCmp;
|
||||
public int IldctCmp;
|
||||
public int DiaSize;
|
||||
public int LastPredictorCount;
|
||||
public int MePreCmp;
|
||||
public int PreDiaSize;
|
||||
public int MeSubpelQuality;
|
||||
public int MeRange;
|
||||
public int SliceFlags;
|
||||
public int MbDecision;
|
||||
public IntPtr IntraMatrix;
|
||||
public IntPtr InterMatrix;
|
||||
public int IntraDcPrecision;
|
||||
public int SkipTop;
|
||||
public int SkipBottom;
|
||||
public int MbLmin;
|
||||
public int MbLmax;
|
||||
public int BidirRefine;
|
||||
public int KeyintMin;
|
||||
public int Refs;
|
||||
public int Mv0Threshold;
|
||||
public int ColorPrimaries;
|
||||
public int ColorPrc;
|
||||
public int Colorspace;
|
||||
public int ColorRange;
|
||||
public int ChromaSampleLocation;
|
||||
public int Slices;
|
||||
public int FieldOrder;
|
||||
public int SampleRate;
|
||||
public int Channels;
|
||||
public int SampleFmt;
|
||||
public int FrameSize;
|
||||
public int FrameNumber;
|
||||
public int BlockAlign;
|
||||
public int CutOff;
|
||||
public ulong ChannelLayout;
|
||||
public ulong RequestChannelLayout;
|
||||
public int AudioServiceType;
|
||||
public int RequestSampleFmt;
|
||||
public IntPtr GetBuffer2;
|
||||
public float QCompress;
|
||||
public float QBlur;
|
||||
public int QMin;
|
||||
public int QMax;
|
||||
public int MaxQdiff;
|
||||
public int RcBufferSize;
|
||||
public int RcOverrideCount;
|
||||
public IntPtr RcOverride;
|
||||
public long RcMaxRate;
|
||||
public long RcMinRate;
|
||||
public float RcMax_available_vbv_use;
|
||||
public float RcMin_vbv_overflow_use;
|
||||
public int RcInitialBufferOccupancy;
|
||||
public int Trellis;
|
||||
public IntPtr StatsOut;
|
||||
public IntPtr StatsIn;
|
||||
public int WorkaroundBugs;
|
||||
public int StrictStdCompliance;
|
||||
public int ErrorConcealment;
|
||||
public int Debug;
|
||||
public int ErrRecognition;
|
||||
public long ReorderedOpaque;
|
||||
public IntPtr HwAccel;
|
||||
public IntPtr HwAccelContext;
|
||||
public Array8<ulong> Error;
|
||||
public int DctAlgo;
|
||||
public int IdctAlgo;
|
||||
public int BitsPerCodedSample;
|
||||
public int BitsPerRawSample;
|
||||
public int LowRes;
|
||||
public int ThreadCount;
|
||||
public int ThreadType;
|
||||
public int ActiveThreadType;
|
||||
public int ThreadSafeCallbacks;
|
||||
public IntPtr Execute;
|
||||
public IntPtr Execute2;
|
||||
public int NsseWeight;
|
||||
public int Profile;
|
||||
public int Level;
|
||||
public int SkipLoopFilter;
|
||||
public int SkipIdct;
|
||||
public int SkipFrame;
|
||||
public IntPtr SubtitleHeader;
|
||||
public int SubtitleHeaderSize;
|
||||
public int InitialPadding;
|
||||
public AVRational Framerate;
|
||||
public int SwPixFmt;
|
||||
public AVRational PktTimebase;
|
||||
public IntPtr CodecDescriptor;
|
||||
public long PtsCorrectionNumFaultyPts;
|
||||
public long PtsCorrectionNumFaultyDts;
|
||||
public long PtsCorrectionLastPts;
|
||||
public long PtsCorrectionLastDts;
|
||||
public IntPtr SubCharenc;
|
||||
public int SubCharencMode;
|
||||
public int SkipAlpha;
|
||||
public int SeekPreroll;
|
||||
public int DebugMv;
|
||||
public IntPtr ChromaIntraMatrix;
|
||||
public IntPtr DumpSeparator;
|
||||
public IntPtr CodecWhitelist;
|
||||
public uint Properties;
|
||||
public IntPtr CodedSideData;
|
||||
public int NbCodedSideData;
|
||||
public IntPtr HwFramesCtx;
|
||||
public int SubTextFormat;
|
||||
public int TrailingPadding;
|
||||
public long MaxPixels;
|
||||
public IntPtr HwDeviceCtx;
|
||||
public int HwAccelFlags;
|
||||
public int applyCropping;
|
||||
public int ExtraHwFrames;
|
||||
public int DiscardDamagedPercentage;
|
||||
public long MaxSamples;
|
||||
public int ExportSideData;
|
||||
public IntPtr GetEncodeBuffer;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs
Normal file
8
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
enum AVCodecID
|
||||
{
|
||||
AV_CODEC_ID_H264 = 27,
|
||||
AV_CODEC_ID_VP8 = 139,
|
||||
}
|
||||
}
|
26
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecLegacy.cs
Normal file
26
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecLegacy.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct AVCodecLegacy
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public unsafe byte* Name;
|
||||
public unsafe byte* LongName;
|
||||
public int Type;
|
||||
public AVCodecID Id;
|
||||
public int Capabilities;
|
||||
public byte MaxLowRes;
|
||||
public unsafe AVRational* SupportedFramerates;
|
||||
public IntPtr PixFmts;
|
||||
public IntPtr SupportedSamplerates;
|
||||
public IntPtr SampleFmts;
|
||||
// Deprecated
|
||||
public unsafe ulong* ChannelLayouts;
|
||||
public unsafe IntPtr PrivClass;
|
||||
public IntPtr Profiles;
|
||||
public unsafe byte* WrapperName;
|
||||
public IntPtr ChLayouts;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
37
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs
Normal file
37
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct AVFrame
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public Array8<IntPtr> Data;
|
||||
public Array8<int> LineSize;
|
||||
public IntPtr ExtendedData;
|
||||
public int Width;
|
||||
public int Height;
|
||||
public int NumSamples;
|
||||
public int Format;
|
||||
public int KeyFrame;
|
||||
public int PictureType;
|
||||
public AVRational SampleAspectRatio;
|
||||
public long Pts;
|
||||
public long PktDts;
|
||||
public AVRational TimeBase;
|
||||
public int CodedPictureNumber;
|
||||
public int DisplayPictureNumber;
|
||||
public int Quality;
|
||||
public IntPtr Opaque;
|
||||
public int RepeatPicture;
|
||||
public int InterlacedFrame;
|
||||
public int TopFieldFirst;
|
||||
public int PaletteHasChanged;
|
||||
public long ReorderedOpaque;
|
||||
public int SampleRate;
|
||||
public ulong ChannelLayout;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
// NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference.
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs
Normal file
15
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
enum AVLog
|
||||
{
|
||||
Panic = 0,
|
||||
Fatal = 8,
|
||||
Error = 16,
|
||||
Warning = 24,
|
||||
Info = 32,
|
||||
Verbose = 40,
|
||||
Debug = 48,
|
||||
Trace = 56,
|
||||
MaxOffset = 64
|
||||
}
|
||||
}
|
26
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs
Normal file
26
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
using AVBufferRef = System.IntPtr;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct AVPacket
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public unsafe AVBufferRef *Buf;
|
||||
public long Pts;
|
||||
public long Dts;
|
||||
public unsafe byte* Data;
|
||||
public int Size;
|
||||
public int StreamIndex;
|
||||
public int Flags;
|
||||
public IntPtr SizeData;
|
||||
public int SizeDataElems;
|
||||
public long Duration;
|
||||
public long Position;
|
||||
public IntPtr Opaque;
|
||||
public unsafe AVBufferRef *OpaqueRef;
|
||||
public AVRational TimeBase;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs
Normal file
8
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
public struct AVRational
|
||||
{
|
||||
public int Numerator;
|
||||
public int Denominator;
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs
Normal file
23
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct FFCodec
|
||||
{
|
||||
public unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
|
||||
|
||||
#pragma warning disable CS0649
|
||||
public AVCodec Base;
|
||||
public int CapsInternalOrCbType;
|
||||
public int PrivDataSize;
|
||||
public IntPtr UpdateThreadContext;
|
||||
public IntPtr UpdateThreadContextForUser;
|
||||
public IntPtr Defaults;
|
||||
public IntPtr InitStaticData;
|
||||
public IntPtr Init;
|
||||
public IntPtr CodecCallback;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
// NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference.
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs
Normal file
23
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
struct FFCodecLegacy<T> where T: struct
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public T Base;
|
||||
public uint CapsInternalOrCbType;
|
||||
public int PrivDataSize;
|
||||
public IntPtr UpdateThreadContext;
|
||||
public IntPtr UpdateThreadContextForUser;
|
||||
public IntPtr Defaults;
|
||||
public IntPtr InitStaticData;
|
||||
public IntPtr Init;
|
||||
public IntPtr EncodeSub;
|
||||
public IntPtr Encode2;
|
||||
public IntPtr Decode;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
// NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference.
|
||||
}
|
||||
}
|
129
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs
Normal file
129
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
{
|
||||
static class FFmpegApi
|
||||
{
|
||||
public const string AvCodecLibraryName = "avcodec";
|
||||
public const string AvUtilLibraryName = "avutil";
|
||||
|
||||
private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new Dictionary<string, (int, int)>
|
||||
{
|
||||
{ AvCodecLibraryName, (58, 59) },
|
||||
{ AvUtilLibraryName, (56, 57) }
|
||||
};
|
||||
|
||||
private static string FormatLibraryNameForCurrentOs(string libraryName, int version)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return $"{libraryName}-{version}.dll";
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
return $"lib{libraryName}.so.{version}";
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
return $"lib{libraryName}.{version}.dylib";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Unsupported OS for FFmpeg: {RuntimeInformation.RuntimeIdentifier}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool TryLoadWhitelistedLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle)
|
||||
{
|
||||
handle = IntPtr.Zero;
|
||||
|
||||
if (_librariesWhitelist.TryGetValue(libraryName, out var value))
|
||||
{
|
||||
(int minVersion, int maxVersion) = value;
|
||||
|
||||
for (int version = maxVersion; version >= minVersion; version--)
|
||||
{
|
||||
if (NativeLibrary.TryLoad(FormatLibraryNameForCurrentOs(libraryName, version), assembly, searchPath, out handle))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static FFmpegApi()
|
||||
{
|
||||
NativeLibrary.SetDllImportResolver(typeof(FFmpegApi).Assembly, (name, assembly, path) =>
|
||||
{
|
||||
IntPtr handle;
|
||||
|
||||
if (name == AvUtilLibraryName && TryLoadWhitelistedLibrary(AvUtilLibraryName, assembly, path, out handle))
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
else if (name == AvCodecLibraryName && TryLoadWhitelistedLibrary(AvCodecLibraryName, assembly, path, out handle))
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
});
|
||||
}
|
||||
|
||||
public unsafe delegate void av_log_set_callback_callback(void* a0, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string a2, byte* a3);
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern AVFrame* av_frame_alloc();
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_frame_unref(AVFrame* frame);
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_free(AVFrame* frame);
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_log_set_level(AVLog level);
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_log_set_callback(av_log_set_callback_callback callback);
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern AVLog av_log_get_level();
|
||||
|
||||
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_log_format_line(void* ptr, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string fmt, byte* vl, byte* line, int lineSize, int* printPrefix);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern AVCodec* avcodec_find_decoder(AVCodecID id);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern AVCodecContext* avcodec_alloc_context3(AVCodec* codec);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern int avcodec_close(AVCodecContext* avctx);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void avcodec_free_context(AVCodecContext** avctx);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern AVPacket* av_packet_alloc();
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_packet_unref(AVPacket* pkt);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern void av_packet_free(AVPacket** pkt);
|
||||
|
||||
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static unsafe extern int avcodec_version();
|
||||
}
|
||||
}
|
@ -1,14 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FFmpeg.AutoGen" Version="4.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
using FFmpeg.AutoGen;
|
||||
using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
|
||||
using Ryujinx.Graphics.Video;
|
||||
using System;
|
||||
|
||||
@ -11,31 +11,31 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
||||
public int RequestedWidth { get; }
|
||||
public int RequestedHeight { get; }
|
||||
|
||||
public Plane YPlane => new Plane((IntPtr)Frame->data[0], Stride * Height);
|
||||
public Plane UPlane => new Plane((IntPtr)Frame->data[1], UvStride * UvHeight);
|
||||
public Plane VPlane => new Plane((IntPtr)Frame->data[2], UvStride * UvHeight);
|
||||
public Plane YPlane => new Plane((IntPtr)Frame->Data[0], Stride * Height);
|
||||
public Plane UPlane => new Plane((IntPtr)Frame->Data[1], UvStride * UvHeight);
|
||||
public Plane VPlane => new Plane((IntPtr)Frame->Data[2], UvStride * UvHeight);
|
||||
|
||||
public FrameField Field => Frame->interlaced_frame != 0 ? FrameField.Interlaced : FrameField.Progressive;
|
||||
public FrameField Field => Frame->InterlacedFrame != 0 ? FrameField.Interlaced : FrameField.Progressive;
|
||||
|
||||
public int Width => Frame->width;
|
||||
public int Height => Frame->height;
|
||||
public int Stride => Frame->linesize[0];
|
||||
public int Width => Frame->Width;
|
||||
public int Height => Frame->Height;
|
||||
public int Stride => Frame->LineSize[0];
|
||||
public int UvWidth => (Width + 1) >> 1;
|
||||
public int UvHeight => (Height + 1) >> 1;
|
||||
public int UvStride => Frame->linesize[1];
|
||||
public int UvStride => Frame->LineSize[1];
|
||||
|
||||
public Surface(int width, int height)
|
||||
{
|
||||
RequestedWidth = width;
|
||||
RequestedHeight = height;
|
||||
|
||||
Frame = ffmpeg.av_frame_alloc();
|
||||
Frame = FFmpegApi.av_frame_alloc();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ffmpeg.av_frame_unref(Frame);
|
||||
ffmpeg.av_free(Frame);
|
||||
FFmpegApi.av_frame_unref(Frame);
|
||||
FFmpegApi.av_free(Frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using FFmpeg.AutoGen;
|
||||
using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
|
||||
using Ryujinx.Graphics.Video;
|
||||
using System;
|
||||
|
||||
|
@ -92,7 +92,11 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
pushOpInfo.Consumers.Add(rightBlock, local);
|
||||
}
|
||||
|
||||
rightBlock.SyncTargets.Union(SyncTargets);
|
||||
foreach ((ulong key, SyncTarget value) in SyncTargets)
|
||||
{
|
||||
rightBlock.SyncTargets.Add(key, value);
|
||||
}
|
||||
|
||||
SyncTargets.Clear();
|
||||
|
||||
// Move push ops.
|
||||
|
@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
{
|
||||
InstConditional condOp = new InstConditional(op.RawOpCode);
|
||||
|
||||
if (op.Name == InstName.Exit && condOp.Ccc != Ccc.T)
|
||||
if ((op.Name == InstName.Bra || op.Name == InstName.Exit) && condOp.Ccc != Ccc.T)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -672,6 +672,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
// Make sure we found the correct address,
|
||||
// the push and pop instruction types must match, so:
|
||||
// - BRK can only consume addresses pushed by PBK.
|
||||
// - CONT can only consume addresses pushed by PCNT.
|
||||
// - SYNC can only consume addresses pushed by SSY.
|
||||
if (found)
|
||||
{
|
||||
|
@ -45,12 +45,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
if (isFP64)
|
||||
{
|
||||
return context.PackDouble2x32(
|
||||
context.Config.CreateCbuf(cbufSlot, cbufOffset),
|
||||
context.Config.CreateCbuf(cbufSlot, cbufOffset + 1));
|
||||
Cbuf(cbufSlot, cbufOffset),
|
||||
Cbuf(cbufSlot, cbufOffset + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.Config.CreateCbuf(cbufSlot, cbufOffset);
|
||||
return Cbuf(cbufSlot, cbufOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,6 +300,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
|
||||
if (operand.Type != OperandType.LocalVariable)
|
||||
{
|
||||
if (operand.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
Config.SetUsedConstantBuffer(operand.GetCbufSlot());
|
||||
}
|
||||
|
||||
return new AstOperand(operand);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
Operand addrLow = operation.GetSource(0);
|
||||
|
||||
Operand baseAddrLow = config.CreateCbuf(0, GetStorageCbOffset(config.Stage, storageIndex));
|
||||
Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex));
|
||||
|
||||
Operand baseAddrTrunc = Local();
|
||||
|
||||
@ -152,7 +152,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
Operand addrLow = operation.GetSource(0);
|
||||
|
||||
Operand baseAddrLow = config.CreateCbuf(0, UbeBaseOffset + storageIndex * StorageDescSize);
|
||||
Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize);
|
||||
|
||||
Operand baseAddrTrunc = Local();
|
||||
|
||||
|
@ -75,9 +75,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
int cbOffset = GetStorageCbOffset(config.Stage, slot);
|
||||
|
||||
Operand baseAddrLow = config.CreateCbuf(0, cbOffset);
|
||||
Operand baseAddrHigh = config.CreateCbuf(0, cbOffset + 1);
|
||||
Operand size = config.CreateCbuf(0, cbOffset + 2);
|
||||
Operand baseAddrLow = Cbuf(0, cbOffset);
|
||||
Operand baseAddrHigh = Cbuf(0, cbOffset + 1);
|
||||
Operand size = Cbuf(0, cbOffset + 2);
|
||||
|
||||
Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow);
|
||||
Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow);
|
||||
@ -164,7 +164,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
|
||||
bool isCoordNormalized = !isBindless && config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
||||
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
||||
|
||||
if (!hasInvalidOffset && isCoordNormalized)
|
||||
{
|
||||
|
@ -360,12 +360,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
UsedFeatures |= flags;
|
||||
}
|
||||
|
||||
public Operand CreateCbuf(int slot, int offset)
|
||||
{
|
||||
SetUsedConstantBuffer(slot);
|
||||
return OperandHelper.Cbuf(slot, offset);
|
||||
}
|
||||
|
||||
public void SetUsedConstantBuffer(int slot)
|
||||
{
|
||||
_usedConstantBuffers |= 1 << slot;
|
||||
|
@ -184,18 +184,35 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
|
||||
{
|
||||
LinuxError result;
|
||||
|
||||
bool shouldBlockAfterOperation = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (Blocking && flags.HasFlag(BsdSocketFlags.DontWait))
|
||||
{
|
||||
Blocking = false;
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
result = LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
receiveSize = -1;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
|
||||
if (shouldBlockAfterOperation)
|
||||
{
|
||||
Blocking = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public LinuxError ReceiveFrom(out int receiveSize, Span<byte> buffer, int size, BsdSocketFlags flags, out IPEndPoint remoteEndPoint)
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build7" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GtkSharp" Version="3.22.25.128" />
|
||||
<PackageReference Include="GtkSharp.Dependencies" Version="1.1.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build9" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
|
||||
<PackageReference Include="SPB" Version="0.0.4-build17" />
|
||||
|
@ -1509,7 +1509,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="_internetToggle">
|
||||
<property name="label" translatable="yes">Enable guest Internet access</property>
|
||||
<property name="label" translatable="yes">Enable Guest Internet Access</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
@ -1643,7 +1643,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="_mmHostUnsafe">
|
||||
<property name="label" translatable="yes">Host unchecked (fastest, unsafe)</property>
|
||||
<property name="label" translatable="yes">Host Unchecked (fastest, unsafe)</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
@ -1725,7 +1725,7 @@
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes"> - These may cause instability</property>
|
||||
<property name="label" translatable="yes"> (may cause instability)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@ -1749,7 +1749,7 @@
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="_expandRamToggle">
|
||||
<property name="label" translatable="yes">Expand DRAM size to 6GB</property>
|
||||
<property name="label" translatable="yes">Expand DRAM Size to 6GB</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
|
Reference in New Issue
Block a user