Compare commits

..

5 Commits

Author SHA1 Message Date
Frog Business
dd9a0904f7 Merge f73ffd1fd0 into 91f73a4891 2025-02-13 19:54:38 -06:00
Daniel Nylander
91f73a4891 Updated Swedish translation (#594) 2025-02-13 19:28:39 -06:00
Evan Husted
883d4d863a i18n: chore: [ci skip] missing closing single quote in translation (not JSON breaking) 2025-02-13 19:16:05 -06:00
Dennis
ca5de909a1 Updated German translation (#626) 2025-02-13 19:14:02 -06:00
Barış Hamil
f73ffd1fd0 Ability to assign hotkeys to cycle controllers for players 2025-02-01 12:30:51 +03:00
9 changed files with 314 additions and 125 deletions

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace Ryujinx.Common.Configuration.Hid
{
public class KeyboardHotkeys
@@ -13,5 +15,6 @@ namespace Ryujinx.Common.Configuration.Hid
public Key VolumeDown { get; set; }
public Key CustomVSyncIntervalIncrement { get; set; }
public Key CustomVSyncIntervalDecrement { get; set; }
public List<Key> CycleControllers { get; set; }
}
}

View File

@@ -40,6 +40,7 @@ using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
@@ -49,6 +50,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -1309,6 +1311,18 @@ namespace Ryujinx.Ava
_viewModel.Volume = Device.GetVolume();
break;
case KeyboardHotkeyState.CycleControllersPlayer1:
case KeyboardHotkeyState.CycleControllersPlayer2:
case KeyboardHotkeyState.CycleControllersPlayer3:
case KeyboardHotkeyState.CycleControllersPlayer4:
case KeyboardHotkeyState.CycleControllersPlayer5:
case KeyboardHotkeyState.CycleControllersPlayer6:
case KeyboardHotkeyState.CycleControllersPlayer7:
case KeyboardHotkeyState.CycleControllersPlayer8:
var player = currentHotkeyState - KeyboardHotkeyState.CycleControllersPlayer1;
var ivm = new UI.ViewModels.Input.InputViewModel();
Dispatcher.UIThread.Invoke(() => ivm.CyclePlayerDevice(player));
break;
case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear();
break;
@@ -1391,6 +1405,15 @@ namespace Ryujinx.Ava
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
}
foreach (var cycle in ConfigurationState.Instance.Hid.Hotkeys.Value.CycleControllers?.Select((value, index) => (value, index)) ?? [])
{
if (_keyboardInterface.IsPressed((Key)cycle.value))
{
state = KeyboardHotkeyState.CycleControllersPlayer1 + cycle.index;
break;
}
}
return state;
}
}

View File

@@ -76,7 +76,7 @@
"ID": "MenuBarFileOpenAppletOpenMiiApplet",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Mii-Bearbeitungsapplet",
"el_GR": "",
"en_US": "Mii Edit Applet",
"es_ES": "Applet Editor Mii",
@@ -176,7 +176,7 @@
"ID": "SettingsTabSystemMemoryManagerModeSoftware",
"Translations": {
"ar_SA": "البرنامج",
"de_DE": "",
"de_DE": "Programme",
"el_GR": "Λογισμικό",
"en_US": "Software",
"es_ES": "",
@@ -326,7 +326,7 @@
"ID": "MenuBarFileOpenFromFileError",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Keine Anwendungen im ausgewählten Datei gefunden.",
"el_GR": "",
"en_US": "No applications found in selected file.",
"es_ES": "No se encontraron aplicaciones en el archivo seleccionado.",
@@ -376,7 +376,7 @@
"ID": "MenuBarFileLoadDlcFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "DLC aus Ordner laden",
"el_GR": "",
"en_US": "Load DLC From Folder",
"es_ES": "Cargar DLC Desde Carpeta",
@@ -401,7 +401,7 @@
"ID": "MenuBarFileLoadTitleUpdatesFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Titel-Updates aus Ordner laden",
"el_GR": "",
"en_US": "Load Title Updates From Folder",
"es_ES": "Cargar Actualizaciones de Títulos Desde Carpeta",
@@ -576,7 +576,7 @@
"ID": "MenuBarOptionsStartGamesWithoutUI",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Spiele ohne Benutzeroberfläche starten",
"el_GR": "",
"en_US": "Start Games with UI Hidden",
"es_ES": "",
@@ -589,7 +589,7 @@
"pl_PL": "",
"pt_BR": "Iniciar jogos ocultando a interface",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Starta spel med dolt användargränssnitt",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -751,7 +751,7 @@
"ID": "MenuBarActionsScanAmiiboBin",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Amiibo scannen (aus Bin-Datei)",
"el_GR": "",
"en_US": "Scan An Amiibo (From Bin)",
"es_ES": "",
@@ -851,7 +851,7 @@
"ID": "MenuBarActionsInstallKeys",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel installieren",
"el_GR": "",
"en_US": "Install Keys",
"es_ES": "",
@@ -876,7 +876,7 @@
"ID": "MenuBarFileActionsInstallKeysFromFile",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel aus KEYS oder ZIP installieren",
"el_GR": "",
"en_US": "Install keys from KEYS or ZIP",
"es_ES": "Instalar keys de KEYS o ZIP",
@@ -901,7 +901,7 @@
"ID": "MenuBarFileActionsInstallKeysFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel aus einem Verzeichnis installieren",
"el_GR": "",
"en_US": "Install keys from a directory",
"es_ES": "Instalar keys de un directorio",
@@ -1001,7 +1001,7 @@
"ID": "MenuBarActionsXCITrimmer",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "XCI-Dateien trimmen",
"el_GR": "",
"en_US": "Trim XCI Files",
"es_ES": "Recortar archivos XCI",
@@ -1226,7 +1226,7 @@
"ID": "MenuBarHelpFaqAndGuides",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "FAQ & Anleitungen",
"el_GR": "",
"en_US": "FAQ & Guides",
"es_ES": "",
@@ -1251,7 +1251,7 @@
"ID": "MenuBarHelpFaq",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "FAQ & Fehlerbehebung Seite",
"el_GR": "",
"en_US": "FAQ & Troubleshooting Page",
"es_ES": "",
@@ -1276,7 +1276,7 @@
"ID": "MenuBarHelpFaqTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Öffnet die FAQ- und Fehlerbehebungsseite im offiziellen Ryujinx-Wiki",
"el_GR": "",
"en_US": "Opens the FAQ and Troubleshooting page on the official Ryujinx wiki",
"es_ES": "",
@@ -1301,7 +1301,7 @@
"ID": "MenuBarHelpSetup",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Setup- und Konfigurationsanleitung",
"el_GR": "",
"en_US": "Setup & Configuration Guide",
"es_ES": "",
@@ -1326,7 +1326,7 @@
"ID": "MenuBarHelpSetupTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Öffnet die Setup- und Konfigurationsanleitung im offiziellen Ryujinx-Wiki",
"el_GR": "",
"en_US": "Opens the Setup & Configuration guide on the official Ryujinx wiki",
"es_ES": "",
@@ -1351,7 +1351,7 @@
"ID": "MenuBarHelpMultiplayer",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Multiplayer (LDN/LAN) Anleitung",
"el_GR": "",
"en_US": "Multiplayer (LDN/LAN) Guide",
"es_ES": "",
@@ -1376,7 +1376,7 @@
"ID": "MenuBarHelpMultiplayerTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Öffnet die Multiplayer-Anleitung im offiziellen Ryujinx-Wiki",
"el_GR": "",
"en_US": "Opens the Multiplayer guide on the official Ryujinx wiki",
"es_ES": "",
@@ -1526,7 +1526,7 @@
"ID": "GameListHeaderDeveloper",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Entwickelt von {0}",
"el_GR": "",
"en_US": "Developed by {0}",
"es_ES": "",
@@ -1826,7 +1826,7 @@
"ID": "GameListHeaderCompatibilityStatus",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Kompatibilität:",
"el_GR": "",
"en_US": "Compatibility:",
"es_ES": "",
@@ -2614,7 +2614,7 @@
"pl_PL": "",
"pt_BR": "Extraia o RomFS de um arquivo DLC selecionado",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Extrahera RomFS från en vald DLC-fil",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -3062,7 +3062,7 @@
"ko_KR": "XCI 파일 '{0}' 트리밍",
"no_NO": "Trimming av XCI-filen '{0}'",
"pl_PL": "",
"pt_BR": "Reduzindo arquivo XCI '{0}",
"pt_BR": "Reduzindo arquivo XCI '{0}'",
"ru_RU": "Обрезается XCI файл '{0}'",
"sv_SE": "Optimerar XCI-filen '{0}'",
"th_TH": "",
@@ -4621,7 +4621,7 @@
"zh_CN": "繁体中文(推荐)",
"zh_TW": "正體中文 (建議)"
}
},
},
{
"ID": "SettingsTabSystemSystemLanguageSwedish",
"Translations": {
@@ -4646,7 +4646,7 @@
"zh_CN": "瑞典语",
"zh_TW": ""
}
},
},
{
"ID": "SettingsTabSystemSystemLanguageNorwegian",
"Translations": {
@@ -5922,6 +5922,31 @@
"zh_TW": "啟用警告日誌"
}
},
{
"ID": "SettingsTabHotkeysCycleControllers",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Cycle Controllers",
"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": "SettingsTabLoggingEnableErrorLogs",
"Translations": {
@@ -6464,7 +6489,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Ok",
"th_TH": "ตกลง",
"tr_TR": "Tamam",
"uk_UA": "",
@@ -8364,7 +8389,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Inaktivera",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8389,7 +8414,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Regnbåge",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8439,7 +8464,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Färg",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -15239,7 +15264,7 @@
"pl_PL": "Seria Amiibo",
"pt_BR": "Franquia Amiibo",
"ru_RU": "Серия Amiibo",
"sv_SE": "",
"sv_SE": "Amiibo-serie",
"th_TH": "",
"tr_TR": "Amiibo Serisi",
"uk_UA": "Серія Amiibo",
@@ -18664,7 +18689,7 @@
"pl_PL": "",
"pt_BR": "{0} - Informação",
"ru_RU": "{0} - Информация",
"sv_SE": "",
"sv_SE": "{0} - Information",
"th_TH": "{0} ข้อมูล",
"tr_TR": "{0} - Bilgi",
"uk_UA": "{0} - Інформація",
@@ -19739,7 +19764,7 @@
"pl_PL": "",
"pt_BR": "Configurações de LED",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "LED-inställningar",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -21939,7 +21964,7 @@
"pl_PL": "Głoś",
"pt_BR": "",
"ru_RU": "Громкость",
"sv_SE": "",
"sv_SE": "Volym",
"th_TH": "ระดับเสียง",
"tr_TR": "Ses",
"uk_UA": "Гуч.",
@@ -23351,7 +23376,7 @@
"ID": "SettingsTabSystemVSyncModeTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. \"Unbounded\" ist eine unbegrenzte Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate.",
"es_ES": "",
@@ -23376,7 +23401,7 @@
"ID": "SettingsTabSystemVSyncModeTooltipCustom",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. „Unbounded“ ist eine unbegrenzte Bildwiederholfrequenz. „Benutzerdefinierte Bildwiederholfrequenz“ emuliert die angegebene benutzerdefinierte Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate. 'Custom Refresh Rate' emulates the specified custom refresh rate.",
"es_ES": "",
@@ -23401,7 +23426,7 @@
"ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLassen Sie diese Option deaktiviert, wenn Sie sich nicht sicher sind.",
"el_GR": "",
"en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
"es_ES": "",
@@ -23426,7 +23451,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalValueTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Der Zielwert für die benutzerdefinierte Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "The custom refresh rate target value.",
"es_ES": "",
@@ -23451,7 +23476,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalSliderTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Die benutzerdefinierte Bildwiederholfrequenz als Prozentsatz der normalen Switch-Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "The custom refresh rate, as a percentage of the normal Switch refresh rate.",
"es_ES": "",
@@ -23476,7 +23501,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalPercentage",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz %:",
"el_GR": "",
"en_US": "Custom Refresh Rate %:",
"es_ES": "",
@@ -23501,7 +23526,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalValue",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Wert für benutzerdefinierte Bildwiederholfrequenz:",
"el_GR": "",
"en_US": "Custom Refresh Rate Value:",
"es_ES": "",
@@ -23551,7 +23576,7 @@
"ID": "SettingsTabHotkeysToggleVSyncModeHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "VSync-Modus umschalten:",
"el_GR": "",
"en_US": "Toggle VSync mode:",
"es_ES": "",
@@ -23576,7 +23601,7 @@
"ID": "SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz erhöhen:",
"el_GR": "",
"en_US": "Raise custom refresh rate",
"es_ES": "",
@@ -23601,7 +23626,7 @@
"ID": "SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz senken:",
"el_GR": "",
"en_US": "Lower custom refresh rate:",
"es_ES": "",
@@ -23626,7 +23651,7 @@
"ID": "CompatibilityListLastUpdated",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Zuletzt aktualisiert: {0}",
"el_GR": "",
"en_US": "Last updated: {0}",
"es_ES": "",
@@ -23651,7 +23676,7 @@
"ID": "CompatibilityListWarning",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Diese Kompatibilitätsliste könnte veraltete Einträge enthalten. Teste dennoch Spiele im \"Ingame\"-Status.",
"el_GR": "",
"en_US": "This compatibility list might contain out of date entries.\nDo not be opposed to testing games in the \"Ingame\" status.",
"es_ES": "",
@@ -23676,7 +23701,7 @@
"ID": "CompatibilityListSearchBoxWatermark",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Kompatibilitätseinträge durchsuchen...",
"el_GR": "",
"en_US": "Search compatibility entries...",
"es_ES": "",
@@ -23726,7 +23751,7 @@
"ID": "CompatibilityListOnlyShowOwnedGames",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Nur eigene Spiele anzeigen",
"el_GR": "",
"en_US": "Only show owned games",
"es_ES": "",
@@ -23751,7 +23776,7 @@
"ID": "CompatibilityListPlayable",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Spielbar",
"el_GR": "",
"en_US": "Playable",
"es_ES": "",
@@ -23776,7 +23801,7 @@
"ID": "CompatibilityListIngame",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Im Spiel",
"el_GR": "",
"en_US": "Ingame",
"es_ES": "",
@@ -23951,7 +23976,7 @@
"ID": "CompatibilityListBootsTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Startet, kommt aber nicht über den Titelbildschirm hinaus.",
"el_GR": "",
"en_US": "Boots but does not make it past the title screen.",
"es_ES": "",
@@ -23976,7 +24001,7 @@
"ID": "CompatibilityListNothingTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Startet nicht oder zeigt keine Anzeichen von Aktivität.",
"el_GR": "",
"en_US": "Does not boot or shows no signs of activity.",
"es_ES": "",
@@ -24001,7 +24026,7 @@
"ID": "ExtractAocListHeader",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Wähle ein DLC zum Extrahieren aus",
"el_GR": "",
"en_US": "Select a DLC to Extract",
"es_ES": "",
@@ -24014,7 +24039,7 @@
"pl_PL": "",
"pt_BR": "Selecione um DLC para extrair",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Välj en DLC att extrahera",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",

View File

@@ -14,5 +14,13 @@ namespace Ryujinx.Ava.Common
VolumeDown,
CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement,
CycleControllersPlayer1,
CycleControllersPlayer2,
CycleControllersPlayer3,
CycleControllersPlayer4,
CycleControllersPlayer5,
CycleControllersPlayer6,
CycleControllersPlayer7,
CycleControllersPlayer8
}
}

View File

@@ -1,6 +1,11 @@
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Configuration.Hid;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
namespace Ryujinx.Ava.UI.Models.Input
{
@@ -28,8 +33,15 @@ namespace Ryujinx.Ava.UI.Models.Input
[ObservableProperty] private Key _customVSyncIntervalDecrement;
public ObservableCollection<CycleController> CycleControllers { get; set; } = new ObservableCollection<CycleController>();
public ICommand AddCycleController { get; set; }
public ICommand RemoveCycleController { get; set; }
public bool CanRemoveCycleController => CycleControllers.Count > 0 && CycleControllers.Count < 8;
public HotkeyConfig(KeyboardHotkeys config)
{
AddCycleController = MiniCommand.Create(() => CycleControllers.Add(new CycleController(CycleControllers.Count + 1, Key.Unbound)));
RemoveCycleController = MiniCommand.Create(() => CycleControllers.Remove(CycleControllers.Last()));
if (config == null)
return;
@@ -44,6 +56,7 @@ namespace Ryujinx.Ava.UI.Models.Input
VolumeDown = config.VolumeDown;
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
CycleControllers.AddRange((config.CycleControllers ?? []).Select((x, i) => new CycleController(i + 1, x)));
}
public KeyboardHotkeys GetConfig() =>
@@ -60,6 +73,7 @@ namespace Ryujinx.Ava.UI.Models.Input
VolumeDown = VolumeDown,
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
CycleControllers = CycleControllers.Select(x => x.Hotkey).ToList()
};
}
}

View File

@@ -0,0 +1,48 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Configuration.Hid;
namespace Ryujinx.Ava.UI.ViewModels
{
public class CycleController : BaseModel
{
private string _player;
private Key _hotkey;
public string Player
{
get => _player;
set
{
_player = value;
OnPropertyChanged(nameof(Player));
}
}
public Key Hotkey
{
get => _hotkey;
set
{
_hotkey = value;
OnPropertyChanged(nameof(Hotkey));
}
}
public CycleController(int v, Key x)
{
Player = v switch
{
1 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer1],
2 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer2],
3 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer3],
4 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer4],
5 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer5],
6 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer6],
7 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer7],
8 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer8],
_ => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer] + " " + v
};
Hotkey = x;
}
}
}

View File

@@ -897,5 +897,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
AvaloniaKeyboardDriver.Dispose();
}
public void CyclePlayerDevice(int player)
{
LoadDevices();
PlayerId = (PlayerIndex)player;
Device = (Device + 1) % Devices.Count;
Save();
}
}
}

View File

@@ -1,4 +1,4 @@
<UserControl
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -15,17 +15,18 @@
<viewModels:SettingsViewModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="StackPanel > StackPanel">
<Style Selector="StackPanel StackPanel">
<Setter Property="Margin" Value="10, 0, 0, 0" />
<Setter Property="Orientation" Value="Horizontal" />
</Style>
<Style Selector="StackPanel > StackPanel > TextBlock">
<Style Selector="StackPanel StackPanel > TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Width" Value="230" />
</Style>
<Style Selector="ToggleButton">
<Style Selector="ToggleButton, Button">
<Setter Property="Width" Value="90" />
<Setter Property="Height" Value="27" />
<Setter Property="Padding" Value="0,5,0,5" /> <!-- Added vertical padding -->
</Style>
<Style Selector="ToggleButton > TextBlock">
<Setter Property="TextAlignment" Value="Center" />
@@ -39,79 +40,123 @@
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Name="SettingButtons"
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
Spacing="10"
Name="SettingButtons">
<TextBlock
Classes="h1"
Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
<ToggleButton Name="ToggleVSyncMode">
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
<StackPanel
Margin="10,0,0,0"
Spacing="10"
Orientation="Vertical">
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
<ToggleButton Name="ToggleVSyncMode">
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
<ToggleButton Name="Screenshot">
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
<ToggleButton Name="ShowUI">
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
<ToggleButton Name="Pause">
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
<ToggleButton Name="ToggleMute">
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
<ToggleButton Name="ResScaleUp">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
<ToggleButton Name="ResScaleDown">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
<ToggleButton Name="VolumeUp">
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
<ToggleButton Name="VolumeDown">
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalIncrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalDecrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
<ToggleButton Name="Screenshot">
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
<ToggleButton Name="ShowUI">
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
<ToggleButton Name="Pause">
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
<ToggleButton Name="ToggleMute">
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
<ToggleButton Name="ResScaleUp">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
<ToggleButton Name="ResScaleDown">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
<ToggleButton Name="VolumeUp">
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
<ToggleButton Name="VolumeDown">
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalIncrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalDecrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
<Separator Height="1" />
<StackPanel Margin="0">
<TextBlock
Classes="h1"
Text="{ext:Locale SettingsTabHotkeysCycleControllers}" />
<StackPanel Orientation="Horizontal" Spacing="10">
<Button
Content="{ext:Locale SettingsTabGeneralAdd}"
Margin="10,0,0,0"
Command="{Binding KeyboardHotkey.AddCycleController}" />
<Button
Content="{ext:Locale SettingsTabGeneralRemove}"
IsEnabled="{Binding KeyboardHotkey.CanRemoveCycleController}"
Command="{Binding KeyboardHotkey.RemoveCycleController}" />
</StackPanel>
</StackPanel>
<ItemsControl ItemsSource="{Binding KeyboardHotkey.CycleControllers}"
Name="CycleControllers">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
Margin="10,0,0,0"
Orientation="Vertical"
Spacing="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Player}" />
<ToggleButton>
<TextBlock
Text="{Binding Hotkey, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</ScrollViewer>

View File

@@ -3,11 +3,14 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
using DynamicData;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System.Linq;
using Button = Ryujinx.Input.Button;
using Key = Ryujinx.Common.Configuration.Hid.Key;
@@ -21,16 +24,21 @@ namespace Ryujinx.Ava.UI.Views.Settings
public SettingsHotkeysView()
{
InitializeComponent();
RegisterEvents();
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
CycleControllers.LayoutUpdated += (_, _1) => RegisterEvents();
}
private void RegisterEvents()
{
foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
{
if (visual is ToggleButton button and not CheckBox)
{
button.IsCheckedChanged -= Button_IsCheckedChanged;
button.IsCheckedChanged += Button_IsCheckedChanged;
}
}
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
@@ -116,6 +124,13 @@ namespace Ryujinx.Ava.UI.Views.Settings
case "CustomVSyncIntervalDecrement":
viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>();
break;
default:
var index = button.FindAncestorOfType<ItemsControl>().GetLogicalDescendants().OfType<ToggleButton>().IndexOf(button);
if (index >= 0 && viewModel.KeyboardHotkey.CycleControllers != null)
{
viewModel.KeyboardHotkey.CycleControllers[index].Hotkey = buttonValue.AsHidType<Key>();
}
break;
}
}
};