UI - Avalonia Part 2 (#3351)

* add settings windows and children views

* Expose hotkeys configuration on the UI

* Remove double spacing from locale JSON

* simplify button assigner

* add cemuhook buttons and title to locale

* move common button assigner to own class

* cancel button assigner when window is closed

* remove unused setting

* address review. fix controller profile not loading default when switching devices

* fix updater file name

* Input cleanup (#37)

* addressed review

* add device type to controller device checks

* change accessibility modifier of public classes to internal

* Update Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update de_DE.json

* Update de_DE.json

* Update tr_TR.json

Translated newly added lines

* Update it_IT.json

* fix rebase

* update avalonia

* fix wrong key used for button text

* Align settings window elements

* Tabs to spaces

* Update brazilian portuguese translation

* Minor improvement on brazilian portuguese translation

* fix turkish translation

* remove unused text

* change view related classes to public

* unsubscribe from deferred event if dialog is closed

* Load the default language before loading any other when switching languages

* Make controller settings more compact

* increase default width of settings window, reduce profile buttons width

Co-authored-by: gdk <gab.dark.100@gmail.com>
Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com>
Co-authored-by: Niwu34 <67392333+Niwu34@users.noreply.github.com>
Co-authored-by: aegiff <99728970+aegiff@users.noreply.github.com>
Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com>
This commit is contained in:
Emmanuel Hansen 2022-07-05 18:06:31 +00:00 committed by GitHub
parent d21b403886
commit 594246ea47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 4631 additions and 145 deletions

View file

@ -2,12 +2,16 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Styling;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.Diagnostics;
using System.IO;
namespace Ryujinx.Ava
@ -45,7 +49,30 @@ namespace Ryujinx.Ava
private void ShowRestartDialog()
{
// TODO: Implement Restart Dialog when SettingsWindow is implemented.
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Dispatcher.UIThread.InvokeAsync(async () =>
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var result = await ContentDialogHelper.CreateConfirmationDialog(
(desktop.MainWindow as MainWindow).SettingsWindow,
LocaleManager.Instance["DialogThemeRestartMessage"],
LocaleManager.Instance["DialogThemeRestartSubMessage"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["DialogRestartRequiredMessage"]);
if (result == UserResult.Yes)
{
var path = Process.GetCurrentProcess().MainModule.FileName;
var info = new ProcessStartInfo() { FileName = path, UseShellExecute = false };
var proc = Process.Start(info);
desktop.Shutdown();
Environment.Exit(0);
}
}
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
private void ThemeChanged_Event(object sender, ReactiveEventArgs<string> e)

View file

@ -50,7 +50,7 @@ using WindowState = Avalonia.Controls.WindowState;
namespace Ryujinx.Ava
{
public class AppHost
internal class AppHost
{
private const int CursorHideIdleTime = 8; // Hide Cursor seconds

View file

@ -20,7 +20,7 @@
"MenuBarOptionsManageUserProfiles": "_Profilverwaltung",
"MenuBarActions": "_Aktionen",
"MenuBarOptionsSimulateWakeUpMessage": "Aufwachnachricht",
"MenuBarActionsScanAmiibo": "Amiibo scannen",
"MenuBarActionsScanAmiibo": "Virtualisiere Amiibo",
"MenuBarTools": "_Werkzeuge",
"MenuBarToolsInstallFirmware": "Firmware installieren",
"MenuBarFileToolsInstallFirmwareFromFile": "Installiere Firmware von einer XCI oder einer ZIP Datei",
@ -242,9 +242,7 @@
"ControllerSettingsExtraButtonsRight": "Rechte Aktionstasten",
"ControllerSettingsMisc": "Verschiedenes",
"ControllerSettingsTriggerThreshold": "Empfindlichkeit:",
"ControllerSettingsMotion": "Bewegungssteuerung",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Aktiviere Bewegungssteuerung",
"ControllerSettingsMotion": "Bewegung",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook kompatible Bewegungssteuerung",
"ControllerSettingsMotionControllerSlot": "Controller Slot:",
"ControllerSettingsMotionMirrorInput": "Spiegele Eingabe",
@ -290,9 +288,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Hell",
"SettingsTabGeneralThemeEnableCustomTheme": "Benutzerdefiniertes Thema",
"ButtonBrowse": "Durchsuchen",
"ControllerSettingsMotionConfigureCemuHookSettings": "CemuHook Motion konfigurieren",
"ControllerSettingsConfigureGeneral": "Konfigurieren",
"ControllerSettingsRumble": "Vibration",
"ControllerSettingsRumbleEnable": "Aktiviere Vibration",
"ControllerSettingsRumbleStrongMultiplier": "Starke Vibration - Multiplikator",
"ControllerSettingsRumbleWeakMultiplier": "Schwache Vibration - Multiplikator",
"DialogMessageSaveNotAvailableMessage": "Es existieren keine Speicherdaten für {0} [{1:x16}]",
@ -329,7 +326,7 @@
"DialogUpdaterNoInternetMessage": "Es besteht keine Verbindung mit dem Internet!",
"DialogUpdaterNoInternetSubMessage": "Bitte vergewissern, dass eine funktionierende Internetverbindung existiert!",
"DialogUpdaterDirtyBuildMessage": "Inoffizielle Versionen von Ryujinx können nicht aktualisiert werden",
"DialogUpdaterDirtyBuildSubMessage": "Für eine unterstütze Version: Ryujinx bitte von hier herunterladen https://ryujinx.org/",
"DialogUpdaterDirtyBuildSubMessage": "Lade Ryujinx bitte von hier herunter, um eine unterstützte Version zu erhalten: https://ryujinx.org/",
"DialogRestartRequiredMessage": "Neustart erforderlich",
"DialogThemeRestartMessage": "Das Thema wurde gespeichert. Ein Neustart ist erforderlich, um das Thema anzuwenden.",
"DialogThemeRestartSubMessage": "Jetzt neu starten?",
@ -485,15 +482,13 @@
"EnableInternetAccessTooltip": "Aktiviert den Gast-Internet-Zugang. Die Anwendung verhält sich so, als ob die emulierte Switch-Konsole mit dem Internet verbunden wäre. Beachte, dass in einigen Fällen Anwendungen auch bei deaktivierter Option auf das Internet zugreifen können",
"GameListContextMenuManageCheatToolTip": "Öffnet den Cheat-Manager",
"GameListContextMenuManageCheat": "Cheats verwalten",
"ControllerSettingsStickRange": "Bereich",
"ControllerSettingsStickRange": "Bereich:",
"DialogStopEmulationTitle": "Ryujinx - Beende Emulation",
"DialogStopEmulationMessage": "Emulation wirklich beenden?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "Audio",
"SettingsTabNetwork": "Netzwerk",
"SettingsTabNetworkConnection": "Netwerkverbindung",
"SettingsTabGraphicsFrameRate": "Host Aktualisierungsrate:",
"SettingsTabGraphicsFrameRateTooltip": "Aktiviert die Host Aktualisierungsrate. Auf 0 setzen, um den Grenzwert aufzuheben.",
"SettingsTabCpuCache": "CPU-Cache",
"SettingsTabCpuMemory": "CPU-Speicher",
"DialogUpdaterFlatpakNotSupportedMessage": "Bitte Aktualisiere Ryujinx mit FlatHub",
@ -544,6 +539,22 @@
"LoadingHeading": "{0} wird gestartet",
"CompilingPPTC": "PTC wird kompiliert",
"CompilingShaders": "Shader werden kompiliert",
"AllKeyboards": "Alle Tastaturen",
"OpenFileDialogTitle": "Wähle eine unterstützte Datei",
"OpenFolderDialogTitle": "Wähle einen Ordner mit einem entpackten Spiel",
"AllSupportedFormats": "Alle unterstützten Formate",
"RyujinxUpdater": "Ryujinx - Updater",
"SettingsTabHotkeys": "Tastatur Hotkeys",
"SettingsTabHotkeysHotkeys": "Tastatur Hotkeys",
"SettingsTabHotkeysToggleVsyncHotkey": "Aktiviert/Deaktiviert VSync:",
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
"SettingsTabHotkeysShowUiHotkey": "Zeige UI:",
"SettingsTabHotkeysPauseHotkey": "Pausieren:",
"SettingsTabHotkeysToggleMuteHotkey": "Stummschalten:",
"ControllerMotionTitle": "Bewegungssteuerung - Einstellungen",
"ControllerRumbleTitle": "Vibration - Einstellungen",
"SettingsSelectThemeFileDialogTitle" : "Wähle ein benutzerdefiniertes Thema",
"SettingsXamlThemeFile" : "Xaml Thema-Datei",
"SettingsTabGraphicsBackend" : "Grafik-Backend",
"GraphicsBackendTooltip" : "Ändert das Grafik-Backend"
}

View file

@ -243,8 +243,6 @@
"ControllerSettingsMisc": "Διάφορα",
"ControllerSettingsTriggerThreshold": "Κατώφλι Σκανδάλης:",
"ControllerSettingsMotion": "Κίνηση",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Ενεργοποίηση Κίνησης",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Κίνηση συμβατή με CemuHook",
"ControllerSettingsMotionControllerSlot": "Υποδοχή Χειριστηρίου:",
"ControllerSettingsMotionMirrorInput": "Καθρεπτισμός Χειρισμού",
@ -290,9 +288,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Ανοιχτό",
"SettingsTabGeneralThemeEnableCustomTheme": "Ενεργοποίηση Προσαρμοσμένου Θέματος",
"ButtonBrowse": "Αναζήτηση",
"ControllerSettingsMotionConfigureCemuHookSettings": "Ρύθμιση Παραμέτρων Κίνησης CemuHook",
"ControllerSettingsConfigureGeneral": "Παραμέτρων",
"ControllerSettingsRumble": "Δόνηση",
"ControllerSettingsRumbleEnable": "Ενεργοποίηση Δόνησης",
"ControllerSettingsRumbleStrongMultiplier": "Ισχυρός Πολλαπλασιαστής Δόνησης",
"ControllerSettingsRumbleWeakMultiplier": "Αδύναμος Πολλαπλασιαστής Δόνησης",
"DialogMessageSaveNotAvailableMessage": "Δεν υπάρχουν αποθηκευμένα δεδομένα για το {0} [{1:x16}]",
@ -485,15 +482,15 @@
"EnableInternetAccessTooltip": "Επιτρέπει την πρόσβαση επισκέπτη στο Διαδίκτυο. Εάν ενεργοποιηθεί, η εξομοιωμένη κονσόλα Switch θα συμπεριφέρεται σαν να είναι συνδεδεμένη στο Διαδίκτυο. Λάβετε υπόψη ότι σε ορισμένες περιπτώσεις, οι εφαρμογές ενδέχεται να εξακολουθούν να έχουν πρόσβαση στο Διαδίκτυο, ακόμη και όταν αυτή η επιλογή είναι απενεργοποιημένη",
"GameListContextMenuManageCheatToolTip" : "Διαχείριση Κόλπων",
"GameListContextMenuManageCheat" : "Διαχείριση Κόλπων",
"ControllerSettingsStickRange" : "Εύρος",
"ControllerSettingsStickRange" : "Εύρος:",
"DialogStopEmulationTitle" : "Ryujinx - Διακοπή εξομοίωσης",
"DialogStopEmulationMessage": "Είστε βέβαιοι ότι θέλετε να σταματήσετε την εξομοίωση;",
"SettingsTabCpu": "Επεξεργαστής",
"SettingsTabAudio": "Ήχος",
"SettingsTabNetwork": "Δίκτυο",
"SettingsTabNetworkConnection" : "Σύνδεση δικτύου",
"SettingsTabGraphicsFrameRate" : "Ρυθμός Ανανέωσης Υπολογιστή:",
"SettingsTabGraphicsFrameRateTooltip" : "Προκαθορίζει το ρυθμό ανανέωσης του υπολογιστή. Ορίστε το στο 0 για να αφαιρέσετε το όριο.",
"SettingsTabCpuCache" : "Προσωρινή Μνήμη CPU",
"SettingsTabCpuMemory" : "Μνήμη CPU"
"SettingsTabCpuMemory" : "Μνήμη CPU",
"ControllerMotionTitle": "Motion Control Settings",
"ControllerRumbleTitle": "Rumble Settings"
}

View file

@ -243,8 +243,6 @@
"ControllerSettingsMisc": "Miscellaneous",
"ControllerSettingsTriggerThreshold": "Trigger Threshold:",
"ControllerSettingsMotion": "Motion",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Enable Motion Controls",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Use CemuHook compatible motion",
"ControllerSettingsMotionControllerSlot": "Controller Slot:",
"ControllerSettingsMotionMirrorInput": "Mirror Input",
@ -290,9 +288,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Light",
"SettingsTabGeneralThemeEnableCustomTheme": "Enable Custom Theme",
"ButtonBrowse": "Browse",
"ControllerSettingsMotionConfigureCemuHookSettings": "Configure CemuHook Motion",
"ControllerSettingsConfigureGeneral": "Configure",
"ControllerSettingsRumble": "Rumble",
"ControllerSettingsRumbleEnable": "Enable Rumble",
"ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier",
"ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier",
"DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]",
@ -485,15 +482,13 @@
"EnableInternetAccessTooltip": "Enables guest Internet access. If enabled, the application will behave as if the emulated Switch console was connected to the Internet. Note that in some cases, applications may still access the Internet even with this option disabled",
"GameListContextMenuManageCheatToolTip": "Manage Cheats",
"GameListContextMenuManageCheat": "Manage Cheats",
"ControllerSettingsStickRange": "Range",
"ControllerSettingsStickRange": "Range:",
"DialogStopEmulationTitle": "Ryujinx - Stop Emulation",
"DialogStopEmulationMessage": "Are you sure you want to stop emulation?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "Audio",
"SettingsTabNetwork": "Network",
"SettingsTabNetworkConnection": "Network Connection",
"[REMOVE]SettingsTabGraphicsFrameRate": "Host Refresh Rate:",
"[REMOVE]SettingsTabGraphicsFrameRateTooltip": "Sets host refresh rate. Set to 0 to remove limit.",
"SettingsTabCpuCache": "CPU Cache",
"SettingsTabCpuMemory": "CPU Memory",
"DialogUpdaterFlatpakNotSupportedMessage": "Please update Ryujinx via FlatHub.",
@ -548,5 +543,16 @@
"OpenFileDialogTitle": "Select a supported file to open",
"OpenFolderDialogTitle": "Select a folder with an unpacked game",
"AllSupportedFormats": "All Supported Formats",
"RyujinxUpdater": "Ryujinx Updater"
"RyujinxUpdater": "Ryujinx Updater",
"SettingsTabHotkeys": "Keyboard Hotkeys",
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
"SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:",
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
"SettingsTabHotkeysPauseHotkey": "Pause:",
"SettingsTabHotkeysToggleMuteHotkey": "Mute:",
"ControllerMotionTitle": "Motion Control Settings",
"ControllerRumbleTitle": "Rumble Settings",
"SettingsSelectThemeFileDialogTitle" : "Select Theme File",
"SettingsXamlThemeFile" : "Xaml Theme File"
}

View file

@ -242,8 +242,6 @@
"ControllerSettingsMisc": "Misceláneo",
"ControllerSettingsTriggerThreshold": "Límite de gatillos:",
"ControllerSettingsMotion": "Movimiento",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Habilitar controles por movimiento",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usar movimiento compatible con CemuHook",
"ControllerSettingsMotionControllerSlot": "Puerto del mando:",
"ControllerSettingsMotionMirrorInput": "Paralelizar derecho e izquierdo",
@ -289,9 +287,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Claro",
"SettingsTabGeneralThemeEnableCustomTheme": "Habilitar tema personalizado",
"ButtonBrowse": "Buscar",
"ControllerSettingsMotionConfigureCemuHookSettings": "Configurar controles por movimiento de CemuHook",
"ControllerSettingsConfigureGeneral": "Configurar",
"ControllerSettingsRumble": "Vibración",
"ControllerSettingsRumbleEnable": "Habilitar vibraciones",
"ControllerSettingsRumbleStrongMultiplier": "Multiplicador de vibraciones fuertes",
"ControllerSettingsRumbleWeakMultiplier": "Multiplicador de vibraciones débiles",
"DialogMessageSaveNotAvailableMessage": "No hay datos de guardado para {0} [{1:x16}]",
@ -483,15 +480,15 @@
"EnableInternetAccessTooltip": "Activa el acceso a Internet del guest. Cuando esté activo, la aplicación actuará como si la Nintendo Switch emulada estuviese conectada a Internet. Ten en cuenta que algunas aplicaciones pueden intentar acceder a Internet incluso con esta opción desactivada.",
"GameListContextMenuManageCheatToolTip" : "Activa o desactiva los cheats",
"GameListContextMenuManageCheat" : "Administrar cheats",
"ControllerSettingsStickRange" : "Alcance",
"ControllerSettingsStickRange" : "Alcance:",
"DialogStopEmulationTitle" : "Ryujinx - Detener emulación",
"DialogStopEmulationMessage": "¿Seguro que quieres detener la emulación actual?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "Audio",
"SettingsTabNetwork": "Red",
"SettingsTabNetworkConnection" : "Conexión de red",
"SettingsTabGraphicsFrameRate" : "Velocidad máxima de fotogramas:",
"SettingsTabGraphicsFrameRateTooltip" : "Fija el límite de fotogramas del host. Elige 0 para deshabilitar el límite.",
"SettingsTabCpuCache" : "Caché de CPU",
"SettingsTabCpuMemory" : "Memoria de CPU"
"SettingsTabCpuMemory" : "Memoria de CPU",
"ControllerMotionTitle": "Motion Control Settings",
"ControllerRumbleTitle": "Rumble Settings"
}

View file

@ -235,8 +235,7 @@
"ControllerSettingsMisc": "Divers",
"ControllerSettingsTriggerThreshold": "Seuil de gachettes:",
"ControllerSettingsMotion": "Mouvements",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Activer le capteur de mouvements",
"ControllerSettingsConfigureGeneral": "Configurer",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Utiliser un capteur de mouvements CemuHook",
"ControllerSettingsMotionControllerSlot": "Contrôleur ID:",
"ControllerSettingsMotionMirrorInput": "Inverser les contrôles",
@ -266,5 +265,7 @@
"InputDialogAddNewProfileSubtext": "(Longueur max.: {0})",
"AvatarChoose": "Choisir",
"AvatarSetBackgroundColor": "Choisir une couleur de fond",
"AvatarClose": "Fermer"
"AvatarClose": "Fermer",
"ControllerMotionTitle": "Motion Control Settings",
"ControllerRumbleTitle": "Rumble Settings"
}

View file

@ -243,8 +243,6 @@
"ControllerSettingsMisc": "Miscellanee",
"ControllerSettingsTriggerThreshold": "Sensibilità dei grilletti:",
"ControllerSettingsMotion": "Movimento",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Attiva sensore di movimento",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usa sensore compatibile con CemuHook",
"ControllerSettingsMotionControllerSlot": "Slot del controller:",
"ControllerSettingsMotionMirrorInput": "Input specchiato",
@ -290,9 +288,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Chiara",
"SettingsTabGeneralThemeEnableCustomTheme": "Attiva tema personalizzato",
"ButtonBrowse": "Sfoglia",
"ControllerSettingsMotionConfigureCemuHookSettings": "Configura movimento CemuHook",
"ControllerSettingsConfigureGeneral": "Configura",
"ControllerSettingsRumble": "Vibrazione",
"ControllerSettingsRumbleEnable": "Attiva vibrazione",
"ControllerSettingsRumbleStrongMultiplier": "Moltiplicatore vibrazione forte",
"ControllerSettingsRumbleWeakMultiplier": "Moltiplicatore vibrazione debole",
"DialogMessageSaveNotAvailableMessage": "Non ci sono dati di salvataggio per {0} [{1:x16}]",
@ -461,7 +458,7 @@
"TraceLogTooltip": "Attiva messaggi trace log",
"GuestLogTooltip": "Attiva messaggi guest log",
"FileAccessLogTooltip": "Attiva messaggi file access log",
"FSAccessLogModeTooltip": "Attiva output FS access log alla console. Le mpdalità possibili sono 0-3",
"FSAccessLogModeTooltip": "Attiva output FS access log alla console. Le modalità possibili sono 0-3",
"DeveloperOptionTooltip": "Usa con attenzione",
"OpenGlLogLevel": "Richiede livelli di log appropriati abilitati",
"DebugLogTooltip": "Attiva messaggi debug log",
@ -485,15 +482,13 @@
"EnableInternetAccessTooltip": "Attiva il guest Internet access. Se abilitato, l'applicazione si comporterà come se la console Switch emulata fosse collegata a Internet. Si noti che in alcuni casi, le applicazioni possono comunque accedere a Internet anche con questa opzione disabilitata",
"GameListContextMenuManageCheatToolTip": "Gestisci Cheats",
"GameListContextMenuManageCheat": "Gestisci Cheats",
"ControllerSettingsStickRange": "Raggio",
"ControllerSettingsStickRange": "Raggio:",
"DialogStopEmulationTitle": "Ryujinx - Ferma emulazione",
"DialogStopEmulationMessage": "Sei sicuro di voler fermare l'emulazione?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "Audio",
"SettingsTabNetwork": "Rete",
"SettingsTabNetworkConnection": "Connessione di rete",
"SettingsTabGraphicsFrameRate": "Frequenza di aggiornamento dell'host:",
"SettingsTabGraphicsFrameRateTooltip": "Imposta la frequenza di aggiornamento dell'host. Imposta a 0 per rimuovere il limite.",
"SettingsTabCpuCache": "Cache CPU",
"SettingsTabCpuMemory": "Memoria CPU",
"DialogUpdaterFlatpakNotSupportedMessage": "Per favore aggiorna Ryujinx via FlatHub.",
@ -543,5 +538,21 @@
"ApiError": "Errore dell'API.",
"LoadingHeading": "Caricamento di {0}",
"CompilingPPTC": "Compilazione PTC",
"CompilingShaders": "Compilazione Shaders"
"CompilingShaders": "Compilazione Shaders",
"AllKeyboards": "Tutte le tastiere",
"OpenFileDialogTitle": "Seleziona un file supportato da aprire",
"OpenFolderDialogTitle": "Seleziona una cartella con un gioco estratto",
"AllSupportedFormats": "Tutti i formati supportati",
"RyujinxUpdater": "Ryujinx Updater",
"SettingsTabHotkeys": "Tasti di scelta rapida",
"SettingsTabHotkeysHotkeys": "Tasti di scelta rapida",
"SettingsTabHotkeysToggleVsyncHotkey": "VSync:",
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
"SettingsTabHotkeysShowUiHotkey": "Mostra UI:",
"SettingsTabHotkeysPauseHotkey": "Metti in pausa:",
"SettingsTabHotkeysToggleMuteHotkey": "Muta:",
"ControllerMotionTitle": "Impostazioni dei sensori di movimento",
"ControllerRumbleTitle": "Impostazioni di vibrazione",
"SettingsSelectThemeFileDialogTitle" : "Seleziona file del tema",
"SettingsXamlThemeFile" : "File del tema xaml"
}

View file

@ -242,8 +242,6 @@
"ControllerSettingsMisc": "여러 가지 잡다한",
"ControllerSettingsTriggerThreshold": "트리거 임계값 :",
"ControllerSettingsMotion": "운동",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "모션 컨트롤 활성화",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook 호환 모션 사용",
"ControllerSettingsMotionControllerSlot": "컨트롤러 슬롯 :",
"ControllerSettingsMotionMirrorInput": "미러 입력",
@ -289,9 +287,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "빛",
"SettingsTabGeneralThemeEnableCustomTheme": "사용자 정의 테마 활성화",
"ButtonBrowse": "검색",
"ControllerSettingsMotionConfigureCemuHookSettings": "CemuHook 모션 구성",
"ControllerSettingsConfigureGeneral": "션 구성",
"ControllerSettingsRumble": "하인 좌석",
"ControllerSettingsRumbleEnable": "럼블을 활성화",
"ControllerSettingsRumbleStrongMultiplier": "강력한 럼블 배율기",
"ControllerSettingsRumbleWeakMultiplier": "약한 럼블 승수",
"DialogMessageSaveNotAvailableMessage": "에 대한 세이브 데이터가 없습니다 {0} [{1:x16}]",
@ -483,15 +480,15 @@
"EnableInternetAccessTooltip": "게스트 인터넷 액세스를 활성화합니다. 활성화된 경우 응용 프로그램은 에뮬레이트된 스위치 콘솔이 인터넷에 연결된 것처럼 작동합니다. 경우에 따라 이 옵션이 비활성화된 경우에도 응용 프로그램이 인터넷에 계속 액세스할 수 있습니다",
"GameListContextMenuManageCheatToolTip" : "치트 관리",
"GameListContextMenuManageCheat" : "치트 관리",
"ControllerSettingsStickRange" : "범위",
"ControllerSettingsStickRange" : "범위:",
"DialogStopEmulationTitle" : "Ryujinx - 에뮬레이션 중지",
"DialogStopEmulationMessage": "에뮬레이션을 중지하시겠습니까?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "오디오",
"SettingsTabNetwork": "회로망",
"SettingsTabNetworkConnection" : "네트워크 연결",
"SettingsTabGraphicsFrameRate" : "호스트 새로 고침 빈도 :",
"SettingsTabGraphicsFrameRateTooltip" : "호스트 새로 고침 빈도를 설정합니다. 제한을 제거하려면 0으로 설정하십시오.",
"SettingsTabCpuCache" : "CPU 캐시",
"SettingsTabCpuMemory" : "CPU 메모리"
"SettingsTabCpuMemory" : "CPU 메모리",
"ControllerMotionTitle": "Motion Control Settings",
"ControllerRumbleTitle": "Rumble Settings"
}

View file

@ -243,8 +243,6 @@
"ControllerSettingsMisc": "Miscelâneas",
"ControllerSettingsTriggerThreshold": "Sensibilidade do gatilho:",
"ControllerSettingsMotion": "Sensor de movimento",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Habilitar sensor de movimento",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usar sensor compatível com CemuHook",
"ControllerSettingsMotionControllerSlot": "Slot do controle:",
"ControllerSettingsMotionMirrorInput": "Espelhar movimento",
@ -290,9 +288,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Claro",
"SettingsTabGeneralThemeEnableCustomTheme": "Habilitar tema customizado",
"ButtonBrowse": "Procurar",
"ControllerSettingsMotionConfigureCemuHookSettings": "Configurar sensor de movimento CemuHook",
"ControllerSettingsConfigureGeneral": "Configurar",
"ControllerSettingsRumble": "Vibração",
"ControllerSettingsRumbleEnable": "Habilitar vibração",
"ControllerSettingsRumbleStrongMultiplier": "Multiplicador de vibração forte",
"ControllerSettingsRumbleWeakMultiplier": "Multiplicador de vibração fraca",
"DialogMessageSaveNotAvailableMessage": "Não há jogos salvos para {0} [{1:x16}]",
@ -442,7 +439,7 @@
"MemoryManagerTooltip": "Muda como a memória do sistema convidado é acessada. Tem um grande impacto na performance da CPU emulada.",
"MemoryManagerSoftwareTooltip": "Usar uma tabela de página via software para tradução de endereços. Maior precisão, porém performance mais baixa.",
"MemoryManagerHostTooltip": "Mapeia memória no espaço de endereço hóspede diretamente. Compilação e execução do JIT muito mais rápida.",
"MemoryManagerUnsafeTooltip": "Mapeia memória diretamente, mas sem limitar o endereço ao espaço de endereço do sistema convidado antes de acessar. Mais rápido, porém menos seguro. O aplicativo convidado pode acessar memória de qualquer parte do Ryujinx, então apenas rode programas em que você confia nesse modo.",
"MemoryManagerUnsafeTooltip": "Mapeia memória diretamente, mas sem limitar o acesso ao espaço de endereçamento do sistema convidado. Mais rápido, porém menos seguro. O aplicativo convidado pode acessar memória de qualquer parte do Ryujinx, então apenas rode programas em que você confia nesse modo.",
"DRamTooltip": "Expande a memória do sistema emulado de 4GB para 6GB",
"IgnoreMissingServicesTooltip": "Habilita ou desabilita a opção de ignorar serviços não implementados",
"GraphicsBackendThreadingTooltip": "Habilita multithreading do backend gráfico",
@ -485,15 +482,13 @@
"EnableInternetAccessTooltip": "Habilita acesso à internet do programa convidado. Se habilitado, o aplicativo vai se comportar como se o sistema Switch emulado estivesse conectado a Internet. Note que em alguns casos, aplicativos podem acessar a Internet mesmo com essa opção desabilitada",
"GameListContextMenuManageCheatToolTip": "Gerenciar Cheats",
"GameListContextMenuManageCheat": "Gerenciar Cheats",
"ControllerSettingsStickRange": "Intervalo",
"ControllerSettingsStickRange": "Intervalo:",
"DialogStopEmulationTitle": "Ryujinx - Parar emulação",
"DialogStopEmulationMessage": "Tem certeza que deseja parar a emulação?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "Áudio",
"SettingsTabNetwork": "Rede",
"SettingsTabNetworkConnection": "Conexão de rede",
"SettingsTabGraphicsFrameRate": "Taxa de atualização do hóspede:",
"SettingsTabGraphicsFrameRateTooltip": "Define a taxa de atualização do hóspede. Coloque em 0 para remover o limite.",
"SettingsTabCpuCache": "Cache da CPU",
"SettingsTabCpuMemory": "Memória da CPU",
"DialogUpdaterFlatpakNotSupportedMessage": "Por favor, atualize o Ryujinx pelo FlatHub.",
@ -540,5 +535,24 @@
"Handheld": "Portátil",
"ConnectionError": "Erro de conexão.",
"AboutPageDeveloperListMore": "{0} e mais...",
"ApiError": "Erro de API."
"ApiError": "Erro de API.",
"LoadingHeading": "Carregando {0}",
"CompilingPPTC": "Compilando PTC",
"CompilingShaders": "Compilando Shaders",
"AllKeyboards": "Todos os teclados",
"OpenFileDialogTitle": "Selecione um arquivo suportado para abrir",
"OpenFolderDialogTitle": "Selecione um diretório com um jogo extraído",
"AllSupportedFormats": "Todos os formatos suportados",
"RyujinxUpdater": "Atualizador do Ryujinx",
"SettingsTabHotkeys": "Atalhos do teclado",
"SettingsTabHotkeysHotkeys": "Atalhos do teclado",
"SettingsTabHotkeysToggleVsyncHotkey": "Mudar VSync:",
"SettingsTabHotkeysScreenshotHotkey": "Captura de tela:",
"SettingsTabHotkeysShowUiHotkey": "Exibir UI:",
"SettingsTabHotkeysPauseHotkey": "Pausar:",
"SettingsTabHotkeysToggleMuteHotkey": "Mudo:",
"ControllerMotionTitle": "Configurações do controle de movimento",
"ControllerRumbleTitle": "Configurações de vibração",
"SettingsSelectThemeFileDialogTitle" : "Selecionar arquivo do tema",
"SettingsXamlThemeFile" : "Arquivo de tema Xaml"
}

View file

@ -242,8 +242,6 @@
"ControllerSettingsMisc": "Разное",
"ControllerSettingsTriggerThreshold": "Порог срабатывания:",
"ControllerSettingsMotion": "Движение",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Включить управление движением",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Используйте движение, совместимое с CemuHook",
"ControllerSettingsMotionControllerSlot": "Слот контроллера:",
"ControllerSettingsMotionMirrorInput": "Зеркальный ввод",
@ -289,9 +287,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Светлая",
"SettingsTabGeneralThemeEnableCustomTheme": "Включить пользовательскую тему",
"ButtonBrowse": "Обзор",
"ControllerSettingsMotionConfigureCemuHookSettings": "Настройка движения CemuHook",
"ControllerSettingsConfigureGeneral": "Настройка",
"ControllerSettingsRumble": "Вибрация",
"ControllerSettingsRumbleEnable": "Включить вибрацию",
"ControllerSettingsRumbleStrongMultiplier": "Множитель сильной вибрации",
"ControllerSettingsRumbleWeakMultiplier": "Множитель слабой вибрации",
"DialogMessageSaveNotAvailableMessage": "Нет сохраненных данных для {0} [{1:x16}]",
@ -483,15 +480,15 @@
"EnableInternetAccessTooltip": "Включает гостевой доступ в Интернет. Если этот параметр включен, приложение будет вести себя так, как если бы эмулированная консоль Switch была подключена к Интернету. Обратите внимание, что в некоторых случаях приложения могут по-прежнему получать доступ к Интернету, даже если эта опция отключена.",
"GameListContextMenuManageCheatToolTip" : "Управление читами",
"GameListContextMenuManageCheat" : "Управление читами",
"ControllerSettingsStickRange" : "Диапазон",
"ControllerSettingsStickRange" : "Диапазон:",
"DialogStopEmulationTitle" : "Ryujinx - Остановить эмуляцию",
"DialogStopEmulationMessage": "Вы уверены, что хотите остановить эмуляцию?",
"SettingsTabCpu": "ЦП",
"SettingsTabAudio": "Аудио",
"SettingsTabNetwork": "Сеть",
"SettingsTabNetworkConnection" : "Подключение к сети",
"SettingsTabGraphicsFrameRate" : "Частота обновления хоста:",
"SettingsTabGraphicsFrameRateTooltip" : "Устанавливает частоту обновления хоста. Установите на 0, чтобы снять ограничение.",
"SettingsTabCpuCache" : "Кэш ЦП",
"SettingsTabCpuMemory" : "Память ЦП"
"SettingsTabCpuMemory" : "Память ЦП",
"ControllerMotionTitle": "Motion Control Settings",
"ControllerRumbleTitle": "Rumble Settings"
}

View file

@ -243,8 +243,6 @@
"ControllerSettingsMisc": "Misc.",
"ControllerSettingsTriggerThreshold": "Tetik Eşiği:",
"ControllerSettingsMotion": "Hareket",
"ControllerSettingsCemuHook": "CemuHook",
"ControllerSettingsMotionEnableMotionControls": "Hareket Kontrollerini Etkinleştir",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook Uyumlu Hareket",
"ControllerSettingsMotionControllerSlot": "Kontrolcü Yuvası:",
"ControllerSettingsMotionMirrorInput": "Girişi Aynala",
@ -290,9 +288,8 @@
"SettingsTabGeneralThemeBaseStyleLight": "Aydınlık",
"SettingsTabGeneralThemeEnableCustomTheme": "Özel Tema Etkinleştir",
"ButtonBrowse": "Göz At",
"ControllerSettingsMotionConfigureCemuHookSettings": "CemuHook Hareket Ayarla",
"ControllerSettingsConfigureGeneral": "Ayarla",
"ControllerSettingsRumble": "Titreşim",
"ControllerSettingsRumbleEnable": "Titreşimi Etkinleştir",
"ControllerSettingsRumbleStrongMultiplier": "Güçlü Titreşim Çarpanı",
"ControllerSettingsRumbleWeakMultiplier": "Zayıf Titreşim Çarpanı",
"DialogMessageSaveNotAvailableMessage": "{0} [{1:x16}] için kayıt verisi yok",
@ -485,15 +482,13 @@
"EnableInternetAccessTooltip": "Guest internet erişimini etkinleştirir. Etkinleştirilmişse, uygulama emüle edilen Switch konsolu internete bağlıymış gibi davranır. Not: Bazı durumlarda uygulamalar bu seçenek devre dışı olmasına rağmen internete erişebilir",
"GameListContextMenuManageCheatToolTip" : "Hileleri Yönet",
"GameListContextMenuManageCheat" : "Hileleri Yönet",
"ControllerSettingsStickRange" : "Bölge (Range)",
"ControllerSettingsStickRange" : "Bölge (Range):",
"DialogStopEmulationTitle" : "Ryujinx - Emülasyonu Durdur",
"DialogStopEmulationMessage": "Emülasyonu durdurmak istediğinizden emin misiniz?",
"SettingsTabCpu": "CPU",
"SettingsTabAudio": "Ses",
"SettingsTabNetwork": "Ağ",
"SettingsTabNetworkConnection" : "Ağ Bağlantısı",
"SettingsTabGraphicsFrameRate" : "Host Yenileme Hızı:",
"SettingsTabGraphicsFrameRateTooltip" : "Host yenileme hızını ayarlar. Limiti kaldırmak için 0'ı seçin.",
"SettingsTabCpuCache" : "CPU Cache",
"SettingsTabCpuMemory" : "CPU Hafızası",
"DialogUpdaterFlatpakNotSupportedMessage": "Lütfen Ryujinx'i FlatHub aracılığıyla güncelleyin.",
@ -543,5 +538,21 @@
"ApiError": "API Hatası.",
"LoadingHeading": "Yükleniyor {0}",
"CompilingPPTC": "PTC derleniyor",
"CompilingShaders": "Shaderlar derleniyor"
"CompilingShaders": "Shaderlar derleniyor",
"AllKeyboards": "Tüm klavyeler",
"OpenFileDialogTitle": "Açılacak desteklenen bir dosya seçin",
"OpenFolderDialogTitle": "Sıkıştırılmamış oyun içeren klasör seçin",
"AllSupportedFormats": "Tüm Desteklenen Formatlar",
"RyujinxUpdater": "Ryujinx Güncelleyicisi",
"SettingsTabHotkeys": "Klavye Kısayolları",
"SettingsTabHotkeysHotkeys": "Klavye Kısayolları",
"SettingsTabHotkeysToggleVsyncHotkey": "VSync'i Etkinleştir:",
"SettingsTabHotkeysScreenshotHotkey": "Ekran Görüntüsü Al:",
"SettingsTabHotkeysShowUiHotkey": "Arayüzü Göster:",
"SettingsTabHotkeysPauseHotkey": "Duraklat:",
"SettingsTabHotkeysToggleMuteHotkey": "Sustur:",
"ControllerMotionTitle": "Hareket Kontrol Seçenekleri",
"ControllerRumbleTitle": "Titreşim Seçenekleri",
"SettingsSelectThemeFileDialogTitle" : "Tema Dosyası Seçin",
"SettingsXamlThemeFile" : "Xaml Tema Dosyası"
}

View file

@ -492,8 +492,6 @@
"SettingsTabAudio": "音频",
"SettingsTabNetwork": "网络",
"SettingsTabNetworkConnection": "网络连接",
"[REMOVE]SettingsTabGraphicsFrameRate": "主机刷新率:",
"[REMOVE]SettingsTabGraphicsFrameRateTooltip": "设置主机刷新率。设为 0 可以取消帧率限制",
"SettingsTabCpuCache": "CPU 缓存",
"SettingsTabCpuMemory": "CPU 内存",
"DialogUpdaterFlatpakNotSupportedMessage": "请通过 FlatHub 更新 Ryujinx。",

View file

@ -161,11 +161,11 @@
<Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlBorderColor}" />
</Style>
<Style Selector="TabItem &gt; ScrollViewer">
<Style Selector="TabItem > ScrollViewer">
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="Margin" Value="0,-5,0,0" />
</Style>
<Style Selector="TabItem &gt; ScrollViewer &gt; Border">
<Style Selector="TabItem > ScrollViewer > Border">
<Setter Property="BorderThickness" Value="0,1,0,0" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource HighlightBrush}" />

View file

@ -28,7 +28,7 @@ using Path = System.IO.Path;
namespace Ryujinx.Ava.Common
{
public static class ApplicationHelper
internal static class ApplicationHelper
{
private static HorizonClient _horizonClient;
private static AccountManager _accountManager;

View file

@ -5,7 +5,7 @@ using System;
namespace Ryujinx.Ava.Common.Locale
{
public class LocaleExtension : MarkupExtension
internal class LocaleExtension : MarkupExtension
{
public LocaleExtension(string key)
{

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Ava.Common.Locale
}
// Load english first, if the target language translation is incomplete, we default to english.
LoadLanguage(DefaultLanguageCode);
LoadDefaultLanguage();
if (localeLanguageCode != DefaultLanguageCode)
{
@ -79,6 +79,11 @@ namespace Ryujinx.Ava.Common.Locale
OnPropertyChanged("Item");
}
public void LoadDefaultLanguage()
{
LoadLanguage(DefaultLanguageCode);
}
public void LoadLanguage(string languageCode)
{
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");

View file

@ -10,7 +10,7 @@ using Key = Ryujinx.Input.Key;
namespace Ryujinx.Ava.Input
{
public class AvaloniaKeyboard : IKeyboard
internal class AvaloniaKeyboard : IKeyboard
{
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
private readonly AvaloniaKeyboardDriver _driver;

View file

@ -10,7 +10,7 @@ using Key = Ryujinx.Input.Key;
namespace Ryujinx.Ava.Input
{
public class AvaloniaKeyboardDriver : IGamepadDriver
internal class AvaloniaKeyboardDriver : IGamepadDriver
{
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
private readonly Control _control;

View file

@ -5,7 +5,7 @@ using AvaKey = Avalonia.Input.Key;
namespace Ryujinx.Ava.Input
{
public static class AvaloniaMappingHelper
internal static class AvaloniaMappingHelper
{
private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count]
{

View file

@ -6,7 +6,7 @@ using System.Numerics;
namespace Ryujinx.Ava.Input
{
public class AvaloniaMouse : IMouse
internal class AvaloniaMouse : IMouse
{
private AvaloniaMouseDriver _driver;

View file

@ -9,7 +9,7 @@ using Size = System.Drawing.Size;
namespace Ryujinx.Ava.Input
{
public class AvaloniaMouseDriver : IGamepadDriver
internal class AvaloniaMouseDriver : IGamepadDriver
{
private Control _widget;
private bool _isDisposed;

View file

@ -23,7 +23,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Modules
{
public static class Updater
internal static class Updater
{
private const string GitHubApiURL = "https://api.github.com";
internal static bool Running;

View file

@ -185,7 +185,7 @@ namespace Ryujinx.Ava
}
}
private static void ReloadConfig()
public static void ReloadConfig()
{
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");

View file

@ -18,16 +18,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.14" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.14" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.14" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.14" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.14" />
<PackageReference Include="Avalonia.Svg" Version="0.10.13" />
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.13" />
<PackageReference Include="Avalonia" Version="0.10.15" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.15" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.15" />
<PackageReference Include="Avalonia.Svg" Version="0.10.14" />
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.14" />
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageReference Include="DynamicData" Version="7.7.14" />
<PackageReference Include="FluentAvaloniaUI" Version="1.3.4" />
<PackageReference Include="DynamicData" Version="7.9.4" />
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build9" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />

View file

@ -1,5 +1,6 @@
using Avalonia.Controls;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
@ -45,13 +46,61 @@ namespace Ryujinx.Ava.Ui.Applet
public bool DisplayMessageDialog(string title, string message)
{
// TODO : Show controller applet. Needs settings window to be implemented.
Dispatcher.UIThread.InvokeAsync(() =>
ManualResetEvent dialogCloseEvent = new(false);
bool okPressed = false;
Dispatcher.UIThread.InvokeAsync(async () =>
{
ContentDialogHelper.ShowNotAvailableMessage(_parent);
try
{
ManualResetEvent deferEvent = new(false);
bool opened = false;
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
title,
message,
"",
LocaleManager.Instance["DialogOpenSettingsWindowLabel"],
"",
LocaleManager.Instance["SettingsButtonClose"],
(int)Symbol.Important,
deferEvent,
async (window) =>
{
if (opened)
{
return;
}
opened = true;
_parent.SettingsWindow = new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager);
await _parent.SettingsWindow.ShowDialog(window);
opened = false;
});
if (response == UserResult.Ok)
{
okPressed = true;
}
dialogCloseEvent.Set();
}
catch (Exception ex)
{
ContentDialogHelper.CreateErrorDialog(_parent, string.Format(LocaleManager.Instance["DialogMessageDialogErrorExceptionMessage"], ex));
dialogCloseEvent.Set();
}
});
return true;
dialogCloseEvent.WaitOne();
return okPressed;
}
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)

View file

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Applet
{
public class ErrorAppletWindow : StyleableWindow
internal class ErrorAppletWindow : StyleableWindow
{
private readonly Window _owner;
private object _buttonResponse;

View file

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
{
public class SwkbdAppletDialog : UserControl
internal class SwkbdAppletDialog : UserControl
{
private Predicate<int> _checkLength;
private int _inputMax;

View file

@ -5,7 +5,7 @@ using System.Runtime.Versioning;
namespace Ryujinx.Ava.Ui.Controls
{
[SupportedOSPlatform("linux")]
public class AvaloniaGlxContext : SPB.Platform.GLX.GLXOpenGLContext
internal class AvaloniaGlxContext : SPB.Platform.GLX.GLXOpenGLContext
{
public AvaloniaGlxContext(IntPtr handle)
: base(FramebufferFormat.Default, 0, 0, 0, false, null)

View file

@ -5,7 +5,7 @@ using System.Runtime.Versioning;
namespace Ryujinx.Ava.Ui.Controls
{
[SupportedOSPlatform("windows")]
public class AvaloniaWglContext : SPB.Platform.WGL.WGLOpenGLContext
internal class AvaloniaWglContext : SPB.Platform.WGL.WGLOpenGLContext
{
public AvaloniaWglContext(IntPtr handle)
: base(FramebufferFormat.Default, 0, 0, 0, false, null)

View file

@ -7,7 +7,7 @@ using System.IO;
namespace Ryujinx.Ava.Ui.Controls
{
public class BitmapArrayValueConverter : IValueConverter
internal class BitmapArrayValueConverter : IValueConverter
{
public static BitmapArrayValueConverter Instance = new();

View file

@ -0,0 +1,118 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.LogicalTree;
using Avalonia.Threading;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
{
internal class ButtonKeyAssigner
{
internal class ButtonAssignedEventArgs : EventArgs
{
public ToggleButton Button { get; }
public bool IsAssigned { get; }
public ButtonAssignedEventArgs(ToggleButton button, bool isAssigned)
{
Button = button;
IsAssigned = isAssigned;
}
}
public ToggleButton ToggledButton { get; set; }
private bool _isWaitingForInput;
private bool _shouldUnbind;
public event EventHandler<ButtonAssignedEventArgs> ButtonAssigned;
public ButtonKeyAssigner(ToggleButton toggleButton)
{
ToggledButton = toggleButton;
}
public async void GetInputAndAssign(IButtonAssigner assigner, IKeyboard keyboard = null)
{
Dispatcher.UIThread.Post(() =>
{
ToggledButton.IsChecked = true;
});
if (_isWaitingForInput)
{
Dispatcher.UIThread.Post(() =>
{
Cancel();
});
return;
}
_isWaitingForInput = true;
assigner.Initialize();
await Task.Run(async () =>
{
while (true)
{
if (!_isWaitingForInput)
{
return;
}
await Task.Delay(10);
assigner.ReadInput();
if (assigner.HasAnyButtonPressed() || assigner.ShouldCancel() || (keyboard != null && keyboard.IsPressed(Key.Escape)))
{
break;
}
}
});
await Dispatcher.UIThread.InvokeAsync(() =>
{
string pressedButton = assigner.GetPressedButton();
if (_shouldUnbind)
{
SetButtonText(ToggledButton, "Unbound");
}
else if (pressedButton != "")
{
SetButtonText(ToggledButton, pressedButton);
}
_shouldUnbind = false;
_isWaitingForInput = false;
ToggledButton.IsChecked = false;
ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, pressedButton != null));
static void SetButtonText(ToggleButton button, string text)
{
ILogical textBlock = button.GetLogicalDescendants().First(x => x is TextBlock);
if (textBlock != null && textBlock is TextBlock block)
{
block.Text = text;
}
}
});
}
public void Cancel(bool shouldUnbind = false)
{
_isWaitingForInput = false;
ToggledButton.IsChecked = false;
_shouldUnbind = shouldUnbind;
}
}
}

View file

@ -97,10 +97,12 @@ namespace Ryujinx.Ava.Ui.Controls
});
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
contentDialog.PrimaryButtonClick -= DeferClose;
result = UserResult.No;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{
contentDialog.PrimaryButtonClick -= DeferClose;
result = UserResult.Cancel;
});
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
@ -115,6 +117,8 @@ namespace Ryujinx.Ava.Ui.Controls
return;
}
contentDialog.PrimaryButtonClick -= DeferClose;
startedDeferring = true;
var deferral = args.GetDeferral();

View file

@ -4,7 +4,7 @@ using System;
namespace Ryujinx.Ava.Ui.Controls
{
public static class IGlContextExtension
internal static class IGlContextExtension
{
public static OpenGLContextBase AsOpenGLContextBase(this IGlContext context)
{

View file

@ -6,7 +6,7 @@ using System.Globalization;
namespace Ryujinx.Ava.Ui.Controls
{
public class KeyValueConverter : IValueConverter
internal class KeyValueConverter : IValueConverter
{
public static KeyValueConverter Instance = new();

View file

@ -3,7 +3,7 @@ using System;
namespace Ryujinx.Ava.Ui.Controls
{
public class OpenToolkitBindingsContext : IBindingsContext
internal class OpenToolkitBindingsContext : IBindingsContext
{
private readonly Func<string, IntPtr> _getProcAddress;

View file

@ -18,7 +18,7 @@ using System;
namespace Ryujinx.Ava.Ui.Controls
{
public class RendererControl : Control
internal class RendererControl : Control
{
private int _image;

View file

@ -0,0 +1,6 @@
using Ryujinx.Common.Configuration.Hid;
namespace Ryujinx.Ava.Ui.Models
{
internal record ControllerModel(ControllerType Type, string Name);
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Ava.Ui.Models
{
public enum DeviceType
{
None,
Keyboard,
Controller
}
}

View file

@ -3,7 +3,7 @@ using System.Collections;
namespace Ryujinx.Ava.Ui.Models
{
public class FileSizeSortComparer : IComparer
internal class FileSizeSortComparer : IComparer
{
public int Compare(object x, object y)
{

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Ryujinx.Ava.Ui.Models.Generic
{
public class FileSizeSortComparer : IComparer<ApplicationData>
internal class FileSizeSortComparer : IComparer<ApplicationData>
{
public FileSizeSortComparer() { }
public FileSizeSortComparer(bool isAscending) { _order = isAscending ? 1 : -1; }

View file

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace Ryujinx.Ava.Ui.Models.Generic
{
public class LastPlayedSortComparer : IComparer<ApplicationData>
internal class LastPlayedSortComparer : IComparer<ApplicationData>
{
public LastPlayedSortComparer() { }
public LastPlayedSortComparer(bool isAscending) { IsAscending = isAscending; }

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Ryujinx.Ava.Ui.Models.Generic
{
public class TimePlayedSortComparer : IComparer<ApplicationData>
internal class TimePlayedSortComparer : IComparer<ApplicationData>
{
public TimePlayedSortComparer() { }
public TimePlayedSortComparer(bool isAscending) { _order = isAscending ? 1 : -1; }

View file

@ -7,7 +7,7 @@ using System;
namespace Ryujinx.Ava.Ui.Models
{
public class InputConfiguration<Key, Stick> : BaseModel
internal class InputConfiguration<Key, Stick> : BaseModel
{
private float _deadzoneRight;
private float _triggerThreshold;

View file

@ -4,7 +4,7 @@ using System.Collections;
namespace Ryujinx.Ava.Ui.Models
{
public class LastPlayedSortComparer : IComparer
internal class LastPlayedSortComparer : IComparer
{
public int Compare(object x, object y)
{

View file

@ -0,0 +1,6 @@
using Ryujinx.Common.Configuration.Hid;
namespace Ryujinx.Ava.Ui.Models
{
public record PlayerModel(PlayerIndex Id, string Name);
}

View file

@ -1,6 +1,6 @@
namespace Ryujinx.Ava.Ui.Models
{
public class ProfileImageModel
internal class ProfileImageModel
{
public ProfileImageModel(string name, byte[] data)
{

View file

@ -2,7 +2,7 @@
namespace Ryujinx.Ava.Ui.Models
{
public class StatusUpdatedEventArgs : EventArgs
internal class StatusUpdatedEventArgs : EventArgs
{
public bool VSyncEnabled { get; }
public float Volume { get; }

View file

@ -3,7 +3,7 @@ using System.Collections;
namespace Ryujinx.Ava.Ui.Models
{
public class TimePlayedSortComparer : IComparer
internal class TimePlayedSortComparer : IComparer
{
public int Compare(object x, object y)
{

View file

@ -1,6 +1,6 @@
namespace Ryujinx.Ava.Ui.Windows
namespace Ryujinx.Ava.Ui.Models
{
public class TimeZone
internal class TimeZone
{
public TimeZone(string utcDifference, string location, string abbreviation)
{

View file

@ -3,7 +3,7 @@ using Ryujinx.Ava.Common.Locale;
namespace Ryujinx.Ava.Ui.Models
{
public class TitleUpdateModel
internal class TitleUpdateModel
{
public bool IsEnabled { get; set; }
public bool IsNoUpdate { get; }

View file

@ -0,0 +1,887 @@
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Svg.Skia;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.Input;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
using Key = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.Ui.ViewModels
{
public class ControllerSettingsViewModel : BaseModel, IDisposable
{
private const string Disabled = "disabled";
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
private const string JoyConPairResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg";
private const string JoyConLeftResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg";
private const string JoyConRightResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg";
private const string KeyboardString = "keyboard";
private const string ControllerString = "controller";
private readonly MainWindow _mainWindow;
private PlayerIndex _playerId;
private int _controller;
private string _controllerImage;
private int _device;
private object _configuration;
private string _profileName;
private bool _isLoaded;
private readonly UserControl _owner;
public IGamepadDriver AvaloniaKeyboardDriver { get; }
public IGamepad SelectedGamepad { get; private set; }
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
internal ObservableCollection<ControllerModel> Controllers { get; set; }
public AvaloniaList<string> ProfilesList { get; set; }
public AvaloniaList<string> DeviceList { get; set; }
// XAML Flags
public bool ShowSettings => _device > 0;
public bool IsController => _device > 1;
public bool IsKeyboard => !IsController;
public bool IsRight { get; set; }
public bool IsLeft { get; set; }
public bool IsModified { get; set; }
public object Configuration
{
get => _configuration;
set
{
_configuration = value;
OnPropertyChanged();
}
}
public PlayerIndex PlayerId
{
get => _playerId;
set
{
if (IsModified)
{
return;
}
IsModified = false;
_playerId = value;
if (!Enum.IsDefined(typeof(PlayerIndex), _playerId))
{
_playerId = PlayerIndex.Player1;
}
LoadConfiguration();
LoadDevice();
LoadProfiles();
_isLoaded = true;
OnPropertyChanged();
}
}
public int Controller
{
get => _controller;
set
{
_controller = value;
if (_controller == -1)
{
_controller = 0;
}
if (Controllers.Count > 0 && value < Controllers.Count && _controller > -1)
{
ControllerType controller = Controllers[_controller].Type;
IsLeft = true;
IsRight = true;
switch (controller)
{
case ControllerType.Handheld:
ControllerImage = JoyConPairResource;
break;
case ControllerType.ProController:
ControllerImage = ProControllerResource;
break;
case ControllerType.JoyconPair:
ControllerImage = JoyConPairResource;
break;
case ControllerType.JoyconLeft:
ControllerImage = JoyConLeftResource;
IsRight = false;
break;
case ControllerType.JoyconRight:
ControllerImage = JoyConRightResource;
IsLeft = false;
break;
}
LoadInputDriver();
LoadProfiles();
}
OnPropertyChanged();
NotifyChanges();
}
}
public string ControllerImage
{
get => _controllerImage;
set
{
_controllerImage = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Image));
}
}
public SvgImage Image
{
get
{
SvgImage image = new SvgImage();
if (!string.IsNullOrWhiteSpace(_controllerImage))
{
SvgSource source = new SvgSource();
source.Load(EmbeddedResources.GetStream(_controllerImage));
image.Source = source;
}
return image;
}
}
public string ProfileName
{
get => _profileName; set
{
_profileName = value;
OnPropertyChanged();
}
}
public int Device
{
get => _device;
set
{
_device = value < 0 ? 0 : value;
if (_device >= Devices.Count)
{
return;
}
var selected = Devices[_device].Type;
if (selected != DeviceType.None)
{
LoadControllers();
if (_isLoaded)
{
LoadConfiguration(LoadDefaultConfiguration());
}
}
OnPropertyChanged();
NotifyChanges();
}
}
public InputConfig Config { get; set; }
public ControllerSettingsViewModel(UserControl owner) : this()
{
_owner = owner;
if (Program.PreviewerDetached)
{
_mainWindow =
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current
.ApplicationLifetime).MainWindow;
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
if (_mainWindow.AppHost != null)
{
_mainWindow.AppHost.NpadManager.BlockInputUpdates();
}
_isLoaded = false;
LoadDevices();
PlayerId = PlayerIndex.Player1;
}
}
public ControllerSettingsViewModel()
{
PlayerIndexes = new ObservableCollection<PlayerModel>();
Controllers = new ObservableCollection<ControllerModel>();
Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
ProfilesList = new AvaloniaList<string>();
DeviceList = new AvaloniaList<string>();
ControllerImage = ProControllerResource;
PlayerIndexes.Add(new(PlayerIndex.Player1, LocaleManager.Instance["ControllerSettingsPlayer1"]));
PlayerIndexes.Add(new(PlayerIndex.Player2, LocaleManager.Instance["ControllerSettingsPlayer2"]));
PlayerIndexes.Add(new(PlayerIndex.Player3, LocaleManager.Instance["ControllerSettingsPlayer3"]));
PlayerIndexes.Add(new(PlayerIndex.Player4, LocaleManager.Instance["ControllerSettingsPlayer4"]));
PlayerIndexes.Add(new(PlayerIndex.Player5, LocaleManager.Instance["ControllerSettingsPlayer5"]));
PlayerIndexes.Add(new(PlayerIndex.Player6, LocaleManager.Instance["ControllerSettingsPlayer6"]));
PlayerIndexes.Add(new(PlayerIndex.Player7, LocaleManager.Instance["ControllerSettingsPlayer7"]));
PlayerIndexes.Add(new(PlayerIndex.Player8, LocaleManager.Instance["ControllerSettingsPlayer8"]));
PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance["ControllerSettingsHandheld"]));
}
private void LoadConfiguration(InputConfig inputConfig = null)
{
Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerId);
if (Config is StandardKeyboardInputConfig keyboardInputConfig)
{
Configuration = new InputConfiguration<Key, ConfigStickInputId>(keyboardInputConfig);
}
if (Config is StandardControllerInputConfig controllerInputConfig)
{
Configuration = new InputConfiguration<ConfigGamepadInputId, ConfigStickInputId>(controllerInputConfig);
}
}
public void LoadDevice()
{
if (Config == null || Config.Backend == InputBackendType.Invalid)
{
Device = 0;
}
else
{
var type = DeviceType.None;
if (Config is StandardKeyboardInputConfig)
{
type = DeviceType.Keyboard;
}
if (Config is StandardControllerInputConfig)
{
type = DeviceType.Controller;
}
var item = Devices.FirstOrDefault(x => x.Type == type && x.Id == Config.Id);
if (item != default)
{
Device = Devices.ToList().FindIndex(x => x.Id == item.Id);
}
else
{
Device = 0;
}
}
}
public async void ShowMotionConfig()
{
await MotionSettingsWindow.Show(this, _owner.GetVisualRoot() as StyleableWindow);
}
public async void ShowRumbleConfig()
{
await RumbleSettingsWindow.Show(this, _owner.GetVisualRoot() as StyleableWindow);
}
private void LoadInputDriver()
{
if (_device < 0)
{
return;
}
string id = GetCurrentGamepadId();
var type = Devices[Device].Type;
if (type == DeviceType.None)
{
return;
}
else if (type == DeviceType.Keyboard)
{
if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver)
{
// NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused...
SelectedGamepad = AvaloniaKeyboardDriver.GetGamepad(id);
}
else
{
SelectedGamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
}
}
else
{
SelectedGamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
}
}
private void HandleOnGamepadDisconnected(string id)
{
Dispatcher.UIThread.Post(() =>
{
LoadDevices();
});
}
private void HandleOnGamepadConnected(string id)
{
Dispatcher.UIThread.Post(() =>
{
LoadDevices();
});
}
private string GetCurrentGamepadId()
{
if (_device < 0)
{
return string.Empty;
}
var device = Devices[Device];
if (device.Type == DeviceType.None)
{
return null;
}
return device.Id.Split(" ")[0];
}
public void LoadControllers()
{
Controllers.Clear();
if (_playerId == PlayerIndex.Handheld)
{
Controllers.Add(new(ControllerType.Handheld, LocaleManager.Instance["ControllerSettingsControllerTypeHandheld"]));
Controller = 0;
}
else
{
Controllers.Add(new(ControllerType.ProController, LocaleManager.Instance["ControllerSettingsControllerTypeProController"]));
Controllers.Add(new(ControllerType.JoyconPair, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConPair"]));
Controllers.Add(new(ControllerType.JoyconLeft, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConLeft"]));
Controllers.Add(new(ControllerType.JoyconRight, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConRight"]));
if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
{
Controller = Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType);
}
else
{
Controller = 0;
}
}
}
private static string GetShortGamepadName(string str)
{
const string Ellipsis = "...";
const int MaxSize = 50;
if (str.Length > MaxSize)
{
return str.Substring(0, MaxSize - Ellipsis.Length) + Ellipsis;
}
return str;
}
public void LoadDevices()
{
lock (Devices)
{
Devices.Clear();
DeviceList.Clear();
Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance["ControllerSettingsDeviceDisabled"]));
foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
{
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
if (gamepad != null)
{
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)} ({id})"));
}
}
foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds)
{
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
if (gamepad != null)
{
Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({id})"));
}
}
DeviceList.AddRange(Devices.Select(x => x.Name));
Device = Math.Min(Device, DeviceList.Count);
}
}
private string GetProfileBasePath()
{
string path = AppDataManager.ProfilesDirPath;
var type = Devices[Device == -1 ? 0 : Device].Type;
if (type == DeviceType.Keyboard)
{
path = Path.Combine(path, KeyboardString);
}
else if (type == DeviceType.Controller)
{
path = Path.Combine(path, ControllerString);
}
return path;
}
private void LoadProfiles()
{
ProfilesList.Clear();
string basePath = GetProfileBasePath();
if (!Directory.Exists(basePath))
{
Directory.CreateDirectory(basePath);
}
ProfilesList.Add((LocaleManager.Instance["ControllerSettingsProfileDefault"]));
foreach (string profile in Directory.GetFiles(basePath, "*.json", SearchOption.AllDirectories))
{
ProfilesList.Add(Path.GetFileNameWithoutExtension(profile));
}
if (string.IsNullOrWhiteSpace(ProfileName))
{
ProfileName = LocaleManager.Instance["ControllerSettingsProfileDefault"];
}
}
public InputConfig LoadDefaultConfiguration()
{
var activeDevice = Devices.FirstOrDefault();
if (Devices.Count > 0 && Device < Devices.Count && Device >= 0)
{
activeDevice = Devices[Device];
}
InputConfig config;
if (activeDevice.Type == DeviceType.Keyboard)
{
string id = activeDevice.Id;
config = new StandardKeyboardInputConfig
{
Version = Ryujinx.Common.Configuration.Hid.InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = id,
ControllerType = ControllerType.ProController,
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
DpadUp = Key.Up,
DpadDown = Key.Down,
DpadLeft = Key.Left,
DpadRight = Key.Right,
ButtonMinus = Key.Minus,
ButtonL = Key.E,
ButtonZl = Key.Q,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound
},
LeftJoyconStick =
new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.W,
StickDown = Key.S,
StickLeft = Key.A,
StickRight = Key.D,
StickButton = Key.F
},
RightJoycon = new RightJoyconCommonConfig<Key>
{
ButtonA = Key.Z,
ButtonB = Key.X,
ButtonX = Key.C,
ButtonY = Key.V,
ButtonPlus = Key.Plus,
ButtonR = Key.U,
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.I,
StickDown = Key.K,
StickLeft = Key.J,
StickRight = Key.L,
StickButton = Key.H
}
};
}
else if (activeDevice.Type == DeviceType.Controller)
{
bool isNintendoStyle = Devices.ToList().Find(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
string id = activeDevice.Id.Split(" ")[0];
config = new StandardControllerInputConfig
{
Version = Ryujinx.Common.Configuration.Hid.InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = id,
ControllerType = ControllerType.ProController,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
RangeLeft = 1.0f,
RangeRight = 1.0f,
TriggerThreshold = 0.5f,
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
{
DpadUp = ConfigGamepadInputId.DpadUp,
DpadDown = ConfigGamepadInputId.DpadDown,
DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus,
ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{
ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
ButtonPlus = ConfigGamepadInputId.Plus,
ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound
},
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false
},
Motion = new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true,
Sensitivity = 100,
GyroDeadzone = 1
},
Rumble = new RumbleConfigController
{
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false
}
};
}
else
{
config = new InputConfig();
}
config.PlayerIndex = _playerId;
return config;
}
public void LoadProfile()
{
if (Device == 0)
{
return;
}
InputConfig config = null;
if (string.IsNullOrWhiteSpace(ProfileName))
{
return;
}
if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
{
config = LoadDefaultConfiguration();
}
else
{
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
if (!File.Exists(path))
{
var index = ProfilesList.IndexOf(ProfileName);
if (index != -1)
{
ProfilesList.RemoveAt(index);
}
return;
}
try
{
using (Stream stream = File.OpenRead(path))
{
config = JsonHelper.Deserialize<InputConfig>(stream);
}
}
catch (JsonException) { }
catch (InvalidOperationException)
{
ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow,
String.Format(LocaleManager.Instance["DialogProfileInvalidProfileErrorMessage"], ProfileName));
Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
return;
}
}
if (config != null)
{
_isLoaded = false;
LoadConfiguration(config);
LoadDevice();
_isLoaded = true;
NotifyChanges();
}
}
public async void SaveProfile()
{
if (Device == 0)
{
return;
}
if (Configuration == null)
{
return;
}
if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
{
ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow, LocaleManager.Instance["DialogProfileDefaultProfileOverwriteErrorMessage"]);
return;
}
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (validFileName)
{
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
InputConfig config = null;
if (IsKeyboard)
{
config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig();
}
else if (IsController)
{
config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
}
config.ControllerType = Controllers[_controller].Type;
string jsonString = JsonHelper.Serialize(config, true);
await File.WriteAllTextAsync(path, jsonString);
LoadProfiles();
}
else
{
ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow, LocaleManager.Instance["DialogProfileInvalidProfileNameErrorMessage"]);
}
}
}
public async void RemoveProfile()
{
if (Device == 0 || ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"] || ProfilesList.IndexOf(ProfileName) == -1)
{
return;
}
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
_owner.GetVisualRoot() as StyleableWindow,
LocaleManager.Instance["DialogProfileDeleteProfileTitle"],
LocaleManager.Instance["DialogProfileDeleteProfileMessage"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
if (result == UserResult.Yes)
{
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
if (File.Exists(path))
{
File.Delete(path);
}
LoadProfiles();
}
}
public void Save()
{
IsModified = false;
List<InputConfig> newConfig = new();
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
newConfig.Remove(newConfig.Find(x => x == null));
if (Device == 0)
{
newConfig.Remove(newConfig.Find(x => x.PlayerIndex == this.PlayerId));
}
else
{
var device = Devices[Device];
if (device.Type == DeviceType.Keyboard)
{
var inputConfig = Configuration as InputConfiguration<Key, ConfigStickInputId>;
inputConfig.Id = device.Id;
}
else
{
var inputConfig = Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>;
inputConfig.Id = device.Id.Split(" ")[0];
}
var config = !IsController
? (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig()
: (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
config.ControllerType = Controllers[_controller].Type;
config.PlayerIndex = _playerId;
int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
if (i == -1)
{
newConfig.Add(config);
}
else
{
newConfig[i] = config;
}
}
_mainWindow.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
// Atomically replace and signal input change.
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
public void NotifyChange(string property)
{
OnPropertyChanged(property);
}
public void NotifyChanges()
{
OnPropertyChanged(nameof(Configuration));
OnPropertyChanged(nameof(IsController));
OnPropertyChanged(nameof(ShowSettings));
OnPropertyChanged(nameof(IsKeyboard));
OnPropertyChanged(nameof(IsRight));
OnPropertyChanged(nameof(IsLeft));
}
public void Dispose()
{
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
_mainWindow.AppHost?.NpadManager.UnblockInputUpdates();
SelectedGamepad?.Dispose();
AvaloniaKeyboardDriver.Dispose();
}
}
}

View file

@ -11,6 +11,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
@ -35,7 +36,7 @@ using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
namespace Ryujinx.Ava.Ui.ViewModels
{
public class MainWindowViewModel : BaseModel
internal class MainWindowViewModel : BaseModel
{
private readonly MainWindow _owner;
private ObservableCollection<ApplicationData> _applications;
@ -86,9 +87,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
if (Program.PreviewerDetached)
{
ShowUiKey = KeyGesture.Parse(ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi.ToString());
ScreenshotKey = KeyGesture.Parse(ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot.ToString());
PauseKey = KeyGesture.Parse(ConfigurationState.Instance.Hid.Hotkeys.Value.Pause.ToString());
LoadConfigurableHotKeys();
Volume = ConfigurationState.Instance.System.AudioVolume;
}
@ -836,6 +835,22 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
public void LoadConfigurableHotKeys()
{
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
{
ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
}
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
{
ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
}
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
{
PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
}
}
public void TakeScreenshot()
{
_owner.AppHost.ScreenshotRequested = true;
@ -930,10 +945,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
public void OpenSettings()
public async void OpenSettings()
{
// TODO : Implement Settings window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
_owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager);
await _owner.SettingsWindow.ShowDialog(_owner);
LoadConfigurableHotKeys();
}
public void ManageProfiles()
@ -951,6 +968,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void ChangeLanguage(object obj)
{
LocaleManager.Instance.LoadDefaultLanguage();
LocaleManager.Instance.LoadLanguage((string)obj);
}
@ -1350,7 +1368,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
dialogMessage += LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallConfirmMessage"];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(_owner, dialogTitle, dialogMessage, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
_owner,
dialogTitle,
dialogMessage,
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallWaitMessage"]);

View file

@ -0,0 +1,403 @@
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Threading;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.Input;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System;
using System;
using System.Collections.Generic;
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
namespace Ryujinx.Ava.Ui.ViewModels
{
internal class SettingsViewModel : BaseModel
{
private readonly VirtualFileSystem _virtualFileSystem;
private readonly ContentManager _contentManager;
private readonly StyleableWindow _owner;
private TimeZoneContentManager _timeZoneContentManager;
private readonly List<string> _validTzRegions;
private float _customResolutionScale;
private int _resolutionScale;
private int _graphicsBackendMultithreadingIndex;
private float _previousVolumeLevel;
private float _volume;
public int ResolutionScale
{
get => _resolutionScale;
set
{
_resolutionScale = value;
OnPropertyChanged(nameof(CustomResolutionScale));
OnPropertyChanged(nameof(IsCustomResolutionScaleActive));
}
}
public int GraphicsBackendMultithreadingIndex
{
get => _graphicsBackendMultithreadingIndex;
set
{
_graphicsBackendMultithreadingIndex = value;
if (_owner != null)
{
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateInfoDialog(_owner,
LocaleManager.Instance["DialogSettingsBackendThreadingWarningMessage"],
"",
"",
LocaleManager.Instance["InputDialogOk"],
LocaleManager.Instance["DialogSettingsBackendThreadingWarningTitle"]);
});
}
}
OnPropertyChanged();
}
}
public float CustomResolutionScale
{
get => _customResolutionScale;
set
{
_customResolutionScale = MathF.Round(value, 1);
OnPropertyChanged();
}
}
public bool EnableDiscordIntegration { get; set; }
public bool CheckUpdatesOnStart { get; set; }
public bool ShowConfirmExit { get; set; }
public bool HideCursorOnIdle { get; set; }
public bool EnableDockedMode { get; set; }
public bool EnableKeyboard { get; set; }
public bool EnableMouse { get; set; }
public bool EnableVsync { get; set; }
public bool EnablePptc { get; set; }
public bool EnableInternetAccess { get; set; }
public bool EnableFsIntegrityChecks { get; set; }
public bool IgnoreMissingServices { get; set; }
public bool ExpandDramSize { get; set; }
public bool EnableShaderCache { get; set; }
public bool EnableFileLog { get; set; }
public bool EnableStub { get; set; }
public bool EnableInfo { get; set; }
public bool EnableWarn { get; set; }
public bool EnableError { get; set; }
public bool EnableTrace { get; set; }
public bool EnableGuest { get; set; }
public bool EnableFsAccessLog { get; set; }
public bool EnableDebug { get; set; }
public bool IsOpenAlEnabled { get; set; }
public bool IsSoundIoEnabled { get; set; }
public bool IsSDL2Enabled { get; set; }
public bool EnableCustomTheme { get; set; }
public bool IsCustomResolutionScaleActive => _resolutionScale == 0;
public string TimeZone { get; set; }
public string ShaderDumpPath { get; set; }
public string CustomThemePath { get; set; }
public int Language { get; set; }
public int Region { get; set; }
public int FsGlobalAccessLogMode { get; set; }
public int AudioBackend { get; set; }
public int MaxAnisotropy { get; set; }
public int AspectRatio { get; set; }
public int OpenglDebugLevel { get; set; }
public int MemoryMode { get; set; }
public int BaseStyleIndex { get; set; }
public float Volume
{
get => _volume;
set
{
_volume = value;
ConfigurationState.Instance.System.AudioVolume.Value = (float)(_volume / 100);
OnPropertyChanged();
}
}
public DateTimeOffset DateOffset { get; set; }
public TimeSpan TimeOffset { get; set; }
public AvaloniaList<TimeZone> TimeZones { get; set; }
public AvaloniaList<string> GameDirectories { get; set; }
private KeyboardHotkeys _keyboardHotkeys;
public KeyboardHotkeys KeyboardHotkeys
{
get => _keyboardHotkeys;
set
{
_keyboardHotkeys = value;
OnPropertyChanged();
}
}
public IGamepadDriver AvaloniaKeyboardDriver { get; }
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager, StyleableWindow owner) : this()
{
_virtualFileSystem = virtualFileSystem;
_contentManager = contentManager;
_owner = owner;
if (Program.PreviewerDetached)
{
LoadTimeZones();
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
}
}
public SettingsViewModel()
{
GameDirectories = new AvaloniaList<string>();
TimeZones = new AvaloniaList<TimeZone>();
_validTzRegions = new List<string>();
CheckSoundBackends();
if (Program.PreviewerDetached)
{
LoadCurrentConfiguration();
}
}
public void CheckSoundBackends()
{
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
}
public void LoadTimeZones()
{
_timeZoneContentManager = new TimeZoneContentManager();
_timeZoneContentManager.InitializeInstance(_virtualFileSystem, _contentManager, IntegrityCheckLevel.None);
foreach ((int offset, string location, string abbr) in _timeZoneContentManager.ParseTzOffsets())
{
int hours = Math.DivRem(offset, 3600, out int seconds);
int minutes = Math.Abs(seconds) / 60;
string abbr2 = abbr.StartsWith('+') || abbr.StartsWith('-') ? string.Empty : abbr;
TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2));
_validTzRegions.Add(location);
}
}
public void ValidateAndSetTimeZone(string location)
{
if (_validTzRegions.Contains(location))
{
TimeZone = location;
OnPropertyChanged(nameof(TimeZone));
}
}
public async void BrowseTheme()
{
var dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance["SettingsSelectThemeFileDialogTitle"],
AllowMultiple = false
};
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance["SettingsXamlThemeFile"] });
var file = await dialog.ShowAsync(_owner);
if (file != null && file.Length > 0)
{
CustomThemePath = file[0];
OnPropertyChanged(nameof(CustomThemePath));
}
}
public void LoadCurrentConfiguration()
{
ConfigurationState config = ConfigurationState.Instance;
GameDirectories.Clear();
GameDirectories.AddRange(config.Ui.GameDirs.Value);
EnableDiscordIntegration = config.EnableDiscordIntegration;
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit;
HideCursorOnIdle = config.HideCursorOnIdle;
EnableDockedMode = config.System.EnableDockedMode;
EnableKeyboard = config.Hid.EnableKeyboard;
EnableMouse = config.Hid.EnableMouse;
EnableVsync = config.Graphics.EnableVsync;
EnablePptc = config.System.EnablePtc;
EnableInternetAccess = config.System.EnableInternetAccess;
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
IgnoreMissingServices = config.System.IgnoreMissingServices;
ExpandDramSize = config.System.ExpandRam;
EnableShaderCache = config.Graphics.EnableShaderCache;
EnableFileLog = config.Logger.EnableFileLog;
EnableStub = config.Logger.EnableStub;
EnableInfo = config.Logger.EnableInfo;
EnableWarn = config.Logger.EnableWarn;
EnableError = config.Logger.EnableError;
EnableTrace = config.Logger.EnableTrace;
EnableGuest = config.Logger.EnableGuest;
EnableDebug = config.Logger.EnableDebug;
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
EnableCustomTheme = config.Ui.EnableCustomTheme;
Volume = config.System.AudioVolume * 100;
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
TimeZone = config.System.TimeZone;
ShaderDumpPath = config.Graphics.ShadersDumpPath;
CustomThemePath = config.Ui.CustomThemePath;
BaseStyleIndex = config.Ui.BaseStyle == "Light" ? 0 : 1;
Language = (int)config.System.Language.Value;
Region = (int)config.System.Region.Value;
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
AudioBackend = (int)config.System.AudioBackend.Value;
MemoryMode = (int)config.System.MemoryManagerMode.Value;
float anisotropy = config.Graphics.MaxAnisotropy;
MaxAnisotropy = anisotropy == -1 ? 0 : (int)(MathF.Log2(anisotropy));
AspectRatio = (int)config.Graphics.AspectRatio.Value;
int resolution = config.Graphics.ResScale;
ResolutionScale = resolution == -1 ? 0 : resolution;
CustomResolutionScale = config.Graphics.ResScaleCustom;
DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset);
DateOffset = dateTimeOffset.Date;
TimeOffset = dateTimeOffset.TimeOfDay;
KeyboardHotkeys = config.Hid.Hotkeys.Value;
_previousVolumeLevel = Volume;
}
public void SaveSettings()
{
List<string> gameDirs = new List<string>(GameDirectories);
ConfigurationState config = ConfigurationState.Instance;
if (_validTzRegions.Contains(TimeZone))
{
config.System.TimeZone.Value = TimeZone;
}
config.Logger.EnableError.Value = EnableError;
config.Logger.EnableTrace.Value = EnableTrace;
config.Logger.EnableWarn.Value = EnableWarn;
config.Logger.EnableInfo.Value = EnableInfo;
config.Logger.EnableStub.Value = EnableStub;
config.Logger.EnableDebug.Value = EnableDebug;
config.Logger.EnableGuest.Value = EnableGuest;
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
config.Logger.EnableFileLog.Value = EnableFileLog;
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
config.System.EnableDockedMode.Value = EnableDockedMode;
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.HideCursorOnIdle.Value = HideCursorOnIdle;
config.Graphics.EnableVsync.Value = EnableVsync;
config.Graphics.EnableShaderCache.Value = EnableShaderCache;
config.System.EnablePtc.Value = EnablePptc;
config.System.EnableInternetAccess.Value = EnableInternetAccess;
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
config.System.ExpandRam.Value = ExpandDramSize;
config.Hid.EnableKeyboard.Value = EnableKeyboard;
config.Hid.EnableMouse.Value = EnableMouse;
config.Ui.CustomThemePath.Value = CustomThemePath;
config.Ui.EnableCustomTheme.Value = EnableCustomTheme;
config.Ui.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
config.System.Language.Value = (Language)Language;
config.System.Region.Value = (Region)Region;
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
{
DriverUtilities.ToggleOGLThreading(GraphicsBackendMultithreadingIndex == (int)BackendThreading.Off);
}
config.Graphics.BackendThreading.Value = (BackendThreading)GraphicsBackendMultithreadingIndex;
TimeSpan systemTimeOffset = DateOffset - DateTime.Now;
config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
config.Graphics.ShadersDumpPath.Value = ShaderDumpPath;
config.Ui.GameDirs.Value = gameDirs;
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
float anisotropy = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
config.Graphics.MaxAnisotropy.Value = anisotropy;
config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
config.Graphics.ResScale.Value = ResolutionScale == 0 ? -1 : ResolutionScale;
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
config.System.AudioVolume.Value = Volume / 100;
AudioBackend audioBackend = (AudioBackend)AudioBackend;
if (audioBackend != config.System.AudioBackend.Value)
{
config.System.AudioBackend.Value = audioBackend;
Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}");
}
config.Hid.Hotkeys.Value = KeyboardHotkeys;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
MainWindow.UpdateGraphicsConfig();
_previousVolumeLevel = Volume;
}
public void RevertIfNotSaved()
{
Program.ReloadConfig();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,200 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Key = Ryujinx.Input.Key;
namespace Ryujinx.Ava.Ui.Windows
{
public class ControllerSettingsWindow : UserControl
{
private bool _dialogOpen;
public Grid SettingButtons { get; set; }
private ButtonKeyAssigner _currentAssigner;
internal ControllerSettingsViewModel ViewModel { get; set; }
public ControllerSettingsWindow()
{
DataContext = ViewModel = new ControllerSettingsViewModel(this);
InitializeComponent();
foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
{
if (visual is ToggleButton button && !(visual is CheckBox))
{
button.Checked += Button_Checked;
button.Unchecked += Button_Unchecked;
}
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
SettingButtons = this.FindControl<Grid>("SettingButtons");
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (_currentAssigner != null && _currentAssigner.ToggledButton != null && !_currentAssigner.ToggledButton.IsPointerOver)
{
_currentAssigner.Cancel();
}
}
private void Button_Checked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton button)
{
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
{
return;
}
bool isStick = button.Tag != null && button.Tag.ToString() == "stick";
if (_currentAssigner == null && (bool)button.IsChecked)
{
_currentAssigner = new ButtonKeyAssigner(button);
FocusManager.Instance.Focus(this, NavigationMethod.Pointer);
PointerPressed += MouseClick;
IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
IButtonAssigner assigner = CreateButtonAssigner(isStick);
_currentAssigner.ButtonAssigned += (sender, e) =>
{
if (e.IsAssigned)
{
ViewModel.IsModified = true;
}
};
_currentAssigner.GetInputAndAssign(assigner, keyboard);
}
else
{
if (_currentAssigner != null)
{
ToggleButton oldButton = _currentAssigner.ToggledButton;
_currentAssigner.Cancel();
_currentAssigner = null;
button.IsChecked = false;
}
}
}
}
public void SaveCurrentProfile()
{
ViewModel.Save();
}
private IButtonAssigner CreateButtonAssigner(bool forStick)
{
IButtonAssigner assigner;
var device = ViewModel.Devices[ViewModel.Device];
if (device.Type == Models.DeviceType.Keyboard)
{
assigner = new KeyboardKeyAssigner((IKeyboard)ViewModel.SelectedGamepad);
}
else if (device.Type == Models.DeviceType.Controller)
{
InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.Id == ViewModel.SelectedGamepad.Id);
assigner = new GamepadButtonAssigner(ViewModel.SelectedGamepad, (config as StandardControllerInputConfig).TriggerThreshold, forStick);
}
else
{
throw new Exception("Controller not supported");
}
return assigner;
}
private void Button_Unchecked(object sender, RoutedEventArgs e)
{
_currentAssigner?.Cancel();
_currentAssigner = null;
}
private void MouseClick(object sender, PointerPressedEventArgs e)
{
bool shouldUnbind = false;
if (e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed)
{
shouldUnbind = true;
}
_currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick;
}
private async void PlayerIndexBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ViewModel.IsModified && !_dialogOpen)
{
_dialogOpen = true;
var result = await ContentDialogHelper.CreateConfirmationDialog(
this.GetVisualRoot() as StyleableWindow,
LocaleManager.Instance["DialogControllerSettingsModifiedConfirmMessage"],
LocaleManager.Instance["DialogControllerSettingsModifiedConfirmSubMessage"],
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
if (result == UserResult.Yes)
{
ViewModel.Save();
}
_dialogOpen = false;
ViewModel.IsModified = false;
if (e.AddedItems.Count > 0)
{
(PlayerIndex key, _) = (KeyValuePair<PlayerIndex, string>)e.AddedItems[0];
ViewModel.PlayerId = key;
}
}
}
public void Dispose()
{
_currentAssigner?.Cancel();
_currentAssigner = null;
ViewModel.Dispose();
}
}
}

View file

@ -58,10 +58,10 @@ namespace Ryujinx.Ava.Ui.Windows
public LibHacHorizonManager LibHacHorizonManager { get; private set; }
public AppHost AppHost { get; private set; }
internal AppHost AppHost { get; private set; }
public InputManager InputManager { get; private set; }
public RendererControl GlRenderer { get; private set; }
internal RendererControl GlRenderer { get; private set; }
public ContentControl ContentFrame { get; private set; }
public TextBlock LoadStatus { get; private set; }
public TextBlock FirmwareStatus { get; private set; }
@ -78,7 +78,8 @@ namespace Ryujinx.Ava.Ui.Windows
public HotKeyControl DockToggleHotKey { get; private set; }
public HotKeyControl ExitHotKey { get; private set; }
public ToggleSplitButton VolumeStatus { get; set; }
public MainWindowViewModel ViewModel { get; private set; }
internal MainWindowViewModel ViewModel { get; private set; }
public SettingsWindow SettingsWindow { get; set; }
public bool CanUpdate
{

View file

@ -0,0 +1,140 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Windows.MotionSettingsWindow">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock
Margin="0"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionGyroSensitivity}" />
<Slider
Margin="0,-5,0,-5"
Width="150"
MaxWidth="150"
TickFrequency="0.01"
IsSnapToTickEnabled="True"
Maximum="100"
Minimum="0"
Value="{Binding Sensitivity, Mode=TwoWay}" />
<TextBlock HorizontalAlignment="Center"
Margin="5, 0"
Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock
Margin="0"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionGyroDeadzone}" />
<Slider
Margin="0,-5,0,-5"
Width="150"
MaxWidth="150"
TickFrequency="0.01"
IsSnapToTickEnabled="True"
Maximum="100"
Minimum="0"
Value="{Binding GyroDeadzone, Mode=TwoWay}" />
<TextBlock
VerticalAlignment="Center"
Margin="5, 0"
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
</StackPanel>
<Separator Height="1" Margin="0,5" />
<CheckBox Margin="5" IsChecked="{Binding EnableCemuHookMotion}">
<TextBlock Margin="0,3,0,0" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" />
</CheckBox>
</StackPanel>
<Border Grid.Row="1"
Padding="20,5"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
HorizontalAlignment="Stretch">
<Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Vertical">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionServerHost}" />
<TextBox
Height="30"
MinWidth="100"
MaxWidth="100"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding DsuServerHost, Mode=TwoWay}" />
<TextBlock
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text=":" />
<TextBox
Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding DsuServerPort, Mode=TwoWay}" />
</StackPanel>
<StackPanel Orientation="Vertical">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,10,0,0" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionControllerSlot}" />
<ui:NumberBox Grid.Row="0" Grid.Column="1"
Name="CemuHookSlotUpDown"
SmallChange="1"
LargeChange="1"
Maximum="4"
Minimum="0"
Value="{Binding Slot}" />
<TextBlock Margin="0,10,0,0" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" />
<ui:NumberBox Grid.Row="1" Grid.Column="1"
Name="CemuHookRightJoyConSlotUpDown"
SmallChange="1"
LargeChange="1"
Maximum="4"
Minimum="0"
Value="{Binding AltSlot}" />
</Grid>
</StackPanel>
<CheckBox HorizontalAlignment="Center"
IsChecked="{Binding MirrorInput, Mode=TwoWay}">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionMirrorInput}" />
</CheckBox>
</StackPanel>
</Grid>
</Border>
</Grid>
</UserControl>

View file

@ -0,0 +1,81 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Common.Configuration.Hid.Controller;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Windows
{
public class MotionSettingsWindow : UserControl
{
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
public MotionSettingsWindow()
{
InitializeComponent();
}
public MotionSettingsWindow(ControllerSettingsViewModel viewmodel)
{
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
{
Slot = config.Slot,
AltSlot = config.AltSlot,
DsuServerHost = config.DsuServerHost,
DsuServerPort = config.DsuServerPort,
MirrorInput = config.MirrorInput,
EnableMotion = config.EnableMotion,
Sensitivity = config.Sensitivity,
GyroDeadzone = config.GyroDeadzone,
EnableCemuHookMotion = config.EnableCemuHookMotion
};
InitializeComponent();
}
private void InitializeComponent()
{
DataContext = _viewmodel;
AvaloniaXamlLoader.Load(this);
}
public static async Task Show(ControllerSettingsViewModel viewmodel, StyleableWindow window)
{
ContentDialog contentDialog = window.ContentDialog;
string name = string.Empty;
MotionSettingsWindow content = new MotionSettingsWindow(viewmodel);
if (contentDialog != null)
{
contentDialog.Title = LocaleManager.Instance["ControllerMotionTitle"];
contentDialog.PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"];
contentDialog.SecondaryButtonText = "";
contentDialog.CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"];
contentDialog.Content = content;
contentDialog.PrimaryButtonClick += (sender, args) =>
{
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
config.Slot = content._viewmodel.Slot;
config.EnableMotion = content._viewmodel.EnableMotion;
config.Sensitivity = content._viewmodel.Sensitivity;
config.GyroDeadzone = content._viewmodel.GyroDeadzone;
config.AltSlot = content._viewmodel.AltSlot;
config.DsuServerHost = content._viewmodel.DsuServerHost;
config.DsuServerPort = content._viewmodel.DsuServerPort;
config.EnableCemuHookMotion = content._viewmodel.EnableCemuHookMotion;
config.MirrorInput = content._viewmodel.MirrorInput;
};
await contentDialog.ShowAsync();
}
}
}
}

View file

@ -0,0 +1,57 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Windows.RumbleSettingsWindow">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock
Width="100"
TextWrapping="WrapWithOverflow"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsRumbleStrongMultiplier}" />
<Slider
Margin="0,-5,0,-5"
Width="200"
TickFrequency="0.01"
IsSnapToTickEnabled="True"
Maximum="10"
Minimum="0"
Value="{Binding StrongRumble, Mode=TwoWay}" />
<TextBlock
VerticalAlignment="Center"
Margin="5,0"
Text="{Binding StrongRumble, StringFormat=\{0:0.00\}}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock
Width="100"
TextWrapping="WrapWithOverflow"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsRumbleWeakMultiplier}" />
<Slider
Margin="0,-5,0,-5"
Width="200"
MaxWidth="200"
Maximum="10"
TickFrequency="0.01"
IsSnapToTickEnabled="True"
Minimum="0"
Value="{Binding WeakRumble, Mode=TwoWay}" />
<TextBlock
VerticalAlignment="Center"
Margin="5,0"
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

View file

@ -0,0 +1,67 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Common.Configuration.Hid.Controller;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Windows
{
public class RumbleSettingsWindow : UserControl
{
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
public RumbleSettingsWindow()
{
InitializeComponent();
}
public RumbleSettingsWindow(ControllerSettingsViewModel viewmodel)
{
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
{
StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble
};
InitializeComponent();
}
private void InitializeComponent()
{
DataContext = _viewmodel;
AvaloniaXamlLoader.Load(this);
}
public static async Task Show(ControllerSettingsViewModel viewmodel, StyleableWindow window)
{
ContentDialog contentDialog = window.ContentDialog;
string name = string.Empty;
RumbleSettingsWindow content = new RumbleSettingsWindow(viewmodel);
if (contentDialog != null)
{
contentDialog.Title = LocaleManager.Instance["ControllerRumbleTitle"];
contentDialog.PrimaryButtonText = LocaleManager.Instance["ControllerSettingsSave"];
contentDialog.SecondaryButtonText = "";
contentDialog.CloseButtonText = LocaleManager.Instance["ControllerSettingsClose"];
contentDialog.Content = content;
contentDialog.PrimaryButtonClick += (sender, args) =>
{
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
config.StrongRumble = content._viewmodel.StrongRumble;
config.WeakRumble = content._viewmodel.WeakRumble;
};
await contentDialog.ShowAsync();
}
}
}
}

View file

@ -0,0 +1,897 @@
<window:StyleableWindow
x:Class="Ryujinx.Ava.Ui.Windows.SettingsWindow"
xmlns="https://github.com/avaloniaui"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Width="1100"
Height="768"
d:DesignWidth="800"
d:DesignHeight="950"
MinWidth="800"
MinHeight="480"
WindowStartupLocation="CenterOwner"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel"
mc:Ignorable="d">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<Window.Resources>
<controls:KeyValueConverter x:Key="Key" />
</Window.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl
Grid.Row="1"
Focusable="False"
IsVisible="False"
KeyboardNavigation.IsTabStop="False">
<ui:ContentDialog Name="ContentDialog"
IsPrimaryButtonEnabled="True"
IsSecondaryButtonEnabled="True"
IsVisible="False" />
</ContentControl>
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
<ScrollViewer Name="UiPage"
Margin="0,0,10,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralGeneral}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ToggleDiscordTooltip}"
Text="{locale:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
</CheckBox>
<CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
<TextBlock Text="{locale:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
</CheckBox>
<CheckBox IsChecked="{Binding ShowConfirmExit}">
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
</CheckBox>
<CheckBox IsChecked="{Binding HideCursorOnIdle}">
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<ListBox
Name="GameList"
MinHeight="150"
Items="{Binding GameDirectories}" />
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
Name="PathBox"
Margin="0"
ToolTip.Tip="{locale:Locale AddGameDirBoxTooltip}"
VerticalAlignment="Stretch" />
<Button
Name="AddButton"
Grid.Column="1"
MinWidth="90"
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale AddGameDirTooltip}"
Click="AddButton_OnClick">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralAdd}" />
</Button>
<Button
Name="RemoveButton"
Grid.Column="2"
MinWidth="90"
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale RemoveGameDirTooltip}"
Click="RemoveButton_OnClick">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralRemove}" />
</Button>
</Grid>
</StackPanel>
<Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralTheme}" />
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<CheckBox IsChecked="{Binding EnableCustomTheme}"
ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}">
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" />
</CheckBox>
<TextBlock VerticalAlignment="Center"
Margin="0,10,0,0"
Grid.Row="1"
Text="{locale:Locale SettingsTabGeneralThemeCustomTheme}"
ToolTip.Tip="{locale:Locale CustomThemePathTooltip}" />
<TextBox Margin="0,10,0,0"
Grid.Row="1"
Grid.Column="1"
Text="{Binding CustomThemePath}" />
<Button Grid.Row="1"
Grid.Column="2"
Margin="10,10,0,0"
Command="{ReflectionBinding BrowseTheme}"
ToolTip.Tip="{locale:Locale CustomThemeBrowseTooltip}"
Content="{locale:Locale ButtonBrowse}" />
<TextBlock VerticalAlignment="Center"
Margin="0,10,0,0"
Grid.Row="2"
Text="{locale:Locale SettingsTabGeneralThemeBaseStyle}" />
<ComboBox VerticalAlignment="Center"
Margin="0,10,0,0"
Grid.Column="1"
Grid.Row="2"
MinWidth="100"
SelectedIndex="{Binding BaseStyleIndex}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleLight}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleDark}" />
</ComboBoxItem>
</ComboBox>
</Grid>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer Name="InputPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Padding="0,0,2,0"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel Margin="4" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
IsChecked="{Binding EnableDockedMode}">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
</CheckBox>
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
IsChecked="{Binding EnableKeyboard}">
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
</CheckBox>
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
IsChecked="{Binding EnableMouse}">
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
</CheckBox>
</StackPanel>
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0,0,0,0" MinHeight="600" />
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer Name="HotkeysPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel Margin="10,5" Orientation="Vertical" Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer Name="SystemPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabSystemCore}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemRegion}"
Width="250" />
<ComboBox SelectedIndex="{Binding Region}"
ToolTip.Tip="{locale:Locale RegionTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionUSA}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionEurope}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionAustralia}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionChina}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionKorea}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionTaiwan}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding Language}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageGerman}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageItalian}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageKorean}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageDutch}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguagePortuguese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageRussian}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}"
Width="250" />
<AutoCompleteBox
Name="TimeZoneBox"
Width="350"
FilterMode="Contains"
Items="{Binding TimeZones}"
SelectionChanged="TimeZoneBox_OnSelectionChanged"
Text="{Binding Path=TimeZone, Mode=OneWay}"
TextChanged="TimeZoneBox_OnTextChanged"
ValueMemberBinding="{ReflectionBinding TzMultiBinding}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTime}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="250"/>
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="350" />
</StackPanel>
<StackPanel Margin="250,0,0,10" Orientation="Horizontal">
<TimePicker
VerticalAlignment="Center"
ClockIdentifier="24HourClock"
SelectedTime="{Binding TimeOffset}"
Width="350"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableVsync}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabSystemHacks}" />
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
</StackPanel>
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding ExpandDramSize}"
ToolTip.Tip="{locale:Locale DRamTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
</CheckBox>
<CheckBox IsChecked="{Binding IgnoreMissingServices}"
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
</CheckBox>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="CpuPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabCpuCache}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding EnablePptc}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnablePptc}"
ToolTip.Tip="{locale:Locale PptcToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabCpuMemory}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemMemoryManagerMode}"
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding MemoryMode}"
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerSoftwareTooltip}">
<TextBlock
Text="{locale:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
</ComboBoxItem>
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerHostTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemMemoryManagerModeHost}" />
</ComboBoxItem>
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerUnsafeTooltip}">
<TextBlock
Text="{locale:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="GraphicsPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10, 5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsEnhancements}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<CheckBox IsChecked="{Binding EnableShaderCache}"
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
<TextBlock Text="{locale:Locale SettingsTabGraphicsEnableShaderCache}" />
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering}"
Width="250" />
<ComboBox SelectedIndex="{Binding MaxAnisotropy}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}">
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabGraphicsAnisotropicFilteringAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering2x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering4x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering8x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering16x}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"
Text="{locale:Locale SettingsTabGraphicsResolutionScale}"
Width="250" />
<ComboBox SelectedIndex="{Binding ResolutionScale}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale2x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale3x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
</ComboBoxItem>
</ComboBox>
<ui:NumberBox
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale ResolutionScaleEntryTooltip}"
MinWidth="150"
SmallChange="0.1"
LargeChange="1"
SimpleNumberFormat="F2"
SpinButtonPlacementMode="Inline"
IsVisible="{Binding IsCustomResolutionScaleActive}"
Maximum="100"
Minimum="0.1"
Value="{Binding CustomResolutionScale}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}"
Text="{locale:Locale SettingsTabGraphicsAspectRatio}"
Width="250" />
<ComboBox SelectedIndex="{Binding AspectRatio}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio4x3}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x10}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio21x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio32x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatioStretch}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
<Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale GraphicsBackendThreadingTooltip}"
Text="{locale:Locale SettingsTabGraphicsBackendMultithreading}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale GalThreadingTooltip}"
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonOff}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonOn}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
<Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}"
Text="{locale:Locale SettingsTabGraphicsShaderDumpPath}"
Width="250" />
<TextBox Text="{Binding ShaderDumpPath}"
Width="350"
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}" />
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="AudioPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabAudio}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioBackend}"
ToolTip.Tip="{locale:Locale AudioBackendTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding AudioBackend}"
Width="350"
HorizontalContentAlignment="Left">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendDummy}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsOpenAlEnabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendOpenAL}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSoundIO}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSDL2}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioVolume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Width="250" />
<ui:NumberBox Value="{Binding Volume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Width="350"
SmallChange="1"
LargeChange="10"
SimpleNumberFormat="F0"
SpinButtonPlacementMode="Inline"
Minimum="0"
Maximum="100" />
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<Slider Value="{Binding Volume}"
Margin="250,0,0,0"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Minimum="0"
Maximum="100"
SmallChange="5"
TickFrequency="5"
IsSnapToTickEnabled="True"
LargeChange="10"
Width="350" />
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="NetworkPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabNetworkConnection}" />
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
</CheckBox>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="LoggingPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border>
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabLoggingLogging}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableFileLog}"
ToolTip.Tip="{locale:Locale FileLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableLoggingToFile}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableStub}"
ToolTip.Tip="{locale:Locale StubLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableStubLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableInfo}"
ToolTip.Tip="{locale:Locale InfoLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableInfoLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableWarn}"
ToolTip.Tip="{locale:Locale WarnLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableWarningLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableError}"
ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableTrace}"
ToolTip.Tip="{locale:Locale TraceLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableGuest}"
ToolTip.Tip="{locale:Locale GuestLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDebug}"
ToolTip.Tip="{locale:Locale DebugLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsAccessLog}"
ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
</CheckBox>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"
Text="{locale:Locale SettingsTabLoggingFsGlobalAccessLogMode}"
Width="285" />
<ui:NumberBox
Maximum="3"
Minimum="0"
Width="150"
SpinButtonPlacementMode="Inline"
SmallChange="1"
LargeChange="1"
Value="{Binding FsGlobalAccessLogMode}" />
</StackPanel>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabLoggingOpenglLogLevel}"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
Width="285" />
<ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
Width="150"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingOpenglLogLevelNone}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingOpenglLogLevelError}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabLoggingOpenglLogLevelPerformance}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingOpenglLogLevelAll}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</Grid>
<ui:NavigationView Grid.Row="1" IsSettingsVisible="False" Name="NavPanel" IsBackEnabled="False"
PaneDisplayMode="LeftCompact"
Margin="2,10,10,0"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ui:NavigationView.MenuItems>
<ui:NavigationViewItem IsSelected="True"
Content="{locale:Locale SettingsTabGeneral}"
Tag="UiPage"
Icon="New" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabInput}"
Tag="InputPage"
Icon="Games" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabHotkeys}"
Tag="HotkeysPage"
Icon="Keyboard" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabSystem}"
Tag="SystemPage"
Icon="Settings" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabCpu}"
Tag="CpuPage">
<ui:NavigationViewItem.Icon>
<ui:FontIcon FontFamily="avares://Ryujinx.Ava/Assets/Fonts#Segoe Fluent Icons"
Glyph="{controls:GlyphValueConverter Chip}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabGraphics}"
Tag="GraphicsPage"
Icon="Image" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabAudio}"
Icon="Audio"
Tag="AudioPage" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabNetwork}"
Tag="NetworkPage"
Icon="Globe" />
<ui:NavigationViewItem
Content="{locale:Locale SettingsTabLogging}"
Tag="LoggingPage"
Icon="Document" />
</ui:NavigationView.MenuItems>
</ui:NavigationView>
<StackPanel
Grid.Row="2"
Margin="10"
Spacing="10"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Content="{locale:Locale SettingsButtonSave}" Click="SaveButton_Clicked" />
<Button Content="{locale:Locale SettingsButtonClose}" Click="CloseButton_Clicked" />
<Button Content="{locale:Locale SettingsButtonApply}"
Click="ApplyButton_Clicked" />
</StackPanel>
</Grid>
</window:StyleableWindow>

View file

@ -0,0 +1,298 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
namespace Ryujinx.Ava.Ui.Windows
{
public class SettingsWindow : StyleableWindow
{
private ListBox _gameList;
private TextBox _pathBox;
private AutoCompleteBox _timeZoneBox;
private ControllerSettingsWindow _controllerSettings;
// Pages
private Control _uiPage;
private Control _inputPage;
private Control _hotkeysPage;
private Control _systemPage;
private Control _cpuPage;
private Control _graphicsPage;
private Control _audioPage;
private Control _networkPage;
private Control _loggingPage;
private NavigationView _navPanel;
private ButtonKeyAssigner _currentAssigner;
internal SettingsViewModel ViewModel { get; set; }
public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager)
{
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["Settings"]}";
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
DataContext = ViewModel;
InitializeComponent();
AttachDebugDevTools();
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
MultiBinding tzMultiBinding = new() { Converter = converter };
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
tzMultiBinding.Bindings.Add(new Binding("Location"));
tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
_timeZoneBox.ValueMemberBinding = tzMultiBinding;
}
public SettingsWindow()
{
ViewModel = new SettingsViewModel();
DataContext = ViewModel;
InitializeComponent();
AttachDebugDevTools();
}
[Conditional("DEBUG")]
private void AttachDebugDevTools()
{
this.AttachDevTools();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_pathBox = this.FindControl<TextBox>("PathBox");
_gameList = this.FindControl<ListBox>("GameList");
_timeZoneBox = this.FindControl<AutoCompleteBox>("TimeZoneBox");
_controllerSettings = this.FindControl<ControllerSettingsWindow>("ControllerSettings");
_uiPage = this.FindControl<Control>("UiPage");
_inputPage = this.FindControl<Control>("InputPage");
_hotkeysPage = this.FindControl<Control>("HotkeysPage");
_systemPage = this.FindControl<Control>("SystemPage");
_cpuPage = this.FindControl<Control>("CpuPage");
_graphicsPage = this.FindControl<Control>("GraphicsPage");
_audioPage = this.FindControl<Control>("AudioPage");
_networkPage = this.FindControl<Control>("NetworkPage");
_loggingPage = this.FindControl<Control>("LoggingPage");
var pageGrid = this.FindControl<Grid>("Pages");
pageGrid.Children.Clear();
_navPanel = this.FindControl<NavigationView>("NavPanel");
_navPanel.SelectionChanged += NavPanelOnSelectionChanged;
_navPanel.SelectedItem = _navPanel.MenuItems.ElementAt(0);
}
private void Button_Checked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton button)
{
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
{
return;
}
if (_currentAssigner == null && (bool)button.IsChecked)
{
_currentAssigner = new ButtonKeyAssigner(button);
FocusManager.Instance.Focus(this, NavigationMethod.Pointer);
PointerPressed += MouseClick;
IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]);
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner);
}
else
{
if (_currentAssigner != null)
{
ToggleButton oldButton = _currentAssigner.ToggledButton;
_currentAssigner.Cancel();
_currentAssigner = null;
button.IsChecked = false;
}
}
}
}
private void Button_Unchecked(object sender, RoutedEventArgs e)
{
_currentAssigner?.Cancel();
_currentAssigner = null;
}
private void MouseClick(object sender, PointerPressedEventArgs e)
{
bool shouldUnbind = false;
if (e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed)
{
shouldUnbind = true;
}
_currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick;
}
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
{
if (e.SelectedItem is NavigationViewItem navitem)
{
switch (navitem.Tag.ToString())
{
case "UiPage":
_navPanel.Content = _uiPage;
break;
case "InputPage":
_navPanel.Content = _inputPage;
break;
case "HotkeysPage":
_navPanel.Content = _hotkeysPage;
break;
case "SystemPage":
_navPanel.Content = _systemPage;
break;
case "CpuPage":
_navPanel.Content = _cpuPage;
break;
case "GraphicsPage":
_navPanel.Content = _graphicsPage;
break;
case "AudioPage":
_navPanel.Content = _audioPage;
break;
case "NetworkPage":
_navPanel.Content = _networkPage;
break;
case "LoggingPage":
_navPanel.Content = _loggingPage;
break;
}
}
}
private async void AddButton_OnClick(object sender, RoutedEventArgs e)
{
string path = _pathBox.Text;
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
{
ViewModel.GameDirectories.Add(path);
}
else
{
path = await new OpenFolderDialog().ShowAsync(this);
if (!string.IsNullOrWhiteSpace(path))
{
ViewModel.GameDirectories.Add(path);
}
}
}
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
{
List<string> selected = new(_gameList.SelectedItems.Cast<string>());
foreach (string path in selected)
{
ViewModel.GameDirectories.Remove(path);
}
}
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
if (e.AddedItems[0] is TimeZone timeZone)
{
e.Handled = true;
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
}
}
}
private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
{
if (sender is AutoCompleteBox box)
{
if (box.SelectedItem != null && box.SelectedItem is TimeZone timeZone)
{
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
}
}
}
private void SaveButton_Clicked(object sender, RoutedEventArgs e)
{
SaveSettings();
Close();
}
private void CloseButton_Clicked(object sender, RoutedEventArgs e)
{
ViewModel.RevertIfNotSaved();
Close();
}
private void ApplyButton_Clicked(object sender, RoutedEventArgs e)
{
SaveSettings();
}
private void SaveSettings()
{
ViewModel.SaveSettings();
_controllerSettings?.SaveCurrentProfile();
if (Owner is MainWindow window)
{
window.ViewModel.LoadApplications();
}
}
protected override void OnClosed(EventArgs e)
{
_controllerSettings.Dispose();
_currentAssigner?.Cancel();
_currentAssigner = null;
base.OnClosed(e);
}
}
}

View file

@ -58,7 +58,7 @@ namespace Ryujinx.Ava.Ui.Windows
{
if (_restartQuery)
{
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava";
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());

View file

@ -1,6 +1,6 @@
namespace Ryujinx.Common.Configuration.Hid
{
public struct KeyboardHotkeys
public class KeyboardHotkeys
{
public Key ToggleVsync { get; set; }
public Key Screenshot { get; set; }

View file

@ -239,7 +239,7 @@ namespace Ryujinx.Ui.Windows
_gtk3KeyboardDriver.Dispose();
}
private static string GetShrinkedGamepadName(string str)
private static string GetShortGamepadName(string str)
{
const string ShrinkChars = "...";
const int MaxSize = 50;
@ -264,7 +264,7 @@ namespace Ryujinx.Ui.Windows
if (gamepad != null)
{
_inputDevice.Append($"keyboard/{id}", GetShrinkedGamepadName($"{gamepad.Name} ({id})"));
_inputDevice.Append($"keyboard/{id}", GetShortGamepadName($"{gamepad.Name} ({id})"));
gamepad.Dispose();
}
@ -276,7 +276,7 @@ namespace Ryujinx.Ui.Windows
if (gamepad != null)
{
_inputDevice.Append($"controller/{id}", GetShrinkedGamepadName($"{gamepad.Name} ({id})"));
_inputDevice.Append($"controller/{id}", GetShortGamepadName($"{gamepad.Name} ({id})"));
gamepad.Dispose();
}