mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-06-28 00:16:23 +02:00
Extended hotkeys to player1-8 + h, localized the overlay
This commit is contained in:
parent
ef0dac5533
commit
3ec079855d
16 changed files with 338 additions and 79 deletions
|
@ -24373,12 +24373,12 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysCycleInputDevicePlayer1",
|
||||
"ID": "SettingsTabHotkeysCycleInputDevicePlayerX",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Cycle Input Device Player 1:",
|
||||
"en_US": "Cycle Input Device {0}:",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
|
@ -24398,12 +24398,12 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysCycleInputDevicePlayer2",
|
||||
"ID": "ControllerOverlayTitle",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Cycle Input Device Player 2:",
|
||||
"en_US": "Controller Bindings",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
|
@ -24423,12 +24423,12 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysCycleInputDevicePlayer3",
|
||||
"ID": "ControllerOverlayNoController",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Cycle Input Device Player 3:",
|
||||
"en_US": "No controller assigned",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
|
@ -24448,12 +24448,62 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabHotkeysCycleInputDevicePlayer4",
|
||||
"ID": "ControllerOverlayKeyboard",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Cycle Input Device Player 4:",
|
||||
"en_US": "Keyboard",
|
||||
"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": "ControllerOverlayController",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Controller",
|
||||
"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": "ControllerOverlayUnknown",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Unknown",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
|
|
|
@ -19,5 +19,10 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||
public Key CycleInputDevicePlayer2 { get; set; }
|
||||
public Key CycleInputDevicePlayer3 { get; set; }
|
||||
public Key CycleInputDevicePlayer4 { get; set; }
|
||||
public Key CycleInputDevicePlayer5 { get; set; }
|
||||
public Key CycleInputDevicePlayer6 { get; set; }
|
||||
public Key CycleInputDevicePlayer7 { get; set; }
|
||||
public Key CycleInputDevicePlayer8 { get; set; }
|
||||
public Key CycleInputDeviceHandheld { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,18 @@ using OriginalInputBackendType = Ryujinx.Common.Configuration.Hid.InputBackendTy
|
|||
|
||||
namespace Ryujinx.Graphics.Gpu.Overlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Localization strings for the controller overlay
|
||||
/// </summary>
|
||||
public class ControllerOverlayLocalization
|
||||
{
|
||||
public string TitleText { get; set; } = "Controller Bindings";
|
||||
public string NoControllerText { get; set; } = "No controller assigned";
|
||||
public string KeyboardText { get; set; } = "Keyboard";
|
||||
public string ControllerText { get; set; } = "Controller";
|
||||
public string UnknownText { get; set; } = "Unknown";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controller overlay that shows controller bindings matching the original AXAML design
|
||||
/// </summary>
|
||||
|
@ -23,9 +35,11 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
private const float PlayerTextSize = 22;
|
||||
|
||||
private float _lifespan = 0f;
|
||||
private ControllerOverlayLocalization _localization;
|
||||
|
||||
public ControllerOverlay() : base("ControllerOverlay")
|
||||
public ControllerOverlay(ControllerOverlayLocalization localization) : base("ControllerOverlay")
|
||||
{
|
||||
_localization = localization;
|
||||
CreateBaseElements();
|
||||
}
|
||||
|
||||
|
@ -42,8 +56,8 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
};
|
||||
AddElement(background);
|
||||
|
||||
// Title text
|
||||
var titleText = new TextElement(Padding + 30, Padding, "Controller Bindings", TitleTextSize, SKColors.White)
|
||||
// Title text (will be updated with localized text)
|
||||
var titleText = new TextElement(Padding + 30, Padding, _localization.TitleText, TitleTextSize, SKColors.White)
|
||||
{
|
||||
Name = "TitleText",
|
||||
FontStyle = SKFontStyle.Bold
|
||||
|
@ -52,21 +66,27 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show controller bindings matching the original AXAML implementation
|
||||
/// Show controller bindings with localized strings
|
||||
/// </summary>
|
||||
public void ShowControllerBindings(List<OriginalInputConfig> inputConfigs, int durationSeconds = 3)
|
||||
{
|
||||
// Update title text
|
||||
var titleElement = FindElement<TextElement>("TitleText");
|
||||
if (titleElement != null)
|
||||
{
|
||||
titleElement.Text = _localization.TitleText;
|
||||
}
|
||||
|
||||
// Reset lifespan and opacity
|
||||
_lifespan = durationSeconds;
|
||||
|
||||
// Clear existing player bindings
|
||||
ClearPlayerBindings();
|
||||
|
||||
// Debug: Log input data
|
||||
// Group controllers by player index
|
||||
// Group controllers by player index (support all players + handheld)
|
||||
var playerBindings = new Dictionary<OriginalPlayerIndex, List<OriginalInputConfig>>();
|
||||
|
||||
foreach (var config in inputConfigs.Where(c => c.PlayerIndex <= OriginalPlayerIndex.Player4))
|
||||
foreach (var config in inputConfigs.Where(c => c.PlayerIndex <= OriginalPlayerIndex.Handheld))
|
||||
{
|
||||
if (!playerBindings.ContainsKey(config.PlayerIndex))
|
||||
{
|
||||
|
@ -77,10 +97,17 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
|
||||
float currentY = Padding + 40; // After title
|
||||
|
||||
// Add player bindings to UI
|
||||
for (int i = 0; i < 4; i++)
|
||||
// Add player bindings to UI (support 8 players + handheld)
|
||||
var playerIndices = new[]
|
||||
{
|
||||
var playerIndex = (OriginalPlayerIndex)i;
|
||||
OriginalPlayerIndex.Player1, OriginalPlayerIndex.Player2, OriginalPlayerIndex.Player3, OriginalPlayerIndex.Player4,
|
||||
OriginalPlayerIndex.Player5, OriginalPlayerIndex.Player6, OriginalPlayerIndex.Player7, OriginalPlayerIndex.Player8,
|
||||
OriginalPlayerIndex.Handheld
|
||||
};
|
||||
|
||||
for (int i = 0; i < playerIndices.Length; i++)
|
||||
{
|
||||
var playerIndex = playerIndices[i];
|
||||
float rowY = currentY + (i * (PlayerRowHeight + PlayerSpacing));
|
||||
|
||||
// Player number with colored background (circular badge)
|
||||
|
@ -93,13 +120,14 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
AddElement(playerBadge);
|
||||
|
||||
// Player number text
|
||||
var playerLabel = new TextElement(Padding + 12, rowY + 2, $"P{i + 1}", PlayerTextSize, SKColors.White)
|
||||
string playerLabel = playerIndex == OriginalPlayerIndex.Handheld ? "H" : $"P{(int)playerIndex + 1}";
|
||||
var playerLabelElement = new TextElement(Padding + 12, rowY + 2, playerLabel, PlayerTextSize, SKColors.White)
|
||||
{
|
||||
Name = $"PlayerLabel_{i}",
|
||||
FontStyle = SKFontStyle.Bold,
|
||||
TextAlign = SKTextAlign.Center
|
||||
};
|
||||
AddElement(playerLabel);
|
||||
AddElement(playerLabelElement);
|
||||
|
||||
// Controller info
|
||||
if (playerBindings.ContainsKey(playerIndex))
|
||||
|
@ -107,37 +135,32 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
var controllers = playerBindings[playerIndex];
|
||||
var controllerNames = controllers.Select(c => GetControllerDisplayName(c)).ToList();
|
||||
|
||||
var controllerText = new TextElement(Padding + 56, rowY + 2, string.Join(", ", controllerNames), PlayerTextSize, new SKColor(144, 238, 144)) // LightGreen
|
||||
var controllerTextElement = new TextElement(Padding + 56, rowY + 2, string.Join(", ", controllerNames), PlayerTextSize, new SKColor(144, 238, 144)) // LightGreen
|
||||
{
|
||||
Name = $"ControllerText_{i}",
|
||||
FontStyle = SKFontStyle.Bold
|
||||
};
|
||||
AddElement(controllerText);
|
||||
AddElement(controllerTextElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
var noControllerText = new TextElement(Padding + 56, rowY + 2, "No controller assigned", PlayerTextSize, new SKColor(128, 128, 128)) // Gray
|
||||
var noControllerTextElement = new TextElement(Padding + 56, rowY + 2, _localization.NoControllerText, PlayerTextSize, new SKColor(128, 128, 128)) // Gray
|
||||
{
|
||||
Name = $"NoControllerText_{i}",
|
||||
FontStyle = SKFontStyle.Italic
|
||||
};
|
||||
AddElement(noControllerText);
|
||||
AddElement(noControllerTextElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total height and update background
|
||||
float totalHeight = Padding + 40 + (4 * (PlayerRowHeight + PlayerSpacing)) + Padding + 40; // Extra space for duration text
|
||||
float totalHeight = Padding + 40 + (playerIndices.Length * (PlayerRowHeight + PlayerSpacing)) + Padding + 20;
|
||||
var background = FindElement<RectangleElement>("Background");
|
||||
if (background != null)
|
||||
{
|
||||
background.Height = totalHeight;
|
||||
}
|
||||
|
||||
// Duration text at bottom
|
||||
string durationText = durationSeconds == 1
|
||||
? "This overlay will disappear in 1 second"
|
||||
: $"This overlay will disappear in {durationSeconds} seconds";
|
||||
|
||||
// Show the overlay (position will be set by Window class with actual dimensions)
|
||||
IsVisible = true;
|
||||
}
|
||||
|
@ -150,19 +173,24 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
1 => new SKColor(54, 162, 235), // Blue for Player 2
|
||||
2 => new SKColor(255, 206, 84), // Yellow for Player 3
|
||||
3 => new SKColor(75, 192, 192), // Green for Player 4
|
||||
4 => new SKColor(153, 102, 255), // Purple for Player 5
|
||||
5 => new SKColor(255, 159, 64), // Orange for Player 6
|
||||
6 => new SKColor(199, 199, 199), // Light Gray for Player 7
|
||||
7 => new SKColor(83, 102, 255), // Indigo for Player 8
|
||||
8 => new SKColor(255, 99, 132), // Pink for Handheld
|
||||
_ => new SKColor(128, 128, 128) // Gray fallback
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetControllerDisplayName(OriginalInputConfig config)
|
||||
private string GetControllerDisplayName(OriginalInputConfig config)
|
||||
{
|
||||
if (string.IsNullOrEmpty(config.Name))
|
||||
{
|
||||
return config.Backend switch
|
||||
{
|
||||
OriginalInputBackendType.WindowKeyboard => "Keyboard",
|
||||
OriginalInputBackendType.GamepadSDL2 => "Controller",
|
||||
_ => "Unknown"
|
||||
OriginalInputBackendType.WindowKeyboard => _localization.KeyboardText,
|
||||
OriginalInputBackendType.GamepadSDL2 => _localization.ControllerText,
|
||||
_ => _localization.UnknownText
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.Gpu.Overlay
|
|||
/// </summary>
|
||||
public class ImageElement : OverlayElement
|
||||
{
|
||||
private SKBitmap? _bitmap;
|
||||
private byte[]? _imageData;
|
||||
private string? _imagePath;
|
||||
private SKBitmap _bitmap;
|
||||
private byte[] _imageData;
|
||||
private string _imagePath;
|
||||
|
||||
public SKFilterQuality FilterQuality { get; set; } = SKFilterQuality.Medium;
|
||||
public bool MaintainAspectRatio { get; set; } = true;
|
||||
|
|
|
@ -107,9 +107,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||
_overlayManager = new OverlayManager();
|
||||
|
||||
_frameQueue = new ConcurrentQueue<PresentationTexture>();
|
||||
|
||||
// Initialize controller overlay
|
||||
InitializeControllerOverlay();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -258,12 +255,11 @@ namespace Ryujinx.Graphics.Gpu
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize controller overlay
|
||||
/// Add overlay to the overlay manager
|
||||
/// </summary>
|
||||
private void InitializeControllerOverlay()
|
||||
public void AddOverlay(Overlay.Overlay overlay)
|
||||
{
|
||||
var controllerOverlay = new ControllerOverlay();
|
||||
_overlayManager.AddOverlay(controllerOverlay);
|
||||
_overlayManager.AddOverlay(overlay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -363,17 +359,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show controller overlay with the provided input configurations
|
||||
/// </summary>
|
||||
/// <param name="inputConfigs">List of input configurations to display</param>
|
||||
/// <param name="durationSeconds">Duration to show the overlay in seconds</param>
|
||||
public void ShowControllerBindings(List<Common.Configuration.Hid.InputConfig> inputConfigs, int durationSeconds = 3)
|
||||
{
|
||||
var controllerOverlay = _overlayManager.FindOverlay("ControllerOverlay") as ControllerOverlay;
|
||||
controllerOverlay?.ShowControllerBindings(inputConfigs, durationSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the overlay manager for external access
|
||||
/// </summary>
|
||||
|
|
|
@ -19,5 +19,10 @@ namespace Ryujinx.Ava.Common
|
|||
CycleInputDevicePlayer2,
|
||||
CycleInputDevicePlayer3,
|
||||
CycleInputDevicePlayer4,
|
||||
CycleInputDevicePlayer5,
|
||||
CycleInputDevicePlayer6,
|
||||
CycleInputDevicePlayer7,
|
||||
CycleInputDevicePlayer8,
|
||||
CycleInputDeviceHandheld,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml.MarkupExtensions;
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
|
@ -18,11 +19,19 @@ namespace Ryujinx.Ava.Common.Markup
|
|||
|
||||
internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension<string>
|
||||
{
|
||||
public IValueConverter Converter { get; set; }
|
||||
|
||||
public override string Name => "Translation";
|
||||
protected override string Value => LocaleManager.Instance[key];
|
||||
|
||||
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
|
||||
=> bindingExtension.Source = LocaleManager.Instance;
|
||||
{
|
||||
bindingExtension.Source = LocaleManager.Instance;
|
||||
if (Converter != null)
|
||||
{
|
||||
bindingExtension.Converter = Converter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class WindowTitleExtension(LocaleKeys key, bool includeVersion) : BasicMarkupExtension<string>
|
||||
|
|
|
@ -33,6 +33,7 @@ using Ryujinx.Common.Utilities;
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Multithreading;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.Graphics.Gpu.Overlay;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
|
@ -129,6 +130,7 @@ namespace Ryujinx.Ava.Systems
|
|||
private readonly bool _isFirmwareTitle;
|
||||
|
||||
private readonly Lock _lockObject = new();
|
||||
private ControllerOverlay _controllerOverlay;
|
||||
|
||||
public event EventHandler AppExit;
|
||||
public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
|
||||
|
@ -932,6 +934,18 @@ namespace Ryujinx.Ava.Systems
|
|||
_viewModel.UiHandler
|
||||
)
|
||||
);
|
||||
|
||||
// Initialize controller overlay with localization
|
||||
var localization = new ControllerOverlayLocalization
|
||||
{
|
||||
TitleText = LocaleManager.Instance[LocaleKeys.ControllerOverlayTitle],
|
||||
NoControllerText = LocaleManager.Instance[LocaleKeys.ControllerOverlayNoController],
|
||||
KeyboardText = LocaleManager.Instance[LocaleKeys.ControllerOverlayKeyboard],
|
||||
ControllerText = LocaleManager.Instance[LocaleKeys.ControllerOverlayController],
|
||||
UnknownText = LocaleManager.Instance[LocaleKeys.ControllerOverlayUnknown]
|
||||
};
|
||||
_controllerOverlay = new ControllerOverlay(localization);
|
||||
Device.Gpu.Window.AddOverlay(_controllerOverlay);
|
||||
}
|
||||
|
||||
private static IHardwareDeviceDriver InitializeAudio()
|
||||
|
@ -1350,6 +1364,21 @@ namespace Ryujinx.Ava.Systems
|
|||
case KeyboardHotkeyState.CycleInputDevicePlayer4:
|
||||
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player4);
|
||||
break;
|
||||
case KeyboardHotkeyState.CycleInputDevicePlayer5:
|
||||
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player5);
|
||||
break;
|
||||
case KeyboardHotkeyState.CycleInputDevicePlayer6:
|
||||
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player6);
|
||||
break;
|
||||
case KeyboardHotkeyState.CycleInputDevicePlayer7:
|
||||
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player7);
|
||||
break;
|
||||
case KeyboardHotkeyState.CycleInputDevicePlayer8:
|
||||
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player8);
|
||||
break;
|
||||
case KeyboardHotkeyState.CycleInputDeviceHandheld:
|
||||
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Handheld);
|
||||
break;
|
||||
case KeyboardHotkeyState.None:
|
||||
(_keyboardInterface as AvaloniaKeyboard).Clear();
|
||||
break;
|
||||
|
@ -1458,7 +1487,7 @@ namespace Ryujinx.Ava.Systems
|
|||
NpadManager.ReloadConfiguration(currentConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
|
||||
// Show controller overlay
|
||||
ShowControllerOverlay(currentConfig);
|
||||
ShowControllerOverlay(currentConfig, ConfigurationState.Instance.ControllerOverlayInputCycleDuration.Value);
|
||||
}
|
||||
|
||||
private InputConfig CreateDefaultInputConfig((DeviceType Type, string Id, string Name) device, PlayerIndex playerIndex)
|
||||
|
@ -1476,21 +1505,17 @@ namespace Ryujinx.Ava.Systems
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ShowControllerOverlay(List<InputConfig> inputConfigs)
|
||||
public void ShowControllerOverlay(List<InputConfig> inputConfigs, int duration)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Show overlay through the GPU context window directly
|
||||
if (Device?.Gpu?.Window != null)
|
||||
if (_controllerOverlay != null)
|
||||
{
|
||||
int duration = ConfigurationState.Instance.ControllerOverlayInputCycleDuration.Value;
|
||||
Device.Gpu.Window.ShowControllerBindings(inputConfigs, duration);
|
||||
_controllerOverlay.ShowControllerBindings(inputConfigs, duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, "AppHost: Cannot show overlay - Device.Gpu.Window is null");
|
||||
Logger.Warning?.Print(LogClass.Application, "AppHost: Cannot show overlay - ControllerOverlay is null");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -1567,6 +1592,26 @@ namespace Ryujinx.Ava.Systems
|
|||
{
|
||||
state = KeyboardHotkeyState.CycleInputDevicePlayer4;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer5))
|
||||
{
|
||||
state = KeyboardHotkeyState.CycleInputDevicePlayer5;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer6))
|
||||
{
|
||||
state = KeyboardHotkeyState.CycleInputDevicePlayer6;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer7))
|
||||
{
|
||||
state = KeyboardHotkeyState.CycleInputDevicePlayer7;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer8))
|
||||
{
|
||||
state = KeyboardHotkeyState.CycleInputDevicePlayer8;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDeviceHandheld))
|
||||
{
|
||||
state = KeyboardHotkeyState.CycleInputDeviceHandheld;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 71;
|
||||
public const int CurrentVersion = 72;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
|
|
|
@ -486,6 +486,34 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
{
|
||||
cff.ControllerOverlayGameStartDuration = 3;
|
||||
cff.ControllerOverlayInputCycleDuration = 2;
|
||||
}),
|
||||
(72, static cff =>
|
||||
{
|
||||
cff.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVSyncMode = cff.Hotkeys.ToggleVSyncMode,
|
||||
Screenshot = cff.Hotkeys.Screenshot,
|
||||
ShowUI = cff.Hotkeys.ShowUI,
|
||||
Pause = cff.Hotkeys.Pause,
|
||||
ToggleMute = cff.Hotkeys.ToggleMute,
|
||||
ResScaleUp = cff.Hotkeys.ResScaleUp,
|
||||
ResScaleDown = cff.Hotkeys.ResScaleDown,
|
||||
VolumeUp = cff.Hotkeys.VolumeUp,
|
||||
VolumeDown = cff.Hotkeys.VolumeDown,
|
||||
CustomVSyncIntervalIncrement = cff.Hotkeys.CustomVSyncIntervalIncrement,
|
||||
CustomVSyncIntervalDecrement = cff.Hotkeys.CustomVSyncIntervalDecrement,
|
||||
TurboMode = cff.Hotkeys.TurboMode,
|
||||
TurboModeWhileHeld = cff.Hotkeys.TurboModeWhileHeld,
|
||||
CycleInputDevicePlayer1 = Key.Unbound,
|
||||
CycleInputDevicePlayer2 = Key.Unbound,
|
||||
CycleInputDevicePlayer3 = Key.Unbound,
|
||||
CycleInputDevicePlayer4 = Key.Unbound,
|
||||
CycleInputDevicePlayer5 = Key.Unbound,
|
||||
CycleInputDevicePlayer6 = Key.Unbound,
|
||||
CycleInputDevicePlayer7 = Key.Unbound,
|
||||
CycleInputDevicePlayer8 = Key.Unbound,
|
||||
CycleInputDeviceHandheld = Key.Unbound
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -273,7 +273,16 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
CustomVSyncIntervalIncrement = Key.Unbound,
|
||||
CustomVSyncIntervalDecrement = Key.Unbound,
|
||||
TurboMode = Key.Unbound,
|
||||
TurboModeWhileHeld = false
|
||||
TurboModeWhileHeld = false,
|
||||
CycleInputDevicePlayer1 = Key.Unbound,
|
||||
CycleInputDevicePlayer2 = Key.Unbound,
|
||||
CycleInputDevicePlayer3 = Key.Unbound,
|
||||
CycleInputDevicePlayer4 = Key.Unbound,
|
||||
CycleInputDevicePlayer5 = Key.Unbound,
|
||||
CycleInputDevicePlayer6 = Key.Unbound,
|
||||
CycleInputDevicePlayer7 = Key.Unbound,
|
||||
CycleInputDevicePlayer8 = Key.Unbound,
|
||||
CycleInputDeviceHandheld = Key.Unbound
|
||||
};
|
||||
Hid.RainbowSpeed.Value = 1f;
|
||||
Hid.InputConfig.Value =
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using Avalonia.Data.Converters;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal class PlayerHotkeyLabelConverter : IValueConverter
|
||||
{
|
||||
public static readonly PlayerHotkeyLabelConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is string playerName && !string.IsNullOrEmpty(playerName))
|
||||
{
|
||||
string baseText = LocaleManager.Instance[LocaleKeys.SettingsTabHotkeysCycleInputDevicePlayerX];
|
||||
return string.Format(baseText, playerName);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,16 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||
|
||||
[ObservableProperty] private Key _cycleInputDevicePlayer4;
|
||||
|
||||
[ObservableProperty] private Key _cycleInputDevicePlayer5;
|
||||
|
||||
[ObservableProperty] private Key _cycleInputDevicePlayer6;
|
||||
|
||||
[ObservableProperty] private Key _cycleInputDevicePlayer7;
|
||||
|
||||
[ObservableProperty] private Key _cycleInputDevicePlayer8;
|
||||
|
||||
[ObservableProperty] private Key _cycleInputDeviceHandheld;
|
||||
|
||||
public HotkeyConfig(KeyboardHotkeys config)
|
||||
{
|
||||
if (config == null)
|
||||
|
@ -62,6 +72,11 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||
CycleInputDevicePlayer2 = config.CycleInputDevicePlayer2;
|
||||
CycleInputDevicePlayer3 = config.CycleInputDevicePlayer3;
|
||||
CycleInputDevicePlayer4 = config.CycleInputDevicePlayer4;
|
||||
CycleInputDevicePlayer5 = config.CycleInputDevicePlayer5;
|
||||
CycleInputDevicePlayer6 = config.CycleInputDevicePlayer6;
|
||||
CycleInputDevicePlayer7 = config.CycleInputDevicePlayer7;
|
||||
CycleInputDevicePlayer8 = config.CycleInputDevicePlayer8;
|
||||
CycleInputDeviceHandheld = config.CycleInputDeviceHandheld;
|
||||
}
|
||||
|
||||
public KeyboardHotkeys GetConfig() =>
|
||||
|
@ -83,7 +98,12 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||
CycleInputDevicePlayer1 = CycleInputDevicePlayer1,
|
||||
CycleInputDevicePlayer2 = CycleInputDevicePlayer2,
|
||||
CycleInputDevicePlayer3 = CycleInputDevicePlayer3,
|
||||
CycleInputDevicePlayer4 = CycleInputDevicePlayer4
|
||||
CycleInputDevicePlayer4 = CycleInputDevicePlayer4,
|
||||
CycleInputDevicePlayer5 = CycleInputDevicePlayer5,
|
||||
CycleInputDevicePlayer6 = CycleInputDevicePlayer6,
|
||||
CycleInputDevicePlayer7 = CycleInputDevicePlayer7,
|
||||
CycleInputDevicePlayer8 = CycleInputDevicePlayer8,
|
||||
CycleInputDeviceHandheld = CycleInputDeviceHandheld
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1710,10 +1710,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
// Always show overlay - if no configs, it will show test data
|
||||
int duration = ConfigurationState.Instance.ControllerOverlayGameStartDuration.Value;
|
||||
// Show overlay through the GPU context window directly
|
||||
if (AppHost?.Device?.Gpu?.Window != null)
|
||||
{
|
||||
AppHost.Device.Gpu.Window.ShowControllerBindings(inputConfigs, duration);
|
||||
}
|
||||
AppHost.ShowControllerOverlay(inputConfigs, duration);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -121,29 +121,59 @@
|
|||
<CheckBox IsChecked="{Binding KeyboardHotkey.TurboModeWhileHeld}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer1}" Classes="settingHeader" />
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer1, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer1">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer1, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer2}" Classes="settingHeader" />
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer2, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer2">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer2, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer3}" Classes="settingHeader" />
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer3, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer3">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer3, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer4}" Classes="settingHeader" />
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer4, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer4">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer4, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer5, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer5">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer5, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer6, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer6">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer6, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer7, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer7">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer7, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsPlayer8, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDevicePlayer8">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer8, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsHandheld, Converter={x:Static helpers:PlayerHotkeyLabelConverter.Instance}}" Classes="settingHeader" />
|
||||
<ToggleButton Name="CycleInputDeviceHandheld">
|
||||
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDeviceHandheld, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
|
|
|
@ -86,7 +86,12 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||
{ "CycleInputDevicePlayer1", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer1 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer2", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer2 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer3", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer3 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer4", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer4 = Key.Unbound }
|
||||
{ "CycleInputDevicePlayer4", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer4 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer5", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer5 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer6", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer6 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer7", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer7 = Key.Unbound },
|
||||
{ "CycleInputDevicePlayer8", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer8 = Key.Unbound },
|
||||
{ "CycleInputDeviceHandheld", () => viewModel.KeyboardHotkey.CycleInputDeviceHandheld = Key.Unbound }
|
||||
};
|
||||
|
||||
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
|
||||
|
@ -178,6 +183,21 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||
case "CycleInputDevicePlayer4":
|
||||
ViewModel.KeyboardHotkey.CycleInputDevicePlayer4 = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "CycleInputDevicePlayer5":
|
||||
ViewModel.KeyboardHotkey.CycleInputDevicePlayer5 = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "CycleInputDevicePlayer6":
|
||||
ViewModel.KeyboardHotkey.CycleInputDevicePlayer6 = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "CycleInputDevicePlayer7":
|
||||
ViewModel.KeyboardHotkey.CycleInputDevicePlayer7 = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "CycleInputDevicePlayer8":
|
||||
ViewModel.KeyboardHotkey.CycleInputDevicePlayer8 = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "CycleInputDeviceHandheld":
|
||||
ViewModel.KeyboardHotkey.CycleInputDeviceHandheld = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue