merge with upstream
This commit is contained in:
@@ -319,31 +319,12 @@ namespace Ryujinx.Ava
|
||||
public void VSyncModeToggle()
|
||||
{
|
||||
VSyncMode oldVSyncMode = Device.VSyncMode;
|
||||
VSyncMode newVSyncMode = VSyncMode.Switch;
|
||||
bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value;
|
||||
|
||||
switch (oldVSyncMode)
|
||||
{
|
||||
case VSyncMode.Switch:
|
||||
newVSyncMode = VSyncMode.Unbounded;
|
||||
break;
|
||||
case VSyncMode.Unbounded:
|
||||
if (customVSyncIntervalEnabled)
|
||||
{
|
||||
newVSyncMode = VSyncMode.Custom;
|
||||
}
|
||||
else
|
||||
{
|
||||
newVSyncMode = VSyncMode.Switch;
|
||||
}
|
||||
|
||||
break;
|
||||
case VSyncMode.Custom:
|
||||
newVSyncMode = VSyncMode.Switch;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateVSyncMode(this, new ReactiveEventArgs<VSyncMode>(oldVSyncMode, newVSyncMode));
|
||||
UpdateVSyncMode(this, new ReactiveEventArgs<VSyncMode>(
|
||||
oldVSyncMode,
|
||||
oldVSyncMode.Next(customVSyncIntervalEnabled))
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs<int> e)
|
||||
@@ -536,7 +517,7 @@ namespace Ryujinx.Ava
|
||||
Device?.System.ChangeDockedModeState(e.NewValue);
|
||||
}
|
||||
|
||||
private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
|
||||
public void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
|
||||
{
|
||||
Device?.SetVolume(e.NewValue);
|
||||
|
||||
@@ -957,7 +938,9 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
||||
ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.MatchSystemTime
|
||||
? 0
|
||||
: ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.TimeZone,
|
||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||
@@ -975,13 +958,13 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static IHardwareDeviceDriver InitializeAudio()
|
||||
{
|
||||
List<AudioBackend> availableBackends = new List<AudioBackend>
|
||||
{
|
||||
List<AudioBackend> availableBackends =
|
||||
[
|
||||
AudioBackend.SDL2,
|
||||
AudioBackend.SoundIo,
|
||||
AudioBackend.OpenAl,
|
||||
AudioBackend.Dummy,
|
||||
};
|
||||
AudioBackend.Dummy
|
||||
];
|
||||
|
||||
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
|
||||
|
||||
@@ -1058,6 +1041,7 @@ namespace Ryujinx.Ava
|
||||
if (_viewModel.StartGamesInFullscreen)
|
||||
{
|
||||
_viewModel.WindowState = WindowState.FullScreen;
|
||||
_viewModel.Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
}
|
||||
|
||||
if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUI)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style Selector="MenuItem.withCheckbox Viewbox#PART_IconPresenter">
|
||||
<Setter Property="MaxHeight" Value="36" />
|
||||
<Setter Property="MinHeight" Value="36" />
|
||||
<Setter Property="MaxWidth" Value="36" />
|
||||
<Setter Property="MinWidth" Value="36" />
|
||||
</Style>
|
||||
<Style Selector="MenuItem.withCheckbox ContentPresenter#PART_HeaderPresenter">
|
||||
<Setter Property="Padding" Value="-10,0,0,0" />
|
||||
</Style>
|
||||
</Styles>
|
||||
|
||||
@@ -218,6 +218,15 @@
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{DynamicResource ThemeControlBorderColor}" />
|
||||
</Style>
|
||||
<Style Selector="MenuItem.withCheckbox Viewbox#PART_IconPresenter">
|
||||
<Setter Property="MaxHeight" Value="36" />
|
||||
<Setter Property="MinHeight" Value="36" />
|
||||
<Setter Property="MaxWidth" Value="36" />
|
||||
<Setter Property="MinWidth" Value="36" />
|
||||
</Style>
|
||||
<Style Selector="MenuItem.withCheckbox ContentPresenter#PART_HeaderPresenter">
|
||||
<Setter Property="Padding" Value="-10,0,0,0" />
|
||||
</Style>
|
||||
<Style Selector="TabItem > ScrollViewer">
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource ThemeBackgroundColor}" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -144,7 +144,7 @@ namespace Ryujinx.Ava.Common
|
||||
|
||||
public static void ExtractSection(string destination, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
|
||||
{
|
||||
CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||
CancellationTokenSource cancellationToken = new();
|
||||
|
||||
UpdateWaitWindow waitingDialog = new(
|
||||
RyujinxApp.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
|
||||
@@ -171,14 +171,14 @@ namespace Ryujinx.Ava.Common
|
||||
}
|
||||
else
|
||||
{
|
||||
PartitionFileSystem pfsTemp = new PartitionFileSystem();
|
||||
PartitionFileSystem pfsTemp = new();
|
||||
pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||
pfs = pfsTemp;
|
||||
}
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using UniqueRef<IFile> ncaFile = new UniqueRef<IFile>();
|
||||
using UniqueRef<IFile> ncaFile = new();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
@@ -244,8 +244,8 @@ namespace Ryujinx.Ava.Common
|
||||
string source = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
string output = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
|
||||
using UniqueRef<IFileSystem> uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
|
||||
using UniqueRef<IFileSystem> uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
|
||||
using UniqueRef<IFileSystem> uniqueSourceFs = new(ncaFileSystem);
|
||||
using UniqueRef<IFileSystem> uniqueOutputFs = new(new LocalFileSystem(destination));
|
||||
|
||||
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
|
||||
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
|
||||
@@ -299,7 +299,7 @@ namespace Ryujinx.Ava.Common
|
||||
|
||||
public static void ExtractAoc(string destination, string updateFilePath, string updateName)
|
||||
{
|
||||
CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||
CancellationTokenSource cancellationToken = new();
|
||||
|
||||
UpdateWaitWindow waitingDialog = new(
|
||||
RyujinxApp.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
|
||||
@@ -317,13 +317,13 @@ namespace Ryujinx.Ava.Common
|
||||
string extension = Path.GetExtension(updateFilePath).ToLower();
|
||||
if (extension is ".nsp")
|
||||
{
|
||||
PartitionFileSystem pfsTemp = new PartitionFileSystem();
|
||||
PartitionFileSystem pfsTemp = new();
|
||||
pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||
IFileSystem pfs = pfsTemp;
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using UniqueRef<IFile> ncaFile = new UniqueRef<IFile>();
|
||||
using UniqueRef<IFile> ncaFile = new();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace Ryujinx.Ava.Common
|
||||
|
||||
if (publicDataNca is null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Extraction failure. The NCA was not present in the selected file");
|
||||
Logger.Error?.Print(LogClass.Application, "Extraction failure. The PublicData NCA was not present in the selected file");
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
@@ -349,10 +349,6 @@ namespace Ryujinx.Ava.Common
|
||||
return;
|
||||
}
|
||||
|
||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
int index = Nca.GetSectionIndexFromType(NcaSectionType.Data, publicDataNca.Header.ContentType);
|
||||
|
||||
try
|
||||
@@ -364,8 +360,8 @@ namespace Ryujinx.Ava.Common
|
||||
string source = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
string output = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
|
||||
using UniqueRef<IFileSystem> uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
|
||||
using UniqueRef<IFileSystem> uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
|
||||
using UniqueRef<IFileSystem> uniqueSourceFs = new(ncaFileSystem);
|
||||
using UniqueRef<IFileSystem> uniqueOutputFs = new(new LocalFileSystem(destination));
|
||||
|
||||
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
|
||||
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
|
||||
|
||||
@@ -44,6 +44,16 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
|
||||
SetDynamicValues(LocaleKeys.DialogConfirmationTitle, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.DialogUpdaterTitle, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.DialogErrorTitle, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.DialogWarningTitle, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.DialogExitTitle, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.DialogStopEmulationTitle, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
|
||||
}
|
||||
|
||||
public string this[LocaleKeys key]
|
||||
@@ -88,11 +98,16 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
public static string FormatDynamicValue(LocaleKeys key, params object[] values)
|
||||
=> Instance.UpdateAndGetDynamicValue(key, values);
|
||||
|
||||
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
|
||||
public void SetDynamicValues(LocaleKeys key, params object[] values)
|
||||
{
|
||||
_dynamicValues[key] = values;
|
||||
|
||||
OnPropertyChanged("Translation");
|
||||
}
|
||||
|
||||
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
|
||||
{
|
||||
SetDynamicValues(key, values);
|
||||
|
||||
return this[key];
|
||||
}
|
||||
@@ -125,7 +140,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode)
|
||||
{
|
||||
Dictionary<LocaleKeys, string> localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
Dictionary<LocaleKeys, string> localeStrings = new();
|
||||
|
||||
_localeData ??= EmbeddedResources.ReadAllText("Ryujinx/Assets/locales.json")
|
||||
.Into(it => JsonHelper.Deserialize(it, LocalesJsonContext.Default.LocalesJson));
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Ryujinx.Ava.Common.Models
|
||||
{
|
||||
public static XCITrimmerFileModel FromApplicationData(ApplicationData applicationData, XCIFileTrimmerLog logger)
|
||||
{
|
||||
XCIFileTrimmer trimmer = new XCIFileTrimmer(applicationData.Path, logger);
|
||||
XCIFileTrimmer trimmer = new(applicationData.Path, logger);
|
||||
|
||||
return new XCITrimmerFileModel(
|
||||
applicationData.Name,
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.Common
|
||||
{
|
||||
public static class ThemeManager
|
||||
{
|
||||
public static event Action ThemeChanged;
|
||||
|
||||
public static void OnThemeChanged()
|
||||
{
|
||||
ThemeChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
using DiscordRPC;
|
||||
using Gommon;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Ava.Utilities.PlayReport;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.Horizon;
|
||||
using Ryujinx.Horizon.Prepo.Types;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
@@ -18,12 +20,12 @@ namespace Ryujinx.Ava
|
||||
public static Timestamps GuestAppStartedAt { get; set; }
|
||||
|
||||
private static string VersionString
|
||||
=> (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}";
|
||||
=> (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}";
|
||||
|
||||
private static readonly string _description =
|
||||
ReleaseInformation.IsValid
|
||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
|
||||
: "dev build";
|
||||
private static readonly string _description =
|
||||
ReleaseInformation.IsValid
|
||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}"
|
||||
: "dev build";
|
||||
|
||||
private const string ApplicationId = "1293250299716173864";
|
||||
|
||||
@@ -32,6 +34,11 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static DiscordRpcClient _discordClient;
|
||||
private static RichPresence _discordPresenceMain;
|
||||
private static RichPresence _discordPresencePlaying;
|
||||
private static ApplicationMetadata _currentApp;
|
||||
|
||||
public static bool HasAssetImage(string titleId) => TitleIDs.DiscordGameAssetKeys.ContainsIgnoreCase(titleId);
|
||||
public static bool HasAnalyzer(string titleId) => PlayReports.Analyzer.TitleIds.ContainsIgnoreCase(titleId);
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
@@ -39,8 +46,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = "ryujinx",
|
||||
LargeImageText = TruncateToByteLength(_description)
|
||||
LargeImageKey = "ryujinx", LargeImageText = TruncateToByteLength(_description)
|
||||
},
|
||||
Details = "Main Menu",
|
||||
State = "Idling",
|
||||
@@ -49,6 +55,8 @@ namespace Ryujinx.Ava
|
||||
|
||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
||||
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
|
||||
HorizonStatic.PlayReport += HandlePlayReport;
|
||||
PlayReports.Initialize();
|
||||
}
|
||||
|
||||
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
||||
@@ -79,16 +87,15 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
if (titleId.TryGet(out string tid))
|
||||
SwitchToPlayingState(
|
||||
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||
Switch.Shared.Processes.ActiveApplication
|
||||
);
|
||||
else
|
||||
else
|
||||
SwitchToMainState();
|
||||
}
|
||||
|
||||
private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
||||
{
|
||||
_discordClient?.SetPresence(new RichPresence
|
||||
private static RichPresence CreatePlayingState(ApplicationMetadata appMeta, ProcessResult procRes) =>
|
||||
new()
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
@@ -102,10 +109,44 @@ namespace Ryujinx.Ava
|
||||
? $"Total play time: {ValueFormatUtils.FormatTimeSpan(appMeta.TimePlayed)}"
|
||||
: "Never played",
|
||||
Timestamps = GuestAppStartedAt ??= Timestamps.Now
|
||||
});
|
||||
};
|
||||
|
||||
private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
||||
{
|
||||
_discordClient?.SetPresence(_discordPresencePlaying ??= CreatePlayingState(appMeta, procRes));
|
||||
_currentApp = appMeta;
|
||||
}
|
||||
|
||||
private static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain);
|
||||
private static void SwitchToMainState()
|
||||
{
|
||||
_discordClient?.SetPresence(_discordPresenceMain);
|
||||
_discordPresencePlaying = null;
|
||||
_currentApp = null;
|
||||
}
|
||||
|
||||
private static void HandlePlayReport(PlayReport playReport)
|
||||
{
|
||||
if (_discordClient is null) return;
|
||||
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
||||
if (_discordPresencePlaying is null) return;
|
||||
|
||||
FormattedValue formattedValue =
|
||||
PlayReports.Analyzer.Format(TitleIDs.CurrentApplication.Value, _currentApp, playReport);
|
||||
|
||||
if (!formattedValue.Handled) return;
|
||||
|
||||
_discordPresencePlaying.Details = TruncateToByteLength(
|
||||
formattedValue.Reset
|
||||
? $"Playing {_currentApp.Title}"
|
||||
: formattedValue.FormattedString
|
||||
);
|
||||
|
||||
if (_discordClient.CurrentPresence.Details.Equals(_discordPresencePlaying.Details))
|
||||
return; //don't trigger an update if the set presence Details are identical to current
|
||||
|
||||
_discordClient.SetPresence(_discordPresencePlaying);
|
||||
Logger.Info?.Print(LogClass.UI, "Updated Discord RPC based on a supported play report.");
|
||||
}
|
||||
|
||||
private static string TruncateToByteLength(string input)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.GraphicsDriver;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Logging.Targets;
|
||||
@@ -26,6 +27,7 @@ using Ryujinx.SDL2.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
@@ -288,6 +290,10 @@ namespace Ryujinx.Headless
|
||||
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
|
||||
|
||||
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
|
||||
|
||||
if (_inputConfiguration.OfType<StandardControllerInputConfig>()
|
||||
.Any(ic => ic?.Led?.UseRainbow ?? false))
|
||||
Rainbow.Enable();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -152,7 +151,7 @@ namespace Ryujinx.Headless
|
||||
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
|
||||
|
||||
if (NeedsOverride(nameof(IgnoreControllerApplet)))
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet;
|
||||
|
||||
return;
|
||||
|
||||
@@ -394,7 +393,7 @@ namespace Ryujinx.Headless
|
||||
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
|
||||
public string GraphicsShadersDumpPath { get; set; }
|
||||
|
||||
[Option("graphics-backend", Required = false, Default = GraphicsBackend.OpenGl, HelpText = "Change Graphics Backend to use.")]
|
||||
[Option("graphics-backend", Required = false, Default = GraphicsBackend.Vulkan, HelpText = "Change Graphics Backend to use.")]
|
||||
public GraphicsBackend GraphicsBackend { get; set; }
|
||||
|
||||
[Option("preferred-gpu-vendor", Required = false, Default = "", HelpText = "When using the Vulkan backend, prefer using the GPU from the specified vendor.")]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Humanizer;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
@@ -514,7 +513,7 @@ namespace Ryujinx.Headless
|
||||
Exit();
|
||||
}
|
||||
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText)
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
|
||||
{
|
||||
SDL_MessageBoxData data = new()
|
||||
{
|
||||
@@ -522,7 +521,7 @@ namespace Ryujinx.Headless
|
||||
message = message,
|
||||
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle,
|
||||
window = WindowHandle
|
||||
};
|
||||
|
||||
for (int i = 0; i < buttonsText.Length; i++)
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.Input
|
||||
{
|
||||
internal class AvaloniaKeyboardDriver : IGamepadDriver
|
||||
{
|
||||
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
|
||||
private static readonly string[] _keyboardIdentifers = ["0"];
|
||||
private readonly Control _control;
|
||||
private readonly HashSet<AvaKey> _pressedKeys;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.Input
|
||||
public AvaloniaKeyboardDriver(Control control)
|
||||
{
|
||||
_control = control;
|
||||
_pressedKeys = new HashSet<AvaKey>();
|
||||
_pressedKeys = [];
|
||||
|
||||
_control.KeyDown += OnKeyPress;
|
||||
_control.KeyUp += OnKeyRelease;
|
||||
|
||||
@@ -7,7 +7,8 @@ namespace Ryujinx.Ava.Input
|
||||
{
|
||||
internal static class AvaloniaKeyboardMappingHelper
|
||||
{
|
||||
private static readonly AvaKey[] _keyMapping = {
|
||||
private static readonly AvaKey[] _keyMapping =
|
||||
[
|
||||
// NOTE: Invalid
|
||||
AvaKey.None,
|
||||
|
||||
@@ -143,8 +144,8 @@ namespace Ryujinx.Ava.Input
|
||||
AvaKey.OemBackslash,
|
||||
|
||||
// NOTE: invalid
|
||||
AvaKey.None,
|
||||
};
|
||||
AvaKey.None
|
||||
];
|
||||
|
||||
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;
|
||||
|
||||
|
||||
@@ -5,11 +5,9 @@ using Gommon;
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Projektanker.Icons.Avalonia.FontAwesome;
|
||||
using Projektanker.Icons.Avalonia.MaterialDesign;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Ava.Utilities.SystemInfo;
|
||||
using Ryujinx.Common;
|
||||
@@ -23,7 +21,6 @@ using Ryujinx.SDL2.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -50,6 +47,7 @@ namespace Ryujinx.Ava
|
||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
|
||||
{
|
||||
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PreviewerDetached = true;
|
||||
@@ -112,7 +110,11 @@ namespace Ryujinx.Ava
|
||||
// Hook unhandled exception and process exit events.
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
||||
=> ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating);
|
||||
TaskScheduler.UnobservedTaskException += (sender, e)
|
||||
=> ProcessUnhandledException(sender, e.Exception, false);
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit();
|
||||
|
||||
|
||||
|
||||
// Setup base data directory.
|
||||
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
|
||||
@@ -206,6 +208,16 @@ namespace Ryujinx.Ava
|
||||
_ => ConfigurationState.Instance.Graphics.GraphicsBackend
|
||||
};
|
||||
|
||||
// Check if backend threading was overridden
|
||||
if (CommandLineState.OverrideBackendThreading is not null)
|
||||
ConfigurationState.Instance.Graphics.BackendThreading.Value = CommandLineState.OverrideBackendThreading.ToLower() switch
|
||||
{
|
||||
"auto" => BackendThreading.Auto,
|
||||
"off" => BackendThreading.Off,
|
||||
"on" => BackendThreading.On,
|
||||
_ => ConfigurationState.Instance.Graphics.BackendThreading
|
||||
};
|
||||
|
||||
// Check if docked mode was overriden.
|
||||
if (CommandLineState.OverrideDockedMode.HasValue)
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
|
||||
@@ -272,9 +284,7 @@ namespace Ryujinx.Ava
|
||||
log.PrintMsg(LogClass.Application, message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (isTerminating)
|
||||
Exit();
|
||||
}
|
||||
|
||||
@@ -123,13 +123,11 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</AvaloniaResource>
|
||||
<AvaloniaResource Include="Assets\Styles\Styles.xaml" />
|
||||
<AvaloniaResource Include="Assets\Styles\CheckboxMenuItemStyle.axaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\locales.json" />
|
||||
<None Remove="Assets\Styles\Themes.xaml" />
|
||||
<None Remove="Assets\Styles\CheckboxMenuItemStyle.xaml" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
@@ -151,7 +149,6 @@
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Assets\locales.json" />
|
||||
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
||||
<EmbeddedResource Include="Assets\Styles\CheckboxMenuItemStyle.axaml" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
|
||||
@@ -16,6 +16,5 @@
|
||||
<Application.Styles>
|
||||
<sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" />
|
||||
<StyleInclude Source="/Assets/Styles/Styles.xaml" />
|
||||
<StyleInclude Source="/Assets/Styles/CheckboxMenuItemStyle.axaml"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
|
||||
@@ -22,16 +22,21 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
public class RyujinxApp : Application
|
||||
{
|
||||
internal static string FormatTitle(LocaleKeys? windowTitleKey = null)
|
||||
public static event Action ThemeChanged;
|
||||
|
||||
internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true)
|
||||
=> windowTitleKey is null
|
||||
? $"{FullAppName} {Program.Version}"
|
||||
: $"{FullAppName} {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}";
|
||||
? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}"
|
||||
: $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)} - {LocaleManager.Instance[windowTitleKey.Value]}";
|
||||
|
||||
public static readonly string FullAppName = ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx";
|
||||
public static readonly string FullAppName = string.Intern(ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx");
|
||||
|
||||
public static MainWindow MainWindow => Current!
|
||||
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()
|
||||
.MainWindow.Cast<MainWindow>();
|
||||
|
||||
public static IClassicDesktopStyleApplicationLifetime AppLifetime => Current!
|
||||
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>();
|
||||
|
||||
public static bool IsClipboardAvailable(out IClipboard clipboard)
|
||||
{
|
||||
@@ -109,7 +114,7 @@ namespace Ryujinx.Ava
|
||||
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
||||
}
|
||||
|
||||
ThemeManager.OnThemeChanged();
|
||||
ThemeChanged?.Invoke();
|
||||
|
||||
RequestedThemeVariant = baseStyle switch
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
bool okPressed = false;
|
||||
|
||||
if (ConfigurationState.Instance.System.IgnoreApplet)
|
||||
if (ConfigurationState.Instance.System.IgnoreControllerApplet)
|
||||
return false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
@@ -75,31 +75,32 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
bool opened = false;
|
||||
|
||||
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
||||
title,
|
||||
message,
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||
(int)Symbol.Important,
|
||||
deferEvent,
|
||||
async window =>
|
||||
{
|
||||
if (opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
title,
|
||||
message,
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||
(int)Symbol.Important,
|
||||
deferEvent,
|
||||
async window =>
|
||||
{
|
||||
if (opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
opened = true;
|
||||
|
||||
_parent.SettingsWindow = new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager);
|
||||
_parent.SettingsWindow =
|
||||
new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager);
|
||||
|
||||
await _parent.SettingsWindow.ShowDialog(window);
|
||||
await _parent.SettingsWindow.ShowDialog(window);
|
||||
|
||||
_parent.SettingsWindow = null;
|
||||
_parent.SettingsWindow = null;
|
||||
|
||||
opened = false;
|
||||
});
|
||||
opened = false;
|
||||
});
|
||||
|
||||
if (response == UserResult.Ok)
|
||||
{
|
||||
@@ -110,7 +111,9 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||
LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
|
||||
|
||||
dialogCloseEvent.Set();
|
||||
}
|
||||
@@ -134,7 +137,9 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
try
|
||||
{
|
||||
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
|
||||
(UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
|
||||
(UserResult result, string userInput) =
|
||||
await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard],
|
||||
args);
|
||||
|
||||
if (result == UserResult.Ok)
|
||||
{
|
||||
@@ -146,7 +151,9 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
error = true;
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||
LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -172,12 +179,13 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
try
|
||||
{
|
||||
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
|
||||
SoftwareKeyboardUIArgs args = new SoftwareKeyboardUIArgs();
|
||||
SoftwareKeyboardUIArgs args = new();
|
||||
args.KeyboardMode = KeyboardMode.Default;
|
||||
args.InitialText = "Ryujinx";
|
||||
args.StringLengthMin = 1;
|
||||
args.StringLengthMax = 25;
|
||||
(UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.CabinetDialog], args);
|
||||
(UserResult result, string userInput) =
|
||||
await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.CabinetDialog], args);
|
||||
if (result == UserResult.Ok)
|
||||
{
|
||||
inputText = userInput;
|
||||
@@ -201,11 +209,13 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
dialogCloseEvent.Set();
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.CabinetScanDialog],
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.CabinetTitle]);
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
LocaleManager.Instance[LocaleKeys.CabinetScanDialog],
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.CabinetTitle]
|
||||
);
|
||||
});
|
||||
dialogCloseEvent.WaitOne();
|
||||
}
|
||||
@@ -217,7 +227,8 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
_parent.ViewModel.AppHost?.Stop();
|
||||
}
|
||||
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons,
|
||||
(uint Module, uint Description)? errorCode = null)
|
||||
{
|
||||
ManualResetEvent dialogCloseEvent = new(false);
|
||||
|
||||
@@ -229,9 +240,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
ErrorAppletWindow msgDialog = new(_parent, buttons, message)
|
||||
{
|
||||
Title = title,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen,
|
||||
Width = 400
|
||||
Title = title, WindowStartupLocation = WindowStartupLocation.CenterScreen, Width = 400
|
||||
};
|
||||
|
||||
object response = await msgDialog.Run();
|
||||
@@ -249,7 +258,9 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
dialogCloseEvent.Set();
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||
LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -259,38 +270,36 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
}
|
||||
|
||||
public IDynamicTextInputHandler CreateDynamicTextInputHandler() => new AvaloniaDynamicTextInputHandler(_parent);
|
||||
|
||||
|
||||
public UserProfile ShowPlayerSelectDialog()
|
||||
{
|
||||
UserId selected = UserId.Null;
|
||||
byte[] defaultGuestImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/GuestUserImage.jpg");
|
||||
UserProfile guest = new UserProfile(new UserId("00000000000000000000000000000080"), "Guest", defaultGuestImage);
|
||||
|
||||
UserProfile guest = new(new UserId("00000000000000000000000000000080"), "Guest", defaultGuestImage);
|
||||
|
||||
ManualResetEvent dialogCloseEvent = new(false);
|
||||
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
ObservableCollection<BaseModel> profiles = [];
|
||||
NavigationDialogHost nav = new();
|
||||
|
||||
|
||||
_parent.AccountManager.GetAllUsers()
|
||||
.OrderBy(x => x.Name)
|
||||
.ForEach(profile => profiles.Add(new Models.UserProfile(profile, nav)));
|
||||
|
||||
|
||||
profiles.Add(new Models.UserProfile(guest, nav));
|
||||
UserSelectorDialogViewModel viewModel = new()
|
||||
ProfileSelectorDialogViewModel viewModel = new()
|
||||
{
|
||||
Profiles = profiles,
|
||||
SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
|
||||
Profiles = profiles, SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
|
||||
};
|
||||
UserSelectorDialog content = new(viewModel);
|
||||
(selected, _) = await UserSelectorDialog.ShowInputDialog(content);
|
||||
|
||||
(selected, _) = await ProfileSelectorDialog.ShowInputDialog(viewModel);
|
||||
|
||||
dialogCloseEvent.Set();
|
||||
});
|
||||
|
||||
|
||||
dialogCloseEvent.WaitOne();
|
||||
|
||||
|
||||
UserProfile profile = _parent.AccountManager.LastOpenedUser;
|
||||
if (selected == guest.UserId)
|
||||
{
|
||||
@@ -311,6 +320,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Applet.UserSelectorDialog"
|
||||
x:Class="Ryujinx.Ava.UI.Applet.ProfileSelectorDialog"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -12,14 +12,9 @@
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True"
|
||||
x:DataType="viewModels:UserSelectorDialogViewModel">
|
||||
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
|
||||
x:DataType="viewModels:ProfileSelectorDialogViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:UserSelectorDialogViewModel />
|
||||
<viewModels:ProfileSelectorDialogViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
@@ -80,7 +75,7 @@
|
||||
Height="96"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
MaxWidth="90"
|
||||
@@ -110,12 +105,5 @@
|
||||
</ListBox>
|
||||
</Border>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0 24 0 0"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -6,27 +6,25 @@ using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||
using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
public partial class UserSelectorDialog : UserControl, INotifyPropertyChanged
|
||||
public partial class ProfileSelectorDialog : UserControl
|
||||
{
|
||||
public UserSelectorDialogViewModel ViewModel { get; set; }
|
||||
public ProfileSelectorDialogViewModel ViewModel { get; set; }
|
||||
|
||||
public UserSelectorDialog(UserSelectorDialogViewModel viewModel)
|
||||
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
|
||||
{
|
||||
DataContext = ViewModel = viewModel;
|
||||
|
||||
InitializeComponent();
|
||||
ViewModel = viewModel;
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
private void Grid_PointerEntered(object sender, PointerEventArgs e)
|
||||
@@ -56,7 +54,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
if (ViewModel.Profiles[selectedIndex] is UserProfile userProfile)
|
||||
{
|
||||
ViewModel.SelectedUserId = userProfile.UserId;
|
||||
Logger.Info?.Print(LogClass.UI, $"Selected user: {userProfile.UserId}");
|
||||
Logger.Info?.Print(LogClass.UI, $"Selected: {userProfile.UserId}", "ProfileSelector");
|
||||
|
||||
ObservableCollection<BaseModel> newProfiles = [];
|
||||
|
||||
@@ -64,7 +62,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
if (item is UserProfile originalItem)
|
||||
{
|
||||
UserProfileSft profile = new UserProfileSft(originalItem.UserId, originalItem.Name, originalItem.Image);
|
||||
UserProfileSft profile = new(originalItem.UserId, originalItem.Name, originalItem.Image);
|
||||
|
||||
if (profile.UserId == ViewModel.SelectedUserId)
|
||||
{
|
||||
@@ -81,7 +79,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<(UserId Id, bool Result)> ShowInputDialog(UserSelectorDialog content)
|
||||
public static async Task<(UserId Id, bool Result)> ShowInputDialog(ProfileSelectorDialogViewModel viewModel)
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
@@ -89,22 +87,25 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.Cancel],
|
||||
Content = content,
|
||||
Content = new ProfileSelectorDialog(viewModel),
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
|
||||
UserId result = UserId.Null;
|
||||
bool input = false;
|
||||
|
||||
contentDialog.Closed += Handler;
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
|
||||
return (result, input);
|
||||
|
||||
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Result == ContentDialogResult.Primary)
|
||||
{
|
||||
if (contentDialog.Content is UserSelectorDialog view)
|
||||
{
|
||||
result = view.ViewModel.SelectedUserId;
|
||||
input = true;
|
||||
}
|
||||
result = viewModel.SelectedUserId;
|
||||
input = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -112,12 +113,6 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
input = false;
|
||||
}
|
||||
}
|
||||
|
||||
contentDialog.Closed += Handler;
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
|
||||
return (result, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,12 +144,12 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
case KeyboardMode.Numeric:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumeric);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(NumericCharacterValidation.IsNumeric);
|
||||
_checkInput = text => text.All(CharacterValidation.IsNumeric);
|
||||
break;
|
||||
case KeyboardMode.Alphabet:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value));
|
||||
_checkInput = text => text.All(value => !CharacterValidation.IsCJK(value));
|
||||
break;
|
||||
case KeyboardMode.ASCII:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII);
|
||||
|
||||
@@ -19,6 +19,17 @@
|
||||
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||
Icon="{ext:Icon fa-solid fa-bookmark}"
|
||||
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
||||
<MenuItem
|
||||
IsVisible="{Binding HasCompatibilityEntry}"
|
||||
Click="OpenApplicationCompatibility_Click"
|
||||
Header="{ext:Locale GameListContextMenuShowCompatEntry}"
|
||||
Icon="{ext:Icon mdi-gamepad}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuShowCompatEntryToolTip}"/>
|
||||
<MenuItem
|
||||
Click="OpenApplicationData_Click"
|
||||
Header="{ext:Locale GameListContextMenuShowGameData}"
|
||||
Icon="{ext:Icon mdi-chart-line}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuShowGameDataToolTip}"/>
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Click="OpenUserSaveDirectory_Click"
|
||||
@@ -74,13 +85,17 @@
|
||||
Header="{ext:Locale GameListContextMenuTrimXCI}"
|
||||
IsEnabled="{Binding TrimXCIEnabled}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
|
||||
<MenuItem
|
||||
Click="PurgePtcCache_Click"
|
||||
Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}"
|
||||
Icon="{ext:Icon mdi-refresh}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
||||
<MenuItem
|
||||
Click="NukePtcCache_Click"
|
||||
Header="{ext:Locale GameListContextMenuCacheManagementNukePptc}"
|
||||
Icon="{ext:Icon mdi-delete-alert}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementNukePptcToolTip}" />
|
||||
<MenuItem
|
||||
Click="PurgeShaderCache_Click"
|
||||
Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
||||
@@ -107,6 +122,7 @@
|
||||
Header="{ext:Locale GameListContextMenuExtractDataRomFS}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
||||
<MenuItem
|
||||
IsVisible="{Binding HasDlc}"
|
||||
Click="ExtractAocRomFs_Click"
|
||||
Header="{ext:Locale GameListContextMenuExtractDataAocRomFS}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataAocRomFSToolTip}" />
|
||||
|
||||
@@ -2,9 +2,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LibHac;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common;
|
||||
@@ -15,16 +12,14 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Compat;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
@@ -128,7 +123,11 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
public async void OpenModManager_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await ModManagerWindow.Show(viewModel.SelectedApplication.Id, viewModel.SelectedApplication.Name);
|
||||
await ModManagerWindow.Show(
|
||||
viewModel.SelectedApplication.Id,
|
||||
viewModel.SelectedApplication.IdBase,
|
||||
viewModel.ApplicationLibrary,
|
||||
viewModel.SelectedApplication.Name);
|
||||
}
|
||||
|
||||
public async void PurgePtcCache_Click(object sender, RoutedEventArgs args)
|
||||
@@ -146,7 +145,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0"));
|
||||
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1"));
|
||||
|
||||
List<FileInfo> cacheFiles = new();
|
||||
List<FileInfo> cacheFiles = [];
|
||||
|
||||
if (mainDir.Exists)
|
||||
{
|
||||
@@ -175,6 +174,52 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public async void NukePtcCache_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
return;
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateLocalizedConfirmationDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCNukeMessage, viewModel.SelectedApplication.Name)
|
||||
);
|
||||
|
||||
if (result == UserResult.Yes)
|
||||
{
|
||||
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0"));
|
||||
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1"));
|
||||
|
||||
List<FileInfo> cacheFiles = [];
|
||||
|
||||
if (mainDir.Exists)
|
||||
{
|
||||
cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
|
||||
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
|
||||
}
|
||||
|
||||
if (backupDir.Exists)
|
||||
{
|
||||
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
|
||||
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
|
||||
}
|
||||
|
||||
if (cacheFiles.Count > 0)
|
||||
{
|
||||
foreach (FileInfo file in cacheFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
@@ -189,8 +234,8 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader"));
|
||||
|
||||
List<DirectoryInfo> oldCacheDirectories = new();
|
||||
List<FileInfo> newCacheFiles = new();
|
||||
List<DirectoryInfo> oldCacheDirectories = [];
|
||||
List<FileInfo> newCacheFiles = [];
|
||||
|
||||
if (shaderCacheDir.Exists)
|
||||
{
|
||||
@@ -289,7 +334,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
return;
|
||||
|
||||
DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.IdBase, viewModel.ApplicationLibrary);
|
||||
DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.Id, viewModel.ApplicationLibrary);
|
||||
|
||||
if (selectedDlc is not null)
|
||||
{
|
||||
@@ -341,6 +386,18 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
viewModel.SelectedApplication.Icon
|
||||
);
|
||||
}
|
||||
|
||||
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
|
||||
}
|
||||
|
||||
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await ApplicationDataView.Show(viewModel.SelectedApplication);
|
||||
}
|
||||
|
||||
public async void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
@@ -350,12 +407,8 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
public async void TrimXCI_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
MainWindowViewModel viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await viewModel.TrimXCIFile(viewModel.SelectedApplication.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
189
src/Ryujinx/UI/Controls/ApplicationDataView.axaml
Normal file
189
src/Ryujinx/UI/Controls/ApplicationDataView.axaml
Normal file
@@ -0,0 +1,189 @@
|
||||
<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:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:ext="using:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.UI.Controls.ApplicationDataView"
|
||||
x:DataType="viewModels:ApplicationDataViewModel">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Margin="0"
|
||||
MaxWidth="256"
|
||||
MinWidth="256"
|
||||
Source="{Binding AppData.Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
<Border Margin="5, 0" Width="1" Height="256" BorderBrush="Gray" Background="Gray" />
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid
|
||||
RowDefinitions="Auto,Auto,Auto"
|
||||
ColumnDefinitions="*">
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock HorizontalAlignment="Left"
|
||||
Text="{Binding FormattedVersion}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock HorizontalAlignment="Left"
|
||||
Text="{Binding FormattedDeveloper}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock HorizontalAlignment="Stretch"
|
||||
Text="{Binding FormattedFileExtension}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock HorizontalAlignment="Stretch"
|
||||
Text="{Binding FormattedFileSize}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Separator Grid.Row="1" Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||
<StackPanel Grid.Row="2"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Vertical"
|
||||
Spacing="5">
|
||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding AppData.HasPlayabilityInfo}">
|
||||
<TextBlock Padding="0, 0, 5, 0" Text="{ext:Locale GameListHeaderCompatibilityStatus}" />
|
||||
<Button
|
||||
Click="PlayabilityStatus_OnClick"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Background="{DynamicResource AppListBackgroundColor}"
|
||||
Padding="0">
|
||||
<TextBlock
|
||||
Margin="1.5"
|
||||
Tag="{Binding AppData.IdString}"
|
||||
Text="{Binding AppData.LocalizedStatus}"
|
||||
Foreground="{Binding AppData.PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap">
|
||||
<ToolTip.Tip>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock
|
||||
Text="{Binding AppData.LocalizedStatusTooltip}" />
|
||||
<Separator
|
||||
Margin="0, 10, 0, 10"
|
||||
IsVisible="{Binding AppData.HasCompatibilityLabels}" />
|
||||
<TextBlock
|
||||
IsVisible="{Binding AppData.HasCompatibilityLabels}"
|
||||
Text="{Binding AppData.FormattedCompatibilityLabels}" />
|
||||
</StackPanel>
|
||||
</ToolTip.Tip>
|
||||
</TextBlock>
|
||||
<Button.Styles>
|
||||
<Style Selector="Button">
|
||||
<Setter Property="MinWidth"
|
||||
Value="0" />
|
||||
<!-- avoids very wide buttons from the overall project avalonia style -->
|
||||
</Style>
|
||||
</Button.Styles>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Padding="0, 0, 5, 0" Text="{ext:Locale GameListHeaderTitleId}" />
|
||||
<Button
|
||||
Click="IdString_OnClick"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Background="{DynamicResource AppListBackgroundColor}"
|
||||
Padding="0">
|
||||
<TextBlock
|
||||
Margin="1.5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding AppData.IdString}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||
<StackPanel Orientation="Vertical" Spacing="5">
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasRichPresenceAsset}"/>
|
||||
<TextBlock
|
||||
Foreground="ForestGreen"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding AppData.HasRichPresenceAsset}"
|
||||
Text="{ext:Locale GameInfoRpcImage}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" >
|
||||
</TextBlock>
|
||||
<ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasRichPresenceAsset}"/>
|
||||
<TextBlock
|
||||
Foreground="Red"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding !AppData.HasRichPresenceAsset}"
|
||||
Text="{ext:Locale GameInfoRpcImage}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" >
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5" ToolTip.Tip="{Binding DynamicRichPresenceDescription}">
|
||||
<ui:SymbolIcon
|
||||
Foreground="ForestGreen"
|
||||
Symbol="Checkmark"
|
||||
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
|
||||
<TextBlock
|
||||
Foreground="ForestGreen"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"
|
||||
Text="{ext:Locale GameInfoRpcDynamic}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap">
|
||||
</TextBlock>
|
||||
<ui:SymbolIcon
|
||||
Foreground="Red"
|
||||
Symbol="Cancel"
|
||||
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
|
||||
<TextBlock
|
||||
Foreground="Red"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"
|
||||
Text="{ext:Locale GameInfoRpcDynamic}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" >
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding AppData.HasLdnGames}"
|
||||
Text="{Binding FormattedLdnInfo}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<Separator IsVisible="{Binding AppData.HasLdnGames}" Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||
<StackPanel Orientation="Vertical" Spacing="5">
|
||||
<Grid
|
||||
ColumnDefinitions="Auto,*,Auto">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Text="{ext:Locale GameListHeaderLastPlayed}"
|
||||
VerticalAlignment="Top"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="NoWrap" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Text="{Binding AppData.LastPlayedString}"
|
||||
TextAlignment="End"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
<Grid
|
||||
ColumnDefinitions="Auto,*,Auto"
|
||||
IsVisible="{Binding AppData.HasPlayedPreviously}">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Text="{ext:Locale GameListHeaderTimePlayed}"
|
||||
VerticalAlignment="Top"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="NoWrap" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding AppData.TimePlayedString}"
|
||||
TextAlignment="End"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
83
src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs
Normal file
83
src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Compat;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class ApplicationDataView : UserControl
|
||||
{
|
||||
public static async Task Show(ApplicationData appData)
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
Title = appData.Name,
|
||||
PrimaryButtonText = string.Empty,
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||
MinWidth = 256,
|
||||
Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) }
|
||||
};
|
||||
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
closeButton.Setters.Add(new Setter(WidthProperty, 160d));
|
||||
|
||||
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty,
|
||||
Avalonia.Layout.HorizontalAlignment.Center));
|
||||
|
||||
contentDialog.Styles.Add(closeButton);
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
}
|
||||
|
||||
public ApplicationDataView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not Button { Content: TextBlock playabilityLabel })
|
||||
return;
|
||||
|
||||
if (RyujinxApp.AppLifetime.Windows.TryGetFirst(x => x is ContentDialogOverlayWindow, out Window window))
|
||||
window.Close(ContentDialogResult.None);
|
||||
|
||||
await CompatibilityList.Show((string)playabilityLabel.Tag);
|
||||
}
|
||||
|
||||
private async void IdString_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is not MainWindowViewModel mwvm)
|
||||
return;
|
||||
|
||||
if (sender is not Button { Content: TextBlock idText })
|
||||
return;
|
||||
|
||||
if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard))
|
||||
return;
|
||||
|
||||
ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
|
||||
if (appData is null)
|
||||
return;
|
||||
|
||||
await clipboard.SetTextAsync(appData.IdString);
|
||||
|
||||
NotificationHelper.ShowInformation(
|
||||
"Copied Title ID",
|
||||
$"{appData.Name} ({appData.IdString})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
mc:Ignorable="d"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
@@ -68,7 +65,7 @@
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
||||
Source="{Binding Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
<Panel
|
||||
Grid.Row="1"
|
||||
Height="50"
|
||||
|
||||
@@ -2,7 +2,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using System;
|
||||
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
mc:Ignorable="d"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
@@ -62,7 +59,7 @@
|
||||
Classes.large="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridLarge}"
|
||||
Classes.normal="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridMedium}"
|
||||
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
|
||||
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
||||
Source="{Binding Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
<Border
|
||||
Grid.Column="2"
|
||||
Margin="0,0,5,0"
|
||||
@@ -89,6 +86,41 @@
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Click="PlayabilityStatus_OnClick"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding HasPlayabilityInfo}"
|
||||
Background="{DynamicResource AppListBackgroundColor}"
|
||||
Margin="-1, 0, 0, 0"
|
||||
Padding="0">
|
||||
<ToolTip.Tip>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock
|
||||
Text="{Binding LocalizedStatusTooltip}" />
|
||||
<Separator
|
||||
Margin="0, 10, 0, 10"
|
||||
IsVisible="{Binding HasCompatibilityLabels}" />
|
||||
<TextBlock
|
||||
IsVisible="{Binding HasCompatibilityLabels}"
|
||||
Text="{Binding FormattedCompatibilityLabels}" />
|
||||
</StackPanel>
|
||||
</ToolTip.Tip>
|
||||
<TextBlock
|
||||
Margin="1.5"
|
||||
Tag="{Binding IdString}"
|
||||
Text="{Binding LocalizedStatus}"
|
||||
Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<Button.Styles>
|
||||
<Style Selector="Button">
|
||||
<Setter Property="MinWidth"
|
||||
Value="0" />
|
||||
<!-- avoids very wide buttons from the overall project avalonia style -->
|
||||
</Style>
|
||||
</Button.Styles>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel
|
||||
@@ -120,7 +152,8 @@
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Converter={helpers:MultiplayerInfoConverter}}"
|
||||
IsVisible="{Binding HasLdnGames}"
|
||||
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Compat;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
@@ -30,6 +29,17 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
if (sender is ListBox { SelectedItem: ApplicationData selected })
|
||||
RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
|
||||
}
|
||||
|
||||
private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is not MainWindowViewModel mwvm)
|
||||
return;
|
||||
|
||||
if (sender is not Button { Content: TextBlock playabilityLabel })
|
||||
return;
|
||||
|
||||
await CompatibilityList.Show((string)playabilityLabel.Tag);
|
||||
}
|
||||
|
||||
private async void IdString_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
|
||||
@@ -108,13 +108,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
SaveDataFilter saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, default, saveDataId: default, index: default);
|
||||
|
||||
using UniqueRef<SaveDataIterator> saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||
using UniqueRef<SaveDataIterator> saveDataIterator = new();
|
||||
|
||||
HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||
|
||||
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||
|
||||
HashSet<UserId> lostAccounts = new();
|
||||
HashSet<UserId> lostAccounts = [];
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -128,7 +128,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
for (int i = 0; i < readCount; i++)
|
||||
{
|
||||
SaveDataInfo save = saveDataInfo[i];
|
||||
UserId id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
|
||||
UserId id = new((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
|
||||
if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId == id) == null)
|
||||
{
|
||||
lostAccounts.Add(id);
|
||||
|
||||
@@ -159,6 +159,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
Symbol = (Symbol)symbol,
|
||||
Margin = new Thickness(10),
|
||||
FontSize = 40,
|
||||
FlowDirection = FlowDirection.LeftToRight,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class BitmapArrayValueConverter : IValueConverter
|
||||
{
|
||||
public static BitmapArrayValueConverter Instance = new();
|
||||
public static readonly BitmapArrayValueConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class DownloadableContentLabelConverter : IMultiValueConverter
|
||||
{
|
||||
public static DownloadableContentLabelConverter Instance = new();
|
||||
public static readonly DownloadableContentLabelConverter Instance = new();
|
||||
|
||||
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class KeyValueConverter : IValueConverter
|
||||
{
|
||||
public static KeyValueConverter Instance = new();
|
||||
public static readonly KeyValueConverter Instance = new();
|
||||
|
||||
private static readonly Dictionary<Key, LocaleKeys> _keysMap = new()
|
||||
{
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class MultiplayerInfoConverter : MarkupExtension, IValueConverter
|
||||
{
|
||||
private static readonly MultiplayerInfoConverter _instance = new();
|
||||
public static readonly MultiplayerInfoConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is ApplicationData applicationData)
|
||||
{
|
||||
if (applicationData.PlayerCount != 0 && applicationData.GameCount != 0)
|
||||
{
|
||||
return $"Hosted Games: {applicationData.GameCount}\nOnline Players: {applicationData.PlayerCount}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value is not ApplicationData { HasLdnGames: true } applicationData)
|
||||
return "";
|
||||
}
|
||||
|
||||
return new StringBuilder()
|
||||
.AppendLine(
|
||||
LocaleManager.Instance[LocaleKeys.GameListHeaderHostedGames]
|
||||
.Format(applicationData.GameCount))
|
||||
.Append(
|
||||
LocaleManager.Instance[LocaleKeys.GameListHeaderPlayerCount]
|
||||
.Format(applicationData.PlayerCount))
|
||||
.ToString();
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
@@ -37,7 +35,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return _instance;
|
||||
return Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
LocaleKeys.CompatibilityListNothing or
|
||||
LocaleKeys.CompatibilityListBoots or
|
||||
LocaleKeys.CompatibilityListMenus => Brushes.Red,
|
||||
LocaleKeys.CompatibilityListIngame => Brushes.Yellow,
|
||||
LocaleKeys.CompatibilityListIngame => Brushes.DarkOrange,
|
||||
_ => Brushes.ForestGreen
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class TitleUpdateLabelConverter : IMultiValueConverter
|
||||
{
|
||||
public static TitleUpdateLabelConverter Instance = new();
|
||||
public static readonly TitleUpdateLabelConverter Instance = new();
|
||||
|
||||
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
internal class XCITrimmerFileSpaceSavingsConverter : IValueConverter
|
||||
{
|
||||
private const long _bytesPerMB = 1024 * 1024;
|
||||
public static XCITrimmerFileSpaceSavingsConverter Instance = new();
|
||||
public static readonly XCITrimmerFileSpaceSavingsConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Avalonia.Logging;
|
||||
using Avalonia.Utilities;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Text;
|
||||
@@ -14,13 +15,19 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
internal class LoggerAdapter : ILogSink
|
||||
{
|
||||
private static bool _avaloniaLogsEnabled = ConfigurationState.Instance.Logger.EnableAvaloniaLog;
|
||||
|
||||
public static void Register()
|
||||
{
|
||||
AvaLogger.Sink = new LoggerAdapter();
|
||||
ConfigurationState.Instance.Logger.EnableAvaloniaLog.Event
|
||||
+= (_, e) => _avaloniaLogsEnabled = e.NewValue;
|
||||
}
|
||||
|
||||
private static RyuLogger.Log? GetLog(AvaLogLevel level, string area)
|
||||
{
|
||||
if (!_avaloniaLogsEnabled) return null;
|
||||
|
||||
return level switch
|
||||
{
|
||||
AvaLogLevel.Verbose => RyuLogger.Debug,
|
||||
@@ -50,8 +57,8 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
private static string Format(AvaLogLevel level, string area, string template, object source, object[] v)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
CharacterReader r = new CharacterReader(template.AsSpan());
|
||||
StringBuilder result = new();
|
||||
CharacterReader r = new(template.AsSpan());
|
||||
int i = 0;
|
||||
|
||||
result.Append('[');
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
Margin = new Thickness(0, 0, 15, 40),
|
||||
};
|
||||
|
||||
Lazy<AsyncWorkQueue<Notification>> maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>(
|
||||
Lazy<AsyncWorkQueue<Notification>> maybeAsyncWorkQueue = new(
|
||||
() => new AsyncWorkQueue<Notification>(notification =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace Ryujinx.Ava.UI.Models
|
||||
public class CheatNode : BaseModel
|
||||
{
|
||||
private bool _isEnabled = false;
|
||||
public ObservableCollection<CheatNode> SubNodes { get; } = new();
|
||||
public string CleanName => Name[1..^7];
|
||||
public ObservableCollection<CheatNode> SubNodes { get; } = [];
|
||||
public string CleanName => Name.Length > 0 ? Name[1..^7] : Name;
|
||||
public string BuildIdKey => $"{BuildId}-{Name}";
|
||||
public bool IsRootNode { get; }
|
||||
public string Name { get; }
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models.Input
|
||||
{
|
||||
public class GamepadInputConfig : BaseModel
|
||||
public partial class GamepadInputConfig : BaseModel
|
||||
{
|
||||
public bool EnableCemuHookMotion { get; set; }
|
||||
public string DsuServerHost { get; set; }
|
||||
@@ -25,402 +25,58 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
public ControllerType ControllerType { get; set; }
|
||||
public PlayerIndex PlayerIndex { get; set; }
|
||||
|
||||
private StickInputId _leftJoystick;
|
||||
public StickInputId LeftJoystick
|
||||
{
|
||||
get => _leftJoystick;
|
||||
set
|
||||
{
|
||||
_leftJoystick = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private StickInputId _leftJoystick;
|
||||
[ObservableProperty] private bool _leftInvertStickX;
|
||||
[ObservableProperty] private bool _leftInvertStickY;
|
||||
[ObservableProperty] private bool _leftRotate90;
|
||||
[ObservableProperty] private GamepadInputId _leftStickButton;
|
||||
|
||||
private bool _leftInvertStickX;
|
||||
public bool LeftInvertStickX
|
||||
{
|
||||
get => _leftInvertStickX;
|
||||
set
|
||||
{
|
||||
_leftInvertStickX = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private StickInputId _rightJoystick;
|
||||
[ObservableProperty] private bool _rightInvertStickX;
|
||||
[ObservableProperty] private bool _rightInvertStickY;
|
||||
[ObservableProperty] private bool _rightRotate90;
|
||||
[ObservableProperty] private GamepadInputId _rightStickButton;
|
||||
|
||||
private bool _leftInvertStickY;
|
||||
public bool LeftInvertStickY
|
||||
{
|
||||
get => _leftInvertStickY;
|
||||
set
|
||||
{
|
||||
_leftInvertStickY = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private GamepadInputId _dpadUp;
|
||||
[ObservableProperty] private GamepadInputId _dpadDown;
|
||||
[ObservableProperty] private GamepadInputId _dpadLeft;
|
||||
[ObservableProperty] private GamepadInputId _dpadRight;
|
||||
|
||||
private bool _leftRotate90;
|
||||
public bool LeftRotate90
|
||||
{
|
||||
get => _leftRotate90;
|
||||
set
|
||||
{
|
||||
_leftRotate90 = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _leftStickButton;
|
||||
public GamepadInputId LeftStickButton
|
||||
{
|
||||
get => _leftStickButton;
|
||||
set
|
||||
{
|
||||
_leftStickButton = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private StickInputId _rightJoystick;
|
||||
public StickInputId RightJoystick
|
||||
{
|
||||
get => _rightJoystick;
|
||||
set
|
||||
{
|
||||
_rightJoystick = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _rightInvertStickX;
|
||||
public bool RightInvertStickX
|
||||
{
|
||||
get => _rightInvertStickX;
|
||||
set
|
||||
{
|
||||
_rightInvertStickX = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _rightInvertStickY;
|
||||
public bool RightInvertStickY
|
||||
{
|
||||
get => _rightInvertStickY;
|
||||
set
|
||||
{
|
||||
_rightInvertStickY = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _rightRotate90;
|
||||
public bool RightRotate90
|
||||
{
|
||||
get => _rightRotate90;
|
||||
set
|
||||
{
|
||||
_rightRotate90 = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _rightStickButton;
|
||||
public GamepadInputId RightStickButton
|
||||
{
|
||||
get => _rightStickButton;
|
||||
set
|
||||
{
|
||||
_rightStickButton = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _dpadUp;
|
||||
public GamepadInputId DpadUp
|
||||
{
|
||||
get => _dpadUp;
|
||||
set
|
||||
{
|
||||
_dpadUp = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _dpadDown;
|
||||
public GamepadInputId DpadDown
|
||||
{
|
||||
get => _dpadDown;
|
||||
set
|
||||
{
|
||||
_dpadDown = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _dpadLeft;
|
||||
public GamepadInputId DpadLeft
|
||||
{
|
||||
get => _dpadLeft;
|
||||
set
|
||||
{
|
||||
_dpadLeft = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _dpadRight;
|
||||
public GamepadInputId DpadRight
|
||||
{
|
||||
get => _dpadRight;
|
||||
set
|
||||
{
|
||||
_dpadRight = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonL;
|
||||
public GamepadInputId ButtonL
|
||||
{
|
||||
get => _buttonL;
|
||||
set
|
||||
{
|
||||
_buttonL = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonMinus;
|
||||
public GamepadInputId ButtonMinus
|
||||
{
|
||||
get => _buttonMinus;
|
||||
set
|
||||
{
|
||||
_buttonMinus = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _leftButtonSl;
|
||||
public GamepadInputId LeftButtonSl
|
||||
{
|
||||
get => _leftButtonSl;
|
||||
set
|
||||
{
|
||||
_leftButtonSl = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _leftButtonSr;
|
||||
public GamepadInputId LeftButtonSr
|
||||
{
|
||||
get => _leftButtonSr;
|
||||
set
|
||||
{
|
||||
_leftButtonSr = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonZl;
|
||||
public GamepadInputId ButtonZl
|
||||
{
|
||||
get => _buttonZl;
|
||||
set
|
||||
{
|
||||
_buttonZl = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonA;
|
||||
public GamepadInputId ButtonA
|
||||
{
|
||||
get => _buttonA;
|
||||
set
|
||||
{
|
||||
_buttonA = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonB;
|
||||
public GamepadInputId ButtonB
|
||||
{
|
||||
get => _buttonB;
|
||||
set
|
||||
{
|
||||
_buttonB = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonX;
|
||||
public GamepadInputId ButtonX
|
||||
{
|
||||
get => _buttonX;
|
||||
set
|
||||
{
|
||||
_buttonX = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonY;
|
||||
public GamepadInputId ButtonY
|
||||
{
|
||||
get => _buttonY;
|
||||
set
|
||||
{
|
||||
_buttonY = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonR;
|
||||
public GamepadInputId ButtonR
|
||||
{
|
||||
get => _buttonR;
|
||||
set
|
||||
{
|
||||
_buttonR = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonPlus;
|
||||
public GamepadInputId ButtonPlus
|
||||
{
|
||||
get => _buttonPlus;
|
||||
set
|
||||
{
|
||||
_buttonPlus = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _rightButtonSl;
|
||||
public GamepadInputId RightButtonSl
|
||||
{
|
||||
get => _rightButtonSl;
|
||||
set
|
||||
{
|
||||
_rightButtonSl = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _rightButtonSr;
|
||||
public GamepadInputId RightButtonSr
|
||||
{
|
||||
get => _rightButtonSr;
|
||||
set
|
||||
{
|
||||
_rightButtonSr = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputId _buttonZr;
|
||||
public GamepadInputId ButtonZr
|
||||
{
|
||||
get => _buttonZr;
|
||||
set
|
||||
{
|
||||
_buttonZr = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private float _deadzoneLeft;
|
||||
public float DeadzoneLeft
|
||||
{
|
||||
get => _deadzoneLeft;
|
||||
set
|
||||
{
|
||||
_deadzoneLeft = MathF.Round(value, 3);
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private float _deadzoneRight;
|
||||
public float DeadzoneRight
|
||||
{
|
||||
get => _deadzoneRight;
|
||||
set
|
||||
{
|
||||
_deadzoneRight = MathF.Round(value, 3);
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private float _rangeLeft;
|
||||
public float RangeLeft
|
||||
{
|
||||
get => _rangeLeft;
|
||||
set
|
||||
{
|
||||
_rangeLeft = MathF.Round(value, 3);
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private float _rangeRight;
|
||||
public float RangeRight
|
||||
{
|
||||
get => _rangeRight;
|
||||
set
|
||||
{
|
||||
_rangeRight = MathF.Round(value, 3);
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private float _triggerThreshold;
|
||||
public float TriggerThreshold
|
||||
{
|
||||
get => _triggerThreshold;
|
||||
set
|
||||
{
|
||||
_triggerThreshold = MathF.Round(value, 3);
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableMotion;
|
||||
public bool EnableMotion
|
||||
{
|
||||
get => _enableMotion;
|
||||
set
|
||||
{
|
||||
_enableMotion = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableRumble;
|
||||
public bool EnableRumble
|
||||
{
|
||||
get => _enableRumble;
|
||||
set
|
||||
{
|
||||
_enableRumble = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private GamepadInputId _buttonMinus;
|
||||
[ObservableProperty] private GamepadInputId _buttonPlus;
|
||||
|
||||
private bool _enableLedChanging;
|
||||
[ObservableProperty] private GamepadInputId _buttonA;
|
||||
[ObservableProperty] private GamepadInputId _buttonB;
|
||||
[ObservableProperty] private GamepadInputId _buttonX;
|
||||
[ObservableProperty] private GamepadInputId _buttonY;
|
||||
|
||||
[ObservableProperty] private GamepadInputId _buttonZl;
|
||||
[ObservableProperty] private GamepadInputId _buttonZr;
|
||||
|
||||
[ObservableProperty] private GamepadInputId _buttonL;
|
||||
[ObservableProperty] private GamepadInputId _buttonR;
|
||||
|
||||
[ObservableProperty] private GamepadInputId _leftButtonSl;
|
||||
[ObservableProperty] private GamepadInputId _leftButtonSr;
|
||||
|
||||
[ObservableProperty] private GamepadInputId _rightButtonSl;
|
||||
[ObservableProperty] private GamepadInputId _rightButtonSr;
|
||||
|
||||
public bool EnableLedChanging
|
||||
{
|
||||
get => _enableLedChanging;
|
||||
set
|
||||
{
|
||||
_enableLedChanging = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private float _deadzoneLeft;
|
||||
[ObservableProperty] private float _deadzoneRight;
|
||||
|
||||
[ObservableProperty] private float _rangeLeft;
|
||||
[ObservableProperty] private float _rangeRight;
|
||||
|
||||
[ObservableProperty] private float _triggerThreshold;
|
||||
|
||||
[ObservableProperty] private bool _enableMotion;
|
||||
|
||||
[ObservableProperty] private bool _enableRumble;
|
||||
|
||||
[ObservableProperty] private bool _enableLedChanging;
|
||||
|
||||
[ObservableProperty] private Color _ledColor;
|
||||
|
||||
public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed;
|
||||
|
||||
@@ -449,18 +105,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
|
||||
private Color _ledColor;
|
||||
|
||||
public Color LedColor
|
||||
{
|
||||
get => _ledColor;
|
||||
set
|
||||
{
|
||||
_ledColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public GamepadInputConfig(InputConfig config)
|
||||
{
|
||||
|
||||
@@ -1,322 +1,52 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models.Input
|
||||
{
|
||||
public class KeyboardInputConfig : BaseModel
|
||||
public partial class KeyboardInputConfig : BaseModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public ControllerType ControllerType { get; set; }
|
||||
public PlayerIndex PlayerIndex { get; set; }
|
||||
|
||||
private Key _leftStickUp;
|
||||
public Key LeftStickUp
|
||||
{
|
||||
get => _leftStickUp;
|
||||
set
|
||||
{
|
||||
_leftStickUp = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private Key _leftStickUp;
|
||||
[ObservableProperty] private Key _leftStickDown;
|
||||
[ObservableProperty] private Key _leftStickLeft;
|
||||
[ObservableProperty] private Key _leftStickRight;
|
||||
[ObservableProperty] private Key _leftStickButton;
|
||||
|
||||
private Key _leftStickDown;
|
||||
public Key LeftStickDown
|
||||
{
|
||||
get => _leftStickDown;
|
||||
set
|
||||
{
|
||||
_leftStickDown = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private Key _rightStickUp;
|
||||
[ObservableProperty] private Key _rightStickDown;
|
||||
[ObservableProperty] private Key _rightStickLeft;
|
||||
[ObservableProperty] private Key _rightStickRight;
|
||||
[ObservableProperty] private Key _rightStickButton;
|
||||
|
||||
private Key _leftStickLeft;
|
||||
public Key LeftStickLeft
|
||||
{
|
||||
get => _leftStickLeft;
|
||||
set
|
||||
{
|
||||
_leftStickLeft = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _leftStickRight;
|
||||
public Key LeftStickRight
|
||||
{
|
||||
get => _leftStickRight;
|
||||
set
|
||||
{
|
||||
_leftStickRight = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _leftStickButton;
|
||||
public Key LeftStickButton
|
||||
{
|
||||
get => _leftStickButton;
|
||||
set
|
||||
{
|
||||
_leftStickButton = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightStickUp;
|
||||
public Key RightStickUp
|
||||
{
|
||||
get => _rightStickUp;
|
||||
set
|
||||
{
|
||||
_rightStickUp = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightStickDown;
|
||||
public Key RightStickDown
|
||||
{
|
||||
get => _rightStickDown;
|
||||
set
|
||||
{
|
||||
_rightStickDown = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightStickLeft;
|
||||
public Key RightStickLeft
|
||||
{
|
||||
get => _rightStickLeft;
|
||||
set
|
||||
{
|
||||
_rightStickLeft = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightStickRight;
|
||||
public Key RightStickRight
|
||||
{
|
||||
get => _rightStickRight;
|
||||
set
|
||||
{
|
||||
_rightStickRight = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightStickButton;
|
||||
public Key RightStickButton
|
||||
{
|
||||
get => _rightStickButton;
|
||||
set
|
||||
{
|
||||
_rightStickButton = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _dpadUp;
|
||||
public Key DpadUp
|
||||
{
|
||||
get => _dpadUp;
|
||||
set
|
||||
{
|
||||
_dpadUp = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _dpadDown;
|
||||
public Key DpadDown
|
||||
{
|
||||
get => _dpadDown;
|
||||
set
|
||||
{
|
||||
_dpadDown = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _dpadLeft;
|
||||
public Key DpadLeft
|
||||
{
|
||||
get => _dpadLeft;
|
||||
set
|
||||
{
|
||||
_dpadLeft = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _dpadRight;
|
||||
public Key DpadRight
|
||||
{
|
||||
get => _dpadRight;
|
||||
set
|
||||
{
|
||||
_dpadRight = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonL;
|
||||
public Key ButtonL
|
||||
{
|
||||
get => _buttonL;
|
||||
set
|
||||
{
|
||||
_buttonL = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonMinus;
|
||||
public Key ButtonMinus
|
||||
{
|
||||
get => _buttonMinus;
|
||||
set
|
||||
{
|
||||
_buttonMinus = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _leftButtonSl;
|
||||
public Key LeftButtonSl
|
||||
{
|
||||
get => _leftButtonSl;
|
||||
set
|
||||
{
|
||||
_leftButtonSl = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _leftButtonSr;
|
||||
public Key LeftButtonSr
|
||||
{
|
||||
get => _leftButtonSr;
|
||||
set
|
||||
{
|
||||
_leftButtonSr = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonZl;
|
||||
public Key ButtonZl
|
||||
{
|
||||
get => _buttonZl;
|
||||
set
|
||||
{
|
||||
_buttonZl = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonA;
|
||||
public Key ButtonA
|
||||
{
|
||||
get => _buttonA;
|
||||
set
|
||||
{
|
||||
_buttonA = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonB;
|
||||
public Key ButtonB
|
||||
{
|
||||
get => _buttonB;
|
||||
set
|
||||
{
|
||||
_buttonB = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonX;
|
||||
public Key ButtonX
|
||||
{
|
||||
get => _buttonX;
|
||||
set
|
||||
{
|
||||
_buttonX = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonY;
|
||||
public Key ButtonY
|
||||
{
|
||||
get => _buttonY;
|
||||
set
|
||||
{
|
||||
_buttonY = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonR;
|
||||
public Key ButtonR
|
||||
{
|
||||
get => _buttonR;
|
||||
set
|
||||
{
|
||||
_buttonR = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonPlus;
|
||||
public Key ButtonPlus
|
||||
{
|
||||
get => _buttonPlus;
|
||||
set
|
||||
{
|
||||
_buttonPlus = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightButtonSl;
|
||||
public Key RightButtonSl
|
||||
{
|
||||
get => _rightButtonSl;
|
||||
set
|
||||
{
|
||||
_rightButtonSl = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _rightButtonSr;
|
||||
public Key RightButtonSr
|
||||
{
|
||||
get => _rightButtonSr;
|
||||
set
|
||||
{
|
||||
_rightButtonSr = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Key _buttonZr;
|
||||
public Key ButtonZr
|
||||
{
|
||||
get => _buttonZr;
|
||||
set
|
||||
{
|
||||
_buttonZr = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private Key _dpadUp;
|
||||
[ObservableProperty] private Key _dpadDown;
|
||||
[ObservableProperty] private Key _dpadLeft;
|
||||
[ObservableProperty] private Key _dpadRight;
|
||||
|
||||
[ObservableProperty] private Key _buttonMinus;
|
||||
[ObservableProperty] private Key _buttonPlus;
|
||||
|
||||
[ObservableProperty] private Key _buttonA;
|
||||
[ObservableProperty] private Key _buttonB;
|
||||
[ObservableProperty] private Key _buttonX;
|
||||
[ObservableProperty] private Key _buttonY;
|
||||
|
||||
[ObservableProperty] private Key _buttonL;
|
||||
[ObservableProperty] private Key _buttonR;
|
||||
|
||||
[ObservableProperty] private Key _buttonZl;
|
||||
[ObservableProperty] private Key _buttonZr;
|
||||
|
||||
[ObservableProperty] private Key _leftButtonSl;
|
||||
[ObservableProperty] private Key _leftButtonSr;
|
||||
|
||||
[ObservableProperty] private Key _rightButtonSl;
|
||||
[ObservableProperty] private Key _rightButtonSr;
|
||||
|
||||
public KeyboardInputConfig(InputConfig config)
|
||||
{
|
||||
@@ -367,7 +97,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
|
||||
public InputConfig GetConfig()
|
||||
{
|
||||
StandardKeyboardInputConfig config = new StandardKeyboardInputConfig
|
||||
StandardKeyboardInputConfig config = new()
|
||||
{
|
||||
Id = Id,
|
||||
Backend = InputBackendType.WindowKeyboard,
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class ModModel : BaseModel
|
||||
public partial class ModModel : BaseModel
|
||||
{
|
||||
private bool _enabled;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
_enabled = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private bool _enabled;
|
||||
|
||||
public bool InSd { get; }
|
||||
public string Path { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public string FormattedName =>
|
||||
InSd && ulong.TryParse(Name, NumberStyles.HexNumber, null, out ulong applicationId)
|
||||
? $"Atmosphère: {RyujinxApp.MainWindow.ApplicationLibrary.GetNameForApplicationId(applicationId)}"
|
||||
: Name;
|
||||
|
||||
public ModModel(string path, string name, bool enabled, bool inSd)
|
||||
{
|
||||
Path = path;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class ProfileImageModel : BaseModel
|
||||
public partial class ProfileImageModel : BaseModel
|
||||
{
|
||||
public ProfileImageModel(string name, byte[] data)
|
||||
{
|
||||
@@ -14,19 +15,6 @@ namespace Ryujinx.Ava.UI.Models
|
||||
public string Name { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
private SolidColorBrush _backgroundColor = new(Colors.White);
|
||||
|
||||
public SolidColorBrush BackgroundColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _backgroundColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
_backgroundColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private SolidColorBrush _backgroundColor = new(Colors.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ using Gommon;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Ncm;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class TempProfile : BaseModel
|
||||
public partial class TempProfile : BaseModel
|
||||
{
|
||||
private readonly UserProfile _profile;
|
||||
private byte[] _image;
|
||||
private string _name = String.Empty;
|
||||
[ObservableProperty] private byte[] _image;
|
||||
[ObservableProperty] private string _name = String.Empty;
|
||||
private UserId _userId;
|
||||
|
||||
public static uint MaxProfileNameLength => 0x20;
|
||||
|
||||
public byte[] Image
|
||||
{
|
||||
get => _image;
|
||||
set
|
||||
{
|
||||
_image = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public UserId UserId
|
||||
{
|
||||
get => _userId;
|
||||
@@ -36,21 +26,9 @@ namespace Ryujinx.Ava.UI.Models
|
||||
|
||||
public string UserIdString => _userId.ToString();
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public TempProfile(UserProfile profile)
|
||||
{
|
||||
_profile = profile;
|
||||
|
||||
if (_profile != null)
|
||||
if (profile != null)
|
||||
{
|
||||
Image = profile.Image;
|
||||
Name = profile.Name;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Views.User;
|
||||
@@ -8,65 +9,15 @@ using Profile = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class UserProfile : BaseModel
|
||||
public partial class UserProfile : BaseModel
|
||||
{
|
||||
private readonly Profile _profile;
|
||||
private readonly NavigationDialogHost _owner;
|
||||
private byte[] _image;
|
||||
private string _name;
|
||||
private UserId _userId;
|
||||
private bool _isPointerOver;
|
||||
private IBrush _backgroundColor;
|
||||
|
||||
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 bool IsPointerOver
|
||||
{
|
||||
get => _isPointerOver;
|
||||
set
|
||||
{
|
||||
_isPointerOver = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IBrush BackgroundColor
|
||||
{
|
||||
get => _backgroundColor;
|
||||
set
|
||||
{
|
||||
_backgroundColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private byte[] _image;
|
||||
[ObservableProperty] private string _name;
|
||||
[ObservableProperty] private UserId _userId;
|
||||
[ObservableProperty] private bool _isPointerOver;
|
||||
[ObservableProperty] private IBrush _backgroundColor;
|
||||
|
||||
public UserProfile(Profile profile, NavigationDialogHost owner)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Platform;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Helper;
|
||||
using SharpMetal.QuartzCore;
|
||||
using System;
|
||||
|
||||
@@ -7,14 +8,12 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
{
|
||||
public CAMetalLayer CreateSurface()
|
||||
{
|
||||
if (OperatingSystem.IsMacOS())
|
||||
if (OperatingSystem.IsMacOS() && RunningPlatform.IsArm)
|
||||
{
|
||||
return new CAMetalLayer(MetalLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Cannot create a {nameof(CAMetalLayer)} without being on ARM Mac.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,19 +43,19 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
|
||||
public RendererHost(string titleId)
|
||||
{
|
||||
switch (TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend))
|
||||
{
|
||||
case GraphicsBackend.OpenGl:
|
||||
EmbeddedWindow = new EmbeddedWindowOpenGL();
|
||||
break;
|
||||
case GraphicsBackend.Metal:
|
||||
EmbeddedWindow = new EmbeddedWindowMetal();
|
||||
break;
|
||||
case GraphicsBackend.Vulkan:
|
||||
EmbeddedWindow = new EmbeddedWindowVulkan();
|
||||
break;
|
||||
}
|
||||
|
||||
Focusable = true;
|
||||
FlowDirection = FlowDirection.LeftToRight;
|
||||
|
||||
EmbeddedWindow =
|
||||
#pragma warning disable CS8509
|
||||
TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend) switch
|
||||
#pragma warning restore CS8509
|
||||
{
|
||||
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
|
||||
GraphicsBackend.Metal => new EmbeddedWindowMetal(),
|
||||
GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(),
|
||||
};
|
||||
|
||||
string backendText = EmbeddedWindow switch
|
||||
{
|
||||
EmbeddedWindowVulkan => "Vulkan",
|
||||
|
||||
@@ -2,6 +2,7 @@ using Avalonia.Media.Imaging;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
@@ -24,30 +25,35 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Version = RyujinxApp.FullAppName + "\n" + Program.Version;
|
||||
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
|
||||
|
||||
ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
|
||||
RyujinxApp.ThemeChanged += Ryujinx_ThemeChanged;
|
||||
}
|
||||
|
||||
private void ThemeManager_ThemeChanged()
|
||||
private void Ryujinx_ThemeChanged()
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
|
||||
}
|
||||
|
||||
private const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
|
||||
|
||||
private void UpdateLogoTheme(string theme)
|
||||
{
|
||||
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark);
|
||||
|
||||
string themeName = isDarkTheme ? "Dark" : "Light";
|
||||
|
||||
string basePath = "resm:Ryujinx.Assets.UIImages.";
|
||||
string themeSuffix = isDarkTheme ? "Dark.png" : "Light.png";
|
||||
|
||||
GithubLogo = LoadBitmap($"{basePath}Logo_GitHub_{themeSuffix}?assembly=Ryujinx");
|
||||
DiscordLogo = LoadBitmap($"{basePath}Logo_Discord_{themeSuffix}?assembly=Ryujinx");
|
||||
GithubLogo = LoadBitmap(LogoPathFormat.Format("GitHub", themeName));
|
||||
DiscordLogo = LoadBitmap(LogoPathFormat.Format("Discord", themeName));
|
||||
}
|
||||
|
||||
private static Bitmap LoadBitmap(string uri) => new(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ThemeManager.ThemeChanged -= ThemeManager_ThemeChanged;
|
||||
RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged;
|
||||
|
||||
GithubLogo.Dispose();
|
||||
DiscordLogo.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
||||
|
||||
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
||||
_amiiboList = new List<AmiiboApi>();
|
||||
_amiiboSeries = new ObservableCollection<string>();
|
||||
_amiibos = new AvaloniaList<AmiiboApi>();
|
||||
_amiiboList = [];
|
||||
_amiiboSeries = [];
|
||||
_amiibos = [];
|
||||
|
||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx/Assets/UIImages/Logo_Amiibo.png");
|
||||
|
||||
@@ -264,7 +264,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
|
||||
|
||||
// Neither local or remote files are valid JSON, close window.
|
||||
ShowInfoDialog();
|
||||
await ShowInfoDialog();
|
||||
Close();
|
||||
}
|
||||
else if (!remoteIsValid)
|
||||
@@ -273,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
// Only the local file is valid, the local one should be used
|
||||
// but the user should be warned.
|
||||
ShowInfoDialog();
|
||||
await ShowInfoDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +525,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
AmiiboImage = bitmap;
|
||||
}
|
||||
|
||||
private static async void ShowInfoDialog()
|
||||
private static async Task ShowInfoDialog()
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
|
||||
|
||||
29
src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs
Normal file
29
src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.PlayReport;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public class ApplicationDataViewModel : BaseModel
|
||||
{
|
||||
public ApplicationData AppData { get; }
|
||||
|
||||
public ApplicationDataViewModel(ApplicationData appData) => AppData = appData;
|
||||
|
||||
public string DynamicRichPresenceDescription =>
|
||||
AppData.HasDynamicRichPresenceSupport
|
||||
? AppData.RichPresenceSpec.Value.Description
|
||||
: GameSpec.DefaultDescription;
|
||||
|
||||
public string FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version);
|
||||
public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer);
|
||||
public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension);
|
||||
public string FormattedFileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize].Format(AppData.FileSizeString);
|
||||
|
||||
public string FormattedLdnInfo =>
|
||||
$"{LocaleManager.Instance[LocaleKeys.GameListHeaderHostedGames].Format(AppData.GameCount)}" +
|
||||
$"\n" +
|
||||
$"{LocaleManager.Instance[LocaleKeys.GameListHeaderPlayerCount].Format(AppData.PlayerCount)}";
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public DlcSelectViewModel(ulong titleId, ApplicationLibrary appLibrary)
|
||||
{
|
||||
_dlcs = appLibrary.DownloadableContents.Items
|
||||
.Where(x => x.Dlc.TitleIdBase == titleId)
|
||||
.Select(x => x.Dlc)
|
||||
_dlcs = appLibrary.FindDlcsFor(titleId)
|
||||
.OrderBy(it => it.IsBundled ? 0 : 1)
|
||||
.ThenBy(it => it.TitleId)
|
||||
.ToArray();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
@@ -9,22 +8,20 @@ using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Application = Avalonia.Application;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public partial class DownloadableContentManagerViewModel : BaseModel
|
||||
{
|
||||
private readonly ApplicationLibrary _applicationLibrary;
|
||||
private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
|
||||
[ObservableProperty] private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
|
||||
[ObservableProperty] private AvaloniaList<DownloadableContentModel> _views = new();
|
||||
private AvaloniaList<DownloadableContentModel> _downloadableContents = [];
|
||||
[ObservableProperty] private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = [];
|
||||
[ObservableProperty] private AvaloniaList<DownloadableContentModel> _views = [];
|
||||
[ObservableProperty] private bool _showBundledContentNotice = false;
|
||||
|
||||
private string _search;
|
||||
@@ -72,8 +69,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void LoadDownloadableContents()
|
||||
{
|
||||
IEnumerable<(DownloadableContentModel Dlc, bool IsEnabled)> dlcs = _applicationLibrary.DownloadableContents.Items
|
||||
.Where(it => it.Dlc.TitleIdBase == _applicationData.IdBase);
|
||||
(DownloadableContentModel Dlc, bool IsEnabled)[] dlcs = _applicationLibrary.FindDlcConfigurationFor(_applicationData.Id);
|
||||
|
||||
bool hasBundledContent = false;
|
||||
foreach ((DownloadableContentModel dlc, bool isEnabled) in dlcs)
|
||||
@@ -139,9 +135,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
new("NSP")
|
||||
{
|
||||
Patterns = new[] { "*.nsp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||
Patterns = ["*.nsp"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.nsp"],
|
||||
MimeTypes = ["application/x-nx-nsp"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Avalonia.Svg.Skia;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.Views.Input;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.UI.Views.Input;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
@@ -47,6 +47,23 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
ParentModel = model;
|
||||
model.NotifyChangesEvent += OnParentModelChanged;
|
||||
OnParentModelChanged();
|
||||
config.PropertyChanged += (_, args) =>
|
||||
{
|
||||
if (args.PropertyName is nameof(Config.UseRainbowLed))
|
||||
{
|
||||
if (Config is { UseRainbowLed: true, TurnOffLed: false, EnableLedChanging: true })
|
||||
Rainbow.Updated += (ref Color color) => ParentModel.SelectedGamepad.SetLed((uint)color.ToArgb());
|
||||
else
|
||||
{
|
||||
Rainbow.Reset();
|
||||
|
||||
if (Config.TurnOffLed)
|
||||
ParentModel.SelectedGamepad.ClearLed();
|
||||
else
|
||||
ParentModel.SelectedGamepad.SetLed(Config.LedColor.ToUInt32());
|
||||
}
|
||||
}
|
||||
};
|
||||
Config = config;
|
||||
}
|
||||
|
||||
@@ -59,16 +76,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
await RumbleInputView.Show(this);
|
||||
}
|
||||
|
||||
public RelayCommand LedDisabledChanged => Commands.Create(() =>
|
||||
|
||||
public async void ShowLedConfig()
|
||||
{
|
||||
if (!Config.EnableLedChanging) return;
|
||||
|
||||
if (Config.TurnOffLed)
|
||||
ParentModel.SelectedGamepad.ClearLed();
|
||||
else
|
||||
ParentModel.SelectedGamepad.SetLed(Config.LedColor.ToUInt32());
|
||||
});
|
||||
await LedInputView.Show(this);
|
||||
}
|
||||
|
||||
public void OnParentModelChanged()
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
@@ -63,7 +64,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
get => _selectedGamepad;
|
||||
private set
|
||||
{
|
||||
Rainbow.Reset();
|
||||
|
||||
_selectedGamepad = value;
|
||||
|
||||
if (ConfigViewModel is ControllerInputViewModel { Config.UseRainbowLed: true })
|
||||
Rainbow.Updated += (ref Color color) => _selectedGamepad.SetLed((uint)color.ToArgb());
|
||||
|
||||
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
|
||||
}
|
||||
}
|
||||
@@ -259,11 +266,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
public InputViewModel()
|
||||
{
|
||||
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
||||
Controllers = new ObservableCollection<ControllerModel>();
|
||||
Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
|
||||
ProfilesList = new AvaloniaList<string>();
|
||||
DeviceList = new AvaloniaList<string>();
|
||||
PlayerIndexes = [];
|
||||
Controllers = [];
|
||||
Devices = [];
|
||||
ProfilesList = [];
|
||||
DeviceList = [];
|
||||
|
||||
ControllerImage = ProControllerResource;
|
||||
|
||||
|
||||
69
src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs
Normal file
69
src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Humanizer;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public partial class LedInputViewModel : BaseModel
|
||||
{
|
||||
public required InputViewModel ParentModel { get; init; }
|
||||
|
||||
public RelayCommand LedDisabledChanged => Commands.Create(() =>
|
||||
{
|
||||
if (!EnableLedChanging) return;
|
||||
|
||||
if (TurnOffLed)
|
||||
ParentModel.SelectedGamepad.ClearLed();
|
||||
else
|
||||
ParentModel.SelectedGamepad.SetLed(LedColor.ToUInt32());
|
||||
});
|
||||
|
||||
[ObservableProperty] private bool _enableLedChanging;
|
||||
[ObservableProperty] private Color _ledColor;
|
||||
|
||||
public string RainbowSpeedText => RainbowSpeed.ToString(CultureInfo.CurrentCulture).Truncate(4, string.Empty);
|
||||
|
||||
public float RainbowSpeed
|
||||
{
|
||||
get => ConfigurationState.Instance.Hid.RainbowSpeed;
|
||||
set
|
||||
{
|
||||
ConfigurationState.Instance.Hid.RainbowSpeed.Value = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(RainbowSpeedText));
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed;
|
||||
|
||||
private bool _turnOffLed;
|
||||
|
||||
public bool TurnOffLed
|
||||
{
|
||||
get => _turnOffLed;
|
||||
set
|
||||
{
|
||||
_turnOffLed = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _useRainbowLed;
|
||||
|
||||
public bool UseRainbowLed
|
||||
{
|
||||
get => _useRainbowLed;
|
||||
set
|
||||
{
|
||||
_useRainbowLed = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
@@ -104,6 +105,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
[ObservableProperty] private bool _isSubMenuOpen;
|
||||
[ObservableProperty] private ApplicationContextMenu _listAppContextMenu;
|
||||
[ObservableProperty] private ApplicationContextMenu _gridAppContextMenu;
|
||||
[ObservableProperty] private bool _updateAvailable;
|
||||
|
||||
public static AsyncRelayCommand UpdateCommand { get; } = Commands.Create(async () =>
|
||||
{
|
||||
if (Updater.CanUpdate(true))
|
||||
await Updater.BeginUpdateAsync(true);
|
||||
});
|
||||
|
||||
private bool _showLoadProgress;
|
||||
private bool _isGameRunning;
|
||||
@@ -349,6 +357,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo;
|
||||
|
||||
public bool HasDlc => ApplicationLibrary.HasDlcs(SelectedApplication.Id);
|
||||
|
||||
public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
|
||||
|
||||
public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
|
||||
@@ -629,15 +641,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
return SortMode switch
|
||||
{
|
||||
ApplicationSort.Title => LocaleManager.Instance[LocaleKeys.GameListHeaderApplication],
|
||||
ApplicationSort.Developer => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper],
|
||||
ApplicationSort.LastPlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderLastPlayed],
|
||||
ApplicationSort.TotalTimePlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderTimePlayed],
|
||||
ApplicationSort.FileType => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension],
|
||||
ApplicationSort.FileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize],
|
||||
ApplicationSort.Path => LocaleManager.Instance[LocaleKeys.GameListHeaderPath],
|
||||
ApplicationSort.Favorite => LocaleManager.Instance[LocaleKeys.CommonFavorite],
|
||||
ApplicationSort.TitleId => LocaleManager.Instance[LocaleKeys.DlcManagerTableHeadingTitleIdLabel],
|
||||
ApplicationSort.Title => LocaleManager.Instance[LocaleKeys.GameListHeaderApplication],
|
||||
ApplicationSort.Developer => LocaleManager.Instance[LocaleKeys.GameListSortDeveloper],
|
||||
ApplicationSort.LastPlayed => LocaleManager.Instance[LocaleKeys.GameListSortLastPlayed],
|
||||
ApplicationSort.TotalTimePlayed => LocaleManager.Instance[LocaleKeys.GameListSortTimePlayed],
|
||||
ApplicationSort.FileType => LocaleManager.Instance[LocaleKeys.GameListSortFileExtension],
|
||||
ApplicationSort.FileSize => LocaleManager.Instance[LocaleKeys.GameListSortFileSize],
|
||||
ApplicationSort.Path => LocaleManager.Instance[LocaleKeys.GameListSortPath],
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -1143,10 +1155,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
List<string> dirs = result.Select(it => it.Path.LocalPath).ToList();
|
||||
int numAdded = onDirsSelected(dirs, out int numRemoved);
|
||||
|
||||
string msg = String.Join("\r\n", new string[] {
|
||||
string msg = string.Join("\n",
|
||||
string.Format(LocaleManager.Instance[localeMessageRemovedKey], numRemoved),
|
||||
string.Format(LocaleManager.Instance[localeMessageAddedKey], numAdded)
|
||||
});
|
||||
);
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
@@ -1245,21 +1257,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
|
||||
{
|
||||
Patterns = new[] { "*.xci", "*.zip" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
|
||||
MimeTypes = new[] { "application/x-nx-xci", "application/zip" },
|
||||
Patterns = ["*.xci", "*.zip"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci", "public.zip-archive"],
|
||||
MimeTypes = ["application/x-nx-xci", "application/zip"],
|
||||
},
|
||||
new("XCI")
|
||||
{
|
||||
Patterns = new[] { "*.xci" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||
MimeTypes = new[] { "application/x-nx-xci" },
|
||||
Patterns = ["*.xci"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci"],
|
||||
MimeTypes = ["application/x-nx-xci"],
|
||||
},
|
||||
new("ZIP")
|
||||
{
|
||||
Patterns = new[] { "*.zip" },
|
||||
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
||||
MimeTypes = new[] { "application/zip" },
|
||||
Patterns = ["*.zip"],
|
||||
AppleUniformTypeIdentifiers = ["public.zip-archive"],
|
||||
MimeTypes = ["application/zip"],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1292,21 +1304,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
|
||||
{
|
||||
Patterns = new[] { "*.keys", "*.zip" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
|
||||
MimeTypes = new[] { "application/keys", "application/zip" },
|
||||
Patterns = ["*.keys", "*.zip"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci", "public.zip-archive"],
|
||||
MimeTypes = ["application/keys", "application/zip"],
|
||||
},
|
||||
new("KEYS")
|
||||
{
|
||||
Patterns = new[] { "*.keys" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||
MimeTypes = new[] { "application/keys" },
|
||||
Patterns = ["*.keys"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci"],
|
||||
MimeTypes = ["application/keys"],
|
||||
},
|
||||
new("ZIP")
|
||||
{
|
||||
Patterns = new[] { "*.zip" },
|
||||
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
||||
MimeTypes = new[] { "application/zip" },
|
||||
Patterns = ["*.zip"],
|
||||
AppleUniformTypeIdentifiers = ["public.zip-archive"],
|
||||
MimeTypes = ["application/zip"],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1335,6 +1347,25 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
|
||||
}
|
||||
|
||||
public void OpenScreenshotsFolder()
|
||||
{
|
||||
string screenshotsDir = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(screenshotsDir))
|
||||
Directory.CreateDirectory(screenshotsDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
OpenHelper.OpenFolder(screenshotsDir);
|
||||
}
|
||||
|
||||
public void OpenLogsFolder()
|
||||
{
|
||||
string logPath = AppDataManager.GetOrCreateLogsDir();
|
||||
@@ -1418,53 +1449,53 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||
{
|
||||
Patterns = new[] { "*.nsp", "*.xci", "*.nca", "*.nro", "*.nso" },
|
||||
AppleUniformTypeIdentifiers = new[]
|
||||
{
|
||||
Patterns = ["*.nsp", "*.xci", "*.nca", "*.nro", "*.nso"],
|
||||
AppleUniformTypeIdentifiers =
|
||||
[
|
||||
"com.ryujinx.nsp",
|
||||
"com.ryujinx.xci",
|
||||
"com.ryujinx.nca",
|
||||
"com.ryujinx.nro",
|
||||
"com.ryujinx.nso",
|
||||
},
|
||||
MimeTypes = new[]
|
||||
{
|
||||
"com.ryujinx.nso"
|
||||
],
|
||||
MimeTypes =
|
||||
[
|
||||
"application/x-nx-nsp",
|
||||
"application/x-nx-xci",
|
||||
"application/x-nx-nca",
|
||||
"application/x-nx-nro",
|
||||
"application/x-nx-nso",
|
||||
},
|
||||
"application/x-nx-nso"
|
||||
],
|
||||
},
|
||||
new("NSP")
|
||||
{
|
||||
Patterns = new[] { "*.nsp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||
Patterns = ["*.nsp"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.nsp"],
|
||||
MimeTypes = ["application/x-nx-nsp"],
|
||||
},
|
||||
new("XCI")
|
||||
{
|
||||
Patterns = new[] { "*.xci" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||
MimeTypes = new[] { "application/x-nx-xci" },
|
||||
Patterns = ["*.xci"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci"],
|
||||
MimeTypes = ["application/x-nx-xci"],
|
||||
},
|
||||
new("NCA")
|
||||
{
|
||||
Patterns = new[] { "*.nca" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
|
||||
MimeTypes = new[] { "application/x-nx-nca" },
|
||||
Patterns = ["*.nca"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.nca"],
|
||||
MimeTypes = ["application/x-nx-nca"],
|
||||
},
|
||||
new("NRO")
|
||||
{
|
||||
Patterns = new[] { "*.nro" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
|
||||
MimeTypes = new[] { "application/x-nx-nro" },
|
||||
Patterns = ["*.nro"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.nro"],
|
||||
MimeTypes = ["application/x-nx-nro"],
|
||||
},
|
||||
new("NSO")
|
||||
{
|
||||
Patterns = new[] { "*.nso" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
|
||||
MimeTypes = new[] { "application/x-nx-nso" },
|
||||
Patterns = ["*.nso"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.nso"],
|
||||
MimeTypes = ["application/x-nx-nso"],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1690,7 +1721,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||
{
|
||||
Patterns = new[] { "*.bin" },
|
||||
Patterns = ["*.bin"],
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1802,7 +1833,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
XCIFileTrimmer trimmer = new XCIFileTrimmer(filename, new XCITrimmerLog.MainWindow(this));
|
||||
XCIFileTrimmer trimmer = new(filename, new XCITrimmerLog.MainWindow(this));
|
||||
|
||||
if (trimmer.CanBeTrimmed)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
@@ -23,12 +24,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
private readonly string _modJsonPath;
|
||||
|
||||
private AvaloniaList<ModModel> _mods = new();
|
||||
[ObservableProperty] private AvaloniaList<ModModel> _views = new();
|
||||
[ObservableProperty] private AvaloniaList<ModModel> _selectedMods = new();
|
||||
private AvaloniaList<ModModel> _mods = [];
|
||||
[ObservableProperty] private AvaloniaList<ModModel> _views = [];
|
||||
[ObservableProperty] private AvaloniaList<ModModel> _selectedMods = [];
|
||||
|
||||
private string _search;
|
||||
private readonly ulong _applicationId;
|
||||
private readonly ulong[] _installedDlcIds;
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
|
||||
private static readonly ModMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
@@ -61,18 +63,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
get => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count);
|
||||
}
|
||||
|
||||
public ModManagerViewModel(ulong applicationId)
|
||||
public ModManagerViewModel(ulong applicationId, ulong applicationIdBase, ApplicationLibrary appLibrary)
|
||||
{
|
||||
_applicationId = applicationId;
|
||||
|
||||
_installedDlcIds = appLibrary.DownloadableContents.Keys
|
||||
.Where(x => x.TitleIdBase == applicationIdBase)
|
||||
.Select(x => x.TitleId)
|
||||
.ToArray();
|
||||
|
||||
_modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json");
|
||||
|
||||
_storageProvider = RyujinxApp.MainWindow.StorageProvider;
|
||||
|
||||
LoadMods(applicationId);
|
||||
LoadMods(applicationId, _installedDlcIds);
|
||||
}
|
||||
|
||||
private void LoadMods(ulong applicationId)
|
||||
private void LoadMods(ulong applicationId, ulong[] installedDlcIds)
|
||||
{
|
||||
Mods.Clear();
|
||||
SelectedMods.Clear();
|
||||
@@ -82,13 +89,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
foreach (string path in modsBasePaths)
|
||||
{
|
||||
bool inSd = path == ModLoader.GetSdModsBasePath();
|
||||
ModLoader.ModCache modCache = new ModLoader.ModCache();
|
||||
ModLoader.ModCache modCache = new();
|
||||
|
||||
ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId);
|
||||
ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId, _installedDlcIds);
|
||||
|
||||
foreach (ModLoader.Mod<DirectoryInfo> mod in modCache.RomfsDirs)
|
||||
{
|
||||
ModModel modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
|
||||
ModModel modModel = new(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
|
||||
if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
|
||||
{
|
||||
Mods.Add(modModel);
|
||||
@@ -102,7 +109,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
foreach (ModLoader.Mod<DirectoryInfo> mod in modCache.ExefsDirs)
|
||||
{
|
||||
ModModel modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
|
||||
ModModel modModel = new(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
|
||||
if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
|
||||
{
|
||||
Mods.Add(modModel);
|
||||
@@ -278,7 +285,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
File.Copy(file, file.Replace(directory.Parent.ToString(), destinationDir), true);
|
||||
}
|
||||
|
||||
LoadMods(_applicationId);
|
||||
LoadMods(_applicationId, _installedDlcIds);
|
||||
}
|
||||
|
||||
public async void Add()
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public partial class UserSelectorDialogViewModel : BaseModel
|
||||
public partial class ProfileSelectorDialogViewModel : BaseModel
|
||||
{
|
||||
|
||||
[ObservableProperty] private UserId _selectedUserId;
|
||||
@@ -2,7 +2,7 @@ using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Gommon;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
@@ -13,9 +13,11 @@ using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Ava.Utilities.Configuration.System;
|
||||
using Ryujinx.Ava.Utilities.Configuration.UI;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Multiplayer;
|
||||
using Ryujinx.Common.GraphicsDriver;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
@@ -27,8 +29,6 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||
|
||||
@@ -49,9 +49,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private int _graphicsBackendMultithreadingIndex;
|
||||
private float _volume;
|
||||
[ObservableProperty] private bool _isVulkanAvailable = true;
|
||||
[ObservableProperty] private bool _gameDirectoryChanged;
|
||||
[ObservableProperty] private bool _autoloadDirectoryChanged;
|
||||
private readonly List<string> _gpuIds = new();
|
||||
[ObservableProperty] private bool _gameListNeedsRefresh;
|
||||
private readonly List<string> _gpuIds = [];
|
||||
private int _graphicsBackendIndex;
|
||||
private int _scalingFilter;
|
||||
private int _scalingFilterLevel;
|
||||
@@ -115,10 +114,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
|
||||
|
||||
public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
||||
|
||||
public bool IsMacOS => OperatingSystem.IsMacOS();
|
||||
|
||||
public bool EnableDiscordIntegration { get; set; }
|
||||
public bool CheckUpdatesOnStart { get; set; }
|
||||
public bool ShowConfirmExit { get; set; }
|
||||
@@ -126,10 +121,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool RememberWindowState { get; set; }
|
||||
public bool ShowTitleBar { get; set; }
|
||||
public int HideCursor { get; set; }
|
||||
public int UpdateCheckerType { get; set; }
|
||||
public bool EnableDockedMode { get; set; }
|
||||
public bool EnableKeyboard { get; set; }
|
||||
public bool EnableMouse { get; set; }
|
||||
public bool EnableAutoAssign { get; set; }
|
||||
public bool DisableInputWhenOutOfFocus { get; set; }
|
||||
|
||||
public int FocusLostActionType { get; set; }
|
||||
|
||||
public VSyncMode VSyncMode
|
||||
{
|
||||
get => _vSyncMode;
|
||||
@@ -201,7 +201,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableTextureRecompression { get; set; }
|
||||
public bool EnableMacroHLE { get; set; }
|
||||
public bool EnableColorSpacePassthrough { get; set; }
|
||||
public bool ColorSpacePassthroughAvailable => IsMacOS;
|
||||
public bool ColorSpacePassthroughAvailable => RunningPlatform.IsMacOS;
|
||||
public bool EnableFileLog { get; set; }
|
||||
public bool EnableStub { get; set; }
|
||||
public bool EnableInfo { get; set; }
|
||||
@@ -210,6 +210,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableTrace { get; set; }
|
||||
public bool EnableGuest { get; set; }
|
||||
public bool EnableFsAccessLog { get; set; }
|
||||
public bool EnableAvaloniaLog { get; set; }
|
||||
public bool EnableDebug { get; set; }
|
||||
public bool IsOpenAlEnabled { get; set; }
|
||||
public bool IsSoundIoEnabled { get; set; }
|
||||
@@ -297,6 +298,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty] private bool _matchSystemTime;
|
||||
|
||||
public DateTimeOffset CurrentDate { get; set; }
|
||||
|
||||
public TimeSpan CurrentTime { get; set; }
|
||||
@@ -331,9 +334,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
|
||||
private static partial Regex LdnPassphraseRegex();
|
||||
|
||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
@@ -415,17 +415,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex)));
|
||||
}
|
||||
|
||||
public void MatchSystemTime()
|
||||
{
|
||||
(DateTimeOffset dto, TimeSpan timeOfDay) = DateTimeOffset.Now.Extract();
|
||||
|
||||
CurrentDate = dto;
|
||||
CurrentTime = timeOfDay;
|
||||
|
||||
OnPropertyChanged(nameof(CurrentDate));
|
||||
OnPropertyChanged(nameof(CurrentTime));
|
||||
}
|
||||
|
||||
public async Task LoadTimeZones()
|
||||
{
|
||||
_timeZoneContentManager = new TimeZoneContentManager();
|
||||
@@ -471,7 +460,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private bool ValidateLdnPassphrase(string passphrase)
|
||||
{
|
||||
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && LdnPassphraseRegex().IsMatch(passphrase));
|
||||
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && Patterns.LdnPassphrase.IsMatch(passphrase));
|
||||
}
|
||||
|
||||
public void ValidateAndSetTimeZone(string location)
|
||||
@@ -493,6 +482,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
RememberWindowState = config.RememberWindowState;
|
||||
ShowTitleBar = config.ShowTitleBar;
|
||||
HideCursor = (int)config.HideCursor.Value;
|
||||
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
|
||||
FocusLostActionType = (int)config.FocusLostActionType.Value;
|
||||
|
||||
GameDirectories.Clear();
|
||||
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
||||
@@ -513,6 +504,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
EnableKeyboard = config.Hid.EnableKeyboard;
|
||||
EnableMouse = config.Hid.EnableMouse;
|
||||
EnableAutoAssign = config.Hid.EnableAutoAssign;
|
||||
DisableInputWhenOutOfFocus = config.Hid.DisableInputWhenOutOfFocus;
|
||||
|
||||
// Keyboard Hotkeys
|
||||
KeyboardHotkey = new HotkeyConfig(config.Hid.Hotkeys.Value);
|
||||
@@ -528,13 +520,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
CurrentDate = currentDateTime.Date;
|
||||
CurrentTime = currentDateTime.TimeOfDay;
|
||||
|
||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval.Value;
|
||||
MatchSystemTime = config.System.MatchSystemTime;
|
||||
|
||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
||||
CustomVSyncInterval = config.Graphics.CustomVSyncInterval;
|
||||
VSyncMode = config.Graphics.VSyncMode;
|
||||
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
||||
DramSize = config.System.DramSize;
|
||||
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
||||
IgnoreApplet = config.System.IgnoreApplet;
|
||||
IgnoreApplet = config.System.IgnoreControllerApplet;
|
||||
|
||||
// CPU
|
||||
EnablePptc = config.System.EnablePtc;
|
||||
@@ -577,6 +571,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
EnableGuest = config.Logger.EnableGuest;
|
||||
EnableDebug = config.Logger.EnableDebug;
|
||||
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
||||
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
|
||||
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
||||
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
||||
|
||||
@@ -597,16 +592,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.RememberWindowState.Value = RememberWindowState;
|
||||
config.ShowTitleBar.Value = ShowTitleBar;
|
||||
config.HideCursor.Value = (HideCursorMode)HideCursor;
|
||||
|
||||
if (GameDirectoryChanged)
|
||||
{
|
||||
config.UI.GameDirs.Value = [..GameDirectories];
|
||||
}
|
||||
|
||||
if (AutoloadDirectoryChanged)
|
||||
{
|
||||
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
|
||||
}
|
||||
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
|
||||
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
|
||||
config.UI.GameDirs.Value = [..GameDirectories];
|
||||
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
|
||||
|
||||
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
||||
{
|
||||
@@ -622,24 +611,29 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.Hid.EnableMouse.Value = EnableMouse;
|
||||
bool activatingAutoAssign = EnableAutoAssign && !config.Hid.EnableAutoAssign;
|
||||
config.Hid.EnableAutoAssign.Value = EnableAutoAssign;
|
||||
config.Hid.DisableInputWhenOutOfFocus.Value = DisableInputWhenOutOfFocus;
|
||||
|
||||
// Keyboard Hotkeys
|
||||
config.Hid.Hotkeys.Value = KeyboardHotkey.GetConfig();
|
||||
|
||||
// System
|
||||
config.System.Region.Value = (Region)Region;
|
||||
|
||||
if (config.System.Language.Value != (Language)Language)
|
||||
GameListNeedsRefresh = true;
|
||||
|
||||
config.System.Language.Value = (Language)Language;
|
||||
|
||||
if (_validTzRegions.Contains(TimeZone))
|
||||
{
|
||||
config.System.TimeZone.Value = TimeZone;
|
||||
}
|
||||
|
||||
config.System.MatchSystemTime.Value = MatchSystemTime;
|
||||
config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
|
||||
config.System.DramSize.Value = DramSize;
|
||||
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
|
||||
config.System.IgnoreApplet.Value = (EnableAutoAssign) || IgnoreApplet;
|
||||
config.System.IgnoreControllerApplet.Value = (EnableAutoAssign) || IgnoreApplet;
|
||||
|
||||
// CPU
|
||||
config.System.EnablePtc.Value = EnablePptc;
|
||||
@@ -697,6 +691,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.Logger.EnableGuest.Value = EnableGuest;
|
||||
config.Logger.EnableDebug.Value = EnableDebug;
|
||||
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
|
||||
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
|
||||
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
||||
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
||||
|
||||
@@ -725,8 +720,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
SaveSettingsEvent?.Invoke();
|
||||
}
|
||||
|
||||
GameDirectoryChanged = false;
|
||||
AutoloadDirectoryChanged = false;
|
||||
GameListNeedsRefresh = false;
|
||||
}
|
||||
|
||||
private static void RevertIfNotSaved()
|
||||
@@ -745,6 +739,25 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
CloseWindow?.Invoke();
|
||||
}
|
||||
|
||||
[ObservableProperty] private bool _wantsToReset;
|
||||
|
||||
public AsyncRelayCommand ResetButton => Commands.Create(async () =>
|
||||
{
|
||||
if (!WantsToReset) return;
|
||||
|
||||
CloseWindow?.Invoke();
|
||||
ConfigurationState.Instance.LoadDefault();
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
RyujinxApp.MainWindow.LoadApplications();
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
$"Your {RyujinxApp.FullAppName} configuration has been reset.",
|
||||
"",
|
||||
string.Empty,
|
||||
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||
"Configuration Reset");
|
||||
});
|
||||
|
||||
public void CancelButton()
|
||||
{
|
||||
RevertIfNotSaved();
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private ApplicationLibrary ApplicationLibrary { get; }
|
||||
private ApplicationData ApplicationData { get; }
|
||||
|
||||
[ObservableProperty] private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||
[ObservableProperty] private AvaloniaList<object> _views = new();
|
||||
[ObservableProperty] private AvaloniaList<TitleUpdateModel> _titleUpdates = [];
|
||||
[ObservableProperty] private AvaloniaList<object> _views = [];
|
||||
[ObservableProperty] private object _selectedUpdate = new TitleUpdateViewModelNoUpdate();
|
||||
[ObservableProperty] private bool _showBundledContentNotice;
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void LoadUpdates()
|
||||
{
|
||||
IEnumerable<(TitleUpdateModel TitleUpdate, bool IsSelected)> updates = ApplicationLibrary.TitleUpdates.Items
|
||||
.Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase);
|
||||
(TitleUpdateModel TitleUpdate, bool IsSelected)[] updates = ApplicationLibrary.FindUpdateConfigurationFor(ApplicationData.Id);
|
||||
|
||||
bool hasBundledContent = false;
|
||||
SelectedUpdate = new TitleUpdateViewModelNoUpdate();
|
||||
@@ -149,9 +148,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||
{
|
||||
Patterns = new[] { "*.nsp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||
Patterns = ["*.nsp"],
|
||||
AppleUniformTypeIdentifiers = ["com.ryujinx.nsp"],
|
||||
MimeTypes = ["application/x-nx-nsp"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public UserFirmwareAvatarSelectorViewModel()
|
||||
{
|
||||
_images = new ObservableCollection<ProfileImageModel>();
|
||||
_images = [];
|
||||
|
||||
LoadImagesFromStore();
|
||||
PropertyChanged += (_, args) =>
|
||||
@@ -104,7 +104,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
// TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
|
||||
if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs"))
|
||||
{
|
||||
using UniqueRef<IFile> file = new UniqueRef<IFile>();
|
||||
using UniqueRef<IFile> file = new();
|
||||
|
||||
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public UserProfileViewModel()
|
||||
{
|
||||
Profiles = new ObservableCollection<BaseModel>();
|
||||
LostProfiles = new ObservableCollection<UserProfile>();
|
||||
Profiles = [];
|
||||
LostProfiles = [];
|
||||
IsEmpty = !LostProfiles.Any();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
[ObservableProperty] private int _sortIndex;
|
||||
[ObservableProperty] private int _orderIndex;
|
||||
[ObservableProperty] private string _search;
|
||||
[ObservableProperty] private ObservableCollection<SaveModel> _saves = new();
|
||||
[ObservableProperty] private ObservableCollection<SaveModel> _views = new();
|
||||
[ObservableProperty] private ObservableCollection<SaveModel> _saves = [];
|
||||
[ObservableProperty] private ObservableCollection<SaveModel> _views = [];
|
||||
private readonly AccountManager _accountManager;
|
||||
|
||||
public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private readonly Ryujinx.Common.Logging.XCIFileTrimmerLog _logger;
|
||||
private ApplicationLibrary ApplicationLibrary => _mainWindowViewModel.ApplicationLibrary;
|
||||
private Optional<XCITrimmerFileModel> _processingApplication = null;
|
||||
private AvaloniaList<XCITrimmerFileModel> _allXCIFiles = new();
|
||||
private AvaloniaList<XCITrimmerFileModel> _selectedXCIFiles = new();
|
||||
private AvaloniaList<XCITrimmerFileModel> _displayedXCIFiles = new();
|
||||
private AvaloniaList<XCITrimmerFileModel> _allXCIFiles = [];
|
||||
private AvaloniaList<XCITrimmerFileModel> _selectedXCIFiles = [];
|
||||
private AvaloniaList<XCITrimmerFileModel> _displayedXCIFiles = [];
|
||||
private MainWindowViewModel _mainWindowViewModel;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private string _search;
|
||||
@@ -183,7 +183,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
XCIFileTrimmer trimmer = new XCIFileTrimmer(xciApp.Path, _logger);
|
||||
XCIFileTrimmer trimmer = new(xciApp.Path, _logger);
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:ControllerInputViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:KeyValueConverter x:Key="Key" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="ToggleButton">
|
||||
<Setter Property="Width" Value="90" />
|
||||
@@ -78,7 +75,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonZl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonZl, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonZl, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -94,7 +91,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonL">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonL, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonL, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -110,7 +107,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonMinus">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonMinus, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonMinus, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -144,7 +141,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickButton">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickButton, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -161,7 +158,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftJoystick" Tag="stick">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftJoystick, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftJoystick, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -254,7 +251,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadUp, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadUp, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -271,7 +268,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadDown, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadDown, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -288,7 +285,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadLeft, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -305,7 +302,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadRight, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadRight, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -368,7 +365,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSr, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -386,7 +383,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSl, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -404,7 +401,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSr, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -422,7 +419,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSl, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -495,8 +492,6 @@
|
||||
Margin="0,-1,0,0">
|
||||
<Grid IsVisible="{Binding ParentModel.HasLed}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
@@ -505,39 +500,14 @@
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{Binding Config.EnableLedChanging, Mode=TwoWay}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLedColor}" />
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLed}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
Margin="5, 10, 5, 10"
|
||||
MinWidth="0"
|
||||
<Button
|
||||
Margin="10"
|
||||
Grid.Column="1"
|
||||
IsVisible="{Binding ParentModel.CanClearLed}"
|
||||
IsChecked="{Binding Config.TurnOffLed, Mode=TwoWay}"
|
||||
Command="{Binding LedDisabledChanged}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLedColorDisable}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
Margin="5, 10 5,10"
|
||||
MinWidth="0"
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding !Config.TurnOffLed}"
|
||||
IsChecked="{Binding Config.UseRainbowLed, Mode=TwoWay}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLedColorRainbow}" />
|
||||
</CheckBox>
|
||||
<ui:ColorPickerButton
|
||||
Grid.Column="3"
|
||||
IsEnabled="{Binding Config.ShowLedColorPicker}"
|
||||
Margin="5, 10, 10, 10"
|
||||
IsMoreButtonVisible="False"
|
||||
UseColorPalette="False"
|
||||
UseColorTriangle="False"
|
||||
UseColorWheel="False"
|
||||
ShowAcceptDismissButtons="False"
|
||||
IsAlphaEnabled="False"
|
||||
AttachedToVisualTree="ColorPickerButton_OnAttachedToVisualTree"
|
||||
ColorChanged="ColorPickerButton_OnColorChanged"
|
||||
Color="{Binding Config.LedColor, Mode=TwoWay}">
|
||||
</ui:ColorPickerButton>
|
||||
Command="{Binding ShowLedConfig}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsConfigureGeneral}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
@@ -577,7 +547,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonZr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonZr, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonZr, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -595,7 +565,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonR">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonR, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonR, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -613,7 +583,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonPlus">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonPlus, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonPlus, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -648,7 +618,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonA">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonA, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonA, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -665,7 +635,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonB">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonB, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonB, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -682,7 +652,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonX">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonX, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonX, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -699,7 +669,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonY">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonY, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonY, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -733,7 +703,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickButton">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickButton, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -751,7 +721,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightJoystick" Tag="stick">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightJoystick, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightJoystick, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
|
||||
@@ -4,14 +4,11 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.Assigner;
|
||||
using System.Linq;
|
||||
using Button = Ryujinx.Input.Button;
|
||||
using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
||||
|
||||
@@ -246,24 +243,5 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
|
||||
{
|
||||
if (!args.NewColor.HasValue) return;
|
||||
if (DataContext is not ControllerInputViewModel cVm) return;
|
||||
if (!cVm.Config.EnableLedChanging) return;
|
||||
if (cVm.Config.TurnOffLed) return;
|
||||
|
||||
cVm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (DataContext is not ControllerInputViewModel cVm) return;
|
||||
if (!cVm.Config.EnableLedChanging) return;
|
||||
if (cVm.Config.TurnOffLed) return;
|
||||
|
||||
cVm.ParentModel.SelectedGamepad.SetLed(cVm.Config.LedColor.ToUInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:KeyboardInputViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:KeyValueConverter x:Key="Key" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="ToggleButton">
|
||||
<Setter Property="Width" Value="90" />
|
||||
@@ -76,7 +73,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonZl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonZl, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonZl, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -92,7 +89,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonL">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonL, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonL, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -108,7 +105,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonMinus">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonMinus, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonMinus, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -143,7 +140,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickButton">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickButton, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -160,7 +157,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickUp, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickUp, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -177,7 +174,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickDown, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickDown, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -194,7 +191,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickLeft, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -211,7 +208,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftStickRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftStickRight, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftStickRight, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -247,7 +244,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadUp, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadUp, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -264,7 +261,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadDown, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadDown, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -281,7 +278,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadLeft, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -298,7 +295,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="DpadRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.DpadRight, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.DpadRight, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -341,7 +338,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSr, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -359,7 +356,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="LeftButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.LeftButtonSl, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.LeftButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -377,7 +374,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightButtonSr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSr, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightButtonSr, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -395,7 +392,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightButtonSl">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightButtonSl, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightButtonSl, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -437,7 +434,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonZr">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonZr, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonZr, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -455,7 +452,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonR">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonR, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonR, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -473,7 +470,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonPlus">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonPlus, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonPlus, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -508,7 +505,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonA">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonA, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonA, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -525,7 +522,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonB">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonB, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonB, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -542,7 +539,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonX">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonX, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonX, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -559,7 +556,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="ButtonY">
|
||||
<TextBlock
|
||||
Text="{Binding Config.ButtonY, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.ButtonY, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -594,7 +591,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickButton">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickButton, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickButton, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -611,7 +608,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickUp">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickUp, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickUp, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -628,7 +625,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickDown">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickDown, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickDown, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -645,7 +642,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickLeft">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickLeft, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickLeft, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
@@ -662,7 +659,7 @@
|
||||
TextAlignment="Center" />
|
||||
<ToggleButton Name="RightStickRight">
|
||||
<TextBlock
|
||||
Text="{Binding Config.RightStickRight, Converter={StaticResource Key}}"
|
||||
Text="{Binding Config.RightStickRight, Converter={x:Static helpers:KeyValueConverter.Instance}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
|
||||
64
src/Ryujinx/UI/Views/Input/LedInputView.axaml
Normal file
64
src/Ryujinx/UI/Views/Input/LedInputView.axaml
Normal file
@@ -0,0 +1,64 @@
|
||||
<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:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="viewModels:LedInputViewModel"
|
||||
x:Class="Ryujinx.UI.Views.Input.LedInputView">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding ParentModel.CanClearLed}">
|
||||
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColorDisable}" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
MinWidth="0"
|
||||
IsChecked="{Binding TurnOffLed, Mode=TwoWay}"
|
||||
Command="{Binding LedDisabledChanged}">
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsEnabled="{Binding !TurnOffLed}">
|
||||
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColorRainbow}" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
MinWidth="0"
|
||||
IsChecked="{Binding UseRainbowLed, Mode=TwoWay}">
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsEnabled="{Binding !TurnOffLed}">
|
||||
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColorRainbowSpeed}" />
|
||||
<Slider HorizontalAlignment="Center"
|
||||
Value="{Binding RainbowSpeed}"
|
||||
Width="175"
|
||||
Margin="0,-3,0,0"
|
||||
Height="32"
|
||||
Padding="0,-5"
|
||||
TickFrequency="0.25"
|
||||
LargeChange="1"
|
||||
SmallChange="0.25"
|
||||
VerticalAlignment="Center"
|
||||
Minimum="1"
|
||||
Maximum="10" />
|
||||
<TextBlock Margin="5,0"
|
||||
MinWidth="75"
|
||||
Text="{Binding RainbowSpeedText}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsEnabled="{Binding ShowLedColorPicker}">
|
||||
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColor}" />
|
||||
<ui:ColorPickerButton
|
||||
Margin="5"
|
||||
IsMoreButtonVisible="False"
|
||||
UseColorPalette="False"
|
||||
UseColorTriangle="False"
|
||||
UseColorWheel="False"
|
||||
ShowAcceptDismissButtons="False"
|
||||
IsAlphaEnabled="False"
|
||||
AttachedToVisualTree="ColorPickerButton_OnAttachedToVisualTree"
|
||||
ColorChanged="ColorPickerButton_OnColorChanged"
|
||||
Color="{Binding LedColor, Mode=TwoWay}">
|
||||
</ui:ColorPickerButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
73
src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs
Normal file
73
src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.UI.Views.Input
|
||||
{
|
||||
public partial class LedInputView : UserControl
|
||||
{
|
||||
private readonly LedInputViewModel _viewModel;
|
||||
|
||||
public LedInputView(ControllerInputViewModel viewModel)
|
||||
{
|
||||
DataContext = _viewModel = new LedInputViewModel
|
||||
{
|
||||
ParentModel = viewModel.ParentModel,
|
||||
TurnOffLed = viewModel.Config.TurnOffLed,
|
||||
EnableLedChanging = viewModel.Config.EnableLedChanging,
|
||||
LedColor = viewModel.Config.LedColor,
|
||||
UseRainbowLed = viewModel.Config.UseRainbowLed,
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
|
||||
{
|
||||
if (!args.NewColor.HasValue) return;
|
||||
if (DataContext is not LedInputViewModel lvm) return;
|
||||
if (!lvm.EnableLedChanging) return;
|
||||
if (lvm.TurnOffLed) return;
|
||||
|
||||
lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (DataContext is not LedInputViewModel lvm) return;
|
||||
if (!lvm.EnableLedChanging) return;
|
||||
if (lvm.TurnOffLed) return;
|
||||
|
||||
lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32());
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerInputViewModel viewModel)
|
||||
{
|
||||
LedInputView content = new(viewModel);
|
||||
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.ControllerLedTitle],
|
||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
|
||||
Content = content,
|
||||
};
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
config.EnableLedChanging = content._viewModel.EnableLedChanging;
|
||||
config.LedColor = content._viewModel.LedColor;
|
||||
config.UseRainbowLed = content._viewModel.UseRainbowLed;
|
||||
config.TurnOffLed = content._viewModel.TurnOffLed;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@
|
||||
Command="{Binding OpenRyujinxFolder}"
|
||||
Header="{ext:Locale MenuBarFileOpenEmuFolder}"
|
||||
ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenScreenshotsFolder}"
|
||||
Header="{ext:Locale MenuBarFileOpenScreenshotsFolder}"
|
||||
ToolTip.Tip="{ext:Locale OpenScreenshotFolderTooltip}"/>
|
||||
<MenuItem
|
||||
Command="{Binding OpenLogsFolder}"
|
||||
Header="{ext:Locale MenuBarFileOpenLogsFolder}"
|
||||
|
||||
@@ -2,7 +2,6 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Gommon;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
@@ -51,13 +50,9 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
|
||||
XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
|
||||
AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
|
||||
CompatibilityListMenuItem.Command = Commands.Create(CompatibilityList.Show);
|
||||
|
||||
UpdateMenuItem.Command = Commands.Create(async () =>
|
||||
{
|
||||
if (Updater.CanUpdate(true))
|
||||
await Updater.BeginUpdateAsync(true);
|
||||
});
|
||||
CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show());
|
||||
|
||||
UpdateMenuItem.Command = MainWindowViewModel.UpdateCommand;
|
||||
|
||||
FaqMenuItem.Command =
|
||||
SetupGuideMenuItem.Command =
|
||||
@@ -134,7 +129,12 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||
|
||||
Rainbow.Enable();
|
||||
|
||||
await Window.SettingsWindow.ShowDialog(Window);
|
||||
|
||||
Rainbow.Disable();
|
||||
Rainbow.Reset();
|
||||
|
||||
Window.SettingsWindow = null;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
DockPanel.Dock="Bottom"
|
||||
IsVisible="{Binding ShowMenuAndStatusBar}"
|
||||
ColumnDefinitions="Auto,Auto,*,Auto,Auto">
|
||||
ColumnDefinitions="Auto,Auto,*,Auto,Auto,Auto">
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Margin="5"
|
||||
@@ -280,9 +280,31 @@
|
||||
Text="{Binding GpuNameText}"
|
||||
TextAlignment="Start" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
<StackPanel
|
||||
Grid.Column="4"
|
||||
Margin="0,0,5,0"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="EnableNonGameRunningControls" />
|
||||
<Binding Path="UpdateAvailable" />
|
||||
</MultiBinding>
|
||||
</StackPanel.IsVisible>
|
||||
<Button Margin="0, 0, 5, -2"
|
||||
Command="{Binding UpdateCommand}"
|
||||
Background="{DynamicResource SystemAccentColor}">
|
||||
<TextBlock
|
||||
Margin="-5"
|
||||
Foreground="{StaticResource SystemColorButtonTextColor}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale UpdaterBackgroundStatusBarButtonText}" />
|
||||
</Button>
|
||||
<controls:MiniVerticalSeparator Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="5"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowFirmwareStatus}"
|
||||
Orientation="Horizontal">
|
||||
|
||||
@@ -113,37 +113,37 @@
|
||||
Tag="TitleId" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale GameListHeaderDeveloper}"
|
||||
Content="{ext:Locale GameListSortDeveloper}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
|
||||
Tag="Developer" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale GameListHeaderTimePlayed}"
|
||||
Content="{ext:Locale GameListSortTimePlayed}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
|
||||
Tag="TotalTimePlayed" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale GameListHeaderLastPlayed}"
|
||||
Content="{ext:Locale GameListSortLastPlayed}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
|
||||
Tag="LastPlayed" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale GameListHeaderFileExtension}"
|
||||
Content="{ext:Locale GameListSortFileExtension}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
|
||||
Tag="FileType" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale GameListHeaderFileSize}"
|
||||
Content="{ext:Locale GameListSortFileSize}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
|
||||
Tag="FileSize" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale GameListHeaderPath}"
|
||||
Content="{ext:Locale GameListSortPath}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
|
||||
Tag="Path" />
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
@@ -69,7 +70,7 @@
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding UseHypervisor}"
|
||||
IsVisible="{Binding IsAppleSiliconMac}"
|
||||
IsVisible="{x:Static helper:RunningPlatform.IsArmMac}"
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemUseHypervisor}"
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
Design.Width="1000"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
@@ -48,7 +49,7 @@
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
|
||||
<TextBlock Text="OpenGL" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsAppleSiliconMac}">
|
||||
<ComboBoxItem IsEnabled="{x:Static helper:RunningPlatform.IsArmMac}">
|
||||
<TextBlock Text="Metal (ARM Mac only, Experimental)" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:KeyValueConverter x:Key="Key" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="StackPanel > StackPanel">
|
||||
<Setter Property="Margin" Value="10, 0, 0, 0" />
|
||||
@@ -52,67 +49,67 @@
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
|
||||
<ToggleButton Name="ToggleVSyncMode">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
|
||||
<ToggleButton Name="Screenshot">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
|
||||
<ToggleButton Name="ShowUI">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
|
||||
<ToggleButton Name="Pause">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
|
||||
<ToggleButton Name="ToggleMute">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
|
||||
<ToggleButton Name="ResScaleUp">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
|
||||
<ToggleButton Name="ResScaleDown">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
|
||||
<ToggleButton Name="VolumeUp">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
|
||||
<ToggleButton Name="VolumeDown">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
|
||||
<ToggleButton Name="CustomVSyncIntervalIncrement">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
|
||||
<ToggleButton Name="CustomVSyncIntervalDecrement">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={StaticResource Key}}" />
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
ToolTip.Tip="{ext:Locale DebugLogTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableAvaloniaLog}"
|
||||
ToolTip.Tip="{ext:Locale AvaloniaLogTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableAvaloniaLogs}" />
|
||||
</CheckBox>
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale FSAccessLogModeTooltip}"
|
||||
|
||||
@@ -7,23 +7,26 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsNetworkView : UserControl
|
||||
{
|
||||
private readonly Random _random;
|
||||
|
||||
public SettingsViewModel ViewModel;
|
||||
|
||||
public SettingsNetworkView()
|
||||
{
|
||||
_random = new Random();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void GenLdnPassButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
byte[] code = new byte[4];
|
||||
new Random().NextBytes(code);
|
||||
_random.NextBytes(code);
|
||||
ViewModel.LdnPassphrase = $"Ryujinx-{BitConverter.ToUInt32(code):x8}";
|
||||
}
|
||||
|
||||
private void ClearLdnPassButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.LdnPassphrase = "";
|
||||
ViewModel.LdnPassphrase = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<UserControl.Resources>
|
||||
<helpers:TimeZoneConverter x:Key="TimeZone" />
|
||||
</UserControl.Resources>
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
@@ -162,7 +159,7 @@
|
||||
Text="{Binding Path=TimeZone, Mode=OneWay}"
|
||||
TextChanged="TimeZoneBox_OnTextChanged"
|
||||
ToolTip.Tip="{ext:Locale TimezoneTooltip}"
|
||||
ValueMemberBinding="{Binding Mode=OneWay, Converter={StaticResource TimeZone}}" />
|
||||
ValueMemberBinding="{Binding Mode=OneWay, Converter={x:Static helpers:TimeZoneConverter.Instance}}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="0,0,0,10"
|
||||
@@ -173,7 +170,8 @@
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}"
|
||||
Width="250"/>
|
||||
<DatePicker
|
||||
VerticalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding !MatchSystemTime}"
|
||||
SelectedDate="{Binding CurrentDate}"
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}"
|
||||
Width="350" />
|
||||
@@ -184,17 +182,21 @@
|
||||
<TimePicker
|
||||
VerticalAlignment="Center"
|
||||
ClockIdentifier="24HourClock"
|
||||
IsEnabled="{Binding !MatchSystemTime}"
|
||||
SelectedTime="{Binding CurrentTime}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}" />
|
||||
<Button
|
||||
Margin="10, 0, 0, 0"
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Click="MatchSystemTime_OnClick"
|
||||
Background="{DynamicResource SystemAccentColor}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemTimeMatch}" />
|
||||
</Button>
|
||||
Text="{ext:Locale SettingsTabSystemSystemTimeMatch}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}"
|
||||
Width="250"/>
|
||||
<CheckBox
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding MatchSystemTime}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}"/>
|
||||
</StackPanel>
|
||||
<Separator />
|
||||
<StackPanel Margin="0,10,0,10"
|
||||
@@ -315,8 +317,8 @@
|
||||
<CheckBox
|
||||
IsEnabled="{Binding !EnableAutoAssign}"
|
||||
IsChecked="{Binding IgnoreApplet}"
|
||||
ToolTip.Tip="{ext:Locale IgnoreAppletTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreApplet}" />
|
||||
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
IsChecked="{Binding EnableCustomVSyncInterval}"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||
|
||||
@@ -35,7 +33,5 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
|
||||
}
|
||||
}
|
||||
|
||||
private void MatchSystemTime_OnClick(object sender, RoutedEventArgs e) => ViewModel.MatchSystemTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
@@ -30,18 +31,57 @@
|
||||
ToolTip.Tip="{ext:Locale ToggleDiscordTooltip}"
|
||||
Text="{ext:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowConfirmExit}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralShowConfirmExitDialog}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding RememberWindowState}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralRememberWindowState}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowTitleBar}" Name="ShowTitleBarBox">
|
||||
<CheckBox IsChecked="{Binding ShowTitleBar}" IsVisible="{x:Static helper:RunningPlatform.IsWindows}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralShowTitleBar}" />
|
||||
</CheckBox>
|
||||
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralFocusLossType}"
|
||||
Width="150" />
|
||||
<ComboBox SelectedIndex="{Binding FocusLostActionType}"
|
||||
HorizontalContentAlignment="Left"
|
||||
MinWidth="100">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeDoNothing}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeBlockInput}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeMuteAudio}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeBlockInputAndMuteAudio}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypePauseEmulation}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}"
|
||||
Width="150" />
|
||||
<ComboBox SelectedIndex="{Binding UpdateCheckerType}"
|
||||
HorizontalContentAlignment="Left"
|
||||
MinWidth="100">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchOff}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchPromptAtStartup}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchBackground}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralHideCursor}"
|
||||
|
||||
@@ -2,7 +2,6 @@ using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.VisualTree;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
@@ -22,7 +21,6 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
public SettingsUiView()
|
||||
{
|
||||
InitializeComponent();
|
||||
ShowTitleBarBox.IsVisible = OperatingSystem.IsWindows();
|
||||
AddGameDirButton.Command =
|
||||
Commands.Create(() => AddDirButton(GameDirPathBox, ViewModel.GameDirectories, true));
|
||||
AddAutoloadDirButton.Command =
|
||||
@@ -38,11 +36,8 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
directories.Add(path);
|
||||
|
||||
addDirBox.Clear();
|
||||
|
||||
if (isGameList)
|
||||
ViewModel.GameDirectoryChanged = true;
|
||||
else
|
||||
ViewModel.AutoloadDirectoryChanged = true;
|
||||
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -52,10 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
directories.Add(folder.Value.Path.LocalPath);
|
||||
|
||||
if (isGameList)
|
||||
ViewModel.GameDirectoryChanged = true;
|
||||
else
|
||||
ViewModel.AutoloadDirectoryChanged = true;
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +59,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
foreach (string path in new List<string>(GameDirsList.SelectedItems.Cast<string>()))
|
||||
{
|
||||
ViewModel.GameDirectories.Remove(path);
|
||||
ViewModel.GameDirectoryChanged = true;
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
|
||||
if (GameDirsList.ItemCount > 0)
|
||||
@@ -83,7 +75,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
foreach (string path in new List<string>(AutoloadDirsList.SelectedItems.Cast<string>()))
|
||||
{
|
||||
ViewModel.AutoloadDirectories.Remove(path);
|
||||
ViewModel.AutoloadDirectoryChanged = true;
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
|
||||
if (AutoloadDirsList.ItemCount > 0)
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
mc:Ignorable="d"
|
||||
Focusable="True"
|
||||
x:DataType="models:TempProfile">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -74,7 +71,7 @@
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
</Panel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:UserFirmwareAvatarSelectorViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -62,7 +59,7 @@
|
||||
<Panel
|
||||
Background="{Binding BackgroundColor}"
|
||||
Margin="5">
|
||||
<Image Source="{Binding Data, Converter={StaticResource ByteImage}}" />
|
||||
<Image Source="{Binding Data, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
</Panel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
|
||||
@@ -66,11 +66,11 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
if (ViewModel.SelectedImage != null)
|
||||
{
|
||||
using MemoryStream streamJpg = new MemoryStream();
|
||||
using MemoryStream streamJpg = new();
|
||||
using SKBitmap bitmap = SKBitmap.Decode(ViewModel.SelectedImage);
|
||||
using SKBitmap newBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
|
||||
using SKBitmap newBitmap = new(bitmap.Width, bitmap.Height);
|
||||
|
||||
using (SKCanvas canvas = new SKCanvas(newBitmap))
|
||||
using (SKCanvas canvas = new(newBitmap))
|
||||
{
|
||||
canvas.Clear(new SKColor(
|
||||
ViewModel.BackgroundColor.R,
|
||||
|
||||
@@ -70,9 +70,9 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||
{
|
||||
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" },
|
||||
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" },
|
||||
Patterns = ["*.jpg", "*.jpeg", "*.png", "*.bmp"],
|
||||
AppleUniformTypeIdentifiers = ["public.jpeg", "public.png", "com.microsoft.bmp"],
|
||||
MimeTypes = ["image/jpeg", "image/png", "image/bmp"],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -103,7 +103,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
|
||||
SKBitmap resizedBitmap = bitmap.Resize(new SKImageInfo(256, 256), SKFilterQuality.High);
|
||||
|
||||
using MemoryStream streamJpg = new MemoryStream();
|
||||
using MemoryStream streamJpg = new();
|
||||
|
||||
if (resizedBitmap != null)
|
||||
{
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:UserSaveManagerViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -147,7 +144,7 @@
|
||||
IsVisible="{Binding InGameList}"
|
||||
Width="42"
|
||||
Height="42"
|
||||
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
||||
Source="{Binding Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
<TextBlock
|
||||
MaxLines="3"
|
||||
Width="320"
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
public void LoadSaves()
|
||||
{
|
||||
ViewModel.Saves.Clear();
|
||||
ObservableCollection<SaveModel> saves = new ObservableCollection<SaveModel>();
|
||||
ObservableCollection<SaveModel> saves = [];
|
||||
SaveDataFilter saveDataFilter = SaveDataFilter.Make(
|
||||
programId: default,
|
||||
saveType: SaveDataType.Account,
|
||||
@@ -75,7 +75,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
saveDataId: default,
|
||||
index: default);
|
||||
|
||||
using UniqueRef<SaveDataIterator> saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||
using UniqueRef<SaveDataIterator> saveDataIterator = new();
|
||||
|
||||
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
SaveDataInfo save = saveDataInfo[i];
|
||||
if (save.ProgramId.Value != 0)
|
||||
{
|
||||
SaveModel saveModel = new SaveModel(save);
|
||||
SaveModel saveModel = new(save);
|
||||
saves.Add(saveModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
mc:Ignorable="d"
|
||||
Focusable="True"
|
||||
x:DataType="viewModels:UserProfileViewModel">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Design.DataContext>
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
@@ -74,7 +71,7 @@
|
||||
Height="96"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
Source="{Binding Image, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
MaxWidth="90"
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
CornerRadius="15"
|
||||
Tag="https://discord.gg/dHPrkBkkyA"
|
||||
Tag="https://discord.gg/PEuzjrFXUA"
|
||||
ToolTip.Tip="{ext:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Image Source="{Binding DiscordLogo}" />
|
||||
</Button>
|
||||
@@ -142,42 +142,40 @@
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch" RowDefinitions="Auto,Auto">
|
||||
VerticalAlignment="Stretch" RowDefinitions="Auto,Auto,Auto">
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="0,10,0,0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontSize="15"
|
||||
Classes="h1"
|
||||
FontWeight="Bold"
|
||||
Text="{ext:Locale AboutRyujinxAboutTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{ext:Locale AboutRyujinxAboutContent}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Separator Grid.Row="1" Margin="0,20" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
Grid.Row="2"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontSize="15"
|
||||
Classes="h1"
|
||||
FontWeight="Bold"
|
||||
Text="{ext:Locale AboutRyujinxMaintainersTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Margin="0, 0, 0, 5"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Developers}"/>
|
||||
<TextBlock
|
||||
FontSize="15"
|
||||
Classes="h1"
|
||||
FontWeight="Bold"
|
||||
Text="{ext:Locale AboutRyujinxFormerMaintainersTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
FontSize="11"
|
||||
Text="{Binding FormerDevelopers}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Margin="0, 5, 0, 0"
|
||||
Padding="5"
|
||||
HorizontalAlignment="Left"
|
||||
Background="Transparent"
|
||||
|
||||
@@ -18,8 +18,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public AboutWindow()
|
||||
{
|
||||
DataContext = new AboutWindowViewModel();
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
GitHubRepoButton.Tag =
|
||||
@@ -28,12 +26,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public static async Task Show()
|
||||
{
|
||||
using AboutWindowViewModel viewModel = new();
|
||||
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = string.Empty,
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = new AboutWindow()
|
||||
Content = new AboutWindow { DataContext = viewModel }
|
||||
};
|
||||
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
Width="500"
|
||||
Height="500"
|
||||
Width="600"
|
||||
Height="750"
|
||||
MinWidth="500"
|
||||
MinHeight="500"
|
||||
x:DataType="window:CheatWindow"
|
||||
|
||||
@@ -34,7 +34,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
|
||||
{
|
||||
LoadedCheats = new AvaloniaList<CheatNode>();
|
||||
MinWidth = 500;
|
||||
MinHeight = 650;
|
||||
|
||||
LoadedCheats = [];
|
||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
@@ -59,9 +62,9 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
int cheatAdded = 0;
|
||||
|
||||
ModLoader.ModCache mods = new ModLoader.ModCache();
|
||||
ModLoader.ModCache mods = new();
|
||||
|
||||
ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleIdValue);
|
||||
ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleIdValue, []);
|
||||
|
||||
string currentCheatFile = string.Empty;
|
||||
string buildId = string.Empty;
|
||||
@@ -81,7 +84,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
LoadedCheats.Add(currentGroup);
|
||||
}
|
||||
|
||||
CheatNode model = new CheatNode(cheat.Name, buildId, string.Empty, false, enabled.Contains($"{buildId}-{cheat.Name}"));
|
||||
CheatNode model = new(cheat.Name, buildId, string.Empty, false, enabled.Contains($"{buildId}-{cheat.Name}"));
|
||||
currentGroup?.SubNodes.Add(model);
|
||||
|
||||
cheatAdded++;
|
||||
|
||||
@@ -2,8 +2,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user