Compare commits
29 Commits
Canary-1.2
...
3abee2a0be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3abee2a0be | ||
|
|
57c22a1f32 | ||
|
|
94f34a9ed1 | ||
|
|
776c0cb5cf | ||
|
|
f7976753fd | ||
|
|
b45a65fbdc | ||
|
|
ef1529a2d9 | ||
|
|
768406cb67 | ||
|
|
ed5cb82aa8 | ||
|
|
c48a2e6ba0 | ||
|
|
90f2b089eb | ||
|
|
c410474d83 | ||
|
|
ffdc419417 | ||
|
|
da3f4e1d3a | ||
|
|
69d79322bb | ||
|
|
c3af1dbf1a | ||
|
|
10ac381525 | ||
|
|
e104ee6be3 | ||
|
|
e65d1ec6c9 | ||
|
|
534f92506b | ||
|
|
10d20c1ae3 | ||
|
|
e294a79975 | ||
|
|
ec06a86899 | ||
|
|
2e4de17472 | ||
|
|
9227cbe5a7 | ||
|
|
332bcdfaf1 | ||
|
|
1c8276197f | ||
|
|
a3596ba858 | ||
|
|
3ffcc72117 |
@@ -97,7 +97,7 @@ If you are planning to contribute or just want to learn more about this project
|
||||
|
||||
- **Input**
|
||||
|
||||
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers.
|
||||
We currently have support for keyboard, mouse, touch input, Joy-Con input support, and nearly all controllers.
|
||||
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
||||
In all scenarios, you can set up everything inside the input configuration menu.
|
||||
|
||||
|
||||
@@ -631,6 +631,7 @@
|
||||
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
|
||||
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
|
||||
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17
|
||||
01008c2019598000,"Bluey: The Videogame",,playable,2025-02-11 04:38:00
|
||||
01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40
|
||||
010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15
|
||||
01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
|
||||
@@ -1382,6 +1383,9 @@
|
||||
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34
|
||||
01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
|
||||
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51
|
||||
0100c3c012718000,"Grand Theft Auto: III – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||
0100182014022000,"Grand Theft Auto: Vice City – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||
010065a014024000,"Grand Theft Auto: San Andreas – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
|
||||
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
|
||||
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
|
||||
@@ -2729,7 +2733,7 @@
|
||||
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11
|
||||
0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12
|
||||
010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34
|
||||
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53
|
||||
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00
|
||||
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
|
||||
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
|
||||
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42
|
||||
|
||||
|
@@ -24,7 +24,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||
|
||||
private static CacheMemoryAllocator _cacheAllocator;
|
||||
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
||||
|
||||
private static readonly List<CacheEntry> _cacheEntries = [];
|
||||
|
||||
@@ -40,37 +40,48 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
JitUnwindWindows.RemoveFunctionTableHandler(
|
||||
_jitRegions[0].Pointer);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _jitRegions.Count; i++)
|
||||
{
|
||||
_jitRegions[i].Dispose();
|
||||
}
|
||||
|
||||
_jitRegions.Clear();
|
||||
_cacheAllocators.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
_activeRegionIndex = 0;
|
||||
|
||||
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||
_jitRegions.Add(firstRegion);
|
||||
_activeRegionIndex = 0;
|
||||
|
||||
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
|
||||
_cacheAllocators.Add(firstCacheAllocator);
|
||||
|
||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
}
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
JitUnwindWindows.InstallFunctionTableHandler(
|
||||
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||
);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +147,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||
{
|
||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
_cacheEntries.RemoveAt(entryIndex);
|
||||
}
|
||||
|
||||
@@ -167,30 +178,24 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||
|
||||
if (allocOffset >= 0)
|
||||
{
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset >= 0)
|
||||
{
|
||||
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
_activeRegionIndex = i;
|
||||
return allocOffset;
|
||||
}
|
||||
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
int exhaustedRegion = _activeRegionIndex;
|
||||
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
||||
_jitRegions.Add(newRegion);
|
||||
_activeRegionIndex = _jitRegions.Count - 1;
|
||||
|
||||
int newRegionNumber = _activeRegionIndex;
|
||||
|
||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||
|
||||
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||
_cacheAllocators.Add(new CacheMemoryAllocator(CacheSize));
|
||||
|
||||
int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||
if (allocOffsetNew < 0)
|
||||
{
|
||||
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace ARMeilleure.Translation.Cache
|
||||
nint context,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static unsafe partial bool RtlDeleteFunctionTable(
|
||||
ulong tableIdentifier);
|
||||
|
||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||
|
||||
private static int _sizeOfRuntimeFunction;
|
||||
@@ -91,6 +96,23 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
||||
{
|
||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||
|
||||
bool result;
|
||||
|
||||
unsafe
|
||||
{
|
||||
result = RtlDeleteFunctionTable(codeCachePtr | 3);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException("Failure removing function table callback.");
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
||||
{
|
||||
int offset = (int)((long)controlPc - context.ToInt64());
|
||||
|
||||
@@ -13,5 +13,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
public Key VolumeDown { get; set; }
|
||||
public Key CustomVSyncIntervalIncrement { get; set; }
|
||||
public Key CustomVSyncIntervalDecrement { get; set; }
|
||||
public Key TurboMode { get; set; }
|
||||
public bool TurboModeWhileHeld { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,34 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Helper
|
||||
{
|
||||
public enum OperatingSystemType
|
||||
{
|
||||
MacOS,
|
||||
Linux,
|
||||
Windows
|
||||
}
|
||||
|
||||
public static class RunningPlatform
|
||||
{
|
||||
public static readonly OperatingSystemType CurrentOS
|
||||
= IsMacOS
|
||||
? OperatingSystemType.MacOS
|
||||
: IsWindows
|
||||
? OperatingSystemType.Windows
|
||||
: IsLinux
|
||||
? OperatingSystemType.Linux
|
||||
: throw new PlatformNotSupportedException();
|
||||
|
||||
public static Architecture Architecture => RuntimeInformation.OSArchitecture;
|
||||
public static Architecture CurrentProcessArchitecture => RuntimeInformation.ProcessArchitecture;
|
||||
|
||||
public static bool IsMacOS => OperatingSystem.IsMacOS();
|
||||
public static bool IsWindows => OperatingSystem.IsWindows();
|
||||
public static bool IsLinux => OperatingSystem.IsLinux();
|
||||
|
||||
public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64;
|
||||
public static bool IsArm => Architecture is Architecture.Arm64;
|
||||
|
||||
public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64;
|
||||
public static bool IsX64 => Architecture is Architecture.X64;
|
||||
|
||||
public static bool IsIntelMac => IsMacOS && IsX64;
|
||||
public static bool IsArmMac => IsMacOS && IsArm;
|
||||
|
||||
@@ -8,10 +8,17 @@ namespace Ryujinx.Cpu
|
||||
/// </summary>
|
||||
public interface ITickSource : ICounter
|
||||
{
|
||||
public const long RealityTickScalar = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Time elapsed since the counter was created.
|
||||
/// </summary>
|
||||
TimeSpan ElapsedTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clock tick scalar, in percent points (100 = 1.0).
|
||||
/// </summary>
|
||||
long TickScalar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time elapsed since the counter was created, in seconds.
|
||||
|
||||
@@ -14,12 +14,37 @@ namespace Ryujinx.Cpu
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ulong Counter => (ulong)(ElapsedSeconds * Frequency);
|
||||
|
||||
|
||||
public long TickScalar { get; set; }
|
||||
|
||||
|
||||
private static long _acumElapsedTicks;
|
||||
|
||||
|
||||
private static long _lastElapsedTicks;
|
||||
|
||||
|
||||
private long ElapsedTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
long elapsedTicks = _tickCounter.ElapsedTicks;
|
||||
|
||||
_acumElapsedTicks += (elapsedTicks - _lastElapsedTicks) * TickScalar / 100;
|
||||
|
||||
_lastElapsedTicks = elapsedTicks;
|
||||
|
||||
return _acumElapsedTicks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimeSpan ElapsedTime => _tickCounter.Elapsed;
|
||||
|
||||
public TimeSpan ElapsedTime => Stopwatch.GetElapsedTime(0, ElapsedTicks);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq;
|
||||
public double ElapsedSeconds => ElapsedTicks * _hostTickFreq;
|
||||
|
||||
public TickSource(ulong frequency)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL.Multithreading;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
@@ -10,6 +12,20 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
bool PreferThreading { get; }
|
||||
|
||||
public IRenderer TryMakeThreaded(BackendThreading backendThreading = BackendThreading.Auto)
|
||||
{
|
||||
if (backendThreading is BackendThreading.On ||
|
||||
(backendThreading is BackendThreading.Auto && PreferThreading))
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): True");
|
||||
return new ThreadedRenderer(this);
|
||||
}
|
||||
|
||||
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): False");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
IPipeline Pipeline { get; }
|
||||
|
||||
IWindow Window { get; }
|
||||
|
||||
@@ -1065,7 +1065,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
private void UpdateIndexBufferState()
|
||||
{
|
||||
IndexBufferState indexBuffer = _state.State.IndexBufferState;
|
||||
IndexBufferState? indexBufferNullable = _state?.State.IndexBufferState;
|
||||
|
||||
if (!indexBufferNullable.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IndexBufferState indexBuffer = indexBufferNullable.Value;
|
||||
|
||||
if (_drawState.IndexCount == 0)
|
||||
{
|
||||
|
||||
@@ -127,10 +127,7 @@ namespace Ryujinx.HLE.HOS.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
string serviceName;
|
||||
|
||||
|
||||
serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
|
||||
string serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
|
||||
|
||||
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
|
||||
|
||||
private readonly LanDiscovery _lanDiscovery;
|
||||
|
||||
public LdnMitmClient(HLEConfiguration config)
|
||||
public LdnMitmClient(HleConfiguration config)
|
||||
{
|
||||
UnicastIPAddressInformation localIpInterface = NetworkHelpers.GetLocalInterface(config.MultiplayerLanInterfaceId).Item2;
|
||||
|
||||
|
||||
@@ -51,13 +51,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
||||
private string _passphrase;
|
||||
private byte[] _gameVersion = new byte[0x10];
|
||||
|
||||
private readonly HLEConfiguration _config;
|
||||
private readonly HleConfiguration _config;
|
||||
|
||||
public event EventHandler<NetworkChangeEventArgs> NetworkChange;
|
||||
|
||||
public ProxyConfig Config { get; private set; }
|
||||
|
||||
public LdnMasterProxyClient(string address, int port, HLEConfiguration config) : base(address, port)
|
||||
public LdnMasterProxyClient(string address, int port, HleConfiguration config) : base(address, port)
|
||||
{
|
||||
if (ProxyHelpers.SupportsNoDelay())
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.PreciseSleep;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||
@@ -89,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
}
|
||||
else
|
||||
{
|
||||
_ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval;
|
||||
_ticksPerFrame = ((Stopwatch.Frequency / _device.TargetVSyncInterval) * 100) / _device.TickScalar;
|
||||
_targetVSyncInterval = _device.TargetVSyncInterval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,55 +15,55 @@ namespace Ryujinx.HLE
|
||||
/// <summary>
|
||||
/// HLE configuration.
|
||||
/// </summary>
|
||||
public class HLEConfiguration
|
||||
public class HleConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// The virtual file system used by the FS service.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly VirtualFileSystem VirtualFileSystem;
|
||||
internal VirtualFileSystem VirtualFileSystem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The manager for handling a LibHac Horizon instance.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly LibHacHorizonManager LibHacHorizonManager;
|
||||
internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The account manager used by the account service.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly AccountManager AccountManager;
|
||||
internal AccountManager AccountManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The content manager used by the NCM service.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly ContentManager ContentManager;
|
||||
internal ContentManager ContentManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The persistent information between run for multi-application capabilities.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
public readonly UserChannelPersistence UserChannelPersistence;
|
||||
public UserChannelPersistence UserChannelPersistence { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GPU renderer to use for all GPU operations.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly IRenderer GpuRenderer;
|
||||
internal IRenderer GpuRenderer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The audio device driver to use for all audio operations.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly IHardwareDeviceDriver AudioDeviceDriver;
|
||||
internal IHardwareDeviceDriver AudioDeviceDriver { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The handler for various UI related operations needed outside of HLE.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly IHostUIHandler HostUIHandler;
|
||||
internal IHostUIHandler HostUIHandler { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control the memory configuration used by the emulation context.
|
||||
@@ -102,6 +102,11 @@ namespace Ryujinx.HLE
|
||||
/// Control if the Profiled Translation Cache (PTC) should be used.
|
||||
/// </summary>
|
||||
internal readonly bool EnablePtc;
|
||||
|
||||
/// <summary>
|
||||
/// Control the arbitrary scalar applied to emulated CPU tick timing.
|
||||
/// </summary>
|
||||
public long TickScalar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control if the guest application should be told that there is a Internet connection available.
|
||||
@@ -195,20 +200,13 @@ namespace Ryujinx.HLE
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
public EnabledDirtyHack[] Hacks { internal get; set; }
|
||||
|
||||
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
|
||||
LibHacHorizonManager libHacHorizonManager,
|
||||
ContentManager contentManager,
|
||||
AccountManager accountManager,
|
||||
UserChannelPersistence userChannelPersistence,
|
||||
IRenderer gpuRenderer,
|
||||
IHardwareDeviceDriver audioDeviceDriver,
|
||||
MemoryConfiguration memoryConfiguration,
|
||||
IHostUIHandler hostUIHandler,
|
||||
public HleConfiguration(MemoryConfiguration memoryConfiguration,
|
||||
SystemLanguage systemLanguage,
|
||||
RegionCode region,
|
||||
VSyncMode vSyncMode,
|
||||
bool enableDockedMode,
|
||||
bool enablePtc,
|
||||
long tickScalar,
|
||||
bool enableInternetAccess,
|
||||
IntegrityCheckLevel fsIntegrityCheckLevel,
|
||||
int fsGlobalAccessLogMode,
|
||||
@@ -227,21 +225,14 @@ namespace Ryujinx.HLE
|
||||
int customVSyncInterval,
|
||||
EnabledDirtyHack[] dirtyHacks = null)
|
||||
{
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
LibHacHorizonManager = libHacHorizonManager;
|
||||
AccountManager = accountManager;
|
||||
ContentManager = contentManager;
|
||||
UserChannelPersistence = userChannelPersistence;
|
||||
GpuRenderer = gpuRenderer;
|
||||
AudioDeviceDriver = audioDeviceDriver;
|
||||
MemoryConfiguration = memoryConfiguration;
|
||||
HostUIHandler = hostUIHandler;
|
||||
SystemLanguage = systemLanguage;
|
||||
Region = region;
|
||||
VSyncMode = vSyncMode;
|
||||
CustomVSyncInterval = customVSyncInterval;
|
||||
EnableDockedMode = enableDockedMode;
|
||||
EnablePtc = enablePtc;
|
||||
TickScalar = tickScalar;
|
||||
EnableInternetAccess = enableInternetAccess;
|
||||
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
|
||||
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
|
||||
@@ -259,5 +250,30 @@ namespace Ryujinx.HLE
|
||||
MultiplayerLdnServer = multiplayerLdnServer;
|
||||
Hacks = dirtyHacks ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the pre-configured services to use for this <see cref="HleConfiguration"/> instance.
|
||||
/// </summary>
|
||||
public HleConfiguration Configure(
|
||||
VirtualFileSystem virtualFileSystem,
|
||||
LibHacHorizonManager libHacHorizonManager,
|
||||
ContentManager contentManager,
|
||||
AccountManager accountManager,
|
||||
UserChannelPersistence userChannelPersistence,
|
||||
IRenderer gpuRenderer,
|
||||
IHardwareDeviceDriver audioDeviceDriver,
|
||||
IHostUIHandler hostUIHandler
|
||||
)
|
||||
{
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
LibHacHorizonManager = libHacHorizonManager;
|
||||
AccountManager = accountManager;
|
||||
ContentManager = contentManager;
|
||||
UserChannelPersistence = userChannelPersistence;
|
||||
GpuRenderer = gpuRenderer;
|
||||
AudioDeviceDriver = audioDeviceDriver;
|
||||
HostUIHandler = hostUIHandler;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,16 +71,24 @@ namespace Ryujinx.HLE.Loaders.Mods
|
||||
int patchOffset = (int)offset;
|
||||
int patchSize = patch.Length;
|
||||
|
||||
if (patchOffset < protectedOffset || patchOffset > memory.Length)
|
||||
if (patchOffset < protectedOffset)
|
||||
{
|
||||
continue; // Add warning?
|
||||
Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch protected memory ({patchOffset:x} is within protected boundary of {protectedOffset:x}).");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patchOffset > memory.Length)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch out of bounds memory (offset {patchOffset} ({patchOffset:x}) exceeds memory buffer length {memory.Length}).");
|
||||
continue;
|
||||
}
|
||||
|
||||
patchOffset -= protectedOffset;
|
||||
|
||||
if (patchOffset + patchSize > memory.Length)
|
||||
{
|
||||
patchSize = memory.Length - patchOffset; // Add warning?
|
||||
Logger.Warning?.Print(LogClass.ModLoader, $"Patch offset ({patchOffset:x}) + size ({patchSize}) is greater than the size of the memory buffer ({memory.Length}). Attempting to fix this...");
|
||||
patchSize = memory.Length - patchOffset;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}");
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Ryujinx.HLE
|
||||
{
|
||||
public class PerformanceStatistics
|
||||
{
|
||||
private readonly Switch _device;
|
||||
|
||||
private const int FrameTypeGame = 0;
|
||||
private const int PercentTypeFifo = 0;
|
||||
|
||||
@@ -28,8 +30,10 @@ namespace Ryujinx.HLE
|
||||
|
||||
private readonly System.Timers.Timer _resetTimer;
|
||||
|
||||
public PerformanceStatistics()
|
||||
public PerformanceStatistics(Switch device)
|
||||
{
|
||||
_device = device;
|
||||
|
||||
_frameRate = new double[1];
|
||||
_accumulatedFrameTime = new double[1];
|
||||
_previousFrameTime = new double[1];
|
||||
@@ -162,14 +166,6 @@ namespace Ryujinx.HLE
|
||||
return 1000 / _frameRate[FrameTypeGame];
|
||||
}
|
||||
|
||||
public string FormatGameFrameRate()
|
||||
{
|
||||
double frameRate = GetGameFrameRate();
|
||||
double frameTime = GetGameFrameTime();
|
||||
|
||||
return $"{frameRate:00.00} FPS ({frameTime:00.00}ms)";
|
||||
}
|
||||
|
||||
public string FormatFifoPercent()
|
||||
{
|
||||
double fifoPercent = GetFifoPercent();
|
||||
|
||||
@@ -4,6 +4,7 @@ using Ryujinx.Audio.Backends.CompatLayer;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
@@ -20,31 +21,39 @@ namespace Ryujinx.HLE
|
||||
{
|
||||
public static Switch Shared { get; private set; }
|
||||
|
||||
public HLEConfiguration Configuration { get; }
|
||||
public HleConfiguration Configuration { get; }
|
||||
public IHardwareDeviceDriver AudioDeviceDriver { get; }
|
||||
public MemoryBlock Memory { get; }
|
||||
public GpuContext Gpu { get; }
|
||||
public VirtualFileSystem FileSystem { get; }
|
||||
public HOS.Horizon System { get; }
|
||||
|
||||
public bool TurboMode = false;
|
||||
|
||||
public long TickScalar
|
||||
{
|
||||
get => System?.TickSource?.TickScalar ?? ITickSource.RealityTickScalar;
|
||||
set => System.TickSource.TickScalar = value;
|
||||
}
|
||||
|
||||
public ProcessLoader Processes { get; }
|
||||
public PerformanceStatistics Statistics { get; }
|
||||
public Hid Hid { get; }
|
||||
public TamperMachine TamperMachine { get; }
|
||||
public IHostUIHandler UIHandler { get; }
|
||||
|
||||
public int CpuCoresCount = 4; //Switch 1 has 4 cores
|
||||
public int CpuCoresCount = 4; // Switch has a quad-core Tegra X1 SoC
|
||||
|
||||
public VSyncMode VSyncMode { get; set; }
|
||||
public bool CustomVSyncIntervalEnabled { get; set; }
|
||||
public int CustomVSyncInterval { get; set; }
|
||||
|
||||
public long TargetVSyncInterval { get; set; } = 60;
|
||||
|
||||
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
||||
|
||||
public DirtyHacks DirtyHacks { get; }
|
||||
|
||||
public Switch(HLEConfiguration configuration)
|
||||
public Switch(HleConfiguration configuration)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
|
||||
ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver);
|
||||
@@ -64,7 +73,7 @@ namespace Ryujinx.HLE
|
||||
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
|
||||
Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks);
|
||||
System = new HOS.Horizon(this);
|
||||
Statistics = new PerformanceStatistics();
|
||||
Statistics = new PerformanceStatistics(this);
|
||||
Hid = new Hid(this, System.HidStorage);
|
||||
Processes = new ProcessLoader(this);
|
||||
TamperMachine = new TamperMachine();
|
||||
@@ -75,6 +84,7 @@ namespace Ryujinx.HLE
|
||||
|
||||
VSyncMode = Configuration.VSyncMode;
|
||||
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
||||
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
||||
System.State.DockedMode = Configuration.EnableDockedMode;
|
||||
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||
System.EnablePtc = Configuration.EnablePtc;
|
||||
@@ -94,16 +104,20 @@ namespace Ryujinx.HLE
|
||||
Gpu.GPFifo.DispatchCalls();
|
||||
}
|
||||
|
||||
public void IncrementCustomVSyncInterval()
|
||||
public int IncrementCustomVSyncInterval()
|
||||
{
|
||||
CustomVSyncInterval += 1;
|
||||
UpdateVSyncInterval();
|
||||
|
||||
return CustomVSyncInterval;
|
||||
}
|
||||
|
||||
public void DecrementCustomVSyncInterval()
|
||||
public int DecrementCustomVSyncInterval()
|
||||
{
|
||||
CustomVSyncInterval -= 1;
|
||||
UpdateVSyncInterval();
|
||||
|
||||
return CustomVSyncInterval;
|
||||
}
|
||||
|
||||
public void UpdateVSyncInterval()
|
||||
@@ -122,6 +136,12 @@ namespace Ryujinx.HLE
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleTurbo()
|
||||
{
|
||||
TurboMode = !TurboMode;
|
||||
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
||||
}
|
||||
|
||||
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
||||
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
|
||||
public bool LoadNca(string ncaFile, BlitStruct<ApplicationControlProperty>? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData);
|
||||
|
||||
@@ -4,9 +4,9 @@ using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
using DiscordRPC;
|
||||
using Gommon;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
@@ -35,11 +35,9 @@ using Ryujinx.Graphics.GAL.Multithreading;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using SkiaSharp;
|
||||
@@ -902,53 +900,19 @@ namespace Ryujinx.Ava
|
||||
_ => new OpenGLRenderer()
|
||||
};
|
||||
|
||||
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
|
||||
|
||||
bool isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
|
||||
if (isGALThreaded)
|
||||
{
|
||||
renderer = new ThreadedRenderer(renderer);
|
||||
}
|
||||
|
||||
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
|
||||
|
||||
// Initialize Configuration.
|
||||
MemoryConfiguration memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
|
||||
|
||||
Device = new Switch(new HLEConfiguration(
|
||||
VirtualFileSystem,
|
||||
_viewModel.LibHacHorizonManager,
|
||||
ContentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
InitializeAudio(),
|
||||
memoryConfiguration,
|
||||
_viewModel.UiHandler,
|
||||
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
||||
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
||||
ConfigurationState.Instance.Graphics.VSyncMode,
|
||||
ConfigurationState.Instance.System.EnableDockedMode,
|
||||
ConfigurationState.Instance.System.EnablePtc,
|
||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
||||
ConfigurationState.Instance.System.MatchSystemTime
|
||||
? 0
|
||||
: ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.TimeZone,
|
||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||
ConfigurationState.Instance.System.AudioVolume,
|
||||
ConfigurationState.Instance.System.UseHypervisor,
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
|
||||
ConfigurationState.Instance.Multiplayer.Mode,
|
||||
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
||||
ConfigurationState.Instance.Multiplayer.GetLdnServer(),
|
||||
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
|
||||
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
|
||||
Device = new Switch(ConfigurationState.Instance.CreateHleConfiguration()
|
||||
.Configure(
|
||||
VirtualFileSystem,
|
||||
_viewModel.LibHacHorizonManager,
|
||||
ContentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading),
|
||||
InitializeAudio(),
|
||||
_viewModel.UiHandler
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static IHardwareDeviceDriver InitializeAudio()
|
||||
@@ -1113,6 +1077,13 @@ namespace Ryujinx.Ava
|
||||
});
|
||||
|
||||
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
|
||||
|
||||
// Reload settings when the game is turned off
|
||||
// (resets custom settings if there were any)
|
||||
Program.ReloadConfig();
|
||||
|
||||
// Reload application list (changes the status of the user setting if it was added or removed during the game)
|
||||
Dispatcher.UIThread.Post(() => RyujinxApp.MainWindow.LoadApplications());
|
||||
}
|
||||
|
||||
public void InitStatus()
|
||||
@@ -1145,11 +1116,23 @@ namespace Ryujinx.Ava
|
||||
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
|
||||
dockedMode,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||
Device.Statistics.FormatGameFrameRate(),
|
||||
FormatGameFrameRate(),
|
||||
Device.Statistics.FormatFifoPercent(),
|
||||
_displayCount));
|
||||
}
|
||||
|
||||
private string FormatGameFrameRate()
|
||||
{
|
||||
string frameRate = Device.Statistics.GetGameFrameRate().ToString("00.00");
|
||||
string frameTime = Device.Statistics.GetGameFrameTime().ToString("00.00");
|
||||
|
||||
return Device.TurboMode
|
||||
? LocaleManager.GetUnformatted(LocaleKeys.FpsTurboStatusBarText)
|
||||
.Format(frameRate, frameTime, Device.TickScalar)
|
||||
: LocaleManager.GetUnformatted(LocaleKeys.FpsStatusBarText)
|
||||
.Format(frameRate, frameTime);
|
||||
}
|
||||
|
||||
public async Task ShowExitPrompt()
|
||||
{
|
||||
bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit;
|
||||
@@ -1175,6 +1158,9 @@ namespace Ryujinx.Ava
|
||||
|
||||
private void UpdateShaderCount()
|
||||
{
|
||||
if (_displayCount is 0 && _renderer.ProgramCount is 0)
|
||||
return;
|
||||
|
||||
// If there is a mismatch between total program compile and previous count
|
||||
// this means new shaders have been compiled and should be displayed.
|
||||
if (_renderer.ProgramCount != _previousCount)
|
||||
@@ -1242,18 +1228,28 @@ namespace Ryujinx.Ava
|
||||
|
||||
if (currentHotkeyState != _prevHotkeyState)
|
||||
{
|
||||
if (ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld &&
|
||||
_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode) != Device.TurboMode)
|
||||
{
|
||||
Device.ToggleTurbo();
|
||||
}
|
||||
|
||||
switch (currentHotkeyState)
|
||||
{
|
||||
case KeyboardHotkeyState.ToggleVSyncMode:
|
||||
VSyncModeToggle();
|
||||
break;
|
||||
case KeyboardHotkeyState.CustomVSyncIntervalDecrement:
|
||||
Device.DecrementCustomVSyncInterval();
|
||||
_viewModel.CustomVSyncInterval -= 1;
|
||||
_viewModel.CustomVSyncInterval = Device.DecrementCustomVSyncInterval();
|
||||
break;
|
||||
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
|
||||
Device.IncrementCustomVSyncInterval();
|
||||
_viewModel.CustomVSyncInterval += 1;
|
||||
_viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval();
|
||||
break;
|
||||
case KeyboardHotkeyState.TurboMode:
|
||||
if (!ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld)
|
||||
{
|
||||
Device.ToggleTurbo();
|
||||
}
|
||||
break;
|
||||
case KeyboardHotkeyState.Screenshot:
|
||||
ScreenshotRequested = true;
|
||||
@@ -1384,6 +1380,10 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode))
|
||||
{
|
||||
state = KeyboardHotkeyState.TurboMode;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<Styles
|
||||
<Styles
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia">
|
||||
<Design.PreviewWith>
|
||||
<Border Height="2000"
|
||||
@@ -30,7 +31,8 @@
|
||||
<Button
|
||||
Name="btnRem"
|
||||
HorizontalAlignment="Right"
|
||||
Content="Add" />
|
||||
Content="Add"
|
||||
Classes="red"/>
|
||||
<TextBox
|
||||
Width="100"
|
||||
VerticalAlignment="Center"
|
||||
@@ -41,7 +43,13 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ui:NumberBox Value="1" />
|
||||
<MenuItem
|
||||
Header="123 0000"
|
||||
ToolTip.Tip="What this"/>
|
||||
<TextBlock
|
||||
Classes="globalConfigMarker"/>
|
||||
</StackPanel>
|
||||
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
<Style Selector="DropDownButton">
|
||||
@@ -331,6 +339,14 @@
|
||||
<Setter Property="Margin"
|
||||
Value="0,5,0,0" />
|
||||
</Style>
|
||||
<Style Selector="TextBlock.globalConfigMarker" >
|
||||
<Setter Property="Foreground" Value="SeaGreen"/>
|
||||
<Setter Property="Margin" Value="5,0,0,0"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Text" Value="{ext:Locale GameSpecificConfigurationGlobal}"/>
|
||||
</Style>
|
||||
<Style Selector="StackPanel.globalConfigMarker">
|
||||
</Style>
|
||||
<Style Selector="ContextMenu">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" />
|
||||
@@ -373,6 +389,19 @@
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="Button.red /template/ ContentPresenter">
|
||||
<Setter Property="CornerRadius" Value="4"/>
|
||||
<Setter Property="Background" Value="red"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Style>
|
||||
<Style Selector="Button.red:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="CornerRadius" Value="4"/>
|
||||
<Setter Property="Background" Value="{DynamicResource WarningBackgroundColor}" />
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Style>
|
||||
|
||||
|
||||
|
||||
<Styles.Resources>
|
||||
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
||||
Color="{DynamicResource SystemAccentColor}" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
@@ -12,11 +12,13 @@
|
||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
<Color x:Key="Warning">#800080</Color>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
@@ -29,11 +31,13 @@
|
||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#13c3a4</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
<Color x:Key="Warning">#800080</Color>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
@@ -46,11 +50,13 @@
|
||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
|
||||
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
|
||||
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
<Color x:Key="Warning">#FFA500</Color>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -464,7 +464,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Abrir Pasta de Capturas de Tela",
|
||||
"ru_RU": "Открыть папку со скриншотами",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Öppna skärmbildsmappen",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Відкрити теку скріншотів",
|
||||
@@ -1564,7 +1564,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Desenvolvido por {0}",
|
||||
"ru_RU": "Разработана {0}",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Utvecklat av {0}",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Розроблено: {0}",
|
||||
@@ -1864,7 +1864,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Compatibilidade:",
|
||||
"ru_RU": "Совместимость:",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Kompatibilitet:",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Сумісність:",
|
||||
@@ -1889,7 +1889,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "ID do Título:",
|
||||
"ru_RU": "ID приложения",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Titel-id:",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "ID гри:",
|
||||
@@ -1914,7 +1914,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Jogos Hospedados: {0}",
|
||||
"ru_RU": "Запущенно игр: {0}",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Värdskap för spel: {0}",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Розміщені ігри: {0}",
|
||||
@@ -1939,7 +1939,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Jogadores Online: {0}",
|
||||
"ru_RU": "Игроков онлайн: {0}",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Online-spelare: {0}",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Гравців онлайн: {0}",
|
||||
@@ -2289,7 +2289,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Limpar Cache PPTC",
|
||||
"ru_RU": "Очистить кэш PPTC",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Rensa PPTC-cache",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Очистити кеш PPTC",
|
||||
@@ -2314,7 +2314,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Apaga os arquivos de cache PPTC do aplicativo",
|
||||
"ru_RU": "Удаляет все файлы кэша PPTC для приложения",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Tar bort alla PPTC-cachefiler för applikationen",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Видаляє всі файли кешу PPTC для застосунку",
|
||||
@@ -2747,6 +2747,56 @@
|
||||
"zh_TW": "建立桌面捷徑,啟動選取的應用程式"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListContextMenuCreateCustomConfiguration",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Create Custom Configuration",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Создать пользовательскую конфигурацию",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListContextMenuEditCustomConfiguration",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Edit Custom Configuration",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Изменить пользовательскую конфигурацию",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListContextMenuCreateShortcutToolTipMacOS",
|
||||
"Translations": {
|
||||
@@ -2772,6 +2822,56 @@
|
||||
"zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CreateCustomConfigurationToolTip",
|
||||
"Translations": {
|
||||
"ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية",
|
||||
"de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel",
|
||||
"el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι",
|
||||
"en_US": "Creates an independent configuration for the selected game",
|
||||
"es_ES": "Crea una configuración independiente para el juego actual",
|
||||
"fr_FR": "Crée une configuration indépendante pour le jeu en cours",
|
||||
"he_IL": "יוצר תצורה עצמאית למשחק הנוכחי",
|
||||
"it_IT": "Crea una configurazione indipendente per il gioco attuale",
|
||||
"ja_JP": "現在のゲーム用の独立した設定を作成します",
|
||||
"ko_KR": "현재 게임에 대한 독립적인 설정을 생성합니다",
|
||||
"no_NO": "Oppretter en uavhengig konfigurasjon for det gjeldende spillet",
|
||||
"pl_PL": "Tworzy niezależną konfigurację dla bieżącej gry",
|
||||
"pt_BR": "Cria uma configuração independente para o jogo atual",
|
||||
"ru_RU": "Создает независимую конфигурацию для текущей игры",
|
||||
"sv_SE": "Skapar en oberoende konfiguration för det aktuella spelet",
|
||||
"th_TH": "สร้างการกำหนดค่าที่เป็นอิสระสำหรับเกมปัจจุบัน",
|
||||
"tr_TR": "Mevcut oyun için bağımsız bir yapılandırma oluşturur",
|
||||
"uk_UA": "Створює незалежну конфігурацію для поточної гри",
|
||||
"zh_CN": "为当前游戏创建独立的配置",
|
||||
"zh_TW": "為當前遊戲創建獨立的配置"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "EditCustomConfigurationToolTip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Edit your existing independent configuration for the selected game",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Отредактировать существующую независимую конфигурацию для выбранной игры.",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListContextMenuShowCompatEntry",
|
||||
"Translations": {
|
||||
@@ -2789,7 +2889,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Mostrar Dados de Compatibilidade",
|
||||
"ru_RU": "Показать записи о совместимости",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Visa kompatibilitetspost",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Iнформація про сумісність",
|
||||
@@ -2814,7 +2914,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.",
|
||||
"ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Visa valt spel i kompatibilitetslistan som du normalt sett kan komma åt via hjälpmenyn.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Показати цю гру в Списку Сумісності. Список сумісності також можна зайти в меню Довідки.",
|
||||
@@ -2839,7 +2939,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Mostrar Informações do Jogo",
|
||||
"ru_RU": "Показать информацию об игре",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Visa spelinformation",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Інформація про гру",
|
||||
@@ -2864,7 +2964,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.",
|
||||
"ru_RU": "Показывать статистику и подробную информацию о выбранной игре.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Visa statistik och detaljer om det aktuella spelet.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Показати статистику та деталі обраної гри.",
|
||||
@@ -3297,6 +3397,31 @@
|
||||
"zh_TW": "設定"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsWithInfo",
|
||||
"Translations": {
|
||||
"ar_SA": "{0} - إعدادات",
|
||||
"de_DE": "Einstellungen - {0}",
|
||||
"el_GR": "Ρυθμίσεις - {0}",
|
||||
"en_US": "Settings - {0}",
|
||||
"es_ES": "Configuración - {0}",
|
||||
"fr_FR": "Paramètres - {0}",
|
||||
"he_IL": "{0} - הגדרות",
|
||||
"it_IT": "Impostazioni - {0}",
|
||||
"ja_JP": "設定 - {0}",
|
||||
"ko_KR": "설정 - {0}",
|
||||
"no_NO": "Innstillinger - {0}",
|
||||
"pl_PL": "Ustawienia - {0}",
|
||||
"pt_BR": "Configurações - {0}",
|
||||
"ru_RU": "Параметры - {0}",
|
||||
"sv_SE": "Inställningar - {0}",
|
||||
"th_TH": "ตั้งค่า - {0}",
|
||||
"tr_TR": "Ayarlar - {0}",
|
||||
"uk_UA": "Налаштування - {0}",
|
||||
"zh_CN": "设置 - {0}",
|
||||
"zh_TW": "設定 - {0}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabGeneral",
|
||||
"Translations": {
|
||||
@@ -3389,7 +3514,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Verificar Atualizações:",
|
||||
"ru_RU": "Проверка наличия обновлений",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Leta efter uppdateringar:",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Перевірка оновлень:",
|
||||
@@ -3414,7 +3539,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Desligado",
|
||||
"ru_RU": "Отключить",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Av",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вимкнути",
|
||||
@@ -3439,7 +3564,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Ao Abrir",
|
||||
"ru_RU": "При запуске",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Fråga",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Запитувати щоразу",
|
||||
@@ -3464,7 +3589,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "2° Plano",
|
||||
"ru_RU": "В фоне",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Bakgrund",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Оновлювати в фоні",
|
||||
@@ -3489,7 +3614,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Ao Perder o Foco:",
|
||||
"ru_RU": "При выходе эмулятора из фокуса",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "När emulatorn tappar fokus:",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "При втраті фокуса емулятором:",
|
||||
@@ -3514,7 +3639,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Não Fazer Nada",
|
||||
"ru_RU": "Ничего не делать",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Gör ingenting",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Нічого не робити",
|
||||
@@ -3539,7 +3664,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Bloquear Controles",
|
||||
"ru_RU": "Блокировать управление",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Blockera inmatning",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Блокувати введення",
|
||||
@@ -3564,7 +3689,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Ficar Mudo",
|
||||
"ru_RU": "Отключить звук",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Stäng av ljudet",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вимкнути звук",
|
||||
@@ -3589,7 +3714,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Bloquear Controles & Ficar Mudo",
|
||||
"ru_RU": "Блокировать управление и отключить звук",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Blockera inmatningar och stäng av ljudet",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Блокувати введення та Вимкнути звук",
|
||||
@@ -3614,7 +3739,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Pausar a Emulação",
|
||||
"ru_RU": "Поставить паузу",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Pausa emuleringen",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Поставити на паузу",
|
||||
@@ -3689,7 +3814,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Desativar Controles Quando Estiver Fora de Foco",
|
||||
"ru_RU": "Отключает управление при выходе из фокуса",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Inaktivera inmatning när fokus tappas",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
@@ -4714,7 +4839,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Sincronizar com o Sistema PC",
|
||||
"ru_RU": "Соответствовать времени в системе",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Matcha systemtid",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
@@ -4772,6 +4897,81 @@
|
||||
"zh_TW": "低功耗 PPTC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemTurboMultiplier",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Turbo Mode multiplier:",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Multiplicateur du Mode Turbo :",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemTurboMultiplierValueToolTip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "The Turbo mode multiplier target value.\n\nLeave at 200 if unsure.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "La valeur souhaitée du multiplicateur du Mode Turbo.\n\nGarder à 200 si incertain.",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemTurboMultiplierToolTip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Turbo mode is an emulator feature which effectively causes speed up or slow down when a game is not frame-rate sensitive.\nYou can toggle this feature in-game with a hotkey, configurable in Ryujinx Keyboard Hotkeys settings.\n\nLeave at 200 if unsure.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Le Mode Turbo est une fonctionnalité de l'émulateur qui accélère ou ralentit le jeu lorsque ce dernier n'est pas sensible au framerate.\nVous pouvez changer cette option en jeu avec un raccourci clavier, configurable dans les paramètres de Raccourcis clavier de Ryujinx.\n\nGarder à 200 si incertain.",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemEnableFsIntegrityChecks",
|
||||
"Translations": {
|
||||
@@ -4888,7 +5088,7 @@
|
||||
"no_NO": "Lyd Inn/Ut",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"ru_RU": "Выход/Вход звука",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
@@ -5139,7 +5339,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Ignorar Applet do Controlador",
|
||||
"ru_RU": "Игнорировать апплет контроллера",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Ignorera kontroller-applet",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Ігнорувати Аплет Контролера",
|
||||
@@ -5989,7 +6189,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Habilitar Logs da IU",
|
||||
"ru_RU": "Включить журнал интерфейса",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Aktivera gränssnittsloggar",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Увімкнути журнали інтерфейсу",
|
||||
@@ -6389,7 +6589,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Redefinir Configurações",
|
||||
"ru_RU": "Сбросить настройки",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Nollställ inställningar",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Скинути налаштування",
|
||||
@@ -6414,7 +6614,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Quero redefinir minhas configurações.",
|
||||
"ru_RU": "Я хочу сбросить свои настройки.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Jag vill nollställa mina inställningar.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Я хочу скинути налаштування.",
|
||||
@@ -6438,7 +6638,7 @@
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"ru_RU": "Ок",
|
||||
"sv_SE": "Ok",
|
||||
"th_TH": "ตกลง",
|
||||
"tr_TR": "Tamam",
|
||||
@@ -6888,7 +7088,7 @@
|
||||
"no_NO": "",
|
||||
"pl_PL": "Pro Kontroler",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"ru_RU": "Pro контроллер",
|
||||
"sv_SE": "",
|
||||
"th_TH": "โปรคอนโทรลเลอร์",
|
||||
"tr_TR": "Profesyonel Kumanda",
|
||||
@@ -8389,7 +8589,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Velocidade do Arco-íris",
|
||||
"ru_RU": "Скорость переливания",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Regnbågshastighet",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
@@ -10355,7 +10555,7 @@
|
||||
"el_GR": "",
|
||||
"en_US": "Unbound",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Pas Attribuée",
|
||||
"fr_FR": "Non Attribuée",
|
||||
"he_IL": "",
|
||||
"it_IT": "Non assegnato",
|
||||
"ja_JP": "",
|
||||
@@ -12647,6 +12847,31 @@
|
||||
"zh_TW": "正在下載更新..."
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "DialogRebooterMessage",
|
||||
"Translations": {
|
||||
"ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل",
|
||||
"de_DE": "Bitte warten Sie, der Emulator wird neu gestartet",
|
||||
"el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται",
|
||||
"en_US": "Please wait, the emulator is restarting",
|
||||
"es_ES": "Por favor, espere, el emulador se está reiniciando",
|
||||
"fr_FR": "Veuillez patienter, l'émulateur est en train de redémarrer",
|
||||
"he_IL": "אנא המתן, המחקה מתארגן מחדש",
|
||||
"it_IT": "Attendere prego, l'emulatore si sta riavviando",
|
||||
"ja_JP": "お待ちください、エミュレーターが再起動しています",
|
||||
"ko_KR": "잠시만 기다려 주세요, 에뮬레이터가 재시작 중입니다",
|
||||
"no_NO": "Vennligst vent, emulatoren starter på nytt",
|
||||
"pl_PL": "Proszę czekać, emulator jest w trakcie ponownego uruchamiania",
|
||||
"pt_BR": "Por favor, aguarde, o emulador está reiniciando",
|
||||
"ru_RU": "Пожалуйста, подождите, эмулятор перезапускается",
|
||||
"sv_SE": "Vänligen vänta, emulatorn startar om",
|
||||
"th_TH": "กรุณารอสักครู่, ตัวจำลองกำลังเริ่มใหม่",
|
||||
"tr_TR": "Lütfen bekleyin, emülatör yeniden başlatılıyor",
|
||||
"uk_UA": "Будь ласка, зачекайте, емулятор перезавантажується",
|
||||
"zh_CN": "请稍等,模拟器正在重新启动",
|
||||
"zh_TW": "請稍候,模擬器正在重新啟動"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "DialogUpdaterExtractionMessage",
|
||||
"Translations": {
|
||||
@@ -13664,7 +13889,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?",
|
||||
"ru_RU": "Вы собираетесь удалить все данные PPTC из:\n\n{0}\n\nВы уверены, что хотите продолжить?",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Du är på väg att ta bort allt PPTC-data från:\n\n{0}\n\nÄr du säker på att du vill fortsätta?",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Ви збираєтесь видалити всі дані PPTC з:\n\n{0}\n\nБажаєте продовжити цю операцію?",
|
||||
@@ -16514,7 +16739,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.",
|
||||
"ru_RU": "Диалоговое окно апплета контроллера не будет отображаться, если геймпад отключен во время работы приложения.\n\nОставьте выключенным, если не уверены.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Handkontroller-appleten kommer inte att visas om gamepaden är frånkopplad under tiden en applikation körs.\n\nLämna AV om du är osäker.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.",
|
||||
@@ -16989,7 +17214,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.",
|
||||
"ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Skriver ut loggmeddelanden från Avalonia (användargränssnittet) i konsollen.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Виводити повідомлення журналу Avalonia (UI) в консоль",
|
||||
@@ -17189,7 +17414,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Abre a pasta de capturas de tela do Ryujinx",
|
||||
"ru_RU": "Открывает папку скриншотов Ryujinx",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Öppna Ryujinx skärmbildsmapp",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx",
|
||||
@@ -17922,6 +18147,56 @@
|
||||
"zh_TW": "更新已停用!"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "FpsStatusBarText",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "{0} FPS ({1}ms)",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "FpsTurboStatusBarText",
|
||||
"Translations": {
|
||||
"ar_SA": "{0} FPS ({1}ms), التوربو %{2}",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "{0} FPS ({1}ms), Turbo ({2}%)",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "UpdaterBackgroundStatusBarButtonText",
|
||||
"Translations": {
|
||||
@@ -17939,7 +18214,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Atualização Disponível!",
|
||||
"ru_RU": "Доступно обновление!",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Uppdatering finns tillgänglig!",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Доступне оновлення!",
|
||||
@@ -19522,6 +19797,31 @@
|
||||
"zh_TW": "{0} 更新程式"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "RyujinxRebooter",
|
||||
"Translations": {
|
||||
"ar_SA": "إعادة تشغيل {0}",
|
||||
"de_DE": "Neustart von {0}",
|
||||
"el_GR": "Επανεκκίνηση {0}",
|
||||
"en_US": "{0} Reboot",
|
||||
"es_ES": "Reinicio de {0}",
|
||||
"fr_FR": "Redémarrage de {0}",
|
||||
"he_IL": "אתחול {0}",
|
||||
"it_IT": "Riavvio di {0}",
|
||||
"ja_JP": "{0} 再起動",
|
||||
"ko_KR": "{0} 재부팅",
|
||||
"no_NO": "Omstart av {0}",
|
||||
"pl_PL": "Ponowne uruchomienie {0}",
|
||||
"pt_BR": "Reinício de {0}",
|
||||
"ru_RU": "{0} Перезагрузка",
|
||||
"sv_SE": "Ominläsning av {0}",
|
||||
"th_TH": "เริ่มต้นใหม่ {0}",
|
||||
"tr_TR": "{0} Yeniden Başlatma",
|
||||
"uk_UA": "Перезавантаження {0}",
|
||||
"zh_CN": "{0} 重启",
|
||||
"zh_TW": "{0} 重新啟動"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeys",
|
||||
"Translations": {
|
||||
@@ -19838,7 +20138,7 @@
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"ru_RU": "Амибо",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
@@ -23622,6 +23922,81 @@
|
||||
"zh_TW": "降低自訂的重新整理頻率"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysTurboMode",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Turbo mode:",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Mode Turbo :",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysTurboModeToolTip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "The Turbo mode hotkey.\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\n\nLeave Unbound if unsure.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Le raccourci clavier Mode Turbo.\nConfigurez le comportement du Mode Turbo dans les paramètres de CPU de Ryujinx.\n\nLaisser Non Attribuée si incertain.",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysOnlyWhilePressed",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Only while pressed",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Seulement quand le raccourci est maintenu",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CompatibilityListLastUpdated",
|
||||
"Translations": {
|
||||
@@ -23889,7 +24264,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.",
|
||||
"ru_RU": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Startar upp och spelas utan några krascher eller GPU-fel av några slag och med en hastighet som är snabb nog för bra upplevelse på en genomsnittlig PC.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Запускається та оптимально працює (без збоїв або графічних багів) на середньостатистичному комп'ютері.",
|
||||
@@ -23914,7 +24289,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.",
|
||||
"ru_RU": "Запускается и работает, но возникает одна или несколько из следующих проблем: сбои, взаимоблокировки, ошибки GPU, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Startar och går in i spelet men lider av ett eller flera av följande: kraschar, deadlocks, GPU-buggar, distraherande dåligt ljud eller är helt enkelt för långsamt. Spelet kan fortfarande spelas hela vägen igenom, men inte så som spelet är avsett att spelas.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Запускається, але в грі на вас чекатимуть одна або декілька наступних проблем: збої, зависання, графічні баги, спотворений звук або ж гра загалом працюватиме надто повільно. Можливо, її все ще можна пройти, але досвід буде не найкращим.",
|
||||
@@ -23939,7 +24314,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.",
|
||||
"ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Startar upp och går förbi titelskärmen men tar sig inte in i huvudspelet.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Запускається та проходить початковий екран, але пограти не вийде.",
|
||||
@@ -23964,7 +24339,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Inizializa, mas não passa da tela de título.",
|
||||
"ru_RU": "Загружается, но не проходит дальше титульного экрана.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Startar upp men tar sig inte förbi titelskärmen.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Запускається, але не відображає навіть початкового екрану.",
|
||||
@@ -23989,7 +24364,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Não inicializa ou não mostra sinais de atividade.",
|
||||
"ru_RU": "Не запускается или не подаёт признаков жизни.",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Startar inte upp eller visar någon form av aktivitet.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Взагалі не запускається.",
|
||||
@@ -23997,6 +24372,56 @@
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameSpecificConfigurationHeader",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Custom Config",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameSpecificConfigurationGlobal",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "(Global)",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "ExtractAocListHeader",
|
||||
"Translations": {
|
||||
@@ -24039,7 +24464,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Imagem da Presença do Discord",
|
||||
"ru_RU": "Изображение для статуса активности",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Bild för Rich Presence",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Зображення картки активності Discord",
|
||||
@@ -24064,7 +24489,7 @@
|
||||
"pl_PL": "",
|
||||
"pt_BR": "Presença Dinâmica do Discord",
|
||||
"ru_RU": "Динамический статус активности",
|
||||
"sv_SE": "",
|
||||
"sv_SE": "Dynamisk Rich Presence",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Динамічна картка активності Discord",
|
||||
|
||||
@@ -13,7 +13,7 @@ using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
|
||||
@@ -14,5 +14,6 @@ namespace Ryujinx.Ava.Common
|
||||
VolumeDown,
|
||||
CustomVSyncIntervalIncrement,
|
||||
CustomVSyncIntervalDecrement,
|
||||
TurboMode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,16 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName);
|
||||
}
|
||||
|
||||
public static string GetUnformatted(LocaleKeys key) => Instance.Get(key);
|
||||
|
||||
public string Get(LocaleKeys key) =>
|
||||
_localeStrings.TryGetValue(key, out string value)
|
||||
? value
|
||||
: key.ToString();
|
||||
|
||||
public string this[LocaleKeys key]
|
||||
{
|
||||
get
|
||||
|
||||
@@ -10,6 +10,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Multithreading;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
@@ -311,50 +312,44 @@ namespace Ryujinx.Headless
|
||||
|
||||
return new OpenGLRenderer();
|
||||
}
|
||||
|
||||
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options)
|
||||
{
|
||||
BackendThreading threadingMode = options.BackendThreading;
|
||||
|
||||
bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
|
||||
|
||||
if (threadedGAL)
|
||||
{
|
||||
renderer = new ThreadedRenderer(renderer);
|
||||
}
|
||||
|
||||
HLEConfiguration configuration = new(_virtualFileSystem,
|
||||
_libHacHorizonManager,
|
||||
_contentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
options.DramSize,
|
||||
window,
|
||||
options.SystemLanguage,
|
||||
options.SystemRegion,
|
||||
options.VSyncMode,
|
||||
!options.DisableDockedMode,
|
||||
!options.DisablePTC,
|
||||
options.EnableInternetAccess,
|
||||
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
options.FsGlobalAccessLogMode,
|
||||
options.SystemTimeOffset,
|
||||
options.SystemTimeZone,
|
||||
options.MemoryManagerMode,
|
||||
options.IgnoreMissingServices,
|
||||
options.AspectRatio,
|
||||
options.AudioVolume,
|
||||
options.UseHypervisor ?? true,
|
||||
options.MultiplayerLanInterfaceId,
|
||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
||||
false,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
options.CustomVSyncInterval);
|
||||
|
||||
return new Switch(configuration);
|
||||
}
|
||||
|
||||
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) =>
|
||||
new(
|
||||
new HleConfiguration(
|
||||
options.DramSize,
|
||||
options.SystemLanguage,
|
||||
options.SystemRegion,
|
||||
options.VSyncMode,
|
||||
!options.DisableDockedMode,
|
||||
!options.DisablePTC,
|
||||
ITickSource.RealityTickScalar,
|
||||
options.EnableInternetAccess,
|
||||
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
options.FsGlobalAccessLogMode,
|
||||
options.SystemTimeOffset,
|
||||
options.SystemTimeZone,
|
||||
options.MemoryManagerMode,
|
||||
options.IgnoreMissingServices,
|
||||
options.AspectRatio,
|
||||
options.AudioVolume,
|
||||
options.UseHypervisor ?? true,
|
||||
options.MultiplayerLanInterfaceId,
|
||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
||||
false,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
options.CustomVSyncInterval
|
||||
)
|
||||
.Configure(
|
||||
_virtualFileSystem,
|
||||
_libHacHorizonManager,
|
||||
_contentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer.TryMakeThreaded(options.BackendThreading),
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
window
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,10 @@ namespace Ryujinx.Ava
|
||||
public static double DesktopScaleFactor { get; set; } = 1.0;
|
||||
public static string Version { get; private set; }
|
||||
public static string ConfigurationPath { get; private set; }
|
||||
public static string GlobalConfigurationPath { get; private set; }
|
||||
public static bool PreviewerDetached { get; private set; }
|
||||
public static bool UseHardwareAcceleration { get; private set; }
|
||||
public static string BackendThreadingArg { get; private set; }
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
|
||||
@@ -156,11 +158,38 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(gameId))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
string gameDir = Path.Combine(AppDataManager.GamesDirPath, gameId, ReleaseInformation.ConfigName);
|
||||
|
||||
// Should load with the game if there is a custom setting for the game
|
||||
if (rememberGlobalDir)
|
||||
{
|
||||
GlobalConfigurationPath = ConfigurationPath;
|
||||
}
|
||||
|
||||
if (changeFolderForGame)
|
||||
{
|
||||
ConfigurationPath = gameDir;
|
||||
}
|
||||
|
||||
return gameDir;
|
||||
}
|
||||
|
||||
public static void ReloadConfig()
|
||||
{
|
||||
//It is necessary that when a user setting appears, the global setting remains available
|
||||
GlobalConfigurationPath = null;
|
||||
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||
|
||||
|
||||
// Now load the configuration as the other subsystems are now registered
|
||||
if (File.Exists(localConfigurationPath))
|
||||
{
|
||||
@@ -217,6 +246,11 @@ namespace Ryujinx.Ava
|
||||
_ => ConfigurationState.Instance.Graphics.BackendThreading
|
||||
};
|
||||
|
||||
if (CommandLineState.OverrideBackendThreadingAfterReboot is not null)
|
||||
{
|
||||
BackendThreadingArg = CommandLineState.OverrideBackendThreadingAfterReboot;
|
||||
}
|
||||
|
||||
// Check if docked mode was overriden.
|
||||
if (CommandLineState.OverrideDockedMode.HasValue)
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
|
||||
@@ -232,6 +266,33 @@ namespace Ryujinx.Ava
|
||||
_ => ConfigurationState.Instance.HideCursor,
|
||||
};
|
||||
|
||||
// Check if memoryManagerMode was overridden.
|
||||
if (CommandLineState.OverrideMemoryManagerMode is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
|
||||
{
|
||||
ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
|
||||
}
|
||||
|
||||
// Check if PPTC was overridden.
|
||||
if (CommandLineState.OverridePPTC is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
|
||||
{
|
||||
ConfigurationState.Instance.System.EnablePtc.Value = result;
|
||||
}
|
||||
|
||||
// Check if region was overridden.
|
||||
if (CommandLineState.OverrideSystemRegion is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result))
|
||||
{
|
||||
ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result;
|
||||
}
|
||||
|
||||
//Check if language was overridden.
|
||||
if (CommandLineState.OverrideSystemLanguage is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result))
|
||||
{
|
||||
ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result;
|
||||
}
|
||||
|
||||
// Check if hardware-acceleration was overridden.
|
||||
if (CommandLineState.OverrideHardwareAcceleration != null)
|
||||
|
||||
76
src/Ryujinx/Rebooter.cs
Normal file
76
src/Ryujinx/Rebooter.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
{
|
||||
internal static class Rebooter
|
||||
{
|
||||
|
||||
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||
|
||||
|
||||
public static void RebootAppWithGame(string gamePath, List<string> args)
|
||||
{
|
||||
_ = Reboot(gamePath, args);
|
||||
|
||||
}
|
||||
|
||||
private static async Task Reboot(string gamePath, List<string> args)
|
||||
{
|
||||
|
||||
bool shouldRestart = true;
|
||||
|
||||
TaskDialog taskDialog = new()
|
||||
{
|
||||
Header = LocaleManager.Instance[LocaleKeys.RyujinxRebooter],
|
||||
SubHeader = LocaleManager.Instance[LocaleKeys.DialogRebooterMessage],
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Games },
|
||||
XamlRoot = RyujinxApp.MainWindow,
|
||||
};
|
||||
|
||||
if (shouldRestart)
|
||||
{
|
||||
List<string> arguments = CommandLineState.Arguments.ToList();
|
||||
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
var dialogTask = taskDialog.ShowAsync(true);
|
||||
await Task.Delay(500);
|
||||
|
||||
// Find the process name.
|
||||
string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty;
|
||||
|
||||
// Fallback if the executable could not be found.
|
||||
if (ryuName.Length == 0 || !Path.Exists(Path.Combine(executableDirectory, ryuName)))
|
||||
{
|
||||
ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
||||
}
|
||||
|
||||
ProcessStartInfo processStart = new(ryuName)
|
||||
{
|
||||
UseShellExecute = true,
|
||||
WorkingDirectory = executableDirectory,
|
||||
};
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
processStart.ArgumentList.Add(arg);
|
||||
}
|
||||
|
||||
processStart.ArgumentList.Add(gamePath);
|
||||
|
||||
Process.Start(processStart);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,10 @@
|
||||
<AvaloniaResource Include="UI\**\*.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</AvaloniaResource>
|
||||
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-Bold.ttf" />
|
||||
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-BoldItalic.ttf" />
|
||||
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-Italic.ttf" />
|
||||
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-Regular.ttf" />
|
||||
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
|
||||
<AvaloniaResource Include="Assets\Styles\Themes.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
_parent.SettingsWindow =
|
||||
new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager);
|
||||
|
||||
await _parent.SettingsWindow.ShowDialog(window);
|
||||
await StyleableAppWindow.ShowAsync(_parent.SettingsWindow, window);
|
||||
|
||||
_parent.SettingsWindow = null;
|
||||
|
||||
|
||||
@@ -17,12 +17,8 @@
|
||||
<viewModels:ProfileSelectorDialogViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
|
||||
|
||||
<Border
|
||||
CornerRadius="5"
|
||||
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
|
||||
|
||||
@@ -9,17 +9,14 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
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 ProfileSelectorDialog : UserControl
|
||||
public partial class ProfileSelectorDialog : RyujinxControl<ProfileSelectorDialogViewModel>
|
||||
{
|
||||
public ProfileSelectorDialogViewModel ViewModel { get; set; }
|
||||
|
||||
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
|
||||
{
|
||||
DataContext = ViewModel = viewModel;
|
||||
|
||||
@@ -19,6 +19,18 @@
|
||||
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||
Icon="{ext:Icon fa-solid fa-bookmark}"
|
||||
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
||||
<MenuItem
|
||||
Click="EditGameConfiguration_Click"
|
||||
IsVisible="{Binding SelectedApplication.HasIndependentConfiguration}"
|
||||
Header="{ext:Locale GameListContextMenuEditCustomConfiguration}"
|
||||
Icon="{ext:Icon fa-solid fa-gear}"
|
||||
ToolTip.Tip="{ext:Locale EditCustomConfigurationToolTip}" />
|
||||
<MenuItem
|
||||
Click="EditGameConfiguration_Click"
|
||||
IsVisible="{Binding !SelectedApplication.HasIndependentConfiguration}"
|
||||
Header="{ext:Locale GameListContextMenuCreateCustomConfiguration}"
|
||||
Icon="{ext:Icon fa-solid fa-gear}"
|
||||
ToolTip.Tip="{ext:Locale CreateCustomConfigurationToolTip}" />
|
||||
<MenuItem
|
||||
IsVisible="{Binding HasCompatibilityEntry}"
|
||||
Click="OpenApplicationCompatibility_Click"
|
||||
|
||||
@@ -9,6 +9,7 @@ using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Views.Misc;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
@@ -26,6 +27,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public class ApplicationContextMenu : MenuFlyout
|
||||
{
|
||||
|
||||
public ApplicationContextMenu()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -91,11 +93,14 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
public async void OpenCheatManager_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await new CheatWindow(
|
||||
viewModel.VirtualFileSystem,
|
||||
viewModel.SelectedApplication.IdString,
|
||||
viewModel.SelectedApplication.Name,
|
||||
viewModel.SelectedApplication.Path).ShowDialog((Window)viewModel.TopLevel);
|
||||
await StyleableAppWindow.ShowAsync(
|
||||
new CheatWindow(
|
||||
viewModel.VirtualFileSystem,
|
||||
viewModel.SelectedApplication.IdString,
|
||||
viewModel.SelectedApplication.Name,
|
||||
viewModel.SelectedApplication.Path
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void OpenModsDirectory_Click(object sender, RoutedEventArgs args)
|
||||
@@ -200,7 +205,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
if (backupDir.Exists)
|
||||
{
|
||||
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
|
||||
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
|
||||
cacheFiles.AddRange(backupDir.EnumerateFiles("*.info"));
|
||||
}
|
||||
|
||||
if (cacheFiles.Count > 0)
|
||||
@@ -386,13 +391,26 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
viewModel.SelectedApplication.Icon
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public async void EditGameConfiguration_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
{
|
||||
await StyleableAppWindow.ShowAsync(new GameSpecificSettingsWindow(viewModel));
|
||||
|
||||
// just checking for file presence
|
||||
viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString,false,false));
|
||||
|
||||
viewModel.RefreshView();
|
||||
}
|
||||
}
|
||||
|
||||
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 })
|
||||
|
||||
@@ -23,13 +23,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class NavigationDialogHost : UserControl
|
||||
public partial class NavigationDialogHost : RyujinxControl<UserProfileViewModel>
|
||||
{
|
||||
public AccountManager AccountManager { get; }
|
||||
public ContentManager ContentManager { get; }
|
||||
public VirtualFileSystem VirtualFileSystem { get; }
|
||||
public HorizonClient HorizonClient { get; }
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
public NavigationDialogHost()
|
||||
{
|
||||
|
||||
24
src/Ryujinx/UI/Controls/RyujinxControl.cs
Normal file
24
src/Ryujinx/UI/Controls/RyujinxControl.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Avalonia.Controls;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public class RyujinxControl<TViewModel> : UserControl where TViewModel : BaseModel
|
||||
{
|
||||
public TViewModel ViewModel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DataContext is not TViewModel viewModel)
|
||||
throw new InvalidOperationException(
|
||||
$"Underlying DataContext is not of type {typeof(TViewModel).AsPrettyString()}; " +
|
||||
$"Actual type is {DataContext?.GetType().AsPrettyString()}");
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
set => DataContext = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
@@ -20,6 +22,23 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
private static bool _isChoiceDialogOpen;
|
||||
private static ContentDialogOverlayWindow _contentDialogOverlayWindow;
|
||||
|
||||
public static ContentDialog ApplyStyles(
|
||||
this ContentDialog contentDialog,
|
||||
double closeButtonWidth = 80,
|
||||
HorizontalAlignment buttonSpaceAlignment = HorizontalAlignment.Right)
|
||||
{
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
closeButton.Setters.Add(new Setter(Layoutable.WidthProperty, closeButtonWidth));
|
||||
|
||||
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||
closeButtonParent.Setters.Add(new Setter(Layoutable.HorizontalAlignmentProperty, buttonSpaceAlignment));
|
||||
|
||||
contentDialog.Styles.Add(closeButton);
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
return contentDialog;
|
||||
}
|
||||
|
||||
private async static Task<UserResult> ShowContentDialog(
|
||||
string title,
|
||||
object content,
|
||||
@@ -39,19 +58,19 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
SecondaryButtonText = secondaryButton,
|
||||
CloseButtonText = closeButton,
|
||||
Content = content,
|
||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
PrimaryButtonCommand = Commands.Create(() =>
|
||||
{
|
||||
result = primaryButtonResult;
|
||||
})
|
||||
};
|
||||
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
contentDialog.SecondaryButtonCommand = Commands.Create(() =>
|
||||
{
|
||||
result = UserResult.No;
|
||||
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
||||
});
|
||||
|
||||
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
||||
contentDialog.CloseButtonCommand = Commands.Create(() =>
|
||||
{
|
||||
result = UserResult.Cancel;
|
||||
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
||||
@@ -384,6 +403,10 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
Position = parent.PointToScreen(new Point()),
|
||||
ShowInTaskbar = false,
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
_contentDialogOverlayWindow.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control));
|
||||
#endif
|
||||
|
||||
parent.PositionChanged += OverlayOnPositionChanged;
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public sealed class MiniCommand<T> : MiniCommand, ICommand
|
||||
{
|
||||
private readonly Action<T> _callback;
|
||||
private bool _busy;
|
||||
private readonly Func<T, Task> _asyncCallback;
|
||||
|
||||
public MiniCommand(Action<T> callback)
|
||||
{
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
public MiniCommand(Func<T, Task> callback)
|
||||
{
|
||||
_asyncCallback = callback;
|
||||
}
|
||||
|
||||
private bool Busy
|
||||
{
|
||||
get => _busy;
|
||||
set
|
||||
{
|
||||
_busy = value;
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public override event EventHandler CanExecuteChanged;
|
||||
public override bool CanExecute(object parameter) => !_busy;
|
||||
|
||||
public override async void Execute(object parameter)
|
||||
{
|
||||
if (Busy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
Busy = true;
|
||||
if (_callback != null)
|
||||
{
|
||||
_callback((T)parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _asyncCallback((T)parameter);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class MiniCommand : ICommand
|
||||
{
|
||||
public static MiniCommand Create(Action callback) => new MiniCommand<object>(_ => callback());
|
||||
public static MiniCommand Create<TArg>(Action<TArg> callback) => new MiniCommand<TArg>(callback);
|
||||
public static MiniCommand CreateFromTask(Func<Task> callback) => new MiniCommand<object>(_ => callback());
|
||||
public static MiniCommand CreateFromTask<TArg>(Func<TArg, Task> callback) => new MiniCommand<TArg>(callback);
|
||||
|
||||
public abstract bool CanExecute(object parameter);
|
||||
public abstract void Execute(object parameter);
|
||||
public abstract event EventHandler CanExecuteChanged;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,10 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
|
||||
[ObservableProperty] private Key _customVSyncIntervalDecrement;
|
||||
|
||||
[ObservableProperty] private Key _turboMode;
|
||||
|
||||
[ObservableProperty] private bool _turboModeWhileHeld;
|
||||
|
||||
public HotkeyConfig(KeyboardHotkeys config)
|
||||
{
|
||||
if (config == null)
|
||||
@@ -44,6 +48,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
VolumeDown = config.VolumeDown;
|
||||
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
|
||||
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
|
||||
TurboMode = config.TurboMode;
|
||||
TurboModeWhileHeld = config.TurboModeWhileHeld;
|
||||
}
|
||||
|
||||
public KeyboardHotkeys GetConfig() =>
|
||||
@@ -60,6 +66,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
VolumeDown = VolumeDown,
|
||||
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
|
||||
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
|
||||
TurboMode = TurboMode,
|
||||
TurboModeWhileHeld = TurboModeWhileHeld
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
260
src/Ryujinx/UI/Models/Input/StickVisualizer.cs
Normal file
260
src/Ryujinx/UI/Models/Input/StickVisualizer.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models.Input
|
||||
{
|
||||
public class StickVisualizer : BaseModel, IDisposable
|
||||
{
|
||||
public const int DrawStickPollRate = 50; // Milliseconds per poll.
|
||||
public const int DrawStickCircumference = 5;
|
||||
public const float DrawStickScaleFactor = DrawStickCanvasCenter;
|
||||
public const int DrawStickCanvasSize = 100;
|
||||
public const int DrawStickBorderSize = DrawStickCanvasSize + 5;
|
||||
public const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2;
|
||||
public const float MaxVectorLength = DrawStickCanvasSize / 2;
|
||||
|
||||
public CancellationTokenSource PollTokenSource;
|
||||
public CancellationToken PollToken;
|
||||
|
||||
private static float _vectorLength;
|
||||
private static float _vectorMultiplier;
|
||||
|
||||
private bool disposedValue;
|
||||
|
||||
private DeviceType _type;
|
||||
public DeviceType Type
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
_type = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GamepadInputConfig _gamepadConfig;
|
||||
public GamepadInputConfig GamepadConfig
|
||||
{
|
||||
get => _gamepadConfig;
|
||||
set
|
||||
{
|
||||
_gamepadConfig = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private KeyboardInputConfig _keyboardConfig;
|
||||
public KeyboardInputConfig KeyboardConfig
|
||||
{
|
||||
get => _keyboardConfig;
|
||||
set
|
||||
{
|
||||
_keyboardConfig = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private (float, float) _uiStickLeft;
|
||||
public (float, float) UiStickLeft
|
||||
{
|
||||
get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor);
|
||||
set
|
||||
{
|
||||
_uiStickLeft = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(UiStickRightX));
|
||||
OnPropertyChanged(nameof(UiStickRightY));
|
||||
OnPropertyChanged(nameof(UiDeadzoneRight));
|
||||
}
|
||||
}
|
||||
|
||||
private (float, float) _uiStickRight;
|
||||
public (float, float) UiStickRight
|
||||
{
|
||||
get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor);
|
||||
set
|
||||
{
|
||||
_uiStickRight = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(UiStickLeftX));
|
||||
OnPropertyChanged(nameof(UiStickLeftY));
|
||||
OnPropertyChanged(nameof(UiDeadzoneLeft));
|
||||
}
|
||||
}
|
||||
|
||||
public float UiStickLeftX => ClampVector(UiStickLeft).Item1;
|
||||
public float UiStickLeftY => ClampVector(UiStickLeft).Item2;
|
||||
public float UiStickRightX => ClampVector(UiStickRight).Item1;
|
||||
public float UiStickRightY => ClampVector(UiStickRight).Item2;
|
||||
|
||||
public int UiStickCircumference => DrawStickCircumference;
|
||||
public int UiCanvasSize => DrawStickCanvasSize;
|
||||
public int UiStickBorderSize => DrawStickBorderSize;
|
||||
|
||||
public float? UiDeadzoneLeft => _gamepadConfig?.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference;
|
||||
public float? UiDeadzoneRight => _gamepadConfig?.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference;
|
||||
|
||||
private InputViewModel Parent;
|
||||
|
||||
public StickVisualizer(InputViewModel parent)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
PollTokenSource = new CancellationTokenSource();
|
||||
PollToken = PollTokenSource.Token;
|
||||
|
||||
Task.Run(Initialize, PollToken);
|
||||
}
|
||||
|
||||
public void UpdateConfig(object config)
|
||||
{
|
||||
if (config is ControllerInputViewModel padConfig)
|
||||
{
|
||||
GamepadConfig = padConfig.Config;
|
||||
Type = DeviceType.Controller;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (config is KeyboardInputViewModel keyConfig)
|
||||
{
|
||||
KeyboardConfig = keyConfig.Config;
|
||||
Type = DeviceType.Keyboard;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Type = DeviceType.None;
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
(float, float) leftBuffer;
|
||||
(float, float) rightBuffer;
|
||||
|
||||
while (!PollToken.IsCancellationRequested)
|
||||
{
|
||||
leftBuffer = (0f, 0f);
|
||||
rightBuffer = (0f, 0f);
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case DeviceType.Keyboard:
|
||||
IKeyboard keyboard = (IKeyboard)Parent.AvaloniaKeyboardDriver.GetGamepad("0");
|
||||
|
||||
if (keyboard != null)
|
||||
{
|
||||
KeyboardStateSnapshot snapshot = keyboard.GetKeyboardStateSnapshot();
|
||||
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickRight))
|
||||
{
|
||||
leftBuffer.Item1 += 1;
|
||||
}
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickLeft))
|
||||
{
|
||||
leftBuffer.Item1 -= 1;
|
||||
}
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickUp))
|
||||
{
|
||||
leftBuffer.Item2 += 1;
|
||||
}
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickDown))
|
||||
{
|
||||
leftBuffer.Item2 -= 1;
|
||||
}
|
||||
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickRight))
|
||||
{
|
||||
rightBuffer.Item1 += 1;
|
||||
}
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickLeft))
|
||||
{
|
||||
rightBuffer.Item1 -= 1;
|
||||
}
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickUp))
|
||||
{
|
||||
rightBuffer.Item2 += 1;
|
||||
}
|
||||
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickDown))
|
||||
{
|
||||
rightBuffer.Item2 -= 1;
|
||||
}
|
||||
|
||||
UiStickLeft = leftBuffer;
|
||||
UiStickRight = rightBuffer;
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceType.Controller:
|
||||
IGamepad controller = Parent.SelectedGamepad;
|
||||
|
||||
if (controller != null)
|
||||
{
|
||||
leftBuffer = controller.GetStick((StickInputId)GamepadConfig.LeftJoystick);
|
||||
rightBuffer = controller.GetStick((StickInputId)GamepadConfig.RightJoystick);
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceType.None:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unable to poll device type \"{Type}\"");
|
||||
}
|
||||
|
||||
UiStickLeft = leftBuffer;
|
||||
UiStickRight = rightBuffer;
|
||||
|
||||
await Task.Delay(DrawStickPollRate, PollToken);
|
||||
}
|
||||
|
||||
PollTokenSource.Dispose();
|
||||
}
|
||||
|
||||
public static (float, float) ClampVector((float, float) vect)
|
||||
{
|
||||
_vectorMultiplier = 1;
|
||||
_vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2));
|
||||
|
||||
if (_vectorLength > MaxVectorLength)
|
||||
{
|
||||
_vectorMultiplier = MaxVectorLength / _vectorLength;
|
||||
}
|
||||
|
||||
vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter;
|
||||
vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter;
|
||||
|
||||
return vect;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
PollTokenSource.Cancel();
|
||||
}
|
||||
|
||||
KeyboardConfig = null;
|
||||
GamepadConfig = null;
|
||||
Parent = null;
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,7 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Renderer
|
||||
@@ -38,32 +36,6 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
EmbeddedWindowOpenGL => GraphicsBackend.OpenGl,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
public RendererHost(string titleId)
|
||||
{
|
||||
Focusable = true;
|
||||
FlowDirection = FlowDirection.LeftToRight;
|
||||
|
||||
EmbeddedWindow =
|
||||
#pragma warning disable CS8524
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch
|
||||
#pragma warning restore CS8524
|
||||
{
|
||||
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
|
||||
GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(),
|
||||
};
|
||||
|
||||
string backendText = EmbeddedWindow switch
|
||||
{
|
||||
EmbeddedWindowVulkan => "Vulkan",
|
||||
EmbeddedWindowOpenGL => "OpenGL",
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend ({ConfigurationState.Instance.Graphics.GraphicsBackend.Value}): {backendText}");
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
|
||||
private void Initialize()
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using Avalonia.Svg.Skia;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.Views.Input;
|
||||
using Ryujinx.Common.Utilities;
|
||||
@@ -10,8 +14,30 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public partial class ControllerInputViewModel : BaseModel
|
||||
{
|
||||
[ObservableProperty] private GamepadInputConfig _config;
|
||||
private GamepadInputConfig _config;
|
||||
public GamepadInputConfig Config
|
||||
{
|
||||
get => _config;
|
||||
set
|
||||
{
|
||||
_config = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private StickVisualizer _visualizer;
|
||||
public StickVisualizer Visualizer
|
||||
{
|
||||
get => _visualizer;
|
||||
set
|
||||
{
|
||||
_visualizer = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isLeft;
|
||||
public bool IsLeft
|
||||
{
|
||||
@@ -37,14 +63,15 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
}
|
||||
|
||||
public bool HasSides => IsLeft ^ IsRight;
|
||||
|
||||
|
||||
[ObservableProperty] private SvgImage _image;
|
||||
|
||||
|
||||
public InputViewModel ParentModel { get; }
|
||||
|
||||
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
|
||||
|
||||
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer)
|
||||
{
|
||||
ParentModel = model;
|
||||
Visualizer = visualizer;
|
||||
model.NotifyChangesEvent += OnParentModelChanged;
|
||||
OnParentModelChanged();
|
||||
config.PropertyChanged += (_, args) =>
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
private int _controller;
|
||||
private string _controllerImage;
|
||||
private int _device;
|
||||
[ObservableProperty] private object _configViewModel;
|
||||
private object _configViewModel;
|
||||
[ObservableProperty] private string _profileName;
|
||||
private bool _isLoaded;
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
|
||||
}
|
||||
}
|
||||
public StickVisualizer VisualStick { get; private set; }
|
||||
|
||||
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
|
||||
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
|
||||
@@ -94,6 +95,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
public bool IsModified { get; set; }
|
||||
public event Action NotifyChangesEvent;
|
||||
|
||||
public object ConfigViewModel
|
||||
{
|
||||
get => _configViewModel;
|
||||
set
|
||||
{
|
||||
_configViewModel = value;
|
||||
|
||||
VisualStick.UpdateConfig(value);
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerIndex PlayerIdChoose
|
||||
{
|
||||
get => _playerIdChoose;
|
||||
@@ -269,6 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
Devices = [];
|
||||
ProfilesList = [];
|
||||
DeviceList = [];
|
||||
VisualStick = new StickVisualizer(this);
|
||||
|
||||
ControllerImage = ProControllerResource;
|
||||
|
||||
@@ -289,12 +304,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
if (Config is StandardKeyboardInputConfig keyboardInputConfig)
|
||||
{
|
||||
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig));
|
||||
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick);
|
||||
}
|
||||
|
||||
if (Config is StandardControllerInputConfig controllerInputConfig)
|
||||
{
|
||||
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig));
|
||||
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,6 +908,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
|
||||
|
||||
VisualStick.Dispose();
|
||||
|
||||
SelectedGamepad?.Dispose();
|
||||
|
||||
AvaloniaKeyboardDriver.Dispose();
|
||||
|
||||
@@ -6,7 +6,29 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public partial class KeyboardInputViewModel : BaseModel
|
||||
{
|
||||
[ObservableProperty] private KeyboardInputConfig _config;
|
||||
private KeyboardInputConfig _config;
|
||||
public KeyboardInputConfig Config
|
||||
{
|
||||
get => _config;
|
||||
set
|
||||
{
|
||||
_config = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private StickVisualizer _visualizer;
|
||||
public StickVisualizer Visualizer
|
||||
{
|
||||
get => _visualizer;
|
||||
set
|
||||
{
|
||||
_visualizer = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isLeft;
|
||||
public bool IsLeft
|
||||
@@ -38,9 +60,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
public readonly InputViewModel ParentModel;
|
||||
|
||||
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config)
|
||||
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config, StickVisualizer visualizer)
|
||||
{
|
||||
ParentModel = model;
|
||||
Visualizer = visualizer;
|
||||
model.NotifyChangesEvent += OnParentModelChanged;
|
||||
OnParentModelChanged();
|
||||
Config = config;
|
||||
|
||||
@@ -356,6 +356,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
set
|
||||
{
|
||||
ListSelectedApplication = value;
|
||||
GridSelectedApplication = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo;
|
||||
@@ -1085,7 +1090,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_rendererWaitEvent.WaitOne();
|
||||
|
||||
AppHost?.Start();
|
||||
|
||||
|
||||
AppHost?.DisposeContext();
|
||||
}
|
||||
|
||||
@@ -1551,8 +1556,50 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool InitializeUserConfig(ApplicationData application)
|
||||
{
|
||||
// Code where conditions will be met before loading the user configuration (Global Config)
|
||||
BackendThreading backendThreadingValue = ConfigurationState.Instance.Graphics.BackendThreading.Value;
|
||||
string BackendThreadingInit = Program.BackendThreadingArg;
|
||||
|
||||
if (BackendThreadingInit is null)
|
||||
{
|
||||
BackendThreadingInit = ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
|
||||
}
|
||||
|
||||
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
|
||||
string idGame = application.IdBaseString;
|
||||
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat))
|
||||
{
|
||||
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame);
|
||||
}
|
||||
|
||||
// Code where conditions will be executed after loading user configuration
|
||||
if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit)
|
||||
{
|
||||
|
||||
List<string> Arguments = new List<string>
|
||||
{
|
||||
"--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading
|
||||
};
|
||||
|
||||
Rebooter.RebootAppWithGame(application.Path, Arguments);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
|
||||
if (InitializeUserConfig(application))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppHost != null)
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
@@ -1568,7 +1615,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
#if RELEASE
|
||||
await PerformanceCheck();
|
||||
#endif
|
||||
|
||||
|
||||
Logger.RestartTime();
|
||||
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
|
||||
@@ -1613,6 +1660,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||
gameThread.Start();
|
||||
|
||||
}
|
||||
|
||||
public void SwitchToRenderer(bool startFullscreen) =>
|
||||
@@ -1699,7 +1747,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
string titleId = AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper();
|
||||
AmiiboWindow window = new(ShowAll, LastScannedAmiiboId, titleId);
|
||||
|
||||
await window.ShowDialog(Window);
|
||||
await StyleableAppWindow.ShowAsync(window);
|
||||
|
||||
if (window.IsScanned)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -27,6 +28,7 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
@@ -58,6 +60,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private bool _enableCustomVSyncInterval;
|
||||
private int _customVSyncIntervalPercentageProxy;
|
||||
private VSyncMode _vSyncMode;
|
||||
private long _turboModeMultiplier;
|
||||
|
||||
public event Action CloseWindow;
|
||||
public event Action SaveSettingsEvent;
|
||||
@@ -68,6 +71,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public SettingsHacksViewModel DirtyHacks { get; }
|
||||
|
||||
private readonly bool _isGameRunning;
|
||||
private Bitmap _gameIcon;
|
||||
private string _gameTitle;
|
||||
private string _gamePath;
|
||||
private string _gameId;
|
||||
public bool IsGameRunning => _isGameRunning;
|
||||
public Bitmap GameIcon => _gameIcon;
|
||||
public string GamePath => _gamePath;
|
||||
public string GameTitle => _gameTitle;
|
||||
public string GameId => _gameId;
|
||||
public bool IsGameTitleNotNull => !string.IsNullOrEmpty(GameTitle);
|
||||
public double PanelOpacity => IsGameTitleNotNull ? 0.5 : 1;
|
||||
|
||||
public int ResolutionScale
|
||||
{
|
||||
get => _resolutionScale;
|
||||
@@ -192,6 +208,25 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
public bool EnablePptc { get; set; }
|
||||
public bool EnableLowPowerPptc { get; set; }
|
||||
|
||||
|
||||
public long TurboMultiplier
|
||||
{
|
||||
get => _turboModeMultiplier;
|
||||
set
|
||||
{
|
||||
if (_turboModeMultiplier != value)
|
||||
{
|
||||
_turboModeMultiplier = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged((nameof(TurboMultiplierPercentageText)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string TurboMultiplierPercentageText => $"{TurboMultiplier}%";
|
||||
|
||||
public bool EnableInternetAccess { get; set; }
|
||||
public bool EnableFsIntegrityChecks { get; set; }
|
||||
public bool IgnoreMissingServices { get; set; }
|
||||
@@ -335,7 +370,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false)
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
@@ -348,7 +383,51 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel()
|
||||
public SettingsViewModel(
|
||||
VirtualFileSystem virtualFileSystem,
|
||||
ContentManager contentManager,
|
||||
bool gameRunning,
|
||||
string gamePath,
|
||||
string gameName,
|
||||
string gameId,
|
||||
byte[] gameIconData,
|
||||
bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig)
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
|
||||
if (gameIconData != null && gameIconData.Length > 0)
|
||||
{
|
||||
using (var ms = new MemoryStream(gameIconData))
|
||||
{
|
||||
_gameIcon = new Bitmap(ms);
|
||||
}
|
||||
}
|
||||
|
||||
_isGameRunning = gameRunning;
|
||||
_gamePath = gamePath;
|
||||
_gameTitle = gameName;
|
||||
_gameId = gameId;
|
||||
|
||||
if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window
|
||||
{
|
||||
string gameDir = Program.GetDirGameUserConfig(gameId, false, true);
|
||||
if (ConfigurationFileFormat.TryLoad(gameDir, out ConfigurationFileFormat configurationFileFormat))
|
||||
{
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, gameDir, gameId);
|
||||
}
|
||||
|
||||
LoadCurrentConfiguration(); // Needed to load custom configuration
|
||||
}
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Task.Run(LoadTimeZones);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel(bool noLoadGlobalConfig = false)
|
||||
{
|
||||
GameDirectories = [];
|
||||
AutoloadDirectories = [];
|
||||
@@ -363,7 +442,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Task.Run(LoadAvailableGpus);
|
||||
LoadCurrentConfiguration();
|
||||
|
||||
// if (!noLoadGlobalConfig)// Default is false, but loading custom config avoids double call
|
||||
LoadCurrentConfiguration();
|
||||
|
||||
DirtyHacks = new SettingsHacksViewModel(this);
|
||||
}
|
||||
@@ -533,6 +614,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
EnableLowPowerPptc = config.System.EnableLowPowerPtc;
|
||||
MemoryMode = (int)config.System.MemoryManagerMode.Value;
|
||||
UseHypervisor = config.System.UseHypervisor;
|
||||
TurboMultiplier = config.System.TickScalar;
|
||||
|
||||
// Graphics
|
||||
GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
|
||||
@@ -592,8 +674,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.HideCursor.Value = (HideCursorMode)HideCursor;
|
||||
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
|
||||
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
|
||||
config.UI.GameDirs.Value = [..GameDirectories];
|
||||
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
|
||||
config.UI.GameDirs.Value = [.. GameDirectories];
|
||||
config.UI.AutoloadDirs.Value = [.. AutoloadDirectories];
|
||||
|
||||
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
||||
{
|
||||
@@ -614,10 +696,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
// 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))
|
||||
{
|
||||
@@ -636,6 +718,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc;
|
||||
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
|
||||
config.System.UseHypervisor.Value = UseHypervisor;
|
||||
config.System.TickScalar.Value = TurboMultiplier;
|
||||
|
||||
// Graphics
|
||||
config.Graphics.VSyncMode.Value = VSyncMode;
|
||||
@@ -696,7 +779,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.Multiplayer.DisableP2p.Value = DisableP2P;
|
||||
config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
|
||||
config.Multiplayer.LdnServer.Value = LdnServer;
|
||||
|
||||
|
||||
// Dirty Hacks
|
||||
config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix;
|
||||
|
||||
@@ -712,7 +795,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private static void RevertIfNotSaved()
|
||||
{
|
||||
Program.ReloadConfig();
|
||||
// maybe this is an unnecessary check(all options need to be tested)
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfigurationPath))
|
||||
{
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyButton()
|
||||
@@ -720,6 +807,26 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
public void DeleteConfigGame()
|
||||
{
|
||||
string gameDir = Program.GetDirGameUserConfig(GameId,false,false);
|
||||
|
||||
if (File.Exists(gameDir))
|
||||
{
|
||||
File.Delete(gameDir);
|
||||
}
|
||||
|
||||
RevertIfNotSaved();
|
||||
CloseWindow?.Invoke();
|
||||
}
|
||||
|
||||
public void SaveUserConfig()
|
||||
{
|
||||
SaveSettings();
|
||||
RevertIfNotSaved(); // Revert global configuration after saving user configuration
|
||||
CloseWindow?.Invoke();
|
||||
}
|
||||
|
||||
public void OkButton()
|
||||
{
|
||||
SaveSettings();
|
||||
|
||||
@@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel
|
||||
public partial class UserFirmwareAvatarSelectorViewModel : BaseModel
|
||||
{
|
||||
private static readonly Dictionary<string, byte[]> _avatarStore = new();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
internal partial class UserProfileImageSelectorViewModel : BaseModel
|
||||
public partial class UserProfileImageSelectorViewModel : BaseModel
|
||||
{
|
||||
[ObservableProperty] private bool _firmwareFound;
|
||||
}
|
||||
|
||||
@@ -34,12 +34,7 @@
|
||||
<!-- Button / JoyStick Settings -->
|
||||
<Grid
|
||||
Name="SettingButtons"
|
||||
MinHeight="450">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
MinHeight="450" ColumnDefinitions="Auto,*,Auto">
|
||||
<!-- Left Controls -->
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
@@ -54,15 +49,7 @@
|
||||
CornerRadius="5">
|
||||
<Grid
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*">
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
@@ -316,17 +303,99 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
Margin="0,10"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Margin="0,0, 0, 5"
|
||||
MinHeight="90">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Image
|
||||
Margin="5,10"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Orientation="Horizontal"
|
||||
Spacing="20"
|
||||
HorizontalAlignment="Center">
|
||||
<Border
|
||||
BorderBrush="Transparent"
|
||||
Height="{Binding Visualizer.UiStickBorderSize}"
|
||||
Width="{Binding Visualizer.UiStickBorderSize}"
|
||||
IsVisible="{Binding IsLeft}">
|
||||
<Canvas
|
||||
Background="{DynamicResource ThemeBackgroundColor}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}">
|
||||
<Grid
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Background="{DynamicResource ThemeBackgroundColor}">
|
||||
<Ellipse
|
||||
HorizontalAlignment="Center"
|
||||
Stroke="{DynamicResource ThemeControlBorderColor}"
|
||||
StrokeThickness="1"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}" />
|
||||
<Ellipse
|
||||
HorizontalAlignment="Center"
|
||||
Fill="Black"
|
||||
Opacity="100"
|
||||
Height="{Binding Visualizer.UiDeadzoneLeft}"
|
||||
Width="{Binding Visualizer.UiDeadzoneLeft}" />
|
||||
</Grid>
|
||||
<Ellipse
|
||||
Fill="{DynamicResource Warning}"
|
||||
Width="{Binding Visualizer.UiStickCircumference}"
|
||||
Height="{Binding Visualizer.UiStickCircumference}"
|
||||
Canvas.Bottom="{Binding Visualizer.UiStickLeftY}"
|
||||
Canvas.Left="{Binding Visualizer.UiStickLeftX}" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="Transparent"
|
||||
Height="{Binding Visualizer.UiStickBorderSize}"
|
||||
Width="{Binding Visualizer.UiStickBorderSize}"
|
||||
IsVisible="{Binding IsRight}">
|
||||
<Canvas
|
||||
Background="{DynamicResource ThemeBackgroundColor}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}">
|
||||
<Grid
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Background="{DynamicResource ThemeBackgroundColor}">
|
||||
<Ellipse
|
||||
HorizontalAlignment="Center"
|
||||
Stroke="{DynamicResource ThemeControlBorderColor}"
|
||||
StrokeThickness="1"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}" />
|
||||
<Ellipse
|
||||
HorizontalAlignment="Center"
|
||||
Fill="Black"
|
||||
Opacity="100"
|
||||
Height="{Binding Visualizer.UiDeadzoneRight}"
|
||||
Width="{Binding Visualizer.UiDeadzoneRight}" />
|
||||
</Grid>
|
||||
<Ellipse
|
||||
Fill="{DynamicResource Warning}"
|
||||
Width="{Binding Visualizer.UiStickCircumference}"
|
||||
Height="{Binding Visualizer.UiStickCircumference}"
|
||||
Canvas.Bottom="{Binding Visualizer.UiStickRightY}"
|
||||
Canvas.Left="{Binding Visualizer.UiStickRightX}" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5">
|
||||
<StackPanel
|
||||
Margin="8"
|
||||
Orientation="Vertical">
|
||||
@@ -345,8 +414,8 @@
|
||||
Minimum="0"
|
||||
Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Width="25"
|
||||
Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" />
|
||||
Width="25"
|
||||
Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
@@ -428,7 +497,7 @@
|
||||
</Border>
|
||||
<!-- Motion, Rumble, LED -->
|
||||
<StackPanel
|
||||
Margin="0,10,0,0"
|
||||
Margin="0,5,0,0"
|
||||
Spacing="5"
|
||||
Orientation="Vertical"
|
||||
VerticalAlignment="Bottom">
|
||||
@@ -438,11 +507,7 @@
|
||||
CornerRadius="5"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<CheckBox
|
||||
Margin="10"
|
||||
MinWidth="0"
|
||||
@@ -464,11 +529,7 @@
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,-1,0,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<CheckBox
|
||||
Margin="10"
|
||||
MinWidth="0"
|
||||
@@ -490,11 +551,7 @@
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,-1,0,0">
|
||||
<Grid IsVisible="{Binding ParentModel.HasLed}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid IsVisible="{Binding ParentModel.HasLed}" ColumnDefinitions="*,Auto">
|
||||
<CheckBox
|
||||
Margin="10, 10, 5, 10"
|
||||
MinWidth="0"
|
||||
@@ -526,15 +583,7 @@
|
||||
CornerRadius="5">
|
||||
<Grid
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*">
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
|
||||
@@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
@@ -14,7 +15,7 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
public partial class ControllerInputView : UserControl
|
||||
public partial class ControllerInputView : RyujinxControl<ControllerInputViewModel>
|
||||
{
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
|
||||
@@ -217,20 +218,12 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
PointerPressed -= MouseClick;
|
||||
}
|
||||
|
||||
private IButtonAssigner CreateButtonAssigner(bool forStick)
|
||||
{
|
||||
IButtonAssigner assigner;
|
||||
|
||||
ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel;
|
||||
|
||||
assigner = new GamepadButtonAssigner(
|
||||
controllerInputViewModel.ParentModel.SelectedGamepad,
|
||||
(controllerInputViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
|
||||
private IButtonAssigner CreateButtonAssigner(bool forStick) =>
|
||||
new GamepadButtonAssigner(
|
||||
ViewModel.ParentModel.SelectedGamepad,
|
||||
(ViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
|
||||
forStick);
|
||||
|
||||
return assigner;
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
|
||||
@@ -35,22 +35,13 @@
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical"
|
||||
Spacing="5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="*,10,*">
|
||||
<!-- Player Selection -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
VerticalAlignment="Center" ColumnDefinitions="Auto,*">
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
@@ -77,14 +68,7 @@
|
||||
Grid.Column="2"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
VerticalAlignment="Center" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
@@ -139,22 +123,12 @@
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Separator />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="*,10,*">
|
||||
<!-- Input Device -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*,Auto">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="5,0,10,0"
|
||||
@@ -186,11 +160,7 @@
|
||||
Grid.Column="2"
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
VerticalAlignment="Center" ColumnDefinitions="Auto,*">
|
||||
<TextBlock
|
||||
Margin="5,0,10,0"
|
||||
Width="90"
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using Avalonia.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
public partial class InputView : UserControl
|
||||
public partial class InputView : RyujinxControl<InputViewModel>
|
||||
{
|
||||
private bool _dialogOpen;
|
||||
private InputViewModel ViewModel { get; set; }
|
||||
|
||||
public InputView()
|
||||
{
|
||||
DataContext = ViewModel = new InputViewModel(this);
|
||||
ViewModel = new InputViewModel(this);
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -32,12 +32,7 @@
|
||||
<!-- Button / JoyStick Settings -->
|
||||
<Grid
|
||||
Name="SettingButtons"
|
||||
MinHeight="450">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
MinHeight="450" ColumnDefinitions="Auto,*,Auto">
|
||||
<!-- Left Controls -->
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
@@ -52,15 +47,7 @@
|
||||
CornerRadius="5">
|
||||
<Grid
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*">
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
@@ -309,12 +296,79 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Margin="0,10"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
MinHeight="90">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Orientation="Horizontal"
|
||||
Spacing="20"
|
||||
HorizontalAlignment="Center">
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Height="{Binding Visualizer.UiStickBorderSize}"
|
||||
Width="{Binding Visualizer.UiStickBorderSize}"
|
||||
IsVisible="{Binding IsLeft}">
|
||||
<Canvas
|
||||
Background="{DynamicResource ThemeBackgroundColor}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}">
|
||||
<Grid
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Background="{DynamicResource ThemeBackgroundColor}">
|
||||
<Ellipse
|
||||
HorizontalAlignment="Center"
|
||||
Stroke="Black"
|
||||
StrokeThickness="1"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}"/>
|
||||
</Grid>
|
||||
<Ellipse
|
||||
Fill="Red"
|
||||
Width="{Binding Visualizer.UiStickCircumference}"
|
||||
Height="{Binding Visualizer.UiStickCircumference}"
|
||||
Canvas.Bottom="{Binding Visualizer.UiStickLeftY}"
|
||||
Canvas.Left="{Binding Visualizer.UiStickLeftX}" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Height="{Binding Visualizer.UiStickBorderSize}"
|
||||
Width="{Binding Visualizer.UiStickBorderSize}"
|
||||
IsVisible="{Binding IsRight}">
|
||||
<Canvas
|
||||
Background="{DynamicResource ThemeBackgroundColor}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}">
|
||||
<Grid
|
||||
Height="{Binding Visualizer.UiCanvasSize}"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Background="{DynamicResource ThemeBackgroundColor}">
|
||||
<Ellipse
|
||||
HorizontalAlignment="Center"
|
||||
Stroke="Black"
|
||||
StrokeThickness="1"
|
||||
Width="{Binding Visualizer.UiCanvasSize}"
|
||||
Height="{Binding Visualizer.UiCanvasSize}"/>
|
||||
</Grid>
|
||||
<Ellipse
|
||||
Fill="Red"
|
||||
Width="{Binding Visualizer.UiStickCircumference}"
|
||||
Height="{Binding Visualizer.UiStickCircumference}"
|
||||
Canvas.Bottom="{Binding Visualizer.UiStickRightY}"
|
||||
Canvas.Left="{Binding Visualizer.UiStickRightX}" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
@@ -413,15 +467,7 @@
|
||||
CornerRadius="5">
|
||||
<Grid
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*">
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
|
||||
@@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Input;
|
||||
@@ -13,7 +14,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
public partial class KeyboardInputView : UserControl
|
||||
public partial class KeyboardInputView : RyujinxControl<KeyboardInputViewModel>
|
||||
{
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
|
||||
@@ -60,106 +61,103 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
|
||||
PointerPressed += MouseClick;
|
||||
|
||||
if (DataContext is not KeyboardInputViewModel viewModel)
|
||||
return;
|
||||
|
||||
IKeyboard keyboard =
|
||||
(IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
|
||||
(IKeyboard)ViewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
|
||||
IButtonAssigner assigner =
|
||||
new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad);
|
||||
new KeyboardKeyAssigner((IKeyboard)ViewModel.ParentModel.SelectedGamepad);
|
||||
|
||||
_currentAssigner.ButtonAssigned += (_, e) =>
|
||||
_currentAssigner.ButtonAssigned += (_, be) =>
|
||||
{
|
||||
if (e.ButtonValue.HasValue)
|
||||
if (be.ButtonValue.HasValue)
|
||||
{
|
||||
Button buttonValue = e.ButtonValue.Value;
|
||||
viewModel.ParentModel.IsModified = true;
|
||||
Button buttonValue = be.ButtonValue.Value;
|
||||
ViewModel.ParentModel.IsModified = true;
|
||||
|
||||
switch (button.Name)
|
||||
{
|
||||
case "ButtonZl":
|
||||
viewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonL":
|
||||
viewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonMinus":
|
||||
viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickButton":
|
||||
viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickUp":
|
||||
viewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickDown":
|
||||
viewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickRight":
|
||||
viewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickLeft":
|
||||
viewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadUp":
|
||||
viewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadDown":
|
||||
viewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadLeft":
|
||||
viewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadRight":
|
||||
viewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftButtonSr":
|
||||
viewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftButtonSl":
|
||||
viewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightButtonSr":
|
||||
viewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightButtonSl":
|
||||
viewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonZr":
|
||||
viewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonR":
|
||||
viewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonPlus":
|
||||
viewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonA":
|
||||
viewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonB":
|
||||
viewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonX":
|
||||
viewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonY":
|
||||
viewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickButton":
|
||||
viewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickUp":
|
||||
viewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickDown":
|
||||
viewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickRight":
|
||||
viewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickLeft":
|
||||
viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
|
||||
ViewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,18 @@
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
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
|
||||
public partial class LedInputView : RyujinxControl<LedInputViewModel>
|
||||
{
|
||||
private readonly LedInputViewModel _viewModel;
|
||||
|
||||
public LedInputView(ControllerInputViewModel viewModel)
|
||||
{
|
||||
DataContext = _viewModel = new LedInputViewModel
|
||||
ViewModel = new LedInputViewModel
|
||||
{
|
||||
ParentModel = viewModel.ParentModel,
|
||||
TurnOffLed = viewModel.Config.TurnOffLed,
|
||||
@@ -29,20 +28,18 @@ namespace Ryujinx.UI.Views.Input
|
||||
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;
|
||||
if (!ViewModel.EnableLedChanging) return;
|
||||
if (ViewModel.TurnOffLed) return;
|
||||
|
||||
lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
|
||||
ViewModel.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;
|
||||
if (!ViewModel.EnableLedChanging) return;
|
||||
if (ViewModel.TurnOffLed) return;
|
||||
|
||||
lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32());
|
||||
ViewModel.ParentModel.SelectedGamepad.SetLed(ViewModel.LedColor.ToUInt32());
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerInputViewModel viewModel)
|
||||
@@ -57,13 +54,13 @@ namespace Ryujinx.UI.Views.Input
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
|
||||
Content = content,
|
||||
};
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
contentDialog.PrimaryButtonClick += (_, _) =>
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
config.EnableLedChanging = content._viewModel.EnableLedChanging;
|
||||
config.LedColor = content._viewModel.LedColor;
|
||||
config.UseRainbowLed = content._viewModel.UseRainbowLed;
|
||||
config.TurnOffLed = content._viewModel.TurnOffLed;
|
||||
config.EnableLedChanging = content.ViewModel.EnableLedChanging;
|
||||
config.LedColor = content.ViewModel.LedColor;
|
||||
config.UseRainbowLed = content.ViewModel.UseRainbowLed;
|
||||
config.TurnOffLed = content.ViewModel.TurnOffLed;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
|
||||
x:DataType="viewModels:MotionInputViewModel"
|
||||
Focusable="True">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="10" RowDefinitions="Auto,*">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
@@ -80,11 +76,7 @@
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid VerticalAlignment="Top" RowDefinitions="Auto,*">
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -118,15 +110,7 @@
|
||||
Text="{Binding DsuServerPort, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,*">
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
public partial class MotionInputView : UserControl
|
||||
public partial class MotionInputView : RyujinxControl<MotionInputViewModel>
|
||||
{
|
||||
private readonly MotionInputViewModel _viewModel;
|
||||
|
||||
public MotionInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -20,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
|
||||
_viewModel = new MotionInputViewModel
|
||||
ViewModel = new MotionInputViewModel
|
||||
{
|
||||
Slot = config.Slot,
|
||||
AltSlot = config.AltSlot,
|
||||
@@ -33,7 +32,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerInputViewModel viewModel)
|
||||
@@ -48,17 +46,17 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
|
||||
Content = content,
|
||||
};
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
contentDialog.PrimaryButtonClick += (_, _) =>
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
config.Slot = content._viewModel.Slot;
|
||||
config.Sensitivity = content._viewModel.Sensitivity;
|
||||
config.GyroDeadzone = content._viewModel.GyroDeadzone;
|
||||
config.AltSlot = content._viewModel.AltSlot;
|
||||
config.DsuServerHost = content._viewModel.DsuServerHost;
|
||||
config.DsuServerPort = content._viewModel.DsuServerPort;
|
||||
config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
|
||||
config.MirrorInput = content._viewModel.MirrorInput;
|
||||
config.Slot = content.ViewModel.Slot;
|
||||
config.Sensitivity = content.ViewModel.Sensitivity;
|
||||
config.GyroDeadzone = content.ViewModel.GyroDeadzone;
|
||||
config.AltSlot = content.ViewModel.AltSlot;
|
||||
config.DsuServerHost = content.ViewModel.DsuServerHost;
|
||||
config.DsuServerPort = content.ViewModel.DsuServerPort;
|
||||
config.EnableCemuHookMotion = content.ViewModel.EnableCemuHookMotion;
|
||||
config.MirrorInput = content.ViewModel.MirrorInput;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
|
||||
@@ -10,11 +10,7 @@
|
||||
x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView"
|
||||
x:DataType="viewModels:RumbleInputViewModel"
|
||||
Focusable="True">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="10" RowDefinitions="Auto,*">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
public partial class RumbleInputView : UserControl
|
||||
public partial class RumbleInputView : RyujinxControl<RumbleInputViewModel>
|
||||
{
|
||||
private readonly RumbleInputViewModel _viewModel;
|
||||
|
||||
public RumbleInputView()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -20,15 +19,13 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
|
||||
_viewModel = new RumbleInputViewModel
|
||||
ViewModel = new RumbleInputViewModel
|
||||
{
|
||||
StrongRumble = config.StrongRumble,
|
||||
WeakRumble = config.WeakRumble,
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerInputViewModel viewModel)
|
||||
@@ -44,11 +41,11 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
Content = content,
|
||||
};
|
||||
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
contentDialog.PrimaryButtonClick += (_, _) =>
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
config.StrongRumble = content._viewModel.StrongRumble;
|
||||
config.WeakRumble = content._viewModel.WeakRumble;
|
||||
config.StrongRumble = content.ViewModel.StrongRumble;
|
||||
config.WeakRumble = content.ViewModel.WeakRumble;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
|
||||
@@ -6,6 +6,7 @@ using Gommon;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
@@ -19,15 +20,15 @@ using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainMenuBarView : UserControl
|
||||
public partial class MainMenuBarView : RyujinxControl<MainWindowViewModel>
|
||||
{
|
||||
public MainWindow Window { get; private set; }
|
||||
public MainWindowViewModel ViewModel { get; private set; }
|
||||
|
||||
public MainMenuBarView()
|
||||
{
|
||||
@@ -72,7 +73,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
Content = $".{it.FileName}",
|
||||
IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
|
||||
Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName))
|
||||
Command = Commands.Create(() => Window.ToggleFileType(it.FileName))
|
||||
}
|
||||
);
|
||||
|
||||
@@ -107,7 +108,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
Margin = new Thickness(3, 0, 3, 0),
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
Header = languageName,
|
||||
Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language))
|
||||
Command = Commands.Create(() => MainWindowViewModel.ChangeLanguage(language))
|
||||
};
|
||||
|
||||
yield return menuItem;
|
||||
@@ -130,9 +131,26 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||
|
||||
Rainbow.Enable();
|
||||
|
||||
await Window.SettingsWindow.ShowDialog(Window);
|
||||
|
||||
|
||||
if (ViewModel.SelectedApplication is null) // Checks if game data exists
|
||||
{
|
||||
await StyleableAppWindow.ShowAsync(Window.SettingsWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool customConfigExists = File.Exists(Program.GetDirGameUserConfig(ViewModel.SelectedApplication.IdString));
|
||||
|
||||
if (!ViewModel.IsGameRunning || !customConfigExists)
|
||||
{
|
||||
await Window.SettingsWindow.ShowDialog(Window); // The game is not running, or if the user configuration does not exist
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is a custom configuration in the folder
|
||||
await StyleableAppWindow.ShowAsync(new GameSpecificSettingsWindow(ViewModel, customConfigExists));
|
||||
}
|
||||
}
|
||||
|
||||
Rainbow.Disable();
|
||||
Rainbow.Reset();
|
||||
|
||||
@@ -158,11 +176,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||
|
||||
await new CheatWindow(
|
||||
Window.VirtualFileSystem,
|
||||
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
|
||||
name,
|
||||
ViewModel.SelectedApplication.Path).ShowDialog(Window);
|
||||
await StyleableAppWindow.ShowAsync(
|
||||
new CheatWindow(
|
||||
Window.VirtualFileSystem,
|
||||
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
|
||||
name,
|
||||
ViewModel.SelectedApplication.Path)
|
||||
);
|
||||
|
||||
ViewModel.AppHost.Device.EnableCheats();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common;
|
||||
@@ -13,7 +15,7 @@ using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainStatusBarView : UserControl
|
||||
public partial class MainStatusBarView : RyujinxControl<MainWindowViewModel>
|
||||
{
|
||||
public MainWindow Window;
|
||||
|
||||
@@ -29,7 +31,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
Window = window;
|
||||
DataContext = window.ViewModel;
|
||||
ViewModel = window.ViewModel;
|
||||
LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if (Window.ViewModel.EnableNonGameRunningControls)
|
||||
|
||||
@@ -3,16 +3,15 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainViewControls : UserControl
|
||||
public partial class MainViewControls : RyujinxControl<MainWindowViewModel>
|
||||
{
|
||||
public MainWindowViewModel ViewModel;
|
||||
|
||||
public MainViewControls()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
DataContext = ViewModel = window.ViewModel;
|
||||
ViewModel = window.ViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
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:Class="Ryujinx.Ava.UI.Views.Misc.ApplicationDataView"
|
||||
x:DataType="viewModels:ApplicationDataViewModel">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Margin="0"
|
||||
@@ -1,9 +1,11 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
@@ -12,9 +14,9 @@ using Ryujinx.Ava.Utilities.Compat;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
namespace Ryujinx.Ava.UI.Views.Misc
|
||||
{
|
||||
public partial class ApplicationDataView : UserControl
|
||||
public partial class ApplicationDataView : RyujinxControl<ApplicationDataViewModel>
|
||||
{
|
||||
public static async Task Show(ApplicationData appData)
|
||||
{
|
||||
@@ -25,20 +27,10 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||
MinWidth = 256,
|
||||
Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) }
|
||||
Content = new ApplicationDataView { ViewModel = 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);
|
||||
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles(160, HorizontalAlignment.Center));
|
||||
}
|
||||
|
||||
public ApplicationDataView()
|
||||
@@ -1,5 +1,5 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Controls.ApplicationGridView"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Misc.ApplicationGridView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
@@ -7,16 +7,14 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Focusable="True"
|
||||
mc:Ignorable="d"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid RowDefinitions="*">
|
||||
<ListBox
|
||||
Grid.Row="0"
|
||||
Padding="8"
|
||||
@@ -56,11 +54,7 @@
|
||||
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="4">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid RowDefinitions="Auto,Auto">
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -73,12 +67,18 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
IsVisible="{Binding HasIndependentConfiguration}"
|
||||
Text="{ext:Locale GameSpecificConfigurationHeader}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource Warning}" />
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -86,10 +86,28 @@
|
||||
Margin="5,5,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="16"
|
||||
FontSize="18"
|
||||
Foreground="{DynamicResource FavoriteApplicationIconColor}"
|
||||
IsVisible="{Binding Favorite}"
|
||||
Symbol="StarFilled" />
|
||||
<Grid IsVisible="{Binding !$parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
|
||||
<Border
|
||||
Margin="15,35,5,15"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="90"
|
||||
Height="20"
|
||||
CornerRadius="4"
|
||||
IsVisible="{Binding HasIndependentConfiguration}"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale GameSpecificConfigurationHeader}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
@@ -1,13 +1,15 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
namespace Ryujinx.Ava.UI.Views.Misc
|
||||
{
|
||||
public partial class ApplicationGridView : UserControl
|
||||
public partial class ApplicationGridView : RyujinxControl<MainWindowViewModel>
|
||||
{
|
||||
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
|
||||
RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
|
||||
@@ -1,21 +1,19 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Controls.ApplicationListView"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Misc.ApplicationListView"
|
||||
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:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Focusable="True"
|
||||
mc:Ignorable="d"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
x:DataType="viewModels:MainWindowViewModel">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid RowDefinitions="*">
|
||||
<ListBox
|
||||
Name="GameListBox"
|
||||
Grid.Row="0"
|
||||
@@ -156,6 +154,13 @@
|
||||
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding HasIndependentConfiguration}"
|
||||
Text="{ext:Locale GameSpecificConfigurationHeader}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource Warning}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="4"
|
||||
@@ -2,6 +2,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
@@ -9,9 +10,9 @@ using Ryujinx.Ava.Utilities.Compat;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
namespace Ryujinx.Ava.UI.Views.Misc
|
||||
{
|
||||
public partial class ApplicationListView : UserControl
|
||||
public partial class ApplicationListView : RyujinxControl<MainWindowViewModel>
|
||||
{
|
||||
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
|
||||
RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
|
||||
@@ -32,9 +33,6 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
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;
|
||||
|
||||
@@ -43,16 +41,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
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);
|
||||
ApplicationData appData = ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text);
|
||||
if (appData is null)
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
xmlns:models="using:Ryujinx.Ava.Common.Models"
|
||||
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.Ava.UI.Controls.DlcSelectView"
|
||||
x:Class="Ryujinx.Ava.UI.Views.Misc.DlcSelectView"
|
||||
x:DataType="viewModels:DlcSelectViewModel">
|
||||
<Grid RowDefinitions="*,Auto,*">
|
||||
<TextBlock
|
||||
@@ -3,14 +3,15 @@ using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
namespace Ryujinx.Ava.UI.Views.Misc
|
||||
{
|
||||
public partial class DlcSelectView : UserControl
|
||||
public partial class DlcSelectView : RyujinxControl<DlcSelectViewModel>
|
||||
{
|
||||
public DlcSelectView()
|
||||
{
|
||||
@@ -28,20 +29,10 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = string.Empty,
|
||||
Content = new DlcSelectView { DataContext = viewModel }
|
||||
Content = new DlcSelectView { ViewModel = viewModel }
|
||||
};
|
||||
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
|
||||
|
||||
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty,
|
||||
Avalonia.Layout.HorizontalAlignment.Right));
|
||||
|
||||
contentDialog.Styles.Add(closeButton);
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles());
|
||||
|
||||
return viewModel.SelectedDlc;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
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"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
@@ -76,6 +77,57 @@
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Spacing="5">
|
||||
<TextBlock
|
||||
Classes="h1"
|
||||
Text="{ext:Locale SettingsTabSystemHacks}" />
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SecondaryTextColor}"
|
||||
TextDecorations="Underline"
|
||||
Text="{ext:Locale SettingsTabSystemHacksNote}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Margin="0,0,0,10"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Text="{ext:Locale SettingsTabSystemTurboMultiplier}"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierToolTip}"
|
||||
Width="250" />
|
||||
<ui:NumberBox ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
|
||||
Value="{Binding TurboMultiplier}"
|
||||
Width="165"
|
||||
SmallChange="1.0"
|
||||
LargeChange="10"
|
||||
SimpleNumberFormat="F0"
|
||||
SpinButtonPlacementMode="Hidden"
|
||||
Minimum="50"
|
||||
Maximum="500" />
|
||||
<Slider Value="{Binding TurboMultiplier}"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
|
||||
MinWidth="175"
|
||||
Margin="10,-3,0,0"
|
||||
Height="32"
|
||||
Padding="0,-5"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="10"
|
||||
SmallChange="1"
|
||||
VerticalAlignment="Center"
|
||||
Minimum="50"
|
||||
Maximum="500" />
|
||||
<TextBlock Margin="5,0"
|
||||
Width="40"
|
||||
Text="{Binding TurboMultiplierPercentageText}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<Setter Property="Margin" Value="10, 0, 0, 0" />
|
||||
<Setter Property="Orientation" Value="Horizontal" />
|
||||
</Style>
|
||||
<Style Selector="StackPanel > StackPanel > TextBlock">
|
||||
<Style Selector="StackPanel > StackPanel > TextBlock.settingHeader">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="Width" Value="230" />
|
||||
</Style>
|
||||
@@ -47,71 +47,79 @@
|
||||
Classes="h1"
|
||||
Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="ToggleVSyncMode">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="Screenshot">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="ShowUI">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="Pause">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="ToggleMute">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="ResScaleUp">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="ResScaleDown">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="VolumeUp">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="VolumeDown">
|
||||
<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}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CustomVSyncIntervalIncrement">
|
||||
<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}" />
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CustomVSyncIntervalDecrement">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysTurboMode}" Classes="settingHeader" ToolTip.Tip="{ext:Locale SettingsTabHotkeysTurboModeToolTip}" Background="Transparent" />
|
||||
<ToggleButton Name="TurboMode">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.TurboMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysOnlyWhilePressed}" Margin="10,0" />
|
||||
<CheckBox IsChecked="{Binding KeyboardHotkey.TurboModeWhileHeld}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -116,6 +116,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
case "CustomVSyncIntervalDecrement":
|
||||
viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "TurboMode":
|
||||
viewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,12 +21,7 @@
|
||||
<Border Classes="settings">
|
||||
<Panel
|
||||
Margin="10">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<views:InputView
|
||||
Grid.Row="0"
|
||||
Name="InputView" />
|
||||
|
||||
@@ -156,6 +156,8 @@
|
||||
ValueMemberBinding="{Binding Mode=OneWay, Converter={x:Static helpers:TimeZoneConverter.Instance}}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="0,0,0,10"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
@@ -169,8 +171,11 @@
|
||||
SelectedDate="{Binding CurrentDate}"
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}"
|
||||
Width="350" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="250,0,0,10"
|
||||
Orientation="Horizontal">
|
||||
<TimePicker
|
||||
@@ -180,8 +185,12 @@
|
||||
SelectedTime="{Binding CurrentTime}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabSystemSystemTimeMatch}"
|
||||
@@ -191,6 +200,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding MatchSystemTime}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}"/>
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
<Separator />
|
||||
<StackPanel Margin="0,10,0,10"
|
||||
|
||||
@@ -27,20 +27,42 @@
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGeneralGeneral}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale ToggleDiscordTooltip}"
|
||||
Text="{ext:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale ToggleDiscordTooltip}"
|
||||
Text="{ext:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowConfirmExit}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralShowConfirmExitDialog}" />
|
||||
<CheckBox
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
IsChecked="{Binding ShowConfirmExit}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralShowConfirmExitDialog}" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding RememberWindowState}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralRememberWindowState}" />
|
||||
<CheckBox
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
IsChecked="{Binding RememberWindowState}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralRememberWindowState}" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowTitleBar}" IsVisible="{x:Static helper:RunningPlatform.IsWindows}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralShowTitleBar}" />
|
||||
<CheckBox
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
IsChecked="{Binding ShowTitleBar}" IsVisible="{x:Static helper:RunningPlatform.IsWindows}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralShowTitleBar}" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
Margin="0, 15, 0, 0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralFocusLossType}"
|
||||
Width="150" />
|
||||
@@ -64,7 +86,11 @@
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="0, 15, 0, 0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}"
|
||||
Width="150" />
|
||||
@@ -81,8 +107,11 @@
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchBackground}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
Margin="0, 15, 0, 0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralHideCursor}"
|
||||
Width="150" />
|
||||
@@ -100,7 +129,11 @@
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0, 15, 0, 10" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="0, 15, 0, 10"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralTheme}"
|
||||
@@ -118,11 +151,17 @@
|
||||
<TextBlock Text="{ext:Locale SettingsTabGeneralThemeDark}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGeneralGameDirectories}" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGeneralGameDirectories}" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
@@ -138,12 +177,7 @@
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
</ListBox>
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto,Auto">
|
||||
<TextBox
|
||||
Name="GameDirPathBox"
|
||||
Margin="0"
|
||||
@@ -172,10 +206,15 @@
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel Orientation="Vertical" Spacing="5">
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGeneralAutoloadDirectories}" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGeneralAutoloadDirectories}" />
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{ext:Locale SettingsTabGeneralAutoloadNote}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
@@ -191,12 +230,7 @@
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
</ListBox>
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto,Auto">
|
||||
<TextBox
|
||||
Name="AutoloadDirPathBox"
|
||||
Margin="0"
|
||||
|
||||
@@ -14,15 +14,7 @@
|
||||
mc:Ignorable="d"
|
||||
Focusable="True"
|
||||
x:DataType="models:TempProfile">
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="0" ColumnDefinitions="Auto,*" RowDefinitions="*,Auto">
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
|
||||
@@ -13,13 +13,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
public partial class UserEditorView : UserControl
|
||||
public partial class UserEditorView : RyujinxControl<TempProfile>
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
private UserProfile _profile;
|
||||
private bool _isNewUser;
|
||||
|
||||
public TempProfile TempProfile { get; set; }
|
||||
|
||||
public static uint MaxProfileNameLength => 0x20;
|
||||
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
|
||||
|
||||
@@ -42,7 +41,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
(NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
|
||||
_isNewUser = isNewUser;
|
||||
_profile = profile;
|
||||
TempProfile = new TempProfile(_profile);
|
||||
ViewModel = new TempProfile(_profile);
|
||||
|
||||
_parent = parent;
|
||||
break;
|
||||
@@ -51,8 +50,6 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
|
||||
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
|
||||
|
||||
DataContext = TempProfile;
|
||||
|
||||
AddPictureButton.IsVisible = _isNewUser;
|
||||
ChangePictureButton.IsVisible = !_isNewUser;
|
||||
IdLabel.IsVisible = _profile != null;
|
||||
@@ -72,7 +69,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
if (_isNewUser)
|
||||
{
|
||||
if (TempProfile.Name != String.Empty || TempProfile.Image != null)
|
||||
if (ViewModel.Name != string.Empty || ViewModel.Image != null)
|
||||
{
|
||||
if (await ContentDialogHelper.CreateChoiceDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
|
||||
@@ -89,7 +86,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image)
|
||||
if (_profile.Name != ViewModel.Name || _profile.Image != ViewModel.Image)
|
||||
{
|
||||
if (await ContentDialogHelper.CreateChoiceDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
|
||||
@@ -115,31 +112,31 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
DataValidationErrors.ClearErrors(NameBox);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(TempProfile.Name))
|
||||
if (string.IsNullOrWhiteSpace(ViewModel.Name))
|
||||
{
|
||||
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError]));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (TempProfile.Image == null)
|
||||
if (ViewModel.Image == null)
|
||||
{
|
||||
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
|
||||
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_profile != null && !_isNewUser)
|
||||
{
|
||||
_profile.Name = TempProfile.Name;
|
||||
_profile.Image = TempProfile.Image;
|
||||
_profile.Name = ViewModel.Name;
|
||||
_profile.Image = ViewModel.Image;
|
||||
_profile.UpdateState();
|
||||
_parent.AccountManager.SetUserName(_profile.UserId, _profile.Name);
|
||||
_parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image);
|
||||
}
|
||||
else if (_isNewUser)
|
||||
{
|
||||
_parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId);
|
||||
_parent.AccountManager.AddUser(ViewModel.Name, ViewModel.Image, ViewModel.UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -151,7 +148,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
|
||||
public void SelectProfileImage()
|
||||
{
|
||||
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
|
||||
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel));
|
||||
}
|
||||
|
||||
private void ChangePictureButton_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -20,13 +20,7 @@
|
||||
<Grid
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto,Auto">
|
||||
<ListBox
|
||||
Grid.Row="1"
|
||||
BorderThickness="0"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
@@ -11,7 +10,7 @@ using System.IO;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
public partial class UserFirmwareAvatarSelectorView : UserControl
|
||||
public partial class UserFirmwareAvatarSelectorView : RyujinxControl<UserFirmwareAvatarSelectorViewModel>
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
private TempProfile _profile;
|
||||
@@ -20,8 +19,6 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
ContentManager = contentManager;
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -55,8 +52,6 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
|
||||
public ContentManager ContentManager { get; private set; }
|
||||
|
||||
internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; }
|
||||
|
||||
private void GoBack(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_parent.GoBack();
|
||||
|
||||
@@ -17,12 +17,7 @@
|
||||
</Design.DataContext>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
VerticalAlignment="Center" RowDefinitions="Auto,70,Auto">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
TextWrapping="Wrap"
|
||||
|
||||
@@ -15,14 +15,12 @@ using System.IO;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
public partial class UserProfileImageSelectorView : UserControl
|
||||
public partial class UserProfileImageSelectorView : RyujinxControl<UserProfileImageSelectorViewModel>
|
||||
{
|
||||
private ContentManager _contentManager;
|
||||
private NavigationDialogHost _parent;
|
||||
private TempProfile _profile;
|
||||
|
||||
internal UserProfileImageSelectorViewModel ViewModel { get; private set; }
|
||||
|
||||
public UserProfileImageSelectorView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -18,11 +18,7 @@
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
VerticalAlignment="Stretch" RowDefinitions="*,Auto">
|
||||
<Border
|
||||
CornerRadius="5"
|
||||
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
|
||||
@@ -41,11 +37,7 @@
|
||||
VerticalAlignment="Stretch"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="5">
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Margin="0" ColumnDefinitions="*,Auto">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding UserId}"
|
||||
|
||||
@@ -4,10 +4,11 @@ using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
public partial class UserRecovererView : UserControl
|
||||
public partial class UserRecovererView : RyujinxControl<UserProfileViewModel>
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
|
||||
|
||||
@@ -19,19 +19,10 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:UserSaveManagerViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*">
|
||||
<StackPanel
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
@@ -80,11 +71,7 @@
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="10,0, 0, 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
Margin="10,0, 0, 0" ColumnDefinitions="Auto,*">
|
||||
<Label Content="{ext:Locale Search}" VerticalAlignment="Center" />
|
||||
<TextBox
|
||||
Margin="5,0,0,0"
|
||||
@@ -118,11 +105,7 @@
|
||||
</ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:SaveModel">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto">
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Orientation="Horizontal"
|
||||
|
||||
@@ -23,10 +23,8 @@ using UserId = LibHac.Fs.UserId;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
public partial class UserSaveManagerView : UserControl
|
||||
public partial class UserSaveManagerView : RyujinxControl<UserSaveManagerViewModel>
|
||||
{
|
||||
internal UserSaveManagerViewModel ViewModel { get; private set; }
|
||||
|
||||
private AccountManager _accountManager;
|
||||
private HorizonClient _horizonClient;
|
||||
private VirtualFileSystem _virtualFileSystem;
|
||||
@@ -66,7 +64,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
|
||||
public void LoadSaves()
|
||||
{
|
||||
ViewModel.Saves.Clear();
|
||||
Dispatcher.UIThread.Post(() => ViewModel.Saves.Clear());
|
||||
ObservableCollection<SaveModel> saves = [];
|
||||
SaveDataFilter saveDataFilter = SaveDataFilter.Make(
|
||||
programId: default,
|
||||
|
||||
@@ -18,11 +18,7 @@
|
||||
<Design.DataContext>
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
|
||||
<Border
|
||||
CornerRadius="5"
|
||||
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
|
||||
|
||||
@@ -11,12 +11,10 @@ using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
public partial class UserSelectorViews : UserControl
|
||||
public partial class UserSelectorViews : RyujinxControl<UserProfileViewModel>
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
public UserSelectorViews()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -5,6 +5,7 @@ using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common;
|
||||
@@ -14,7 +15,7 @@ using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class AboutWindow : UserControl
|
||||
public partial class AboutWindow : RyujinxControl<AboutWindowViewModel>
|
||||
{
|
||||
public AboutWindow()
|
||||
{
|
||||
@@ -33,19 +34,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
PrimaryButtonText = string.Empty,
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = new AboutWindow { DataContext = viewModel }
|
||||
Content = new AboutWindow { ViewModel = viewModel }
|
||||
};
|
||||
|
||||
Style closeButton = new(x => x.Name("CloseButton"));
|
||||
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
|
||||
|
||||
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Right));
|
||||
|
||||
contentDialog.Styles.Add(closeButton);
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles());
|
||||
}
|
||||
|
||||
private void Button_OnClick(object sender, RoutedEventArgs e)
|
||||
|
||||
149
src/Ryujinx/UI/Windows/GameSpecificSettingsWindow.axaml
Normal file
149
src/Ryujinx/UI/Windows/GameSpecificSettingsWindow.axaml
Normal file
@@ -0,0 +1,149 @@
|
||||
<window:StyleableAppWindow
|
||||
x:Class="Ryujinx.Ava.UI.Windows.GameSpecificSettingsWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
Width="1100"
|
||||
Height="910"
|
||||
MinWidth="800"
|
||||
MinHeight="480"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
x:DataType="viewModels:SettingsViewModel"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600" RowDefinitions="Auto,*,Auto">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Grid.Row="1"
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False"/>
|
||||
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
|
||||
<settings:SettingsUiView Name="UiPage" />
|
||||
<settings:SettingsInputView Name="InputPage" />
|
||||
<settings:SettingsSystemView Name="SystemPage" />
|
||||
<settings:SettingsCPUView Name="CpuPage" />
|
||||
<settings:SettingsGraphicsView Name="GraphicsPage" />
|
||||
<settings:SettingsAudioView Name="AudioPage" />
|
||||
<settings:SettingsNetworkView Name="NetworkPage" />
|
||||
<settings:SettingsLoggingView Name="LoggingPage" />
|
||||
<settings:SettingsHacksView Name="HacksPage" />
|
||||
</Grid>
|
||||
<ui:NavigationView
|
||||
Grid.Row="1"
|
||||
IsSettingsVisible="False"
|
||||
Name="NavPanel"
|
||||
IsBackEnabled="False"
|
||||
PaneDisplayMode="Left"
|
||||
Margin="2,10,10,0"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
OpenPaneLength="200"
|
||||
IsPaneToggleButtonVisible="False">
|
||||
|
||||
<!-- For image -->
|
||||
<ui:NavigationView.PaneHeader>
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" RowDefinitions="Auto,Auto,Auto,5">
|
||||
<TextBlock Text="{Binding GameId}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,10"
|
||||
TextAlignment="Center" Grid.Row="0" />
|
||||
<Image Source="{Binding GameIcon}"
|
||||
Width="160"
|
||||
Height="160"
|
||||
Grid.Row="1"
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center" />
|
||||
<TextBlock Text="{Binding GameTitle}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,10"
|
||||
TextAlignment="Center" Grid.Row="2" />
|
||||
<Separator Height="1" Grid.Row="3" Margin="0,0,0,10" HorizontalAlignment="Stretch"/>
|
||||
</Grid>
|
||||
</ui:NavigationView.PaneHeader>
|
||||
|
||||
<ui:NavigationView.MenuItems>
|
||||
<ui:NavigationViewItem
|
||||
IsSelected="True"
|
||||
Content="{ext:Locale SettingsTabGeneral}"
|
||||
Tag="UiPage"
|
||||
IconSource="New" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabInput}"
|
||||
Tag="InputPage"
|
||||
IconSource="Games" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabSystem}"
|
||||
Tag="SystemPage"
|
||||
IconSource="Settings" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabCpu}"
|
||||
Tag="CpuPage">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ui:FontIconSource
|
||||
FontFamily="avares://Ryujinx/Assets/Fonts#Segoe Fluent Icons"
|
||||
Glyph="{helpers:GlyphValueConverter Chip}" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabGraphics}"
|
||||
Tag="GraphicsPage"
|
||||
IconSource="Image" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabAudio}"
|
||||
IconSource="Audio"
|
||||
Tag="AudioPage" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabNetwork}"
|
||||
Tag="NetworkPage"
|
||||
IconSource="Globe" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabLogging}"
|
||||
Tag="LoggingPage"
|
||||
IconSource="Document" />
|
||||
<ui:NavigationViewItem
|
||||
IsVisible="{Binding ShowDirtyHacks}"
|
||||
Content="Dirty Hacks"
|
||||
Tag="HacksPage"
|
||||
IconSource="Code" />
|
||||
</ui:NavigationView.MenuItems>
|
||||
</ui:NavigationView>
|
||||
|
||||
<ReversibleStackPanel
|
||||
Grid.Row="2"
|
||||
Margin="10"
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
ReverseOrder="{x:Static helper:RunningPlatform.IsMacOS}">
|
||||
<Button
|
||||
Content="{ext:Locale SettingsButtonSave}"
|
||||
Command="{Binding SaveUserConfig}" />
|
||||
<Button
|
||||
HotKey="Escape"
|
||||
Content="{ext:Locale SettingsButtonClose}"
|
||||
Command="{Binding CancelButton}" />
|
||||
<Button
|
||||
IsVisible="{Binding IsGameRunning}"
|
||||
Content="{ext:Locale SettingsButtonApply}"
|
||||
Command="{Binding ApplyButton}" />
|
||||
<Button
|
||||
IsVisible="{Binding !IsGameRunning}"
|
||||
Content="{ext:Locale UserProfilesDelete}"
|
||||
Command="{Binding DeleteConfigGame}"
|
||||
Classes="red"/>
|
||||
</ReversibleStackPanel>
|
||||
</Grid>
|
||||
</window:StyleableAppWindow>
|
||||
115
src/Ryujinx/UI/Windows/GameSpecificSettingsWindow.axaml.cs
Normal file
115
src/Ryujinx/UI/Windows/GameSpecificSettingsWindow.axaml.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media.Imaging;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Key = Avalonia.Input.Key;
|
||||
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class GameSpecificSettingsWindow : StyleableAppWindow
|
||||
{
|
||||
internal readonly SettingsViewModel ViewModel;
|
||||
|
||||
public GameSpecificSettingsWindow(MainWindowViewModel viewModel, bool findUserConfigDir = true)
|
||||
{
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.SettingsWithInfo], viewModel.SelectedApplication.Name, viewModel.SelectedApplication.IdString);
|
||||
|
||||
DataContext = ViewModel = new SettingsViewModel(
|
||||
viewModel.VirtualFileSystem,
|
||||
viewModel.ContentManager,
|
||||
viewModel.IsGameRunning,
|
||||
viewModel.SelectedApplication.Path,
|
||||
viewModel.SelectedApplication.Name,
|
||||
viewModel.SelectedApplication.IdString,
|
||||
viewModel.SelectedApplication.Icon,
|
||||
findUserConfigDir);
|
||||
|
||||
ViewModel.CloseWindow += Close;
|
||||
ViewModel.SaveSettingsEvent += SaveSettings;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
InputPage.InputView?.SaveCurrentProfile();
|
||||
}
|
||||
|
||||
|
||||
private void Load()
|
||||
{
|
||||
Pages.Children.Clear();
|
||||
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
||||
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
||||
}
|
||||
|
||||
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
|
||||
{
|
||||
|
||||
if (e.SelectedItem is NavigationViewItem navItem && navItem.Tag is not null)
|
||||
{
|
||||
switch (navItem.Tag.ToString())
|
||||
{
|
||||
case nameof(UiPage):
|
||||
UiPage.ViewModel = ViewModel;
|
||||
NavPanel.Content = UiPage;
|
||||
break;
|
||||
case nameof(InputPage):
|
||||
NavPanel.Content = InputPage;
|
||||
break;
|
||||
case nameof(SystemPage):
|
||||
SystemPage.ViewModel = ViewModel;
|
||||
NavPanel.Content = SystemPage;
|
||||
break;
|
||||
case nameof(CpuPage):
|
||||
NavPanel.Content = CpuPage;
|
||||
break;
|
||||
case nameof(GraphicsPage):
|
||||
NavPanel.Content = GraphicsPage;
|
||||
break;
|
||||
case nameof(AudioPage):
|
||||
NavPanel.Content = AudioPage;
|
||||
break;
|
||||
case nameof(NetworkPage):
|
||||
NetworkPage.ViewModel = ViewModel;
|
||||
NavPanel.Content = NetworkPage;
|
||||
break;
|
||||
case nameof(LoggingPage):
|
||||
NavPanel.Content = LoggingPage;
|
||||
break;
|
||||
case nameof(HacksPage):
|
||||
HacksPage.DataContext = ViewModel;
|
||||
NavPanel.Content = HacksPage;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
InputPage.Dispose(); // You need to unload the gamepad settings, otherwise the controls will be blocked
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main"
|
||||
xmlns:viewsMisc="clr-namespace:Ryujinx.Ava.UI.Views.Misc"
|
||||
Cursor="{Binding Cursor}"
|
||||
Title="{Binding Title}"
|
||||
WindowState="{Binding WindowState}"
|
||||
@@ -73,7 +74,7 @@
|
||||
<main:MainViewControls
|
||||
Name="ViewControls"
|
||||
Grid.Row="0"/>
|
||||
<controls:ApplicationListView
|
||||
<viewsMisc:ApplicationListView
|
||||
x:Name="ApplicationList"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -81,7 +82,7 @@
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsVisible="{Binding IsList}" />
|
||||
<controls:ApplicationGridView
|
||||
<viewsMisc:ApplicationGridView
|
||||
x:Name="ApplicationGrid"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
_deferLoad = true;
|
||||
_launchPath = launchPathArg;
|
||||
_launchApplicationId = launchApplicationId;
|
||||
_startFullscreen = startFullscreenArg;
|
||||
_startFullscreen = startFullscreenArg;
|
||||
}
|
||||
|
||||
public void SwitchToGameControl(bool startFullscreen = false)
|
||||
@@ -385,6 +385,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (applicationData != null)
|
||||
{
|
||||
ViewModel.SelectedApplication = applicationData;
|
||||
await ViewModel.LoadApplication(applicationData, _startFullscreen);
|
||||
}
|
||||
else
|
||||
@@ -396,6 +397,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
else
|
||||
{
|
||||
applicationData = applications[0];
|
||||
ViewModel.SelectedApplication = applicationData;
|
||||
await ViewModel.LoadApplication(applicationData, _startFullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,11 +70,7 @@
|
||||
<DataTemplate
|
||||
DataType="models:ModModel">
|
||||
<Panel Margin="10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
Width="1100"
|
||||
Height="768"
|
||||
Height="927"
|
||||
MinWidth="800"
|
||||
MinHeight="480"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
@@ -8,7 +6,6 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Key = Avalonia.Input.Key;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
@@ -27,10 +24,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt));
|
||||
#endif
|
||||
}
|
||||
|
||||
public SettingsWindow()
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using FluentAvalonia.UI.Windowing;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public abstract class StyleableAppWindow : AppWindow
|
||||
{
|
||||
public static async Task ShowAsync(StyleableAppWindow appWindow, Window owner = null)
|
||||
{
|
||||
#if DEBUG
|
||||
appWindow.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control));
|
||||
#endif
|
||||
await appWindow.ShowDialog(owner ?? RyujinxApp.MainWindow);
|
||||
}
|
||||
|
||||
protected StyleableAppWindow()
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
@@ -36,6 +47,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public abstract class StyleableWindow : Window
|
||||
{
|
||||
public static async Task ShowAsync(StyleableWindow window, Window owner = null)
|
||||
{
|
||||
#if DEBUG
|
||||
window.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control));
|
||||
#endif
|
||||
await window.ShowDialog(owner ?? RyujinxApp.MainWindow);
|
||||
}
|
||||
|
||||
protected StyleableWindow()
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<Window
|
||||
x:Class="Ryujinx.Ava.UI.Controls.UpdateWaitWindow"
|
||||
x:Class="Ryujinx.Ava.UI.Windows.UpdateWaitWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -13,15 +13,7 @@
|
||||
<Grid
|
||||
Margin="20"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
VerticalAlignment="Stretch" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
|
||||
<Image
|
||||
Grid.Row="1"
|
||||
Height="70"
|
||||
@@ -1,8 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class UpdateWaitWindow : StyleableWindow
|
||||
{
|
||||
@@ -13,14 +13,7 @@
|
||||
x:DataType="viewModels:XCITrimmerViewModel"
|
||||
Focusable="True"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="20 0 20 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="20 0 20 0" RowDefinitions="Auto,Auto,*,Auto,Auto">
|
||||
<Panel
|
||||
Margin="10 10 10 10"
|
||||
Grid.Row="0">
|
||||
@@ -30,12 +23,7 @@
|
||||
Margin="0 0 10 10"
|
||||
IsVisible="{Binding !Processing}"
|
||||
Grid.Row="1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Orientation="Horizontal">
|
||||
@@ -145,11 +133,7 @@
|
||||
<DataTemplate
|
||||
DataType="models:XCITrimmerFileModel">
|
||||
<Panel Margin="10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="65*" />
|
||||
<ColumnDefinition Width="35*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="65*,35*">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="10 0 10 0"
|
||||
@@ -160,11 +144,7 @@
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding Name}">
|
||||
</TextBlock>
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="45*" />
|
||||
<ColumnDefinition Width="55*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="1" ColumnDefinitions="45*,55*">
|
||||
<ProgressBar
|
||||
Height="10"
|
||||
Margin="10 0 10 0"
|
||||
@@ -226,15 +206,7 @@
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Padding="2.5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
@@ -274,11 +246,7 @@
|
||||
<Panel
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Orientation="Horizontal"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user