Compare commits

..

3 Commits

149 changed files with 1155 additions and 1340 deletions

View File

@@ -97,7 +97,7 @@ If you are planning to contribute or just want to learn more about this project
- **Input** - **Input**
We currently have support for keyboard, mouse, touch input, Joy-Con input support, and nearly all controllers. We currently have support for keyboard, mouse, touch input, JoyCon 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. 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. In all scenarios, you can set up everything inside the input configuration menu.

View File

@@ -20,4 +20,42 @@ if command -v gamemoderun > /dev/null 2>&1; then
COMMAND="$COMMAND gamemoderun" COMMAND="$COMMAND gamemoderun"
fi fi
exec $COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@" # Check if user already has a manual Avalonia scaling override or session type is x11.
if [[ -n "${AVALONIA_GLOBAL_SCALE_FACTOR-}" || "$(echo "$XDG_SESSION_TYPE")" == "x11" ]]; then
echo "Scaling: Performed by environment, skipping." >&2
else
# Query monitor config directly (GNOME), default display only.
if [[ "$(echo "$XDG_CURRENT_DESKTOP")" == "GNOME" && -f ~/.config/monitors.xml ]] then
echo -n 'Scaling: Monitor config located, querying scale...' >&2
SCALING="$(grep '<scale' ~/.config/monitors.xml -m 1 | cut -f2 -d">"|cut -f1 -d"<")"
SCALING="${SCALING##* }"
echo "found! Factor: ${SCALING}" >&2
# Fallback to X DPI query for others.
# Plasma handles this fine, GNOME will always round up e.g. 1.25 -> 2.00.
elif command -v xrdb >/dev/null; then
echo -n 'Scaling: Attempting to get scaling from X DPI value...' >&2
dpi="$(xrdb -get Xft.dpi)"
if [[ -n "${dpi}" ]]; then
SCALING=$(echo "scale=2; ${dpi}/96" | bc)
fi
echo "found! Factor: ${SCALING}"
# Query kscreen-doctor for Plasma as a fallback.
elif [[ "$(echo "$XDG_CURRENT_DESKTOP")" == "KDE" ]] && command -v kscreen-doctor >/dev/null; then
echo -n 'Scaling: Attempting to get Plasma desktop scaling factor...' >&2
SCALING="$(kscreen-doctor --outputs | grep "Scale" -m 1)"
SCALING="${SCALING##* }"
SCALING=$(echo $SCALING | sed 's/\x1B\[[0-9;]*m//g') # Trim ANSI chars from ksd output.
echo "found! Factor: ${SCALING}"
fi
if [[ -z "${SCALING-}" || "${SCALING-}" == "0" ]]; then
echo 'Unset invalid scaling value' >&2
SCALING="1"
fi
COMMAND="$COMMAND AVALONIA_GLOBAL_SCALE_FACTOR=$SCALING"
fi
exec $COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"

View File

@@ -631,7 +631,6 @@
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27 010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41 0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17 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 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 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 01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
@@ -1383,9 +1382,6 @@
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34 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 01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51 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 0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42 01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21 0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
@@ -2733,7 +2729,7 @@
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11 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 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 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,2024-03-04 16:35:00 01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44 010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11 0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42 0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42
1 title_id game_name labels status last_updated
631 010030D012FF6000 Bus Driver Simulator playable 2022-10-17 13:55:27
632 0100A9101418C000 BUSTAFELLOWS nvdec playable 2020-10-17 20:04:41
633 0100177005C8A000 BUTCHER playable 2021-01-11 18:50:17
01008c2019598000 Bluey: The Videogame playable 2025-02-11 04:38:00
634 01000B900D8B0000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda slow;nvdec playable 2024-04-01 22:43:40
635 010065700EE06000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo demo;gpu;nvdec ingame 2021-02-14 21:48:15
636 01005C00117A8000 Café Enchanté playable 2020-11-13 14:54:25
1382 0100763015C2E000 Gunvolt Chronicles: Luminous Avenger iX 2 crash;Needs Update nothing 2022-04-29 15:34:34
1383 01002C8018554000 Gurimugurimoa OnceMore Demo playable 2022-07-29 22:07:31
1384 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
1385 0100822012D76000 HAAK gpu ingame 2023-02-19 14:31:05
1386 01007E100EFA8000 Habroxia playable 2020-06-16 23:04:42
1387 0100535012974000 Hades vulkan playable 2022-10-05 10:45:21
2729 0100C2500FC20000 Splatoon™ 3 ldn-works;opengl-backend-bug;LAN;amd-vendor-bug playable 2024-08-04 23:49:11
2730 0100BA0018500000 Splatoon™ 3: Splatfest World Premiere gpu;online-broken;demo ingame 2022-09-19 03:17:12
2731 010062800D39C000 SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated online-broken;UE4;ldn-broken;vulkan-backend-bug playable 2023-08-01 19:29:34
2732 01009FB0172F4000 SpongeBob SquarePants: The Cosmic Shake gpu;UE4 ingame 2024-03-04 16:35:00 2023-08-01 19:29:53
2733 010097C01336A000 Spooky Chase playable 2022-11-04 12:17:44
2734 0100C6100D75E000 Spooky Ghosts Dot Com playable 2021-06-15 15:16:11
2735 0100DE9005170000 Sports Party nvdec playable 2021-03-05 13:40:42

View File

@@ -1,6 +1,4 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL.Multithreading;
using System; using System;
using System.Threading; using System.Threading;
@@ -12,20 +10,6 @@ namespace Ryujinx.Graphics.GAL
bool PreferThreading { get; } 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; } IPipeline Pipeline { get; }
IWindow Window { get; } IWindow Window { get; }

View File

@@ -15,55 +15,55 @@ namespace Ryujinx.HLE
/// <summary> /// <summary>
/// HLE configuration. /// HLE configuration.
/// </summary> /// </summary>
public class HleConfiguration public class HLEConfiguration
{ {
/// <summary> /// <summary>
/// The virtual file system used by the FS service. /// The virtual file system used by the FS service.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal VirtualFileSystem VirtualFileSystem { get; private set; } internal readonly VirtualFileSystem VirtualFileSystem;
/// <summary> /// <summary>
/// The manager for handling a LibHac Horizon instance. /// The manager for handling a LibHac Horizon instance.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal LibHacHorizonManager LibHacHorizonManager { get; private set; } internal readonly LibHacHorizonManager LibHacHorizonManager;
/// <summary> /// <summary>
/// The account manager used by the account service. /// The account manager used by the account service.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal AccountManager AccountManager { get; private set; } internal readonly AccountManager AccountManager;
/// <summary> /// <summary>
/// The content manager used by the NCM service. /// The content manager used by the NCM service.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal ContentManager ContentManager { get; private set; } internal readonly ContentManager ContentManager;
/// <summary> /// <summary>
/// The persistent information between run for multi-application capabilities. /// The persistent information between run for multi-application capabilities.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
public UserChannelPersistence UserChannelPersistence { get; private set; } public readonly UserChannelPersistence UserChannelPersistence;
/// <summary> /// <summary>
/// The GPU renderer to use for all GPU operations. /// The GPU renderer to use for all GPU operations.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal IRenderer GpuRenderer { get; private set; } internal readonly IRenderer GpuRenderer;
/// <summary> /// <summary>
/// The audio device driver to use for all audio operations. /// The audio device driver to use for all audio operations.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal IHardwareDeviceDriver AudioDeviceDriver { get; private set; } internal readonly IHardwareDeviceDriver AudioDeviceDriver;
/// <summary> /// <summary>
/// The handler for various UI related operations needed outside of HLE. /// The handler for various UI related operations needed outside of HLE.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal IHostUIHandler HostUIHandler { get; private set; } internal readonly IHostUIHandler HostUIHandler;
/// <summary> /// <summary>
/// Control the memory configuration used by the emulation context. /// Control the memory configuration used by the emulation context.
@@ -195,7 +195,15 @@ namespace Ryujinx.HLE
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
public EnabledDirtyHack[] Hacks { internal get; set; } public EnabledDirtyHack[] Hacks { internal get; set; }
public HleConfiguration(MemoryConfiguration memoryConfiguration, public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
ContentManager contentManager,
AccountManager accountManager,
UserChannelPersistence userChannelPersistence,
IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver,
MemoryConfiguration memoryConfiguration,
IHostUIHandler hostUIHandler,
SystemLanguage systemLanguage, SystemLanguage systemLanguage,
RegionCode region, RegionCode region,
VSyncMode vSyncMode, VSyncMode vSyncMode,
@@ -219,7 +227,15 @@ namespace Ryujinx.HLE
int customVSyncInterval, int customVSyncInterval,
EnabledDirtyHack[] dirtyHacks = null) EnabledDirtyHack[] dirtyHacks = null)
{ {
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration; MemoryConfiguration = memoryConfiguration;
HostUIHandler = hostUIHandler;
SystemLanguage = systemLanguage; SystemLanguage = systemLanguage;
Region = region; Region = region;
VSyncMode = vSyncMode; VSyncMode = vSyncMode;
@@ -243,30 +259,5 @@ namespace Ryujinx.HLE
MultiplayerLdnServer = multiplayerLdnServer; MultiplayerLdnServer = multiplayerLdnServer;
Hacks = dirtyHacks ?? []; 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;
}
} }
} }

View File

@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
private readonly LanDiscovery _lanDiscovery; private readonly LanDiscovery _lanDiscovery;
public LdnMitmClient(HleConfiguration config) public LdnMitmClient(HLEConfiguration config)
{ {
UnicastIPAddressInformation localIpInterface = NetworkHelpers.GetLocalInterface(config.MultiplayerLanInterfaceId).Item2; UnicastIPAddressInformation localIpInterface = NetworkHelpers.GetLocalInterface(config.MultiplayerLanInterfaceId).Item2;

View File

@@ -51,13 +51,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
private string _passphrase; private string _passphrase;
private byte[] _gameVersion = new byte[0x10]; private byte[] _gameVersion = new byte[0x10];
private readonly HleConfiguration _config; private readonly HLEConfiguration _config;
public event EventHandler<NetworkChangeEventArgs> NetworkChange; public event EventHandler<NetworkChangeEventArgs> NetworkChange;
public ProxyConfig Config { get; private set; } 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()) if (ProxyHelpers.SupportsNoDelay())
{ {

View File

@@ -71,24 +71,16 @@ namespace Ryujinx.HLE.Loaders.Mods
int patchOffset = (int)offset; int patchOffset = (int)offset;
int patchSize = patch.Length; int patchSize = patch.Length;
if (patchOffset < protectedOffset) if (patchOffset < protectedOffset || patchOffset > memory.Length)
{ {
Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch protected memory ({patchOffset:x} is within protected boundary of {protectedOffset:x})."); continue; // Add warning?
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; patchOffset -= protectedOffset;
if (patchOffset + patchSize > memory.Length) if (patchOffset + patchSize > memory.Length)
{ {
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; // Add warning?
patchSize = memory.Length - patchOffset;
} }
Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}"); Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}");

View File

@@ -20,7 +20,7 @@ namespace Ryujinx.HLE
{ {
public static Switch Shared { get; private set; } public static Switch Shared { get; private set; }
public HleConfiguration Configuration { get; } public HLEConfiguration Configuration { get; }
public IHardwareDeviceDriver AudioDeviceDriver { get; } public IHardwareDeviceDriver AudioDeviceDriver { get; }
public MemoryBlock Memory { get; } public MemoryBlock Memory { get; }
public GpuContext Gpu { get; } public GpuContext Gpu { get; }
@@ -44,7 +44,7 @@ namespace Ryujinx.HLE
public DirtyHacks DirtyHacks { get; } public DirtyHacks DirtyHacks { get; }
public Switch(HleConfiguration configuration) public Switch(HLEConfiguration configuration)
{ {
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer); ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver); ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver);
@@ -94,20 +94,16 @@ namespace Ryujinx.HLE
Gpu.GPFifo.DispatchCalls(); Gpu.GPFifo.DispatchCalls();
} }
public int IncrementCustomVSyncInterval() public void IncrementCustomVSyncInterval()
{ {
CustomVSyncInterval += 1; CustomVSyncInterval += 1;
UpdateVSyncInterval(); UpdateVSyncInterval();
return CustomVSyncInterval;
} }
public int DecrementCustomVSyncInterval() public void DecrementCustomVSyncInterval()
{ {
CustomVSyncInterval -= 1; CustomVSyncInterval -= 1;
UpdateVSyncInterval(); UpdateVSyncInterval();
return CustomVSyncInterval;
} }
public void UpdateVSyncInterval() public void UpdateVSyncInterval()

View File

@@ -21,8 +21,8 @@ using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
@@ -902,19 +902,53 @@ namespace Ryujinx.Ava
_ => new OpenGLRenderer() _ => 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. // Initialize Configuration.
Device = new Switch(ConfigurationState.Instance.CreateHleConfiguration() MemoryConfiguration memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
.Configure(
VirtualFileSystem, Device = new Switch(new HLEConfiguration(
_viewModel.LibHacHorizonManager, VirtualFileSystem,
ContentManager, _viewModel.LibHacHorizonManager,
_accountManager, ContentManager,
_userChannelPersistence, _accountManager,
renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading), _userChannelPersistence,
InitializeAudio(), renderer,
_viewModel.UiHandler 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));
} }
private static IHardwareDeviceDriver InitializeAudio() private static IHardwareDeviceDriver InitializeAudio()
@@ -1148,9 +1182,6 @@ namespace Ryujinx.Ava
private void UpdateShaderCount() private void UpdateShaderCount()
{ {
if (_displayCount is 0 && _renderer.ProgramCount is 0)
return;
// If there is a mismatch between total program compile and previous count // If there is a mismatch between total program compile and previous count
// this means new shaders have been compiled and should be displayed. // this means new shaders have been compiled and should be displayed.
if (_renderer.ProgramCount != _previousCount) if (_renderer.ProgramCount != _previousCount)
@@ -1224,10 +1255,12 @@ namespace Ryujinx.Ava
VSyncModeToggle(); VSyncModeToggle();
break; break;
case KeyboardHotkeyState.CustomVSyncIntervalDecrement: case KeyboardHotkeyState.CustomVSyncIntervalDecrement:
_viewModel.CustomVSyncInterval = Device.DecrementCustomVSyncInterval(); Device.DecrementCustomVSyncInterval();
_viewModel.CustomVSyncInterval -= 1;
break; break;
case KeyboardHotkeyState.CustomVSyncIntervalIncrement: case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
_viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval(); Device.IncrementCustomVSyncInterval();
_viewModel.CustomVSyncInterval += 1;
break; break;
case KeyboardHotkeyState.Screenshot: case KeyboardHotkeyState.Screenshot:
ScreenshotRequested = true; ScreenshotRequested = true;

View File

@@ -440,7 +440,7 @@
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double> <x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
<x:Double x:Key="MenuItemHeight">26</x:Double> <x:Double x:Key="MenuItemHeight">26</x:Double>
<x:Double x:Key="TabItemMinHeight">28</x:Double> <x:Double x:Key="TabItemMinHeight">28</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">700</x:Double> <x:Double x:Key="ContentDialogMaxWidth">900</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double> <x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
</Styles.Resources> </Styles.Resources>
</Styles> </Styles>

View File

@@ -464,7 +464,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Abrir Pasta de Capturas de Tela", "pt_BR": "Abrir Pasta de Capturas de Tela",
"ru_RU": "Открыть папку со скриншотами", "ru_RU": "Открыть папку со скриншотами",
"sv_SE": "Öppna skärmbildsmappen", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Відкрити теку скріншотів", "uk_UA": "Відкрити теку скріншотів",
@@ -1564,7 +1564,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Desenvolvido por {0}", "pt_BR": "Desenvolvido por {0}",
"ru_RU": "Разработана {0}", "ru_RU": "Разработана {0}",
"sv_SE": "Utvecklat av {0}", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Розроблено: {0}", "uk_UA": "Розроблено: {0}",
@@ -1864,7 +1864,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Compatibilidade:", "pt_BR": "Compatibilidade:",
"ru_RU": "Совместимость:", "ru_RU": "Совместимость:",
"sv_SE": "Kompatibilitet:", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Сумісність:", "uk_UA": "Сумісність:",
@@ -1889,7 +1889,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "ID do Título:", "pt_BR": "ID do Título:",
"ru_RU": "ID приложения", "ru_RU": "ID приложения",
"sv_SE": "Titel-id:", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "ID гри:", "uk_UA": "ID гри:",
@@ -1914,7 +1914,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Jogos Hospedados: {0}", "pt_BR": "Jogos Hospedados: {0}",
"ru_RU": "Запущенно игр: {0}", "ru_RU": "Запущенно игр: {0}",
"sv_SE": "Värdskap för spel: {0}", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Розміщені ігри: {0}", "uk_UA": "Розміщені ігри: {0}",
@@ -1939,7 +1939,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Jogadores Online: {0}", "pt_BR": "Jogadores Online: {0}",
"ru_RU": "Игроков онлайн: {0}", "ru_RU": "Игроков онлайн: {0}",
"sv_SE": "Online-spelare: {0}", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Гравців онлайн: {0}", "uk_UA": "Гравців онлайн: {0}",
@@ -2289,7 +2289,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Limpar Cache PPTC", "pt_BR": "Limpar Cache PPTC",
"ru_RU": "Очистить кэш PPTC", "ru_RU": "Очистить кэш PPTC",
"sv_SE": "Rensa PPTC-cache", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Очистити кеш PPTC", "uk_UA": "Очистити кеш PPTC",
@@ -2314,7 +2314,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Apaga os arquivos de cache PPTC do aplicativo", "pt_BR": "Apaga os arquivos de cache PPTC do aplicativo",
"ru_RU": "Удаляет все файлы кэша PPTC для приложения", "ru_RU": "Удаляет все файлы кэша PPTC для приложения",
"sv_SE": "Tar bort alla PPTC-cachefiler för applikationen", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Видаляє всі файли кешу PPTC для застосунку", "uk_UA": "Видаляє всі файли кешу PPTC для застосунку",
@@ -2763,7 +2763,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Создать пользовательскую конфигурацию", "ru_RU": "",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -2788,7 +2788,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Изменить пользовательскую конфигурацию", "ru_RU": "",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -2863,7 +2863,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Отредактировать существующую независимую конфигурацию для выбранной игры.", "ru_RU": "",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -2889,7 +2889,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Mostrar Dados de Compatibilidade", "pt_BR": "Mostrar Dados de Compatibilidade",
"ru_RU": "Показать записи о совместимости", "ru_RU": "Показать записи о совместимости",
"sv_SE": "Visa kompatibilitetspost", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Iнформація про сумісність", "uk_UA": "Iнформація про сумісність",
@@ -2914,7 +2914,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.", "pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.",
"ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.", "ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.",
"sv_SE": "Visa valt spel i kompatibilitetslistan som du normalt sett kan komma åt via hjälpmenyn.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Показати цю гру в Списку Сумісності. Список сумісності також можна зайти в меню Довідки.", "uk_UA": "Показати цю гру в Списку Сумісності. Список сумісності також можна зайти в меню Довідки.",
@@ -2939,7 +2939,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Mostrar Informações do Jogo", "pt_BR": "Mostrar Informações do Jogo",
"ru_RU": "Показать информацию об игре", "ru_RU": "Показать информацию об игре",
"sv_SE": "Visa spelinformation", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Інформація про гру", "uk_UA": "Інформація про гру",
@@ -2964,7 +2964,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.", "pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.",
"ru_RU": "Показывать статистику и подробную информацию о выбранной игре.", "ru_RU": "Показывать статистику и подробную информацию о выбранной игре.",
"sv_SE": "Visa statistik och detaljer om det aktuella spelet.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Показати статистику та деталі обраної гри.", "uk_UA": "Показати статистику та деталі обраної гри.",
@@ -3514,7 +3514,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Verificar Atualizações:", "pt_BR": "Verificar Atualizações:",
"ru_RU": "Проверка наличия обновлений", "ru_RU": "Проверка наличия обновлений",
"sv_SE": "Leta efter uppdateringar:", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Перевірка оновлень:", "uk_UA": "Перевірка оновлень:",
@@ -3539,7 +3539,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Desligado", "pt_BR": "Desligado",
"ru_RU": "Отключить", "ru_RU": "Отключить",
"sv_SE": "Av", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Вимкнути", "uk_UA": "Вимкнути",
@@ -3564,7 +3564,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Ao Abrir", "pt_BR": "Ao Abrir",
"ru_RU": "При запуске", "ru_RU": "При запуске",
"sv_SE": "Fråga", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Запитувати щоразу", "uk_UA": "Запитувати щоразу",
@@ -3589,7 +3589,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "2° Plano", "pt_BR": "2° Plano",
"ru_RU": "В фоне", "ru_RU": "В фоне",
"sv_SE": "Bakgrund", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Оновлювати в фоні", "uk_UA": "Оновлювати в фоні",
@@ -3614,7 +3614,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Ao Perder o Foco:", "pt_BR": "Ao Perder o Foco:",
"ru_RU": "При выходе эмулятора из фокуса", "ru_RU": "При выходе эмулятора из фокуса",
"sv_SE": "När emulatorn tappar fokus:", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "При втраті фокуса емулятором:", "uk_UA": "При втраті фокуса емулятором:",
@@ -3639,7 +3639,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Não Fazer Nada", "pt_BR": "Não Fazer Nada",
"ru_RU": "Ничего не делать", "ru_RU": "Ничего не делать",
"sv_SE": "Gör ingenting", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Нічого не робити", "uk_UA": "Нічого не робити",
@@ -3664,7 +3664,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Bloquear Controles", "pt_BR": "Bloquear Controles",
"ru_RU": "Блокировать управление", "ru_RU": "Блокировать управление",
"sv_SE": "Blockera inmatning", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Блокувати введення", "uk_UA": "Блокувати введення",
@@ -3689,7 +3689,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Ficar Mudo", "pt_BR": "Ficar Mudo",
"ru_RU": "Отключить звук", "ru_RU": "Отключить звук",
"sv_SE": "Stäng av ljudet", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Вимкнути звук", "uk_UA": "Вимкнути звук",
@@ -3714,7 +3714,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Bloquear Controles & Ficar Mudo", "pt_BR": "Bloquear Controles & Ficar Mudo",
"ru_RU": "Блокировать управление и отключить звук", "ru_RU": "Блокировать управление и отключить звук",
"sv_SE": "Blockera inmatningar och stäng av ljudet", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Блокувати введення та Вимкнути звук", "uk_UA": "Блокувати введення та Вимкнути звук",
@@ -3739,7 +3739,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Pausar a Emulação", "pt_BR": "Pausar a Emulação",
"ru_RU": "Поставить паузу", "ru_RU": "Поставить паузу",
"sv_SE": "Pausa emuleringen", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Поставити на паузу", "uk_UA": "Поставити на паузу",
@@ -3814,7 +3814,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Desativar Controles Quando Estiver Fora de Foco", "pt_BR": "Desativar Controles Quando Estiver Fora de Foco",
"ru_RU": "Отключает управление при выходе из фокуса", "ru_RU": "Отключает управление при выходе из фокуса",
"sv_SE": "Inaktivera inmatning när fokus tappas", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4839,7 +4839,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Sincronizar com o Sistema PC", "pt_BR": "Sincronizar com o Sistema PC",
"ru_RU": "Соответствовать времени в системе", "ru_RU": "Соответствовать времени в системе",
"sv_SE": "Matcha systemtid", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -5013,7 +5013,7 @@
"no_NO": "Lyd Inn/Ut", "no_NO": "Lyd Inn/Ut",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Выход/Вход звука", "ru_RU": "",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -5264,7 +5264,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Ignorar Applet do Controlador", "pt_BR": "Ignorar Applet do Controlador",
"ru_RU": "Игнорировать апплет контроллера", "ru_RU": "Игнорировать апплет контроллера",
"sv_SE": "Ignorera kontroller-applet", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Ігнорувати Аплет Контролера", "uk_UA": "Ігнорувати Аплет Контролера",
@@ -6114,7 +6114,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Habilitar Logs da IU", "pt_BR": "Habilitar Logs da IU",
"ru_RU": "Включить журнал интерфейса", "ru_RU": "Включить журнал интерфейса",
"sv_SE": "Aktivera gränssnittsloggar", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Увімкнути журнали інтерфейсу", "uk_UA": "Увімкнути журнали інтерфейсу",
@@ -6514,7 +6514,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Redefinir Configurações", "pt_BR": "Redefinir Configurações",
"ru_RU": "Сбросить настройки", "ru_RU": "Сбросить настройки",
"sv_SE": "Nollställ inställningar", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Скинути налаштування", "uk_UA": "Скинути налаштування",
@@ -6539,7 +6539,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Quero redefinir minhas configurações.", "pt_BR": "Quero redefinir minhas configurações.",
"ru_RU": "Я хочу сбросить свои настройки.", "ru_RU": "Я хочу сбросить свои настройки.",
"sv_SE": "Jag vill nollställa mina inställningar.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Я хочу скинути налаштування.", "uk_UA": "Я хочу скинути налаштування.",
@@ -6563,7 +6563,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Ок", "ru_RU": "",
"sv_SE": "Ok", "sv_SE": "Ok",
"th_TH": "ตกลง", "th_TH": "ตกลง",
"tr_TR": "Tamam", "tr_TR": "Tamam",
@@ -7013,7 +7013,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "Pro Kontroler", "pl_PL": "Pro Kontroler",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Pro контроллер", "ru_RU": "",
"sv_SE": "", "sv_SE": "",
"th_TH": "โปรคอนโทรลเลอร์", "th_TH": "โปรคอนโทรลเลอร์",
"tr_TR": "Profesyonel Kumanda", "tr_TR": "Profesyonel Kumanda",
@@ -8514,7 +8514,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Velocidade do Arco-íris", "pt_BR": "Velocidade do Arco-íris",
"ru_RU": "Скорость переливания", "ru_RU": "Скорость переливания",
"sv_SE": "Regnbågshastighet", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -13814,7 +13814,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?", "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Вы уверены, что хотите продолжить?", "ru_RU": "Вы собираетесь удалить все данные PPTC из:\n\n{0}\n\nВы уверены, что хотите продолжить?",
"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?", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Ви збираєтесь видалити всі дані PPTC з:\n\n{0}\n\nБажаєте продовжити цю операцію?", "uk_UA": "Ви збираєтесь видалити всі дані PPTC з:\n\n{0}\n\nБажаєте продовжити цю операцію?",
@@ -16664,7 +16664,7 @@
"pl_PL": "", "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.", "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Оставьте выключенным, если не уверены.", "ru_RU": "Диалоговое окно апплета контроллера не будет отображаться, если геймпад отключен во время работы приложения.\n\nОставьте выключенным, если не уверены.",
"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.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.", "uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.",
@@ -17139,7 +17139,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.", "pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.",
"ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.", "ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.",
"sv_SE": "Skriver ut loggmeddelanden från Avalonia (användargränssnittet) i konsollen.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Виводити повідомлення журналу Avalonia (UI) в консоль", "uk_UA": "Виводити повідомлення журналу Avalonia (UI) в консоль",
@@ -17339,7 +17339,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Abre a pasta de capturas de tela do Ryujinx", "pt_BR": "Abre a pasta de capturas de tela do Ryujinx",
"ru_RU": "Открывает папку скриншотов Ryujinx", "ru_RU": "Открывает папку скриншотов Ryujinx",
"sv_SE": "Öppna Ryujinx skärmbildsmapp", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx", "uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx",
@@ -18089,7 +18089,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Atualização Disponível!", "pt_BR": "Atualização Disponível!",
"ru_RU": "Доступно обновление!", "ru_RU": "Доступно обновление!",
"sv_SE": "Uppdatering finns tillgänglig!", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Доступне оновлення!", "uk_UA": "Доступне оновлення!",
@@ -20013,7 +20013,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Амибо", "ru_RU": "",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -23822,31 +23822,6 @@
"zh_TW": "上次更新時間: {0}" "zh_TW": "上次更新時間: {0}"
} }
}, },
{
"ID": "CompatibilityListTitle",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Compatibility List - {0} entries",
"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": "CompatibilityListWarning", "ID": "CompatibilityListWarning",
"Translations": { "Translations": {
@@ -23897,31 +23872,6 @@
"zh_TW": "搜尋相容性列表紀錄..." "zh_TW": "搜尋相容性列表紀錄..."
} }
}, },
{
"ID": "CompatibilityListSearchBoxWatermarkWithCount",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Search {0} compatibility entries...",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "Søk i {0} kompatibilitetsoppføringer...",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "CompatibilityListOpen", "ID": "CompatibilityListOpen",
"Translations": { "Translations": {
@@ -24114,7 +24064,7 @@
"pl_PL": "", "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.", "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": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.", "ru_RU": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.",
"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.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Запускається та оптимально працює (без збоїв або графічних багів) на середньостатистичному комп'ютері.", "uk_UA": "Запускається та оптимально працює (без збоїв або графічних багів) на середньостатистичному комп'ютері.",
@@ -24139,7 +24089,7 @@
"pl_PL": "", "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.", "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, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.", "ru_RU": "Запускается и работает, но возникает одна или несколько из следующих проблем: сбои, взаимоблокировки, ошибки GPU, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.",
"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.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Запускається, але в грі на вас чекатимуть одна або декілька наступних проблем: збої, зависання, графічні баги, спотворений звук або ж гра загалом працюватиме надто повільно. Можливо, її все ще можна пройти, але досвід буде не найкращим.", "uk_UA": "Запускається, але в грі на вас чекатимуть одна або декілька наступних проблем: збої, зависання, графічні баги, спотворений звук або ж гра загалом працюватиме надто повільно. Можливо, її все ще можна пройти, але досвід буде не найкращим.",
@@ -24164,7 +24114,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.", "pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.",
"ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.", "ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.",
"sv_SE": "Startar upp och går förbi titelskärmen men tar sig inte in i huvudspelet.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Запускається та проходить початковий екран, але пограти не вийде.", "uk_UA": "Запускається та проходить початковий екран, але пограти не вийде.",
@@ -24189,7 +24139,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Inizializa, mas não passa da tela de título.", "pt_BR": "Inizializa, mas não passa da tela de título.",
"ru_RU": "Загружается, но не проходит дальше титульного экрана.", "ru_RU": "Загружается, но не проходит дальше титульного экрана.",
"sv_SE": "Startar upp men tar sig inte förbi titelskärmen.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Запускається, але не відображає навіть початкового екрану.", "uk_UA": "Запускається, але не відображає навіть початкового екрану.",
@@ -24214,7 +24164,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Não inicializa ou não mostra sinais de atividade.", "pt_BR": "Não inicializa ou não mostra sinais de atividade.",
"ru_RU": "Не запускается или не подаёт признаков жизни.", "ru_RU": "Не запускается или не подаёт признаков жизни.",
"sv_SE": "Startar inte upp eller visar någon form av aktivitet.", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Взагалі не запускається.", "uk_UA": "Взагалі не запускається.",
@@ -24314,7 +24264,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Imagem da Presença do Discord", "pt_BR": "Imagem da Presença do Discord",
"ru_RU": "Изображение для статуса активности", "ru_RU": "Изображение для статуса активности",
"sv_SE": "Bild för Rich Presence", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Зображення картки активності Discord", "uk_UA": "Зображення картки активності Discord",
@@ -24339,7 +24289,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Presença Dinâmica do Discord", "pt_BR": "Presença Dinâmica do Discord",
"ru_RU": "Динамический статус активности", "ru_RU": "Динамический статус активности",
"sv_SE": "Dynamisk Rich Presence", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Динамічна картка активності Discord", "uk_UA": "Динамічна картка активності Discord",

View File

@@ -13,10 +13,10 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@@ -216,7 +216,11 @@ namespace Ryujinx.Ava.Common
return; return;
} }
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, ConfigurationState.Instance.System.IntegrityCheckLevel, programIndex, out _); IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
if (updatePatchNca is not null) if (updatePatchNca is not null)
{ {
patchNca = updatePatchNca; patchNca = updatePatchNca;

View File

@@ -1,7 +1,6 @@
using Gommon; using Gommon;
using Ryujinx.Ava.Systems;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System; using System;
@@ -56,8 +55,6 @@ namespace Ryujinx.Ava.Common.Locale
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.CompatibilityListSearchBoxWatermarkWithCount, CompatibilityCsv.Entries.Length);
SetDynamicValues(LocaleKeys.CompatibilityListTitle, CompatibilityCsv.Entries.Length);
} }
public string this[LocaleKeys key] public string this[LocaleKeys key]

View File

@@ -1,4 +1,4 @@
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;

View File

@@ -1,9 +1,9 @@
using DiscordRPC; using DiscordRPC;
using Gommon; using Gommon;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.PlayReport; using Ryujinx.Ava.Utilities.PlayReport;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;

View File

@@ -2,7 +2,7 @@ using DiscordRPC;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Ava; using Ryujinx.Ava;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
@@ -312,42 +312,49 @@ namespace Ryujinx.Headless
return new OpenGLRenderer(); return new OpenGLRenderer();
} }
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) => private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options)
new( {
new HleConfiguration( BackendThreading threadingMode = options.BackendThreading;
options.DramSize,
options.SystemLanguage, bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
options.SystemRegion,
options.VSyncMode, if (threadedGAL)
!options.DisableDockedMode, {
!options.DisablePTC, renderer = new ThreadedRenderer(renderer);
options.EnableInternetAccess, }
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode, HLEConfiguration configuration = new(_virtualFileSystem,
options.SystemTimeOffset, _libHacHorizonManager,
options.SystemTimeZone, _contentManager,
options.MemoryManagerMode, _accountManager,
options.IgnoreMissingServices, _userChannelPersistence,
options.AspectRatio, renderer,
options.AudioVolume, new SDL2HardwareDeviceDriver(),
options.UseHypervisor ?? true, options.DramSize,
options.MultiplayerLanInterfaceId, window,
Common.Configuration.Multiplayer.MultiplayerMode.Disabled, options.SystemLanguage,
false, options.SystemRegion,
string.Empty, options.VSyncMode,
string.Empty, !options.DisableDockedMode,
options.CustomVSyncInterval !options.DisablePTC,
) options.EnableInternetAccess,
.Configure( !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
_virtualFileSystem, options.FsGlobalAccessLogMode,
_libHacHorizonManager, options.SystemTimeOffset,
_contentManager, options.SystemTimeZone,
_accountManager, options.MemoryManagerMode,
_userChannelPersistence, options.IgnoreMissingServices,
renderer.TryMakeThreaded(options.BackendThreading), options.AspectRatio,
new SDL2HardwareDeviceDriver(), options.AudioVolume,
window options.UseHypervisor ?? true,
) options.MultiplayerLanInterfaceId,
); Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
false,
string.Empty,
string.Empty,
options.CustomVSyncInterval);
return new Switch(configuration);
}
} }
} }

View File

@@ -1,7 +1,7 @@
using CommandLine; using CommandLine;
using Gommon; using Gommon;
using Ryujinx.Ava; using Ryujinx.Ava;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;

View File

@@ -1,7 +1,6 @@
using CommandLine; using CommandLine;
using Gommon; using Gommon;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE; using Ryujinx.HLE;
@@ -38,7 +37,7 @@ namespace Ryujinx.Headless
EnableInternetAccess = configurationState.System.EnableInternetAccess; EnableInternetAccess = configurationState.System.EnableInternetAccess;
if (NeedsOverride(nameof(DisableFsIntegrityChecks))) if (NeedsOverride(nameof(DisableFsIntegrityChecks)))
DisableFsIntegrityChecks = !configurationState.System.EnableFsIntegrityChecks; DisableFsIntegrityChecks = configurationState.System.EnableFsIntegrityChecks;
if (NeedsOverride(nameof(FsGlobalAccessLogMode))) if (NeedsOverride(nameof(FsGlobalAccessLogMode)))
FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode; FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode;
@@ -59,10 +58,10 @@ namespace Ryujinx.Headless
DisableDockedMode = !configurationState.System.EnableDockedMode; DisableDockedMode = !configurationState.System.EnableDockedMode;
if (NeedsOverride(nameof(SystemLanguage))) if (NeedsOverride(nameof(SystemLanguage)))
SystemLanguage = configurationState.System.Language.Value.ToHLE(); SystemLanguage = (SystemLanguage)(int)configurationState.System.Language.Value;
if (NeedsOverride(nameof(SystemRegion))) if (NeedsOverride(nameof(SystemRegion)))
SystemRegion = configurationState.System.Region.Value.ToHLE(); SystemRegion = (RegionCode)(int)configurationState.System.Region.Value;
if (NeedsOverride(nameof(SystemTimeZone))) if (NeedsOverride(nameof(SystemTimeZone)))
SystemTimeZone = configurationState.System.TimeZone; SystemTimeZone = configurationState.System.TimeZone;

View File

@@ -8,8 +8,7 @@ using Projektanker.Icons.Avalonia.MaterialDesign;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Utilities.SystemInfo; using Ryujinx.Ava.Utilities.SystemInfo;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
@@ -283,16 +282,16 @@ namespace Ryujinx.Ava
// Check if region was overridden. // Check if region was overridden.
if (CommandLineState.OverrideSystemRegion is not null) if (CommandLineState.OverrideSystemRegion is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out HLE.HOS.SystemState.RegionCode result)) if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result))
{ {
ConfigurationState.Instance.System.Region.Value = result.ToUI(); ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result;
} }
//Check if language was overridden. //Check if language was overridden.
if (CommandLineState.OverrideSystemLanguage is not null) if (CommandLineState.OverrideSystemLanguage is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out HLE.HOS.SystemState.SystemLanguage result)) if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result))
{ {
ConfigurationState.Instance.System.Language.Value = result.ToUI(); ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result;
} }
// Check if hardware-acceleration was overridden. // Check if hardware-acceleration was overridden.

View File

@@ -17,9 +17,4 @@
<sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" /> <sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" />
<StyleInclude Source="/Assets/Styles/Styles.xaml" /> <StyleInclude Source="/Assets/Styles/Styles.xaml" />
</Application.Styles> </Application.Styles>
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="About Ryujinx" Click="AboutRyujinx_OnClick" />
</NativeMenu>
</NativeMenu.Menu>
</Application> </Application>

View File

@@ -12,7 +12,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
@@ -147,10 +147,5 @@ namespace Ryujinx.Ava
Current is RyujinxApp { PlatformSettings: not null } app Current is RyujinxApp { PlatformSettings: not null } app
? ConvertThemeVariant(app.PlatformSettings.GetColorValues().ThemeVariant) ? ConvertThemeVariant(app.PlatformSettings.GetColorValues().ThemeVariant)
: ThemeVariant.Default; : ThemeVariant.Default;
private async void AboutRyujinx_OnClick(object sender, EventArgs e)
{
await AboutWindow.Show();
}
} }
} }

View File

@@ -1,26 +0,0 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Systems.Configuration.System
{
[JsonConverter(typeof(TypedStringEnumConverter<Region>))]
public enum Region
{
Japan,
USA,
Europe,
Australia,
China,
Korea,
Taiwan,
}
public static class RegionEnumHelper
{
public static Region ToUI(this HLE.HOS.SystemState.RegionCode hleRegion)
=> (Region)hleRegion;
public static HLE.HOS.SystemState.RegionCode ToHLE(this Region uiRegion)
=> (HLE.HOS.SystemState.RegionCode)uiRegion;
}
}

View File

@@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets;

View File

@@ -17,8 +17,12 @@
<viewModels:ProfileSelectorDialogViewModel /> <viewModels:ProfileSelectorDialogViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border <Border
CornerRadius="5" CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"

View File

@@ -9,14 +9,17 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile; using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
namespace Ryujinx.Ava.UI.Applet namespace Ryujinx.Ava.UI.Applet
{ {
public partial class ProfileSelectorDialog : RyujinxControl<ProfileSelectorDialogViewModel> public partial class ProfileSelectorDialog : UserControl
{ {
public ProfileSelectorDialogViewModel ViewModel { get; set; }
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel) public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
{ {
DataContext = ViewModel = viewModel; DataContext = ViewModel = viewModel;

View File

@@ -9,10 +9,10 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Views.Misc;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
@@ -26,7 +26,6 @@ namespace Ryujinx.Ava.UI.Controls
{ {
public class ApplicationContextMenu : MenuFlyout public class ApplicationContextMenu : MenuFlyout
{ {
public ApplicationContextMenu() public ApplicationContextMenu()
{ {
InitializeComponent(); InitializeComponent();
@@ -407,7 +406,7 @@ namespace Ryujinx.Ava.UI.Controls
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args) public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
{ {
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await CompatibilityListWindow.Show(viewModel.SelectedApplication.IdString); await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
} }
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args) public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)

View File

@@ -7,7 +7,7 @@
xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Views.Misc.ApplicationDataView" x:Class="Ryujinx.Ava.UI.Controls.ApplicationDataView"
x:DataType="viewModels:ApplicationDataViewModel"> x:DataType="viewModels:ApplicationDataViewModel">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Margin="0" <Image Margin="0"

View File

@@ -1,20 +1,20 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Styling;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Compat;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Misc namespace Ryujinx.Ava.UI.Controls
{ {
public partial class ApplicationDataView : RyujinxControl<ApplicationDataViewModel> public partial class ApplicationDataView : UserControl
{ {
public static async Task Show(ApplicationData appData) public static async Task Show(ApplicationData appData)
{ {
@@ -25,10 +25,20 @@ namespace Ryujinx.Ava.UI.Views.Misc
SecondaryButtonText = string.Empty, SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
MinWidth = 256, MinWidth = 256,
Content = new ApplicationDataView { ViewModel = new ApplicationDataViewModel(appData) } Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) }
}; };
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles(160, HorizontalAlignment.Center)); Style closeButton = new(x => x.Name("CloseButton"));
closeButton.Setters.Add(new Setter(WidthProperty, 160d));
Style closeButtonParent = new(x => x.Name("CommandSpace"));
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty,
Avalonia.Layout.HorizontalAlignment.Center));
contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent);
await ContentDialogHelper.ShowAsync(contentDialog);
} }
public ApplicationDataView() public ApplicationDataView()
@@ -44,18 +54,21 @@ namespace Ryujinx.Ava.UI.Views.Misc
if (RyujinxApp.AppLifetime.Windows.TryGetFirst(x => x is ContentDialogOverlayWindow, out Window window)) if (RyujinxApp.AppLifetime.Windows.TryGetFirst(x => x is ContentDialogOverlayWindow, out Window window))
window.Close(ContentDialogResult.None); window.Close(ContentDialogResult.None);
await CompatibilityListWindow.Show((string)playabilityLabel.Tag); await CompatibilityList.Show((string)playabilityLabel.Tag);
} }
private async void IdString_OnClick(object sender, RoutedEventArgs e) private async void IdString_OnClick(object sender, RoutedEventArgs e)
{ {
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock idText }) if (sender is not Button { Content: TextBlock idText })
return; return;
if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard)) if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard))
return; return;
ApplicationData appData = RyujinxApp.MainWindow.ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text); ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
if (appData is null) if (appData is null)
return; return;

View File

@@ -1,5 +1,5 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Misc.ApplicationGridView" x:Class="Ryujinx.Ava.UI.Controls.ApplicationGridView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
@@ -14,7 +14,10 @@
mc:Ignorable="d" mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<Grid RowDefinitions="*"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox <ListBox
Grid.Row="0" Grid.Row="0"
Padding="8" Padding="8"
@@ -54,7 +57,11 @@
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}" Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="4"> CornerRadius="4">
<Grid RowDefinitions="Auto,Auto"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image <Image
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -1,15 +1,13 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System; using System;
namespace Ryujinx.Ava.UI.Views.Misc namespace Ryujinx.Ava.UI.Controls
{ {
public partial class ApplicationGridView : RyujinxControl<MainWindowViewModel> public partial class ApplicationGridView : UserControl
{ {
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);

View File

@@ -1,5 +1,5 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Misc.ApplicationListView" x:Class="Ryujinx.Ava.UI.Controls.ApplicationListView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -13,7 +13,10 @@
mc:Ignorable="d" mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<Grid RowDefinitions="*"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox <ListBox
Name="GameListBox" Name="GameListBox"
Grid.Row="0" Grid.Row="0"

View File

@@ -2,17 +2,16 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities.Compat;
using System; using System;
using System.Linq; using System.Linq;
namespace Ryujinx.Ava.UI.Views.Misc namespace Ryujinx.Ava.UI.Controls
{ {
public partial class ApplicationListView : RyujinxControl<MainWindowViewModel> public partial class ApplicationListView : UserControl
{ {
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
@@ -33,21 +32,27 @@ namespace Ryujinx.Ava.UI.Views.Misc
private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e) private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
{ {
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock playabilityLabel }) if (sender is not Button { Content: TextBlock playabilityLabel })
return; return;
await CompatibilityListWindow.Show((string)playabilityLabel.Tag); await CompatibilityList.Show((string)playabilityLabel.Tag);
} }
private async void IdString_OnClick(object sender, RoutedEventArgs e) private async void IdString_OnClick(object sender, RoutedEventArgs e)
{ {
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock idText }) if (sender is not Button { Content: TextBlock idText })
return; return;
if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard)) if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard))
return; return;
ApplicationData appData = ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text); ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
if (appData is null) if (appData is null)
return; return;

View File

@@ -7,7 +7,7 @@
xmlns:models="using:Ryujinx.Ava.Common.Models" xmlns:models="using:Ryujinx.Ava.Common.Models"
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Views.Misc.DlcSelectView" x:Class="Ryujinx.Ava.UI.Controls.DlcSelectView"
x:DataType="viewModels:DlcSelectViewModel"> x:DataType="viewModels:DlcSelectViewModel">
<Grid RowDefinitions="*,Auto,*"> <Grid RowDefinitions="*,Auto,*">
<TextBlock <TextBlock

View File

@@ -3,15 +3,14 @@ using Avalonia.Styling;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Misc namespace Ryujinx.Ava.UI.Controls
{ {
public partial class DlcSelectView : RyujinxControl<DlcSelectViewModel> public partial class DlcSelectView : UserControl
{ {
public DlcSelectView() public DlcSelectView()
{ {
@@ -29,10 +28,20 @@ namespace Ryujinx.Ava.UI.Views.Misc
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue], PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
SecondaryButtonText = string.Empty, SecondaryButtonText = string.Empty,
CloseButtonText = string.Empty, CloseButtonText = string.Empty,
Content = new DlcSelectView { ViewModel = viewModel } Content = new DlcSelectView { DataContext = viewModel }
}; };
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles()); 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);
return viewModel.SelectedDlc; return viewModel.SelectedDlc;
} }

View File

@@ -23,12 +23,13 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class NavigationDialogHost : RyujinxControl<UserProfileViewModel> public partial class NavigationDialogHost : UserControl
{ {
public AccountManager AccountManager { get; } public AccountManager AccountManager { get; }
public ContentManager ContentManager { get; } public ContentManager ContentManager { get; }
public VirtualFileSystem VirtualFileSystem { get; } public VirtualFileSystem VirtualFileSystem { get; }
public HorizonClient HorizonClient { get; } public HorizonClient HorizonClient { get; }
public UserProfileViewModel ViewModel { get; set; }
public NavigationDialogHost() public NavigationDialogHost()
{ {

View File

@@ -1,24 +0,0 @@
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;
}
}
}

View File

@@ -1,5 +1,5 @@
<Window <Window
x:Class="Ryujinx.Ava.UI.Windows.UpdateWaitWindow" x:Class="Ryujinx.Ava.UI.Controls.UpdateWaitWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -13,7 +13,15 @@
<Grid <Grid
Margin="20" Margin="20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto"> VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image <Image
Grid.Row="1" Grid.Row="1"
Height="70" Height="70"

View File

@@ -1,7 +1,8 @@
using Avalonia.Controls; using Avalonia.Controls;
using Ryujinx.Ava.UI.Windows;
using System.Threading; using System.Threading;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Controls
{ {
public partial class UpdateWaitWindow : StyleableWindow public partial class UpdateWaitWindow : StyleableWindow
{ {

View File

@@ -1,5 +1,5 @@
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
namespace Ryujinx.Ava.UI.Helpers namespace Ryujinx.Ava.UI.Helpers
{ {

View File

@@ -4,7 +4,6 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.Core; using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
@@ -22,23 +21,6 @@ namespace Ryujinx.Ava.UI.Helpers
private static bool _isChoiceDialogOpen; private static bool _isChoiceDialogOpen;
private static ContentDialogOverlayWindow _contentDialogOverlayWindow; 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( private async static Task<UserResult> ShowContentDialog(
string title, string title,
object content, object content,
@@ -58,19 +40,19 @@ namespace Ryujinx.Ava.UI.Helpers
SecondaryButtonText = secondaryButton, SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton, CloseButtonText = closeButton,
Content = content, Content = content,
PrimaryButtonCommand = Commands.Create(() => PrimaryButtonCommand = MiniCommand.Create(() =>
{ {
result = primaryButtonResult; result = primaryButtonResult;
}) })
}; };
contentDialog.SecondaryButtonCommand = Commands.Create(() => contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{ {
result = UserResult.No; result = UserResult.No;
contentDialog.PrimaryButtonClick -= deferCloseAction; contentDialog.PrimaryButtonClick -= deferCloseAction;
}); });
contentDialog.CloseButtonCommand = Commands.Create(() => contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{ {
result = UserResult.Cancel; result = UserResult.Cancel;
contentDialog.PrimaryButtonClick -= deferCloseAction; contentDialog.PrimaryButtonClick -= deferCloseAction;

View File

@@ -2,7 +2,7 @@ using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Gommon; using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;

View File

@@ -1,7 +1,7 @@
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Utilities; using Avalonia.Utilities;
using Gommon; using Gommon;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Text; using System.Text;

View File

@@ -0,0 +1,72 @@
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;
}
}

View File

@@ -1,4 +1,4 @@
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,4 +1,4 @@
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,260 +0,0 @@
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);
}
}
}

View File

@@ -3,7 +3,7 @@ using LibHac.Fs;
using LibHac.Ncm; using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using System.IO; using System.IO;
using System.Linq; using System.Linq;

View File

@@ -1,7 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Platform; using Avalonia.Platform;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using SPB.Graphics; using SPB.Graphics;

View File

@@ -1,5 +1,5 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;

View File

@@ -1,8 +1,10 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using System; using System;
namespace Ryujinx.Ava.UI.Renderer namespace Ryujinx.Ava.UI.Renderer
@@ -36,6 +38,32 @@ namespace Ryujinx.Ava.UI.Renderer
EmbeddedWindowOpenGL => GraphicsBackend.OpenGl, EmbeddedWindowOpenGL => GraphicsBackend.OpenGl,
_ => throw new NotImplementedException() _ => 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() private void Initialize()

View File

@@ -5,7 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Gommon; using Gommon;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using System; using System;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels

View File

@@ -1,4 +1,4 @@
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System; using System;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels

View File

@@ -1,7 +1,7 @@
using Gommon; using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.PlayReport; using Ryujinx.Ava.Utilities.PlayReport;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System.Linq; using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels

View File

@@ -7,7 +7,7 @@ using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;

View File

@@ -1,9 +1,5 @@
using Avalonia.Svg.Skia; using Avalonia.Svg.Skia;
using CommunityToolkit.Mvvm.ComponentModel; 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.Models.Input;
using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Ava.UI.Views.Input;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -14,30 +10,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
public partial class ControllerInputViewModel : BaseModel public partial class ControllerInputViewModel : BaseModel
{ {
private GamepadInputConfig _config; [ObservableProperty] 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; private bool _isLeft;
public bool IsLeft public bool IsLeft
{ {
@@ -63,15 +37,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
public bool HasSides => IsLeft ^ IsRight; public bool HasSides => IsLeft ^ IsRight;
[ObservableProperty] private SvgImage _image; [ObservableProperty] private SvgImage _image;
public InputViewModel ParentModel { get; } public InputViewModel ParentModel { get; }
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer) public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
{ {
ParentModel = model; ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged; model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged(); OnParentModelChanged();
config.PropertyChanged += (_, args) => config.PropertyChanged += (_, args) =>

View File

@@ -10,7 +10,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
@@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private int _controller; private int _controller;
private string _controllerImage; private string _controllerImage;
private int _device; private int _device;
private object _configViewModel; [ObservableProperty] private object _configViewModel;
[ObservableProperty] private string _profileName; [ObservableProperty] private string _profileName;
private bool _isLoaded; private bool _isLoaded;
@@ -74,7 +74,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed)); OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
} }
} }
public StickVisualizer VisualStick { get; private set; }
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; } public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; } public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
@@ -95,19 +94,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsModified { get; set; } public bool IsModified { get; set; }
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;
public object ConfigViewModel
{
get => _configViewModel;
set
{
_configViewModel = value;
VisualStick.UpdateConfig(value);
OnPropertyChanged();
}
}
public PlayerIndex PlayerIdChoose public PlayerIndex PlayerIdChoose
{ {
get => _playerIdChoose; get => _playerIdChoose;
@@ -283,7 +269,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
Devices = []; Devices = [];
ProfilesList = []; ProfilesList = [];
DeviceList = []; DeviceList = [];
VisualStick = new StickVisualizer(this);
ControllerImage = ProControllerResource; ControllerImage = ProControllerResource;
@@ -304,12 +289,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (Config is StandardKeyboardInputConfig keyboardInputConfig) if (Config is StandardKeyboardInputConfig keyboardInputConfig)
{ {
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick); ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig));
} }
if (Config is StandardControllerInputConfig controllerInputConfig) if (Config is StandardControllerInputConfig controllerInputConfig)
{ {
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick); ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig));
} }
} }
@@ -908,8 +893,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
VisualStick.Dispose();
SelectedGamepad?.Dispose(); SelectedGamepad?.Dispose();
AvaloniaKeyboardDriver.Dispose(); AvaloniaKeyboardDriver.Dispose();

View File

@@ -6,29 +6,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
public partial class KeyboardInputViewModel : BaseModel public partial class KeyboardInputViewModel : BaseModel
{ {
private KeyboardInputConfig _config; [ObservableProperty] 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; private bool _isLeft;
public bool IsLeft public bool IsLeft
@@ -60,10 +38,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public readonly InputViewModel ParentModel; public readonly InputViewModel ParentModel;
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config, StickVisualizer visualizer) public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config)
{ {
ParentModel = model; ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged; model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged(); OnParentModelChanged();
Config = config; Config = config;

View File

@@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Humanizer; using Humanizer;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using System.Globalization; using System.Globalization;
namespace Ryujinx.Ava.UI.ViewModels.Input namespace Ryujinx.Ava.UI.ViewModels.Input

View File

@@ -23,8 +23,8 @@ using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Models.Generic; using Ryujinx.Ava.UI.Models.Generic;
using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;

View File

@@ -7,7 +7,7 @@ using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Gommon; using Gommon;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {

View File

@@ -12,9 +12,9 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration.System; using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.GraphicsDriver;

View File

@@ -6,7 +6,7 @@ using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;

View File

@@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class UserFirmwareAvatarSelectorViewModel : BaseModel internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel
{ {
private static readonly Dictionary<string, byte[]> _avatarStore = new(); private static readonly Dictionary<string, byte[]> _avatarStore = new();

View File

@@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class UserProfileImageSelectorViewModel : BaseModel internal partial class UserProfileImageSelectorViewModel : BaseModel
{ {
[ObservableProperty] private bool _firmwareFound; [ObservableProperty] private bool _firmwareFound;
} }

View File

@@ -6,7 +6,7 @@ using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;

View File

@@ -34,7 +34,12 @@
<!-- Button / JoyStick Settings --> <!-- Button / JoyStick Settings -->
<Grid <Grid
Name="SettingButtons" Name="SettingButtons"
MinHeight="450" ColumnDefinitions="Auto,*,Auto"> MinHeight="450">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Left Controls --> <!-- Left Controls -->
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -49,7 +54,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
@@ -303,99 +316,17 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<!-- Controller Picture --> <!-- Controller Picture -->
<Image
Margin="0,10"
MaxHeight="300"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="{Binding Image}" />
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
Margin="0,0, 0, 5"
MinHeight="90"> 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 <StackPanel
Margin="8" Margin="8"
Orientation="Vertical"> Orientation="Vertical">
@@ -414,8 +345,8 @@
Minimum="0" Minimum="0"
Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" /> Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" />
<TextBlock <TextBlock
Width="25" Width="25"
Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" /> Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -497,7 +428,7 @@
</Border> </Border>
<!-- Motion, Rumble, LED --> <!-- Motion, Rumble, LED -->
<StackPanel <StackPanel
Margin="0,5,0,0" Margin="0,10,0,0"
Spacing="5" Spacing="5"
Orientation="Vertical" Orientation="Vertical"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
@@ -507,7 +438,11 @@
CornerRadius="5" CornerRadius="5"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Grid ColumnDefinitions="*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<CheckBox <CheckBox
Margin="10" Margin="10"
MinWidth="0" MinWidth="0"
@@ -529,7 +464,11 @@
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,-1,0,0"> Margin="0,-1,0,0">
<Grid ColumnDefinitions="*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<CheckBox <CheckBox
Margin="10" Margin="10"
MinWidth="0" MinWidth="0"
@@ -551,7 +490,11 @@
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,-1,0,0"> Margin="0,-1,0,0">
<Grid IsVisible="{Binding ParentModel.HasLed}" ColumnDefinitions="*,Auto"> <Grid IsVisible="{Binding ParentModel.HasLed}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<CheckBox <CheckBox
Margin="10, 10, 5, 10" Margin="10, 10, 5, 10"
MinWidth="0" MinWidth="0"
@@ -583,7 +526,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"

View File

@@ -4,7 +4,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
@@ -15,7 +14,7 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class ControllerInputView : RyujinxControl<ControllerInputViewModel> public partial class ControllerInputView : UserControl
{ {
private ButtonKeyAssigner _currentAssigner; private ButtonKeyAssigner _currentAssigner;
@@ -218,12 +217,20 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed -= MouseClick; PointerPressed -= MouseClick;
} }
private IButtonAssigner CreateButtonAssigner(bool forStick) => private IButtonAssigner CreateButtonAssigner(bool forStick)
new GamepadButtonAssigner( {
ViewModel.ParentModel.SelectedGamepad, IButtonAssigner assigner;
(ViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel;
assigner = new GamepadButtonAssigner(
controllerInputViewModel.ParentModel.SelectedGamepad,
(controllerInputViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
forStick); forStick);
return assigner;
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);

View File

@@ -35,13 +35,22 @@
Margin="0 0 0 5" Margin="0 0 0 5"
Orientation="Vertical" Orientation="Vertical"
Spacing="5"> Spacing="5">
<Grid ColumnDefinitions="*,10,*"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Player Selection --> <!-- Player Selection -->
<Grid <Grid
Grid.Column="0" Grid.Column="0"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*"> VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90" Width="90"
@@ -68,7 +77,14 @@
Grid.Column="2" Grid.Column="2"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*,Auto,Auto,Auto"> VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90" Width="90"
@@ -123,12 +139,22 @@
</Grid> </Grid>
</Grid> </Grid>
<Separator /> <Separator />
<Grid ColumnDefinitions="*,10,*"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Input Device --> <!-- Input Device -->
<Grid <Grid
Grid.Column="0" Grid.Column="0"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*,Auto"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"
Margin="5,0,10,0" Margin="5,0,10,0"
@@ -160,7 +186,11 @@
Grid.Column="2" Grid.Column="2"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*"> VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90" Width="90"

View File

@@ -1,19 +1,19 @@
using Avalonia.Controls; using Avalonia.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class InputView : RyujinxControl<InputViewModel> public partial class InputView : UserControl
{ {
private bool _dialogOpen; private bool _dialogOpen;
private InputViewModel ViewModel { get; set; }
public InputView() public InputView()
{ {
ViewModel = new InputViewModel(this); DataContext = ViewModel = new InputViewModel(this);
InitializeComponent(); InitializeComponent();
} }

View File

@@ -32,7 +32,12 @@
<!-- Button / JoyStick Settings --> <!-- Button / JoyStick Settings -->
<Grid <Grid
Name="SettingButtons" Name="SettingButtons"
MinHeight="450" ColumnDefinitions="Auto,*,Auto"> MinHeight="450">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Left Controls --> <!-- Left Controls -->
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -47,7 +52,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
@@ -296,79 +309,12 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<!-- Controller Picture --> <!-- Controller Picture -->
<Border <Image
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Margin="0,10" Margin="0,10"
MinHeight="90"> MaxHeight="300"
<StackPanel HorizontalAlignment="Stretch"
Margin="10" VerticalAlignment="Stretch"
Orientation="Horizontal" Source="{Binding Image}" />
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 <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
@@ -467,7 +413,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"

View File

@@ -4,7 +4,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input; using Ryujinx.Input;
@@ -14,7 +13,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class KeyboardInputView : RyujinxControl<KeyboardInputViewModel> public partial class KeyboardInputView : UserControl
{ {
private ButtonKeyAssigner _currentAssigner; private ButtonKeyAssigner _currentAssigner;
@@ -61,103 +60,106 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed += MouseClick; PointerPressed += MouseClick;
IKeyboard keyboard = if (DataContext is not KeyboardInputViewModel viewModel)
(IKeyboard)ViewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. return;
IButtonAssigner assigner =
new KeyboardKeyAssigner((IKeyboard)ViewModel.ParentModel.SelectedGamepad);
_currentAssigner.ButtonAssigned += (_, be) => IKeyboard keyboard =
(IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
IButtonAssigner assigner =
new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad);
_currentAssigner.ButtonAssigned += (_, e) =>
{ {
if (be.ButtonValue.HasValue) if (e.ButtonValue.HasValue)
{ {
Button buttonValue = be.ButtonValue.Value; Button buttonValue = e.ButtonValue.Value;
ViewModel.ParentModel.IsModified = true; viewModel.ParentModel.IsModified = true;
switch (button.Name) switch (button.Name)
{ {
case "ButtonZl": case "ButtonZl":
ViewModel.Config.ButtonZl = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
break; break;
case "ButtonL": case "ButtonL":
ViewModel.Config.ButtonL = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
break; break;
case "ButtonMinus": case "ButtonMinus":
ViewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickButton": case "LeftStickButton":
ViewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickUp": case "LeftStickUp":
ViewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickDown": case "LeftStickDown":
ViewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickRight": case "LeftStickRight":
ViewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickLeft": case "LeftStickLeft":
ViewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
break; break;
case "DpadUp": case "DpadUp":
ViewModel.Config.DpadUp = buttonValue.AsHidType<Key>(); viewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
break; break;
case "DpadDown": case "DpadDown":
ViewModel.Config.DpadDown = buttonValue.AsHidType<Key>(); viewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
break; break;
case "DpadLeft": case "DpadLeft":
ViewModel.Config.DpadLeft = buttonValue.AsHidType<Key>(); viewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
break; break;
case "DpadRight": case "DpadRight":
ViewModel.Config.DpadRight = buttonValue.AsHidType<Key>(); viewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
break; break;
case "LeftButtonSr": case "LeftButtonSr":
ViewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>(); viewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
break; break;
case "LeftButtonSl": case "LeftButtonSl":
ViewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>(); viewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
break; break;
case "RightButtonSr": case "RightButtonSr":
ViewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>(); viewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
break; break;
case "RightButtonSl": case "RightButtonSl":
ViewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>(); viewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
break; break;
case "ButtonZr": case "ButtonZr":
ViewModel.Config.ButtonZr = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
break; break;
case "ButtonR": case "ButtonR":
ViewModel.Config.ButtonR = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
break; break;
case "ButtonPlus": case "ButtonPlus":
ViewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
break; break;
case "ButtonA": case "ButtonA":
ViewModel.Config.ButtonA = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
break; break;
case "ButtonB": case "ButtonB":
ViewModel.Config.ButtonB = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
break; break;
case "ButtonX": case "ButtonX":
ViewModel.Config.ButtonX = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
break; break;
case "ButtonY": case "ButtonY":
ViewModel.Config.ButtonY = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
break; break;
case "RightStickButton": case "RightStickButton":
ViewModel.Config.RightStickButton = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
break; break;
case "RightStickUp": case "RightStickUp":
ViewModel.Config.RightStickUp = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
break; break;
case "RightStickDown": case "RightStickDown":
ViewModel.Config.RightStickDown = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
break; break;
case "RightStickRight": case "RightStickRight":
ViewModel.Config.RightStickRight = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
break; break;
case "RightStickLeft": case "RightStickLeft":
ViewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
break; break;
} }
} }

View File

@@ -2,18 +2,19 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.UI.Views.Input namespace Ryujinx.UI.Views.Input
{ {
public partial class LedInputView : RyujinxControl<LedInputViewModel> public partial class LedInputView : UserControl
{ {
private readonly LedInputViewModel _viewModel;
public LedInputView(ControllerInputViewModel viewModel) public LedInputView(ControllerInputViewModel viewModel)
{ {
ViewModel = new LedInputViewModel DataContext = _viewModel = new LedInputViewModel
{ {
ParentModel = viewModel.ParentModel, ParentModel = viewModel.ParentModel,
TurnOffLed = viewModel.Config.TurnOffLed, TurnOffLed = viewModel.Config.TurnOffLed,
@@ -28,18 +29,20 @@ namespace Ryujinx.UI.Views.Input
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args) private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
{ {
if (!args.NewColor.HasValue) return; if (!args.NewColor.HasValue) return;
if (!ViewModel.EnableLedChanging) return; if (DataContext is not LedInputViewModel lvm) return;
if (ViewModel.TurnOffLed) return; if (!lvm.EnableLedChanging) return;
if (lvm.TurnOffLed) return;
ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
} }
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{ {
if (!ViewModel.EnableLedChanging) return; if (DataContext is not LedInputViewModel lvm) return;
if (ViewModel.TurnOffLed) return; if (!lvm.EnableLedChanging) return;
if (lvm.TurnOffLed) return;
ViewModel.ParentModel.SelectedGamepad.SetLed(ViewModel.LedColor.ToUInt32()); lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32());
} }
public static async Task Show(ControllerInputViewModel viewModel) public static async Task Show(ControllerInputViewModel viewModel)
@@ -54,13 +57,13 @@ namespace Ryujinx.UI.Views.Input
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content, Content = content,
}; };
contentDialog.PrimaryButtonClick += (_, _) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.EnableLedChanging = content.ViewModel.EnableLedChanging; config.EnableLedChanging = content._viewModel.EnableLedChanging;
config.LedColor = content.ViewModel.LedColor; config.LedColor = content._viewModel.LedColor;
config.UseRainbowLed = content.ViewModel.UseRainbowLed; config.UseRainbowLed = content._viewModel.UseRainbowLed;
config.TurnOffLed = content.ViewModel.TurnOffLed; config.TurnOffLed = content._viewModel.TurnOffLed;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();

View File

@@ -11,7 +11,11 @@
x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
x:DataType="viewModels:MotionInputViewModel" x:DataType="viewModels:MotionInputViewModel"
Focusable="True"> Focusable="True">
<Grid Margin="10" RowDefinitions="Auto,*"> <Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel <StackPanel
Orientation="Horizontal" Orientation="Horizontal"
@@ -76,7 +80,11 @@
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Grid VerticalAlignment="Top" RowDefinitions="Auto,*"> <Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Center" HorizontalAlignment="Center"
@@ -110,7 +118,15 @@
Text="{Binding DsuServerPort, Mode=TwoWay}" /> Text="{Binding DsuServerPort, Mode=TwoWay}" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<Grid RowDefinitions="*,*" ColumnDefinitions="*,*"> <Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="0,10,0,0" Margin="0,10,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@@ -1,15 +1,16 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class MotionInputView : RyujinxControl<MotionInputViewModel> public partial class MotionInputView : UserControl
{ {
private readonly MotionInputViewModel _viewModel;
public MotionInputView() public MotionInputView()
{ {
InitializeComponent(); InitializeComponent();
@@ -19,7 +20,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
ViewModel = new MotionInputViewModel _viewModel = new MotionInputViewModel
{ {
Slot = config.Slot, Slot = config.Slot,
AltSlot = config.AltSlot, AltSlot = config.AltSlot,
@@ -32,6 +33,7 @@ namespace Ryujinx.Ava.UI.Views.Input
}; };
InitializeComponent(); InitializeComponent();
DataContext = _viewModel;
} }
public static async Task Show(ControllerInputViewModel viewModel) public static async Task Show(ControllerInputViewModel viewModel)
@@ -46,17 +48,17 @@ namespace Ryujinx.Ava.UI.Views.Input
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content, Content = content,
}; };
contentDialog.PrimaryButtonClick += (_, _) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.Slot = content.ViewModel.Slot; config.Slot = content._viewModel.Slot;
config.Sensitivity = content.ViewModel.Sensitivity; config.Sensitivity = content._viewModel.Sensitivity;
config.GyroDeadzone = content.ViewModel.GyroDeadzone; config.GyroDeadzone = content._viewModel.GyroDeadzone;
config.AltSlot = content.ViewModel.AltSlot; config.AltSlot = content._viewModel.AltSlot;
config.DsuServerHost = content.ViewModel.DsuServerHost; config.DsuServerHost = content._viewModel.DsuServerHost;
config.DsuServerPort = content.ViewModel.DsuServerPort; config.DsuServerPort = content._viewModel.DsuServerPort;
config.EnableCemuHookMotion = content.ViewModel.EnableCemuHookMotion; config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
config.MirrorInput = content.ViewModel.MirrorInput; config.MirrorInput = content._viewModel.MirrorInput;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();

View File

@@ -10,7 +10,11 @@
x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView"
x:DataType="viewModels:RumbleInputViewModel" x:DataType="viewModels:RumbleInputViewModel"
Focusable="True"> Focusable="True">
<Grid Margin="10" RowDefinitions="Auto,*"> <Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock

View File

@@ -1,15 +1,16 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class RumbleInputView : RyujinxControl<RumbleInputViewModel> public partial class RumbleInputView : UserControl
{ {
private readonly RumbleInputViewModel _viewModel;
public RumbleInputView() public RumbleInputView()
{ {
InitializeComponent(); InitializeComponent();
@@ -19,13 +20,15 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
ViewModel = new RumbleInputViewModel _viewModel = new RumbleInputViewModel
{ {
StrongRumble = config.StrongRumble, StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble, WeakRumble = config.WeakRumble,
}; };
InitializeComponent(); InitializeComponent();
DataContext = _viewModel;
} }
public static async Task Show(ControllerInputViewModel viewModel) public static async Task Show(ControllerInputViewModel viewModel)
@@ -41,11 +44,11 @@ namespace Ryujinx.Ava.UI.Views.Input
Content = content, Content = content,
}; };
contentDialog.PrimaryButtonClick += (_, _) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.StrongRumble = content.ViewModel.StrongRumble; config.StrongRumble = content._viewModel.StrongRumble;
config.WeakRumble = content.ViewModel.WeakRumble; config.WeakRumble = content._viewModel.WeakRumble;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();

View File

@@ -6,14 +6,13 @@ using Gommon;
using LibHac.Common; using LibHac.Common;
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Ava.UI.Views.Misc; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -26,9 +25,10 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
public partial class MainMenuBarView : RyujinxControl<MainWindowViewModel> public partial class MainMenuBarView : UserControl
{ {
public MainWindow Window { get; private set; } public MainWindow Window { get; private set; }
public MainWindowViewModel ViewModel { get; private set; }
public MainMenuBarView() public MainMenuBarView()
{ {
@@ -51,7 +51,7 @@ namespace Ryujinx.Ava.UI.Views.Main
UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes); UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show); XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show); AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityListWindow.Show()); CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show());
UpdateMenuItem.Command = MainWindowViewModel.UpdateCommand; UpdateMenuItem.Command = MainWindowViewModel.UpdateCommand;
@@ -73,7 +73,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
Content = $".{it.FileName}", Content = $".{it.FileName}",
IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes), IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
Command = Commands.Create(() => Window.ToggleFileType(it.FileName)) Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName))
} }
); );
@@ -108,7 +108,7 @@ namespace Ryujinx.Ava.UI.Views.Main
Margin = new Thickness(3, 0, 3, 0), Margin = new Thickness(3, 0, 3, 0),
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
Header = languageName, Header = languageName,
Command = Commands.Create(() => MainWindowViewModel.ChangeLanguage(language)) Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language))
}; };
yield return menuItem; yield return menuItem;

View File

@@ -4,10 +4,8 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@@ -15,7 +13,7 @@ using System;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
public partial class MainStatusBarView : RyujinxControl<MainWindowViewModel> public partial class MainStatusBarView : UserControl
{ {
public MainWindow Window; public MainWindow Window;
@@ -31,7 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Main
if (VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
Window = window; Window = window;
ViewModel = window.ViewModel; DataContext = window.ViewModel;
LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() => LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() =>
{ {
if (Window.ViewModel.EnableNonGameRunningControls) if (Window.ViewModel.EnableNonGameRunningControls)

View File

@@ -3,15 +3,16 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using System; using System;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
public partial class MainViewControls : RyujinxControl<MainWindowViewModel> public partial class MainViewControls : UserControl
{ {
public MainWindowViewModel ViewModel;
public MainViewControls() public MainViewControls()
{ {
InitializeComponent(); InitializeComponent();
@@ -23,7 +24,7 @@ namespace Ryujinx.Ava.UI.Views.Main
if (VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
ViewModel = window.ViewModel; DataContext = ViewModel = window.ViewModel;
} }
} }

View File

@@ -21,7 +21,12 @@
<Border Classes="settings"> <Border Classes="settings">
<Panel <Panel
Margin="10"> Margin="10">
<Grid RowDefinitions="Auto,*,Auto"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<views:InputView <views:InputView
Grid.Row="0" Grid.Row="0"
Name="InputView" /> Name="InputView" />

View File

@@ -177,7 +177,12 @@
</Style> </Style>
</ListBox.Styles> </ListBox.Styles>
</ListBox> </ListBox>
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto,Auto"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox <TextBox
Name="GameDirPathBox" Name="GameDirPathBox"
Margin="0" Margin="0"
@@ -230,7 +235,12 @@
</Style> </Style>
</ListBox.Styles> </ListBox.Styles>
</ListBox> </ListBox>
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto,Auto"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox <TextBox
Name="AutoloadDirPathBox" Name="AutoloadDirPathBox"
Margin="0" Margin="0"

View File

@@ -14,7 +14,15 @@
mc:Ignorable="d" mc:Ignorable="d"
Focusable="True" Focusable="True"
x:DataType="models:TempProfile"> x:DataType="models:TempProfile">
<Grid Margin="0" ColumnDefinitions="Auto,*" RowDefinitions="*,Auto"> <Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"

View File

@@ -13,12 +13,13 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserEditorView : RyujinxControl<TempProfile> public partial class UserEditorView : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private UserProfile _profile; private UserProfile _profile;
private bool _isNewUser; private bool _isNewUser;
public TempProfile TempProfile { get; set; }
public static uint MaxProfileNameLength => 0x20; public static uint MaxProfileNameLength => 0x20;
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId; public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
@@ -41,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.User
(NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; (NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
_isNewUser = isNewUser; _isNewUser = isNewUser;
_profile = profile; _profile = profile;
ViewModel = new TempProfile(_profile); TempProfile = new TempProfile(_profile);
_parent = parent; _parent = parent;
break; break;
@@ -50,6 +51,8 @@ namespace Ryujinx.Ava.UI.Views.User
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " + ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; $"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
DataContext = TempProfile;
AddPictureButton.IsVisible = _isNewUser; AddPictureButton.IsVisible = _isNewUser;
ChangePictureButton.IsVisible = !_isNewUser; ChangePictureButton.IsVisible = !_isNewUser;
IdLabel.IsVisible = _profile != null; IdLabel.IsVisible = _profile != null;
@@ -69,7 +72,7 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
if (_isNewUser) if (_isNewUser)
{ {
if (ViewModel.Name != string.Empty || ViewModel.Image != null) if (TempProfile.Name != String.Empty || TempProfile.Image != null)
{ {
if (await ContentDialogHelper.CreateChoiceDialog( if (await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
@@ -86,7 +89,7 @@ namespace Ryujinx.Ava.UI.Views.User
} }
else else
{ {
if (_profile.Name != ViewModel.Name || _profile.Image != ViewModel.Image) if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image)
{ {
if (await ContentDialogHelper.CreateChoiceDialog( if (await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
@@ -112,31 +115,31 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
DataValidationErrors.ClearErrors(NameBox); DataValidationErrors.ClearErrors(NameBox);
if (string.IsNullOrWhiteSpace(ViewModel.Name)) if (string.IsNullOrWhiteSpace(TempProfile.Name))
{ {
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError])); DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError]));
return; return;
} }
if (ViewModel.Image == null) if (TempProfile.Image == null)
{ {
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
return; return;
} }
if (_profile != null && !_isNewUser) if (_profile != null && !_isNewUser)
{ {
_profile.Name = ViewModel.Name; _profile.Name = TempProfile.Name;
_profile.Image = ViewModel.Image; _profile.Image = TempProfile.Image;
_profile.UpdateState(); _profile.UpdateState();
_parent.AccountManager.SetUserName(_profile.UserId, _profile.Name); _parent.AccountManager.SetUserName(_profile.UserId, _profile.Name);
_parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image); _parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image);
} }
else if (_isNewUser) else if (_isNewUser)
{ {
_parent.AccountManager.AddUser(ViewModel.Name, ViewModel.Image, ViewModel.UserId); _parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId);
} }
else else
{ {
@@ -148,7 +151,7 @@ namespace Ryujinx.Ava.UI.Views.User
public void SelectProfileImage() public void SelectProfileImage()
{ {
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
} }
private void ChangePictureButton_Click(object sender, RoutedEventArgs e) private void ChangePictureButton_Click(object sender, RoutedEventArgs e)

View File

@@ -20,7 +20,13 @@
<Grid <Grid
Margin="0" Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto,Auto"> VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox <ListBox
Grid.Row="1" Grid.Row="1"
BorderThickness="0" BorderThickness="0"

View File

@@ -1,3 +1,4 @@
using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
@@ -10,7 +11,7 @@ using System.IO;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserFirmwareAvatarSelectorView : RyujinxControl<UserFirmwareAvatarSelectorViewModel> public partial class UserFirmwareAvatarSelectorView : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private TempProfile _profile; private TempProfile _profile;
@@ -19,6 +20,8 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
ContentManager = contentManager; ContentManager = contentManager;
DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
} }
@@ -52,6 +55,8 @@ namespace Ryujinx.Ava.UI.Views.User
public ContentManager ContentManager { get; private set; } public ContentManager ContentManager { get; private set; }
internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; }
private void GoBack(object sender, RoutedEventArgs e) private void GoBack(object sender, RoutedEventArgs e)
{ {
_parent.GoBack(); _parent.GoBack();

View File

@@ -17,7 +17,12 @@
</Design.DataContext> </Design.DataContext>
<Grid <Grid
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" RowDefinitions="Auto,70,Auto"> VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="70" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
TextWrapping="Wrap" TextWrapping="Wrap"

View File

@@ -15,12 +15,14 @@ using System.IO;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserProfileImageSelectorView : RyujinxControl<UserProfileImageSelectorViewModel> public partial class UserProfileImageSelectorView : UserControl
{ {
private ContentManager _contentManager; private ContentManager _contentManager;
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private TempProfile _profile; private TempProfile _profile;
internal UserProfileImageSelectorViewModel ViewModel { get; private set; }
public UserProfileImageSelectorView() public UserProfileImageSelectorView()
{ {
InitializeComponent(); InitializeComponent();

View File

@@ -18,7 +18,11 @@
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" <Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" RowDefinitions="*,Auto"> VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border <Border
CornerRadius="5" CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
@@ -37,7 +41,11 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="5"> CornerRadius="5">
<Grid Margin="0" ColumnDefinitions="*,Auto"> <Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding UserId}" Text="{Binding UserId}"

View File

@@ -4,11 +4,10 @@ using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserRecovererView : RyujinxControl<UserProfileViewModel> public partial class UserRecovererView : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;

View File

@@ -19,10 +19,19 @@
<Design.DataContext> <Design.DataContext>
<viewModels:UserSaveManagerViewModel /> <viewModels:UserSaveManagerViewModel />
</Design.DataContext> </Design.DataContext>
<Grid RowDefinitions="Auto,*,Auto"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid <Grid
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel <StackPanel
Spacing="10" Spacing="10"
Orientation="Horizontal" Orientation="Horizontal"
@@ -71,7 +80,11 @@
<Grid <Grid
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="10,0, 0, 0" ColumnDefinitions="Auto,*"> Margin="10,0, 0, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="{ext:Locale Search}" VerticalAlignment="Center" /> <Label Content="{ext:Locale Search}" VerticalAlignment="Center" />
<TextBox <TextBox
Margin="5,0,0,0" Margin="5,0,0,0"
@@ -105,7 +118,11 @@
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="models:SaveModel"> <DataTemplate x:DataType="models:SaveModel">
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Orientation="Horizontal" Orientation="Horizontal"

View File

@@ -23,8 +23,10 @@ using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserSaveManagerView : RyujinxControl<UserSaveManagerViewModel> public partial class UserSaveManagerView : UserControl
{ {
internal UserSaveManagerViewModel ViewModel { get; private set; }
private AccountManager _accountManager; private AccountManager _accountManager;
private HorizonClient _horizonClient; private HorizonClient _horizonClient;
private VirtualFileSystem _virtualFileSystem; private VirtualFileSystem _virtualFileSystem;
@@ -64,7 +66,7 @@ namespace Ryujinx.Ava.UI.Views.User
public void LoadSaves() public void LoadSaves()
{ {
Dispatcher.UIThread.Post(() => ViewModel.Saves.Clear()); ViewModel.Saves.Clear();
ObservableCollection<SaveModel> saves = []; ObservableCollection<SaveModel> saves = [];
SaveDataFilter saveDataFilter = SaveDataFilter.Make( SaveDataFilter saveDataFilter = SaveDataFilter.Make(
programId: default, programId: default,

View File

@@ -18,7 +18,11 @@
<Design.DataContext> <Design.DataContext>
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border <Border
CornerRadius="5" CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"

View File

@@ -11,10 +11,12 @@ using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserSelectorViews : RyujinxControl<UserProfileViewModel> public partial class UserSelectorViews : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
public UserProfileViewModel ViewModel { get; set; }
public UserSelectorViews() public UserSelectorViews()
{ {
InitializeComponent(); InitializeComponent();

View File

@@ -5,7 +5,6 @@ using Avalonia.Layout;
using Avalonia.Styling; using Avalonia.Styling;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common; using Ryujinx.Common;
@@ -15,7 +14,7 @@ using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {
public partial class AboutWindow : RyujinxControl<AboutWindowViewModel> public partial class AboutWindow : UserControl
{ {
public AboutWindow() public AboutWindow()
{ {
@@ -34,10 +33,19 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = string.Empty, PrimaryButtonText = string.Empty,
SecondaryButtonText = string.Empty, SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = new AboutWindow { ViewModel = viewModel } Content = new AboutWindow { DataContext = viewModel }
}; };
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles()); 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);
} }
private void Button_OnClick(object sender, RoutedEventArgs e) private void Button_OnClick(object sender, RoutedEventArgs e)

View File

@@ -2,8 +2,8 @@ using Avalonia.Collections;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using System.Collections.Generic; using System.Collections.Generic;
@@ -38,9 +38,12 @@ namespace Ryujinx.Ava.UI.Windows
MinHeight = 650; MinHeight = 650;
LoadedCheats = []; LoadedCheats = [];
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
BuildId = ApplicationData.GetBuildId(virtualFileSystem, ConfigurationState.Instance.System.IntegrityCheckLevel, titlePath); BuildId = ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath);
InitializeComponent(); InitializeComponent();

View File

@@ -1,82 +0,0 @@
<window:StyleableAppWindow 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="using:Ryujinx.Ava.UI.Helpers"
xmlns:ext="using:Ryujinx.Ava.Common.Markup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:systems="clr-namespace:Ryujinx.Ava.Systems"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
CanResize="False"
mc:Ignorable="d"
MinWidth="800"
MinHeight="745"
x:Class="Ryujinx.Ava.UI.Windows.CompatibilityListWindow"
x:DataType="viewModels:CompatibilityViewModel">
<window:StyleableAppWindow.DataContext>
<viewModels:CompatibilityViewModel />
</window:StyleableAppWindow.DataContext>
<Grid RowDefinitions="Auto,*">
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto" Name="FlushControls">
<Image
Name="RyuLogo"
Margin="15, 0, 7, 0"
Height="25"
Width="25" />
<TextBox Name="SearchBoxFlush" Grid.Column="1" Margin="0, 5, 0, 5" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermarkWithCount}" TextChanged="TextBox_OnTextChanged" />
<CheckBox Grid.Column="2" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
<TextBlock Grid.Column="3" Padding="0, 0, 138, 0" Margin="-10, 0, 18, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
</Grid>
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Name="NormalControls">
<TextBox Name="SearchBoxNormal" Grid.Column="0" Margin="15, 0, 0, 5" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" />
<CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
<TextBlock Grid.Column="2" Padding="0, 0, 1, 0" Margin="-10, 0, 18, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
</Grid>
<ScrollViewer Grid.Row="1">
<ListBox Margin="12, 0, 13, 0"
Background="Transparent"
ItemsSource="{Binding CurrentEntries}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type systems:CompatibilityEntry}">
<Grid MinWidth="800"
Margin="10"
ColumnDefinitions="*,Auto,Auto,*"
Background="Transparent"
ToolTip.Tip="{Binding LocalizedLastUpdated}">
<TextBlock Grid.Column="0"
Text="{Binding GameName}"
Width="525"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" />
<TextBlock Grid.Column="1"
Width="135"
Padding="7, 0, 0, 0"
FontFamily="{StaticResource JetBrainsMono}"
Text="{Binding FormattedTitleId}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" />
<TextBlock Grid.Column="2"
Padding="7, 0"
Text="{Binding LocalizedStatus}"
Width="90"
Background="Transparent"
ToolTip.Tip="{Binding LocalizedStatusDescription}"
Foreground="{Binding Status, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="NoWrap" />
<TextBlock Grid.Column="3"
Text="{Binding FormattedIssueLabels}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
<Grid></Grid>
</Grid>
</window:StyleableAppWindow>

View File

@@ -1,50 +0,0 @@
using Avalonia.Controls;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Windowing;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Windows
{
public partial class CompatibilityListWindow : StyleableAppWindow
{
public static Task Show(string titleId = null) =>
ShowAsync(new CompatibilityListWindow
{
DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary),
SearchBoxFlush = { Text = titleId ?? string.Empty },
SearchBoxNormal = { Text = titleId ?? string.Empty }
});
public CompatibilityListWindow() : base(useCustomTitleBar: true)
{
Title = RyujinxApp.FormatTitle(LocaleKeys.CompatibilityListTitle);
TitleBar.Height = 37;
InitializeComponent();
RyuLogo.Source = MainWindowViewModel.IconBitmap;
FlushControls.IsVisible = !ConfigurationState.Instance.ShowTitleBar;
NormalControls.IsVisible = ConfigurationState.Instance.ShowTitleBar;
}
// ReSharper disable once UnusedMember.Local
// its referenced in the axaml but rider keeps yelling at me that its unused so
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
if (DataContext is not CompatibilityViewModel cvm)
return;
if (sender is not TextBox searchBox)
return;
cvm.Search(searchBox.Text);
}
}
}

Some files were not shown because too many files have changed in this diff Show More